|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis.Symbols;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
/// <summary>
/// Represents a parameter of a method or indexer.
/// </summary>
internal abstract partial class ParameterSymbol : Symbol, IParameterSymbolInternal
{
internal const string ValueParameterName = "value";
internal ParameterSymbol()
{
}
/// <summary>
/// The original definition of this symbol. If this symbol is constructed from another
/// symbol by type substitution then OriginalDefinition gets the original symbol as it was defined in
/// source or metadata.
/// </summary>
public new virtual ParameterSymbol OriginalDefinition
{
get
{
return this;
}
}
protected sealed override Symbol OriginalSymbolDefinition
{
get
{
return this.OriginalDefinition;
}
}
/// <summary>
/// Gets the type of the parameter along with its annotations.
/// </summary>
public abstract TypeWithAnnotations TypeWithAnnotations { get; }
/// <summary>
/// Gets the type of the parameter.
/// </summary>
public TypeSymbol Type => TypeWithAnnotations.Type;
/// <summary>
/// Determines if the parameter ref, out or neither.
/// </summary>
public abstract RefKind RefKind { get; }
/// <summary>
/// Returns true if the parameter is a discard parameter.
/// </summary>
public abstract bool IsDiscard { get; }
/// <summary>
/// Custom modifiers associated with the ref modifier, or an empty array if there are none.
/// </summary>
public abstract ImmutableArray<CustomModifier> RefCustomModifiers { get; }
/// <summary>
/// Describes how the parameter is marshalled when passed to native code.
/// Null if no specific marshalling information is available for the parameter.
/// </summary>
/// <remarks>PE symbols don't provide this information and always return null.</remarks>
internal abstract MarshalPseudoCustomAttributeData? MarshallingInformation { get; }
/// <summary>
/// Returns the marshalling type of this parameter, or 0 if marshalling information isn't available.
/// </summary>
/// <remarks>
/// By default this information is extracted from <see cref="MarshallingInformation"/> if available.
/// Since the compiler does only need to know the marshalling type of symbols that aren't emitted
/// PE symbols just decode the type from metadata and don't provide full marshalling information.
/// </remarks>
internal virtual UnmanagedType MarshallingType
{
get
{
var info = MarshallingInformation;
return info != null ? info.UnmanagedType : 0;
}
}
internal bool IsMarshalAsObject
{
get
{
switch (this.MarshallingType)
{
case UnmanagedType.Interface:
case UnmanagedType.IUnknown:
case Cci.Constants.UnmanagedType_IDispatch:
return true;
}
return false;
}
}
/// <summary>
/// Gets the ordinal position of the parameter. The first parameter has ordinal zero.
/// The "'this' parameter has ordinal -1.
/// </summary>
public abstract int Ordinal { get; }
/// <summary>
/// Returns true if the parameter was declared as a parameter array.
/// Note: it is possible for any parameter to have the [ParamArray] attribute (for instance, in IL),
/// even if it is not the last parameter. So check for that.
/// </summary>
public abstract bool IsParamsArray { get; }
/// <summary>
/// Returns true if the parameter was declared as a parameter collection.
/// Note: it is possible for any parameter to have the [ParamCollection] attribute (for instance, in IL),
/// even if it is not the last parameter. So check for that.
/// </summary>
public abstract bool IsParamsCollection { get; }
internal bool IsParams => IsParamsArray || IsParamsCollection;
/// <summary>
/// Returns true if the parameter is semantically optional.
/// </summary>
/// <remarks>
/// True if and only if the parameter has a default argument syntax,
/// or the parameter is not a params-array and Optional metadata flag is set.
/// </remarks>
public bool IsOptional
{
get
{
// DEV10 COMPATIBILITY: Special handling for ParameterArray params
//
// Ideally we should not need the additional "isParams" check below
// as a ParameterArray param cannot have a default value.
// However, for certain cases of overriding this is not true.
// See test "CodeGenTests.NoDefaultForParams_Dev10781558" for an example.
// See Roslyn bug 10753 and Dev10 bug 781558 for details.
//
// To maintain compatibility with Dev10, we allow such code to compile but explicitly
// classify a ParameterArray param as a required parameter.
//
// Also when we call f() where signature of f is void([Optional]params int[] args)
// an empty array is created and passed to f.
//
// We also do not consider ref/out parameters as optional, unless in COM interop scenarios
// and only for ref.
RefKind refKind;
return !IsParams && IsMetadataOptional &&
((refKind = RefKind) == RefKind.None ||
(refKind is RefKind.In or RefKind.RefReadOnlyParameter) ||
(refKind == RefKind.Ref && ContainingSymbol.ContainingType.IsComImport));
}
}
/// <summary>
/// True if Optional flag is set in metadata.
/// </summary>
internal abstract bool IsMetadataOptional { get; }
/// <summary>
/// True if In flag is set in metadata.
/// </summary>
internal abstract bool IsMetadataIn { get; }
/// <summary>
/// True if Out flag is set in metadata.
/// </summary>
internal abstract bool IsMetadataOut { get; }
/// <summary>
/// Returns true if the parameter explicitly specifies a default value to be passed
/// when no value is provided as an argument to a call.
/// </summary>
/// <remarks>
/// True if the parameter has a default argument syntax,
/// or the parameter is from source and <see cref="DefaultParameterValueAttribute"/> is applied,
/// or the parameter is from metadata and HasDefault metadata flag is set. See
/// <see cref="IsOptional"/> to determine if the parameter will be considered optional by
/// overload resolution.
///
/// The default value can be obtained with <see cref="ExplicitDefaultValue"/> property.
/// </remarks>
[MemberNotNullWhen(true, nameof(ExplicitDefaultConstantValue))]
public bool HasExplicitDefaultValue
{
get
{
// In the symbol model, only optional parameters have default values.
// Internally, however, non-optional parameters may also have default
// values (accessible via DefaultConstantValue). For example, if the
// DefaultParameterValue attribute is applied to a non-optional parameter
// we still want to emit a default parameter value, even if it isn't
// recognized by the language.
// Special Case: params parameters are never optional, but can have
// default values (e.g. if the params-ness is inherited from an
// overridden method, but the current method declares the parameter
// as optional). In such cases, dev11 emits the default value.
return IsOptional && ExplicitDefaultConstantValue != null;
}
}
/// <summary>
/// Returns the default value of the parameter. If <see cref="HasExplicitDefaultValue"/>
/// returns false then DefaultValue throws an InvalidOperationException.
/// </summary>
/// <remarks>
/// If the parameter type is a struct and the default value of the parameter
/// is the default value of the struct type or of type parameter type which is
/// not known to be a referenced type, then this property will return null.
/// </remarks>
/// <exception cref="InvalidOperationException">The parameter has no default value.</exception>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public object? ExplicitDefaultValue
{
get
{
if (HasExplicitDefaultValue)
{
return ExplicitDefaultConstantValue.Value;
}
throw new InvalidOperationException();
}
}
/// <summary>
/// Returns the default value constant of the parameter,
/// or null if the parameter doesn't have a default value or
/// the parameter type is a struct and the default value of the parameter
/// is the default value of the struct type or of type parameter type which is
/// not known to be a referenced type.
/// </summary>
/// <remarks>
/// This is used for emitting. It does not reflect the language semantics
/// (i.e. even non-optional parameters can have default values).
/// </remarks>
internal abstract ConstantValue? ExplicitDefaultConstantValue { get; }
/// <summary>
/// Gets the kind of this symbol.
/// </summary>
public sealed override SymbolKind Kind
{
get
{
return SymbolKind.Parameter;
}
}
/// <summary>
/// Implements visitor pattern.
/// </summary>
internal override TResult Accept<TArgument, TResult>(CSharpSymbolVisitor<TArgument, TResult> visitor, TArgument argument)
{
return visitor.VisitParameter(this, argument);
}
public override void Accept(CSharpSymbolVisitor visitor)
{
visitor.VisitParameter(this);
}
public override TResult Accept<TResult>(CSharpSymbolVisitor<TResult> visitor)
{
return visitor.VisitParameter(this);
}
/// <summary>
/// Get this accessibility that was declared on this symbol. For symbols that do not have
/// accessibility declared on them, returns NotApplicable.
/// </summary>
public override Accessibility DeclaredAccessibility
{
get
{
return Accessibility.NotApplicable;
}
}
/// <summary>
/// Returns true if this symbol was declared as requiring an override; i.e., declared with
/// the "abstract" modifier. Also returns true on a type declared as "abstract", all
/// interface types, and members of interface types.
/// </summary>
public override bool IsAbstract
{
get
{
return false;
}
}
/// <summary>
/// Returns true if this symbol was declared to override a base class member and was also
/// sealed from further overriding; i.e., declared with the "sealed" modifier. Also set for
/// types that do not allow a derived class (declared with "sealed" or "static" or "struct"
/// or "enum" or "delegate").
/// </summary>
public override bool IsSealed
{
get
{
return false;
}
}
/// <summary>
/// Returns true if this symbol is "virtual", has an implementation, and does not override a
/// base class member; i.e., declared with the "virtual" modifier. Does not return true for
/// members declared as abstract or override.
/// </summary>
public override bool IsVirtual
{
get
{
return false;
}
}
/// <summary>
/// Returns true if this symbol was declared to override a base class member; i.e., declared
/// with the "override" modifier. Still returns true if member was declared to override
/// something, but (erroneously) no member to override exists.
/// </summary>
public override bool IsOverride
{
get
{
return false;
}
}
/// <summary>
/// Returns true if this symbol is "static"; i.e., declared with the "static" modifier or
/// implicitly static.
/// </summary>
public override bool IsStatic
{
get
{
return false;
}
}
/// <summary>
/// Returns true if this symbol has external implementation; i.e., declared with the
/// "extern" modifier.
/// </summary>
public override bool IsExtern
{
get
{
return false;
}
}
/// <summary>
/// Returns true if the parameter is the hidden 'this' parameter.
/// </summary>
public virtual bool IsThis
{
get
{
return false;
}
}
/// <summary>
/// Returns data decoded from Obsolete attribute or null if there is no Obsolete attribute.
/// This property returns ObsoleteAttributeData.Uninitialized if attribute arguments haven't been decoded yet.
/// </summary>
internal sealed override ObsoleteAttributeData? ObsoleteAttributeData
{
get { return null; }
}
internal abstract bool IsIDispatchConstant { get; }
internal abstract bool IsIUnknownConstant { get; }
internal abstract bool IsCallerFilePath { get; }
internal abstract bool IsCallerLineNumber { get; }
internal abstract bool IsCallerMemberName { get; }
internal abstract int CallerArgumentExpressionParameterIndex { get; }
internal abstract FlowAnalysisAnnotations FlowAnalysisAnnotations { get; }
internal abstract ImmutableHashSet<string> NotNullIfParameterNotNull { get; }
/// <summary>
/// Indexes of the parameters that will be passed to the constructor of the interpolated string handler type
/// when an interpolated string handler conversion occurs. These indexes are ordered in the order to be passed
/// to the constructor.
/// <para/>
/// Indexes greater than or equal to 0 are references to parameters defined on the containing method or indexer.
/// Indexes less than 0 are constants defined on <see cref="BoundInterpolatedStringArgumentPlaceholder"/>.
/// </summary>
internal abstract ImmutableArray<int> InterpolatedStringHandlerArgumentIndexes { get; }
/// <summary>
/// True if the parameter is attributed with <c>InterpolatedStringHandlerArgumentAttribute</c> and the attribute
/// has some error (such as invalid names).
/// </summary>
internal abstract bool HasInterpolatedStringHandlerArgumentError { get; }
/// <summary>
/// The effective scope. This is from the declared scope, implicit scope and any
/// <c>UnscopedRefAttribute</c>.
/// </summary>
internal abstract ScopedKind EffectiveScope { get; }
internal abstract bool HasUnscopedRefAttribute { get; }
internal abstract bool UseUpdatedEscapeRules { get; }
protected sealed override bool IsHighestPriorityUseSiteErrorCode(int code) => code is (int)ErrorCode.ERR_UnsupportedCompilerFeature or (int)ErrorCode.ERR_BogusType;
public override bool HasUnsupportedMetadata
{
get
{
UseSiteInfo<AssemblySymbol> info = default;
DeriveUseSiteInfoFromParameter(ref info, this);
return info.DiagnosticInfo?.Code is (int)ErrorCode.ERR_BogusType or (int)ErrorCode.ERR_UnsupportedCompilerFeature;
}
}
protected override ISymbol CreateISymbol()
{
return new PublicModel.ParameterSymbol(this);
}
#region IParameterSymbolInternal
ITypeSymbolInternal IParameterSymbolInternal.Type => Type;
RefKind IParameterSymbolInternal.RefKind => RefKind;
#endregion
}
}
|