提交 bf904760 编写于 作者: B Bart Koelman 提交者: Jonathon Marolf

Fixes duplicate namespace insertion (#9229)

* Fixes duplicate namespace insertion

* Added test
上级 96f5ce2f
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Options;
......@@ -476,5 +480,81 @@ class C
public List<int> F;
}");
}
[Fact]
[WorkItem(9228, "https://github.com/dotnet/roslyn/issues/9228")]
public async Task TestDoNotAddDuplicateImportIfNamespaceIsDefinedInSourceAndExternalAssembly()
{
var externalCode =
@"namespace N.M { public class A : System.Attribute { } }";
var code =
@"using System;
using N.M;
class C
{
public void M1(String p1) { }
public void M2([A] String p2) { }
}";
var otherAssemblyReference = GetInMemoryAssemblyReferenceForCode(externalCode);
var project = _emptyProject
.AddMetadataReferences(new[] { otherAssemblyReference })
.WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
project = project.AddDocument("duplicate.cs", externalCode).Project;
var document = project.AddDocument("test.cs", code);
var options = document.Project.Solution.Workspace.Options;
var compilation = await document.Project.Solution.GetCompilationAsync(document.Project, CancellationToken.None);
ImmutableArray<Diagnostic> compilerDiagnostics = compilation.GetDiagnostics(CancellationToken.None);
Assert.Empty(compilerDiagnostics.Where(d => d.Severity == DiagnosticSeverity.Error));
var attribute = compilation.GetTypeByMetadataName("N.M.A");
var syntaxRoot = await document.GetSyntaxRootAsync(CancellationToken.None).ConfigureAwait(false);
SyntaxNode p1SyntaxNode = syntaxRoot.DescendantNodes().OfType<ParameterSyntax>().FirstOrDefault();
// Add N.M.A attribute to p1.
var editor = await DocumentEditor.CreateAsync(document, CancellationToken.None).ConfigureAwait(false);
SyntaxNode attributeSyntax = editor.Generator.Attribute(editor.Generator.TypeExpression(attribute));
editor.AddAttribute(p1SyntaxNode, attributeSyntax);
Document documentWithAttribute = editor.GetChangedDocument();
// Add namespace import.
Document imported = await ImportAdder.AddImportsAsync(documentWithAttribute, null,
CancellationToken.None).ConfigureAwait(false);
var formatted = await Formatter.FormatAsync(imported, options);
var actualText = (await formatted.GetTextAsync()).ToString();
Assert.Equal(actualText,
@"using System;
using N.M;
class C
{
public void M1([global::N.M.A] String p1) { }
public void M2([A] String p2) { }
}");
}
private static MetadataReference GetInMemoryAssemblyReferenceForCode(string code)
{
SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
CSharpCompilation compilation = CSharpCompilation
.Create("test.dll", new[] { tree })
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(TestReferences.NetFx.v4_0_30319.mscorlib);
return compilation.ToMetadataReference();
}
}
}
......@@ -53,7 +53,7 @@ public async Task<Document> AddImportsAsync(Document document, IEnumerable<TextS
var existingNamespaces = new HashSet<INamespaceSymbol>();
await this.GetExistingImportedNamespacesAsync(document, model, existingNamespaces, cancellationToken).ConfigureAwait(false);
var namespacesToAdd = new HashSet<INamespaceSymbol>(namespaces);
var namespacesToAdd = new HashSet<INamespaceSymbol>(namespaces, NamespaceEqualityComparer.Singleton);
namespacesToAdd.RemoveAll(existingNamespaces);
var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false);
......@@ -95,5 +95,39 @@ public async Task<Document> AddImportsAsync(Document document, IEnumerable<TextS
protected abstract INamespaceSymbol GetImportedNamespaceSymbol(SyntaxNode import, SemanticModel model);
protected abstract INamespaceSymbol GetExplicitNamespaceSymbol(SyntaxNode node, SemanticModel model);
protected abstract SyntaxNode InsertNamespaceImport(SyntaxNode root, SyntaxGenerator gen, SyntaxNode import, OptionSet options);
private sealed class NamespaceEqualityComparer : IEqualityComparer<INamespaceSymbol>
{
public static readonly NamespaceEqualityComparer Singleton = new NamespaceEqualityComparer();
private NamespaceEqualityComparer()
{
}
public bool Equals(INamespaceSymbol x, INamespaceSymbol y)
{
if (object.ReferenceEquals(x, y))
{
return true;
}
if (object.ReferenceEquals(x, null) || object.ReferenceEquals(y, null))
{
return false;
}
return x.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) ==
y.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
}
public int GetHashCode(INamespaceSymbol obj)
{
if (object.ReferenceEquals(obj, null))
{
return 0;
}
return obj.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).GetHashCode();
}
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册