From b17ab52e63595647d546b80d00b2af5ddf1cb7ae Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Thu, 30 May 2024 16:30:39 -0700 Subject: [PATCH 1/2] Reduce Linq usage in SymbolDisplayVisitor.CreateAliasMap This method showed up in a customer profile, and I was able to reproduce locally. --- .../SymbolDisplayVisitor_Minimal.cs | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Minimal.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Minimal.cs index b7a82cb78d6b0..1143d12fec94a 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Minimal.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Minimal.cs @@ -196,26 +196,39 @@ private IDictionary CreateAliasMap() // NOTE(cyrusn): If we're currently in a block of usings, then we want to collect the // aliases that are higher up than this block. Using aliases declared in a block of // usings are not usable from within that same block. - var usingDirective = GetAncestorOrThis(startNode); - if (usingDirective != null) + while (startNode != null) { - startNode = usingDirective.Parent!.Parent!; + if (startNode is UsingDirectiveSyntax) + { + startNode = startNode.Parent!.Parent!; + break; + } + + startNode = startNode.Parent; } - var usingAliases = GetAncestorsOrThis(startNode) - .SelectMany(n => n.Usings) - .Concat(GetAncestorsOrThis(startNode).SelectMany(c => c.Usings)) - .Where(u => u.Alias != null) - .Select(u => semanticModel.GetDeclaredSymbol(u) as IAliasSymbol) - .WhereNotNull(); + startNode ??= token.Parent; var builder = ImmutableDictionary.CreateBuilder(); - foreach (var alias in usingAliases) + while (startNode != null) { - if (!builder.ContainsKey(alias.Target)) + var usings = (startNode as BaseNamespaceDeclarationSyntax)?.Usings; + usings ??= (startNode as CompilationUnitSyntax)?.Usings; + + if (usings != null) { - builder.Add(alias.Target, alias); + foreach (var u in usings) + { + if (u.Alias != null + && semanticModel.GetDeclaredSymbol(u) is IAliasSymbol aliasSymbol + && !builder.ContainsKey(aliasSymbol.Target)) + { + builder.Add(aliasSymbol.Target, aliasSymbol); + } + } } + + startNode = startNode.Parent; } return builder.ToImmutable(); @@ -287,18 +300,6 @@ private string RemoveAttributeSuffixIfNecessary(INamedTypeSymbol symbol, string return symbolName; } - private static T? GetAncestorOrThis(SyntaxNode node) where T : SyntaxNode - { - return GetAncestorsOrThis(node).FirstOrDefault(); - } - - private static IEnumerable GetAncestorsOrThis(SyntaxNode node) where T : SyntaxNode - { - return node == null - ? SpecializedCollections.EmptyEnumerable() - : node.AncestorsAndSelf().OfType(); - } - private IDictionary AliasMap { get From 9721d189a71ffc2bdb9d086713233e37f3cdd84c Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Sat, 1 Jun 2024 09:18:30 -0700 Subject: [PATCH 2/2] Update comment to be a bit more clear --- .../Portable/SymbolDisplay/SymbolDisplayVisitor_Minimal.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Minimal.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Minimal.cs index 1143d12fec94a..6f1c92c44ad83 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Minimal.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Minimal.cs @@ -195,7 +195,8 @@ private IDictionary CreateAliasMap() // NOTE(cyrusn): If we're currently in a block of usings, then we want to collect the // aliases that are higher up than this block. Using aliases declared in a block of - // usings are not usable from within that same block. + // usings are not usable from within that same block. This loop moves us outside of the + // immediately enclosing using directive, if any. while (startNode != null) { if (startNode is UsingDirectiveSyntax)