-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Fix ordering issue in analyzer callbacks for nested actions in symbol start analyzers #68490
Conversation
Race condition is detailed in the description of dotnet#68484. Fix is to ensure that we also mark the members of nested types as dependent symbols so that the SymbolEnd actions for the outer type are executed only after callbacks for nested type members have been done. Testing: This bug was identified by the newly added CSharpUsePrimaryConstructorDiagnosticAnalyzer. There is an existing unit test that failed intermittently due to this race condition: https://github.com/CyrusNajmabadi/roslyn/blob/f3e4a27818ea2495606f12fcabd986238b66efe4/src/Analyzers/CSharp/Tests/UsePrimaryConstructor/UsePrimaryConstructorTests.cs#LL741C27-L741C62 A workaround was added to the analyzer to register a dummy SymbolEnd action for nested types to work around this bug. This PR reverts that workaround and I verified that the test passes successfully for hundreds of iterations. I also attempted to create a standalone compiler unit test, but was unable to get this race condition to repro for the test even without the product change.
src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: Cyrus Najmabadi <cyrus.najmabadi@gmail.com>
@dotnet/roslyn-compiler for reviews. |
@dotnet/roslyn-compiler Ping for reviews. |
@mavasani Unfortunately, I am not familiar with the code being modified. Could you explain the following?
|
@AlekseyTs definitely, let me try to summarize. SymbolStart/End pair of actions allow analyzers to register a callback at a start of a symbol declaration (most often a named type symbol), then register nested callbacks within the scope of this symbol's declaration nodes for any syntax nodes or operations within these declaration nodes to accumulate analysis state and finally register a symbol end action callback that gets called after all the callbacks have been made for everything defined within this outer symbol, so it can use the accumulated state to report diagnostics. These analyzers rely on the ordering guarantee that symbol end callback is made after everything defined inside the outer symbol has received all callbacks. To ensure this ordering, whenever non-zero SymbolEndActionsCount are registered within a symbol start callback, we compute the inner dependent symbols for the outer symbol for which symbol start callback was made. This core logic is defined in the below code: roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.AnalyzerExecutionContext.cs Lines 138 to 221 in 06534d5
This logic was only adding the most immediate containing member symbols as dependent symbols, but not member symbols of nested types. This is sufficient for most real world analysis cases (and hence wasn't identified until now), but the analyzer that Cyrus recently added relied on gathering analysis state information even for nodes/operations within the member symbols of nested types to report the correct diagnostic in the symbol end callback for the outer type. This change updates this dependent symbol computation logic to even consider members of nested types as dependent symbols. Additionally, the change in |
@mavasani Thanks for the explanation. Is it possible to add a test? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM (commit 3). From offline discussion, there are tests that fail in the IDE for the IDE analyzer changed in the PR if only the IDE changes are made. However, consider adding a targeted test in the analyzer layer.
@dotnet/roslyn-compiler For the second review |
I have tried adding an analyzer unit test with main...mavasani:roslyn:TestForCyrus, but am unable to get it to fail, with or without the compiler layer product changes. However, I have verified that the IDE unit tests for CSharpUsePrimaryConstructorDiagnosticAnalyzer.cs fail more than half the times if the compiler layer product changes in this PR are reverted. |
@dotnet/roslyn-compiler for second review |
Thank you @cston |
Fixes #68484
Race condition is detailed in the description of #68484.
Fix is to ensure that we also mark the members of nested types as dependent symbols so that the SymbolEnd actions for the outer type are executed only after all callbacks for nested type members have been done.
Testing: This bug was identified by the newly added CSharpUsePrimaryConstructorDiagnosticAnalyzer. There is an existing unit test that failed intermittently due to this race condition: https://github.com/CyrusNajmabadi/roslyn/blob/f3e4a27818ea2495606f12fcabd986238b66efe4/src/Analyzers/CSharp/Tests/UsePrimaryConstructor/UsePrimaryConstructorTests.cs#LL741C27-L741C62 A workaround was added to the analyzer to register a dummy SymbolEnd action for nested types to work around this bug. This PR reverts that workaround and I verified that the test passes successfully for hundreds of iterations. I also attempted to create a standalone compiler unit test, but was unable to get this race condition to repro for the test even without the product change.