From 7a1b12c00c8c154fc0ab34cb508e4df57f44ca48 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 15 May 2024 17:08:10 -0700 Subject: [PATCH 1/9] Avoid unnecessary allocations while finding token matches in a file --- .../Shared/Extensions/SourceTextExtensions.cs | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs index 47d9178365398..36ffa2d3bc228 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs @@ -70,22 +70,24 @@ public static TextChangeRange GetEncompassingTextChangeRange(this SourceText new return TextChangeRange.Collapse(ranges); } - public static int IndexOf(this SourceText text, string value, int startIndex, bool caseSensitive) + public static int IndexOf(this SourceText text, string searchString, int startIndex, bool caseSensitive) { - var length = text.Length - value.Length; - var normalized = caseSensitive ? value : CaseInsensitiveComparison.ToLower(value); + if (searchString.Length == 0) + return -1; + + var searchStringLength = searchString.Length; + var textEnd = text.Length - searchStringLength; - for (var i = startIndex; i <= length; i++) + // Get the first character of the value to search for, with the correct case sensitivity. This way, we don't + // have to call through ToLower when we're trying to find the starting point of a word match + var normalizedFirstChar = caseSensitive ? searchString[0] : CaseInsensitiveComparison.ToLower(searchString[0]); + + for (var i = startIndex; i <= textEnd; i++) { var match = true; - for (var j = 0; j < normalized.Length; j++) + for (var j = 0; j < searchStringLength; j++) { - // just use indexer of source text. perf of indexer depends on actual implementation of SourceText. - // * all of our implementation at editor layer should provide either O(1) or O(log n). - // - // only one implementation we have that could have bad indexer perf is CompositeText with heavily modified text - // at compiler layer but I believe that being used in find all reference will be very rare if not none. - if (!Match(normalized[j], text[i + j], caseSensitive)) + if (!Match(GetValueChar(j), text[i + j], caseSensitive)) { match = false; break; @@ -93,12 +95,13 @@ public static int IndexOf(this SourceText text, string value, int startIndex, bo } if (match) - { return i; - } } return -1; + + char GetValueChar(int index) + => index == 0 ? normalizedFirstChar : caseSensitive ? searchString[index] : CaseInsensitiveComparison.ToLower(searchString[index]); } public static int LastIndexOf(this SourceText text, string value, int startIndex, bool caseSensitive) From b21abc49813710f2a6f4496d867aa887cf41481b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 17 May 2024 00:16:38 -0700 Subject: [PATCH 2/9] Seeing what breaks --- .../NameSyntaxClassifier.cs | 45 ++++++++----------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs index 3b602d47553c0..13c2f36fc72ac 100644 --- a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs +++ b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs @@ -29,19 +29,13 @@ public override void AddClassifications( SegmentedList result, CancellationToken cancellationToken) { - if (syntax is NameSyntax name) - { + if (syntax is IdentifierNameSyntax name) ClassifyTypeSyntax(name, semanticModel, result, cancellationToken); - } } public override ImmutableArray SyntaxNodeTypes { get; } = [ - typeof(AliasQualifiedNameSyntax), - typeof(GenericNameSyntax), typeof(IdentifierNameSyntax), - typeof(QualifiedNameSyntax), - typeof(SimpleNameSyntax), ]; protected override int? GetRightmostNameArity(SyntaxNode node) @@ -58,7 +52,7 @@ protected override bool IsParentAnAttribute(SyntaxNode node) => node.IsParentKind(SyntaxKind.Attribute); private void ClassifyTypeSyntax( - NameSyntax name, + IdentifierNameSyntax name, SemanticModel semanticModel, SegmentedList result, CancellationToken cancellationToken) @@ -73,7 +67,7 @@ private void ClassifyTypeSyntax( } private bool TryClassifySymbol( - NameSyntax name, + IdentifierNameSyntax name, SymbolInfo symbolInfo, SegmentedList result) { @@ -104,7 +98,7 @@ CandidateReason.Ambiguous or } private static bool TryClassifyAmbiguousSymbol( - NameSyntax name, + IdentifierNameSyntax name, SymbolInfo symbolInfo, SegmentedList result) { @@ -138,19 +132,18 @@ private static bool TryClassifyAmbiguousSymbol( } private static bool TryClassifySymbol( - NameSyntax name, + IdentifierNameSyntax name, [NotNullWhen(returnValue: true)] ISymbol? symbol, out ClassifiedSpan classifiedSpan) { // For Namespace parts, we want don't want to classify the QualifiedNameSyntax // nodes, we instead wait for the each IdentifierNameSyntax node to avoid // creating overlapping ClassifiedSpans. - if (symbol is INamespaceSymbol namespaceSymbol && - name is IdentifierNameSyntax identifierNameSyntax) + if (symbol is INamespaceSymbol namespaceSymbol) { // Do not classify the global:: namespace. It is already syntactically classified as a keyword. var isGlobalNamespace = namespaceSymbol.IsGlobalNamespace && - identifierNameSyntax.Identifier.IsKind(SyntaxKind.GlobalKeyword); + name.Identifier.IsKind(SyntaxKind.GlobalKeyword); if (isGlobalNamespace) { classifiedSpan = default; @@ -170,7 +163,7 @@ private static bool TryClassifySymbol( return true; } - if (name is IdentifierNameSyntax { Identifier.Text: "args" } && + if (name is { Identifier.Text: "args" } && symbol is IParameterSymbol { ContainingSymbol: IMethodSymbol { Name: WellKnownMemberNames.TopLevelStatementsEntryPointMethodName } }) { classifiedSpan = new ClassifiedSpan(name.Span, ClassificationTypeNames.Keyword); @@ -281,7 +274,7 @@ private static string GetClassificationForMethod(IMethodSymbol methodSymbol) : ClassificationTypeNames.MethodName; } - private static bool IsInVarContext(NameSyntax name) + private static bool IsInVarContext(IdentifierNameSyntax name) { return name.CheckParent(v => v.Type == name) || @@ -293,18 +286,17 @@ private static bool IsInVarContext(NameSyntax name) } private static bool TryClassifyFromIdentifier( - NameSyntax name, + IdentifierNameSyntax name, SymbolInfo symbolInfo, SegmentedList result) { // Okay - it wasn't a type. If the syntax matches "var q = from" or "q = from", and from // doesn't bind to anything then optimistically color from as a keyword. - if (name is IdentifierNameSyntax identifierName && - identifierName.Identifier.HasMatchingText(SyntaxKind.FromKeyword) && + if (name.Identifier.HasMatchingText(SyntaxKind.FromKeyword) && symbolInfo.Symbol == null) { - var token = identifierName.Identifier; - if (identifierName.IsRightSideOfAnyAssignExpression() || identifierName.IsVariableDeclaratorValue()) + var token = name.Identifier; + if (name.IsRightSideOfAnyAssignExpression() || name.IsVariableDeclaratorValue()) { result.Add(new ClassifiedSpan(token.Span, ClassificationTypeNames.Keyword)); return true; @@ -315,28 +307,27 @@ private static bool TryClassifyFromIdentifier( } private static bool TryClassifyValueIdentifier( - NameSyntax name, + IdentifierNameSyntax name, SymbolInfo symbolInfo, SegmentedList result) { - if (name is IdentifierNameSyntax identifierName && - symbolInfo.Symbol.IsImplicitValueParameter()) + if (symbolInfo.Symbol.IsImplicitValueParameter()) { - result.Add(new ClassifiedSpan(identifierName.Identifier.Span, ClassificationTypeNames.Keyword)); + result.Add(new ClassifiedSpan(name.Identifier.Span, ClassificationTypeNames.Keyword)); return true; } return false; } - private static bool TryClassifySomeContextualKeywordIdentifiersAsKeywords(NameSyntax name, SymbolInfo symbolInfo, SegmentedList result) + private static bool TryClassifySomeContextualKeywordIdentifiersAsKeywords(IdentifierNameSyntax name, SymbolInfo symbolInfo, SegmentedList result) { // Simple approach, if the user ever types one of identifiers from the list and it doesn't actually bind to anything, presume that // they intend to use it as a keyword. This works for all error // cases, while not conflicting with the extremely rare case where such identifiers might actually be used to // reference actual symbols with that names. if (symbolInfo.GetAnySymbol() is null && - name is IdentifierNameSyntax { Identifier.Text: "async" or "nameof" or "partial" }) + name is { Identifier.Text: "async" or "nameof" or "partial" }) { result.Add(new(name.Span, ClassificationTypeNames.Keyword)); return true; From 6a613c7ea6e7cc08bdbe64eeec3f872d9614677d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 17 May 2024 00:18:03 -0700 Subject: [PATCH 3/9] Remove --- .../SyntaxClassification/NameSyntaxClassifier.cs | 10 ---------- .../AbstractNameSyntaxClassifier.cs | 1 - .../SyntaxClassification/NameSyntaxClassifier.vb | 8 -------- 3 files changed, 19 deletions(-) diff --git a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs index 13c2f36fc72ac..be98a7c1d04c6 100644 --- a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs +++ b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs @@ -38,16 +38,6 @@ public override void AddClassifications( typeof(IdentifierNameSyntax), ]; - protected override int? GetRightmostNameArity(SyntaxNode node) - { - if (node is ExpressionSyntax expressionSyntax) - { - return expressionSyntax.GetRightmostName()?.Arity; - } - - return null; - } - protected override bool IsParentAnAttribute(SyntaxNode node) => node.IsParentKind(SyntaxKind.Attribute); diff --git a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractNameSyntaxClassifier.cs b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractNameSyntaxClassifier.cs index 9764597b902f9..6d788161270b0 100644 --- a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractNameSyntaxClassifier.cs +++ b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractNameSyntaxClassifier.cs @@ -11,7 +11,6 @@ namespace Microsoft.CodeAnalysis.Classification.Classifiers; internal abstract class AbstractNameSyntaxClassifier : AbstractSyntaxClassifier { - protected abstract int? GetRightmostNameArity(SyntaxNode node); protected abstract bool IsParentAnAttribute(SyntaxNode node); protected ISymbol? TryGetSymbol(SyntaxNode node, SymbolInfo symbolInfo) diff --git a/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.vb b/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.vb index 0cc5c53729020..b7f441c1d32ab 100644 --- a/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.vb +++ b/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.vb @@ -60,14 +60,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Classification.Classifiers End If End Sub - Protected Overrides Function GetRightmostNameArity(node As SyntaxNode) As Integer? - If TypeOf (node) Is ExpressionSyntax Then - Return DirectCast(node, ExpressionSyntax).GetRightmostName()?.Arity - End If - - Return Nothing - End Function - Protected Overrides Function IsParentAnAttribute(node As SyntaxNode) As Boolean Return node.IsParentKind(SyntaxKind.Attribute) End Function From 7d666833d747faaef0ceba89f4e274931caab1ea Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 17 May 2024 00:21:31 -0700 Subject: [PATCH 4/9] Simply name --- .../NameSyntaxClassifier.cs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs index be98a7c1d04c6..ae1b17f333db6 100644 --- a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs +++ b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs @@ -29,20 +29,21 @@ public override void AddClassifications( SegmentedList result, CancellationToken cancellationToken) { - if (syntax is IdentifierNameSyntax name) + if (syntax is SimpleNameSyntax name) ClassifyTypeSyntax(name, semanticModel, result, cancellationToken); } public override ImmutableArray SyntaxNodeTypes { get; } = [ typeof(IdentifierNameSyntax), + typeof(GenericNameSyntax), ]; protected override bool IsParentAnAttribute(SyntaxNode node) => node.IsParentKind(SyntaxKind.Attribute); private void ClassifyTypeSyntax( - IdentifierNameSyntax name, + SimpleNameSyntax name, SemanticModel semanticModel, SegmentedList result, CancellationToken cancellationToken) @@ -57,7 +58,7 @@ private void ClassifyTypeSyntax( } private bool TryClassifySymbol( - IdentifierNameSyntax name, + SimpleNameSyntax name, SymbolInfo symbolInfo, SegmentedList result) { @@ -88,7 +89,7 @@ CandidateReason.Ambiguous or } private static bool TryClassifyAmbiguousSymbol( - IdentifierNameSyntax name, + SimpleNameSyntax name, SymbolInfo symbolInfo, SegmentedList result) { @@ -122,7 +123,7 @@ private static bool TryClassifyAmbiguousSymbol( } private static bool TryClassifySymbol( - IdentifierNameSyntax name, + SimpleNameSyntax name, [NotNullWhen(returnValue: true)] ISymbol? symbol, out ClassifiedSpan classifiedSpan) { @@ -153,7 +154,7 @@ private static bool TryClassifySymbol( return true; } - if (name is { Identifier.Text: "args" } && + if (name is IdentifierNameSyntax { Identifier.Text: "args" } && symbol is IParameterSymbol { ContainingSymbol: IMethodSymbol { Name: WellKnownMemberNames.TopLevelStatementsEntryPointMethodName } }) { classifiedSpan = new ClassifiedSpan(name.Span, ClassificationTypeNames.Keyword); @@ -264,7 +265,7 @@ private static string GetClassificationForMethod(IMethodSymbol methodSymbol) : ClassificationTypeNames.MethodName; } - private static bool IsInVarContext(IdentifierNameSyntax name) + private static bool IsInVarContext(SimpleNameSyntax name) { return name.CheckParent(v => v.Type == name) || @@ -276,7 +277,7 @@ private static bool IsInVarContext(IdentifierNameSyntax name) } private static bool TryClassifyFromIdentifier( - IdentifierNameSyntax name, + SimpleNameSyntax name, SymbolInfo symbolInfo, SegmentedList result) { @@ -297,7 +298,7 @@ private static bool TryClassifyFromIdentifier( } private static bool TryClassifyValueIdentifier( - IdentifierNameSyntax name, + SimpleNameSyntax name, SymbolInfo symbolInfo, SegmentedList result) { @@ -310,14 +311,14 @@ private static bool TryClassifyValueIdentifier( return false; } - private static bool TryClassifySomeContextualKeywordIdentifiersAsKeywords(IdentifierNameSyntax name, SymbolInfo symbolInfo, SegmentedList result) + private static bool TryClassifySomeContextualKeywordIdentifiersAsKeywords(SimpleNameSyntax name, SymbolInfo symbolInfo, SegmentedList result) { // Simple approach, if the user ever types one of identifiers from the list and it doesn't actually bind to anything, presume that // they intend to use it as a keyword. This works for all error // cases, while not conflicting with the extremely rare case where such identifiers might actually be used to // reference actual symbols with that names. if (symbolInfo.GetAnySymbol() is null && - name is { Identifier.Text: "async" or "nameof" or "partial" }) + name is IdentifierNameSyntax { Identifier.Text: "async" or "nameof" or "partial" }) { result.Add(new(name.Span, ClassificationTypeNames.Keyword)); return true; From c32a40f89090ae88e52e64064dd1243504eae4e7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 17 May 2024 00:24:29 -0700 Subject: [PATCH 5/9] full span intersection --- .../AbstractEmbeddedLanguageClassificationService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/EmbeddedLanguages/Classification/AbstractEmbeddedLanguageClassificationService.cs b/src/Features/Core/Portable/EmbeddedLanguages/Classification/AbstractEmbeddedLanguageClassificationService.cs index 6b73e38851462..acb8bda57ac91 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/Classification/AbstractEmbeddedLanguageClassificationService.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/Classification/AbstractEmbeddedLanguageClassificationService.cs @@ -89,7 +89,7 @@ public void VisitTokens(SyntaxNode node) while (stack.TryPop(out var currentNodeOrToken)) { _cancellationToken.ThrowIfCancellationRequested(); - if (currentNodeOrToken.Span.IntersectsWith(_textSpan)) + if (currentNodeOrToken.FullSpan.IntersectsWith(_textSpan)) { if (currentNodeOrToken.IsNode) { From 2d3828e66ec95deaf1bb59fed84d10a17531feb4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 17 May 2024 10:14:07 -0700 Subject: [PATCH 6/9] Update src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs --- .../SyntaxClassification/NameSyntaxClassifier.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs index ae1b17f333db6..57c81db96db8a 100644 --- a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs +++ b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs @@ -130,7 +130,8 @@ private static bool TryClassifySymbol( // For Namespace parts, we want don't want to classify the QualifiedNameSyntax // nodes, we instead wait for the each IdentifierNameSyntax node to avoid // creating overlapping ClassifiedSpans. - if (symbol is INamespaceSymbol namespaceSymbol) + if (symbol is INamespaceSymbol namespaceSymbol && + name is IdentifierNameSyntax) { // Do not classify the global:: namespace. It is already syntactically classified as a keyword. var isGlobalNamespace = namespaceSymbol.IsGlobalNamespace && From 5b0ff9d89f84d831d327c21d350e3d363c7ab38c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 17 May 2024 10:14:49 -0700 Subject: [PATCH 7/9] revert --- .../Shared/Extensions/SourceTextExtensions.cs | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs index 7fd12d3590a17..1eef6e5b28e97 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs @@ -75,24 +75,22 @@ public static TextChangeRange GetEncompassingTextChangeRange(this SourceText new return TextChangeRange.Collapse(ranges); } - public static int IndexOf(this SourceText text, string searchString, int startIndex, bool caseSensitive) + public static int IndexOf(this SourceText text, string value, int startIndex, bool caseSensitive) { - if (searchString.Length == 0) - return -1; - - var searchStringLength = searchString.Length; - var textEnd = text.Length - searchStringLength; - - // Get the first character of the value to search for, with the correct case sensitivity. This way, we don't - // have to call through ToLower when we're trying to find the starting point of a word match - var normalizedFirstChar = caseSensitive ? searchString[0] : CaseInsensitiveComparison.ToLower(searchString[0]); + var length = text.Length - value.Length; + var normalized = caseSensitive ? value : CaseInsensitiveComparison.ToLower(value); - for (var i = startIndex; i <= textEnd; i++) + for (var i = startIndex; i <= length; i++) { var match = true; - for (var j = 0; j < searchStringLength; j++) + for (var j = 0; j < normalized.Length; j++) { - if (!Match(GetValueChar(j), text[i + j], caseSensitive)) + // just use indexer of source text. perf of indexer depends on actual implementation of SourceText. + // * all of our implementation at editor layer should provide either O(1) or O(log n). + // + // only one implementation we have that could have bad indexer perf is CompositeText with heavily modified text + // at compiler layer but I believe that being used in find all reference will be very rare if not none. + if (!Match(normalized[j], text[i + j], caseSensitive)) { match = false; break; @@ -100,13 +98,12 @@ public static int IndexOf(this SourceText text, string searchString, int startIn } if (match) + { return i; + } } return -1; - - char GetValueChar(int index) - => index == 0 ? normalizedFirstChar : caseSensitive ? searchString[index] : CaseInsensitiveComparison.ToLower(searchString[index]); } public static int LastIndexOf(this SourceText text, string value, int startIndex, bool caseSensitive) From 15ae93c709873db25a5bd57a3b1385d0949a97f6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 17 May 2024 10:19:40 -0700 Subject: [PATCH 8/9] Update src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs --- .../SyntaxClassification/NameSyntaxClassifier.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs index 57c81db96db8a..9c1ce79a9e6e9 100644 --- a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs +++ b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs @@ -284,7 +284,8 @@ private static bool TryClassifyFromIdentifier( { // Okay - it wasn't a type. If the syntax matches "var q = from" or "q = from", and from // doesn't bind to anything then optimistically color from as a keyword. - if (name.Identifier.HasMatchingText(SyntaxKind.FromKeyword) && + if (name if IdentifierNameSyntax && + name.Identifier.HasMatchingText(SyntaxKind.FromKeyword) && symbolInfo.Symbol == null) { var token = name.Identifier; From 4fe82a9dda756644549777d4eb516f13e05e879e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 17 May 2024 10:36:45 -0700 Subject: [PATCH 9/9] fix --- .../Classification/SyntaxClassification/NameSyntaxClassifier.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs index 9c1ce79a9e6e9..2ef45cd853a96 100644 --- a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs +++ b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs @@ -284,7 +284,7 @@ private static bool TryClassifyFromIdentifier( { // Okay - it wasn't a type. If the syntax matches "var q = from" or "q = from", and from // doesn't bind to anything then optimistically color from as a keyword. - if (name if IdentifierNameSyntax && + if (name is IdentifierNameSyntax && name.Identifier.HasMatchingText(SyntaxKind.FromKeyword) && symbolInfo.Symbol == null) {