From 66527db0a83b6ea05c8f7190b391d045381b476e Mon Sep 17 00:00:00 2001 From: Youssef1313 Date: Sat, 5 Mar 2022 20:33:06 +0200 Subject: [PATCH 1/8] Fix type parameter scoping for local functions --- .../Compilation/MemberSemanticModel.cs | 2 +- .../Symbols/Source/LocalFunctionSymbol.cs | 19 +- .../Semantic/Semantics/LocalFunctionTests.cs | 531 ++++++++++++++++++ 3 files changed, 542 insertions(+), 10 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs index 8d106bcd64aa9..bdab55a464d56 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs @@ -358,7 +358,7 @@ private static Binder GetEnclosingBinderInternalWithinRoot(SyntaxNode node, int LocalFunctionSymbol function = GetDeclaredLocalFunction(binder, ownerOfTypeParametersInScope.Identifier); if ((object)function != null) { - binder = function.SignatureBinder; + binder = function.ParameterBinder; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index 34d250feb9f6a..f56b67a19c10c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -14,7 +14,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { internal sealed class LocalFunctionSymbol : SourceMethodSymbolWithAttributes { - private readonly Binder _binder; + private readonly Binder _parameterBinder; + private readonly Binder? _withTypeParamsBinder; // null when no type parameters private readonly Symbol _containingSymbol; private readonly DeclarationModifiers _declarationModifiers; private readonly ImmutableArray _typeParameters; @@ -60,7 +61,7 @@ internal sealed class LocalFunctionSymbol : SourceMethodSymbolWithAttributes if (syntax.TypeParameterList != null) { - binder = new WithMethodTypeParametersBinder(this, binder); + _withTypeParamsBinder = new WithMethodTypeParametersBinder(this, binder); _typeParameters = MakeTypeParameters(_declarationDiagnostics); } else @@ -96,7 +97,7 @@ internal sealed class LocalFunctionSymbol : SourceMethodSymbolWithAttributes _refKind = RefKind.None; } - _binder = binder; + _parameterBinder = binder; } /// @@ -105,7 +106,7 @@ internal sealed class LocalFunctionSymbol : SourceMethodSymbolWithAttributes /// internal Binder ScopeBinder { get; } - internal override Binder ParameterBinder => _binder; + internal override Binder ParameterBinder => _withTypeParamsBinder ?? _parameterBinder; internal LocalFunctionStatementSyntax Syntax => (LocalFunctionStatementSyntax)syntaxReferenceOpt.GetSyntax(); @@ -180,7 +181,7 @@ private void ComputeParameters() var diagnostics = BindingDiagnosticBag.GetInstance(_declarationDiagnostics); var parameters = ParameterHelpers.MakeParameters( - _binder, + ParameterBinder, this, this.Syntax.ParameterList, arglistToken: out arglistToken, @@ -239,7 +240,7 @@ internal void ComputeReturnType() var diagnostics = BindingDiagnosticBag.GetInstance(_declarationDiagnostics); TypeSyntax returnTypeSyntax = Syntax.ReturnType; - TypeWithAnnotations returnType = _binder.BindType(returnTypeSyntax.SkipRef(), diagnostics); + TypeWithAnnotations returnType = ParameterBinder.BindType(returnTypeSyntax.SkipRef(), diagnostics); var compilation = DeclaringCompilation; @@ -334,7 +335,7 @@ internal override TypeWithAnnotations IteratorElementTypeWithAnnotations public SyntaxToken NameToken => Syntax.Identifier; - internal override Binder SignatureBinder => _binder; + internal override Binder SignatureBinder => _parameterBinder; public override ImmutableArray ExplicitInterfaceImplementations => ImmutableArray.Empty; @@ -476,7 +477,7 @@ public override ImmutableArray> GetTypeParam var syntax = Syntax; var diagnostics = BindingDiagnosticBag.GetInstance(_declarationDiagnostics); var constraints = this.MakeTypeParameterConstraintTypes( - _binder, + ParameterBinder, TypeParameters, syntax.TypeParameterList, syntax.ConstraintClauses, @@ -501,7 +502,7 @@ public override ImmutableArray GetTypeParameterCons { var syntax = Syntax; var constraints = this.MakeTypeParameterConstraintKinds( - _binder, + ParameterBinder, TypeParameters, syntax.TypeParameterList, syntax.ConstraintClauses); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs index 1367310b17dfa..31c91fa03a04e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs @@ -7117,5 +7117,536 @@ async void F4() // async static void B4() { } Diagnostic(ErrorCode.WRN_LowerCaseTypeName, "await").WithArguments("await").WithLocation(23, 30)); } + + [Fact, WorkItem(59775, "https://github.com/dotnet/roslyn/issues/59775")] + public void TypeParameterScope_NotInAttributeNameOf() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(); + + [My(nameof(TParameter))] // 1 + void local() { } + } + + [My(nameof(TParameter))] // 2 + void M2() { } +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics( + // (8,20): error CS0103: The name 'TParameter' does not exist in the current context + // [My(nameof(TParameter))] // 1 + Diagnostic(ErrorCode.ERR_NameNotInContext, "TParameter").WithArguments("TParameter").WithLocation(8, 20), + // (12,16): error CS0103: The name 'TParameter' does not exist in the current context + // [My(nameof(TParameter))] // 2 + Diagnostic(ErrorCode.ERR_NameNotInContext, "TParameter").WithArguments("TParameter").WithLocation(12, 16) + ); + } + + [Fact] + public void TypeParameterScope_NotInAttribute() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(); + + [My(TParameter)] // 1 + void local() { } + } + + [My(TParameter)] // 2 + void M2() { } +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(object o) { } +} +"); + comp.VerifyDiagnostics( + // (8,13): error CS0103: The name 'TParameter' does not exist in the current context + // [My(TParameter)] // 1 + Diagnostic(ErrorCode.ERR_NameNotInContext, "TParameter").WithArguments("TParameter").WithLocation(8, 13), + // (12,9): error CS0103: The name 'TParameter' does not exist in the current context + // [My(TParameter)] // 2 + Diagnostic(ErrorCode.ERR_NameNotInContext, "TParameter").WithArguments("TParameter").WithLocation(12, 9) + ); + } + + [Fact] + public void TypeParameterScope_NotInAttributeTypeArgument() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(); + + [My] // 1 + void local() { } + } + + [My] // 2 + void M2() { } +} + +public class MyAttribute : System.Attribute +{ +} +"); + comp.VerifyDiagnostics( + // (8,13): error CS0246: The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?) + // [My] // 1 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "T").WithArguments("T").WithLocation(8, 13), + // (12,9): error CS0246: The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?) + // [My] // 2 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "T").WithArguments("T").WithLocation(12, 9) + ); + } + + [Fact] + public void TypeParameterScope_NotAsAttributeType() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(); + + [T] // 1 + void local() where T : System.Attribute { } + } + + [T] // 2 + void M2() where T : System.Attribute { } +} +"); + comp.VerifyDiagnostics( + // (8,10): error CS0246: The type or namespace name 'TAttribute' could not be found (are you missing a using directive or an assembly reference?) + // [T] // 1 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "T").WithArguments("TAttribute").WithLocation(8, 10), + // (8,10): error CS0246: The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?) + // [T] // 1 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "T").WithArguments("T").WithLocation(8, 10), + // (12,6): error CS0246: The type or namespace name 'TAttribute' could not be found (are you missing a using directive or an assembly reference?) + // [T] // 2 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "T").WithArguments("TAttribute").WithLocation(12, 6), + // (12,6): error CS0246: The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?) + // [T] // 2 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "T").WithArguments("T").WithLocation(12, 6) + ); + } + + [Fact, WorkItem(60110, "https://github.com/dotnet/roslyn/issues/60110")] + public void TypeParameterScope_InParameterAttribute() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(0); + + void local([My(TParameter)] int i) => throw null; + } + + void M2([My(TParameter)] int i) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + // TParameter unexpectedly was found in local function case + // Tracked by https://github.com/dotnet/roslyn/issues/60110 + comp.VerifyDiagnostics( + // (8,36): error CS0119: 'TParameter' is a type, which is not valid in the given context + // void local([My(TParameter)] int i) => throw null; + Diagnostic(ErrorCode.ERR_BadSKunknown, "TParameter").WithArguments("TParameter", "type").WithLocation(8, 36), + // (11,29): error CS0103: The name 'TParameter' does not exist in the current context + // void M2([My(TParameter)] int i) => throw null; + Diagnostic(ErrorCode.ERR_NameNotInContext, "TParameter").WithArguments("TParameter").WithLocation(11, 29) + ); + } + + [Fact] + public void TypeParameterScope_InParameterAttributeNameOf() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(0); + + void local([My(nameof(TParameter))] int i) => throw null; + } + + void M2([My(nameof(TParameter))] int i) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics(); + } + + [Fact] + public void TypeParameterScope_AsParameterAttributeType() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(0); + + void local([TParameter] int i) where TParameter : System.Attribute => throw null; + } + + void M2([TParameter] int i) where TParameter : System.Attribute => throw null; +} +"); + comp.VerifyDiagnostics( + // (8,33): error CS0616: 'TParameter' is not an attribute class + // void local([TParameter] int i) where TParameter : System.Attribute => throw null; + Diagnostic(ErrorCode.ERR_NotAnAttributeClass, "TParameter").WithArguments("TParameter").WithLocation(8, 33), + // (11,26): error CS0616: 'TParameter' is not an attribute class + // void M2([TParameter] int i) where TParameter : System.Attribute => throw null; + Diagnostic(ErrorCode.ERR_NotAnAttributeClass, "TParameter").WithArguments("TParameter").WithLocation(11, 26) + ); + } + + [Fact] + public void TypeParameterScope_InReturnType() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(); + + TParameter local() => throw null; + } + + TParameter M2() => throw null; +} +"); + comp.VerifyDiagnostics(); + } + + [Fact] + public void TypeParameterScope_InParameterType() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(null); + + void local(TParameter p) => throw null; + } + + void M2(TParameter p) => throw null; +} +"); + comp.VerifyDiagnostics(); + } + + [Fact] + public void ParameterScope_NotInAttributeNameOf() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(0); + + [My(nameof(parameter))] // 1 + void local(int parameter) { } + } + + [My(nameof(parameter))] // 2 + void M2(int parameter) { } +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics( + // (8,20): error CS0103: The name 'parameter' does not exist in the current context + // [My(nameof(parameter))] // 1 + Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(8, 20), + // (12,16): error CS0103: The name 'parameter' does not exist in the current context + // [My(nameof(parameter))] // 2 + Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(12, 16) + ); + } + + [Fact] + public void ParameterScope_NotInAttribute() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(0); + + [My(parameter)] // 1 + void local(int parameter) { } + } + + [My(parameter)] // 2 + void M2(int parameter) { } +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(object o) { } +} +"); + comp.VerifyDiagnostics( + // (8,13): error CS0103: The name 'parameter' does not exist in the current context + // [My(parameter)] // 1 + Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(8, 13), + // (12,9): error CS0103: The name 'parameter' does not exist in the current context + // [My(parameter)] // 2 + Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(12, 9) + ); + } + + [Fact] + public void ParameterScope_NotInAttributeTypeArgument() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(0); + + [My] // 1 + void local(int parameter) { } + } + + [My] // 2 + void M2(int parameter) { } +} + +public class MyAttribute : System.Attribute +{ +} +"); + comp.VerifyDiagnostics( + // (8,13): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // [My] // 1 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(8, 13), + // (12,9): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // [My] // 2 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(12, 9) + ); + } + + [Fact] + public void ParameterScope_NotAsAttributeType() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(null); + + [parameter] // 1 + void local(System.Attribute parameter) { } + } + + [parameter] // 2 + void M2(System.Attribute parameter) { } +} +"); + comp.VerifyDiagnostics( + // (8,10): error CS0246: The type or namespace name 'parameterAttribute' could not be found (are you missing a using directive or an assembly reference?) + // [parameter] // 1 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameterAttribute").WithLocation(8, 10), + // (8,10): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // [parameter] // 1 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(8, 10), + // (12,6): error CS0246: The type or namespace name 'parameterAttribute' could not be found (are you missing a using directive or an assembly reference?) + // [parameter] // 2 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameterAttribute").WithLocation(12, 6), + // (12,6): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // [parameter] // 2 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(12, 6) + ); + } + + [Fact] + public void ParameterScope_NotInParameterAttribute() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(0); + + void local([My(parameter)] int parameter) => throw null; + } + + void M2([My(parameter)] int parameter) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics( + // (8,24): error CS0103: The name 'parameter' does not exist in the current context + // void local([My(parameter)] int parameter) => throw null; + Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(8, 24), + // (11,17): error CS0103: The name 'parameter' does not exist in the current context + // void M2([My(parameter)] int parameter) => throw null; + Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(11, 17) + ); + } + + [Fact] + public void ParameterScope_NotInParameterAttributeNameOf() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(0); + + void local([My(nameof(parameter))] int parameter) => throw null; + } + + void M2([My(nameof(parameter))] int parameter) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics( + // (8,31): error CS0103: The name 'parameter' does not exist in the current context + // void local([My(nameof(parameter))] int parameter) => throw null; + Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(8, 31), + // (11,24): error CS0103: The name 'parameter' does not exist in the current context + // void M2([My(nameof(parameter))] int parameter) => throw null; + Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(11, 24) + ); + } + + [Fact] + public void ParameterScope_NotAsParameterAttributeType() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(null); + + void local([parameter] System.Attribute parameter) => throw null; + } + + void M2([parameter] System.Attribute parameter) => throw null; +} +"); + comp.VerifyDiagnostics( + // (8,21): error CS0246: The type or namespace name 'parameterAttribute' could not be found (are you missing a using directive or an assembly reference?) + // void local([parameter] System.Attribute parameter) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameterAttribute").WithLocation(8, 21), + // (8,21): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // void local([parameter] System.Attribute parameter) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(8, 21), + // (11,14): error CS0246: The type or namespace name 'parameterAttribute' could not be found (are you missing a using directive or an assembly reference?) + // void M2([parameter] System.Attribute parameter) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameterAttribute").WithLocation(11, 14), + // (11,14): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // void M2([parameter] System.Attribute parameter) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(11, 14) + ); + } + + [Fact] + public void ParameterScope_NotInReturnType() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(0); + + parameter local(int parameter) => throw null; + } + + parameter M2(int parameter) => throw null; +} +"); + comp.VerifyDiagnostics( + // (8,9): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // parameter local(int parameter) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(8, 9), + // (11,5): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // parameter M2(int parameter) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(11, 5) + ); + } + + [Fact] + public void ParameterScope_NotInParameterType() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(null); + + void local(parameter parameter) => throw null; + } + + void M2(parameter parameter) => throw null; +} +"); + comp.VerifyDiagnostics( + // (8,20): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // void local(parameter parameter) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(8, 20), + // (11,25): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // void M2(parameter parameter) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(11, 25) + ); + } } } From 617e75a703b6708d5b679a126016fbf4a43c79af Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 14 Mar 2022 13:00:20 -0700 Subject: [PATCH 2/8] Fix parameter scoping on lambdas --- .../Portable/Symbols/Source/LambdaSymbol.cs | 2 +- .../Test/Semantic/Semantics/LambdaTests.cs | 265 ++++++++++++++++++ 2 files changed, 266 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs index 9e7a253c67905..eaa0d4e5fb2e5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs @@ -267,7 +267,7 @@ public override bool IsExtensionMethod internal override Binder SignatureBinder => _binder; - internal override Binder ParameterBinder => new WithLambdaParametersBinder(this, _binder); + internal override Binder ParameterBinder => _binder; internal override OneOrMany> GetAttributeDeclarations() { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 17855adb842ae..037cdfc8f0d8e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -6164,5 +6164,270 @@ class A : Attribute { } Diagnostic(ErrorCode.ERR_LambdaWithAttributesToExpressionTree, "[return: A] ([A] x) => x").WithLocation(5, 32) ); } + + [Fact] + public void ParameterScope_NotInAttributeNameOf() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + + var _ = + [My(nameof(parameter))] // 1 + void(int parameter) => { }; + } + + [My(nameof(parameter))] // 2 + void M2(int parameter) { } +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics( + // (8,24): error CS0103: The name 'parameter' does not exist in the current context + // [My(nameof(parameter))] // 1 + Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(8, 24), + // (12,16): error CS0103: The name 'parameter' does not exist in the current context + // [My(nameof(parameter))] // 2 + Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(12, 16) + ); + } + + [Fact] + public void ParameterScope_NotInAttribute() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + var _ = + [My(parameter)] // 1 + void (int parameter) => { }; + } + + [My(parameter)] // 2 + void M2(int parameter) { } +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(object o) { } +} +"); + comp.VerifyDiagnostics( + // (7,17): error CS0103: The name 'parameter' does not exist in the current context + // [My(parameter)] // 1 + Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(7, 17), + // (11,9): error CS0103: The name 'parameter' does not exist in the current context + // [My(parameter)] // 2 + Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(11, 9) + ); + } + + [Fact] + public void ParameterScope_NotInAttributeTypeArgument() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + var _ = + [My] // 1 + void (int parameter) => { }; + } + + [My] // 2 + void M2(int parameter) { } +} + +public class MyAttribute : System.Attribute +{ +} +"); + comp.VerifyDiagnostics( + // (7,17): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // [My] // 1 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(7, 17), + // (11,9): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // [My] // 2 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(11, 9) + ); + } + + [Fact] + public void ParameterScope_NotAsAttributeType() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + var _ = + [parameter] // 1 + void (System.Attribute parameter) => { }; + } + + [parameter] // 2 + void M2(System.Attribute parameter) { } +} +"); + comp.VerifyDiagnostics( + // (7,14): error CS0246: The type or namespace name 'parameterAttribute' could not be found (are you missing a using directive or an assembly reference?) + // [parameter] // 1 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameterAttribute").WithLocation(7, 14), + // (7,14): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // [parameter] // 1 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(7, 14), + // (11,6): error CS0246: The type or namespace name 'parameterAttribute' could not be found (are you missing a using directive or an assembly reference?) + // [parameter] // 2 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameterAttribute").WithLocation(11, 6), + // (11,6): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // [parameter] // 2 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(11, 6) + ); + } + + [Fact] + public void ParameterScope_NotInParameterAttribute() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + var _ = void ([My(parameter)] int parameter) => throw null; + } + + void M2([My(parameter)] int parameter) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics( + // (6,27): error CS0103: The name 'parameter' does not exist in the current context + // var _ = void ([My(parameter)] int parameter) => throw null; + Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(6, 27), + // (9,17): error CS0103: The name 'parameter' does not exist in the current context + // void M2([My(parameter)] int parameter) => throw null; + Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(9, 17) + ); + } + + [Fact] + public void ParameterScope_NotInParameterAttributeNameOf() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + var _ = void ([My(nameof(parameter))] int parameter) => throw null; + } + + void M2([My(nameof(parameter))] int parameter) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics( + // (6,34): error CS0103: The name 'parameter' does not exist in the current context + // var _ = void ([My(nameof(parameter))] int parameter) => throw null; + Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(6, 34), + // (9,24): error CS0103: The name 'parameter' does not exist in the current context + // void M2([My(nameof(parameter))] int parameter) => throw null; + Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(9, 24) + ); + } + + [Fact] + public void ParameterScope_NotAsParameterAttributeType() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + var _ = void ([parameter] System.Attribute parameter) => throw null; + } + + void M2([parameter] System.Attribute parameter) => throw null; +} +"); + comp.VerifyDiagnostics( + // (6,24): error CS0246: The type or namespace name 'parameterAttribute' could not be found (are you missing a using directive or an assembly reference?) + // var _ = void ([parameter] System.Attribute parameter) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameterAttribute").WithLocation(6, 24), + // (6,24): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // var _ = void ([parameter] System.Attribute parameter) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(6, 24), + // (9,14): error CS0246: The type or namespace name 'parameterAttribute' could not be found (are you missing a using directive or an assembly reference?) + // void M2([parameter] System.Attribute parameter) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameterAttribute").WithLocation(9, 14), + // (9,14): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // void M2([parameter] System.Attribute parameter) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(9, 14) + ); + } + + [Fact] + public void ParameterScope_NotInReturnType() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + var _ = parameter(int parameter) => throw null; + } + + parameter M2(int parameter) => throw null; +} +"); + comp.VerifyDiagnostics( + // (6,17): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // var _ = parameter(int parameter) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(6, 17), + // (9,5): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // parameter M2(int parameter) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(9, 5) + ); + } + + [Fact] + public void ParameterScope_NotInParameterType() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + var _ = void (parameter parameter) => throw null; + } + + void M2(parameter parameter) => throw null; +} +"); + comp.VerifyDiagnostics( + // (6,23): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // var _ = void (parameter parameter) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(6, 23), + // (9,25): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // void M2(parameter parameter) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(9, 25) + ); + } } } From 6ad91b0d81180bab05ffdd0833c0b13f16262669 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 14 Mar 2022 17:27:45 -0700 Subject: [PATCH 3/8] Address feedback --- .../Binder/Binder.QueryUnboundLambdaState.cs | 2 +- .../Portable/BoundTree/UnboundLambda.cs | 8 +- .../Compilation/MemberSemanticModel.cs | 2 +- .../Portable/Symbols/ConstraintsHelper.cs | 8 +- .../Portable/Symbols/Source/LambdaSymbol.cs | 4 +- .../Symbols/Source/LocalFunctionSymbol.cs | 21 +- .../Symbols/Source/ParameterHelpers.cs | 14 +- .../Source/SourceComplexParameterSymbol.cs | 8 +- .../SourceMethodSymbolWithAttributes.cs | 6 +- .../Source/SourceTypeParameterSymbol.cs | 2 +- .../Test/Semantic/Semantics/LambdaTests.cs | 12 +- .../Semantic/Semantics/LocalFunctionTests.cs | 571 +++++++++++++++++- 12 files changed, 603 insertions(+), 55 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs b/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs index 4f5f4e34a4531..a7d1438a212c1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs @@ -58,7 +58,7 @@ public override void GenerateAnonymousFunctionConversionError(BindingDiagnosticB base.GenerateAnonymousFunctionConversionError(diagnostics, targetType); } - public override Binder ParameterBinder(LambdaSymbol lambdaSymbol, Binder binder) + public override Binder GetWithParametersBinder(LambdaSymbol lambdaSymbol, Binder binder) { return new WithQueryLambdaParametersBinder(lambdaSymbol, _rangeVariableMap, binder); } diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index fce66b2e44abf..ec5232daa82c8 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -631,7 +631,7 @@ private static TypeWithAnnotations DelegateReturnTypeWithAnnotations(MethodSymbo parameterRefKinds, refKind: default, returnType: default); - var lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, ParameterBinder(lambdaSymbol, Binder)); + var lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, GetWithParametersBinder(lambdaSymbol, Binder)); var block = BindLambdaBody(lambdaSymbol, lambdaBodyBinder, BindingDiagnosticBag.Discarded); var returnTypes = ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)>.GetInstance(); BoundLambda.BlockReturns.GetReturnTypes(returnTypes, block); @@ -704,7 +704,7 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType, bool inExpressionTr else { lambdaSymbol = CreateLambdaSymbol(Binder.ContainingMemberOrLambda, returnType, cacheKey.ParameterTypes, cacheKey.ParameterRefKinds, refKind); - lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, ParameterBinder(lambdaSymbol, Binder), inExpressionTree ? BinderFlags.InExpressionTree : BinderFlags.None); + lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, GetWithParametersBinder(lambdaSymbol, Binder), inExpressionTree ? BinderFlags.InExpressionTree : BinderFlags.None); block = BindLambdaBody(lambdaSymbol, lambdaBodyBinder, diagnostics); } @@ -890,7 +890,7 @@ private void ValidateUnsafeParameters(BindingDiagnosticBag diagnostics, Immutabl parameterTypes, parameterRefKinds, refKind); - var lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, ParameterBinder(lambdaSymbol, Binder)); + var lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, GetWithParametersBinder(lambdaSymbol, Binder)); var block = BindLambdaBody(lambdaSymbol, lambdaBodyBinder, diagnostics); lambdaSymbol.GetDeclarationDiagnostics(diagnostics); return (lambdaSymbol, block, lambdaBodyBinder, diagnostics); @@ -1024,7 +1024,7 @@ public static ReturnInferenceCacheKey Create(NamedTypeSymbol? delegateType, bool } } - public virtual Binder ParameterBinder(LambdaSymbol lambdaSymbol, Binder binder) + public virtual Binder GetWithParametersBinder(LambdaSymbol lambdaSymbol, Binder binder) { return new WithLambdaParametersBinder(lambdaSymbol, binder); } diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs index bdab55a464d56..983f2e40714db 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs @@ -358,7 +358,7 @@ private static Binder GetEnclosingBinderInternalWithinRoot(SyntaxNode node, int LocalFunctionSymbol function = GetDeclaredLocalFunction(binder, ownerOfTypeParametersInScope.Identifier); if ((object)function != null) { - binder = function.ParameterBinder; + binder = function.WithTypeParametersBinder; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs index 3f4d5439a0f92..598316bb71ec9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs @@ -314,7 +314,7 @@ internal static class ConstraintsHelper internal static ImmutableArray> MakeTypeParameterConstraintTypes( this MethodSymbol containingSymbol, - Binder binder, + Binder withTypeParametersBinder, ImmutableArray typeParameters, TypeParameterListSyntax typeParameterList, SyntaxList constraintClauses, @@ -327,11 +327,11 @@ internal static class ConstraintsHelper // Wrap binder from factory in a generic constraints specific binder // to avoid checking constraints when binding type names. - Debug.Assert(!binder.Flags.Includes(BinderFlags.GenericConstraintsClause)); - binder = binder.WithAdditionalFlags(BinderFlags.GenericConstraintsClause | BinderFlags.SuppressConstraintChecks); + Debug.Assert(!withTypeParametersBinder.Flags.Includes(BinderFlags.GenericConstraintsClause)); + withTypeParametersBinder = withTypeParametersBinder.WithAdditionalFlags(BinderFlags.GenericConstraintsClause | BinderFlags.SuppressConstraintChecks); ImmutableArray clauses; - clauses = binder.BindTypeParameterConstraintClauses(containingSymbol, typeParameters, typeParameterList, constraintClauses, + clauses = withTypeParametersBinder.BindTypeParameterConstraintClauses(containingSymbol, typeParameters, typeParameterList, constraintClauses, diagnostics, performOnlyCycleSafeValidation: false); if (clauses.All(clause => clause.ConstraintTypes.IsEmpty)) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs index eaa0d4e5fb2e5..94534b7ba1390 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs @@ -265,9 +265,9 @@ public override bool IsExtensionMethod get { return false; } } - internal override Binder SignatureBinder => _binder; + internal override Binder OuterBinder => _binder; - internal override Binder ParameterBinder => _binder; + internal override Binder WithTypeParametersBinder => _binder; internal override OneOrMany> GetAttributeDeclarations() { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index f56b67a19c10c..9f17c02425416 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -14,8 +14,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { internal sealed class LocalFunctionSymbol : SourceMethodSymbolWithAttributes { - private readonly Binder _parameterBinder; - private readonly Binder? _withTypeParamsBinder; // null when no type parameters + private readonly Binder _binder; private readonly Symbol _containingSymbol; private readonly DeclarationModifiers _declarationModifiers; private readonly ImmutableArray _typeParameters; @@ -61,7 +60,6 @@ internal sealed class LocalFunctionSymbol : SourceMethodSymbolWithAttributes if (syntax.TypeParameterList != null) { - _withTypeParamsBinder = new WithMethodTypeParametersBinder(this, binder); _typeParameters = MakeTypeParameters(_declarationDiagnostics); } else @@ -97,7 +95,7 @@ internal sealed class LocalFunctionSymbol : SourceMethodSymbolWithAttributes _refKind = RefKind.None; } - _parameterBinder = binder; + _binder = binder; } /// @@ -106,7 +104,10 @@ internal sealed class LocalFunctionSymbol : SourceMethodSymbolWithAttributes /// internal Binder ScopeBinder { get; } - internal override Binder ParameterBinder => _withTypeParamsBinder ?? _parameterBinder; + internal override Binder OuterBinder => _binder; + + internal override Binder WithTypeParametersBinder + => _typeParameters.IsEmpty ? _binder : new WithMethodTypeParametersBinder(this, _binder); internal LocalFunctionStatementSyntax Syntax => (LocalFunctionStatementSyntax)syntaxReferenceOpt.GetSyntax(); @@ -181,7 +182,7 @@ private void ComputeParameters() var diagnostics = BindingDiagnosticBag.GetInstance(_declarationDiagnostics); var parameters = ParameterHelpers.MakeParameters( - ParameterBinder, + WithTypeParametersBinder, this, this.Syntax.ParameterList, arglistToken: out arglistToken, @@ -240,7 +241,7 @@ internal void ComputeReturnType() var diagnostics = BindingDiagnosticBag.GetInstance(_declarationDiagnostics); TypeSyntax returnTypeSyntax = Syntax.ReturnType; - TypeWithAnnotations returnType = ParameterBinder.BindType(returnTypeSyntax.SkipRef(), diagnostics); + TypeWithAnnotations returnType = WithTypeParametersBinder.BindType(returnTypeSyntax.SkipRef(), diagnostics); var compilation = DeclaringCompilation; @@ -335,8 +336,6 @@ internal override TypeWithAnnotations IteratorElementTypeWithAnnotations public SyntaxToken NameToken => Syntax.Identifier; - internal override Binder SignatureBinder => _parameterBinder; - public override ImmutableArray ExplicitInterfaceImplementations => ImmutableArray.Empty; public override ImmutableArray Locations => ImmutableArray.Create(Syntax.Identifier.GetLocation()); @@ -477,7 +476,7 @@ public override ImmutableArray> GetTypeParam var syntax = Syntax; var diagnostics = BindingDiagnosticBag.GetInstance(_declarationDiagnostics); var constraints = this.MakeTypeParameterConstraintTypes( - ParameterBinder, + WithTypeParametersBinder, TypeParameters, syntax.TypeParameterList, syntax.ConstraintClauses, @@ -502,7 +501,7 @@ public override ImmutableArray GetTypeParameterCons { var syntax = Syntax; var constraints = this.MakeTypeParameterConstraintKinds( - ParameterBinder, + WithTypeParametersBinder, TypeParameters, syntax.TypeParameterList, syntax.ConstraintClauses); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs index 1b51643ada5cc..cfd99f91a7b28 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols internal static class ParameterHelpers { public static ImmutableArray MakeParameters( - Binder binder, + Binder withTypeParametersBinder, Symbol owner, BaseParameterListSyntax syntax, out SyntaxToken arglistToken, @@ -27,7 +27,7 @@ internal static class ParameterHelpers bool addRefReadOnlyModifier) { return MakeParameters( - binder, + withTypeParametersBinder, owner, syntax.Parameters, out arglistToken, @@ -106,7 +106,7 @@ internal static class ParameterHelpers } private static ImmutableArray MakeParameters( - Binder binder, + Binder withTypeParametersBinder, TOwningSymbol owner, SeparatedSyntaxList parametersList, out SyntaxToken arglistToken, @@ -179,7 +179,7 @@ internal static class ParameterHelpers } Debug.Assert(parameterSyntax.Type != null); - var parameterType = binder.BindType(parameterSyntax.Type, diagnostics, suppressUseSiteDiagnostics: suppressUseSiteDiagnostics); + var parameterType = withTypeParametersBinder.BindType(parameterSyntax.Type, diagnostics, suppressUseSiteDiagnostics: suppressUseSiteDiagnostics); if (!allowRefOrOut && (refKind == RefKind.Ref || refKind == RefKind.Out)) { @@ -197,7 +197,7 @@ internal static class ParameterHelpers } } - TParameterSymbol parameter = parameterCreationFunc(binder, owner, parameterType, parameterSyntax, refKind, parameterIndex, paramsKeyword, thisKeyword, addRefReadOnlyModifier, diagnostics); + TParameterSymbol parameter = parameterCreationFunc(withTypeParametersBinder, owner, parameterType, parameterSyntax, refKind, parameterIndex, paramsKeyword, thisKeyword, addRefReadOnlyModifier, diagnostics); ReportParameterErrors(owner, parameterSyntax, parameter, thisKeyword, paramsKeyword, firstDefault, diagnostics); @@ -224,10 +224,10 @@ internal static class ParameterHelpers default(ImmutableArray); Debug.Assert(methodOwner?.MethodKind != MethodKind.LambdaMethod); - bool allowShadowingNames = binder.Compilation.IsFeatureEnabled(MessageID.IDS_FeatureNameShadowingInNestedFunctions) && + bool allowShadowingNames = withTypeParametersBinder.Compilation.IsFeatureEnabled(MessageID.IDS_FeatureNameShadowingInNestedFunctions) && methodOwner?.MethodKind == MethodKind.LocalFunction; - binder.ValidateParameterNameConflicts(typeParameters, parameters.Cast(), allowShadowingNames, diagnostics); + withTypeParametersBinder.ValidateParameterNameConflicts(typeParameters, parameters.Cast(), allowShadowingNames, diagnostics); } return parameters; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index 832210b4ea495..595e79c9e4181 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -73,7 +73,7 @@ private enum ParameterSyntaxKind : byte _lazyDefaultSyntaxValue = ConstantValue.Unset; } - private Binder ParameterBinderOpt => (ContainingSymbol as SourceMethodSymbolWithAttributes)?.ParameterBinder; + private Binder ParameterBinderOpt => (ContainingSymbol as SourceMethodSymbolWithAttributes)?.WithTypeParametersBinder; internal sealed override SyntaxReference SyntaxReference => _syntaxRef; @@ -246,7 +246,7 @@ private ConstantValue DefaultSyntaxValue } } - private Binder GetBinder(SyntaxNode syntax) + private Binder GetDefaultParameterValueBinder(SyntaxNode syntax) { var binder = ParameterBinderOpt; @@ -289,7 +289,7 @@ private void NullableAnalyzeParameterDefaultValueFromAttributes() return; } - var binder = GetBinder(parameterSyntax); + var binder = GetDefaultParameterValueBinder(parameterSyntax); // Nullable warnings *within* the attribute argument (such as a W-warning for `(string)null`) // are reported when we nullable-analyze attribute arguments separately from here. @@ -329,7 +329,7 @@ private ConstantValue MakeDefaultExpression(BindingDiagnosticBag diagnostics, ou return ConstantValue.NotAvailable; } - binder = GetBinder(defaultSyntax); + binder = GetDefaultParameterValueBinder(defaultSyntax); Binder binderForDefault = binder.CreateBinderForParameterDefaultValue(this, defaultSyntax); Debug.Assert(binderForDefault.InParameterDefaultValue); Debug.Assert(binderForDefault.ContainingMemberOrLambda == ContainingSymbol); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index fb047ab0bfb2b..ae7179d9ef187 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -62,9 +62,9 @@ protected SourceMethodSymbolWithAttributes(SyntaxReference syntaxReferenceOpt) } } - internal virtual Binder? SignatureBinder => null; + internal virtual Binder? OuterBinder => null; - internal virtual Binder? ParameterBinder => null; + internal virtual Binder? WithTypeParametersBinder => null; #nullable disable @@ -283,7 +283,7 @@ private CustomAttributesBag GetAttributesBag(ref CustomAttr declarations, ref lazyCustomAttributesBag, symbolPart, - binderOpt: SignatureBinder); + binderOpt: OuterBinder); } if (bagCreatedOnThisThread) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs index 0364e5248e383..7ae3bfd1e8c50 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs @@ -185,7 +185,7 @@ internal virtual CustomAttributesBag GetAttributesBag() lazyAttributesStored = LoadAndValidateAttributes( OneOrMany.Create(this.MergedAttributeDeclarationSyntaxLists), ref _lazyCustomAttributesBag, - binderOpt: (ContainingSymbol as LocalFunctionSymbol)?.SignatureBinder); + binderOpt: (ContainingSymbol as LocalFunctionSymbol)?.WithTypeParametersBinder); } else { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 037cdfc8f0d8e..45763b92e7368 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -6166,7 +6166,7 @@ class A : Attribute { } } [Fact] - public void ParameterScope_NotInAttributeNameOf() + public void ParameterScope_NotInMethodAttributeNameOf() { var comp = CreateCompilation(@" class C @@ -6199,7 +6199,7 @@ public class MyAttribute : System.Attribute } [Fact] - public void ParameterScope_NotInAttribute() + public void ParameterScope_NotInMethodAttribute() { var comp = CreateCompilation(@" class C @@ -6231,7 +6231,7 @@ public class MyAttribute : System.Attribute } [Fact] - public void ParameterScope_NotInAttributeTypeArgument() + public void ParameterScope_NotInMethodAttributeTypeArgument() { var comp = CreateCompilation(@" class C @@ -6262,7 +6262,7 @@ public class MyAttribute : System.Attribute } [Fact] - public void ParameterScope_NotAsAttributeType() + public void ParameterScope_NotAsMethodAttributeType() { var comp = CreateCompilation(@" class C @@ -6390,7 +6390,7 @@ class C { void M() { - var _ = parameter(int parameter) => throw null; + var _ = parameter (int parameter) => throw null; } parameter M2(int parameter) => throw null; @@ -6398,7 +6398,7 @@ void M() "); comp.VerifyDiagnostics( // (6,17): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) - // var _ = parameter(int parameter) => throw null; + // var _ = parameter (int parameter) => throw null; Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(6, 17), // (9,5): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) // parameter M2(int parameter) => throw null; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs index 31c91fa03a04e..2f5f5742102ce 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs @@ -7119,7 +7119,7 @@ async void F4() } [Fact, WorkItem(59775, "https://github.com/dotnet/roslyn/issues/59775")] - public void TypeParameterScope_NotInAttributeNameOf() + public void TypeParameterScope_NotInMethodAttributeNameOf() { var comp = CreateCompilation(@" class C @@ -7152,7 +7152,7 @@ public class MyAttribute : System.Attribute } [Fact] - public void TypeParameterScope_NotInAttribute() + public void TypeParameterScope_NotInMethodAttribute() { var comp = CreateCompilation(@" class C @@ -7185,7 +7185,7 @@ public class MyAttribute : System.Attribute } [Fact] - public void TypeParameterScope_NotInAttributeTypeArgument() + public void TypeParameterScope_NotInMethodAttributeTypeArgument() { var comp = CreateCompilation(@" class C @@ -7217,7 +7217,7 @@ public class MyAttribute : System.Attribute } [Fact] - public void TypeParameterScope_NotAsAttributeType() + public void TypeParameterScope_NotAsMethodAttributeType() { var comp = CreateCompilation(@" class C @@ -7250,8 +7250,41 @@ void M() ); } + [Fact] + public void TypeParameterScope_NotInMethodAttributeDefault() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(); + + [My(default(TParameter))] + void local() where TParameter : class => throw null; + } + + [My(default(TParameter))] + void M2() where TParameter : class => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(object o) { } +} +"); + comp.VerifyDiagnostics( + // (8,21): error CS0246: The type or namespace name 'TParameter' could not be found (are you missing a using directive or an assembly reference?) + // [My(default(TParameter))] + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "TParameter").WithArguments("TParameter").WithLocation(8, 21), + // (12,17): error CS0246: The type or namespace name 'TParameter' could not be found (are you missing a using directive or an assembly reference?) + // [My(default(TParameter))] + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "TParameter").WithArguments("TParameter").WithLocation(12, 17) + ); + } + [Fact, WorkItem(60110, "https://github.com/dotnet/roslyn/issues/60110")] - public void TypeParameterScope_InParameterAttribute() + public void TypeParameterScope_NotInParameterAttribute() { var comp = CreateCompilation(@" class C @@ -7271,7 +7304,7 @@ public class MyAttribute : System.Attribute public MyAttribute(string name1) { } } "); - // TParameter unexpectedly was found in local function case + // TParameter unexpectedly was found in local function case because of IsInMethodBody logic // Tracked by https://github.com/dotnet/roslyn/issues/60110 comp.VerifyDiagnostics( // (8,36): error CS0119: 'TParameter' is a type, which is not valid in the given context @@ -7308,7 +7341,99 @@ public class MyAttribute : System.Attribute } [Fact] - public void TypeParameterScope_AsParameterAttributeType() + public void TypeParameterScope_InParameterAttributeTypeOf() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(0); + + void local([My(typeof(TParameter))] int i) => throw null; + } + + void M2([My(typeof(TParameter))] int i) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics( + // (8,36): error CS1503: Argument 1: cannot convert from 'System.Type' to 'string' + // void local([My(typeof(TParameter))] int i) => throw null; + Diagnostic(ErrorCode.ERR_BadArgType, "typeof(TParameter)").WithArguments("1", "System.Type", "string").WithLocation(8, 36), + // (11,29): error CS1503: Argument 1: cannot convert from 'System.Type' to 'string' + // void M2([My(typeof(TParameter))] int i) => throw null; + Diagnostic(ErrorCode.ERR_BadArgType, "typeof(TParameter)").WithArguments("1", "System.Type", "string").WithLocation(11, 29) + ); + } + + [Fact] + public void TypeParameterScope_InParameterAttributeSizeOf() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(0); + + void local([My(sizeof(TParameter))] int i) => throw null; + } + + void M2([My(sizeof(TParameter))] int i) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(int i) { } +} +"); + comp.VerifyDiagnostics( + // (8,36): error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('TParameter') + // void local([My(sizeof(TParameter))] int i) => throw null; + Diagnostic(ErrorCode.ERR_ManagedAddr, "sizeof(TParameter)").WithArguments("TParameter").WithLocation(8, 36), + // (8,36): error CS0233: 'TParameter' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // void local([My(sizeof(TParameter))] int i) => throw null; + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(TParameter)").WithArguments("TParameter").WithLocation(8, 36), + // (11,29): error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('TParameter') + // void M2([My(sizeof(TParameter))] int i) => throw null; + Diagnostic(ErrorCode.ERR_ManagedAddr, "sizeof(TParameter)").WithArguments("TParameter").WithLocation(11, 29), + // (11,29): error CS0233: 'TParameter' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // void M2([My(sizeof(TParameter))] int i) => throw null; + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(TParameter)").WithArguments("TParameter").WithLocation(11, 29) + ); + } + + [Fact] + public void TypeParameterScope_InParameterAttributeDefault() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(0); + + void local([My(default(TParameter))] int i) where TParameter : class => throw null; + } + + void M2([My(default(TParameter))] int i) where TParameter : class => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(object o) { } +} +"); + comp.VerifyDiagnostics(); + } + + [Fact] + public void TypeParameterScope_NotAsParameterAttributeType() { var comp = CreateCompilation(@" class C @@ -7372,7 +7497,224 @@ void M() } [Fact] - public void ParameterScope_NotInAttributeNameOf() + public void TypeParameterScope_InTypeConstraint() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(); + + void local() where TParameter : TParameter2 => throw null; + } + + void M2() where TParameter : TParameter2 => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics(); + } + + [Fact] + public void TypeParameterScope_NotInMethodAttributeTypeOf() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(); + + [My(typeof(TParameter))] + void local() => throw null; + } + + [My(typeof(TParameter))] + void M2() => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics( + // (8,20): error CS0246: The type or namespace name 'TParameter' could not be found (are you missing a using directive or an assembly reference?) + // [My(typeof(TParameter))] + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "TParameter").WithArguments("TParameter").WithLocation(8, 20), + // (12,16): error CS0246: The type or namespace name 'TParameter' could not be found (are you missing a using directive or an assembly reference?) + // [My(typeof(TParameter))] + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "TParameter").WithArguments("TParameter").WithLocation(12, 16) + ); + } + + [Fact] + public void TypeParameterScope_NotInTypeParameterAttribute() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(); + + void local<[My(TParameter)] TParameter>() => throw null; + } + + void M2<[My(TParameter)] TParameter>() => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + // TParameter unexpectedly was found in local function case because of IsInMethodBody logic + // Tracked by https://github.com/dotnet/roslyn/issues/60110 + comp.VerifyDiagnostics( + // (8,24): error CS0119: 'TParameter' is a type, which is not valid in the given context + // void local<[My(TParameter)] TParameter>() => throw null; + Diagnostic(ErrorCode.ERR_BadSKunknown, "TParameter").WithArguments("TParameter", "type").WithLocation(8, 24), + // (11,17): error CS0103: The name 'TParameter' does not exist in the current context + // void M2<[My(TParameter)] TParameter>() => throw null; + Diagnostic(ErrorCode.ERR_NameNotInContext, "TParameter").WithArguments("TParameter").WithLocation(11, 17) + ); + } + + [Fact] + public void TypeParameterScope_InTypeParameterAttributeNameOf() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(); + + void local<[My(nameof(TParameter))] TParameter>() => throw null; + } + + void M2<[My(nameof(TParameter))] TParameter>() => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics(); + } + + [Fact] + public void TypeParameterScope_InTypeParameterAttributeDefault() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(); + + void local<[My(default(TParameter))] TParameter>() where TParameter : class => throw null; + } + + void M2<[My(default(TParameter))] TParameter>() where TParameter : class => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(object o) { } +} +"); + comp.VerifyDiagnostics(); + } + + [Fact] + public void TypeParameterScope_AsTypeParameterAttributeType() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(); + + void local<[TParameter] TParameter>() where TParameter : System.Attribute => throw null; + } + + void M2<[TParameter] TParameter>() where TParameter : System.Attribute => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics( + // (8,21): error CS0616: 'TParameter' is not an attribute class + // void local<[TParameter] TParameter>() where TParameter : System.Attribute => throw null; + Diagnostic(ErrorCode.ERR_NotAnAttributeClass, "TParameter").WithArguments("TParameter").WithLocation(8, 21), + // (11,14): error CS0616: 'TParameter' is not an attribute class + // void M2<[TParameter] TParameter>() where TParameter : System.Attribute => throw null; + Diagnostic(ErrorCode.ERR_NotAnAttributeClass, "TParameter").WithArguments("TParameter").WithLocation(11, 14) + ); + } + + [Fact] + public void TypeParameterScope_AsParameterDefaultDefaultValue() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(); + + void local(TParameter s = default(TParameter)) => throw null; + } + + void M2(TParameter s = default(TParameter)) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics(); + } + + [Fact] + public void TypeParameterScope_AsParameterNameOfDefaultValue() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(); + + void local(string s = nameof(TParameter)) => throw null; + } + + void M2(string s = nameof(TParameter)) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics(); + } + + [Fact] + public void ParameterScope_NotInMethodAttributeNameOf() { var comp = CreateCompilation(@" class C @@ -7405,7 +7747,7 @@ public class MyAttribute : System.Attribute } [Fact] - public void ParameterScope_NotInAttribute() + public void ParameterScope_NotInMethodAttribute() { var comp = CreateCompilation(@" class C @@ -7438,7 +7780,7 @@ public class MyAttribute : System.Attribute } [Fact] - public void ParameterScope_NotInAttributeTypeArgument() + public void ParameterScope_NotInMethodAttributeTypeArgument() { var comp = CreateCompilation(@" class C @@ -7470,7 +7812,7 @@ public class MyAttribute : System.Attribute } [Fact] - public void ParameterScope_NotAsAttributeType() + public void ParameterScope_NotAsMethodAttributeType() { var comp = CreateCompilation(@" class C @@ -7648,5 +7990,212 @@ void M() Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(11, 25) ); } + + [Fact] + public void ParameterScope_NotInTypeConstraint() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(0); + M2(0); + + void local(int parameter) where TParameter : parameter => throw null; + } + + void M2(int parameter) where TParameter : parameter => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics( + // (6,9): error CS0311: The type 'object' cannot be used as type parameter 'TParameter' in the generic type or method 'local(int)'. There is no implicit reference conversion from 'object' to 'parameter'. + // local(0); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "local").WithArguments("local(int)", "parameter", "TParameter", "object").WithLocation(6, 9), + // (7,9): error CS0311: The type 'object' cannot be used as type parameter 'TParameter' in the generic type or method 'C.M2(int)'. There is no implicit reference conversion from 'object' to 'parameter'. + // M2(0); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "M2").WithArguments("C.M2(int)", "parameter", "TParameter", "object").WithLocation(7, 9), + // (9,66): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // void local(int parameter) where TParameter : parameter => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(9, 66), + // (12,59): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // void M2(int parameter) where TParameter : parameter => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(12, 59) + ); + } + + [Fact] + public void MethodScope_InParameterAttribute() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(0); + + void local([My(local)] int i) => throw null; + } + + void M2([My(M2)] int i) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics( + // (8,36): error CS1503: Argument 1: cannot convert from 'method group' to 'string' + // void local([My(local)] int i) => throw null; + Diagnostic(ErrorCode.ERR_BadArgType, "local").WithArguments("1", "method group", "string").WithLocation(8, 36), + // (11,29): error CS1503: Argument 1: cannot convert from 'method group' to 'string' + // void M2([My(M2)] int i) => throw null; + Diagnostic(ErrorCode.ERR_BadArgType, "M2").WithArguments("1", "method group", "string").WithLocation(11, 29) + ); + } + + [Fact] + public void MethodScope_InParameterAttributeNameOf() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(0); + + void local([My(nameof(local))] int i) => throw null; + } + + void M2([My(nameof(M2))] int i) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics(); + } + + [Fact] + public void MethodScope_InTypeParameterAttribute() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(0); + + void local<[My(local)] TParameter>(int i) => throw null; + } + + void M2<[My(M2)] TParameter>(int i) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics( + // (8,24): error CS1503: Argument 1: cannot convert from 'method group' to 'string' + // void local<[My(local)] TParameter>(int i) => throw null; + Diagnostic(ErrorCode.ERR_BadArgType, "local").WithArguments("1", "method group", "string").WithLocation(8, 24), + // (11,17): error CS1503: Argument 1: cannot convert from 'method group' to 'string' + // void M2<[My(M2)] TParameter>(int i) => throw null; + Diagnostic(ErrorCode.ERR_BadArgType, "M2").WithArguments("1", "method group", "string").WithLocation(11, 17) + ); + } + + [Fact] + public void MethodScope_InTypeParameterAttributeNameOf() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(0); + + void local<[My(nameof(local))] TParameter>(int i) => throw null; + } + + void M2<[My(nameof(M2))] TParameter>(int i) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics(); + } + + [Fact] + public void MethodScope_InMethodAttribute() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(0); + + [My(local)] + void local(int i) => throw null; + } + + [My(M2)] + void M2(int i) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics( + // (8,13): error CS1503: Argument 1: cannot convert from 'method group' to 'string' + // [My(local)] + Diagnostic(ErrorCode.ERR_BadArgType, "local").WithArguments("1", "method group", "string").WithLocation(8, 13), + // (12,9): error CS1503: Argument 1: cannot convert from 'method group' to 'string' + // [My(M2)] + Diagnostic(ErrorCode.ERR_BadArgType, "M2").WithArguments("1", "method group", "string").WithLocation(12, 9) + ); + } + + [Fact] + public void MethodScope_InMethodAttributeNameOf() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(0); + + [My(nameof(local))] + void local(int i) => throw null; + } + + [My(nameof(M2))] + void M2(int i) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics(); + } } } From 8662b426518ed2ad6bd3b7c84a5edd92b9efd1f9 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 14 Mar 2022 23:04:18 -0700 Subject: [PATCH 4/8] Few more tests and a rename --- .../Source/SourceComplexParameterSymbol.cs | 6 +- .../Semantic/Semantics/LocalFunctionTests.cs | 68 +++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index 595e79c9e4181..a19dcff0d7038 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -73,7 +73,7 @@ private enum ParameterSyntaxKind : byte _lazyDefaultSyntaxValue = ConstantValue.Unset; } - private Binder ParameterBinderOpt => (ContainingSymbol as SourceMethodSymbolWithAttributes)?.WithTypeParametersBinder; + private Binder WithTypeParametersBinderOpt => (ContainingSymbol as SourceMethodSymbolWithAttributes)?.WithTypeParametersBinder; internal sealed override SyntaxReference SyntaxReference => _syntaxRef; @@ -248,7 +248,7 @@ private ConstantValue DefaultSyntaxValue private Binder GetDefaultParameterValueBinder(SyntaxNode syntax) { - var binder = ParameterBinderOpt; + var binder = WithTypeParametersBinderOpt; // If binder is null, then get it from the compilation. Otherwise use the provided binder. // Don't always get it from the compilation because we might be in a speculative context (local function parameter), @@ -551,7 +551,7 @@ internal sealed override CustomAttributesBag GetAttributesB else { var attributeSyntax = this.GetAttributeDeclarations(); - bagCreatedOnThisThread = LoadAndValidateAttributes(attributeSyntax, ref _lazyCustomAttributesBag, binderOpt: ParameterBinderOpt); + bagCreatedOnThisThread = LoadAndValidateAttributes(attributeSyntax, ref _lazyCustomAttributesBag, binderOpt: WithTypeParametersBinderOpt); } if (bagCreatedOnThisThread) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs index 2f5f5742102ce..115d9a1a1e648 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs @@ -8029,6 +8029,74 @@ public class MyAttribute : System.Attribute ); } + [Fact] + public void ParameterScope_NotAsParameterDefaultDefaultValue() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(); + + void local(string parameter = default(parameter)) => throw null; + } + + void M2(string parameter = default(parameter)) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics( + // (8,27): error CS1750: A value of type 'parameter' cannot be used as a default parameter because there are no standard conversions to type 'string' + // void local(string parameter = default(parameter)) => throw null; + Diagnostic(ErrorCode.ERR_NoConversionForDefaultParam, "parameter").WithArguments("parameter", "string").WithLocation(8, 27), + // (8,47): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // void local(string parameter = default(parameter)) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(8, 47), + // (11,20): error CS1750: A value of type 'parameter' cannot be used as a default parameter because there are no standard conversions to type 'string' + // void M2(string parameter = default(parameter)) => throw null; + Diagnostic(ErrorCode.ERR_NoConversionForDefaultParam, "parameter").WithArguments("parameter", "string").WithLocation(11, 20), + // (11,40): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // void M2(string parameter = default(parameter)) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(11, 40) + ); + } + + [Fact] + public void ParameterScope_NotAsParameterNameOfDefaultValue() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + local(); + + void local(string parameter = nameof(parameter)) => throw null; + } + + void M2(string parameter = nameof(parameter)) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics( + // (8,46): error CS0103: The name 'parameter' does not exist in the current context + // void local(string parameter = nameof(parameter)) => throw null; + Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(8, 46), + // (11,39): error CS0103: The name 'parameter' does not exist in the current context + // void M2(string parameter = nameof(parameter)) => throw null; + Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(11, 39) + ); + } + [Fact] public void MethodScope_InParameterAttribute() { From 239ab78b5444a11c6086a7ceac86f6fff9de131e Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 15 Mar 2022 09:19:46 -0700 Subject: [PATCH 5/8] Semantic model --- .../Compilation/MemberSemanticModel.cs | 3 +- .../CSharp/Portable/Syntax/LookupPosition.cs | 30 +++ .../Test/Semantic/Semantics/LambdaTests.cs | 105 +++++++++ .../Semantic/Semantics/LocalFunctionTests.cs | 221 +++++++++++++++--- 4 files changed, 329 insertions(+), 30 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs index 983f2e40714db..41488004a1b57 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs @@ -356,7 +356,8 @@ private static Binder GetEnclosingBinderInternalWithinRoot(SyntaxNode node, int if (ownerOfTypeParametersInScope != null) { LocalFunctionSymbol function = GetDeclaredLocalFunction(binder, ownerOfTypeParametersInScope.Identifier); - if ((object)function != null) + if ((object)function != null + && LookupPosition.IsInLocalFunctionTypeParameterScope(position, function.Syntax)) { binder = function.WithTypeParametersBinder; } diff --git a/src/Compilers/CSharp/Portable/Syntax/LookupPosition.cs b/src/Compilers/CSharp/Portable/Syntax/LookupPosition.cs index e2ec607c57024..3844a5f69300c 100644 --- a/src/Compilers/CSharp/Portable/Syntax/LookupPosition.cs +++ b/src/Compilers/CSharp/Portable/Syntax/LookupPosition.cs @@ -228,6 +228,36 @@ internal static bool IsInMethodTypeParameterScope(int position, MethodDeclaratio return !IsBetweenTokens(position, firstNameToken, firstPostNameToken); } + internal static bool IsInLocalFunctionTypeParameterScope(int position, LocalFunctionStatementSyntax localFunction) + { + Debug.Assert(localFunction != null); + + if (localFunction.TypeParameterList == null) + { + // no type parameters => nothing can be in their scope + return false; + } + + // optimization for a common case - when position is in the ReturnType, we can see type parameters + if (localFunction.ReturnType.FullSpan.Contains(position)) + { + return true; + } + + // Must be in the local function, but not in an attribute on the method. + if (IsInAttributeSpecification(position, localFunction.AttributeLists)) + { + return false; + } + + var firstNameToken = localFunction.Identifier; + var typeParams = localFunction.TypeParameterList; + var firstPostNameToken = typeParams == null ? localFunction.ParameterList.OpenParenToken : typeParams.LessThanToken; + + // Scope does not include local function name. + return !IsBetweenTokens(position, firstNameToken, firstPostNameToken); + } + /// /// Used to determine whether it would be appropriate to use the binder for the statement (if any). /// Not used to determine whether the position is syntactically within the statement. diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 45763b92e7368..0aff75783cf9e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -6196,6 +6196,64 @@ public class MyAttribute : System.Attribute // [My(nameof(parameter))] // 2 Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(12, 16) ); + + VerifyParameter(comp, 0); + VerifyParameter(comp, 1); + } + + /// + /// Look for usages of "parameter" and verify the index-th one. + /// + private void VerifyParameter(CSharpCompilation comp, int index) + { + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var parameterUsages = tree.GetRoot().DescendantNodes().OfType() + .Where(i => i.Identifier.ValueText == "parameter") + .Where(i => i.Ancestors().Any(a => a.IsKind(SyntaxKind.Attribute) || a.IsKind(SyntaxKind.DefaultExpression) || a.IsKind(SyntaxKind.InvocationExpression))) + .ToArray(); + + var parameterUsage = parameterUsages[index]; + + Assert.Null(model.GetSymbolInfo(parameterUsage).Symbol); + Assert.True(model.GetTypeInfo(parameterUsage).Type.IsErrorType()); + Assert.DoesNotContain("parameter", model.LookupSymbols(parameterUsage.Position).ToTestDisplayStrings()); + } + + [Fact] + public void ParameterScope_NotInMethodAttributeTypeOf() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + + var _ = + [My(typeof(parameter))] // 1 + void(int parameter) => { }; + } + + [My(typeof(parameter))] // 2 + void M2(int parameter) { } +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics( + // (8,24): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // [My(typeof(parameter))] // 1 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(8, 24), + // (12,16): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // [My(typeof(parameter))] // 2 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(12, 16) + ); + + VerifyParameter(comp, 0); + VerifyParameter(comp, 1); } [Fact] @@ -6228,6 +6286,9 @@ public class MyAttribute : System.Attribute // [My(parameter)] // 2 Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(11, 9) ); + + VerifyParameter(comp, 0); + VerifyParameter(comp, 1); } [Fact] @@ -6259,6 +6320,9 @@ public class MyAttribute : System.Attribute // [My] // 2 Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(11, 9) ); + + VerifyParameter(comp, 0); + VerifyParameter(comp, 1); } [Fact] @@ -6292,6 +6356,9 @@ void M() // [parameter] // 2 Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(11, 6) ); + + VerifyParameter(comp, 0); + VerifyParameter(comp, 1); } [Fact] @@ -6321,6 +6388,9 @@ public class MyAttribute : System.Attribute // void M2([My(parameter)] int parameter) => throw null; Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(9, 17) ); + + VerifyParameter(comp, 0); + VerifyParameter(comp, 1); } [Fact] @@ -6350,6 +6420,38 @@ public class MyAttribute : System.Attribute // void M2([My(nameof(parameter))] int parameter) => throw null; Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(9, 24) ); + + VerifyParameter(comp, 0); + VerifyParameter(comp, 1); + } + + [Fact] + public void ParameterScope_InParameterDefaultValueNameOf() + { + var comp = CreateCompilation(@" +class C +{ + void M() + { + var _ = void (string parameter = nameof(parameter)) => throw null; + } + + void M2(string parameter = nameof(parameter)) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"); + comp.VerifyDiagnostics( + // (6,40): error CS1065: Default values are not valid in this context. + // var _ = void (string parameter = nameof(parameter)) => throw null; + Diagnostic(ErrorCode.ERR_DefaultValueNotAllowed, "=").WithLocation(6, 40), + // (9,39): error CS0103: The name 'parameter' does not exist in the current context + // void M2(string parameter = nameof(parameter)) => throw null; + Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(9, 39) + ); } [Fact] @@ -6380,6 +6482,9 @@ void M() // void M2([parameter] System.Attribute parameter) => throw null; Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(9, 14) ); + + VerifyParameter(comp, 0); + VerifyParameter(comp, 1); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs index 115d9a1a1e648..127d02f7d48c7 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs @@ -7149,6 +7149,50 @@ public class MyAttribute : System.Attribute // [My(nameof(TParameter))] // 2 Diagnostic(ErrorCode.ERR_NameNotInContext, "TParameter").WithArguments("TParameter").WithLocation(12, 16) ); + + VerifyTParameter(comp, 0, null); + VerifyTParameter(comp, 1, null); + } + + /// + /// Look for usages of "TParameter" and verify the index-th one. + /// + private void VerifyTParameter(CSharpCompilation comp, int index, string expectedMethod, bool findAnyways = false, bool lookupFailsAnyways = false) + { + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var tParameterUsages = tree.GetRoot().DescendantNodes().OfType() + .Where(i => i.Identifier.ValueText == "TParameter") + .Where(i => i.Ancestors().Any(a => a.IsKind(SyntaxKind.Attribute) || a.IsKind(SyntaxKind.TypeConstraint) || a.IsKind(SyntaxKind.DefaultExpression) || a.IsKind(SyntaxKind.InvocationExpression))) + .ToArray(); + + var tParameterUsage = tParameterUsages[index]; + + var symbol = model.GetSymbolInfo(tParameterUsage).Symbol; + if (expectedMethod is null) + { + Assert.Null(symbol); + + var typeInfo = model.GetTypeInfo(tParameterUsage); + if (findAnyways) + { + // In certain cases, like `[TParameter]`, we're able to bind the attribute, find the type but reject it. + // So GetTypeInfo does return a type. + Assert.Equal(SymbolKind.TypeParameter, typeInfo.Type.Kind); + } + else + { + Assert.True(typeInfo.Type.IsErrorType()); + } + + Assert.Equal(findAnyways, model.LookupSymbols(tParameterUsage.Position).ToTestDisplayStrings().Contains("TParameter")); + } + else + { + Assert.Equal(expectedMethod, symbol.ContainingSymbol.ToTestDisplayString()); + Assert.Equal(SymbolKind.TypeParameter, model.GetTypeInfo(tParameterUsage).Type.Kind); + Assert.Equal(!lookupFailsAnyways, model.LookupSymbols(tParameterUsage.Position).ToTestDisplayStrings().Contains("TParameter")); + } } [Fact] @@ -7182,6 +7226,9 @@ public class MyAttribute : System.Attribute // [My(TParameter)] // 2 Diagnostic(ErrorCode.ERR_NameNotInContext, "TParameter").WithArguments("TParameter").WithLocation(12, 9) ); + + VerifyTParameter(comp, 0, null); + VerifyTParameter(comp, 1, null); } [Fact] @@ -7194,12 +7241,12 @@ void M() { local(); - [My] // 1 - void local() { } + [My] // 1 + void local() { } } - [My] // 2 - void M2() { } + [My] // 2 + void M2() { } } public class MyAttribute : System.Attribute @@ -7207,13 +7254,16 @@ public class MyAttribute : System.Attribute } "); comp.VerifyDiagnostics( - // (8,13): error CS0246: The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?) - // [My] // 1 - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "T").WithArguments("T").WithLocation(8, 13), - // (12,9): error CS0246: The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?) - // [My] // 2 - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "T").WithArguments("T").WithLocation(12, 9) + // (8,13): error CS0246: The type or namespace name 'TParameter' could not be found (are you missing a using directive or an assembly reference?) + // [My] // 1 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "TParameter").WithArguments("TParameter").WithLocation(8, 13), + // (12,9): error CS0246: The type or namespace name 'TParameter' could not be found (are you missing a using directive or an assembly reference?) + // [My] // 2 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "TParameter").WithArguments("TParameter").WithLocation(12, 9) ); + + VerifyTParameter(comp, 0, null); + VerifyTParameter(comp, 1, null); } [Fact] @@ -7226,28 +7276,31 @@ void M() { local(); - [T] // 1 - void local() where T : System.Attribute { } + [TParameter] // 1 + void local() where TParameter : System.Attribute { } } - [T] // 2 - void M2() where T : System.Attribute { } + [TParameter] // 2 + void M2() where TParameter : System.Attribute { } } "); comp.VerifyDiagnostics( - // (8,10): error CS0246: The type or namespace name 'TAttribute' could not be found (are you missing a using directive or an assembly reference?) - // [T] // 1 - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "T").WithArguments("TAttribute").WithLocation(8, 10), - // (8,10): error CS0246: The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?) - // [T] // 1 - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "T").WithArguments("T").WithLocation(8, 10), - // (12,6): error CS0246: The type or namespace name 'TAttribute' could not be found (are you missing a using directive or an assembly reference?) - // [T] // 2 - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "T").WithArguments("TAttribute").WithLocation(12, 6), - // (12,6): error CS0246: The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?) - // [T] // 2 - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "T").WithArguments("T").WithLocation(12, 6) + // (8,10): error CS0246: The type or namespace name 'TParameterAttribute' could not be found (are you missing a using directive or an assembly reference?) + // [TParameter] // 1 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "TParameter").WithArguments("TParameterAttribute").WithLocation(8, 10), + // (8,10): error CS0246: The type or namespace name 'TParameter' could not be found (are you missing a using directive or an assembly reference?) + // [TParameter] // 1 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "TParameter").WithArguments("TParameter").WithLocation(8, 10), + // (12,6): error CS0246: The type or namespace name 'TParameterAttribute' could not be found (are you missing a using directive or an assembly reference?) + // [TParameter] // 2 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "TParameter").WithArguments("TParameterAttribute").WithLocation(12, 6), + // (12,6): error CS0246: The type or namespace name 'TParameter' could not be found (are you missing a using directive or an assembly reference?) + // [TParameter] // 2 + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "TParameter").WithArguments("TParameter").WithLocation(12, 6) ); + + VerifyTParameter(comp, 0, null); + VerifyTParameter(comp, 1, null); } [Fact] @@ -7281,6 +7334,9 @@ public class MyAttribute : System.Attribute // [My(default(TParameter))] Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "TParameter").WithArguments("TParameter").WithLocation(12, 17) ); + + VerifyTParameter(comp, 0, null); + VerifyTParameter(comp, 1, null); } [Fact, WorkItem(60110, "https://github.com/dotnet/roslyn/issues/60110")] @@ -7314,6 +7370,9 @@ public class MyAttribute : System.Attribute // void M2([My(TParameter)] int i) => throw null; Diagnostic(ErrorCode.ERR_NameNotInContext, "TParameter").WithArguments("TParameter").WithLocation(11, 29) ); + + //VerifyTParameter(comp, 0, null); + VerifyTParameter(comp, 1, null); } [Fact] @@ -7338,6 +7397,11 @@ public class MyAttribute : System.Attribute } "); comp.VerifyDiagnostics(); + + VerifyTParameter(comp, 0, "void local(System.Int32 i)"); + // LookupSymbols fails to find TParameter + // Tracked by https://github.com/dotnet/roslyn/issues/60194 + VerifyTParameter(comp, 1, "void C.M2(System.Int32 i)", lookupFailsAnyways: true); } [Fact] @@ -7369,6 +7433,9 @@ public class MyAttribute : System.Attribute // void M2([My(typeof(TParameter))] int i) => throw null; Diagnostic(ErrorCode.ERR_BadArgType, "typeof(TParameter)").WithArguments("1", "System.Type", "string").WithLocation(11, 29) ); + + VerifyTParameter(comp, 0, "void local(System.Int32 i)"); + VerifyTParameter(comp, 1, "void C.M2(System.Int32 i)"); } [Fact] @@ -7406,6 +7473,9 @@ public class MyAttribute : System.Attribute // void M2([My(sizeof(TParameter))] int i) => throw null; Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(TParameter)").WithArguments("TParameter").WithLocation(11, 29) ); + + VerifyTParameter(comp, 0, "void local(System.Int32 i)"); + VerifyTParameter(comp, 1, "void C.M2(System.Int32 i)"); } [Fact] @@ -7430,6 +7500,9 @@ public class MyAttribute : System.Attribute } "); comp.VerifyDiagnostics(); + + VerifyTParameter(comp, 0, "void local(System.Int32 i)"); + VerifyTParameter(comp, 1, "void C.M2(System.Int32 i)"); } [Fact] @@ -7456,6 +7529,9 @@ void M() // void M2([TParameter] int i) where TParameter : System.Attribute => throw null; Diagnostic(ErrorCode.ERR_NotAnAttributeClass, "TParameter").WithArguments("TParameter").WithLocation(11, 26) ); + + VerifyTParameter(comp, 0, null, findAnyways: true); + VerifyTParameter(comp, 1, null, findAnyways: true); } [Fact] @@ -7506,10 +7582,10 @@ void M() { local(); - void local() where TParameter : TParameter2 => throw null; + void local() where TParameter2 : TParameter => throw null; } - void M2() where TParameter : TParameter2 => throw null; + void M2() where TParameter2 : TParameter => throw null; } public class MyAttribute : System.Attribute @@ -7518,6 +7594,9 @@ public class MyAttribute : System.Attribute } "); comp.VerifyDiagnostics(); + + VerifyTParameter(comp, 0, "void local()"); + VerifyTParameter(comp, 1, "void C.M2()"); } [Fact] @@ -7551,6 +7630,9 @@ public class MyAttribute : System.Attribute // [My(typeof(TParameter))] Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "TParameter").WithArguments("TParameter").WithLocation(12, 16) ); + + VerifyTParameter(comp, 0, null); + VerifyTParameter(comp, 1, null); } [Fact] @@ -7584,6 +7666,9 @@ public class MyAttribute : System.Attribute // void M2<[My(TParameter)] TParameter>() => throw null; Diagnostic(ErrorCode.ERR_NameNotInContext, "TParameter").WithArguments("TParameter").WithLocation(11, 17) ); + + //VerifyTParameter(comp, 0, null); + VerifyTParameter(comp, 1, null); } [Fact] @@ -7608,6 +7693,11 @@ public class MyAttribute : System.Attribute } "); comp.VerifyDiagnostics(); + + VerifyTParameter(comp, 0, "void local()"); + // LookupSymbols fails to find TParameter + // Tracked by https://github.com/dotnet/roslyn/issues/60194 + VerifyTParameter(comp, 1, "void C.M2()", lookupFailsAnyways: true); } [Fact] @@ -7632,10 +7722,13 @@ public class MyAttribute : System.Attribute } "); comp.VerifyDiagnostics(); + + VerifyTParameter(comp, 0, "void local()"); + VerifyTParameter(comp, 1, "void C.M2()"); } [Fact] - public void TypeParameterScope_AsTypeParameterAttributeType() + public void TypeParameterScope_NotAsTypeParameterAttributeType() { var comp = CreateCompilation(@" class C @@ -7663,6 +7756,9 @@ public class MyAttribute : System.Attribute // void M2<[TParameter] TParameter>() where TParameter : System.Attribute => throw null; Diagnostic(ErrorCode.ERR_NotAnAttributeClass, "TParameter").WithArguments("TParameter").WithLocation(11, 14) ); + + VerifyTParameter(comp, 0, null, findAnyways: true); + VerifyTParameter(comp, 1, null, findAnyways: true); } [Fact] @@ -7687,6 +7783,9 @@ public class MyAttribute : System.Attribute } "); comp.VerifyDiagnostics(); + + VerifyTParameter(comp, 0, "void local([TParameter s = default(TParameter)])"); + VerifyTParameter(comp, 1, "void C.M2([TParameter s = default(TParameter)])"); } [Fact] @@ -7711,6 +7810,11 @@ public class MyAttribute : System.Attribute } "); comp.VerifyDiagnostics(); + + VerifyTParameter(comp, 0, @"void local([System.String s = ""TParameter""])"); + // LookupSymbols fails to find TParameter + // Tracked by https://github.com/dotnet/roslyn/issues/60194 + VerifyTParameter(comp, 1, @"void C.M2([System.String s = ""TParameter""])", lookupFailsAnyways: true); } [Fact] @@ -7744,6 +7848,38 @@ public class MyAttribute : System.Attribute // [My(nameof(parameter))] // 2 Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(12, 16) ); + + VerifyParameter(comp, 0, null); + VerifyParameter(comp, 1, null); + } + + /// + /// Look for usages of "parameter" and verify the index-th one. + /// + private void VerifyParameter(CSharpCompilation comp, int index, string expectedMethod) + { + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var parameterUsages = tree.GetRoot().DescendantNodes().OfType() + .Where(i => i.Identifier.ValueText == "parameter") + .Where(i => i.Ancestors().Any(a => a.IsKind(SyntaxKind.Attribute) || a.IsKind(SyntaxKind.TypeConstraint) || a.IsKind(SyntaxKind.DefaultExpression) || a.IsKind(SyntaxKind.InvocationExpression))) + .ToArray(); + + var parameterUsage = parameterUsages[index]; + + var symbol = model.GetSymbolInfo(parameterUsage).Symbol; + if (expectedMethod is null) + { + Assert.Null(symbol); + Assert.True(model.GetTypeInfo(parameterUsage).Type.IsErrorType()); + Assert.DoesNotContain("parameter", model.LookupSymbols(parameterUsage.Position).ToTestDisplayStrings()); + } + else + { + Assert.Equal(expectedMethod, symbol.ContainingSymbol.ToTestDisplayString()); + Assert.Equal(SymbolKind.Parameter, model.GetTypeInfo(parameterUsage).Type.Kind); + Assert.Contains(symbol, model.LookupSymbols(parameterUsage.Position)); + } } [Fact] @@ -7777,6 +7913,9 @@ public class MyAttribute : System.Attribute // [My(parameter)] // 2 Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(12, 9) ); + + VerifyParameter(comp, 0, null); + VerifyParameter(comp, 1, null); } [Fact] @@ -7809,6 +7948,9 @@ public class MyAttribute : System.Attribute // [My] // 2 Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(12, 9) ); + + VerifyParameter(comp, 0, null); + VerifyParameter(comp, 1, null); } [Fact] @@ -7843,6 +7985,9 @@ void M() // [parameter] // 2 Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(12, 6) ); + + VerifyParameter(comp, 0, null); + VerifyParameter(comp, 1, null); } [Fact] @@ -7874,6 +8019,9 @@ public class MyAttribute : System.Attribute // void M2([My(parameter)] int parameter) => throw null; Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(11, 17) ); + + VerifyParameter(comp, 0, null); + VerifyParameter(comp, 1, null); } [Fact] @@ -7905,6 +8053,9 @@ public class MyAttribute : System.Attribute // void M2([My(nameof(parameter))] int parameter) => throw null; Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(11, 24) ); + + VerifyParameter(comp, 0, null); + VerifyParameter(comp, 1, null); } [Fact] @@ -7937,6 +8088,9 @@ void M() // void M2([parameter] System.Attribute parameter) => throw null; Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(11, 14) ); + + VerifyParameter(comp, 0, null); + VerifyParameter(comp, 1, null); } [Fact] @@ -8027,6 +8181,9 @@ public class MyAttribute : System.Attribute // void M2(int parameter) where TParameter : parameter => throw null; Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(12, 59) ); + + VerifyParameter(comp, 0, null); + VerifyParameter(comp, 1, null); } [Fact] @@ -8064,6 +8221,9 @@ public class MyAttribute : System.Attribute // void M2(string parameter = default(parameter)) => throw null; Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(11, 40) ); + + VerifyParameter(comp, 0, null); + VerifyParameter(comp, 1, null); } [Fact] @@ -8095,6 +8255,9 @@ public class MyAttribute : System.Attribute // void M2(string parameter = nameof(parameter)) => throw null; Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(11, 39) ); + + VerifyParameter(comp, 0, null); + VerifyParameter(comp, 1, null); } [Fact] From e0780c32456f01e9f844c6e23d001f1ea0ca479c Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 15 Mar 2022 22:15:38 -0700 Subject: [PATCH 6/8] Address feedback --- .../Portable/Compilation/MemberSemanticModel.cs | 3 +-- .../CSharp/Portable/Symbols/ConstraintsHelper.cs | 10 +++++----- .../CSharp/Portable/Syntax/LookupPosition.cs | 11 ++++------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs index 41488004a1b57..dc3500ea64cec 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs @@ -250,8 +250,7 @@ private static Binder GetEnclosingBinderInternalWithinRoot(SyntaxNode node, int { Debug.Assert(ownerOfTypeParametersInScope == null); var localFunction = (LocalFunctionStatementSyntax)stmt; - if (localFunction.TypeParameterList != null && - !LookupPosition.IsBetweenTokens(position, localFunction.Identifier, localFunction.TypeParameterList.LessThanToken)) // Scope does not include method name. + if (LookupPosition.IsInLocalFunctionTypeParameterScope(position, localFunction)) { ownerOfTypeParametersInScope = localFunction; } diff --git a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs index 598316bb71ec9..4851248e37bdc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs @@ -344,7 +344,7 @@ internal static class ConstraintsHelper internal static ImmutableArray MakeTypeParameterConstraintKinds( this MethodSymbol containingSymbol, - Binder binder, + Binder withTypeParametersBinder, ImmutableArray typeParameters, TypeParameterListSyntax typeParameterList, SyntaxList constraintClauses) @@ -358,18 +358,18 @@ internal static class ConstraintsHelper if (constraintClauses.Count == 0) { - clauses = binder.GetDefaultTypeParameterConstraintClauses(typeParameterList); + clauses = withTypeParametersBinder.GetDefaultTypeParameterConstraintClauses(typeParameterList); } else { // Wrap binder from factory in a generic constraints specific binder // Also, suppress type argument binding in constraint types, this helps to avoid cycles while we figure out constraint kinds. // to avoid checking constraints when binding type names. - Debug.Assert(!binder.Flags.Includes(BinderFlags.GenericConstraintsClause)); - binder = binder.WithAdditionalFlags(BinderFlags.GenericConstraintsClause | BinderFlags.SuppressConstraintChecks | BinderFlags.SuppressTypeArgumentBinding); + Debug.Assert(!withTypeParametersBinder.Flags.Includes(BinderFlags.GenericConstraintsClause)); + withTypeParametersBinder = withTypeParametersBinder.WithAdditionalFlags(BinderFlags.GenericConstraintsClause | BinderFlags.SuppressConstraintChecks | BinderFlags.SuppressTypeArgumentBinding); // We will recompute this diagnostics more accurately later, when binding without BinderFlags.SuppressTypeArgumentBinding - clauses = binder.BindTypeParameterConstraintClauses(containingSymbol, typeParameters, typeParameterList, constraintClauses, + clauses = withTypeParametersBinder.BindTypeParameterConstraintClauses(containingSymbol, typeParameters, typeParameterList, constraintClauses, BindingDiagnosticBag.Discarded, performOnlyCycleSafeValidation: true); clauses = AdjustConstraintKindsBasedOnConstraintTypes(containingSymbol, typeParameters, clauses); diff --git a/src/Compilers/CSharp/Portable/Syntax/LookupPosition.cs b/src/Compilers/CSharp/Portable/Syntax/LookupPosition.cs index 3844a5f69300c..9015176b4b586 100644 --- a/src/Compilers/CSharp/Portable/Syntax/LookupPosition.cs +++ b/src/Compilers/CSharp/Portable/Syntax/LookupPosition.cs @@ -202,7 +202,7 @@ internal static bool IsInMethodTypeParameterScope(int position, MethodDeclaratio if (methodDecl.TypeParameterList == null) { - // no type parameters => nothing can be in their scope + // no type parameters => they are not in scope return false; } @@ -220,9 +220,7 @@ internal static bool IsInMethodTypeParameterScope(int position, MethodDeclaratio var explicitInterfaceSpecifier = methodDecl.ExplicitInterfaceSpecifier; var firstNameToken = explicitInterfaceSpecifier == null ? methodDecl.Identifier : explicitInterfaceSpecifier.GetFirstToken(); - - var typeParams = methodDecl.TypeParameterList; - var firstPostNameToken = typeParams == null ? methodDecl.ParameterList.OpenParenToken : typeParams.LessThanToken; + var firstPostNameToken = methodDecl.TypeParameterList.LessThanToken; // Scope does not include method name. return !IsBetweenTokens(position, firstNameToken, firstPostNameToken); @@ -234,7 +232,7 @@ internal static bool IsInLocalFunctionTypeParameterScope(int position, LocalFunc if (localFunction.TypeParameterList == null) { - // no type parameters => nothing can be in their scope + // no type parameters => they are not in scope return false; } @@ -251,8 +249,7 @@ internal static bool IsInLocalFunctionTypeParameterScope(int position, LocalFunc } var firstNameToken = localFunction.Identifier; - var typeParams = localFunction.TypeParameterList; - var firstPostNameToken = typeParams == null ? localFunction.ParameterList.OpenParenToken : typeParams.LessThanToken; + var firstPostNameToken = localFunction.TypeParameterList.LessThanToken; // Scope does not include local function name. return !IsBetweenTokens(position, firstNameToken, firstPostNameToken); From 237cbbd2899196c64725ff967fb089ba63a8e050 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Fri, 18 Mar 2022 10:27:57 -0700 Subject: [PATCH 7/8] Remove redundant call --- .../CSharp/Portable/Compilation/MemberSemanticModel.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs index dc3500ea64cec..670764ca4d3f0 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs @@ -355,8 +355,7 @@ private static Binder GetEnclosingBinderInternalWithinRoot(SyntaxNode node, int if (ownerOfTypeParametersInScope != null) { LocalFunctionSymbol function = GetDeclaredLocalFunction(binder, ownerOfTypeParametersInScope.Identifier); - if ((object)function != null - && LookupPosition.IsInLocalFunctionTypeParameterScope(position, function.Syntax)) + if ((object)function != null) { binder = function.WithTypeParametersBinder; } From 55a19d6e0198467954198b4aa23b25416a3daa94 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 21 Mar 2022 12:11:24 -0700 Subject: [PATCH 8/8] remove unused type parameter --- .../CSharp/Test/Semantic/Semantics/LambdaTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 0aff75783cf9e..18b165e3e642b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -6522,16 +6522,16 @@ void M() var _ = void (parameter parameter) => throw null; } - void M2(parameter parameter) => throw null; + void M2(parameter parameter) => throw null; } "); comp.VerifyDiagnostics( // (6,23): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) // var _ = void (parameter parameter) => throw null; Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(6, 23), - // (9,25): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) - // void M2(parameter parameter) => throw null; - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(9, 25) + // (9,13): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) + // void M2(parameter parameter) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(9, 13) ); } }