Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

'Microsoft.CodeAnalysis.CSharp.Diagnostics.AddBraces.CSharpAddBracesDiagnosticAnalyzer' threw an exception of type 'System.InvalidOperationException' with message 'Unexpected true' #33560

Closed
meziantou opened this issue Feb 21, 2019 · 20 comments · Fixed by #36380
Assignees
Labels
Area-IDE Bug Developer Community The issue was originally reported on https://developercommunity.visualstudio.com IDE-CodeStyle Built-in analyzers, fixes, and refactorings Resolution-Fixed The bug has been fixed and/or the requested behavior has been implemented
Milestone

Comments

@meziantou
Copy link
Contributor

Version Used:

VS 2019 preview 3

Warning	AD0001	Analyzer 'Microsoft.CodeAnalysis.CSharp.Diagnostics.AddBraces.CSharpAddBracesDiagnosticAnalyzer' threw an exception of type 'System.InvalidOperationException' with message 'Unexpected true'.	

Microsoft.CodeAnalysis.CSharp.Diagnostics.AddBraces.CSharpAddBracesDiagnosticAnalyzer' threw the following exception:
'Exception occurred with following context:
Compilation: SwitchToSource.CLI
SyntaxTree: C:\Users\Documents\Sources\Command.List.cs
SyntaxNode: else System.Console.WriteLine($" ... [ElseClauseSyntax]@[2781..2931) (54,40)-(55,144)

System.InvalidOperationException: Unexpected true
   at Roslyn.Utilities.Contract.ThrowIfTrue(Boolean condition, String message)
   at Microsoft.CodeAnalysis.Shared.Utilities.CommonFormattingHelpers.AppendTextBetween(SyntaxToken token1, SyntaxToken token2, StringBuilder builder)
   at Microsoft.CodeAnalysis.CSharp.Utilities.FormattingRangeHelper.AreTwoTokensOnSameLine(SyntaxToken token1, SyntaxToken token2)
   at Microsoft.CodeAnalysis.CSharp.Diagnostics.AddBraces.CSharpAddBracesDiagnosticAnalyzer.IsConsideredMultiLine(SyntaxNode statement, SyntaxNode embeddedStatement)
   at Microsoft.CodeAnalysis.CSharp.Diagnostics.AddBraces.CSharpAddBracesDiagnosticAnalyzer.AnalyzeNode(SyntaxNodeAnalysisContext context)
   at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.<>c__50`1.<ExecuteSyntaxNodeAction>b__50_0(ValueTuple`2 data)
   at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info)
-----
'.

The code around the error (The line comments are not in the original code)

          packageToDisplayList.ForEach(listedPackage => // line 48
          {
              System.Console.WriteLine($"- {listedPackage.PackageId}");
              foreach (var versionInfo in listedPackage.Versions)
              {
                  if (versionInfo.Reason == NonSwitchableReason.None)
                      System.Console.WriteLine($"   * {versionInfo.Version} [{versionInfo.State}]"); // line 54
                  else
                      System.Console.WriteLine($"   * {versionInfo.Version} [{versionInfo.State}: {versionInfo.Reason}]");
              }
          });
@vatsalyaagrawal vatsalyaagrawal added Bug help wanted The issue is "up for grabs" - add a comment if you are interested in working on it Area-IDE IDE-CodeStyle Built-in analyzers, fixes, and refactorings labels Feb 21, 2019
@vatsalyaagrawal vatsalyaagrawal added this to InQueue in Small Fixes via automation Feb 21, 2019
@vatsalyaagrawal vatsalyaagrawal added this to the Backlog milestone Feb 21, 2019
@jinujoseph jinujoseph added the Developer Community The issue was originally reported on https://developercommunity.visualstudio.com label Apr 3, 2019
@jinujoseph
Copy link
Contributor

Reported at DC (link) Priority 1

@jinujoseph jinujoseph modified the milestones: Backlog, 16.1.P2, 16.1.P1 Apr 3, 2019
@jinujoseph jinujoseph added this to Backlog in IDE: InternalPriority via automation Apr 3, 2019
@jinujoseph jinujoseph moved this from Backlog to P1-Bugs in IDE: InternalPriority Apr 3, 2019
@jinujoseph jinujoseph assigned genlu and unassigned sharwell Apr 3, 2019
@genlu
Copy link
Member

genlu commented Apr 4, 2019

Hmm, I couldn't trigger the error with code provided (and setting preferbraces to WhenMultiline), @meziantou is this something repro consistently for you?

FYI @sharwell who might have some insight here

@meziantou
Copy link
Contributor Author

I don't repro with VS 2019 preview 5. So, maybe it's already fixed...

@genlu
Copy link
Member

genlu commented Apr 23, 2019

Closed sicne we are unable to repro this issue. Please reopen and provide us more information on how to repro if you are still seeing it.

@genlu genlu closed this as completed Apr 23, 2019
@jinujoseph jinujoseph moved this from P1-Bugs to Planned in IDE: InternalPriority Apr 25, 2019
@jinujoseph jinujoseph moved this from Planned to 16.2 Shipped in IDE: InternalPriority Apr 29, 2019
@jinujoseph jinujoseph moved this from InQueue to Current Sprint in Small Fixes May 7, 2019
@Andrew-Hanlon
Copy link
Contributor

I see that this was closed; however I am receiving the same errors on 16.0.4. On a large solution, I am receiving ~150 warnings with this error. It seems to be occurring around nested if statements where the deeper if/else is single line. Thanks,

@Andrew-Hanlon
Copy link
Contributor

Andrew-Hanlon commented May 22, 2019

I created a pull request that addresses the issue. The resolution should be reviewed, but the issue was traced: The AreTwoTokensOnSameLine method is called with the two parameters being equal. There are no guards or checks for the first and last being equal until the AppendTextBetween method which contractually throws. If they are indeed the same token, then they are certainly on the same line.

@genlu genlu added this to WorkItems I'm tracking in IDE: genlu May 29, 2019
@effleurager
Copy link

I'm getting the same issue in VisualStudio.16.Release/16.1.2+29001.49, on two if statements with one-line else statements.

private void Advance() {
    try {
        int next = input.Read();
        if (next >= 0)
            currentChar = (char)next;
        else
            currentChar = '\0';
    } catch (IOException) {
        currentChar = '\0';
    }
}
public Label DefaultTarget() {
    if (targets.Count > 0)
        return targets[targets.Count - 1];
    else
        return null;
}

@genlu
Copy link
Member

genlu commented Jun 7, 2019

@effleurager Thanks for reporting the issue! I have tried the following code using the version you specified as well as the latest in master, but still couldn't repro the crash :(

What's the codestyle preference you set for braces (I did try all three options though)? Are you able to hit the crash with the code below?

        class Program
        {
            string[] targets;
            private Program input;
            private char currentChar;

            public string DefaultTarget()
            {
                if (targets.Count() > 0)
                    return targets[targets.Count() - 1];
                else
                    return null;
            }

            public void Advance()
            {
                try {
                    int next = input.Read();
                    if (next >= 0)
                        currentChar = (char)next;
                    
                    else
                        currentChar = '\0';
                } catch (IOException) {
                    currentChar = '\0';
                }
            }

            char Read() => 'a';
        }

image

@dev-masih
Copy link

dev-masih commented Jun 9, 2019

I'm hitting this issue for a long time now, so i want to help to completely track down the issue, so tell me if you need any additional info to figure this out (VS 16.1.2).

i got a crash log for you:


Severity	Code	Description	Project	File	Line	Suppression State	Detail Description
Warning	AD0001	Analyzer 'Microsoft.CodeAnalysis.CSharp.Diagnostics.AddBraces.CSharpAddBracesDiagnosticAnalyzer' threw an exception of type 'System.InvalidOperationException' with message 'Unexpected true'.	WebLibCore		1	Active	Analyzer 'Microsoft.CodeAnalysis.CSharp.Diagnostics.AddBraces.CSharpAddBracesDiagnosticAnalyzer' threw the following exception:
'Exception occurred with following context:
Compilation: WebLibCore
SyntaxTree: C:\Workspace\Developments\Work\Libraries\WebLibCore\WebLibCore\Backend\WLCBELogHelper.cs
SyntaxNode: else return await BELogAsync(WLCBELogType ... [ElseClauseSyntax]@[2000..2158) (49,16)-(50,152)

System.InvalidOperationException: Unexpected true
   at Roslyn.Utilities.Contract.ThrowIfTrue(Boolean condition, String message)
   at Microsoft.CodeAnalysis.Shared.Utilities.CommonFormattingHelpers.AppendTextBetween(SyntaxToken token1, SyntaxToken token2, StringBuilder builder)
   at Microsoft.CodeAnalysis.CSharp.Utilities.FormattingRangeHelper.AreTwoTokensOnSameLine(SyntaxToken token1, SyntaxToken token2)
   at Microsoft.CodeAnalysis.CSharp.Diagnostics.AddBraces.CSharpAddBracesDiagnosticAnalyzer.IsConsideredMultiLine(SyntaxNode statement, SyntaxNode embeddedStatement)
   at Microsoft.CodeAnalysis.CSharp.Diagnostics.AddBraces.CSharpAddBracesDiagnosticAnalyzer.AnalyzeNode(SyntaxNodeAnalysisContext context)
   at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.<>c__50`1.<ExecuteSyntaxNodeAction>b__50_0(ValueTuple`2 data)
   at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info)
-----
'.

and this is the code section that reported in crash log:

image
(if you need more code let me know)

and this is my code style rule set
image

i'm waiting for your response if you need additional info on anything just let me know.

@Andrew-Hanlon
Copy link
Contributor

Andrew-Hanlon commented Jun 10, 2019

I have been looking into this issue, I can reproduce the exception while debugging the unit tests by manually bypassing a certain check, and from my testing a simple fix does solve the issue. That said I am unable to understand why/how a SyntaxToken reaches the symptomatic state.

Within the CSharpAddBracesDiagnosticAnalyzer.IsConsideredMultiLine method, multiple checks are performed with specific tokens to identify if the overall statement should be considered as spanning multiple lines. The third such check (in the case of a non-braced else clause) will always provide the same token as both parameters to theFomattingRangeHelper.AreTwoTokensOnSameLine method.

In normal operation, this method uses the SyntaxTree's text to verify if the two tokens are on the same line, in which case checking the same token is unnecessary, but works fine. But (here is the state I don't understand) if the token's SyntaxTree is null or the TryGetText method fails, then an alternate methodology is used which contractually throws an exception when the tokens are the same.

I assume there must be a condition or state where a token's SyntaxTree is null or the TryGetText method will fail, otherwise this code would not have been written - but I do not know how to reproduce this state.

All of the relevant code:

From CSharpAddBracesDiagnosticAnalyzer.IsConsideredMultiLine:

...
// Check the part of the statement preceding the embedded statement (bullet 1)
var lastTokenBeforeEmbeddedStatement = embeddedStatement.GetFirstToken().GetPreviousToken();
if (!FormattingRangeHelper.AreTwoTokensOnSameLine(statement.GetFirstToken(), lastTokenBeforeEmbeddedStatement))
{
	// The part of the statement preceding the embedded statement does not fit on one line. Examples:
	//
	//   for (int i = 0; // <-- The initializer/condition/increment are on separate lines
	//        i < 10;
	//        i++)
	//     SomeMethod();
	return true;
}
...

With a statement like else \n foo(); the first token and the embedded statement's previous token will both be the same 'else' token.

The main code in question is in FomattingRangeHelper:

public static bool AreTwoTokensOnSameLine(SyntaxToken token1, SyntaxToken token2)
{
	var tree = token1.SyntaxTree;
	if (tree != null && tree.TryGetText(out var text)) // <-- When / how does this fail?
	{
		return text.AreOnSameLine(token1, token2);
	}
        // This call eventually throws if token1 == token2
	return !CommonFormattingHelpers.GetTextBetween(token1, token2).ContainsLineBreak();
}

How do we get a token with no SyntaxTree or failed TryGetText call?

Lastly, the simple fix is to update the AreTwoTokensOnSameLine method to include a preemptive check to see if the tokens are equal - since if they are equal then they are certainly on the same line:

public static bool AreTwoTokensOnSameLine(SyntaxToken token1, SyntaxToken token2)
{
        if(token1 == token2) return true;
...

@effleurager
Copy link

@genlu This is my .editorconfig file, but it seems that the issue is rather temperamental as I cannot reproduce the issue on the same initial code or your boiled down class, despite running the same version.

@genlu
Copy link
Member

genlu commented Jun 10, 2019

but it seems that the issue is rather temperamental as I cannot reproduce the issue on the same initial code or your boiled down class, despite running the same version

Yeah, that's what's been puzzling me. There's seems to be some nondeterminism of this bug.

@Andrew-Hanlon Thanks for the detailed analysis! @CyrusNajmabadi do you know in what scenario we might get null from token.SyntaxTree?

@CyrusNajmabadi
Copy link
Member

CyrusNajmabadi commented Jun 10, 2019

Haven't read everything. But is the token a default token?

@CyrusNajmabadi
Copy link
Member

var tree = token1.SyntaxTree;
if (tree != null && tree.TryGetText(out var text)) // <-- When / how does this fail?

I'm not sure why we woudl think this would every necessarily succeed. i.e. just because you have the tree, the text may still be dumped from memory. For example, if this was just some forked tree or analyzing some old snapshot that the main VS buffer had moved forward from.

--

@jasonmalinowski What's our rules/invariants around TryGetText? I don't think we have a guarantee around it that would make it work for something like this.

@Andrew-Hanlon
Copy link
Contributor

@CyrusNajmabadi Thank you, that's good to know. Would you know if there is a way to set-up a unit test for such a scenario (dumped from memory for example)?

In any case, I know my suggested fix solves the issue. I closed a previous PR as I could not produce a unit test to capture the scenario, but I can open a new one with some of these details.

@CyrusNajmabadi
Copy link
Member

CyrusNajmabadi commented Jun 10, 2019

@CyrusNajmabadi Thank you, that's good to know. Would you know if there is a way to set-up a unit test for such a scenario (dumped from memory for example)?

That's a great question. I imagine (and maybe @jasonmalinowski could help here), but you should be able to just work with teh workspace, get the snapshot for the existing buffer, then actually edit/close the buffer, then GC a lot... maybe you'd see it happen?

I'm not really sure where we ever landed in terms of hte invariants around TryGetXXX (esp. around active/open docs). So it's hard to say what would be necessary to get TryGetXXX to fail.

@Andrew-Hanlon
Copy link
Contributor

Andrew-Hanlon commented Jun 10, 2019

A little bit more debugging information:

I was able to debug this situation by running the Roslyn solution and attaching the debugger to the ServiceHub.RoslynCodeAnalysisService32.exe process.

When the 'error' state was hit, I was able to see in the immediate window that indeed it is the TryGetText method that is failing.

The stacktrace is below:

Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Shared.Utilities.CommonFormattingHelpers.AppendTextBetween(Microsoft.CodeAnalysis.SyntaxToken token1, Microsoft.CodeAnalysis.SyntaxToken token2, System.Text.StringBuilder builder) Line 168	C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Shared.Utilities.CommonFormattingHelpers.GetTextBetween(Microsoft.CodeAnalysis.SyntaxToken token1, Microsoft.CodeAnalysis.SyntaxToken token2) Line 160	C#
Microsoft.CodeAnalysis.CSharp.Workspaces.dll!Microsoft.CodeAnalysis.CSharp.Utilities.FormattingRangeHelper.AreTwoTokensOnSameLine(Microsoft.CodeAnalysis.SyntaxToken token1, Microsoft.CodeAnalysis.SyntaxToken token2) Line 299	C#
Microsoft.CodeAnalysis.CSharp.Features.dll!Microsoft.CodeAnalysis.CSharp.Diagnostics.AddBraces.CSharpAddBracesDiagnosticAnalyzer.IsConsideredMultiLine(Microsoft.CodeAnalysis.SyntaxNode statement, Microsoft.CodeAnalysis.SyntaxNode embeddedStatement) Line 172	C#
Microsoft.CodeAnalysis.CSharp.Features.dll!Microsoft.CodeAnalysis.CSharp.Diagnostics.AddBraces.CSharpAddBracesDiagnosticAnalyzer.AnalyzeNode(Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext context) Line 95	C#
Microsoft.CodeAnalysis.dll!Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteSyntaxNodeAction.AnonymousMethod__50_0((System.Action<Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext> action, Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext context) data) Line 726	C#
Microsoft.CodeAnalysis.dll!Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock<System.ValueTuple<System.Action<Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext>, Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext>>(Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer analyzer, System.Action<(System.Action<Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext>, Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext)> analyze, (System.Action<Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext>, Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext) argument, Microsoft.CodeAnalysis.Diagnostics.AnalysisContextInfo? info) Line 1384	C#
Microsoft.CodeAnalysis.dll!Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows<System.ValueTuple<System.Action<Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext>, Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext>>(Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer analyzer, System.Action<(System.Action<Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext>, Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext)> analyze, (System.Action<Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext>, Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext) argument, Microsoft.CodeAnalysis.Diagnostics.AnalysisContextInfo? info) Line 1353	C#
Microsoft.CodeAnalysis.dll!Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteSyntaxNodeAction<Microsoft.CodeAnalysis.CSharp.SyntaxKind>(Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalyzerAction<Microsoft.CodeAnalysis.CSharp.SyntaxKind> syntaxNodeAction, Microsoft.CodeAnalysis.SyntaxNode node, Microsoft.CodeAnalysis.ISymbol containingSymbol, Microsoft.CodeAnalysis.SemanticModel semanticModel, System.Action<Microsoft.CodeAnalysis.Diagnostic> addDiagnostic, System.Func<Microsoft.CodeAnalysis.Diagnostic, bool> isSupportedDiagnostic, Microsoft.CodeAnalysis.Diagnostics.AnalysisState.SyntaxNodeAnalyzerStateData analyzerStateOpt) Line 724	C#
Microsoft.CodeAnalysis.dll!Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteSyntaxNodeActions<Microsoft.CodeAnalysis.CSharp.SyntaxKind>(Microsoft.CodeAnalysis.SyntaxNode node, System.Collections.Generic.IDictionary<Microsoft.CodeAnalysis.CSharp.SyntaxKind, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalyzerAction<Microsoft.CodeAnalysis.CSharp.SyntaxKind>>> nodeActionsByKind, Microsoft.CodeAnalysis.ISymbol containingSymbol, Microsoft.CodeAnalysis.SemanticModel model, System.Func<Microsoft.CodeAnalysis.SyntaxNode, Microsoft.CodeAnalysis.CSharp.SyntaxKind> getKind, System.Action<Microsoft.CodeAnalysis.Diagnostic> addDiagnostic, System.Func<Microsoft.CodeAnalysis.Diagnostic, bool> isSupportedDiagnostic, Microsoft.CodeAnalysis.Diagnostics.AnalysisState.SyntaxNodeAnalyzerStateData analyzerStateOpt) Line 1180	C#
Microsoft.CodeAnalysis.dll!Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteSyntaxNodeActions<Microsoft.CodeAnalysis.CSharp.SyntaxKind>(System.Collections.Generic.IEnumerable<Microsoft.CodeAnalysis.SyntaxNode> nodesToAnalyze, System.Collections.Generic.IDictionary<Microsoft.CodeAnalysis.CSharp.SyntaxKind, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalyzerAction<Microsoft.CodeAnalysis.CSharp.SyntaxKind>>> nodeActionsByKind, Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer analyzer, Microsoft.CodeAnalysis.ISymbol containingSymbol, Microsoft.CodeAnalysis.SemanticModel model, System.Func<Microsoft.CodeAnalysis.SyntaxNode, Microsoft.CodeAnalysis.CSharp.SyntaxKind> getKind, System.Action<Microsoft.CodeAnalysis.Diagnostic> addDiagnostic, System.Func<Microsoft.CodeAnalysis.Diagnostic, bool> isSupportedDiagnostic, Microsoft.CodeAnalysis.Diagnostics.AnalysisState.SyntaxNodeAnalyzerStateData analyzerStateOpt) Line 1159	C#
Microsoft.CodeAnalysis.dll!Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteSyntaxNodeActionsCore<Microsoft.CodeAnalysis.CSharp.SyntaxKind>(System.Collections.Generic.IEnumerable<Microsoft.CodeAnalysis.SyntaxNode> nodesToAnalyze, System.Collections.Generic.IDictionary<Microsoft.CodeAnalysis.CSharp.SyntaxKind, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalyzerAction<Microsoft.CodeAnalysis.CSharp.SyntaxKind>>> nodeActionsByKind, Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer analyzer, Microsoft.CodeAnalysis.ISymbol containingSymbol, Microsoft.CodeAnalysis.SemanticModel model, System.Func<Microsoft.CodeAnalysis.SyntaxNode, Microsoft.CodeAnalysis.CSharp.SyntaxKind> getKind, Microsoft.CodeAnalysis.Text.TextSpan filterSpan, Microsoft.CodeAnalysis.Diagnostics.AnalysisState.SyntaxNodeAnalyzerStateData analyzerStateOpt, bool isGeneratedCode) Line 1129	C#
Microsoft.CodeAnalysis.dll!Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.TryExecuteSyntaxNodeActions<Microsoft.CodeAnalysis.CSharp.SyntaxKind>(System.Collections.Generic.IEnumerable<Microsoft.CodeAnalysis.SyntaxNode> nodesToAnalyze, System.Collections.Generic.IDictionary<Microsoft.CodeAnalysis.CSharp.SyntaxKind, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalyzerAction<Microsoft.CodeAnalysis.CSharp.SyntaxKind>>> nodeActionsByKind, Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer analyzer, Microsoft.CodeAnalysis.SemanticModel model, System.Func<Microsoft.CodeAnalysis.SyntaxNode, Microsoft.CodeAnalysis.CSharp.SyntaxKind> getKind, Microsoft.CodeAnalysis.Text.TextSpan filterSpan, Microsoft.CodeAnalysis.SyntaxReference declaration, int declarationIndex, Microsoft.CodeAnalysis.ISymbol declaredSymbol, Microsoft.CodeAnalysis.Diagnostics.AnalysisScope analysisScope, Microsoft.CodeAnalysis.Diagnostics.AnalysisState analysisStateOpt, bool isGeneratedCode) Line 1098	C#
Microsoft.CodeAnalysis.dll!Microsoft.CodeAnalysis.Diagnostics.AnalyzerDriver<Microsoft.CodeAnalysis.CSharp.SyntaxKind>.TryExecuteDeclaringReferenceActions.__executeNodeActionsByKind|2(Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer analyzer, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.SyntaxNode> nodesToAnalyze, Microsoft.CodeAnalysis.Diagnostics.AnalyzerDriver<Microsoft.CodeAnalysis.CSharp.SyntaxKind>.GroupedAnalyzerActions groupedActions) Line 1892	C#
Microsoft.CodeAnalysis.dll!Microsoft.CodeAnalysis.Diagnostics.AnalyzerDriver<Microsoft.CodeAnalysis.CSharp.SyntaxKind>.TryExecuteDeclaringReferenceActions.__executeNodeActions|1() Line 1881	C#
Microsoft.CodeAnalysis.dll!Microsoft.CodeAnalysis.Diagnostics.AnalyzerDriver<Microsoft.CodeAnalysis.CSharp.SyntaxKind>.TryExecuteDeclaringReferenceActions(Microsoft.CodeAnalysis.SyntaxReference decl, int declarationIndex, Microsoft.CodeAnalysis.Diagnostics.SymbolDeclaredCompilationEvent symbolEvent, Microsoft.CodeAnalysis.Diagnostics.AnalysisScope analysisScope, Microsoft.CodeAnalysis.Diagnostics.AnalysisState analysisStateOpt, Microsoft.CodeAnalysis.Diagnostics.AnalyzerDriver<Microsoft.CodeAnalysis.CSharp.SyntaxKind>.GroupedAnalyzerActions additionalPerSymbolActions, bool shouldExecuteSyntaxNodeActions, bool shouldExecuteOperationActions, bool shouldExecuteCodeBlockActions, bool shouldExecuteOperationBlockActions, bool isInGeneratedCode, System.Threading.CancellationToken cancellationToken) Line 1848	C#
Microsoft.CodeAnalysis.dll!Microsoft.CodeAnalysis.Diagnostics.AnalyzerDriver<Microsoft.CodeAnalysis.CSharp.SyntaxKind>.TryExecuteDeclaringReferenceActions(Microsoft.CodeAnalysis.Diagnostics.SymbolDeclaredCompilationEvent symbolEvent, Microsoft.CodeAnalysis.Diagnostics.AnalysisScope analysisScope, Microsoft.CodeAnalysis.Diagnostics.AnalysisState analysisStateOpt, bool isGeneratedCodeSymbol, Microsoft.CodeAnalysis.Diagnostics.AnalyzerActions additionalPerSymbolActionsOpt, System.Threading.CancellationToken cancellationToken) Line 1739	C#
Microsoft.CodeAnalysis.dll!Microsoft.CodeAnalysis.Diagnostics.AnalyzerDriver.TryProcessSymbolDeclaredAsync(Microsoft.CodeAnalysis.Diagnostics.SymbolDeclaredCompilationEvent symbolEvent, Microsoft.CodeAnalysis.Diagnostics.AnalysisScope analysisScope, Microsoft.CodeAnalysis.Diagnostics.AnalysisState analysisStateOpt, System.Threading.CancellationToken cancellationToken) Line 1060	C#
Microsoft.CodeAnalysis.dll!Microsoft.CodeAnalysis.Diagnostics.AnalyzerDriver.TryProcessEventCoreAsync(Microsoft.CodeAnalysis.Diagnostics.CompilationEvent e, Microsoft.CodeAnalysis.Diagnostics.AnalysisScope analysisScope, Microsoft.CodeAnalysis.Diagnostics.AnalysisState analysisStateOpt, System.Threading.CancellationToken cancellationToken) Line 1001	C#
Microsoft.CodeAnalysis.dll!Microsoft.CodeAnalysis.Diagnostics.AnalyzerDriver.ProcessEventAsync(Microsoft.CodeAnalysis.Diagnostics.CompilationEvent e, Microsoft.CodeAnalysis.Diagnostics.AnalysisScope analysisScope, Microsoft.CodeAnalysis.Diagnostics.AnalysisState analysisStateOpt, System.Threading.CancellationToken cancellationToken) Line 935	C#
Microsoft.CodeAnalysis.dll!Microsoft.CodeAnalysis.Diagnostics.AnalyzerDriver.ProcessCompilationEventsCoreAsync(Microsoft.CodeAnalysis.Diagnostics.AnalysisScope analysisScope, Microsoft.CodeAnalysis.Diagnostics.AnalysisState analysisStateOpt, bool prePopulatedEventQueue, System.Threading.CancellationToken cancellationToken) Line 922	C#

Likewise by applying the equality check fix above, this exception is no longer hit.

@jasonmalinowski
Copy link
Member

Without looking at the code, the only rule I'd be comfortable saying about TryGetText is you shouldn't use it. 😄 Now to back to trying to be useful.

@jasonmalinowski What's our rules/invariants around TryGetText? I don't think we have a guarantee around it that would make it work for something like this.

I don't think there are formally documented rules. The method is only intended for optimizations where you can potentially fast-path something. I suspect you're right that this could fail if you have somebody rewriting a tree, and we haven't yet computed text, or if we did were only holding text lazily. Even if there are "rules" I assume things would get complicated: I would assume that a syntax tree that didn't have realized text would fail TryGetText, but once somebody called GetText() (which is possibly a different extension in the same process!) then maybe it'd work.

If we wanted a unit test to cover a case I wouldn't want the unit test trying to reproduce a "conditions where we know this will fail" because the test is tied to an implementation detail in a different system. I'd say either have the test create it's own derived version of SyntaxTree that will fail TryGetText, or just test the underlying methods there that they behave the same and trust you've got both paths right.

@CyrusNajmabadi
Copy link
Member

I don't think there are formally documented rules. The method is only intended for optimizations where you can potentially fast-path something.

Yeha, the only rule i've ever had is: this is safe if you are certain someone higher in your calltree grabbed this thing and put it in a local**

--

** And even then, you probably need a GC.KeepAlive. And even then... why are you not just passing this value downstream to the people who clearly need it?

@Andrew-Hanlon
Copy link
Contributor

Andrew-Hanlon commented Jun 11, 2019

Thanks for the insight all. So my proposal is that I will create a PR that includes the fix plus a new unit test that covers the AreTwoTokensOnSameLine helper method using a mocked SyntaxNode/Tree that simulates the currently failing case.

Andrew-Hanlon added a commit to Andrew-Hanlon/roslyn that referenced this issue Jun 12, 2019
@sharwell sharwell added Resolution-Fixed The bug has been fixed and/or the requested behavior has been implemented and removed help wanted The issue is "up for grabs" - add a comment if you are interested in working on it labels Jun 14, 2019
@sharwell sharwell modified the milestones: 16.2, 16.2.P4 Jun 14, 2019
sharwell added a commit that referenced this issue Jun 14, 2019
Fix #33560 Handle equal tokens where Text is not available.
@genlu genlu removed this from WorkItems I'm tracking in IDE: genlu Sep 23, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-IDE Bug Developer Community The issue was originally reported on https://developercommunity.visualstudio.com IDE-CodeStyle Built-in analyzers, fixes, and refactorings Resolution-Fixed The bug has been fixed and/or the requested behavior has been implemented
Projects
Small Fixes
  
Completed
10 participants