diff --git a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs
index 885014797626cb924f5fe4cd8fa3d9d3e730e07b..4338a5e44603a3d891ffa751cfd1fc4213f96885 100644
--- a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs
+++ b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs
@@ -888,6 +888,36 @@ public void TestReportingDiagnosticWithInvalidId()
.WithLocation(1, 1));
}
+ [Fact, WorkItem(23667, "https://github.com/dotnet/roslyn/issues/23667")]
+ public void TestReportingDiagnosticWithCSharpCompilerId()
+ {
+ string source = @"";
+ var analyzers = new DiagnosticAnalyzer[] { new AnalyzerWithCSharpCompilerDiagnosticId() };
+ string message = new ArgumentException(string.Format(CodeAnalysisResources.CompilerDiagnosticIdReported, AnalyzerWithCSharpCompilerDiagnosticId.Descriptor.Id), "diagnostic").Message;
+
+ CreateCompilationWithMscorlib45(source)
+ .VerifyDiagnostics()
+ .VerifyAnalyzerDiagnostics(analyzers, null, null, logAnalyzerExceptionAsDiagnostics: true,
+ expected: Diagnostic("AD0001")
+ .WithArguments("Microsoft.CodeAnalysis.CommonDiagnosticAnalyzers+AnalyzerWithCSharpCompilerDiagnosticId", "System.ArgumentException", message)
+ .WithLocation(1, 1));
+ }
+
+ [Fact, WorkItem(23667, "https://github.com/dotnet/roslyn/issues/23667")]
+ public void TestReportingDiagnosticWithBasicCompilerId()
+ {
+ string source = @"";
+ var analyzers = new DiagnosticAnalyzer[] { new AnalyzerWithBasicCompilerDiagnosticId() };
+ string message = new ArgumentException(string.Format(CodeAnalysisResources.CompilerDiagnosticIdReported, AnalyzerWithBasicCompilerDiagnosticId.Descriptor.Id), "diagnostic").Message;
+
+ CreateCompilationWithMscorlib45(source)
+ .VerifyDiagnostics()
+ .VerifyAnalyzerDiagnostics(analyzers, null, null, logAnalyzerExceptionAsDiagnostics: true,
+ expected: Diagnostic("AD0001")
+ .WithArguments("Microsoft.CodeAnalysis.CommonDiagnosticAnalyzers+AnalyzerWithBasicCompilerDiagnosticId", "System.ArgumentException", message)
+ .WithLocation(1, 1));
+ }
+
[Fact, WorkItem(7173, "https://github.com/dotnet/roslyn/issues/7173")]
public void TestReportingDiagnosticWithInvalidLocation()
{
diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs
index e5a359e88712e40e8cce29c36a94990120dc273e..90c5fe44f39af09531007e0b9e31bb81b3cb7a21 100644
--- a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs
+++ b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs
@@ -405,6 +405,15 @@ internal class CodeAnalysisResources {
}
}
+ ///
+ /// Looks up a localized string similar to Reported diagnostic has an ID '{0}', which only a compiler should be reporting..
+ ///
+ internal static string CompilerDiagnosticIdReported {
+ get {
+ return ResourceManager.GetString("CompilerDiagnosticIdReported", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to constructor.
///
diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.resx b/src/Compilers/Core/Portable/CodeAnalysisResources.resx
index 89c2c956fdf750f7c5a2ac0557c47f19f8df1f1b..247f3d5e66873da0b302f6918530757c34a84d4d 100644
--- a/src/Compilers/Core/Portable/CodeAnalysisResources.resx
+++ b/src/Compilers/Core/Portable/CodeAnalysisResources.resx
@@ -446,6 +446,9 @@
Reported diagnostic has an ID '{0}', which is not a valid identifier.
+
+ Reported diagnostic has an ID '{0}', which only a compiler should be reporting.
+
Reported diagnostic '{0}' has a source location in file '{1}', which is not part of the compilation being analyzed.
diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.cs
index 9bdeda8dd3a84f132fe74a43b8b0d7b0b69c7e2e..6c6824ffafed59166dab32ec13edd3715bd73b63 100644
--- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.cs
+++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.cs
@@ -168,6 +168,11 @@ internal bool IsSupportedDiagnostic(DiagnosticAnalyzer analyzer, Diagnostic diag
return true;
}
+ if (IsCompilerReservedDiagnostic(diagnostic.Id))
+ {
+ throw new ArgumentException(string.Format(CodeAnalysisResources.CompilerDiagnosticIdReported, diagnostic.Id), nameof(diagnostic));
+ }
+
// Get all the supported diagnostics and scan them linearly to see if the reported diagnostic is supported by the analyzer.
// The linear scan is okay, given that this runs only if a diagnostic is being reported and a given analyzer is quite unlikely to have hundreds of thousands of supported diagnostics.
var supportedDescriptors = GetSupportedDiagnosticDescriptors(analyzer, analyzerExecutor);
@@ -182,6 +187,18 @@ internal bool IsSupportedDiagnostic(DiagnosticAnalyzer analyzer, Diagnostic diag
return false;
}
+ private static bool IsCompilerReservedDiagnostic(string id)
+ {
+ // Only the compiler analyzer should produce diagnostics with CS or BC prefixes (followed by digit)
+ if (id.Length >= 3 && (id.StartsWith("CS", StringComparison.Ordinal) || id.StartsWith("BC", StringComparison.Ordinal)))
+ {
+ char thirdChar = id[2];
+ return thirdChar >= '0' && thirdChar <= '9';
+ }
+
+ return false;
+ }
+
///
/// Returns true if all the diagnostics that can be produced by this analyzer are suppressed through options.
///
diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf
index d899680f5a4befaf4821cc33c691b09a35e0212b..db8c7d51bfc36d4cd1aa7905e27065b21cf14e71 100644
--- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf
+++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf
@@ -828,6 +828,11 @@
Warning: Could not enable multicore JIT due to exception: {0}.
+
+
+ Reported diagnostic has an ID '{0}', which only a compiler should be reporting.
+
+