Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add analyzer for SemanticModel.GetDeclaredSymbol #6779

Merged
merged 17 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
; 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

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
{
private 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);

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.MicrosoftCodeAnalysisModelExtensions, out var modelExtensions)
|| (getDeclaredSymbolMethod = modelExtensions.GetMembers(nameof(ModelExtensions.GetDeclaredSymbol)).FirstOrDefault() as IMethodSymbol) is null
|| !typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCSharpSyntaxGlobalStatementSyntax, out var globalStatementSymbol)
|| !typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCSharpSyntaxIncompleteMemberSyntax, out var incompleteMemberSymbol)
|| !typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCSharpSyntaxBaseFieldDeclarationSyntax, out var baseFieldDeclarationSymbol))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we could instead look for all the extension GetDeclaredSymbol methods, and use that a list of types that will return positive results? With maybe a special case for the field decl scenario, since that's somewhat misleading?

If we don't want to try doing that, I think we should at least include VariableDeclarationSyntax, as that is also a common error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion, I have implemented it.

{
return;
}

context.RegisterOperationAction(ctx => AnalyzeInvocation(ctx, getDeclaredSymbolMethod, globalStatementSymbol, incompleteMemberSymbol, baseFieldDeclarationSymbol), OperationKind.Invocation);
});
}

private static void AnalyzeInvocation(OperationAnalysisContext context, IMethodSymbol getDeclaredSymbolMethod, INamedTypeSymbol globalStatementSymbol, INamedTypeSymbol incompleteMemberSymbol, INamedTypeSymbol baseFieldDeclarationSymbol)
{
var invocation = (IInvocationOperation)context.Operation;
if (SymbolEqualityComparer.Default.Equals(invocation.TargetMethod, getDeclaredSymbolMethod))
{
var syntaxNodeType = invocation.Arguments[1].Value.WalkDownConversion().Type;
if (syntaxNodeType is not null && syntaxNodeType.GetBaseTypesAndThis().ToSet().Overlaps(new[] { globalStatementSymbol, incompleteMemberSymbol, baseFieldDeclarationSymbol }))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allocates a new array for every invocation of GetDeclaredSymbol. I think this is not necessary.

Also note there is already a DerivesFrom extension method.

{
var diagnostic = invocation.CreateDiagnostic(DiagnosticDescriptor, syntaxNodeType.Name);
context.ReportDiagnostic(diagnostic);
}
}
}

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(DiagnosticDescriptor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -571,4 +571,13 @@
<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 an argument of type 'GlobalStatementSyntax', 'IncompleteMemberSyntax' or a type inheriting from 'BaseFieldDeclarationSyntax' will always return 'null'.</value>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like fields should get a specific error message that instructs the user to extract the variable declarators.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this description needs adjustment as fields now have a separate descriptor/description. Additionally, not sure if we should limit the description to just 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' as the code handles all sub-types of SyntaxNode that do not have an overload. It's fine to mentioned these two as examples, but not as the only node types being handled.

</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>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</value>
333fred marked this conversation as resolved.
Show resolved Hide resolved
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ internal static class DiagnosticIds
public const string NoSettingSpecifiedSymbolIsBannedInAnalyzersRuleId = "RS1036";
public const string AddCompilationEndCustomTagRuleId = "RS1037";
public const string DoNotRegisterCompilerTypesWithBadAssemblyReferenceRuleId = "RS1038";
public const string SemanticModelGetDeclaredSymbolAlwaysReturnsNull = "RS1039";

// 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,21 @@
<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 an argument of type 'GlobalStatementSyntax', 'IncompleteMemberSyntax' or a type inheriting from 'BaseFieldDeclarationSyntax' will always return 'null'.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'GlobalStatementSyntax', 'IncompleteMemberSyntax' or a type inheriting from 'BaseFieldDeclarationSyntax' 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>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="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,21 @@
<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 an argument of type 'GlobalStatementSyntax', 'IncompleteMemberSyntax' or a type inheriting from 'BaseFieldDeclarationSyntax' will always return 'null'.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'GlobalStatementSyntax', 'IncompleteMemberSyntax' or a type inheriting from 'BaseFieldDeclarationSyntax' 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>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="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,21 @@
<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 an argument of type 'GlobalStatementSyntax', 'IncompleteMemberSyntax' or a type inheriting from 'BaseFieldDeclarationSyntax' will always return 'null'.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'GlobalStatementSyntax', 'IncompleteMemberSyntax' or a type inheriting from 'BaseFieldDeclarationSyntax' 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>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="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,21 @@
<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 an argument of type 'GlobalStatementSyntax', 'IncompleteMemberSyntax' or a type inheriting from 'BaseFieldDeclarationSyntax' will always return 'null'.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'GlobalStatementSyntax', 'IncompleteMemberSyntax' or a type inheriting from 'BaseFieldDeclarationSyntax' 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>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="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
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,21 @@
<target state="translated">Non aggiungere gli ID diagnostica dell'analizzatore rimossi alla versione dell'analizzatore non distribuita</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullDescription">
<source>Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'GlobalStatementSyntax', 'IncompleteMemberSyntax' or a type inheriting from 'BaseFieldDeclarationSyntax' will always return 'null'.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'GlobalStatementSyntax', 'IncompleteMemberSyntax' or a type inheriting from 'BaseFieldDeclarationSyntax' 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>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="SymbolIsBannedInAnalyzersDescription">
<source>The symbol has been marked as banned for use in analyzers, and an alternate should be used instead.</source>
<target state="translated">Il simbolo è stato contrassegnato come escluso dall'uso negli analizzatori e al suo posto deve esserne usato uno alternativo.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,21 @@
<target state="translated">削除したアナライザー診断 ID を未出荷のアナライザー リリースに追加しない</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullDescription">
<source>Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'GlobalStatementSyntax', 'IncompleteMemberSyntax' or a type inheriting from 'BaseFieldDeclarationSyntax' will always return 'null'.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'GlobalStatementSyntax', 'IncompleteMemberSyntax' or a type inheriting from 'BaseFieldDeclarationSyntax' 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>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="SymbolIsBannedInAnalyzersDescription">
<source>The symbol has been marked as banned for use in analyzers, and an alternate should be used instead.</source>
<target state="translated">シンボルはこのアナライザーで禁止とマークされているため、代替を使用する必要があります。</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,21 @@
<target state="translated">제거된 분석기 진단 ID를 제공되지 않은 분석기 릴리스에 추가하면 안 됩니다.</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullDescription">
<source>Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'GlobalStatementSyntax', 'IncompleteMemberSyntax' or a type inheriting from 'BaseFieldDeclarationSyntax' will always return 'null'.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'GlobalStatementSyntax', 'IncompleteMemberSyntax' or a type inheriting from 'BaseFieldDeclarationSyntax' 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>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="SymbolIsBannedInAnalyzersDescription">
<source>The symbol has been marked as banned for use in analyzers, and an alternate should be used instead.</source>
<target state="translated">기호가 분석기에서 사용 금지된 것으로 표시되었으며 대신 대체 기호를 사용해야 합니다.</target>
Expand Down
Loading
Loading