-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
[ILLink analyzer] Improve handling of invalid operations #94888
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -335,9 +335,6 @@ TValue ProcessSingleTargetAssignment (IOperation targetOperation, IAssignmentOpe | |
// Seems like this can't happen with a flow capture operation. | ||
Debug.Assert (operation.Target is not IFlowCaptureReferenceOperation); | ||
break; | ||
case IInvalidOperation: | ||
// This can happen for a field assignment in an attribute instance. | ||
// TODO: validate against the field attributes. | ||
case IInstanceReferenceOperation: | ||
// Assignment to 'this' is not tracked currently. | ||
// Not relevant for trimming dataflow. | ||
|
@@ -358,16 +355,7 @@ TValue ProcessSingleTargetAssignment (IOperation targetOperation, IAssignmentOpe | |
// can show up in a flow capture reference (for example, where the right-hand side | ||
// is a null-coalescing operator). | ||
default: | ||
// NoneOperation represents operations which are unimplemented by Roslyn | ||
// (don't have specific I*Operation types), such as pointer dereferences. | ||
if (targetOperation.Kind is OperationKind.None) | ||
break; | ||
|
||
// Assert on anything else as it means we need to implement support for it | ||
// but do not throw here as it means new Roslyn version could cause the analyzer to crash | ||
// which is not fixable by the user. The analyzer is not going to be 100% correct no matter what we do | ||
// so effectively ignoring constructs it doesn't understand is OK. | ||
Debug.Fail ($"{targetOperation.GetType ()}: {targetOperation.Syntax.GetLocation ().GetLineSpan ()}"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we use release builds of the analyzer in local development? Such asserts make it really hard to continue working when they occur. Or something else besides an assert. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I changed back to throwing like we used to, but only in Debug builds. This should surface as AD0001 warnings, which can be silenced if needed, while still giving us feedback on any analyzer crashes. Related to the discussion in #90358. |
||
UnexpectedOperationHandler.Handle (targetOperation); | ||
break; | ||
} | ||
return Visit (operation.Value, state); | ||
|
@@ -584,9 +572,7 @@ public override TValue VisitDelegateCreation (IDelegateCreationOperation operati | |
// No method symbol. | ||
break; | ||
default: | ||
// Unimplemented case that might need special handling. | ||
// Fail in debug mode only. | ||
Debug.Fail ($"{operation.Target.GetType ()}: {operation.Target.Syntax.GetLocation ().GetLineSpan ()}"); | ||
UnexpectedOperationHandler.Handle (operation.Target); | ||
break; | ||
} | ||
|
||
|
@@ -781,7 +767,7 @@ TValue HandleMethodCallHelper ( | |
argumentOperation = callOperation.Arguments[argumentIndex]; | ||
break; | ||
default: | ||
Debug.Fail ($"Unexpected operation {operation} for parameter {parameter.GetDisplayName ()}"); | ||
UnexpectedOperationHandler.Handle (operation); | ||
continue; | ||
}; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// Copyright (c) .NET Foundation and contributors. All rights reserved. | ||
// Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
||
using System.Diagnostics; | ||
using Microsoft.CodeAnalysis.Operations; | ||
|
||
namespace Microsoft.CodeAnalysis | ||
{ | ||
internal static class UnexpectedOperationHandler | ||
{ | ||
// No-op in release builds, but fails in debug builds when | ||
// encountering an unexpected operation. InvalidOperation is skipped because | ||
// it is expected that any part of the control-flow graph may contain an | ||
// InvalidOperation for code that doesn't compile (for example, in an intermediate | ||
// state while editing). | ||
public static void Handle (IOperation operation) | ||
{ | ||
// NoneOperation represents operations which are unimplemented by Roslyn | ||
// (don't have specific I*Operation types), such as pointer dereferences. | ||
if (operation.Kind is OperationKind.None) | ||
return; | ||
|
||
// This can happen for a field assignment in an attribute instance. | ||
// TODO: validate against the field attributes. | ||
if (operation.Kind is OperationKind.Invalid) | ||
return; | ||
|
||
// It's also possible to hit a case where the operation is an unexpected operation kind, | ||
// but the code is in an invalid state where the unexpected operation is not IInvalidOperation, yet one | ||
// of its child operations is. For example: | ||
// | ||
// a + = 3; | ||
// | ||
// This is represented as an assignment where the target is an IBinaryOperation (a +) whose right-hand side | ||
// is an IInvalidOperation. The assignment logic doesn't support assigning to a binary operation, | ||
// but this should still not fail. | ||
foreach (var descendant in operation.Descendants()) { | ||
if (descendant.Kind is OperationKind.Invalid) | ||
return; | ||
} | ||
|
||
// Assert on anything else as it means we need to implement support for it | ||
// but do not throw here as it means new Roslyn version could cause the analyzer to crash | ||
// which is not fixable by the user. The analyzer is not going to be 100% correct no matter what we do | ||
// so effectively ignoring constructs it doesn't understand is OK. | ||
Debug.Fail ($"Unexpected operation type {operation.GetType ()}: {operation.Syntax.GetLocation ().GetLineSpan ()}"); | ||
} | ||
} | ||
} |
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.
I checked that the current behavior doesn't give IInvalidOperation for field assignments in attribute instances - possibly fixed by some recent work that touched the way we do attribute analysis. While I was touching AttributeFieldDataflow I cleaned it up a bit, along with the similar testcase AttributePropertyDataflow.