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

Add IOperation support for anonymous object creation expressions. #20364

Merged

Conversation

mavasani
Copy link
Contributor

Fixes #19926

@@ -95,6 +96,33 @@ internal static IArgument CreateArgumentOperation(ArgumentKind kind, IParameterS
invokedAsExtensionMethod: invokedAsExtensionMethod);
}

private ImmutableArray<IOperation> GetAnonymousObjectCreationInitializers(BoundAnonymousObjectCreationExpression expression)
{
// For error cases, the binder generates only the argument.
Copy link
Contributor Author

@mavasani mavasani Jun 21, 2017

Choose a reason for hiding this comment

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

See #20338 for additional gotchas here. #Resolved

@@ -705,6 +705,68 @@ public static MetadataReference CreateMetadataReferenceFromIlSource(string ilSou
return CompileAndVerify(comp);
}

protected List<SyntaxNode> GetSyntaxNodeList(SyntaxTree syntaxTree)
Copy link
Contributor Author

@mavasani mavasani Jun 21, 2017

Choose a reason for hiding this comment

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

Code just moved from SemanticModelTestBase to CSharpTestBase. #Resolved

@@ -788,4 +788,59 @@ Public MustInherit Class BasicTestBaseBase
End Function
End Class

#Region "IOperation tree validation"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Code just moved from SemanticModelTestBase to BasicTestBase.

@mavasani
Copy link
Contributor Author

mavasani commented Jun 21, 2017

Tagging @dotnet/analyzer-ioperation @dotnet/roslyn-compiler for review. #Resolved

/// <summary>
/// Represents a C# or VB new/New anonymous object creation expression.
/// </summary>
internal sealed partial class AnonymousObjectCreationExpression : BaseAnonymousObjectCreationExpression, IAnonymousObjectCreationExpression
Copy link
Member

@jcouv jcouv Jun 22, 2017

Choose a reason for hiding this comment

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

AnonymousObjectCreationExpression [](start = 34, length = 33)

I didn't see where AnonymousObjectCreationExpression is used. Can you clarify why declare three types: base, lazy, and non-lazy anonymous object creation? #Pending

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Tagging @heejaechang again for more context, HeeJae can correct me if I am wrong - I think the idea here is to have a definition for both the Lazy and non-lazy versions of each Operation node, with the latter being useful for creating a fully populated tree, which is not being used currently, but we plan to use in future.

Copy link
Member

Choose a reason for hiding this comment

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

If they are not used, I don't think they should be in the PR.


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

Copy link
Member

Choose a reason for hiding this comment

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

The base class is used, so ok to keep. The one that is not used, if I understand correctly, is LazyAnonymousObjectCreationExpression.


In reply to: 124861675 [](ancestors = 124861675,123576594)

Copy link
Member

Choose a reason for hiding this comment

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

This issue is still open.


In reply to: 124862019 [](ancestors = 124862019,124861675,123576594)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Julien, I have added comment to #20204 to address your feedback. Tagging @heejaechang to confirm the above reasoning.


In reply to: 125327101 [](ancestors = 125327101,124862019,124861675,123576594)

/// <summary>
/// Explicitly-specified member initializers.
/// </summary>
public override ImmutableArray<IOperation> Initializers { get; }
Copy link
Member

@jcouv jcouv Jun 22, 2017

Choose a reason for hiding this comment

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

Should the Initializers use a more specific type/interface? I see this was considered in the design notes, but all the tests show IAssignmentExpression goes there. Is it possible to get anything else? #Closed

Copy link
Member

Choose a reason for hiding this comment

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

Nevermind, I found one example of some other type (in test AnonymousTypeSymbols_BaseAccessInMembers_OperationTree).


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

Copy link
Contributor Author

@mavasani mavasani Jun 22, 2017

Choose a reason for hiding this comment

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

Yes, I initially had IAssignmentExpression, but there are scenarios where we can get different IOperation for error cases. #Resolved

@mavasani
Copy link
Contributor Author

mavasani commented Jun 23, 2017

Ping any more feedback? #Resolved

}

IOperation target = Create(expression.Declarations[i]);
bool isInvalid = target.IsInvalid || value.IsInvalid;
Copy link
Member

@cston cston Jun 23, 2017

Choose a reason for hiding this comment

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

Is it necessary to consider the entire assignment invalid if either target or value are invalid? Will that mean analyzers will typically skip the valid parts of the assignment? #Pending

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@heejaechang Is actually working on a PR to remove IOperation.IsInvalid flag. It is currently a very fragile heuristic, and we are replacing it with an extension method on IOperation that determines the result by computing if the span has syntax or semantic diagnostics within it.

var o = (Action)(() => (/*<bind>*/new { x = 1, y = new { } }/*</bind>*/).ToString()); ;
}
}
";
Copy link
Member

Choose a reason for hiding this comment

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

What is different about anonymous types created in a lambda?

That said, perhaps reference locals or parameters from the containing method, if there are not already tests that cover captured variables, although captured variables can be tested separately from anonymous types.

Copy link
Contributor Author

@mavasani mavasani Jun 26, 2017

Choose a reason for hiding this comment

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

Nothing really, I was just creating cloned IOperation tests for existing tests in this source files.


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

}
";
string expectedOperationTree = @"
IBlockStatement (2 statements, 2 locals) (OperationKind.BlockStatement, IsInvalid) (Syntax: '{ ... }')
Copy link
Member

@cston cston Jun 24, 2017

Choose a reason for hiding this comment

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

Why is the block marked invalid? #Pending

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same issue as others - this flag is current based off BoundNode.HasErrors. @heejaechang is planning to replace this with an IsInValid extension method.


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

IBlockStatement (2 statements, 2 locals) (OperationKind.BlockStatement, IsInvalid) (Syntax: '{ ... }')
Locals: Local_1: System.Object v1
Local_2: System.Object v2
IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement, IsInvalid) (Syntax: 'object v1 = ... };')
Copy link
Member

@cston cston Jun 24, 2017

Choose a reason for hiding this comment

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

Why is the variable declaration invalid? #Pending

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same issue as above - IsInvalid needs to be changed.


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

{
public static void Test1(int x)
{
using (var v1 = /*<bind>*/new { }/*</bind>*/)
Copy link
Member

Choose a reason for hiding this comment

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

Consider <bind> around entire declaration rather than around anonymous type expression since the expression is not invalid.

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.


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

Left: IPropertyReferenceExpression: System.Double <anonymous type: System.Int32 aa, System.String $1, System.Double bb>.bb { get; } (Static) (OperationKind.PropertyReferenceExpression, Type: System.Double) (Syntax: 'bb')
Right: IFieldReferenceExpression: System.String ClassA.aa (Static) (OperationKind.FieldReferenceExpression, Type: System.String) (Syntax: 'ClassA.aa')
ILiteralExpression (Text: 1.2) (OperationKind.LiteralExpression, Type: System.Double, Constant: 1.2) (Syntax: '1.2')
";
Copy link
Member

@cston cston Jun 24, 2017

Choose a reason for hiding this comment

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

I may be misreading the expected result, but it looks like the string represents { aa = 1, bb = ClassA.aa, 1.2 }. #Resolved

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, that is a correct observation, and hence this is a skipped test. See #20338 for the tracking issue.


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

@@ -705,6 +705,68 @@ public static MetadataReference CreateMetadataReferenceFromIlSource(string ilSou
return CompileAndVerify(comp);
}

protected List<SyntaxNode> GetSyntaxNodeList(SyntaxTree syntaxTree)
Copy link
Member

@cston cston Jun 24, 2017

Choose a reason for hiding this comment

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

Can all of these members be static? #Resolved

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 make that change.


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

@mavasani
Copy link
Contributor Author

mavasani commented Jun 27, 2017

@cston addressed the feedback. #Resolved

@cston
Copy link
Member

cston commented Jun 27, 2017

LGTM

@mavasani
Copy link
Contributor Author

@dotnet/roslyn-infrastructure Seems like ubuntu_16_debug_prtest failed due to an unrelated build error:

Hosting/AssemblyLoader/CoreAssemblyLoaderImpl.cs(7,22): error CS0234: The type or namespace name 'Loader' does not exist in the namespace 'System.Runtime' (are you missing an assembly reference?) [/mnt/j/workspace/dotnet_roslyn/features_ioperation/ubuntu_16_debug_prtest/src/Scripting/Core/Scripting.csproj

@mavasani
Copy link
Contributor Author

@dotnet-bot retest ubuntu_16_debug_prtest please

@mavasani
Copy link
Contributor Author

mavasani commented Jun 27, 2017

@jcouv Can you please take another look? #Resolved

@mavasani
Copy link
Contributor Author

mavasani commented Jun 29, 2017

Ping, I need one more compiler sign off. #Resolved

@jinujoseph
Copy link
Contributor

cc @dotnet/roslyn-compiler

private IPropertyReferenceExpression CreateBoundAnonymousPropertyDeclarationOperation(BoundAnonymousPropertyDeclaration boundAnonymousPropertyDeclaration)
{
PropertySymbol property = boundAnonymousPropertyDeclaration.Property;
Lazy<IOperation> instance = new Lazy<IOperation>(() => null);
Copy link
Member

@jaredpar jaredpar Jun 29, 2017

Choose a reason for hiding this comment

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

Why have a lazy that always returns null? Seems like null coudl just be used directly here. #Resolved

Copy link
Contributor Author

@mavasani mavasani Jul 3, 2017

Choose a reason for hiding this comment

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

I have followed the pattern used in this file. @heejaechang Will having a null child node be handled by the operation factory?

I have added a comment to #20204 to track this feedback for the entire file. #Resolved

case BoundKind.ConstantPattern:
return CreateBoundConstantPatternOperation((BoundConstantPattern)boundNode);
case BoundKind.DeclarationPattern:
return CreateBoundDeclarationPatternOperation((BoundDeclarationPattern)boundNode);
case BoundKind.WildcardPattern:
return null;
Copy link
Member

@jcouv jcouv Jun 29, 2017

Choose a reason for hiding this comment

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

I didn't understand this change. How do you know this is unreachable? #Closed

Copy link
Contributor Author

@mavasani mavasani Jul 3, 2017

Choose a reason for hiding this comment

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

@jcouv - This is addressing feedback from #20276 (comment). We handle this bound node while operation on it's parent here: https://github.com/dotnet/roslyn/pull/20276/files#diff-df80689b64fbe29db28a562eb0dac8caR1154 #Resolved

Copy link
Member

@jaredpar jaredpar left a comment

Choose a reason for hiding this comment

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

There are coding style issues that need to be addressed before checking in. Outside that LGTM.

}

protected const string startString = "/*<bind>*/";
protected const string endString = "/*</bind>*/";
Copy link
Member

@jaredpar jaredpar Jun 29, 2017

Choose a reason for hiding this comment

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

Accessible members should be PascalCased not camelCased. #Resolved

Copy link
Contributor Author

@mavasani mavasani Jul 3, 2017

Choose a reason for hiding this comment

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

This is not new code. I am moving the existing code from SemanticModelTestBase to CSharpTestBase. I'll create a separate compiler test bug to track this work. #Resolved

Copy link
Contributor Author

@mavasani mavasani Jul 3, 2017

Choose a reason for hiding this comment

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

Filed #20611 #Resolved

Copy link
Contributor Author

@mavasani mavasani Jul 3, 2017

Choose a reason for hiding this comment

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

I went ahead and addressed the test feedback in the next commit. #Resolved


if (exprFullText.StartsWith(startString, StringComparison.Ordinal))
{
if (exprFullText.Contains(endString))
Copy link
Member

@jaredpar jaredpar Jun 29, 2017

Choose a reason for hiding this comment

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

Braces are required for anything other than single line if statements. Please add them here. #Resolved

Copy link
Contributor Author

@mavasani mavasani Jul 3, 2017

Choose a reason for hiding this comment

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

Ditto, existing compiler test code being moved around. #Resolved


if (exprFullText.EndsWith(endString, StringComparison.Ordinal))
{
if (exprFullText.Contains(startString))
Copy link
Member

@jaredpar jaredpar Jun 29, 2017

Choose a reason for hiding this comment

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

Same feedback. #Resolved

Copy link
Contributor Author

@mavasani mavasani Jul 3, 2017

Choose a reason for hiding this comment

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

Existing compiler test code being moved around #Resolved

@mavasani mavasani force-pushed the IAnonymousObjectCreation branch from 0b3bd6e to 9bd67cc Compare July 3, 2017 17:52
SyntaxNode syntax = boundAnonymousPropertyDeclaration.Syntax;
ITypeSymbol type = boundAnonymousPropertyDeclaration.Type;
Optional<object> constantValue = ConvertToOptional(boundAnonymousPropertyDeclaration.ConstantValue);
return new LazyPropertyReferenceExpression(property, instance, member, argumentsInEvaluationOrder, isInvalid, syntax, type, constantValue);
Copy link
Member

Choose a reason for hiding this comment

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

Is this change (adding argumentsInEvaluationOrder) covered by test?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Currently the dumper doesn't print out child operation arrays which are empty, hence we cannot cover it in tests. @jinujoseph is working on updating the operation tree dumper to print a header with 0 element count for this case, at which point the unit tests will cover it.

Copy link
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

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

LGTM with one request to test argumentsInEvaluationOrder change.

@mavasani mavasani force-pushed the IAnonymousObjectCreation branch from 0b15671 to f49daed Compare July 3, 2017 21:56
@mavasani mavasani merged commit 548d0fa into dotnet:features/ioperation Jul 6, 2017
@mavasani mavasani deleted the IAnonymousObjectCreation branch July 6, 2017 18:02
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.

6 participants