Skip to content

Commit

Permalink
Add new analyzer for deprecating implementing an interface (#7419)
Browse files Browse the repository at this point in the history
We're officially marking `ISourceGenerator` as deprecated, but we can't actually deprecate the interface, because it's used in several APIs like AnalyzerFileReference. So instead, we just add an analyzer to indicate that the interface should not be implemented. I've made this more general so we can do this with more interfaces in the future if necessary, based the implementation on InternalImplementationOnlyAnalyzer.
  • Loading branch information
333fred authored Sep 27, 2024
1 parent 1d3c176 commit ef8dad3
Show file tree
Hide file tree
Showing 21 changed files with 561 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ Rule ID | Category | Severity | Notes
--------|----------|----------|-------
RS1038 | MicrosoftCodeAnalysisCorrectness | Warning | CompilerExtensionStrictApiAnalyzer
RS1041 | MicrosoftCodeAnalysisCorrectness | Warning | CompilerExtensionTargetFrameworkAnalyzer, [Documentation](https://github.com/dotnet/roslyn-analyzers/blob/main/docs/rules/RS1041.md)
RS1042 | MicrosoftCodeAnalysisCompatibility | Error | ImplementationIsObsoleteAnalyzer
Original file line number Diff line number Diff line change
Expand Up @@ -595,4 +595,14 @@
<data name="DoNotDeclareCompilerFeatureInAssemblyWithUnsupportedTargetFrameworkRuleTitle" xml:space="preserve">
<value>Compiler extensions should be implemented in assemblies targeting netstandard2.0</value>
</data>
</root>
<data name="ImplementationIsObsoleteDescription" xml:space="preserve">
<value>The author of this interface has deprecated implementing this interface.</value>
</data>
<data name="ImplementationIsObsoleteMessage" xml:space="preserve">
<value>Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</value>
<remarks>{2} is a URL</remarks>
</data>
<data name="ImplementationIsObsoleteTitle" xml:space="preserve">
<value>Implementations of this interface are not allowed</value>
</data>
</root>
1 change: 1 addition & 0 deletions src/Microsoft.CodeAnalysis.Analyzers/Core/DiagnosticIds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ internal static class DiagnosticIds
public const string SemanticModelGetDeclaredSymbolAlwaysReturnsNull = "RS1039";
public const string SemanticModelGetDeclaredSymbolAlwaysReturnsNullForField = "RS1040";
public const string DoNotRegisterCompilerTypesWithBadTargetFrameworkRuleId = "RS1041";
public const string ImplementationIsObsoleteRuleId = "RS1042";

// Release tracking analyzer IDs
public const string DeclareDiagnosticIdInAnalyzerReleaseRuleId = "RS2000";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

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

namespace Microsoft.CodeAnalysis.Analyzers
{
using static CodeAnalysisDiagnosticsResources;

[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public class ImplementationIsObsoleteAnalyzer : DiagnosticAnalyzer
{
private const string ImplementationIsObsoleteAttributeName = "ImplementationIsObsoleteAttribute";
private const string ImplementationIsObsoleteAttributeFullName = "System.Runtime.CompilerServices.ImplementationIsObsoleteAttribute";

public static readonly DiagnosticDescriptor Rule = new(
DiagnosticIds.ImplementationIsObsoleteRuleId,
CreateLocalizableResourceString(nameof(ImplementationIsObsoleteTitle)),
CreateLocalizableResourceString(nameof(ImplementationIsObsoleteMessage)),
DiagnosticCategory.MicrosoftCodeAnalysisCompatibility,
DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: CreateLocalizableResourceString(nameof(ImplementationIsObsoleteDescription)));

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

public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType);
}

private static void AnalyzeSymbol(SymbolAnalysisContext context)
{
var namedTypeSymbol = (INamedTypeSymbol)context.Symbol;

// If any interface implemented by this type has the attribute and if the interface and this type are not
// in "internals visible" context, then issue an error.
foreach (INamedTypeSymbol iface in namedTypeSymbol.AllInterfaces)
{
System.Collections.Generic.IEnumerable<AttributeData> attributes = iface.GetAttributes();

// We are doing a string comparison of the name here because we don't care where the attribute comes from.
// CodeAnalysis.dll itself has this attribute and if the user assembly also had it, symbol equality will fail
// but we should still issue the error.
if (attributes.FirstOrDefault(a => a is { AttributeClass.Name: ImplementationIsObsoleteAttributeName, ConstructorArguments: [{ Value: string }] }
&& a.AttributeClass.ToDisplayString().Equals(ImplementationIsObsoleteAttributeFullName, StringComparison.Ordinal)) is { } attr &&
!iface.ContainingAssembly.GivesAccessTo(namedTypeSymbol.ContainingAssembly))
{
context.ReportDiagnostic(namedTypeSymbol.CreateDiagnostic(Rule, namedTypeSymbol.Name, iface.Name, (string)attr.ConstructorArguments[0].Value!));
break;
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,21 @@
<target state="translated">Povolit souběžné provádění</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteDescription">
<source>The author of this interface has deprecated implementing this interface.</source>
<target state="new">The author of this interface has deprecated implementing this interface.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteMessage">
<source>Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</source>
<target state="new">Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteTitle">
<source>Implementations of this interface are not allowed</source>
<target state="new">Implementations of this interface are not allowed</target>
<note />
</trans-unit>
<trans-unit id="InvalidEntryInAnalyzerReleasesFileRuleDescription">
<source>Invalid entry in analyzer release file.</source>
<target state="translated">Neplatná položka v souboru vydané verze analyzátoru</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,21 @@
<target state="translated">Gleichzeitige Ausführung aktivieren</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteDescription">
<source>The author of this interface has deprecated implementing this interface.</source>
<target state="new">The author of this interface has deprecated implementing this interface.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteMessage">
<source>Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</source>
<target state="new">Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteTitle">
<source>Implementations of this interface are not allowed</source>
<target state="new">Implementations of this interface are not allowed</target>
<note />
</trans-unit>
<trans-unit id="InvalidEntryInAnalyzerReleasesFileRuleDescription">
<source>Invalid entry in analyzer release file.</source>
<target state="translated">Ungültiger Eintrag in Analysetool-Releasedatei.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,21 @@
<target state="translated">Habilitar la ejecución simultánea</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteDescription">
<source>The author of this interface has deprecated implementing this interface.</source>
<target state="new">The author of this interface has deprecated implementing this interface.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteMessage">
<source>Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</source>
<target state="new">Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteTitle">
<source>Implementations of this interface are not allowed</source>
<target state="new">Implementations of this interface are not allowed</target>
<note />
</trans-unit>
<trans-unit id="InvalidEntryInAnalyzerReleasesFileRuleDescription">
<source>Invalid entry in analyzer release file.</source>
<target state="translated">Entrada no válida en el archivo de versión del analizador.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,21 @@
<target state="translated">Activer l'exécution simultanée</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteDescription">
<source>The author of this interface has deprecated implementing this interface.</source>
<target state="new">The author of this interface has deprecated implementing this interface.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteMessage">
<source>Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</source>
<target state="new">Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteTitle">
<source>Implementations of this interface are not allowed</source>
<target state="new">Implementations of this interface are not allowed</target>
<note />
</trans-unit>
<trans-unit id="InvalidEntryInAnalyzerReleasesFileRuleDescription">
<source>Invalid entry in analyzer release file.</source>
<target state="translated">Entrée non valide dans le fichier de version d'analyseur.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,21 @@
<target state="translated">Abilita l'esecuzione simultanea</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteDescription">
<source>The author of this interface has deprecated implementing this interface.</source>
<target state="new">The author of this interface has deprecated implementing this interface.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteMessage">
<source>Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</source>
<target state="new">Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteTitle">
<source>Implementations of this interface are not allowed</source>
<target state="new">Implementations of this interface are not allowed</target>
<note />
</trans-unit>
<trans-unit id="InvalidEntryInAnalyzerReleasesFileRuleDescription">
<source>Invalid entry in analyzer release file.</source>
<target state="translated">Voce non valida nel file di versione dell'analizzatore.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,21 @@
<target state="translated">同時実行を有効にします</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteDescription">
<source>The author of this interface has deprecated implementing this interface.</source>
<target state="new">The author of this interface has deprecated implementing this interface.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteMessage">
<source>Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</source>
<target state="new">Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteTitle">
<source>Implementations of this interface are not allowed</source>
<target state="new">Implementations of this interface are not allowed</target>
<note />
</trans-unit>
<trans-unit id="InvalidEntryInAnalyzerReleasesFileRuleDescription">
<source>Invalid entry in analyzer release file.</source>
<target state="translated">アナライザー リリース ファイルに無効なエントリがあります。</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,21 @@
<target state="translated">동시 실행 사용</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteDescription">
<source>The author of this interface has deprecated implementing this interface.</source>
<target state="new">The author of this interface has deprecated implementing this interface.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteMessage">
<source>Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</source>
<target state="new">Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteTitle">
<source>Implementations of this interface are not allowed</source>
<target state="new">Implementations of this interface are not allowed</target>
<note />
</trans-unit>
<trans-unit id="InvalidEntryInAnalyzerReleasesFileRuleDescription">
<source>Invalid entry in analyzer release file.</source>
<target state="translated">분석기 릴리스 파일에 잘못된 항목이 있습니다.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,21 @@
<target state="translated">Włącz wykonywanie współbieżne</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteDescription">
<source>The author of this interface has deprecated implementing this interface.</source>
<target state="new">The author of this interface has deprecated implementing this interface.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteMessage">
<source>Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</source>
<target state="new">Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteTitle">
<source>Implementations of this interface are not allowed</source>
<target state="new">Implementations of this interface are not allowed</target>
<note />
</trans-unit>
<trans-unit id="InvalidEntryInAnalyzerReleasesFileRuleDescription">
<source>Invalid entry in analyzer release file.</source>
<target state="translated">Nieprawidłowy wpis w pliku wydania analizatora.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,21 @@
<target state="translated">Habilitar execução simultânea</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteDescription">
<source>The author of this interface has deprecated implementing this interface.</source>
<target state="new">The author of this interface has deprecated implementing this interface.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteMessage">
<source>Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</source>
<target state="new">Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteTitle">
<source>Implementations of this interface are not allowed</source>
<target state="new">Implementations of this interface are not allowed</target>
<note />
</trans-unit>
<trans-unit id="InvalidEntryInAnalyzerReleasesFileRuleDescription">
<source>Invalid entry in analyzer release file.</source>
<target state="translated">Entrada inválida no arquivo de versão do analisador.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,21 @@
<target state="translated">Включение параллельного выполнения</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteDescription">
<source>The author of this interface has deprecated implementing this interface.</source>
<target state="new">The author of this interface has deprecated implementing this interface.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteMessage">
<source>Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</source>
<target state="new">Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteTitle">
<source>Implementations of this interface are not allowed</source>
<target state="new">Implementations of this interface are not allowed</target>
<note />
</trans-unit>
<trans-unit id="InvalidEntryInAnalyzerReleasesFileRuleDescription">
<source>Invalid entry in analyzer release file.</source>
<target state="translated">Недопустимая запись в файле выпуска анализатора.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,21 @@
<target state="translated">Eşzamanlı yürütmeyi etkinleştir</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteDescription">
<source>The author of this interface has deprecated implementing this interface.</source>
<target state="new">The author of this interface has deprecated implementing this interface.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteMessage">
<source>Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</source>
<target state="new">Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details.</target>
<note />
</trans-unit>
<trans-unit id="ImplementationIsObsoleteTitle">
<source>Implementations of this interface are not allowed</source>
<target state="new">Implementations of this interface are not allowed</target>
<note />
</trans-unit>
<trans-unit id="InvalidEntryInAnalyzerReleasesFileRuleDescription">
<source>Invalid entry in analyzer release file.</source>
<target state="translated">Çözümleyici yayın dosyasında geçersiz girdi.</target>
Expand Down
Loading

0 comments on commit ef8dad3

Please sign in to comment.