Skip to content

Commit

Permalink
Merge pull request #6779 from CollinAlpert/issue_336
Browse files Browse the repository at this point in the history
Add analyzer for SemanticModel.GetDeclaredSymbol
  • Loading branch information
mavasani authored Oct 30, 2023
2 parents 7d01f5f + da0b5d4 commit b40db00
Show file tree
Hide file tree
Showing 22 changed files with 604 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
; Please do not edit this file manually, it should only be updated through code fix application.

### New Rules

Rule ID | Category | Severity | Notes
--------|----------|----------|-------
RS1039 | MicrosoftCodeAnalysisCorrectness | Warning | SemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzer
RS1040 | MicrosoftCodeAnalysisCorrectness | Warning | CSharpSemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzer
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Microsoft.CodeAnalysis.Analyzers;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;

namespace Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers
{
using static CodeAnalysisDiagnosticsResources;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class CSharpSemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzer : DiagnosticAnalyzer
{
internal static readonly DiagnosticDescriptor DiagnosticDescriptor = new(
DiagnosticIds.SemanticModelGetDeclaredSymbolAlwaysReturnsNull,
CreateLocalizableResourceString(nameof(SemanticModelGetDeclaredSymbolAlwaysReturnsNullTitle)),
CreateLocalizableResourceString(nameof(SemanticModelGetDeclaredSymbolAlwaysReturnsNullMessage)),
DiagnosticCategory.MicrosoftCodeAnalysisCorrectness,
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: CreateLocalizableResourceString(nameof(SemanticModelGetDeclaredSymbolAlwaysReturnsNullDescription)),
helpLinkUri: null,
customTags: WellKnownDiagnosticTagsExtensions.Telemetry);

internal static readonly DiagnosticDescriptor FieldDiagnosticDescriptor = new(
DiagnosticIds.SemanticModelGetDeclaredSymbolAlwaysReturnsNullForField,
CreateLocalizableResourceString(nameof(SemanticModelGetDeclaredSymbolAlwaysReturnsNullTitle)),
CreateLocalizableResourceString(nameof(SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldMessage)),
DiagnosticCategory.MicrosoftCodeAnalysisCorrectness,
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: CreateLocalizableResourceString(nameof(SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldDescription)),
helpLinkUri: null,
customTags: WellKnownDiagnosticTagsExtensions.Telemetry);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(DiagnosticDescriptor, FieldDiagnosticDescriptor);

public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterCompilationStartAction(static context =>
{
var typeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation);
IMethodSymbol? getDeclaredSymbolMethod;
if (!typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCSharpCSharpExtensions, out var csharpExtensions)
|| !typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisModelExtensions, out var modelExtensions)
|| !typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCSharpSyntaxBaseFieldDeclarationSyntax, out var baseFieldDeclaration)
|| !typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisSyntaxNode, out var syntaxNode)
|| (getDeclaredSymbolMethod = (IMethodSymbol?)modelExtensions.GetMembers(nameof(ModelExtensions.GetDeclaredSymbol)).FirstOrDefault(m => m is IMethodSymbol { Parameters.Length: >= 2 })) is null)
{
return;
}

var allowedTypes = csharpExtensions.GetMembers(nameof(CSharpExtensions.GetDeclaredSymbol))
.OfType<IMethodSymbol>()
.Where(m => m.Parameters.Length >= 2)
.Select(m => m.Parameters[1].Type);

context.RegisterOperationAction(ctx => AnalyzeInvocation(ctx, getDeclaredSymbolMethod, allowedTypes, baseFieldDeclaration, syntaxNode), OperationKind.Invocation);
});
}

private static void AnalyzeInvocation(OperationAnalysisContext context, IMethodSymbol getDeclaredSymbolMethod, IEnumerable<ITypeSymbol> allowedTypes, INamedTypeSymbol baseFieldDeclarationType, INamedTypeSymbol syntaxNodeType)
{
var invocation = (IInvocationOperation)context.Operation;
if (SymbolEqualityComparer.Default.Equals(invocation.TargetMethod, getDeclaredSymbolMethod))
{
var syntaxNodeDerivingType = invocation.Arguments.GetArgumentForParameterAtIndex(1).Value.WalkDownConversion().Type;
if (syntaxNodeDerivingType is null || syntaxNodeDerivingType.Equals(syntaxNodeType))
{
return;
}

if (syntaxNodeDerivingType.DerivesFrom(baseFieldDeclarationType))
{
context.ReportDiagnostic(invocation.CreateDiagnostic(FieldDiagnosticDescriptor, syntaxNodeDerivingType.Name));
}
else if (allowedTypes.All(type => !syntaxNodeDerivingType.DerivesFrom(type, baseTypesOnly: true, checkTypeParameterConstraints: false)))
{
context.ReportDiagnostic(invocation.CreateDiagnostic(DiagnosticDescriptor, syntaxNodeDerivingType.Name));
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -571,4 +571,19 @@
<data name="DoNotRegisterCompilerTypesWithBadAssemblyReferenceRuleTitle" xml:space="preserve">
<value>Compiler extensions should be implemented in assemblies with compiler-provided references</value>
</data>
<data name="SemanticModelGetDeclaredSymbolAlwaysReturnsNullDescription" xml:space="preserve">
<value>Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'.</value>
</data>
<data name="SemanticModelGetDeclaredSymbolAlwaysReturnsNullMessage" xml:space="preserve">
<value>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</value>
</data>
<data name="SemanticModelGetDeclaredSymbolAlwaysReturnsNullTitle" xml:space="preserve">
<value>This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'</value>
</data>
<data name="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldDescription" xml:space="preserve">
<value>Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead.</value>
</data>
<data name="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldMessage" xml:space="preserve">
<value>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</value>
</data>
</root>
2 changes: 2 additions & 0 deletions src/Microsoft.CodeAnalysis.Analyzers/Core/DiagnosticIds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ internal static class DiagnosticIds
public const string NoSettingSpecifiedSymbolIsBannedInAnalyzersRuleId = "RS1036";
public const string AddCompilationEndCustomTagRuleId = "RS1037";
public const string DoNotRegisterCompilerTypesWithBadAssemblyReferenceRuleId = "RS1038";
public const string SemanticModelGetDeclaredSymbolAlwaysReturnsNull = "RS1039";
public const string SemanticModelGetDeclaredSymbolAlwaysReturnsNullForField = "RS1040";

// Release tracking analyzer IDs
public const string DeclareDiagnosticIdInAnalyzerReleaseRuleId = "RS2000";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,31 @@
<target state="translated">Nepřidávat odebraná ID diagnostiky analyzátoru do nevydané verze analyzátoru</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullDescription">
<source>Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'.</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldDescription">
<source>Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead.</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldMessage">
<source>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</source>
<target state="new">A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullMessage">
<source>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</source>
<target state="new">A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullTitle">
<source>This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'</source>
<target state="new">This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SymbolIsBannedInAnalyzersDescription">
<source>The symbol has been marked as banned for use in analyzers, and an alternate should be used instead.</source>
<target state="translated">Symbol byl označen jako zakázaný pro použití v analyzátorech a místo toho by se měla použít náhrada.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,31 @@
<target state="translated">Fügen Sie einem nicht veröffentlichten Analysetoolrelease keine entfernten Analysetooldiagnose-IDs hinzu.</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullDescription">
<source>Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'.</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldDescription">
<source>Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead.</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldMessage">
<source>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</source>
<target state="new">A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullMessage">
<source>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</source>
<target state="new">A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullTitle">
<source>This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'</source>
<target state="new">This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SymbolIsBannedInAnalyzersDescription">
<source>The symbol has been marked as banned for use in analyzers, and an alternate should be used instead.</source>
<target state="translated">Das Symbol wurde für die Verwendung in dem Analysetool als gesperrt gekennzeichnet, und es muss stattdessen eine Alternative verwendet werden.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,31 @@
<target state="translated">No agregar identificadores de diagnóstico del analizador eliminados a una versión del analizador no incluida</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullDescription">
<source>Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'.</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldDescription">
<source>Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead.</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldMessage">
<source>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</source>
<target state="new">A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullMessage">
<source>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</source>
<target state="new">A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullTitle">
<source>This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'</source>
<target state="new">This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SymbolIsBannedInAnalyzersDescription">
<source>The symbol has been marked as banned for use in analyzers, and an alternate should be used instead.</source>
<target state="translated">El símbolo ha sido marcado como de uso prohibido en los analizadores, y en su lugar debe usarse uno alternativo.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,31 @@
<target state="translated">Ne pas ajouter d'ID de diagnostic d'analyseur supprimé à une version d'analyseur non fournie</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullDescription">
<source>Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'.</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldDescription">
<source>Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead.</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldMessage">
<source>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</source>
<target state="new">A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullMessage">
<source>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</source>
<target state="new">A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullTitle">
<source>This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'</source>
<target state="new">This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SymbolIsBannedInAnalyzersDescription">
<source>The symbol has been marked as banned for use in analyzers, and an alternate should be used instead.</source>
<target state="translated">Le symbole a été marqué comme étant interdit d’utilisation dans les analyseurs, et un autre symbole doit être utilisé à la place.</target>
Expand Down
Loading

0 comments on commit b40db00

Please sign in to comment.