提交 d6aecd9b 编写于 作者: M Manish Vasani

Ensure that analyzer driver processes declared namespace symbols which are...

Ensure that analyzer driver processes declared namespace symbols which are scoped to source assembly being analyzed, not the merged namespace symbols with declaring references across the compilation (including referenced assemblies).
上级 5666116d
......@@ -1491,5 +1491,19 @@ private static void AddExpectedDiagnostic(ArrayBuilder<DiagnosticDescription> bu
var diagnostic = Diagnostic(diagnosticId, squiggledText).WithArguments(arguments).WithLocation(line, column);
builder.Add(diagnostic);
}
[Fact]
public void TestEnsureNoMergedNamespaceSymbolAnalyzer()
{
var source = @"namespace N1.N2 { }";
var metadataReference = CreateCompilationWithMscorlib(source).ToMetadataReference();
var compilation = CreateCompilationWithMscorlib(source, new[] { metadataReference });
compilation.VerifyDiagnostics();
// Analyzer reports a diagnostic if it receives a merged namespace symbol across assemblies in compilation.
var analyzers = new DiagnosticAnalyzer[] { new EnsureNoMergedNamespaceSymbolAnalyzer() };
compilation.VerifyAnalyzerDiagnostics(analyzers);
}
}
}
......@@ -5,6 +5,7 @@
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis
{
......@@ -12,7 +13,7 @@ internal class DeclarationComputer
{
internal static DeclarationInfo GetDeclarationInfo(SemanticModel model, SyntaxNode node, bool getSymbol, IEnumerable<SyntaxNode> executableCodeBlocks, CancellationToken cancellationToken)
{
var declaredSymbol = getSymbol ? model.GetDeclaredSymbol(node, cancellationToken) : null;
var declaredSymbol = GetDeclaredSymbol(model, node, getSymbol, cancellationToken);
var codeBlocks = executableCodeBlocks?.Where(c => c != null).AsImmutableOrEmpty() ?? ImmutableArray<SyntaxNode>.Empty;
return new DeclarationInfo(node, codeBlocks, declaredSymbol);
}
......@@ -24,14 +25,39 @@ internal static DeclarationInfo GetDeclarationInfo(SemanticModel model, SyntaxNo
internal static DeclarationInfo GetDeclarationInfo(SemanticModel model, SyntaxNode node, bool getSymbol, SyntaxNode executableCodeBlock, CancellationToken cancellationToken)
{
var declaredSymbol = getSymbol ? model.GetDeclaredSymbol(node, cancellationToken) : null;
var codeBlock = executableCodeBlock == null ? ImmutableArray<SyntaxNode>.Empty : ImmutableArray.Create(executableCodeBlock);
return new DeclarationInfo(node, codeBlock, declaredSymbol);
return GetDeclarationInfo(model, node, getSymbol, SpecializedCollections.SingletonEnumerable(executableCodeBlock), cancellationToken);
}
internal static DeclarationInfo GetDeclarationInfo(SemanticModel model, SyntaxNode node, bool getSymbol, CancellationToken cancellationToken, params SyntaxNode[] executableCodeBlocks)
{
return GetDeclarationInfo(model, node, getSymbol, executableCodeBlocks.AsEnumerable(), cancellationToken);
}
private static ISymbol GetDeclaredSymbol(SemanticModel model, SyntaxNode node, bool getSymbol, CancellationToken cancellationToken)
{
if (!getSymbol)
{
return null;
}
var declaredSymbol = model.GetDeclaredSymbol(node, cancellationToken);
// For namespace declarations, GetDeclaredSymbol returns a compilation scoped namespace symbol,
// which includes declarations across the compilation, including those in referenced assemblies.
// However, we are only interested in the namespace symbol scoped to the compilation's source assembly.
var namespaceSymbol = declaredSymbol as INamespaceSymbol;
if (namespaceSymbol != null && namespaceSymbol.ConstituentNamespaces.Length > 1)
{
var assemblyToScope = model.Compilation.Assembly;
var assemblyScopedNamespaceSymbol = namespaceSymbol.ConstituentNamespaces.FirstOrDefault(ns => ns.ContainingAssembly == assemblyToScope);
if (assemblyScopedNamespaceSymbol != null)
{
Debug.Assert(assemblyScopedNamespaceSymbol.ConstituentNamespaces.Length == 1);
declaredSymbol = assemblyScopedNamespaceSymbol;
}
}
return declaredSymbol;
}
}
}
......@@ -182,7 +182,8 @@ private static ImmutableArray<CompilationEvent> CreateCompilationEventsForTree(I
{
var builder = ImmutableArray.CreateBuilder<CompilationEvent>();
foreach (var symbol in declaredSymbols)
{
{
Debug.Assert(symbol.ContainingAssembly == compilation.Assembly);
builder.Add(new SymbolDeclaredCompilationEvent(compilation, symbol));
}
......
......@@ -1850,5 +1850,40 @@ class MyClass
Assert.Equal(HiddenDiagnosticsCompilationAnalyzer.Descriptor.Id, diagnostics.Single().Id)
End Using
End Sub
<WpfFact>
Public Sub TestEnsureNoMergedNamespaceSymbolAnalyzer()
Dim test = <Workspace>
<Project Language="C#" AssemblyName="BaseAssembly" CommonReferences="true">
<Document>
namespace N1.N2 { class C1 { } }
</Document>
</Project>
<Project Language="C#" AssemblyName="MainAssembly" CommonReferences="true">
<ProjectReference>BaseAssembly</ProjectReference>
<Document>
namespace N1.N2 { class C2 { } }
</Document>
</Project>
</Workspace>
Using workspace = TestWorkspaceFactory.CreateWorkspace(test)
Dim project = workspace.CurrentSolution.Projects.Single(Function(p As Project) p.Name = "MainAssembly")
' Analyzer reports a diagnostic if it receives a merged namespace symbol across assemblies in compilation.
Dim analyzer = New EnsureNoMergedNamespaceSymbolAnalyzer()
Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer))
project = project.AddAnalyzerReference(analyzerReference)
Dim diagnosticService = New TestDiagnosticAnalyzerService()
Dim descriptorsMap = diagnosticService.GetDiagnosticDescriptors(project)
Assert.Equal(1, descriptorsMap.Count)
Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace)
Dim diagnostics = diagnosticService.GetDiagnosticsAsync(project.Solution, project.Id).WaitAndGetResult(CancellationToken.None)
Assert.Equal(0, diagnostics.Count())
End Using
End Sub
End Class
End Namespace
......@@ -365,6 +365,38 @@ public sealed class AnalyzerWithNoActions : DiagnosticAnalyzer
public override void Initialize(AnalysisContext context) { }
}
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public sealed class EnsureNoMergedNamespaceSymbolAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "DiagnosticId";
public const string Title = "Title";
public const string Message = "Message";
public const string Category = "Category";
public const DiagnosticSeverity Severity = DiagnosticSeverity.Warning;
internal static DiagnosticDescriptor Rule =
new DiagnosticDescriptor(DiagnosticId, Title, Message,
Category, Severity, isEnabledByDefault: true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Namespace);
}
private void AnalyzeSymbol(SymbolAnalysisContext context)
{
// Ensure we are not invoked for merged namespace symbol, but instead for constituent namespace scoped to the source assembly.
var ns = (INamespaceSymbol)context.Symbol;
if (ns.ContainingAssembly != context.Compilation.Assembly || ns.ConstituentNamespaces.Length > 1)
{
context.ReportDiagnostic(Diagnostic.Create(Rule, ns.Locations[0]));
}
}
}
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public sealed class AnalyzerWithNoSupportedDiagnostics : DiagnosticAnalyzer
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册