Skip to content

Commit

Permalink
Fix analysis of IOperation descendants for BoundDelegateCreationExpre…
Browse files Browse the repository at this point in the history
…ssion

The current IOperation API implementation of BoundDelegateCreationExpression has a bunch of issues and needs to be redesigned. This is tracked by dotnet#8897 and will be addressed post 15.3. Meanwhile, to unblock analyzers on code containing delegate creation expressions with lambda arguments, this bound node has been switched to OperationKind.None with override for Children property.

Fixes the first repro case provided in dotnet#8884
  • Loading branch information
mavasani committed May 5, 2017
1 parent e08df4c commit 5a6e108
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 59 deletions.
34 changes: 5 additions & 29 deletions src/Compilers/CSharp/Portable/BoundTree/Expression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -414,44 +414,20 @@ public override TResult Accept<TArgument, TResult>(OperationVisitor<TArgument, T
}
}

internal partial class BoundDelegateCreationExpression : IMethodBindingExpression
internal partial class BoundDelegateCreationExpression
{
IOperation IMemberReferenceExpression.Instance
{
get
{
BoundMethodGroup methodGroup = this.Argument as BoundMethodGroup;
if (methodGroup != null)
{
return methodGroup.InstanceOpt;
}

return null;
}
}

bool IMethodBindingExpression.IsVirtual =>
(object)this.MethodOpt != null &&
(this.MethodOpt.IsVirtual || this.MethodOpt.IsAbstract || this.MethodOpt.IsOverride) &&
!this.SuppressVirtualCalls;

ISymbol IMemberReferenceExpression.Member => this.MethodOpt;

IMethodSymbol IMethodBindingExpression.Method => this.MethodOpt;

protected override OperationKind ExpressionKind => OperationKind.MethodBindingExpression;
protected override OperationKind ExpressionKind => OperationKind.None;

// SyntaxNode for MethodBindingExpression is the argument of DelegateCreationExpression
SyntaxNode IOperation.Syntax => this.Argument.Syntax;
protected override ImmutableArray<IOperation> Children => ImmutableArray.Create<IOperation>(this.Argument);

public override void Accept(OperationVisitor visitor)
{
visitor.VisitMethodBindingExpression(this);
visitor.VisitNoneOperation(this);
}

public override TResult Accept<TArgument, TResult>(OperationVisitor<TArgument, TResult> visitor, TArgument argument)
{
return visitor.VisitMethodBindingExpression(this, argument);
return visitor.VisitNoneOperation(this, argument);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,96 @@ public void M(int x, int y, int z)
VerifyOperationTreeAndDiagnosticsForTest<ObjectCreationExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics);
}

[Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")]
public void ParameterReference_DelegateCreationExpressionWithLambdaArgument()
{
string source = @"
using System;
class Class
{
// Used parameter methods
public void UsedParameterMethod1(Action a)
{
Action a2 = /*<bind>*/new Action(() =>
{
a();
})/*</bind>*/;
}
}
";
string expectedOperationTree = @"
IOperation: (OperationKind.None) (Syntax: 'new Action( ... })')
Children(1): ILambdaExpression (Signature: lambda expression) (OperationKind.LambdaExpression, Type: System.Action) (Syntax: '() => ... }')
IBlockStatement (2 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }')
IExpressionStatement (OperationKind.ExpressionStatement) (Syntax: 'a();')
IInvocationExpression (virtual void System.Action.Invoke()) (OperationKind.InvocationExpression, Type: System.Void) (Syntax: 'a()')
Instance Receiver: IParameterReferenceExpression: a (OperationKind.ParameterReferenceExpression, Type: System.Action) (Syntax: 'a')
IReturnStatement (OperationKind.ReturnStatement) (Syntax: '{ ... }')
";
var expectedDiagnostics = DiagnosticDescription.None;

VerifyOperationTreeAndDiagnosticsForTest<ObjectCreationExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics);
}

[Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")]
public void ParameterReference_DelegateCreationExpressionWithMethodArgument()
{
string source = @"
using System;
class Class
{
public delegate void Delegate(int x, int y);
public void Method(Delegate d)
{
var a = /*<bind>*/new Delegate(Method2)/*</bind>*/;
}
public void Method2(int x, int y)
{
}
}
";
string expectedOperationTree = @"
IOperation: (OperationKind.None) (Syntax: 'new Delegate(Method2)')
Children(1): IOperation: (OperationKind.None) (Syntax: 'Method2')
";
var expectedDiagnostics = DiagnosticDescription.None;

VerifyOperationTreeAndDiagnosticsForTest<ObjectCreationExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics);
}

[Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")]
public void ParameterReference_DelegateCreationExpressionWithInvalidArgument()
{
string source = @"
using System;
class Class
{
public delegate void Delegate(int x, int y);
public void Method(int x)
{
var a = /*<bind>*/new Delegate(x)/*</bind>*/;
}
}
";
string expectedOperationTree = @"
IInvalidExpression (OperationKind.InvalidExpression, Type: Class.Delegate, IsInvalid) (Syntax: 'new Delegate(x)')
Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x')
";
var expectedDiagnostics = new DiagnosticDescription[] {
// CS0149: Method name expected
// var a = /*<bind>*/new Delegate(x)/*</bind>*/;
Diagnostic(ErrorCode.ERR_MethodNameExpected, "x").WithLocation(10, 40)
};

VerifyOperationTreeAndDiagnosticsForTest<ObjectCreationExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics);
}

[Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")]
public void ParameterReference_DynamicCollectionInitializer()
{
Expand Down
37 changes: 7 additions & 30 deletions src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb
Original file line number Diff line number Diff line change
Expand Up @@ -1500,46 +1500,23 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End Class

Friend Partial Class BoundDelegateCreationExpression
Implements IMethodBindingExpression

Private ReadOnly Property IMemberReferenceExpression_Instance As IOperation Implements IMemberReferenceExpression.Instance
Get
If Me.Method.IsShared Then
Return Nothing
Else
Return Me.ReceiverOpt
End If
End Get
End Property

Private ReadOnly Property IMethodBindingExpression_IsVirtual As Boolean Implements IMethodBindingExpression.IsVirtual
Get
Return Me.Method IsNot Nothing AndAlso (Me.Method.IsOverridable OrElse Me.Method.IsOverrides OrElse Me.Method.IsMustOverride) AndAlso Not Me.SuppressVirtualCalls
End Get
End Property

Private ReadOnly Property IMemberReferenceExpression_Member As ISymbol Implements IMemberReferenceExpression.Member
Get
Return Me.Method
End Get
End Property
Protected Overrides Function ExpressionKind() As OperationKind
Return OperationKind.None
End Function

Private ReadOnly Property IMethodBindingExpression_Method As IMethodSymbol Implements IMethodBindingExpression.Method
Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation)
Get
Return Me.Method
Return ImmutableArray.Create(Of IOperation)(Me.ReceiverOpt)
End Get
End Property

Protected Overrides Function ExpressionKind() As OperationKind
Return OperationKind.MethodBindingExpression
End Function

Public Overrides Sub Accept(visitor As OperationVisitor)
visitor.VisitMethodBindingExpression(Me)
visitor.VisitNoneOperation(Me)
End Sub

Public Overrides Function Accept(Of TArgument, TResult)(visitor As OperationVisitor(Of TArgument, TResult), argument As TArgument) As TResult
Return visitor.VisitMethodBindingExpression(Me, argument)
Return visitor.VisitNoneOperation(Me, argument)
End Function
End Class

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,85 @@ IObjectCreationExpression (Constructor: Sub [Class]..ctor()) (OperationKind.Obje
VerifyOperationTreeAndDiagnosticsForTest(Of ObjectCreationExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics)
End Sub

<Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")>
Public Sub ParameterReference_DelegateCreationExpressionWithLambdaArgument()
Dim source = <![CDATA[
Option Strict Off
Imports System

Class Class1
Delegate Sub DelegateType()
Public Sub M(x As Object, y As EventArgs)
Dim eventHandler As New EventHandler(Function() x)'BIND:"New EventHandler(Function() x)"
End Sub
End Class]]>.Value

Dim expectedOperationTree = <![CDATA[
IConversionExpression (ConversionKind.Basic, Implicit) (OperationKind.ConversionExpression, Type: System.EventHandler) (Syntax: 'New EventHa ... nction() x)')
ILambdaExpression (Signature: Function () As System.Object) (OperationKind.LambdaExpression, Type: null) (Syntax: 'Function() x')
IBlockStatement (3 statements, 1 locals) (OperationKind.BlockStatement) (Syntax: 'Function() x')
Locals: Local_1: <anonymous local> As System.Object
IReturnStatement (OperationKind.ReturnStatement) (Syntax: 'x')
IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Object) (Syntax: 'x')
ILabelStatement (Label: exit) (OperationKind.LabelStatement) (Syntax: 'Function() x')
IReturnStatement (OperationKind.ReturnStatement) (Syntax: 'Function() x')
ILocalReferenceExpression: (OperationKind.LocalReferenceExpression, Type: System.Object) (Syntax: 'Function() x')
]]>.Value

Dim expectedDiagnostics = String.Empty

VerifyOperationTreeAndDiagnosticsForTest(Of ObjectCreationExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics)
End Sub

<Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")>
Public Sub ParameterReference_DelegateCreationExpressionWithMethodArgument()
Dim source = <![CDATA[
Imports System

Class Class1
Public Sub M(x As Object, y As EventArgs)
Dim eventHandler As New EventHandler(AddressOf Me.M)'BIND:"New EventHandler(AddressOf Me.M)"
End Sub
End Class]]>.Value

Dim expectedOperationTree = <![CDATA[
IConversionExpression (ConversionKind.Basic, Explicit) (OperationKind.ConversionExpression, Type: System.EventHandler) (Syntax: 'New EventHa ... essOf Me.M)')
IOperation: (OperationKind.None) (Syntax: 'AddressOf Me.M')
Children(1): IInstanceReferenceExpression (InstanceReferenceKind.Explicit) (OperationKind.InstanceReferenceExpression, Type: Class1) (Syntax: 'Me')
]]>.Value

Dim expectedDiagnostics = String.Empty

VerifyOperationTreeAndDiagnosticsForTest(Of ObjectCreationExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics)
End Sub

<Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")>
Public Sub ParameterReference_DelegateCreationExpressionWithInvalidArgument()
Dim source = <![CDATA[
Option Strict Off
Imports System

Class Class1
Delegate Sub DelegateType()
Public Sub M(x As Object, y As EventArgs)
Dim eventHandler As New EventHandler(x)'BIND:"New EventHandler(x)"
End Sub
End Class]]>.Value

Dim expectedOperationTree = <![CDATA[
IInvalidExpression (OperationKind.InvalidExpression, Type: System.EventHandler, IsInvalid) (Syntax: 'New EventHandler(x)')
Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Object) (Syntax: 'x')
]]>.Value

Dim expectedDiagnostics = <![CDATA[
BC32008: Delegate 'EventHandler' requires an 'AddressOf' expression or lambda expression as the only argument to its constructor.
Dim eventHandler As New EventHandler(x)'BIND:"New EventHandler(x)"
~~~
]]>.Value

VerifyOperationTreeAndDiagnosticsForTest(Of ObjectCreationExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics)
End Sub

<Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")>
Public Sub ParameterReference_NameOfExpression()
Dim source = <![CDATA[
Expand Down

0 comments on commit 5a6e108

Please sign in to comment.