roslyn: ISymbol.ToDisplayString throws NullReferenceException in SymbolDisplayVisitor
Version Used: 6.0.100-preview.3.21202.5 in our CI 5.0.300 as reported by @runehalfdan in our repo https://github.com/SonarSource/sonar-dotnet/issues/4525#issuecomment-856652109
Steps to Reproduce:
This issue doesn’t reproduce in a stable way. There’s some nondeterminism in the build and re-running the job usually doesn’t reproduce the issue as seen in our CI as well as in our user report (see link above).
- Clone open source project https://github.com/openiddict/openiddict-core.git
- Checkout
eb35cbefb700b3f219439431aa489f555a27a5de
commit - Plug in https://github.com/SonarSource/sonar-dotnet/releases/tag/8.24.0.32949 analyzer (or any older, we didn’t change the rule in last 3 years)
- You can deactivate all rules for performance/noise reasons except S1698
- Keep rebuilding the solution until the problem occurs
dotnet build /t:Rebuild /warnaserror:AD0001
Error observed:
16:44>CSC : error AD0001: Analyzer 'SonarAnalyzer.Rules.CSharp.ReferenceEqualityCheckWhenEqualsExists'
threw an exception of type 'System.NullReferenceException' with message 'Object reference not set to an instance of an object.'.
[C:\Project\src\OpenIddict.Server.AspNetCore\OpenIddict.Server.AspNetCore.csproj]
16:44>Exception occurred with following context:
Compilation: OpenIddict.Server.AspNetCore
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.CodeAnalysis.CSharp.SymbolDisplayVisitor.AddTupleTypeName(INamedTypeSymbol symbol)
at Microsoft.CodeAnalysis.CSharp.SymbolDisplayVisitor.AddNameAndTypeArgumentsOrParameters(INamedTypeSymbol symbol)
at Microsoft.CodeAnalysis.CSharp.SymbolDisplayVisitor.MinimallyQualify(INamedTypeSymbol symbol)
at Microsoft.CodeAnalysis.CSharp.SymbolDisplayVisitor.VisitNamedTypeWithoutNullability(INamedTypeSymbol symbol)
at Microsoft.CodeAnalysis.CSharp.SymbolDisplayVisitor.VisitNamedType(INamedTypeSymbol symbol)
at Microsoft.CodeAnalysis.CSharp.Symbols.PublicModel.NamedTypeSymbol.Accept(SymbolVisitor visitor)
at Microsoft.CodeAnalysis.CSharp.Symbols.PublicModel.Symbol.Microsoft.CodeAnalysis.ISymbol.Accept(SymbolVisitor visitor)
at Microsoft.CodeAnalysis.CSharp.SymbolDisplay.ToDisplayParts(ISymbol symbol, SemanticModel semanticModelOpt, Int32 positionOpt, SymbolDisplayFormat format, Boolean minimal)
at Microsoft.CodeAnalysis.CSharp.Symbols.PublicModel.Symbol.Microsoft.CodeAnalysis.ISymbol.ToDisplayString(SymbolDisplayFormat format)
at SonarAnalyzer.Helpers.TypeHelper.IsMatch(ITypeSymbol typeSymbol, KnownType type)
at SonarAnalyzer.Rules.CSharp.ReferenceEqualityCheckWhenEqualsExists.GetEqualsOverrides(ITypeSymbol type)
at SonarAnalyzer.Rules.CSharp.ReferenceEqualityCheckWhenEqualsExists.HasEqualsOverride(ITypeSymbol type)
at SonarAnalyzer.Rules.CSharp.ReferenceEqualityCheckWhenEqualsExists.<>c.<Initialize>b__9_1(INamedTypeSymbol t)
at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.MoveNext()
at System.Collections.Generic.HashSet`1.UnionWith(IEnumerable`1 other)
at System.Collections.Generic.HashSet`1..ctor(IEnumerable`1 collection, IEqualityComparer`1 comparer)
at System.Collections.Generic.HashSet`1..ctor(IEnumerable`1 collection)
at SonarAnalyzer.Helpers.EnumerableExtensions.ToHashSet[T](IEnumerable`1 enumerable, IEqualityComparer`1 equalityComparer)
at SonarAnalyzer.Rules.CSharp.ReferenceEqualityCheckWhenEqualsExists.<>c.<Initialize>b__9_0(CompilationStartAnalysisContext compilationStartContext)
at SonarAnalyzer.Helpers.SonarAnalysisContext.<>c__DisplayClass46_0`1.<RegisterContextAction>b__0(TContext c)
at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.<>c.<ExecuteCompilationStartActions>b__44_0(ValueTuple`2 data)
at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info)
-----
Suppress the following diagnostics to disable this analyzer: S1698
16:44>Done executing task "Csc".
Adding try/catch and logging the failed symbol ToString()
and DeclaringSyntaxReferences..SyntaxTree.FilePath
produces:
ToString: OpenIddict.Abstractions.OpenIddictExceptions.ConcurrencyException
FilePath: C:\Project\src\OpenIddict.Abstractions\OpenIddictExceptions.cs
ToString: OpenIddict.Server.OpenIddictServerConfiguration
FilePath: C:\Project\src\OpenIddict.Server\OpenIddictServerConfiguration.cs
ToString: SNINativeMethodWrapper.QTypes
FilePath: - no DeclaringSyntaxReferences
Analyzer Rule source:
In simplified version, rule registers for RegisterCompilationStartAction
(Line 62). There it gathers all the ITypeSymbol
(including nested) from Compilation.GlobalNamespace
. Then it iterates over each ITypeSymbol
and each of it’s .BaseType
and calls .Is(KnownType.System_Object)
(Line 163) that invokes .ToDisplayString()
.
Expected Behavior:
ToDisplayString()
should return some string.
Actual Behavior:
ToDisplayString()
throws exception.
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 12
- Comments: 45 (15 by maintainers)
https://github.com/jjonescz/RoslynIssue53943 - run
repro.ps1
fromopeniddict-core
folder.This started to occur way more often in our CI since last week.
We added this in order to keep analyzer on build-agents (it will now log error as warning, and build will succeed):
<WarningsNotAsErrors>AD0001</WarningsNotAsErrors>
We had to disable Sonar analyzers on the build agents because this issue broke our CD pipeline too many times.
Given there are no repros on NET 6 or later, going to assume that one of our other race condition fixes in tuples took care of this issue as well. If someone can repro this on NET 6, or even better NET 7, please let us know and we’ll dig into it.
It doesn’t appear that this bug was fixed. Moving to 17.9 as I don’t think we have time to do it in 17.8.
That is correct. It was a choice between either getting failed builds that should have been successful due to the NRE, or getting successful builds that should have failed due to analyzer finding something bad. The NRE happened way more often, so we chose this path and ignores the NRE.
@pavel-mikula-sonarsource Let me try your repro again. I see that you added information for adding the .editorconfig. “to enable only one rule”: could you just include the file you use in the repo? “Modify openiddict-core\Directory.Build.props to embed the analyzers”: same here. I’m not sure what that means. Can you include the file in the repo?
@credfeto @pavel-mikula-sonarsource Regarding a crash dump, looking at the description in OP again, I think the problem is the exception happens when an analyzer calls the API, but it looks like we catch and log those exceptions. So the process terminates, rather than crashes. I’ve ran the CoinBot repro for 24 hours and didn’t get any error locally 😕 I’ll try the openiddict repro some more. After that, I may try to give you an instrumented compiler (that crashes rather than reports the exception).
Thanks both for your patience and support.
I just reproduced it with these steps: Clone https://github.com/pavel-mikula-sonarsource/Roslyn_Repro_53943 Run build 11 times openiddict-core/Build.cmd
The first 10 runs were successful, as the issue is nondeterministic.
I did set up the dump via registry, but this didn’t produce a dump file because it is a caught exception. It’s not a crash.
From the logs:
That file has version 3.11.4.37306
@credfeto Thanks for the ping. Let me take a look and get back to you tomorrow.