-
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
changed how GetOperation work. #21857
Conversation
…d never use recovery mode of semantic model.
expect a lot of tests to fail due to behavior change of GetOperation |
{ | ||
var bindingRoot = GetBindingRoot(node); | ||
|
||
// if binding root is field variable declarator, make it initializer |
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.
// if binding root is field variable declarator, make it initializer [](start = 12, length = 68)
// if binding root is field variable declarator, make it initializer [](start = 12, length = 68)
It would be good to provide an explanation why we are doing this. #Closed
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.
added
added null check and make sure we use field initializer instead of whole field decl for C#
14ab0aa
to
afd0475
Compare
@dotnet/roslyn-infrastructure test is not running, is this a known issue? |
@dotnet/analyzer-ioperation @AlekseyTs @cston can I get some code review? this changes how GetOperation works and fixed all tests that are broken due to behavior changes. |
@@ -706,7 +667,7 @@ private IOperation CreateBoundConversionOperation(BoundConversion boundConversio | |||
bool isChecked = conversion.IsNumeric && boundConversion.Checked; | |||
ITypeSymbol type = boundConversion.Type; | |||
Optional<object> constantValue = ConvertToOptional(boundConversion.ConstantValue); | |||
return new LazyCSharpConversionExpression(operand, conversion, isExplicit, isTryCast, isChecked, _semanticModel, syntax, type, constantValue, isImplicit); | |||
return new LazyCSharpConversionExpression(operand, conversion, isExplicit, isTryCast, isChecked, _semanticModel, syntax, type, constantValue, isImplicit || !isExplicit); |
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.
implicit conversion require checking both compiler generated + explicit cast in code since compiler won't mark it as compiler generated
}); | ||
} | ||
var statement = (LocalDeclarationStatementSyntax)boundLocalDeclaration.Syntax; | ||
var variableDeclarator = statement.Declaration.Variables.First(); |
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.
use declarator for IVariableDeclaration since that is what multiple local declaration is using to make things consistent.
} | ||
|
||
private IVariableDeclarationStatement CreateBoundMultipleLocalDeclarationsOperation(BoundMultipleLocalDeclarations boundMultipleLocalDeclarations) | ||
{ | ||
Lazy<ImmutableArray<IVariableDeclaration>> declarations = new Lazy<ImmutableArray<IVariableDeclaration>>(() => | ||
boundMultipleLocalDeclarations.LocalDeclarations.SelectAsArray(declaration => CreateVariableDeclaration(declaration))); | ||
boundMultipleLocalDeclarations.LocalDeclarations.SelectAsArray(declaration => (IVariableDeclaration)Create(declaration))); |
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.
made it to follow the pattern rather than cut in the middle.
Diagnostic("GeneratedCodeAnalyzerSummary").WithArguments("Node: var mixMethodUserVar = 0,Node: var userVar = 0,Operation: MixMethod,Operation: NonHiddenMethod").WithLocation(1, 1)); | ||
|
||
analyzers = new DiagnosticAnalyzer[] { new GeneratedCodeSyntaxAndOperationAnalyzer(GeneratedCodeAnalysisFlags.Analyze, syntaxKinds, operationKinds) }; | ||
compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, | ||
Diagnostic("GeneratedCodeAnalyzerWarning", "var userVar = 0").WithArguments("Node: var userVar = 0").WithLocation(17, 9), | ||
Diagnostic("GeneratedCodeAnalyzerWarning", "var userVar = 0;").WithArguments("Operation: NonHiddenMethod").WithLocation(17, 9), | ||
Diagnostic("GeneratedCodeAnalyzerWarning", "userVar = 0").WithArguments("Operation: NonHiddenMethod").WithLocation(17, 13), |
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.
now it point to declarator rather than declaration statement.
@@ -21,7 +21,7 @@ class Program | |||
{ | |||
static void Main(string[] args) | |||
{ | |||
Action x /*<bind>*/= () => F()/*</bind>*/; | |||
/*<bind>*/Action x = () => F();/*</bind>*/ |
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.
before we moved node around to return something. for example, equal value returned declaration statement. now we always return what we got. we only return IOperation with declaration statement when we got declaration statement for GetOperation.
there is no matching IOperation for equal value in this case or varaible decleration.
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.
Do we have a test that requests operation for a VariableDeclaration: Action x = () => F()
?
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.
Why will the VariableDeclarationSyntax not return an IVariableDeclaration?
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 meant VariableDeclaratorSyntax
not VariableDeclarationSyntax
...
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.
in Operation tree, there is no matching IOperation concept for VariableDeclarationSyntax. if user call GetOperation with it, it will return null.
in IOperation, statement points declarator directly without middle decleration like syntax node.
variable declaration syntax points to multiple declarator there is no such IOperation that can do that.
ILiteralExpression (OperationKind.LiteralExpression, Type: System.Int32, Constant: 10, IsInvalid) (Syntax: '10') | ||
InConversion: null | ||
OutConversion: null | ||
IInvalidExpression (OperationKind.InvalidExpression, Type: System.Int32, IsInvalid) (Syntax: 'this[10]') |
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.
now we will return first IOperation that has matching node given to GetOperation in operation tree. rather than return whatever node returned by node map (with high/low bound node option)
|
||
internal partial class BoundMethodGroup | ||
{ | ||
protected override ImmutableArray<BoundNode> Children => ImmutableArray.Create<BoundNode>(this.ReceiverOpt); |
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.
protected override ImmutableArray Children => ImmutableArray.Create(this.ReceiverOpt); [](start = 8, length = 108)
Instead of duplicating this for BoundMethodGroup and BoundPropertyGroup, consider overriding this property once in BoundMethodOrPropertyGroup #Closed
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.
Dim syntax As SyntaxNode = boundSelectStatement.Syntax | ||
Dim type As ITypeSymbol = Nothing | ||
Dim constantValue As [Optional](Of Object) = New [Optional](Of Object)() | ||
Dim isImplicit As Boolean = boundSelectStatement.WasCompilerGenerated | ||
Return New LazySwitchStatement(value, cases, _semanticModel, syntax, type, constantValue, isImplicit) | ||
End Function | ||
|
||
Private Function CreateBoundCaseBlockOperation(boundCaseBlock As BoundCaseBlock) As ISwitchCase |
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.
Private Function CreateBoundCaseBlockOperation(boundCaseBlock As BoundCaseBlock) As ISwitchCase [](start = 8, length = 95)
Is this change a part of an unrelated commit? #Closed
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 see, it looks like the code was moved from another file.
In reply to: 140051547 [](ancestors = 140051547)
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.
no it was there before. case block wasn't go through the bound node -> ioperation map before, so I fixed it.
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.
ah, ya, it was called directly from case statement or something, so I made it to go through Create(...) but implementation was already there. I just connected it to Create.
@@ -856,10 +859,7 @@ End Structure | |||
references:={MscorlibRef, SystemRef, compilation0.EmitToImageReference(embedInteropTypes:=True)}) | |||
|
|||
Dim expectedOperationTree = < |
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
@cston can you take a look as well? |
{ | ||
get | ||
{ | ||
System.Diagnostics.Debug.Assert(this.Kind == BoundKind.StatementList || this.Kind == BoundKind.Scope); |
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.
Will this assert fail when evaluating the property in the debugger?
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.
let me try. didn't thought of that...
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.
yes. it does. it shows
"Children = 'boundBlock.Children' threw an exception of type 'Microsoft.CodeAnalysis.ThrowingTraceListener.DebugAssertFailureException"
in debug window.
it used to show empty. now that exception for BoundBlock, and child statements for other such as StatementList or Scope.
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.
Consider removing the assert.
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.
but we dont want BoundBlock.Children to be used.
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.
An assert in a property may result in a popup in the debugger though. If it's important to avoid BoundBlock.Children
, perhaps return ImmutableArray<BoundNode>.Empty
when Kind
is not one of the expected values.
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.
sure I can do that.
PS: it doesn't cause pop up, but value in the watch window or data tip says assert exception was thrown.
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.
Aren't statements inside a block children of that block? I think they are.
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.
Children should return something if the bound node is not supported by IOperation yet. this is a workaround we have until we can support all bound nodes we want to support. BoundBlock is already supported so children should never be called. that's why I have put assert.
default: | ||
result = lowestBoundNode; | ||
break; | ||
bindingRoot = parameter.Default; |
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.
Consider return parameter.Default;
here rather than checking other cases below that appear to be distinct. Same comment for other bindingRoot = ...;
cases below.
@@ -1293,17 +1338,24 @@ private CSharpSyntaxNode GetBindingRoot(CSharpSyntaxNode node) | |||
|
|||
StatementSyntax enclosingStatement = null; |
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.
Declaration can be moved inside for
.
@@ -274,9 +247,14 @@ private ImmutableArray<IOperation> GetIOperationChildren(BoundNode boundNode) | |||
} | |||
|
|||
var builder = ArrayBuilder<IOperation>.GetInstance(boundNodeWithChildren.Children.Length); | |||
foreach (var childNode in boundNodeWithChildren.Children) | |||
foreach (BoundNode childNode in boundNodeWithChildren.Children) |
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.
Are there implementations of Children
that allocate? If so, extract a local for boundNodeWithChildren.Children
to avoid unnecessary allocations.
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.
@cston can you give me little bit more on what you meant?
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.
ah, I got it. okay.
return new CSharpArgument(kind, | ||
parameter, | ||
value, | ||
semanticModel: _semanticModel, | ||
syntax: value.Syntax, | ||
syntax: argumentExist ? value.Syntax.Parent : value.Syntax, |
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.
Consider using as
above.
var argument = value.Syntax?.Parent as ArgumentSyntax;
return new CSharpArgument(
...
syntax: argument ?? value.Syntax,
private IOperation CreateBoundCallInstanceOperation(BoundCall boundCall) | ||
{ | ||
if (boundCall.Method == null || boundCall.Method.IsStatic) | ||
{ |
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.
Is this check necessary? In error cases, this will drop the receiver when the method is static but a receiver was provided, and will not prevent Create
from being called for instance methods when a receiver was not included.
Please add tests for those cases if not currently tested.
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 don't know. I converted existing code to the method.
Lazy<IOperation> instance = new Lazy<IOperation>(() => Create(((object)boundCall.Method == null || boundCall.Method.IsStatic) ? null : boundCall.ReceiverOpt));
tagging @dotnet/analyzer-ioperation for people who knows about this.
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 think @cston is correct, we probably don't want to drop receiver unless it is a bound type node. We should follow up on this with tests and necessary implementation changes.
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.
opened #22233
// 2. the constant value of BoundEventAccess is always null. | ||
// 3. the syntax of the boundEventAssignmentOperator is always AssignmentExpressionSyntax, so the syntax for the event reference would be the LHS of the assignment. | ||
IEventSymbol @event = boundEventAssignmentOperator.Event; | ||
Lazy<IOperation> instance = new Lazy<IOperation>(() => Create(boundEventAssignmentOperator.Event.IsStatic ? null : boundEventAssignmentOperator.ReceiverOpt)); |
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.
Is the IsStatic
check necessary?
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 just moved this code from CSharpOperationFactory to this file.
tagging @genlu who wrote the code.
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.
opened #22233
@@ -1080,7 +1082,9 @@ Namespace Microsoft.CodeAnalysis.Semantics | |||
Dim syntax As SyntaxNode = boundBadStatement.Syntax | |||
Dim type As ITypeSymbol = Nothing | |||
Dim constantValue As [Optional](Of Object) = New [Optional](Of Object)() | |||
Dim isImplicit As Boolean = boundBadStatement.WasCompilerGenerated | |||
|
|||
' if child has syntax node point to same syntax node as bad statement, then this invalid statement Is implicit |
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.
is
rather than Is
.
inConversion:=inConversion, | ||
outConversion:=outConversion, | ||
semanticModel:=_semanticModel, | ||
syntax:=If(argumentExist, value.Syntax.Parent, value.Syntax), |
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.
Dim argument = TryCast(value.Syntax?.Parent, ArgumentSyntax)
Return New VisualBasicArgument(
...
syntax:=If(argument, value.Syntax),
I talked with @mavasani and decide to rebase this to master and checkin. |
changed GetOperation to always return one from root operation tree and never use recovery mode of semantic model.