From b8798c445e019f9a39c7be501998552fb58d7209 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Wed, 25 May 2016 12:42:52 -0700 Subject: [PATCH] Order GlobalNamespace declarations to match Compilation.SyntaxTrees --- .../Portable/Compilation/CSharpCompilation.cs | 12 +- .../Portable/Declarations/DeclarationTable.cs | 71 ++++++---- .../Declarations/DeclarationTreeBuilder.cs | 4 +- .../MergedNamespaceDeclaration.cs | 7 - .../Symbols/Source/SourceAssemblySymbol.cs | 19 ++- .../Symbols/Source/SourceModuleSymbol.cs | 14 +- .../CSharp/Test/Symbol/DeclarationTests.cs | 15 +-- .../Source/DeclaringSyntaxNodeTests.cs | 42 ++++++ .../Compilation/VisualBasicCompilation.vb | 10 +- .../Portable/Declarations/DeclarationTable.vb | 52 ++++++-- .../MergedNamespaceDeclaration.vb | 5 - .../SingleNamespaceDeclaration.vb | 5 - .../Portable/Symbols/LexicalSortKey.vb | 2 +- .../Symbols/Source/SourceAssemblySymbol.vb | 3 +- .../Symbols/Source/SourceModuleSymbol.vb | 12 +- .../VisualBasic/Test/Emit/PDB/PDBTests.vb | 124 +++++++++--------- .../SymbolsTests/Source/SourceSymbolTests.vb | 80 +++++++++++ .../Test/Syntax/Parser/DeclarationTests.vb | 15 +-- 18 files changed, 327 insertions(+), 165 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 58701eaba51..5fde923dd54 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -1867,6 +1867,14 @@ internal DeclarationTable Declarations } } + internal MergedNamespaceDeclaration MergedRootDeclaration + { + get + { + return Declarations.GetMergedRoot(this); + } + } + /// /// Gets the diagnostics produced during the parsing stage of a compilation. There are no diagnostics for declarations or accessor or /// method bodies, for example. @@ -2836,7 +2844,7 @@ public override bool ContainsSymbolsWithName(Func predicate, Symbo throw new ArgumentException(CSharpResources.NoNoneSearchCriteria, nameof(filter)); } - return this.Declarations.ContainsName(predicate, filter, cancellationToken); + return DeclarationTable.ContainsName(this.MergedRootDeclaration, predicate, filter, cancellationToken); } /// @@ -2934,7 +2942,7 @@ public IEnumerable GetSymbolsWithName(Func predicate, Sym var result = new HashSet(); var spine = new List(); - AppendSymbolsWithName(spine, _compilation.Declarations.MergedRoot, predicate, filter, result, cancellationToken); + AppendSymbolsWithName(spine, _compilation.MergedRootDeclaration, predicate, filter, result, cancellationToken); return result; } diff --git a/src/Compilers/CSharp/Portable/Declarations/DeclarationTable.cs b/src/Compilers/CSharp/Portable/Declarations/DeclarationTable.cs index 8b731bffa4f..406d87f1537 100644 --- a/src/Compilers/CSharp/Portable/Declarations/DeclarationTable.cs +++ b/src/Compilers/CSharp/Portable/Declarations/DeclarationTable.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -using System.Linq; +using System.Diagnostics; using System.Threading; using Roslyn.Utilities; @@ -32,7 +32,7 @@ internal sealed partial class DeclarationTable private readonly Cache _cache; // The lazily computed total merged declaration. - private readonly Lazy _mergedRoot; + private MergedNamespaceDeclaration _mergedRoot; private readonly Lazy> _typeNames; private readonly Lazy> _namespaceNames; @@ -46,7 +46,6 @@ internal sealed partial class DeclarationTable _allOlderRootDeclarations = allOlderRootDeclarations; _latestLazyRootDeclaration = latestLazyRootDeclaration; _cache = cache ?? new Cache(this); - _mergedRoot = new Lazy(GetMergedRoot); _typeNames = new Lazy>(GetMergedTypeNames); _namespaceNames = new Lazy>(GetMergedNamespaceNames); _referenceDirectives = new Lazy>(GetMergedReferenceDirectives); @@ -88,18 +87,6 @@ public DeclarationTable RemoveRootDeclaration(Lazy 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 @@ -114,8 +101,18 @@ public IEnumerable 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) @@ -128,7 +125,31 @@ private MergedNamespaceDeclaration GetMergedRoot() } else { - return MergedNamespaceDeclaration.Create(oldRoot, _latestLazyRootDeclaration.Value); + var oldRootDeclarations = oldRoot.Declarations; + var builder = ArrayBuilder.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 + { + private readonly CSharpCompilation _compilation; + + internal RootNamespaceLocationComparer(CSharpCompilation compilation) + { + _compilation = compilation; + } + + public int Compare(SingleNamespaceDeclaration x, SingleNamespaceDeclaration y) + { + return _compilation.CompareSourceLocations(x.Location, y.Location); } } @@ -215,14 +236,6 @@ private static ISet GetNames(Declaration declaration, Predicate TypeNames { get @@ -247,14 +260,18 @@ public IEnumerable ReferenceDirectives } } - public bool ContainsName(Func predicate, SymbolFilter filter, CancellationToken cancellationToken) + public static bool ContainsName( + MergedNamespaceDeclaration mergedRoot, + Func 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(); - stack.Push(this.MergedRoot); + stack.Push(mergedRoot); while (stack.Count > 0) { diff --git a/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs b/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs index 4dfe30e339e..dec9e2508d1 100644 --- a/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs +++ b/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs @@ -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, diff --git a/src/Compilers/CSharp/Portable/Declarations/MergedNamespaceDeclaration.cs b/src/Compilers/CSharp/Portable/Declarations/MergedNamespaceDeclaration.cs index fbef92d86b9..74a1402470d 100644 --- a/src/Compilers/CSharp/Portable/Declarations/MergedNamespaceDeclaration.cs +++ b/src/Compilers/CSharp/Portable/Declarations/MergedNamespaceDeclaration.cs @@ -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 diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs index 69d01fe5049..e6d1d24d725 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs @@ -1443,13 +1443,18 @@ internal CommonAssemblyWellKnownAttributeData GetNetModuleDecodedWellKnownAttrib internal ImmutableArray> 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>.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() diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs index b6ead4a5a8e..1fd86ad09d5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs @@ -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; } @@ -347,9 +343,9 @@ public override ImmutableArray Locations { if (_locations.IsDefault) { - ImmutableInterlocked.InterlockedCompareExchange(ref _locations, - _sources.AllRootNamespacesUnordered().Select(n => n.Location).AsImmutable(), - default(ImmutableArray)); + ImmutableInterlocked.InterlockedInitialize( + ref _locations, + DeclaringCompilation.MergedRootDeclaration.Declarations.SelectAsArray(d => (Location)d.Location)); } return _locations; diff --git a/src/Compilers/CSharp/Test/Symbol/DeclarationTests.cs b/src/Compilers/CSharp/Test/Symbol/DeclarationTests.cs index 5918d360edd..d4172d6e325 100644 --- a/src/Compilers/CSharp/Test/Symbol/DeclarationTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/DeclarationTests.cs @@ -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); @@ -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); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DeclaringSyntaxNodeTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DeclaringSyntaxNodeTests.cs index e5d47d3f03b..be8c2eee2ee 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DeclaringSyntaxNodeTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DeclaringSyntaxNodeTests.cs @@ -718,5 +718,47 @@ void f() CheckLambdaDeclaringSyntax(comp, tree, "/*2*/"); CheckLambdaDeclaringSyntax(comp, tree, "/*3*/"); } + + /// + /// Symbol location order should be preserved when trees + /// are replaced in the compilation. + /// + [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("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("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("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("N.C").Locations; + Assert.Equal(new[] { source4, source3 }, locations.Select(l => l.SourceTree)); + } } } diff --git a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index e81699abfe7..a21bbd73fac 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -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 @@ -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 ''' @@ -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 diff --git a/src/Compilers/VisualBasic/Portable/Declarations/DeclarationTable.vb b/src/Compilers/VisualBasic/Portable/Declarations/DeclarationTable.vb index 1f513545db4..7198639f430 100644 --- a/src/Compilers/VisualBasic/Portable/Declarations/DeclarationTable.vb +++ b/src/Compilers/VisualBasic/Portable/Declarations/DeclarationTable.vb @@ -44,7 +44,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Private ReadOnly _cache As Cache ' The lazily computed total merged declaration. - Private ReadOnly _mergedRoot As Lazy(Of MergedNamespaceDeclaration) + Private _mergedRoot As MergedNamespaceDeclaration Private ReadOnly _typeNames As Lazy(Of ICollection(Of String)) Private ReadOnly _namespaceNames As Lazy(Of ICollection(Of String)) @@ -58,7 +58,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Me._allOlderRootDeclarations = allOlderRootDeclarations Me._latestLazyRootDeclaration = latestLazyRootDeclaration Me._cache = If(cache, New Cache(Me)) - Me._mergedRoot = New Lazy(Of MergedNamespaceDeclaration)(AddressOf GetMergedRoot) Me._typeNames = New Lazy(Of ICollection(Of String))(AddressOf GetMergedTypeNames) Me._namespaceNames = New Lazy(Of ICollection(Of String))(AddressOf GetMergedNamespaceNames) Me._referenceDirectives = New Lazy(Of ICollection(Of ReferenceDirective))(AddressOf GetMergedReferenceDirectives) @@ -140,7 +139,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ' old merged root new merged root ' / | | | \ \ ' old singles forest new single tree - Private Function GetMergedRoot() As MergedNamespaceDeclaration + Public Function GetMergedRoot(compilation As VisualBasicCompilation) As MergedNamespaceDeclaration + Debug.Assert(compilation.Declarations Is Me) + If _mergedRoot Is Nothing Then + Interlocked.CompareExchange(_mergedRoot, CalculateMergedRoot(compilation), Nothing) + End If + Return _mergedRoot + End Function + + ' Internal for unit tests only. + Friend Function CalculateMergedRoot(compilation As VisualBasicCompilation) As MergedNamespaceDeclaration Dim oldRoot = Me._cache.MergedRoot.Value Dim latestRoot = GetLatestRootDeclarationIfAny(includeEmbedded:=True) If latestRoot Is Nothing Then @@ -148,10 +156,32 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ElseIf oldRoot Is Nothing Then Return MergedNamespaceDeclaration.Create(latestRoot) Else - Return MergedNamespaceDeclaration.Create(oldRoot, latestRoot) + Dim oldRootDeclarations = oldRoot.Declarations + Dim builder = ArrayBuilder(Of SingleNamespaceDeclaration).GetInstance(oldRootDeclarations.Length + 1) + builder.AddRange(oldRootDeclarations) + builder.Add(_latestLazyRootDeclaration.Root.Value) + ' Sort the root namespace declarations to match the order of SyntaxTrees. + If compilation IsNot Nothing Then + builder.Sort(New RootNamespaceLocationComparer(compilation)) + End If + Return MergedNamespaceDeclaration.Create(builder.ToImmutableAndFree()) End If End Function + Private NotInheritable Class RootNamespaceLocationComparer + Implements IComparer(Of SingleNamespaceDeclaration) + + Private ReadOnly _compilation As VisualBasicCompilation + + Friend Sub New(compilation As VisualBasicCompilation) + _compilation = compilation + End Sub + + Public Function Compare(x As SingleNamespaceDeclaration, y As SingleNamespaceDeclaration) As Integer Implements IComparer(Of SingleNamespaceDeclaration).Compare + Return _compilation.CompareSourceLocations(x.Location, y.Location) + End Function + End Class + Private Function GetMergedTypeNames() As ICollection(Of String) Dim cachedTypeNames = Me._cache.TypeNames.Value Dim latestRoot = GetLatestRootDeclarationIfAny(includeEmbedded:=True) @@ -222,12 +252,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Return result.AsCaseInsensitiveCollection() End Function - Public ReadOnly Property MergedRoot As MergedNamespaceDeclaration - Get - Return _mergedRoot.Value - End Get - End Property - Public ReadOnly Property TypeNames As ICollection(Of String) Get Return _typeNames.Value @@ -246,14 +270,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property - Public Function ContainsName(predicate As Func(Of String, Boolean), filter As SymbolFilter, cancellationToken As CancellationToken) As Boolean + Public Shared Function ContainsName( + mergedRoot As MergedNamespaceDeclaration, + predicate As Func(Of String, Boolean), + filter As SymbolFilter, + cancellationToken As CancellationToken) As Boolean Dim includeNamespace = (filter And SymbolFilter.Namespace) = SymbolFilter.Namespace Dim includeType = (filter And SymbolFilter.Type) = SymbolFilter.Type Dim includeMember = (filter And SymbolFilter.Member) = SymbolFilter.Member Dim stack = New Stack(Of MergedNamespaceOrTypeDeclaration)() - stack.Push(Me.MergedRoot) + stack.Push(mergedRoot) While stack.Count > 0 cancellationToken.ThrowIfCancellationRequested() diff --git a/src/Compilers/VisualBasic/Portable/Declarations/MergedNamespaceDeclaration.vb b/src/Compilers/VisualBasic/Portable/Declarations/MergedNamespaceDeclaration.vb index 6d093535fdb..d4bf44136ee 100644 --- a/src/Compilers/VisualBasic/Portable/Declarations/MergedNamespaceDeclaration.vb +++ b/src/Compilers/VisualBasic/Portable/Declarations/MergedNamespaceDeclaration.vb @@ -29,11 +29,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Return New MergedNamespaceDeclaration(declarations.AsImmutableOrNull) End Function - Public Shared Function Create(mergedDeclaration As MergedNamespaceDeclaration, - declaration As SingleNamespaceDeclaration) As MergedNamespaceDeclaration - Return New MergedNamespaceDeclaration(mergedDeclaration._declarations.Add(declaration)) - End Function - Public Overrides ReadOnly Property Kind As DeclarationKind Get Return DeclarationKind.Namespace diff --git a/src/Compilers/VisualBasic/Portable/Declarations/SingleNamespaceDeclaration.vb b/src/Compilers/VisualBasic/Portable/Declarations/SingleNamespaceDeclaration.vb index 816557e0c9f..80818b4287c 100644 --- a/src/Compilers/VisualBasic/Portable/Declarations/SingleNamespaceDeclaration.vb +++ b/src/Compilers/VisualBasic/Portable/Declarations/SingleNamespaceDeclaration.vb @@ -5,11 +5,6 @@ Imports System.Collections.Generic Imports System.Collections.Immutable Imports System.Diagnostics Imports System.Linq -Imports System.Text -Imports System.Threading -Imports Microsoft.CodeAnalysis.Collections -Imports Microsoft.CodeAnalysis.Text -Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols diff --git a/src/Compilers/VisualBasic/Portable/Symbols/LexicalSortKey.vb b/src/Compilers/VisualBasic/Portable/Symbols/LexicalSortKey.vb index c2a42800b0b..6590cb350d1 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/LexicalSortKey.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/LexicalSortKey.vb @@ -112,7 +112,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Else Debug.Assert(location.PossiblyEmbeddedOrMySourceSpan.Start >= 0) - Dim tree = DirectCast(location.SourceTree, VisualBasicSyntaxTree) + Dim tree = DirectCast(location.PossiblyEmbeddedOrMySourceTree, VisualBasicSyntaxTree) Debug.Assert(tree Is Nothing OrElse tree.GetEmbeddedKind = location.EmbeddedKind) Dim treeKind As SyntaxTreeKind = GetEmbeddedKind(tree) diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceAssemblySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceAssemblySymbol.vb index b789709c81f..c3cc29eb11e 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceAssemblySymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceAssemblySymbol.vb @@ -461,8 +461,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Friend Function GetAttributeDeclarations() As ImmutableArray(Of SyntaxList(Of AttributeListSyntax)) Dim attributeBlocks = ArrayBuilder(Of SyntaxList(Of AttributeListSyntax)).GetInstance() + Dim declarations = DeclaringCompilation.MergedRootDeclaration.Declarations - For Each rootNs In DeclaringCompilation.Declarations.AllRootNamespaces + For Each rootNs As RootSingleNamespaceDeclaration In declarations If rootNs.HasAssemblyAttributes Then Dim compilationUnitSyntax = DirectCast(rootNs.Location.SourceTree.GetRoot(), CompilationUnitSyntax) Dim attributeStatements = compilationUnitSyntax.Attributes diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceModuleSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceModuleSymbol.vb index 50071d2983b..e46d429c896 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceModuleSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceModuleSymbol.vb @@ -102,9 +102,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Get Select Case DeclaringCompilation.Options.Platform Case Platform.Arm - Return System.Reflection.PortableExecutable.Machine.ARMThumb2 + Return System.Reflection.PortableExecutable.Machine.ArmThumb2 Case Platform.X64 - Return System.Reflection.PortableExecutable.Machine.AMD64 + Return System.Reflection.PortableExecutable.Machine.Amd64 Case Platform.Itanium Return System.Reflection.PortableExecutable.Machine.IA64 Case Else @@ -172,7 +172,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Overrides ReadOnly Property GlobalNamespace As NamespaceSymbol Get If _lazyGlobalNamespace Is Nothing Then - Dim globalNS = New SourceNamespaceSymbol(_declarationTable.MergedRoot, Nothing, Me) + Dim globalNS = New SourceNamespaceSymbol(DeclaringCompilation.MergedRootDeclaration, Nothing, Me) Interlocked.CompareExchange(_lazyGlobalNamespace, globalNS, Nothing) End If @@ -193,9 +193,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Overrides ReadOnly Property Locations As ImmutableArray(Of Location) Get If _locations.IsDefault Then - Dim locs = _declarationTable.AllRootNamespaces().SelectAsArray(Function(n) n.Location) - - ImmutableInterlocked.InterlockedCompareExchange(_locations, locs, Nothing) + ImmutableInterlocked.InterlockedInitialize( + _locations, + DeclaringCompilation.MergedRootDeclaration.Declarations.SelectAsArray(Function(d) d.Location)) End If Return _locations diff --git a/src/Compilers/VisualBasic/Test/Emit/PDB/PDBTests.vb b/src/Compilers/VisualBasic/Test/Emit/PDB/PDBTests.vb index 312a0cc322e..1da9cb85cdd 100644 --- a/src/Compilers/VisualBasic/Test/Emit/PDB/PDBTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/PDB/PDBTests.vb @@ -195,16 +195,6 @@ End Class compilation.VerifyPdb( - - - - - - - - - - @@ -215,6 +205,17 @@ End Class + + + + + + + + + + + @@ -279,15 +280,14 @@ End Class - + - - - - + + + - - + + @@ -302,7 +302,7 @@ End Class - + @@ -318,7 +318,7 @@ End Class - + @@ -334,7 +334,7 @@ End Class - + @@ -350,7 +350,7 @@ End Class - + @@ -371,7 +371,7 @@ End Class - + @@ -382,7 +382,7 @@ End Class - + @@ -392,7 +392,7 @@ End Class - + @@ -411,7 +411,7 @@ End Class - + @@ -422,7 +422,7 @@ End Class - + @@ -3871,26 +3871,6 @@ End Class compilation.VerifyPdb( - - - - - - - - - - - - - - - - - - - - @@ -3901,6 +3881,17 @@ End Class + + + + + + + + + + + @@ -3965,15 +3956,24 @@ End Class - + - - - - + + + - - + + + + + + + + + + + + @@ -3988,7 +3988,7 @@ End Class - + @@ -4004,7 +4004,7 @@ End Class - + @@ -4020,7 +4020,7 @@ End Class - + @@ -4036,7 +4036,7 @@ End Class - + @@ -4057,7 +4057,7 @@ End Class - + @@ -4068,7 +4068,7 @@ End Class - + @@ -4078,7 +4078,7 @@ End Class - + @@ -4097,7 +4097,7 @@ End Class - + @@ -4108,7 +4108,7 @@ End Class - + diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/SourceSymbolTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/SourceSymbolTests.vb index 34adb5da509..d2fe8061088 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/SourceSymbolTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/SourceSymbolTests.vb @@ -606,5 +606,85 @@ BC31060: event 'E' implicitly defines 'add_E', which conflicts with a member of Assert.Equal("Add_E", members(3).MetadataName) End Sub + ''' + ''' Symbol location order should be preserved when trees + ''' are replaced in the compilation. + ''' + + + Public Sub PreserveLocationOrderOnReplaceSyntaxTree() + Dim source0 = Parse( +"Namespace N + Partial Class C + End Class +End Namespace +Namespace N0 + Class C0 + End Class +End Namespace") + Dim source1 = Parse( +"Namespace N + Partial Class C + End Class +End Namespace +Namespace N1 + Class C1 + End Class +End Namespace") + Dim source2 = Parse( +"Namespace N + Structure S + End Structure +End Namespace") + Dim source3 = Parse( +"Namespace N + Partial Class C + End Class +End Namespace +Namespace N3 + Class C3 + End Class +End Namespace") + Dim comp0 = CompilationUtils.CreateCompilationWithMscorlib({source0, source1, source2, source3}, options:=TestOptions.ReleaseDll) + comp0.AssertTheseDiagnostics() + Assert.Equal({source0, source1, source2, source3}, comp0.SyntaxTrees) + + Dim locations = comp0.GlobalNamespace.Locations + Assert.Equal({"MyTemplateLocation", "SourceLocation", "SourceLocation", "SourceLocation", "SourceLocation", "MetadataLocation"}, locations.Select(Function(l) l.GetType().Name)) + + ' Location order of partial class should match SyntaxTrees order. + locations = comp0.GetMember(Of NamedTypeSymbol)("N.C").Locations + Assert.Equal({source0, source1, source3}, locations.Select(Function(l) l.SourceTree)) + + ' AddSyntaxTrees will add to the end. + Dim source4 = Parse( +"Namespace N + Partial Class C + End Class +End Namespace +Namespace N4 + Class C4 + End Class +End Namespace") + Dim comp1 = comp0.AddSyntaxTrees(source4) + locations = comp1.GetMember(Of NamedTypeSymbol)("N.C").Locations + Assert.Equal({source0, source1, source3, source4}, locations.Select(Function(l) l.SourceTree)) + + ' ReplaceSyntaxTree should preserve location order. + Dim comp2 = comp0.ReplaceSyntaxTree(source1, source4) + locations = comp2.GetMember(Of NamedTypeSymbol)("N.C").Locations + Assert.Equal({source0, source4, source3}, locations.Select(Function(l) l.SourceTree)) + + ' NamespaceNames and TypeNames do not match SyntaxTrees order. + ' This is expected. + Assert.Equal({"", "N3", "N0", "N", "", "N4", "N"}, comp2.Declarations.NamespaceNames.ToArray()) + Assert.Equal({"C3", "C0", "S", "C", "C4", "C"}, comp2.Declarations.TypeNames.ToArray()) + + ' RemoveSyntaxTrees should preserve order of remaining trees. + Dim comp3 = comp2.RemoveSyntaxTrees(source0) + locations = comp3.GetMember(Of NamedTypeSymbol)("N.C").Locations + Assert.Equal({source4, source3}, locations.Select(Function(l) l.SourceTree)) + End Sub + End Class End Namespace diff --git a/src/Compilers/VisualBasic/Test/Syntax/Parser/DeclarationTests.vb b/src/Compilers/VisualBasic/Test/Syntax/Parser/DeclarationTests.vb index 18e16a72319..1c739e1c9a4 100644 --- a/src/Compilers/VisualBasic/Test/Syntax/Parser/DeclarationTests.vb +++ b/src/Compilers/VisualBasic/Test/Syntax/Parser/DeclarationTests.vb @@ -106,14 +106,15 @@ end namespace Dim table As DeclarationTable = DeclarationTable.Empty Assert.False(table.AllRootNamespaces().Any) - Dim mr = table.MergedRoot + Dim mr = table.CalculateMergedRoot(Nothing) Assert.NotNull(mr) + Assert.True(mr.Declarations.IsEmpty) + Assert.True(table.TypeNames.IsEmpty()) table = table.AddRootDeclaration(Lazy(decl1)) + mr = table.CalculateMergedRoot(Nothing) - Assert.Same(decl1, table.AllRootNamespaces().Single()) - - mr = table.MergedRoot + Assert.Equal(mr.Declarations, {decl1}) Assert.Equal(DeclarationKind.Namespace, mr.Kind) Assert.Equal("", mr.Name) @@ -142,11 +143,9 @@ end namespace Assert.Equal("D", d.Name) table = table.AddRootDeclaration(Lazy(decl2)) - Assert.Equal(2, table.AllRootNamespaces().Length) - Assert.True(table.AllRootNamespaces().Contains(decl1)) - Assert.True(table.AllRootNamespaces().Contains(decl2)) + mr = table.CalculateMergedRoot(Nothing) - mr = table.MergedRoot + Assert.Equal(mr.Declarations, {decl1, decl2}) Assert.Equal(DeclarationKind.Namespace, mr.Kind) Assert.Equal("", mr.Name) -- GitLab