From c79061bc77eaef0f94f7e9c4cf4bdb288c6268ea Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Mon, 20 Nov 2017 16:37:48 -0800 Subject: [PATCH] Return attribute operations as operation blocks for operation block analyzers Fixes #23309 --- Compilers.sln | 10 +- .../CSharpDeclarationComputer.cs | 58 +++++- .../Diagnostics/DiagnosticAnalyzerTests.cs | 170 ++++++++++++++++++ .../VisualBasicDeclarationComputer.vb | 57 ++++-- .../Diagnostics/DiagnosticAnalyzerTests.vb | 129 +++++++++++++ .../Desktop/CommonDiagnosticAnalyzers.cs | 52 ++++++ 6 files changed, 456 insertions(+), 20 deletions(-) diff --git a/Compilers.sln b/Compilers.sln index 072f7973453..72df7ecf7f7 100644 --- a/Compilers.sln +++ b/Compilers.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26730.16 +VisualStudioVersion = 15.0.27102.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeAnalysisTest", "src\Compilers\Core\CodeAnalysisTest\CodeAnalysisTest.csproj", "{A4C99B85-765C-4C65-9C2A-BB609AAB09E6}" EndProject @@ -146,6 +146,10 @@ Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "BasicCodeStyleFixes", "src\ EndProject Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "BasicCodeStyleTests", "src\CodeStyle\VisualBasic\Tests\BasicCodeStyleTests.vbproj", "{E512C6C1-F085-4AD7-B0D9-E8F1A0A2A510}" EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "CSharpAnalyzerDriver", "src\Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.shproj", "{54E08BF5-F819-404F-A18D-0AB9EA81EA04}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "BasicAnalyzerDriver", "src\Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.shproj", "{E8F0BAA5-7327-43D1-9A51-644E81AE55F1}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Compilers\Core\CommandLine\CommandLine.projitems*{06b26dcb-7a12-48ef-ae50-708593abd05f}*SharedItemsImports = 4 @@ -156,6 +160,7 @@ Global src\Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{2523d0e6-df32-4a3e-8ae0-a19bffae2ef6}*SharedItemsImports = 4 src\Compilers\Server\ServerShared\ServerShared.projitems*{32691768-af9c-4cae-9d0f-10721091b9aa}*SharedItemsImports = 13 src\Compilers\Core\CommandLine\CommandLine.projitems*{4b45ca0c-03a0-400f-b454-3d4bcb16af38}*SharedItemsImports = 4 + src\Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{54e08bf5-f819-404f-a18d-0ab9ea81ea04}*SharedItemsImports = 13 src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 4 src\Compilers\Core\CommandLine\CommandLine.projitems*{7ad4fe65-9a30-41a6-8004-aa8f89bcb7f3}*SharedItemsImports = 4 src\Compilers\Core\CommandLine\CommandLine.projitems*{9508f118-f62e-4c16-a6f4-7c3b56e166ad}*SharedItemsImports = 4 @@ -168,6 +173,7 @@ Global src\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{d0bc9be7-24f6-40ca-8dc6-fcb93bd44b34}*SharedItemsImports = 13 src\Dependencies\CodeAnalysis.Debugging\Microsoft.CodeAnalysis.Debugging.projitems*{d73adf7d-2c1c-42ae-b2ab-edc9497e4b71}*SharedItemsImports = 13 src\Compilers\Core\CommandLine\CommandLine.projitems*{e58ee9d7-1239-4961-a0c1-f9ec3952c4c1}*SharedItemsImports = 4 + src\Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{e8f0baa5-7327-43d1-9a51-644e81ae55f1}*SharedItemsImports = 13 EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -469,6 +475,8 @@ Global {2531A8C4-97DD-47BC-A79C-B7846051E137} = {B20208C3-D3A6-4020-A274-6BE3786D29FB} {0141285D-8F6C-42C7-BAF3-3C0CCD61C716} = {B20208C3-D3A6-4020-A274-6BE3786D29FB} {E512C6C1-F085-4AD7-B0D9-E8F1A0A2A510} = {B20208C3-D3A6-4020-A274-6BE3786D29FB} + {54E08BF5-F819-404F-A18D-0AB9EA81EA04} = {32A48625-F0AD-419D-828B-A50BDABA38EA} + {E8F0BAA5-7327-43D1-9A51-644E81AE55F1} = {C65C6143-BED3-46E6-869E-9F0BE6E84C37} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6F599E08-A9EA-4FAA-897F-5D824B0210E6} diff --git a/src/Compilers/CSharp/CSharpAnalyzerDriver/CSharpDeclarationComputer.cs b/src/Compilers/CSharp/CSharpAnalyzerDriver/CSharpDeclarationComputer.cs index afdb3fde119..cf34da16f53 100644 --- a/src/Compilers/CSharp/CSharpAnalyzerDriver/CSharpDeclarationComputer.cs +++ b/src/Compilers/CSharp/CSharpAnalyzerDriver/CSharpDeclarationComputer.cs @@ -82,7 +82,8 @@ private static bool InvalidLevel(int? level) { var t = (TypeDeclarationSyntax)node; foreach (var decl in t.Members) ComputeDeclarations(model, decl, shouldSkip, getSymbol, builder, newLevel, cancellationToken); - builder.Add(GetDeclarationInfo(model, node, getSymbol, cancellationToken)); + var attributes = GetAttributes(t); + builder.Add(GetDeclarationInfo(model, node, getSymbol, attributes, cancellationToken)); return; } @@ -90,20 +91,24 @@ private static bool InvalidLevel(int? level) { var t = (EnumDeclarationSyntax)node; foreach (var decl in t.Members) ComputeDeclarations(model, decl, shouldSkip, getSymbol, builder, newLevel, cancellationToken); - builder.Add(GetDeclarationInfo(model, node, getSymbol, cancellationToken)); + var attributes = GetAttributes(t.AttributeLists); + builder.Add(GetDeclarationInfo(model, node, getSymbol, attributes, cancellationToken)); return; } case SyntaxKind.EnumMemberDeclaration: { var t = (EnumMemberDeclarationSyntax)node; - builder.Add(GetDeclarationInfo(model, node, getSymbol, t.EqualsValue, cancellationToken)); + var attributes = GetAttributes(t.AttributeLists); + var codeBlocks = SpecializedCollections.SingletonEnumerable(t.EqualsValue).Concat(attributes); + builder.Add(GetDeclarationInfo(model, node, getSymbol, codeBlocks, cancellationToken)); return; } case SyntaxKind.DelegateDeclaration: { - builder.Add(GetDeclarationInfo(model, node, getSymbol, cancellationToken)); + var attributes = GetAttributes(((DelegateDeclarationSyntax)node).AttributeLists); + builder.Add(GetDeclarationInfo(model, node, getSymbol, attributes, cancellationToken)); return; } @@ -111,7 +116,8 @@ private static bool InvalidLevel(int? level) { var t = (EventDeclarationSyntax)node; foreach (var decl in t.AccessorList.Accessors) ComputeDeclarations(model, decl, shouldSkip, getSymbol, builder, newLevel, cancellationToken); - builder.Add(GetDeclarationInfo(model, node, getSymbol, cancellationToken)); + var attributes = GetAttributes(t.AttributeLists); + builder.Add(GetDeclarationInfo(model, node, getSymbol, attributes, cancellationToken)); return; } @@ -119,9 +125,11 @@ private static bool InvalidLevel(int? level) case SyntaxKind.FieldDeclaration: { var t = (BaseFieldDeclarationSyntax)node; + var attributes = GetAttributes(t.AttributeLists); foreach (var decl in t.Declaration.Variables) { - builder.Add(GetDeclarationInfo(model, decl, getSymbol, decl.Initializer, cancellationToken)); + var codeBlocks = SpecializedCollections.SingletonEnumerable(decl.Initializer).Concat(attributes); + builder.Add(GetDeclarationInfo(model, decl, getSymbol, codeBlocks, cancellationToken)); } return; @@ -152,7 +160,9 @@ private static bool InvalidLevel(int? level) ComputeDeclarations(model, t.ExpressionBody, shouldSkip, getSymbol, builder, levelsToCompute, cancellationToken); } - builder.Add(GetDeclarationInfo(model, node, getSymbol, cancellationToken, t.Initializer)); + var attributes = GetAttributes(t.AttributeLists); + var codeBlocks = SpecializedCollections.SingletonEnumerable(t.Initializer).Concat(attributes); + builder.Add(GetDeclarationInfo(model, node, getSymbol, codeBlocks, cancellationToken)); return; } @@ -173,6 +183,8 @@ private static bool InvalidLevel(int? level) } var codeBlocks = t.ParameterList != null ? t.ParameterList.Parameters.Select(p => p.Default) : SpecializedCollections.EmptyEnumerable(); + var attributes = GetAttributes(t.AttributeLists); + codeBlocks = codeBlocks.Concat(attributes); builder.Add(GetDeclarationInfo(model, node, getSymbol, codeBlocks, cancellationToken)); return; @@ -187,6 +199,7 @@ private static bool InvalidLevel(int? level) var blocks = ArrayBuilder.GetInstance(); blocks.AddIfNotNull(t.Body); blocks.AddIfNotNull(t.ExpressionBody); + blocks.AddRange(GetAttributes(t.AttributeLists)); builder.Add(GetDeclarationInfo(model, node, getSymbol, blocks, cancellationToken)); blocks.Free(); @@ -215,6 +228,8 @@ private static bool InvalidLevel(int? level) codeBlocks = codeBlocks.Concat(expressionBody); } + codeBlocks = codeBlocks.Concat(GetAttributes(t.AttributeLists)); + builder.Add(GetDeclarationInfo(model, node, getSymbol, codeBlocks, cancellationToken)); return; } @@ -231,6 +246,35 @@ private static bool InvalidLevel(int? level) } } + private static IEnumerable GetAttributes(TypeDeclarationSyntax typeDeclaration) + { + switch (typeDeclaration.Kind()) + { + case SyntaxKind.ClassDeclaration: + return GetAttributes(((ClassDeclarationSyntax)typeDeclaration).AttributeLists); + + case SyntaxKind.StructDeclaration: + return GetAttributes(((StructDeclarationSyntax)typeDeclaration).AttributeLists); + + case SyntaxKind.InterfaceDeclaration: + return GetAttributes(((InterfaceDeclarationSyntax)typeDeclaration).AttributeLists); + + default: + return SpecializedCollections.EmptyEnumerable(); + } + } + + private static IEnumerable GetAttributes(SyntaxList attributeLists) + { + foreach (var attributeList in attributeLists) + { + foreach (var attribute in attributeList.Attributes) + { + yield return attribute; + } + } + } + private static DeclarationInfo GetExpressionBodyDeclarationInfo( BasePropertyDeclarationSyntax declarationWithExpressionBody, ArrowExpressionClauseSyntax expressionBody, diff --git a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs index fa12510e08e..c31b923ec1c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs @@ -2108,5 +2108,175 @@ private void ReportDiagnosticsCore(Action addDiagnostic, Location lo addDiagnostic(diagnostic); } } + + [Fact, WorkItem(23309, "https://github.com/dotnet/roslyn/issues/23309")] + public void TestFieldReferenceAnalyzer_InAttributes() + { + string source = @" +using System; + +internal class MyAttribute : Attribute +{ + public MyAttribute(int f) { } +} + +internal interface MyInterface +{ + event EventHandler MyEvent; +} + +[MyAttribute(FieldForClass)] +internal class C : MyInterface +{ + private const int FieldForClass = 1, FieldForStruct = 2, FieldForInterface = 3, FieldForField = 4, FieldForMethod = 5, + FieldForEnum = 6, FieldForEnumMember = 7, FieldForDelegate = 8, FieldForEventField = 9, FieldForEvent = 10, + FieldForAddHandler = 11, FieldForRemoveHandler = 12, FieldForProperty = 13, FieldForPropertyGetter = 14, FieldForPropertySetter = 15, + FieldForIndexer = 16, FieldForIndexerGetter = 17, FieldForIndexerSetter = 18, FieldForExpressionBodiedMethod = 19, FieldForExpressionBodiedProperty = 20; + + [MyAttribute(FieldForStruct)] + private struct S { } + + [MyAttribute(FieldForInterface)] + private interface I { } + + [MyAttribute(FieldForField)] + private int field2 = 0, field3 = 0; + + [MyAttribute(FieldForMethod)] + private void M1() { } + + [MyAttribute(FieldForEnum)] + private enum E + { + [MyAttribute(FieldForEnumMember)] + F = 0 + } + + [MyAttribute(FieldForDelegate)] + public delegate void Delegate(); + + [MyAttribute(FieldForEventField)] + public event Delegate MyEvent; + + [MyAttribute(FieldForEvent)] + event EventHandler MyInterface.MyEvent + { + [MyAttribute(FieldForAddHandler)] + add + { + } + [MyAttribute(FieldForRemoveHandler)] + remove + { + } + } + + [MyAttribute(FieldForProperty)] + private int P1 + { + [MyAttribute(FieldForPropertyGetter)] + get; + [MyAttribute(FieldForPropertySetter)] + set; + } + + [MyAttribute(FieldForIndexer)] + private int this[int index] + { + [MyAttribute(FieldForIndexerGetter)] + get { return 0; } + [MyAttribute(FieldForIndexerSetter)] + set { } + } + + [MyAttribute(FieldForExpressionBodiedMethod)] + private int M2 => 0; + + [MyAttribute(FieldForExpressionBodiedProperty)] + private int P2 => 0; +} +"; + var tree = CSharpSyntaxTree.ParseText(source); + var compilation = CreateCompilationWithMscorlib45(new[] { tree }); + compilation.VerifyDiagnostics( + // (45,27): warning CS0067: The event 'C.MyEvent' is never used + // public event Delegate MyEvent; + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "MyEvent").WithArguments("C.MyEvent").WithLocation(45, 27), + // (29,17): warning CS0414: The field 'C.field2' is assigned but its value is never used + // private int field2 = 0, field3 = 0; + Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "field2").WithArguments("C.field2").WithLocation(29, 17), + // (29,29): warning CS0414: The field 'C.field3' is assigned but its value is never used + // private int field2 = 0, field3 = 0; + Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "field3").WithArguments("C.field3").WithLocation(29, 29)); + + // Test RegisterOperationBlockAction + TestFieldReferenceAnalyzer_InAttributes_Core(compilation, doOperationBlockAnalysis: true); + + // Test RegisterOperationAction + TestFieldReferenceAnalyzer_InAttributes_Core(compilation, doOperationBlockAnalysis: false); + } + + private static void TestFieldReferenceAnalyzer_InAttributes_Core(Compilation compilation, bool doOperationBlockAnalysis) + { + var analyzers = new DiagnosticAnalyzer[] { new FieldReferenceOperationAnalyzer(doOperationBlockAnalysis) }; + compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, + Diagnostic("ID", "FieldForClass").WithArguments("FieldForClass", "1").WithLocation(14, 14), + Diagnostic("ID", "FieldForStruct").WithArguments("FieldForStruct", "2").WithLocation(22, 18), + Diagnostic("ID", "FieldForInterface").WithArguments("FieldForInterface", "3").WithLocation(25, 18), + Diagnostic("ID", "FieldForField").WithArguments("FieldForField", "4").WithLocation(28, 18), + Diagnostic("ID", "FieldForField").WithArguments("FieldForField", "4").WithLocation(28, 18), + Diagnostic("ID", "FieldForMethod").WithArguments("FieldForMethod", "5").WithLocation(31, 18), + Diagnostic("ID", "FieldForEnum").WithArguments("FieldForEnum", "6").WithLocation(34, 18), + Diagnostic("ID", "FieldForEnumMember").WithArguments("FieldForEnumMember", "7").WithLocation(37, 22), + Diagnostic("ID", "FieldForDelegate").WithArguments("FieldForDelegate", "8").WithLocation(41, 18), + Diagnostic("ID", "FieldForEventField").WithArguments("FieldForEventField", "9").WithLocation(44, 18), + Diagnostic("ID", "FieldForEvent").WithArguments("FieldForEvent", "10").WithLocation(47, 18), + Diagnostic("ID", "FieldForAddHandler").WithArguments("FieldForAddHandler", "11").WithLocation(50, 22), + Diagnostic("ID", "FieldForRemoveHandler").WithArguments("FieldForRemoveHandler", "12").WithLocation(54, 22), + Diagnostic("ID", "FieldForProperty").WithArguments("FieldForProperty", "13").WithLocation(60, 18), + Diagnostic("ID", "FieldForPropertyGetter").WithArguments("FieldForPropertyGetter", "14").WithLocation(63, 22), + Diagnostic("ID", "FieldForPropertySetter").WithArguments("FieldForPropertySetter", "15").WithLocation(65, 22), + Diagnostic("ID", "FieldForIndexer").WithArguments("FieldForIndexer", "16").WithLocation(69, 18), + Diagnostic("ID", "FieldForIndexerGetter").WithArguments("FieldForIndexerGetter", "17").WithLocation(72, 22), + Diagnostic("ID", "FieldForIndexerSetter").WithArguments("FieldForIndexerSetter", "18").WithLocation(74, 22), + Diagnostic("ID", "FieldForExpressionBodiedMethod").WithArguments("FieldForExpressionBodiedMethod", "19").WithLocation(78, 18), + Diagnostic("ID", "FieldForExpressionBodiedProperty").WithArguments("FieldForExpressionBodiedProperty", "20").WithLocation(81, 18)); + } + + [Fact, WorkItem(23309, "https://github.com/dotnet/roslyn/issues/23309")] + public void TestFieldReferenceAnalyzer_InConstructorInitializer() + { + string source = @" +internal class Base +{ + protected Base(int i) { } +} + +internal class Derived : Base +{ + private const int Field = 0; + + public Derived() : base(Field) + { + } +}"; + + var tree = CSharpSyntaxTree.ParseText(source); + var compilation = CreateCompilationWithMscorlib45(new[] { tree }); + compilation.VerifyDiagnostics(); + + // Test RegisterOperationBlockAction + TestFieldReferenceAnalyzer_InConstructorInitializer_Core(compilation, doOperationBlockAnalysis: true); + + // Test RegisterOperationAction + TestFieldReferenceAnalyzer_InConstructorInitializer_Core(compilation, doOperationBlockAnalysis: false); + } + + private static void TestFieldReferenceAnalyzer_InConstructorInitializer_Core(Compilation compilation, bool doOperationBlockAnalysis) + { + var analyzers = new DiagnosticAnalyzer[] { new FieldReferenceOperationAnalyzer(doOperationBlockAnalysis) }; + compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, + Diagnostic("ID", "Field").WithArguments("Field", "0").WithLocation(11, 29)); + } } } diff --git a/src/Compilers/VisualBasic/BasicAnalyzerDriver/VisualBasicDeclarationComputer.vb b/src/Compilers/VisualBasic/BasicAnalyzerDriver/VisualBasicDeclarationComputer.vb index 4a0d676bbec..ecdf34028e0 100644 --- a/src/Compilers/VisualBasic/BasicAnalyzerDriver/VisualBasicDeclarationComputer.vb +++ b/src/Compilers/VisualBasic/BasicAnalyzerDriver/VisualBasicDeclarationComputer.vb @@ -61,16 +61,26 @@ Namespace Microsoft.CodeAnalysis.VisualBasic For Each decl In t.Members ComputeDeclarationsCore(model, decl, shouldSkip, getSymbol, builder, newLevel, cancellationToken) Next - builder.Add(GetDeclarationInfo(model, node, getSymbol, cancellationToken)) + Dim attributes = GetAttributes(t.EnumStatement.AttributeLists) + builder.Add(GetDeclarationInfo(model, node, getSymbol, attributes, cancellationToken)) + Return + Case SyntaxKind.EnumStatement + Dim t = CType(node, EnumStatementSyntax) + Dim attributes = GetAttributes(t.AttributeLists) + builder.Add(GetDeclarationInfo(model, node, getSymbol, attributes, cancellationToken)) Return Case SyntaxKind.EnumMemberDeclaration Dim t = CType(node, EnumMemberDeclarationSyntax) - builder.Add(GetDeclarationInfo(model, node, getSymbol, t.Initializer, cancellationToken)) + Dim attributes = GetAttributes(t.AttributeLists) + Dim codeBlocks = SpecializedCollections.SingletonEnumerable(Of SyntaxNode)(t.Initializer).Concat(attributes) + builder.Add(GetDeclarationInfo(model, node, getSymbol, codeBlocks, cancellationToken)) Return Case SyntaxKind.DelegateSubStatement, SyntaxKind.DelegateFunctionStatement Dim t = CType(node, DelegateStatementSyntax) Dim paramInitializers As IEnumerable(Of SyntaxNode) = GetParameterInitializers(t.ParameterList) - builder.Add(GetDeclarationInfo(model, node, getSymbol, paramInitializers, cancellationToken)) + Dim attributes = GetAttributes(t.AttributeLists) + Dim codeBlocks = paramInitializers.Concat(attributes) + builder.Add(GetDeclarationInfo(model, node, getSymbol, codeBlocks, cancellationToken)) Return Case SyntaxKind.EventBlock Dim t = CType(node, EventBlockSyntax) @@ -78,19 +88,25 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ComputeDeclarationsCore(model, decl, shouldSkip, getSymbol, builder, newLevel, cancellationToken) Next Dim eventInitializers = GetParameterInitializers(t.EventStatement.ParameterList) - builder.Add(GetDeclarationInfo(model, node, getSymbol, eventInitializers, cancellationToken)) + Dim attributes = GetAttributes(t.EventStatement.AttributeLists) + Dim codeBlocks = eventInitializers.Concat(attributes) + builder.Add(GetDeclarationInfo(model, node, getSymbol, codeBlocks, cancellationToken)) Return Case SyntaxKind.EventStatement Dim t = CType(node, EventStatementSyntax) Dim paramInitializers = GetParameterInitializers(t.ParameterList) - builder.Add(GetDeclarationInfo(model, node, getSymbol, paramInitializers, cancellationToken)) + Dim attributes = GetAttributes(t.AttributeLists) + Dim codeBlocks = paramInitializers.Concat(attributes) + builder.Add(GetDeclarationInfo(model, node, getSymbol, codeBlocks, cancellationToken)) Return Case SyntaxKind.FieldDeclaration Dim t = CType(node, FieldDeclarationSyntax) + Dim attributes = GetAttributes(t.AttributeLists) For Each decl In t.Declarators Dim initializer = GetInitializerNode(decl) + Dim codeBlocks = SpecializedCollections.SingletonEnumerable(initializer).Concat(attributes) For Each identifier In decl.Names - builder.Add(GetDeclarationInfo(model, identifier, getSymbol, initializer, cancellationToken)) + builder.Add(GetDeclarationInfo(model, identifier, getSymbol, codeBlocks, cancellationToken)) Next Next Return @@ -100,12 +116,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ComputeDeclarationsCore(model, decl, shouldSkip, getSymbol, builder, newLevel, cancellationToken) Next Dim propertyInitializers = GetInitializerNodes(t.PropertyStatement) - builder.Add(GetDeclarationInfo(model, node, getSymbol, propertyInitializers, cancellationToken)) + Dim attributes = GetAttributes(t.PropertyStatement.AttributeLists) + Dim codeBlocks = propertyInitializers.Concat(attributes) + builder.Add(GetDeclarationInfo(model, node, getSymbol, codeBlocks, cancellationToken)) Return Case SyntaxKind.PropertyStatement Dim t = CType(node, PropertyStatementSyntax) Dim propertyInitializers = GetInitializerNodes(t) - builder.Add(GetDeclarationInfo(model, node, getSymbol, propertyInitializers, cancellationToken)) + Dim attributes = GetAttributes(t.AttributeLists) + Dim codeBlocks = propertyInitializers.Concat(attributes) + builder.Add(GetDeclarationInfo(model, node, getSymbol, codeBlocks, cancellationToken)) Return Case SyntaxKind.CompilationUnit Dim t = CType(node, CompilationUnitSyntax) @@ -119,20 +139,23 @@ Namespace Microsoft.CodeAnalysis.VisualBasic For Each decl In typeBlock.Members ComputeDeclarationsCore(model, decl, shouldSkip, getSymbol, builder, newLevel, cancellationToken) Next - builder.Add(GetDeclarationInfo(model, node, getSymbol, cancellationToken)) + Dim attributes = GetAttributes(typeBlock.BlockStatement.AttributeLists) + builder.Add(GetDeclarationInfo(model, node, getSymbol, attributes, cancellationToken)) Return End If Dim typeStatement = TryCast(node, TypeStatementSyntax) If typeStatement IsNot Nothing Then - builder.Add(GetDeclarationInfo(model, node, getSymbol, cancellationToken)) + Dim attributes = GetAttributes(typeStatement.AttributeLists) + builder.Add(GetDeclarationInfo(model, node, getSymbol, attributes, cancellationToken)) Return End If Dim methodBlock = TryCast(node, MethodBlockBaseSyntax) If methodBlock IsNot Nothing Then Dim paramInitializers = GetParameterInitializers(methodBlock.BlockStatement.ParameterList) - Dim codeBlocks = paramInitializers.Concat(methodBlock) + Dim attributes = GetAttributes(methodBlock.BlockStatement.AttributeLists) + Dim codeBlocks = paramInitializers.Concat(attributes) builder.Add(GetDeclarationInfo(model, node, getSymbol, codeBlocks, cancellationToken)) Return End If @@ -140,7 +163,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim methodStatement = TryCast(node, MethodBaseSyntax) If methodStatement IsNot Nothing Then Dim paramInitializers = GetParameterInitializers(methodStatement.ParameterList) - builder.Add(GetDeclarationInfo(model, node, getSymbol, paramInitializers, cancellationToken)) + Dim attributes = GetAttributes(methodStatement.AttributeLists) + Dim codeBlocks = paramInitializers.Concat(attributes) + builder.Add(GetDeclarationInfo(model, node, getSymbol, codeBlocks, cancellationToken)) Return End If @@ -148,6 +173,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Select End Sub + Private Shared Iterator Function GetAttributes(attributeLists As SyntaxList(Of AttributeListSyntax)) As IEnumerable(Of SyntaxNode) + For Each attributeList In attributeLists + For Each attribute In attributeList.Attributes + Yield attribute + Next + Next + End Function + Private Shared Function GetParameterInitializers(parameterList As ParameterListSyntax) As IEnumerable(Of SyntaxNode) Return If(parameterList IsNot Nothing, parameterList.Parameters.Select(Function(p) p.Default), diff --git a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb index 39d963e7c76..bcc0017efa8 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb @@ -1261,5 +1261,134 @@ End Class Dim diag = Diagnostic(diagnosticId, squiggledText).WithArguments(arguments).WithLocation(line, column) builder.Add(diag) End Sub + + + Public Sub TestFieldReferenceAnalyzer_InAttributes() + Dim source = + + +Friend Class C + Implements MyInterface + Friend Const FieldForClass As Integer = 1, FieldForStruct As Integer = 2, FieldForInterface As Integer = 3, FieldForField As Integer = 4, FieldForMethod As Integer = 5, + FieldForEnum As Integer = 6, FieldForEnumMember As Integer = 7, FieldForDelegateSub As Integer = 8, FieldForDelegateFunction As Integer = 9, FieldForEventField As Integer = 10, + FieldForEvent As Integer = 11, FieldForAddHandler As Integer = 12, FieldForRemoveHandler As Integer = 13, FieldForRaiseHandler As Integer = 14, FieldForProperty As Integer = 15, + FieldForPropertyGetter As Integer = 16, FieldForPropertySetter As Integer = 17, FieldForIndexer As Integer = 18, FieldForIndexerGetter As Integer = 19, FieldForIndexerSetter As Integer = 20 + + + Private Structure S + End Structure + + + Private Interface I + End Interface + + + Private field2 As Integer = 0, field3 As Integer = 0 + + + Private Sub M1() + End Sub + + + Private Enum E + + F = 0 + End Enum + + + Public Delegate Sub [Delegate]() + + + Public Delegate Function Delegate2() + + + Public Event MyEvent As [Delegate] + + + Private Custom Event MyEvent2 As EventHandler Implements MyInterface.MyEvent + + AddHandler(ByVal value As EventHandler) + End AddHandler + + RemoveHandler(ByVal value As EventHandler) + End RemoveHandler + + RaiseEvent() + End RaiseEvent + End Event + + + Private Property P1() As Integer + + Get + Return 0 + End Get + + Set + End Set + End Property + + + Default Property Item(index As Integer) As Integer + + Get + Return 0 + End Get + + Set + End Set + End Property +End Class +]]> + + + + Dim comp = CreateCompilationWithMscorlibAndVBRuntime(source) + comp.VerifyDiagnostics() + + ' Test RegisterOperationBlockAction + TestFieldReferenceAnalyzer_InAttributes_Core(comp, doOperationBlockAnalysis:=True) + + ' Test RegisterOperationAction + TestFieldReferenceAnalyzer_InAttributes_Core(comp, doOperationBlockAnalysis:=False) + End Sub + + Private Shared Sub TestFieldReferenceAnalyzer_InAttributes_Core(comp As Compilation, doOperationBlockAnalysis As Boolean) + comp.VerifyAnalyzerDiagnostics({New FieldReferenceOperationAnalyzer(doOperationBlockAnalysis)}, Nothing, Nothing, False, + Diagnostic("ID", "C.FieldForClass").WithArguments("FieldForClass", "1").WithLocation(13, 14), + Diagnostic("ID", "FieldForStruct").WithArguments("FieldForStruct", "2").WithLocation(21, 18), + Diagnostic("ID", "FieldForInterface").WithArguments("FieldForInterface", "3").WithLocation(25, 18), + Diagnostic("ID", "FieldForField").WithArguments("FieldForField", "4").WithLocation(29, 18), + Diagnostic("ID", "FieldForField").WithArguments("FieldForField", "4").WithLocation(29, 18), + Diagnostic("ID", "FieldForMethod").WithArguments("FieldForMethod", "5").WithLocation(32, 18), + Diagnostic("ID", "FieldForEnum").WithArguments("FieldForEnum", "6").WithLocation(36, 18), + Diagnostic("ID", "FieldForEnumMember").WithArguments("FieldForEnumMember", "7").WithLocation(38, 22), + Diagnostic("ID", "FieldForDelegateSub").WithArguments("FieldForDelegateSub", "8").WithLocation(42, 18), + Diagnostic("ID", "FieldForDelegateFunction").WithArguments("FieldForDelegateFunction", "9").WithLocation(45, 18), + Diagnostic("ID", "FieldForEventField").WithArguments("FieldForEventField", "10").WithLocation(48, 18), + Diagnostic("ID", "FieldForEvent").WithArguments("FieldForEvent", "11").WithLocation(51, 18), + Diagnostic("ID", "FieldForAddHandler").WithArguments("FieldForAddHandler", "12").WithLocation(53, 22), + Diagnostic("ID", "FieldForRemoveHandler").WithArguments("FieldForRemoveHandler", "13").WithLocation(56, 22), + Diagnostic("ID", "FieldForRaiseHandler").WithArguments("FieldForRaiseHandler", "14").WithLocation(59, 22), + Diagnostic("ID", "FieldForProperty").WithArguments("FieldForProperty", "15").WithLocation(64, 18), + Diagnostic("ID", "FieldForPropertyGetter").WithArguments("FieldForPropertyGetter", "16").WithLocation(66, 22), + Diagnostic("ID", "FieldForPropertySetter").WithArguments("FieldForPropertySetter", "17").WithLocation(70, 22), + Diagnostic("ID", "FieldForIndexer").WithArguments("FieldForIndexer", "18").WithLocation(75, 18), + Diagnostic("ID", "FieldForIndexerGetter").WithArguments("FieldForIndexerGetter", "19").WithLocation(77, 22), + Diagnostic("ID", "FieldForIndexerSetter").WithArguments("FieldForIndexerSetter", "20").WithLocation(81, 22)) + End Sub End Class End Namespace diff --git a/src/Test/Utilities/Desktop/CommonDiagnosticAnalyzers.cs b/src/Test/Utilities/Desktop/CommonDiagnosticAnalyzers.cs index 2692e7fdd99..6662ed5771f 100644 --- a/src/Test/Utilities/Desktop/CommonDiagnosticAnalyzers.cs +++ b/src/Test/Utilities/Desktop/CommonDiagnosticAnalyzers.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; using Xunit; @@ -868,6 +869,57 @@ public override void Initialize(AnalysisContext context) } } + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class FieldReferenceOperationAnalyzer : DiagnosticAnalyzer + { + private readonly bool _doOperationBlockAnalysis; + public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor( + "ID", + "Title", + "Field {0} = {1}", + "Category", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public FieldReferenceOperationAnalyzer(bool doOperationBlockAnalysis) + { + _doOperationBlockAnalysis = doOperationBlockAnalysis; + } + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); + public override void Initialize(AnalysisContext context) + { + if (_doOperationBlockAnalysis) + { + context.RegisterOperationBlockAction(operationBlockAnalysisContext => + { + foreach (var operationBlock in operationBlockAnalysisContext.OperationBlocks) + { + foreach (var operation in operationBlock.DescendantsAndSelf().OfType()) + { + AnalyzerFieldReferenceOperation(operation, operationBlockAnalysisContext.ReportDiagnostic); + } + } + }); + } + else + { + context.RegisterOperationAction(AnalyzerOperation, OperationKind.FieldReference); + } + } + + private static void AnalyzerOperation(OperationAnalysisContext operationAnalysisContext) + { + AnalyzerFieldReferenceOperation((IFieldReferenceOperation)operationAnalysisContext.Operation, operationAnalysisContext.ReportDiagnostic); + } + + private static void AnalyzerFieldReferenceOperation(IFieldReferenceOperation operation, Action reportDiagnostic) + { + var diagnostic = Diagnostic.Create(Descriptor, operation.Syntax.GetLocation(), operation.Field.Name, operation.Field.ConstantValue); + reportDiagnostic(diagnostic); + } + } + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public class GeneratedCodeAnalyzer : DiagnosticAnalyzer { -- GitLab