提交 2b6d6f29 编写于 作者: A Andy Gocke 提交者: GitHub

Make local function default parameter value binding lazy (#17014)

Make local function default parameter value binding lazy

Current strict binding can cause circularity problems when local
functions are referenced. This change causes local functions to use lazy
default parameter binding, similar to methods, and then forces their
construction when diagnostics are requested for the local function.

This also requires a mechanism for recording declaration diagnostics
outside of adding to the compilation's DeclarationDiagnostics. A new
type, DeclarationDiagnosticStore is introduced as an abstraction to
store declaration diagnostics on either the compilation or in a local
DiagnosticBag, depending on the needs of the symbol storing diagnostics.

Fixes #16451, #17293
上级 e6a6812d
......@@ -656,7 +656,6 @@
<Compile Include="Symbols\Source\SourcePropertyAccessorSymbol.cs" />
<Compile Include="Symbols\Source\SourcePropertySymbol.cs" />
<Compile Include="Symbols\Source\SourceSimpleParameterSymbol.cs" />
<Compile Include="Symbols\Source\SourceStrictComplexParameterSymbol.cs" />
<Compile Include="Symbols\Source\SourceTypeParameterSymbol.cs" />
<Compile Include="Symbols\Source\SourceUserDefinedConversionSymbol.cs" />
<Compile Include="Symbols\Source\SourceUserDefinedOperatorSymbol.cs" />
......
......@@ -13,43 +13,6 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
internal sealed class LocalFunctionSymbol : MethodSymbol
{
private sealed class ParametersAndDiagnostics
{
internal readonly ImmutableArray<ParameterSymbol> Parameters;
internal readonly bool IsVararg;
internal readonly ImmutableArray<Diagnostic> Diagnostics;
internal ParametersAndDiagnostics(ImmutableArray<ParameterSymbol> parameters, bool isVararg, ImmutableArray<Diagnostic> diagnostics)
{
Parameters = parameters;
IsVararg = isVararg;
Diagnostics = diagnostics;
}
}
private sealed class TypeParameterConstraintsAndDiagnostics
{
internal readonly ImmutableArray<TypeParameterConstraintClause> ConstraintClauses;
internal readonly ImmutableArray<Diagnostic> Diagnostics;
internal TypeParameterConstraintsAndDiagnostics(ImmutableArray<TypeParameterConstraintClause> constraintClauses, ImmutableArray<Diagnostic> diagnostics)
{
ConstraintClauses = constraintClauses;
Diagnostics = diagnostics;
}
}
private sealed class ReturnTypeAndDiagnostics
{
internal readonly TypeSymbol ReturnType;
internal readonly ImmutableArray<Diagnostic> Diagnostics;
internal ReturnTypeAndDiagnostics(TypeSymbol returnType, ImmutableArray<Diagnostic> diagnostics)
{
ReturnType = returnType;
Diagnostics = diagnostics;
}
}
private readonly Binder _binder;
private readonly LocalFunctionStatementSyntax _syntax;
......@@ -57,11 +20,17 @@ internal ReturnTypeAndDiagnostics(TypeSymbol returnType, ImmutableArray<Diagnost
private readonly DeclarationModifiers _declarationModifiers;
private readonly ImmutableArray<LocalFunctionTypeParameterSymbol> _typeParameters;
private readonly RefKind _refKind;
private ParametersAndDiagnostics _lazyParametersAndDiagnostics;
private TypeParameterConstraintsAndDiagnostics _lazyTypeParameterConstraintsAndDiagnostics;
private ReturnTypeAndDiagnostics _lazyReturnTypeAndDiagnostics;
private ImmutableArray<ParameterSymbol> _lazyParameters;
private bool _lazyIsVarArg;
private ImmutableArray<TypeParameterConstraintClause> _lazyTypeParameterConstraints;
private TypeSymbol _lazyReturnType;
private TypeSymbol _iteratorElementType;
private ImmutableArray<Diagnostic> _diagnostics;
// Lock for initializing lazy fields and registering their diagnostics
// Acquire this lock when initializing lazy objects to guarantee their declaration
// diagnostics get added to the store exactly once
private readonly DiagnosticBag _declarationDiagnostics;
public LocalFunctionSymbol(
Binder binder,
......@@ -76,31 +45,35 @@ internal ReturnTypeAndDiagnostics(TypeSymbol returnType, ImmutableArray<Diagnost
DeclarationModifiers.Static |
syntax.Modifiers.ToDeclarationModifiers();
var diagnostics = DiagnosticBag.GetInstance();
ScopeBinder = binder;
binder = binder.WithUnsafeRegionIfNecessary(syntax.Modifiers);
_declarationDiagnostics = new DiagnosticBag();
if (_syntax.TypeParameterList != null)
{
binder = new WithMethodTypeParametersBinder(this, binder);
_typeParameters = MakeTypeParameters(diagnostics);
_typeParameters = MakeTypeParameters(_declarationDiagnostics);
}
else
{
_typeParameters = ImmutableArray<LocalFunctionTypeParameterSymbol>.Empty;
ReportErrorIfHasConstraints(_syntax.ConstraintClauses, diagnostics);
ReportErrorIfHasConstraints(_syntax.ConstraintClauses, _declarationDiagnostics);
}
if (IsExtensionMethod)
{
diagnostics.Add(ErrorCode.ERR_BadExtensionAgg, Locations[0]);
_declarationDiagnostics.Add(ErrorCode.ERR_BadExtensionAgg, Locations[0]);
}
foreach (var param in syntax.ParameterList.Parameters)
{
ReportAttributesDisallowed(param.AttributeLists, _declarationDiagnostics);
}
_binder = binder;
_refKind = (syntax.ReturnType.Kind() == SyntaxKind.RefType) ? RefKind.Ref : RefKind.None;
_diagnostics = diagnostics.ToReadOnlyAndFree();
}
/// <summary>
......@@ -109,49 +82,39 @@ internal ReturnTypeAndDiagnostics(TypeSymbol returnType, ImmutableArray<Diagnost
/// </summary>
internal Binder ScopeBinder { get; }
public Binder ParameterBinder => _binder;
internal void GetDeclarationDiagnostics(DiagnosticBag addTo)
{
// Force attribute binding for diagnostics
// Force complete type parameters
foreach (var typeParam in _typeParameters)
{
typeParam.GetAttributesBag(addTo);
typeParam.ForceComplete(null, default(CancellationToken));
}
// force lazy init
ComputeParameters();
foreach (var p in _syntax.ParameterList.Parameters)
foreach (var p in _lazyParameters)
{
if (p.IsArgList)
{
addTo.Add(ErrorCode.ERR_IllegalVarArgs, p.Location);
}
// Force complete parameters to retrieve all diagnostics
p.ForceComplete(null, default(CancellationToken));
}
ComputeReturnType();
var diags = ImmutableInterlocked.InterlockedExchange(ref _diagnostics, default(ImmutableArray<Diagnostic>));
if (!diags.IsDefault)
{
addTo.AddRange(diags);
addTo.AddRange(_lazyParametersAndDiagnostics.Diagnostics);
// Note _lazyParametersAndDiagnostics and _lazyReturnTypeAndDiagnostics
// are computed always, but _lazyTypeParameterConstraintsAndDiagnostics
// is only computed if there are constraints.
if (_lazyTypeParameterConstraintsAndDiagnostics != null)
{
addTo.AddRange(_lazyTypeParameterConstraintsAndDiagnostics.Diagnostics);
}
addTo.AddRange(_lazyReturnTypeAndDiagnostics.Diagnostics);
}
addTo.AddRange(_declarationDiagnostics);
}
internal override void AddDeclarationDiagnostics(DiagnosticBag diagnostics)
=> _declarationDiagnostics.AddRange(diagnostics);
public override bool IsVararg
{
get
{
ComputeParameters();
return _lazyParametersAndDiagnostics.IsVararg;
return _lazyIsVarArg;
}
}
......@@ -160,49 +123,51 @@ public override ImmutableArray<ParameterSymbol> Parameters
get
{
ComputeParameters();
return _lazyParametersAndDiagnostics.Parameters;
return _lazyParameters;
}
}
private void ComputeParameters()
{
if (_lazyParametersAndDiagnostics != null)
if (_lazyParameters != null)
{
return;
}
var diagnostics = DiagnosticBag.GetInstance();
SyntaxToken arglistToken;
foreach (var param in _syntax.ParameterList.Parameters)
{
ReportAnyAttributes(diagnostics, param.AttributeLists);
}
var diagnostics = DiagnosticBag.GetInstance();
var parameters = ParameterHelpers.MakeParameters(
_binder,
this,
_syntax.ParameterList,
arglistToken: out arglistToken,
diagnostics: diagnostics,
allowRefOrOut: true,
allowThis: true,
beStrict: true);
diagnostics: diagnostics);
var isVararg = (arglistToken.Kind() == SyntaxKind.ArgListKeyword);
if (IsAsync && diagnostics.IsEmptyWithoutResolution)
var isVararg = arglistToken.Kind() == SyntaxKind.ArgListKeyword;
if (isVararg)
{
diagnostics.Add(ErrorCode.ERR_IllegalVarArgs, arglistToken.GetLocation());
}
if (IsAsync)
{
SourceMemberMethodSymbol.ReportAsyncParameterErrors(parameters, diagnostics, this.Locations[0]);
}
var value = new ParametersAndDiagnostics(parameters, isVararg, diagnostics.ToReadOnlyAndFree());
Interlocked.CompareExchange(ref _lazyParametersAndDiagnostics, value, null);
}
private static void ReportAnyAttributes(DiagnosticBag diagnostics, SyntaxList<AttributeListSyntax> attributes)
{
foreach (var attrList in attributes)
lock (_declarationDiagnostics)
{
diagnostics.Add(ErrorCode.ERR_AttributesInLocalFuncDecl, attrList.Location);
if (_lazyParameters != null)
{
diagnostics.Free();
return;
}
_declarationDiagnostics.AddRangeAndFree(diagnostics);
_lazyIsVarArg = isVararg;
_lazyParameters = parameters;
}
}
......@@ -211,7 +176,7 @@ public override TypeSymbol ReturnType
get
{
ComputeReturnType();
return _lazyReturnTypeAndDiagnostics.ReturnType;
return _lazyReturnType;
}
}
......@@ -225,7 +190,7 @@ internal override RefKind RefKind
internal void ComputeReturnType()
{
if (_lazyReturnTypeAndDiagnostics != null)
if (_lazyReturnType != null)
{
return;
}
......@@ -243,13 +208,21 @@ internal void ComputeReturnType()
diagnostics.Add(ErrorCode.ERR_BadAsyncReturn, this.Locations[0]);
}
if (refKind != RefKind.None && returnType.SpecialType == SpecialType.System_Void)
Debug.Assert(refKind == RefKind.None
|| returnType.SpecialType != SpecialType.System_Void
|| returnTypeSyntax.HasErrors);
lock (_declarationDiagnostics)
{
Debug.Assert(returnTypeSyntax.HasErrors);
}
if (_lazyReturnType != null)
{
diagnostics.Free();
return;
}
var value = new ReturnTypeAndDiagnostics(returnType, diagnostics.ToReadOnlyAndFree());
Interlocked.CompareExchange(ref _lazyReturnTypeAndDiagnostics, value, null);
_declarationDiagnostics.AddRangeAndFree(diagnostics);
_lazyReturnType = returnType;
}
}
public override bool ReturnsVoid => ReturnType?.SpecialType == SpecialType.System_Void;
......@@ -371,6 +344,14 @@ internal override bool TryGetThisParameter(out ParameterSymbol thisParameter)
return true;
}
private static void ReportAttributesDisallowed(SyntaxList<AttributeListSyntax> attributes, DiagnosticBag diagnostics)
{
foreach (var attrList in attributes)
{
diagnostics.Add(ErrorCode.ERR_AttributesInLocalFuncDecl, attrList.Location);
}
}
private ImmutableArray<LocalFunctionTypeParameterSymbol> MakeTypeParameters(DiagnosticBag diagnostics)
{
var result = ArrayBuilder<LocalFunctionTypeParameterSymbol>.GetInstance();
......@@ -383,12 +364,13 @@ private ImmutableArray<LocalFunctionTypeParameterSymbol> MakeTypeParameters(Diag
diagnostics.Add(ErrorCode.ERR_IllegalVarianceSyntax, parameter.VarianceKeyword.GetLocation());
}
// Attributes are currently disallowed on local function type parameters
ReportAttributesDisallowed(parameter.AttributeLists, diagnostics);
var identifier = parameter.Identifier;
var location = identifier.GetLocation();
var name = identifier.ValueText;
ReportAnyAttributes(diagnostics, parameter.AttributeLists);
foreach (var @param in result)
{
if (name == @param.Name)
......@@ -432,15 +414,23 @@ internal ImmutableArray<TypeSymbol> GetTypeParameterConstraintTypes(int ordinal)
private TypeParameterConstraintClause GetTypeParameterConstraintClause(int ordinal)
{
if (_lazyTypeParameterConstraintsAndDiagnostics == null)
if (_lazyTypeParameterConstraints == null)
{
var diagnostics = DiagnosticBag.GetInstance();
var constraints = MakeTypeParameterConstraints(diagnostics);
var value = new TypeParameterConstraintsAndDiagnostics(constraints, diagnostics.ToReadOnlyAndFree());
Interlocked.CompareExchange(ref _lazyTypeParameterConstraintsAndDiagnostics, value, null);
lock (_declarationDiagnostics)
{
if (_lazyTypeParameterConstraints == null)
{
_declarationDiagnostics.AddRange(diagnostics);
_lazyTypeParameterConstraints = constraints;
}
}
diagnostics.Free();
}
var clauses = _lazyTypeParameterConstraintsAndDiagnostics.ConstraintClauses;
var clauses = _lazyTypeParameterConstraints;
return (clauses.Length > 0) ? clauses[ordinal] : null;
}
......
......@@ -20,8 +20,7 @@ internal static class ParameterHelpers
out SyntaxToken arglistToken,
DiagnosticBag diagnostics,
bool allowRefOrOut,
bool allowThis,
bool beStrict)
bool allowThis)
{
arglistToken = default(SyntaxToken);
......@@ -85,8 +84,7 @@ internal static class ParameterHelpers
parameterIndex,
(paramsKeyword.Kind() != SyntaxKind.None),
parameterIndex == 0 && thisKeyword.Kind() != SyntaxKind.None,
diagnostics,
beStrict);
diagnostics);
ReportParameterErrors(owner, parameterSyntax, parameter, firstDefault, diagnostics);
......
......@@ -69,34 +69,13 @@ private enum ParameterSyntaxKind : byte
_lazyDefaultSyntaxValue = defaultSyntaxValue;
}
protected virtual Binder ParameterBinder
{
get { return null; }
}
private Binder ParameterBinderOpt => (ContainingSymbol as LocalFunctionSymbol)?.ParameterBinder;
internal override SyntaxReference SyntaxReference
{
get
{
return _syntaxRef;
}
}
internal override SyntaxReference SyntaxReference => _syntaxRef;
internal ParameterSyntax CSharpSyntaxNode
{
get
{
return _syntaxRef == null ? null : (ParameterSyntax)_syntaxRef.GetSyntax();
}
}
internal ParameterSyntax CSharpSyntaxNode => (ParameterSyntax)_syntaxRef?.GetSyntax();
internal SyntaxTree SyntaxTree
{
get
{
return _syntaxRef == null ? null : _syntaxRef.SyntaxTree;
}
}
internal SyntaxTree SyntaxTree => _syntaxRef == null ? null : _syntaxRef.SyntaxTree;
internal override ConstantValue ExplicitDefaultConstantValue
{
......@@ -126,73 +105,27 @@ internal override ConstantValue DefaultValueFromAttributes
}
internal override bool IsIDispatchConstant
{
get
{
CommonParameterWellKnownAttributeData data = GetDecodedWellKnownAttributeData();
return data != null && data.HasIDispatchConstantAttribute;
}
}
=> GetDecodedWellKnownAttributeData()?.HasIDispatchConstantAttribute == true;
internal override bool IsIUnknownConstant
{
get
{
CommonParameterWellKnownAttributeData data = GetDecodedWellKnownAttributeData();
return data != null && data.HasIUnknownConstantAttribute;
}
}
=> GetDecodedWellKnownAttributeData()?.HasIUnknownConstantAttribute == true;
private bool HasCallerLineNumberAttribute
{
get
{
ParameterEarlyWellKnownAttributeData data = GetEarlyDecodedWellKnownAttributeData();
return data != null && data.HasCallerLineNumberAttribute;
}
}
=> GetEarlyDecodedWellKnownAttributeData()?.HasCallerLineNumberAttribute == true;
private bool HasCallerFilePathAttribute
{
get
{
ParameterEarlyWellKnownAttributeData data = GetEarlyDecodedWellKnownAttributeData();
return data != null && data.HasCallerFilePathAttribute;
}
}
=> GetEarlyDecodedWellKnownAttributeData()?.HasCallerFilePathAttribute == true;
private bool HasCallerMemberNameAttribute
{
get
{
ParameterEarlyWellKnownAttributeData data = GetEarlyDecodedWellKnownAttributeData();
return data != null && data.HasCallerMemberNameAttribute;
}
}
=> GetEarlyDecodedWellKnownAttributeData()?.HasCallerMemberNameAttribute == true;
internal override bool IsCallerLineNumber
{
get
{
return HasCallerLineNumberAttribute;
}
}
internal override bool IsCallerLineNumber => HasCallerLineNumberAttribute;
internal override bool IsCallerFilePath
{
get
{
return !HasCallerLineNumberAttribute && HasCallerFilePathAttribute;
}
}
internal override bool IsCallerFilePath => !HasCallerLineNumberAttribute && HasCallerFilePathAttribute;
internal override bool IsCallerMemberName
{
get
{
return !HasCallerLineNumberAttribute && !HasCallerFilePathAttribute && HasCallerMemberNameAttribute;
}
}
internal override bool IsCallerMemberName => !HasCallerLineNumberAttribute
&& !HasCallerFilePathAttribute
&& HasCallerMemberNameAttribute;
private ConstantValue DefaultSyntaxValue
{
......@@ -201,8 +134,16 @@ private ConstantValue DefaultSyntaxValue
if (_lazyDefaultSyntaxValue == ConstantValue.Unset)
{
var diagnostics = DiagnosticBag.GetInstance();
if (Interlocked.CompareExchange(ref _lazyDefaultSyntaxValue, MakeDefaultExpression(diagnostics, ParameterBinder), ConstantValue.Unset) == ConstantValue.Unset)
if (Interlocked.CompareExchange(
ref _lazyDefaultSyntaxValue,
MakeDefaultExpression(diagnostics, ParameterBinderOpt),
ConstantValue.Unset) == ConstantValue.Unset)
{
// This is a race condition where the thread that loses
// the compare exchange tries to access the diagnostics
// before the thread which won the race finishes adding
// them. https://github.com/dotnet/roslyn/issues/17243
AddDeclarationDiagnostics(diagnostics);
}
......@@ -293,25 +234,13 @@ public override string MetadataName
}
}
protected virtual IAttributeTargetSymbol AttributeOwner
{
get { return this; }
}
protected virtual IAttributeTargetSymbol AttributeOwner => this;
IAttributeTargetSymbol IAttributeTargetSymbol.AttributesOwner
{
get { return this.AttributeOwner; }
}
IAttributeTargetSymbol IAttributeTargetSymbol.AttributesOwner => AttributeOwner;
AttributeLocation IAttributeTargetSymbol.DefaultAttributeLocation
{
get { return AttributeLocation.Parameter; }
}
AttributeLocation IAttributeTargetSymbol.DefaultAttributeLocation => AttributeLocation.Parameter;
AttributeLocation IAttributeTargetSymbol.AllowedAttributeLocations
{
get { return AttributeLocation.Parameter; }
}
AttributeLocation IAttributeTargetSymbol.AllowedAttributeLocations => AttributeLocation.Parameter;
/// <summary>
/// Symbol to copy bound attributes from, or null if the attributes are not shared among multiple source parameter symbols.
......@@ -399,12 +328,12 @@ internal virtual OneOrMany<SyntaxList<AttributeListSyntax>> GetAttributeDeclarat
/// <remarks>
/// Forces binding and decoding of attributes.
/// </remarks>
internal CommonParameterWellKnownAttributeData GetDecodedWellKnownAttributeData(DiagnosticBag diagnosticsOpt = null)
internal CommonParameterWellKnownAttributeData GetDecodedWellKnownAttributeData()
{
var attributesBag = _lazyCustomAttributesBag;
if (attributesBag == null || !attributesBag.IsDecodedWellKnownAttributeDataComputed)
{
attributesBag = this.GetAttributesBag(diagnosticsOpt);
attributesBag = this.GetAttributesBag();
}
return (CommonParameterWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData;
......@@ -416,12 +345,12 @@ internal CommonParameterWellKnownAttributeData GetDecodedWellKnownAttributeData(
/// <remarks>
/// Forces binding and decoding of attributes.
/// </remarks>
internal ParameterEarlyWellKnownAttributeData GetEarlyDecodedWellKnownAttributeData(DiagnosticBag diagnosticsOpt = null)
internal ParameterEarlyWellKnownAttributeData GetEarlyDecodedWellKnownAttributeData()
{
var attributesBag = _lazyCustomAttributesBag;
if (attributesBag == null || !attributesBag.IsEarlyDecodedWellKnownAttributeDataComputed)
{
attributesBag = this.GetAttributesBag(diagnosticsOpt);
attributesBag = this.GetAttributesBag();
}
return (ParameterEarlyWellKnownAttributeData)attributesBag.EarlyDecodedWellKnownAttributeData;
......@@ -433,7 +362,7 @@ internal ParameterEarlyWellKnownAttributeData GetEarlyDecodedWellKnownAttributeD
/// <remarks>
/// Forces binding and decoding of attributes.
/// </remarks>
internal sealed override CustomAttributesBag<CSharpAttributeData> GetAttributesBag(DiagnosticBag diagnosticsOpt)
internal sealed override CustomAttributesBag<CSharpAttributeData> GetAttributesBag()
{
if (_lazyCustomAttributesBag == null || !_lazyCustomAttributesBag.IsSealed)
{
......@@ -445,13 +374,13 @@ internal sealed override CustomAttributesBag<CSharpAttributeData> GetAttributesB
bool bagCreatedOnThisThread;
if ((object)copyFrom != null)
{
var attributesBag = copyFrom.GetAttributesBag(diagnosticsOpt);
var attributesBag = copyFrom.GetAttributesBag();
bagCreatedOnThisThread = Interlocked.CompareExchange(ref _lazyCustomAttributesBag, attributesBag, null) == null;
}
else
{
var attributeSyntax = this.GetAttributeDeclarations();
bagCreatedOnThisThread = LoadAndValidateAttributes(attributeSyntax, ref _lazyCustomAttributesBag, addToDiagnostics: diagnosticsOpt, binderOpt: ParameterBinder);
bagCreatedOnThisThread = LoadAndValidateAttributes(attributeSyntax, ref _lazyCustomAttributesBag, binderOpt: ParameterBinderOpt);
}
if (bagCreatedOnThisThread)
......@@ -659,7 +588,7 @@ private void DecodeDefaultParameterValueAttribute(AttributeDescription descripti
/// </summary>
private void VerifyParamDefaultValueMatchesAttributeIfAny(ConstantValue value, CSharpSyntaxNode syntax, DiagnosticBag diagnostics)
{
var data = GetEarlyDecodedWellKnownAttributeData(diagnostics);
var data = GetEarlyDecodedWellKnownAttributeData();
if (data != null)
{
var attrValue = data.DefaultParameterValue;
......@@ -772,10 +701,9 @@ private ConstantValue DecodeDefaultParameterValueAttribute(CSharpAttributeData a
return ConstantValue.Create(arg.Value, constantValueDiscriminator);
}
private bool IsValidCallerInfoContext(AttributeSyntax node)
{
return !ContainingSymbol.IsExplicitInterfaceImplementation() && !ContainingSymbol.IsOperator() && !IsOnPartialImplementation(node);
}
private bool IsValidCallerInfoContext(AttributeSyntax node) => !ContainingSymbol.IsExplicitInterfaceImplementation()
&& !ContainingSymbol.IsOperator()
&& !IsOnPartialImplementation(node);
/// <summary>
/// Is the attribute syntax appearing on a parameter of a partial method implementation part?
......@@ -988,69 +916,31 @@ internal override bool IsMetadataOptional
}
}
internal sealed override bool IsMetadataIn
{
get
{
var data = GetDecodedWellKnownAttributeData();
return data != null && data.HasInAttribute;
}
}
internal sealed override bool IsMetadataIn => GetDecodedWellKnownAttributeData()?.HasInAttribute == true;
internal sealed override bool IsMetadataOut
{
get
{
if (this.RefKind == Microsoft.CodeAnalysis.RefKind.Out)
if (this.RefKind == RefKind.Out)
{
return true;
}
var data = GetDecodedWellKnownAttributeData();
return data != null && data.HasOutAttribute;
return GetDecodedWellKnownAttributeData()?.HasOutAttribute == true;
}
}
internal sealed override MarshalPseudoCustomAttributeData MarshallingInformation
{
get
{
var data = GetDecodedWellKnownAttributeData();
return data != null ? data.MarshallingInformation : null;
}
}
=> GetDecodedWellKnownAttributeData()?.MarshallingInformation;
public override bool IsParams
{
get
{
return (_parameterSyntaxKind & ParameterSyntaxKind.ParamsParameter) != 0;
}
}
public override bool IsParams => (_parameterSyntaxKind & ParameterSyntaxKind.ParamsParameter) != 0;
internal override bool IsExtensionMethodThis
{
get
{
return (_parameterSyntaxKind & ParameterSyntaxKind.ExtensionThisParameter) != 0;
}
}
internal override bool IsExtensionMethodThis => (_parameterSyntaxKind & ParameterSyntaxKind.ExtensionThisParameter) != 0;
public override ImmutableArray<CustomModifier> CustomModifiers
{
get
{
return ImmutableArray<CustomModifier>.Empty;
}
}
public override ImmutableArray<CustomModifier> CustomModifiers => ImmutableArray<CustomModifier>.Empty;
public override ImmutableArray<CustomModifier> RefCustomModifiers
{
get
{
return ImmutableArray<CustomModifier>.Empty;
}
}
public override ImmutableArray<CustomModifier> RefCustomModifiers => ImmutableArray<CustomModifier>.Empty;
internal override void ForceComplete(SourceLocation locationOpt, CancellationToken cancellationToken)
{
......@@ -1089,20 +979,8 @@ internal sealed class SourceComplexParameterSymbolWithCustomModifiers : SourceCo
Debug.Assert(refKind != RefKind.None || _refCustomModifiers.IsEmpty);
}
public override ImmutableArray<CustomModifier> CustomModifiers
{
get
{
return _customModifiers;
}
}
public override ImmutableArray<CustomModifier> CustomModifiers => _customModifiers;
public override ImmutableArray<CustomModifier> RefCustomModifiers
{
get
{
return _refCustomModifiers;
}
}
public override ImmutableArray<CustomModifier> RefCustomModifiers => _refCustomModifiers;
}
}
......@@ -80,8 +80,11 @@ protected override void MethodChecks(DiagnosticBag diagnostics)
SyntaxToken arglistToken;
_lazyParameters = ParameterHelpers.MakeParameters(
bodyBinder, this, parameterList, out arglistToken, diagnostics,
allowRefOrOut: true, allowThis: false, beStrict: false);
bodyBinder, this, parameterList, out arglistToken,
allowRefOrOut: true,
allowThis: false,
diagnostics: diagnostics);
_lazyIsVararg = (arglistToken.Kind() == SyntaxKind.ArgListKeyword);
_lazyReturnType = bodyBinder.GetSpecialType(SpecialType.System_Void, diagnostics, syntax);
......
......@@ -246,8 +246,11 @@ private sealed class InvokeMethod : SourceDelegateMethodSymbol
SyntaxToken arglistToken;
var parameters = ParameterHelpers.MakeParameters(
binder, this, syntax.ParameterList, out arglistToken, diagnostics,
allowRefOrOut: true, allowThis: false, beStrict: false);
binder, this, syntax.ParameterList, out arglistToken,
allowRefOrOut: true,
allowThis: false,
diagnostics: diagnostics);
if (arglistToken.Kind() == SyntaxKind.ArgListKeyword)
{
// This is a parse-time error in the native compiler; it is a semantic analysis error in Roslyn.
......
......@@ -161,8 +161,11 @@ private void MethodChecks(MethodDeclarationSyntax syntax, Binder withTypeParamsB
var signatureBinder = withTypeParamsBinder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.SuppressConstraintChecks, this);
_lazyParameters = ParameterHelpers.MakeParameters(
signatureBinder, this, syntax.ParameterList, out arglistToken, diagnostics,
allowRefOrOut: true, allowThis: true, beStrict: false);
signatureBinder, this, syntax.ParameterList, out arglistToken,
allowRefOrOut: true,
allowThis: true,
diagnostics: diagnostics);
_lazyIsVararg = (arglistToken.Kind() == SyntaxKind.ArgListKeyword);
RefKind refKind;
var returnTypeSyntax = syntax.ReturnType.SkipRef(out refKind);
......
......@@ -925,15 +925,22 @@ private CustomAttributesBag<CSharpAttributeData> GetAttributesBag(ref CustomAttr
}
else if (forReturnType)
{
bagCreatedOnThisThread = LoadAndValidateAttributes(this.GetReturnTypeAttributeDeclarations(), ref lazyCustomAttributesBag, symbolPart: AttributeLocation.Return);
bagCreatedOnThisThread = LoadAndValidateAttributes(
this.GetReturnTypeAttributeDeclarations(),
ref lazyCustomAttributesBag,
symbolPart: AttributeLocation.Return);
}
else
{
bagCreatedOnThisThread = LoadAndValidateAttributes(this.GetAttributeDeclarations(), ref lazyCustomAttributesBag);
}
var part = forReturnType ? CompletionPart.ReturnTypeAttributes : CompletionPart.Attributes;
state.NotePartComplete(part);
if (bagCreatedOnThisThread)
{
var part = forReturnType ? CompletionPart.ReturnTypeAttributes : CompletionPart.Attributes;
state.NotePartComplete(part);
}
return lazyCustomAttributesBag;
}
......
......@@ -3,11 +3,7 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
......@@ -36,8 +32,7 @@ internal abstract class SourceParameterSymbol : SourceParameterSymbolBase
int ordinal,
bool isParams,
bool isExtensionMethodThis,
DiagnosticBag diagnostics,
bool beStrict)
DiagnosticBag declarationDiagnostics)
{
var name = identifier.ValueText;
var locations = ImmutableArray.Create<Location>(new SourceLocation(identifier));
......@@ -47,7 +42,7 @@ internal abstract class SourceParameterSymbol : SourceParameterSymbolBase
// touch the constructor in order to generate proper use-site diagnostics
Binder.ReportUseSiteDiagnosticForSynthesizedAttribute(context.Compilation,
WellKnownMember.System_ParamArrayAttribute__ctor,
diagnostics,
declarationDiagnostics,
identifier.Parent.GetLocation());
}
......@@ -60,23 +55,6 @@ internal abstract class SourceParameterSymbol : SourceParameterSymbolBase
return new SourceSimpleParameterSymbol(owner, parameterType, ordinal, refKind, name, locations);
}
if (beStrict)
{
return new SourceStrictComplexParameterSymbol(
diagnostics,
context,
owner,
ordinal,
parameterType,
refKind,
name,
locations,
syntax.GetReference(),
ConstantValue.Unset,
isParams,
isExtensionMethodThis);
}
return new SourceComplexParameterSymbol(
owner,
ordinal,
......@@ -130,6 +108,9 @@ internal SourceParameterSymbol WithCustomModifiersAndParamsCore(TypeSymbol newTy
this.IsExtensionMethodThis);
}
// Local functions should never have custom modifiers
Debug.Assert(!(ContainingSymbol is LocalFunctionSymbol));
return new SourceComplexParameterSymbolWithCustomModifiers(
this.ContainingSymbol,
this.Ordinal,
......@@ -172,21 +153,26 @@ internal override void ForceComplete(SourceLocation locationOpt, CancellationTok
internal abstract SyntaxList<AttributeListSyntax> AttributeDeclarationList { get; }
internal abstract CustomAttributesBag<CSharpAttributeData> GetAttributesBag(DiagnosticBag diagnosticsOpt);
internal abstract CustomAttributesBag<CSharpAttributeData> GetAttributesBag();
/// <summary>
/// Gets the attributes applied on this symbol.
/// Returns an empty array if there are no attributes.
/// </summary>
/// <remarks>
/// NOTE: This method should always be kept as a sealed override.
/// If you want to override attribute binding logic for a sub-class, then override <see cref="GetAttributesBag"/> method.
/// </remarks>
public sealed override ImmutableArray<CSharpAttributeData> GetAttributes()
{
return this.GetAttributesBag(null).Attributes;
return this.GetAttributesBag().Attributes;
}
/// <summary>
/// The declaration diagnostics for a parameter depend on the containing symbol.
/// For instance, if the containing symbol is a method the declaration diagnostics
/// go on the compilation, but if it is a local function it is part of the local
/// function's declaration diagnostics.
/// </summary>
internal override void AddDeclarationDiagnostics(DiagnosticBag diagnostics)
=> ContainingSymbol.AddDeclarationDiagnostics(diagnostics);
internal abstract SyntaxReference SyntaxReference { get; }
internal abstract bool IsExtensionMethodThis { get; }
......
......@@ -775,8 +775,10 @@ private static ImmutableArray<ParameterSymbol> MakeParameters(Binder binder, Sou
SyntaxToken arglistToken;
var parameters = ParameterHelpers.MakeParameters(
binder, owner, parameterSyntaxOpt, out arglistToken, diagnostics,
allowRefOrOut: false, allowThis: false, beStrict: false);
binder, owner, parameterSyntaxOpt, out arglistToken,
allowRefOrOut: false,
allowThis: false,
diagnostics: diagnostics);
if (arglistToken.Kind() != SyntaxKind.None)
{
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
......@@ -118,7 +112,7 @@ internal override SyntaxList<AttributeListSyntax> AttributeDeclarationList
get { return default(SyntaxList<AttributeListSyntax>); }
}
internal override CustomAttributesBag<CSharpAttributeData> GetAttributesBag(DiagnosticBag diagnosticsOpt)
internal override CustomAttributesBag<CSharpAttributeData> GetAttributesBag()
{
state.NotePartComplete(CompletionPart.Attributes);
return CustomAttributesBag<CSharpAttributeData>.Empty;
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
internal sealed class SourceStrictComplexParameterSymbol : SourceComplexParameterSymbol
{
private readonly Binder _tempBinder;
internal SourceStrictComplexParameterSymbol(
DiagnosticBag diagnostics,
Binder binder,
Symbol owner,
int ordinal,
TypeSymbol parameterType,
RefKind refKind,
string name,
ImmutableArray<Location> locations,
SyntaxReference syntaxRef,
ConstantValue defaultSyntaxValue,
bool isParams,
bool isExtensionMethodThis)
: base(
owner: owner,
ordinal: ordinal,
parameterType: parameterType,
refKind: refKind,
name: name,
locations: locations,
syntaxRef: syntaxRef,
defaultSyntaxValue: defaultSyntaxValue,
isParams: isParams,
isExtensionMethodThis: isExtensionMethodThis)
{
_tempBinder = binder;
var unused = GetAttributesBag(diagnostics);
_lazyDefaultSyntaxValue = MakeDefaultExpression(diagnostics, binder);
_tempBinder = null; // no need to keep it around anymore, just uses up a lot of memory
}
protected override Binder ParameterBinder => _tempBinder;
}
}
......@@ -3,11 +3,10 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
......@@ -159,7 +158,7 @@ AttributeLocation IAttributeTargetSymbol.AllowedAttributeLocations
/// </remarks>
public sealed override ImmutableArray<CSharpAttributeData> GetAttributes()
{
return this.GetAttributesBag(null).Attributes;
return this.GetAttributesBag().Attributes;
}
/// <summary>
......@@ -168,7 +167,7 @@ public sealed override ImmutableArray<CSharpAttributeData> GetAttributes()
/// <remarks>
/// Forces binding and decoding of attributes.
/// </remarks>
internal virtual CustomAttributesBag<CSharpAttributeData> GetAttributesBag(DiagnosticBag diagnosticsOpt)
internal virtual CustomAttributesBag<CSharpAttributeData> GetAttributesBag()
{
if (_lazyCustomAttributesBag == null || !_lazyCustomAttributesBag.IsSealed)
{
......@@ -179,13 +178,12 @@ internal virtual CustomAttributesBag<CSharpAttributeData> GetAttributesBag(Diagn
{
lazyAttributesStored = LoadAndValidateAttributes(
OneOrMany.Create(this.MergedAttributeDeclarationSyntaxLists),
ref _lazyCustomAttributesBag,
addToDiagnostics: diagnosticsOpt);
ref _lazyCustomAttributesBag);
}
else
{
var typeParameter = (SourceTypeParameterSymbolBase)sourceMethod.SourcePartialDefinition.TypeParameters[_ordinal];
CustomAttributesBag<CSharpAttributeData> attributesBag = typeParameter.GetAttributesBag(diagnosticsOpt);
CustomAttributesBag<CSharpAttributeData> attributesBag = typeParameter.GetAttributesBag();
lazyAttributesStored = Interlocked.CompareExchange(ref _lazyCustomAttributesBag, attributesBag, null) == null;
}
......@@ -448,60 +446,39 @@ private TypeParameterConstraintKind GetDeclaredConstraints()
}
}
// TODO: https://github.com/dotnet/roslyn/issues/17244 This type is probably not necessary
internal sealed class LocalFunctionTypeParameterSymbol : SourceTypeParameterSymbolBase
{
private readonly LocalFunctionSymbol _owner;
public LocalFunctionTypeParameterSymbol(LocalFunctionSymbol owner, string name, int ordinal, ImmutableArray<Location> locations, ImmutableArray<SyntaxReference> syntaxRefs)
public LocalFunctionTypeParameterSymbol(
LocalFunctionSymbol owner,
string name,
int ordinal,
ImmutableArray<Location> locations,
ImmutableArray<SyntaxReference> syntaxRefs)
: base(name, ordinal, locations, syntaxRefs)
{
_owner = owner;
}
public override TypeParameterKind TypeParameterKind
{
get
{
return TypeParameterKind.Method;
}
}
internal override void AddDeclarationDiagnostics(DiagnosticBag diagnostics)
=> _owner.AddDeclarationDiagnostics(diagnostics);
public override Symbol ContainingSymbol
{
get { return _owner; }
}
public override TypeParameterKind TypeParameterKind => TypeParameterKind.Method;
public override Symbol ContainingSymbol => _owner;
public override bool HasConstructorConstraint
{
get
{
var constraints = this.GetDeclaredConstraints();
return (constraints & TypeParameterConstraintKind.Constructor) != 0;
}
}
=> (GetDeclaredConstraints() & TypeParameterConstraintKind.Constructor) != 0;
public override bool HasValueTypeConstraint
{
get
{
var constraints = this.GetDeclaredConstraints();
return (constraints & TypeParameterConstraintKind.ValueType) != 0;
}
}
=> (GetDeclaredConstraints() & TypeParameterConstraintKind.ValueType) != 0;
public override bool HasReferenceTypeConstraint
{
get
{
var constraints = this.GetDeclaredConstraints();
return (constraints & TypeParameterConstraintKind.ReferenceType) != 0;
}
}
=> (GetDeclaredConstraints() & TypeParameterConstraintKind.ReferenceType) != 0;
protected override ImmutableArray<TypeParameterSymbol> ContainerTypeParameters
{
get { return _owner.TypeParameters; }
}
protected override ImmutableArray<TypeParameterSymbol> ContainerTypeParameters => _owner.TypeParameters;
protected override TypeParameterBounds ResolveBounds(ConsList<TypeParameterSymbol> inProgress, DiagnosticBag diagnostics)
{
......@@ -509,10 +486,7 @@ protected override TypeParameterBounds ResolveBounds(ConsList<TypeParameterSymbo
return this.ResolveBounds(this.ContainingAssembly.CorLibrary, inProgress.Prepend(this), constraintTypes, false, this.DeclaringCompilation, diagnostics);
}
private TypeParameterConstraintKind GetDeclaredConstraints()
{
return _owner.GetTypeParameterConstraints(this.Ordinal);
}
private TypeParameterConstraintKind GetDeclaredConstraints() => _owner.GetTypeParameterConstraints(Ordinal);
}
/// <summary>
......
......@@ -129,10 +129,9 @@ protected override void MethodChecks(DiagnosticBag diagnostics)
this,
ParameterListSyntax,
out arglistToken,
diagnostics,
allowRefOrOut: true,
allowThis: false,
beStrict: false);
diagnostics: diagnostics);
if (arglistToken.Kind() == SyntaxKind.ArgListKeyword)
{
......
......@@ -753,7 +753,7 @@ internal string GetDebuggerDisplay()
return $"{this.Kind} {this.ToDisplayString(SymbolDisplayFormat.TestFormat)}";
}
internal void AddDeclarationDiagnostics(DiagnosticBag diagnostics)
internal virtual void AddDeclarationDiagnostics(DiagnosticBag diagnostics)
{
if (!diagnostics.IsEmptyWithoutResolution)
{
......
......@@ -260,7 +260,6 @@ internal virtual void PostDecodeWellKnownAttributes(ImmutableArray<CSharpAttribu
/// <param name="lazyCustomAttributesBag"></param>
/// <param name="symbolPart">Specific part of the symbol to which the attributes apply, or <see cref="AttributeLocation.None"/> if the attributes apply to the symbol itself.</param>
/// <param name="earlyDecodingOnly">Indicates that only early decoding should be performed. WARNING: the resulting bag will not be sealed.</param>
/// <param name="addToDiagnostics">Diagnostic bag to report into. If null, diagnostics will be reported into <see cref="AddDeclarationDiagnostics"/></param>
/// <param name="binderOpt">Binder to use. If null, <see cref="DeclaringCompilation"/> GetBinderFactory will be used.</param>
/// <returns>Flag indicating whether lazyCustomAttributes were stored on this thread. Caller should check for this flag and perform NotePartComplete if true.</returns>
internal bool LoadAndValidateAttributes(
......@@ -268,7 +267,6 @@ internal virtual void PostDecodeWellKnownAttributes(ImmutableArray<CSharpAttribu
ref CustomAttributesBag<CSharpAttributeData> lazyCustomAttributesBag,
AttributeLocation symbolPart = AttributeLocation.None,
bool earlyDecodingOnly = false,
DiagnosticBag addToDiagnostics = null,
Binder binderOpt = null)
{
var diagnostics = DiagnosticBag.GetInstance();
......@@ -353,14 +351,7 @@ internal virtual void PostDecodeWellKnownAttributes(ImmutableArray<CSharpAttribu
if (lazyCustomAttributesBag.SetAttributes(boundAttributes))
{
this.RecordPresenceOfBadAttributes(boundAttributes);
if (addToDiagnostics == null)
{
this.AddDeclarationDiagnostics(diagnostics);
}
else
{
addToDiagnostics.AddRange(diagnostics);
}
AddDeclarationDiagnostics(diagnostics);
lazyAttributesStoredOnThisThread = true;
if (lazyCustomAttributesBag.IsEmpty) lazyCustomAttributesBag = CustomAttributesBag<CSharpAttributeData>.Empty;
}
......
......@@ -2242,7 +2242,7 @@ static int B(int val)
}
static object D(object val)
{
T Local<T>(T valu) where T : object
T Local<T>(T valu) where T : class
{
return valu;
}
......
......@@ -30,7 +30,50 @@ internal void VerifyDiagnostics(string source, CSharpCompilationOptions options,
[CompilerTrait(CompilerFeature.LocalFunctions)]
public class LocalFunctionTests : LocalFunctionsTestBase
{
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/16451")]
[Fact]
[WorkItem(17014, "https://github.com/dotnet/roslyn/pull/17014")]
public void RecursiveLocalFuncsAsParameterTypes()
{
var comp = CreateCompilationWithMscorlib(@"
class C
{
void M()
{
int L(L2 l2) => 0;
int L2(L l1) => 0;
}
}");
comp.VerifyDiagnostics(
// (6,15): error CS0246: The type or namespace name 'L2' could not be found (are you missing a using directive or an assembly reference?)
// int L(L2 l2) => 0;
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "L2").WithArguments("L2").WithLocation(6, 15),
// (7,16): error CS0246: The type or namespace name 'L' could not be found (are you missing a using directive or an assembly reference?)
// int L2(L l1) => 0;
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "L").WithArguments("L").WithLocation(7, 16),
// (6,13): warning CS0168: The variable 'L' is declared but never used
// int L(L2 l2) => 0;
Diagnostic(ErrorCode.WRN_UnreferencedVar, "L").WithArguments("L").WithLocation(6, 13),
// (7,13): warning CS0168: The variable 'L2' is declared but never used
// int L2(L l1) => 0;
Diagnostic(ErrorCode.WRN_UnreferencedVar, "L2").WithArguments("L2").WithLocation(7, 13));
}
[Fact]
[WorkItem(16451, "https://github.com/dotnet/roslyn/issues/16451")]
public void BadGenericConstraint()
{
var comp = CreateCompilationWithMscorlib(@"
class C
{
public void M<T>(T value) where T : object { }
}");
comp.VerifyDiagnostics(
// (4,41): error CS0702: Constraint cannot be special class 'object'
// public void M<T>(T value) where T : object { }
Diagnostic(ErrorCode.ERR_SpecialTypeAsBound, "object").WithArguments("object").WithLocation(4, 41));
}
[Fact]
[WorkItem(16451, "https://github.com/dotnet/roslyn/issues/16451")]
public void RecursiveDefaultParameter()
{
......@@ -50,7 +93,7 @@ public static void Main()
comp.DeclarationDiagnostics.Verify();
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/16451")]
[Fact]
[WorkItem(16451, "https://github.com/dotnet/roslyn/issues/16451")]
public void RecursiveDefaultParameter2()
{
......@@ -71,7 +114,7 @@ void M()
comp.DeclarationDiagnostics.Verify();
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/16451")]
[Fact]
[WorkItem(16451, "https://github.com/dotnet/roslyn/issues/16451")]
public void MutuallyRecursiveDefaultParameters()
{
......@@ -378,8 +421,7 @@ public void M()
attrs[2].AttributeClass);
Assert.True(attrs[3].AttributeClass.IsErrorType());
// The following line of code should be uncommented once https://github.com/dotnet/roslyn/issues/17293 is fixed.
//comp.DeclarationDiagnostics.Verify();
comp.DeclarationDiagnostics.Verify();
}
[Fact]
......@@ -990,9 +1032,9 @@ void CallerMemberName([CallerMemberName] int s = 2)
Diagnostic(ErrorCode.ERR_AttributesInLocalFuncDecl, "[CallerMemberName]").WithLocation(9, 31),
// (9,32): error CS4019: CallerMemberNameAttribute cannot be applied because there are no standard conversions from type 'string' to type 'int'
// void CallerMemberName([CallerMemberName] int s = 2)
Diagnostic(ErrorCode.ERR_NoConversionForCallerMemberNameParam, "CallerMemberName").WithArguments("string", "int").WithLocation(9, 32)
);
Diagnostic(ErrorCode.ERR_NoConversionForCallerMemberNameParam, "CallerMemberName").WithArguments("string", "int").WithLocation(9, 32));
}
[WorkItem(10708, "https://github.com/dotnet/roslyn/issues/10708")]
[CompilerTrait(CompilerFeature.Dynamic, CompilerFeature.Params)]
[Fact]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册