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

changed how GetOperation work. #21857

Merged
merged 26 commits into from
Sep 21, 2017
Merged

changed how GetOperation work. #21857

merged 26 commits into from
Sep 21, 2017

Conversation

heejaechang
Copy link
Contributor

changed GetOperation to always return one from root operation tree and never use recovery mode of semantic model.

@heejaechang heejaechang added the PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it. label Aug 31, 2017
@heejaechang
Copy link
Contributor Author

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
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 6, 2017

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

Copy link
Contributor Author

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#
@heejaechang heejaechang force-pushed the Parent2 branch 2 times, most recently from 14ab0aa to afd0475 Compare September 7, 2017 21:55
@heejaechang heejaechang closed this Sep 7, 2017
@heejaechang heejaechang reopened this Sep 7, 2017
@heejaechang
Copy link
Contributor Author

@dotnet/roslyn-infrastructure test is not running, is this a known issue?

@jasonmalinowski
Copy link
Member

@heejaechang heejaechang removed the PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it. label Sep 9, 2017
@heejaechang
Copy link
Contributor Author

@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);
Copy link
Contributor Author

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();
Copy link
Contributor Author

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)));
Copy link
Contributor Author

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),
Copy link
Contributor Author

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>*/
Copy link
Contributor Author

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.

Copy link
Contributor

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()?

Copy link
Contributor

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?

Copy link
Contributor

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...

Copy link
Contributor Author

@heejaechang heejaechang Sep 13, 2017

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]')
Copy link
Contributor Author

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);
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 20, 2017

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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a blocker.


In reply to: 140047143 [](ancestors = 140047143)

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
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 20, 2017

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

Copy link
Contributor

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)

Copy link
Contributor Author

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.

Copy link
Contributor Author

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 = <![CDATA[
IInvalidExpression (OperationKind.InvalidExpression, Type: I, IsInvalid) (Syntax: 'New I(x)')
Children(2):
IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Object, IsInvalid) (Syntax: 'x')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like we are losing information about parameter reference, this feels wrong, please open an issue to follow up.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opened - #22229

...

the information is still there, just user gets the one of children initially that points to same syntax node rather than InvalidExpression. user can use Parent and children to get to the ParameterReferenceExpression.

@AlekseyTs
Copy link
Contributor

IOperation: (OperationKind.None, IsInvalid) (Syntax: 'new I(x)')

It looks like information about parameter reference is lost, please open an issue to follow up.


Refers to: src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs:890 in 4154db4. [](commit_id = 4154db4, deletion_comment = False)

Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@heejaechang
Copy link
Contributor Author

@cston can you take a look as well?

{
get
{
System.Diagnostics.Debug.Assert(this.Kind == BoundKind.StatementList || this.Kind == BoundKind.Scope);
Copy link
Member

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?

Copy link
Contributor Author

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...

Copy link
Contributor Author

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider removing the assert.

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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;
Copy link
Member

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;
Copy link
Member

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)
Copy link
Member

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.

Copy link
Contributor Author

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?

Copy link
Contributor Author

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,
Copy link
Member

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)
{
Copy link
Member

@cston cston Sep 20, 2017

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.

Copy link
Contributor Author

@heejaechang heejaechang Sep 20, 2017

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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));
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Contributor Author

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
Copy link
Member

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),
Copy link
Member

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),

@heejaechang
Copy link
Contributor Author

I talked with @mavasani and decide to rebase this to master and checkin.

@heejaechang heejaechang changed the base branch from features/ioperation to master September 20, 2017 21:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants