Skip to content

Commit

Permalink
Order GlobalNamespace declarations to match Compilation.SyntaxTrees
Browse files Browse the repository at this point in the history
  • Loading branch information
cston committed Jun 1, 2016
1 parent 5d9b822 commit b8798c4
Show file tree
Hide file tree
Showing 18 changed files with 327 additions and 165 deletions.
12 changes: 10 additions & 2 deletions src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1867,6 +1867,14 @@ internal DeclarationTable Declarations
}
}

internal MergedNamespaceDeclaration MergedRootDeclaration
{
get
{
return Declarations.GetMergedRoot(this);
}
}

/// <summary>
/// Gets the diagnostics produced during the parsing stage of a compilation. There are no diagnostics for declarations or accessor or
/// method bodies, for example.
Expand Down Expand Up @@ -2836,7 +2844,7 @@ internal override int CompareSourceLocations(Location loc1, Location loc2)
throw new ArgumentException(CSharpResources.NoNoneSearchCriteria, nameof(filter));
}

return this.Declarations.ContainsName(predicate, filter, cancellationToken);
return DeclarationTable.ContainsName(this.MergedRootDeclaration, predicate, filter, cancellationToken);
}

/// <summary>
Expand Down Expand Up @@ -2934,7 +2942,7 @@ public IEnumerable<ISymbol> GetSymbolsWithName(Func<string, bool> predicate, Sym
var result = new HashSet<ISymbol>();
var spine = new List<MergedNamespaceOrTypeDeclaration>();

AppendSymbolsWithName(spine, _compilation.Declarations.MergedRoot, predicate, filter, result, cancellationToken);
AppendSymbolsWithName(spine, _compilation.MergedRootDeclaration, predicate, filter, result, cancellationToken);

return result;
}
Expand Down
71 changes: 44 additions & 27 deletions src/Compilers/CSharp/Portable/Declarations/DeclarationTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.Threading;
using Roslyn.Utilities;

Expand Down Expand Up @@ -32,7 +32,7 @@ internal sealed partial class DeclarationTable
private readonly Cache _cache;

// The lazily computed total merged declaration.
private readonly Lazy<MergedNamespaceDeclaration> _mergedRoot;
private MergedNamespaceDeclaration _mergedRoot;

private readonly Lazy<ICollection<string>> _typeNames;
private readonly Lazy<ICollection<string>> _namespaceNames;
Expand All @@ -46,7 +46,6 @@ private DeclarationTable(
_allOlderRootDeclarations = allOlderRootDeclarations;
_latestLazyRootDeclaration = latestLazyRootDeclaration;
_cache = cache ?? new Cache(this);
_mergedRoot = new Lazy<MergedNamespaceDeclaration>(GetMergedRoot);
_typeNames = new Lazy<ICollection<string>>(GetMergedTypeNames);
_namespaceNames = new Lazy<ICollection<string>>(GetMergedNamespaceNames);
_referenceDirectives = new Lazy<ICollection<ReferenceDirective>>(GetMergedReferenceDirectives);
Expand Down Expand Up @@ -88,18 +87,6 @@ public DeclarationTable RemoveRootDeclaration(Lazy<RootSingleNamespaceDeclaratio
}
}

public IEnumerable<RootSingleNamespaceDeclaration> AllRootNamespacesUnordered()
{
if (_latestLazyRootDeclaration == null)
{
return _allOlderRootDeclarations;
}
else
{
return _allOlderRootDeclarations.Concat(SpecializedCollections.SingletonEnumerable(_latestLazyRootDeclaration.Value));
}
}

// The merged-tree-reuse story goes like this. We have a "forest" of old declarations, and
// possibly a lone tree of new declarations. We construct a merged declaration by merging
// together everything in the forest. This we can re-use from edit to edit, provided that
Expand All @@ -114,8 +101,18 @@ public IEnumerable<RootSingleNamespaceDeclaration> AllRootNamespacesUnordered()
// old merged root new merged root
// / | | | \ \
// old singles forest new single tree
public MergedNamespaceDeclaration GetMergedRoot(CSharpCompilation compilation)
{
Debug.Assert(compilation.Declarations == this);
if (_mergedRoot == null)
{
Interlocked.CompareExchange(ref _mergedRoot, CalculateMergedRoot(compilation), null);
}
return _mergedRoot;
}

private MergedNamespaceDeclaration GetMergedRoot()
// Internal for unit tests only.
internal MergedNamespaceDeclaration CalculateMergedRoot(CSharpCompilation compilation)
{
var oldRoot = _cache.MergedRoot.Value;
if (_latestLazyRootDeclaration == null)
Expand All @@ -128,7 +125,31 @@ private MergedNamespaceDeclaration GetMergedRoot()
}
else
{
return MergedNamespaceDeclaration.Create(oldRoot, _latestLazyRootDeclaration.Value);
var oldRootDeclarations = oldRoot.Declarations;
var builder = ArrayBuilder<SingleNamespaceDeclaration>.GetInstance(oldRootDeclarations.Length + 1);
builder.AddRange(oldRootDeclarations);
builder.Add(_latestLazyRootDeclaration.Value);
// Sort the root namespace declarations to match the order of SyntaxTrees.
if (compilation != null)
{
builder.Sort(new RootNamespaceLocationComparer(compilation));
}
return MergedNamespaceDeclaration.Create(builder.ToImmutableAndFree());
}
}

private sealed class RootNamespaceLocationComparer : IComparer<SingleNamespaceDeclaration>
{
private readonly CSharpCompilation _compilation;

internal RootNamespaceLocationComparer(CSharpCompilation compilation)
{
_compilation = compilation;
}

public int Compare(SingleNamespaceDeclaration x, SingleNamespaceDeclaration y)
{
return _compilation.CompareSourceLocations(x.Location, y.Location);
}
}

Expand Down Expand Up @@ -215,14 +236,6 @@ private static ISet<string> GetNames(Declaration declaration, Predicate<Declarat
return SpecializedCollections.ReadOnlySet(set);
}

public MergedNamespaceDeclaration MergedRoot
{
get
{
return _mergedRoot.Value;
}
}

public ICollection<string> TypeNames
{
get
Expand All @@ -247,14 +260,18 @@ public IEnumerable<ReferenceDirective> ReferenceDirectives
}
}

public bool ContainsName(Func<string, bool> predicate, SymbolFilter filter, CancellationToken cancellationToken)
public static bool ContainsName(
MergedNamespaceDeclaration mergedRoot,
Func<string, bool> predicate,
SymbolFilter filter,
CancellationToken cancellationToken)
{
var includeNamespace = (filter & SymbolFilter.Namespace) == SymbolFilter.Namespace;
var includeType = (filter & SymbolFilter.Type) == SymbolFilter.Type;
var includeMember = (filter & SymbolFilter.Member) == SymbolFilter.Member;

var stack = new Stack<MergedNamespaceOrTypeDeclaration>();
stack.Push(this.MergedRoot);
stack.Push(mergedRoot);

while (stack.Count > 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,9 @@ public override SingleNamespaceOrTypeDeclaration VisitNamespaceDeclaration(Names
bool hasExterns = node.Externs.Any();
NameSyntax name = node.Name;
CSharpSyntaxNode currentNode = node;
while (name is QualifiedNameSyntax)
QualifiedNameSyntax dotted;
while ((dotted = name as QualifiedNameSyntax) != null)
{
var dotted = name as QualifiedNameSyntax;
var ns = SingleNamespaceDeclaration.Create(
name: dotted.Right.Identifier.ValueText,
hasUsings: hasUsings,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,6 @@ public static MergedNamespaceDeclaration Create(SingleNamespaceDeclaration decla
return new MergedNamespaceDeclaration(ImmutableArray.Create(declaration));
}

public static MergedNamespaceDeclaration Create(
MergedNamespaceDeclaration mergedDeclaration,
SingleNamespaceDeclaration declaration)
{
return new MergedNamespaceDeclaration(mergedDeclaration._declarations.Add(declaration));
}

public override DeclarationKind Kind
{
get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1443,13 +1443,18 @@ internal CommonAssemblyWellKnownAttributeData GetNetModuleDecodedWellKnownAttrib

internal ImmutableArray<SyntaxList<AttributeListSyntax>> GetAttributeDeclarations()
{
var attrList =
from rootNs in DeclaringCompilation.Declarations.AllRootNamespacesUnordered()
where rootNs.HasAssemblyAttributes
select rootNs.Location.SourceTree into tree
orderby _compilation.GetSyntaxTreeOrdinal(tree)
select ((CompilationUnitSyntax)tree.GetRoot()).AttributeLists;
return attrList.ToImmutableArray();
var builder = ArrayBuilder<SyntaxList<AttributeListSyntax>>.GetInstance();
var declarations = DeclaringCompilation.MergedRootDeclaration.Declarations;
foreach (RootSingleNamespaceDeclaration rootNs in declarations)
{
if (rootNs.HasAssemblyAttributes)
{
var tree = rootNs.Location.SourceTree;
var root = (CompilationUnitSyntax)tree.GetRoot();
builder.Add(root.AttributeLists);
}
}
return builder.ToImmutableAndFree();
}

private void EnsureAttributesAreBound()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,18 +186,14 @@ public override NamespaceSymbol GlobalNamespace
{
if ((object)_globalNamespace == null)
{
Interlocked.CompareExchange(ref _globalNamespace, MakeGlobalNamespace(), null);
var globalNS = new SourceNamespaceSymbol(this, this, DeclaringCompilation.MergedRootDeclaration);
Interlocked.CompareExchange(ref _globalNamespace, globalNS, null);
}

return _globalNamespace;
}
}

private SourceNamespaceSymbol MakeGlobalNamespace()
{
return new SourceNamespaceSymbol(this, this, _sources.MergedRoot);
}

internal sealed override bool RequiresCompletion
{
get { return true; }
Expand Down Expand Up @@ -347,9 +343,9 @@ public override ImmutableArray<Location> Locations
{
if (_locations.IsDefault)
{
ImmutableInterlocked.InterlockedCompareExchange(ref _locations,
_sources.AllRootNamespacesUnordered().Select(n => n.Location).AsImmutable<Location>(),
default(ImmutableArray<Location>));
ImmutableInterlocked.InterlockedInitialize(
ref _locations,
DeclaringCompilation.MergedRootDeclaration.Declarations.SelectAsArray(d => (Location)d.Location));
}

return _locations;
Expand Down
15 changes: 6 additions & 9 deletions src/Compilers/CSharp/Test/Symbol/DeclarationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,19 +116,17 @@ partial class D
Assert.Equal(0, d2.Children.Length);

var table = DeclarationTable.Empty;
Assert.Empty(table.AllRootNamespacesUnordered());

var mr = table.MergedRoot;
var mr = table.CalculateMergedRoot(null);
Assert.NotNull(mr);
Assert.True(mr.Declarations.IsEmpty);
Assert.True(table.TypeNames.IsEmpty());

table = table.AddRootDeclaration(Lazy(decl1));
mr = table.CalculateMergedRoot(null);

Assert.Equal(decl1, table.AllRootNamespacesUnordered().Single());
Assert.Equal(mr.Declarations, new[] { decl1 });
Assert.True(table.TypeNames.OrderBy(s => s).SequenceEqual(new[] { "C", "D" }));

mr = table.MergedRoot;

Assert.Equal(DeclarationKind.Namespace, mr.Kind);
Assert.Equal(string.Empty, mr.Name);

Expand Down Expand Up @@ -156,12 +154,11 @@ partial class D
Assert.Equal("D", d.Name);

table = table.AddRootDeclaration(Lazy(decl2));
mr = table.CalculateMergedRoot(null);

Assert.True(table.TypeNames.Distinct().OrderBy(s => s).SequenceEqual(new[] { "C", "D" }));

Assert.Equal(2, table.AllRootNamespacesUnordered().Intersect(new[] { decl1, decl2 }).Count());

mr = table.MergedRoot;
Assert.Equal(mr.Declarations, new[] { decl1, decl2 });

Assert.Equal(DeclarationKind.Namespace, mr.Kind);
Assert.Equal(string.Empty, mr.Name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -718,5 +718,47 @@ void f()
CheckLambdaDeclaringSyntax<SimpleLambdaExpressionSyntax>(comp, tree, "/*2*/");
CheckLambdaDeclaringSyntax<AnonymousMethodExpressionSyntax>(comp, tree, "/*3*/");
}

/// <summary>
/// Symbol location order should be preserved when trees
/// are replaced in the compilation.
/// </summary>
[WorkItem(11015, "https://github.com/dotnet/roslyn/issues/11015")]
[Fact]
public void PreserveLocationOrderOnReplaceSyntaxTree()
{
var source0 = Parse("namespace N { partial class C { } } namespace N0 { } class C0 { }");
var source1 = Parse("namespace N { partial class C { } } namespace N1 { } class C1 { }");
var source2 = Parse("namespace N { struct S { } }");
var source3 = Parse("namespace N { partial class C { } } namespace N3 { } class C3 { }");
var comp0 = CreateCompilationWithMscorlib(new[] { source0, source1, source2, source3 });
comp0.VerifyDiagnostics();
Assert.Equal(new[] { source0, source1, source2, source3 }, comp0.SyntaxTrees);

// Location order of partial class should match SyntaxTrees order.
var locations = comp0.GetMember<NamedTypeSymbol>("N.C").Locations;
Assert.Equal(new[] { source0, source1, source3 }, locations.Select(l => l.SourceTree));

// AddSyntaxTrees will add to the end.
var source4 = Parse("namespace N { partial class C { } } namespace N4 { } class C4 { }");
var comp1 = comp0.AddSyntaxTrees(source4);
locations = comp1.GetMember<NamedTypeSymbol>("N.C").Locations;
Assert.Equal(new[] { source0, source1, source3, source4 }, locations.Select(l => l.SourceTree));

// ReplaceSyntaxTree should preserve location order.
var comp2 = comp0.ReplaceSyntaxTree(source1, source4);
locations = comp2.GetMember<NamedTypeSymbol>("N.C").Locations;
Assert.Equal(new[] { source0, source4, source3 }, locations.Select(l => l.SourceTree));

// NamespaceNames and TypeNames do not match SyntaxTrees order.
// This is expected.
Assert.Equal(new[] { "", "N3", "N0", "N", "", "N4", "N" }, comp2.Declarations.NamespaceNames.ToArray());
Assert.Equal(new[] { "C3", "C0", "S", "C", "C4", "C" }, comp2.Declarations.TypeNames.ToArray());

// RemoveSyntaxTrees should preserve order of remaining trees.
var comp3 = comp2.RemoveSyntaxTrees(source0);
locations = comp3.GetMember<NamedTypeSymbol>("N.C").Locations;
Assert.Equal(new[] { source4, source3 }, locations.Select(l => l.SourceTree));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End Get
End Property

Friend ReadOnly Property MergedRootDeclaration As MergedNamespaceDeclaration
Get
Return Declarations.GetMergedRoot(Me)
End Get
End Property

Public Shadows ReadOnly Property Options As VisualBasicCompilationOptions
Get
Return _options
Expand Down Expand Up @@ -2631,7 +2637,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Throw New ArgumentException(VBResources.NoNoneSearchCriteria, NameOf(filter))
End If

Return Me.Declarations.ContainsName(predicate, filter, cancellationToken)
Return DeclarationTable.ContainsName(MergedRootDeclaration, predicate, filter, cancellationToken)
End Function

''' <summary>
Expand Down Expand Up @@ -2673,7 +2679,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Dim result = New HashSet(Of ISymbol)()
Dim spine = New List(Of MergedNamespaceOrTypeDeclaration)()

AppendSymbolsWithName(spine, Me._compilation.Declarations.MergedRoot, predicate, filter, result, cancellationToken)
AppendSymbolsWithName(spine, _compilation.MergedRootDeclaration, predicate, filter, result, cancellationToken)

Return result
End Function
Expand Down
Loading

0 comments on commit b8798c4

Please sign in to comment.