-
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
Fix IArgument.InConversion/OutConversion #21445
Conversation
I think this is mostly done. Please review @AlekseyTs @cston @dotnet/analyzer-ioperation |
|
|
||
namespace Microsoft.CodeAnalysis.CSharp | ||
{ | ||
internal abstract class BaseCSharpArgument : BaseArgument, IArgument |
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.
BaseArgument
already implements IArgument
.
|
||
Public Overrides ReadOnly Property InConversion As [Optional](Of CommonConversion) | ||
Get | ||
Return If(InConversionInternal = Nothing, New [Optional](Of CommonConversion)(), InConversionInternal.ToCommonConversion()) |
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.
Can use Nothing
instead of New [Optional](Of CommonConversion)()
. Same comment for OutConversion
below.
''' </summary> | ||
''' <param name="argument">The argument to get original info from.</param> | ||
''' <returns>The underlying <see cref="Conversion"/> of the InConversion.</returns> | ||
''' <exception cref="InvalidCastException">If the <see cref="IArgument"/> was not created from Visual Basic code.</exception> |
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.
These methods throw ArgumentException
rather than InvalidCastException
.
/// </summary> | ||
IOperation InConversion { get; } | ||
Optional<CommonConversion> InConversion { get; } |
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.
Optional [](start = 8, length = 26)
It doesn't look like it is a common pattern to use Optional<T>
in IOperation API. Would it be better to use identity conversion for situations when no conversion is needed? #Closed
|
||
Public Overrides ReadOnly Property InConversion As [Optional](Of CommonConversion) | ||
Get | ||
Return If(InConversionInternal = Nothing, New [Optional](Of CommonConversion)(), InConversionInternal.ToCommonConversion()) |
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.
InConversionInternal = Nothing [](start = 26, length = 30)
Conversion
is a structure. What does it mean to compare a structure with Nothing and why is it the right thing to do here? #Closed
|
||
Public Overrides ReadOnly Property OutConversion As [Optional](Of CommonConversion) | ||
Get | ||
Return If(OutConversionInternal = Nothing, New [Optional](Of CommonConversion)(), OutConversionInternal.ToCommonConversion()) |
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.
OutConversionInternal = Nothing [](start = 26, length = 31)
Same issue around Nothing
. #Closed
If expression.Kind = BoundKind.Conversion Then | ||
Dim conversion = DirectCast(expression, BoundConversion) | ||
Dim method As MethodSymbol = Nothing | ||
If conversion.Operand.Kind = BoundKind.UserDefinedConversion Then |
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.
conversion.Operand.Kind = BoundKind.UserDefinedConversion [](start = 19, length = 57)
It feels like we should also make sure that the conversion kind for the conversion
node is user-defined conversion. #Closed
@@ -51,9 +51,7 @@ static void Test2(int y, params int[] x) | |||
IArgument (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument) (Syntax: 'null') | |||
IConversionExpression (Implicit, TryCast: False, Unchecked) (OperationKind.ConversionExpression, Type: System.Int32[], Constant: null) (Syntax: 'null') | |||
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) | |||
Operand: ILiteralExpression (Text: null) (OperationKind.LiteralExpression, Type: null, Constant: null) (Syntax: 'null') | |||
InConversion: null | |||
OutConversion: 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.
It feels like these properties should still be validated. #Closed
@@ -835,5 +758,110 @@ BC30272: 'y' is not a parameter of 'Public Sub M2(x As Integer)'. | |||
VerifyOperationTreeAndDiagnosticsForTest(Of InvocationExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) | |||
End Sub | |||
|
|||
|
|||
|
|||
<CompilerTrait(CompilerFeature.IOperation)> |
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.
<CompilerTrait(CompilerFeature.IOperation)> [](start = 8, length = 43)
It looks like there are three new lines between this test and the one above. We usually use one line to separate tests. #Closed
|
||
<CompilerTrait(CompilerFeature.IOperation)> | ||
<Fact> | ||
Public Sub InOutConversionUserDefined() |
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.
InOutConversionUserDefined [](start = 19, length = 26)
It would be good to have tests for scenarios when standard conversions are involved into user-defined conversion, either for the input value, or for the method's return value. The cases when either BoundUserDefinedConversion.InConversionOpt, or BoundUserDefinedConversion.OutConversionOpt are not null. #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 have added a test in the last commit. Is that what you suggested?
@@ -639,9 +639,23 @@ public override void VisitArgument(IArgument operation) | |||
LogString(")"); | |||
LogCommonPropertiesAndNewLine(operation); | |||
|
|||
if (operation.InConversion.HasValue) |
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 (operation.InConversion.HasValue) [](start = 12, length = 36)
I think for the purpose of testing it is better to dump all properties regardless of their values. Usually people don't think about things they do not see. An absence of a value should be verified against expectations as well. #Closed
Done with review pass. #Closed |
Ping... @AlekseyTs @cston |
@AlekseyTs Added tests for extension methods |
@genlu It looks like there are some legitimate test failures. |
Return (Nothing, Nothing) | ||
End If | ||
|
||
Dim tree = (From t In compilation.SyntaxTrees Where t.FilePath = fileName).Single() |
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 tree = (From t In compilation.SyntaxTrees Where t.FilePath = fileName).Single() [](start = 8, length = 83)
Why so complex? Would node.SyntaxTree
work?
Dim semanticModel = compilation.GetSemanticModel(compilation.SyntaxTrees(0)) | ||
|
||
Dim expectedInKind = ConversionKind.Widening Or ConversionKind.UserDefined | ||
Dim exptectedInMethod = CType(semanticModel.GetDeclaredSymbolFromSyntaxNode(operatorNodes(0)), IMethodSymbol) |
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.
CType(semanticModel.GetDeclaredSymbolFromSyntaxNode(operatorNodes(0)), IMethodSymbol) [](start = 36, length = 85)
This looks more complicated than it should be. There is no need to use semantic model to find methods, I believe that could be done by name.
|
||
<CompilerTrait(CompilerFeature.IOperation)> | ||
<Fact> | ||
Public Sub GettingInOutConverionFromVBArgument() |
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.
GettingInOutConverionFromVBArgument [](start = 19, length = 35)
There is probably a value in testing clone operation, consider keeping the TestCloneInOutConversion unit-test from iteration 21.
Done with review pass (iteration 22). The only blocking issue is the failing test. |
554cb12
to
a6c0b36
Compare
a6c0b36
to
dbc57e3
Compare
@dotnet/roslyn-infrastructure It seems the tests are not triggered by new commits. Any ideas? |
Looking into this. |
@cston Can I get another review from compiler team please? |
|
||
[CompilerTrait(CompilerFeature.IOperation)] | ||
[Fact] | ||
public void GettingInOutConverionFromCSharpArgumentShouldThrowException() |
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.
Typo: Conversion
|
||
Assert.Throws<ArgumentException>(() => argument.GetInConversion()); | ||
Assert.Throws<ArgumentException>(() => argument.GetOutConversion()); | ||
} |
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.
Where are GetInConversion
and GetOutConversion
defined? And why do they throw ArgumentException
in this case?
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.
Those are extension methods for VB only (defined in MS.CA.VB.dll), as it returns VB Conversion
. In this test we pass in an IArgument from C# code, therefore it throws.
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.
Thanks. Perhaps add a comment here that we're calling VB methods so it's clear why we expect exceptions to be thrown.
public override CommonConversion OutConversion => new CommonConversion(exists: true, isIdentity: true, isNumeric: false, isReference: false, methodSymbol: null); | ||
} | ||
|
||
internal sealed partial class CSharpArgument : BaseCSharpArgument |
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 these CSharpArgument
classes need to be partial
?
Return (operation, node) | ||
Else | ||
Return (Nothing, Nothing) | ||
End If |
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 If operation IsNot Nothing
necessary or could this method Return (operation, node)
always?
inConversion:=Nothing, | ||
outConversion:=Nothing, | ||
inConversion:=New Conversion(Conversions.Identity), | ||
outConversion:=New Conversion(Conversions.Identity), | ||
semanticModel:=_semanticModel, | ||
syntax:=value.Syntax, | ||
type:=Nothing, |
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.
It looks like the only difference between If
and Else
is kind
. Consider calculating kind
inside the If
and move the remainder outside the If
.
@@ -2245,6 +2245,7 @@ static void M2(int x) | |||
var invocation = (IInvocationExpression)operation; | |||
var argument = invocation.ArgumentsInEvaluationOrder[0]; | |||
|
|||
// We are calling VB extension methods on IArgument in C# code, therefore exception is expteced here. |
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.
Typo: expected
Return New VisualBasicArgument( | ||
kind, | ||
parameter, | ||
Create(argument), |
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.
value
@@ -105,47 +105,37 @@ Namespace Microsoft.CodeAnalysis.Semantics | |||
isImplicit:=isImplicit) | |||
Case Else | |||
Dim lastParameterIndex = parameters.Length - 1 | |||
Dim kind As ArgumentKind | |||
Dim parameter As ParameterSymbol |
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.
parameter = parameters(index)
in both branches.
Fix #18548. Changed the In/OutConversion from
IConversionExpression
toCommonConversion
(added in #21040 )