-
Notifications
You must be signed in to change notification settings - Fork 4k
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
InitializeParameter refactorings support for local & anonymous functions #24624
Conversation
…ndling of return statement insertion
@@ -48,7 +48,7 @@ internal static class ArrowExpressionClauseSyntaxExtensions | |||
return true; | |||
} | |||
|
|||
private static StatementSyntax ConvertToStatement( | |||
public static StatementSyntax ConvertToStatement( |
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.
This is obviously not ideal... The thing is that this method is also useful for converting expression lambdas to statements, not just expression-bodied members. Maybe it could be moved to ExpressionSyntaxExtensions and then there would be a separate class for lambda extensions? I didn't want to restructure the code significantly in my first contribution, especially in layers below that I wasn't supposed to touch, just to make a simple change in a refactoring.
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 would leave it here for now.
@CyrusNajmabadi could you please give me some feedback? there's a few rough spots I'm not sure what to do about |
class C | ||
public sub new() | ||
dim f = sub (s as string) | ||
|
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.
All these blank lines in VB... the VB syntax generator does that for some reason, I have no idea what causes 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.
yeah... that's not good. we'd def want to fix this. or at least open a bug here. we don't do this for normal sub/function methods right?
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 checked again and it does create blank lines between the statements in methods, but not before the first one... in lambdas it also creates a blank line before the first statement.
In both cases (methods & lambdas) it does not create a blank line after the last statement - so if the check inserted is the last statement, there's no blank line after it, but if there's a return statement at the end, it creates a blank line before the return statement.
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 was suspicious this might have been caused by the call to Isolate (which calls PreserveTrivia) in VisualBasicSyntaxGenerator.WithStatements - there's no such thing in CSharpSyntaxGenerator, but that's not the case, I get same behavior if I directly call WithStatementsInternal. It must be some sort of formatting issue.
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.
@CyrusNajmabadi I identified the issue. It's in ElasticTriviaFormattingRule.TopLevelStatement. It's a one-line fix to add TypeOf statement Is LambdaHeaderSyntax
. Should I include it in this PR?
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'd have no problem with that.
case AnonymousFunctionExpressionSyntax anonymousFunction: | ||
return (SyntaxNode)anonymousFunction.Body; | ||
default: | ||
Debug.Fail($"Unexpected {nameof(functionDeclaration)} type"); |
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.
Throw.
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.
fixed
case AnonymousFunctionExpressionSyntax _: | ||
return null; | ||
default: | ||
Debug.Fail($"Unexpected {nameof(functionDeclaration)} type"); |
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.
throw.
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.
fixed
editor.SetStatements(functionDeclaration, statementToAddAfterOpt == null | ||
? ImmutableArray.Create(statement, convertedStatement) | ||
: ImmutableArray.Create(convertedStatement, statement) | ||
); |
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.
put on previous line.
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.
just the );
?
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.
yup
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.
fixed
{ | ||
switch(declarationSyntax) | ||
// either expression lambda or expression bodied member | ||
return body is ExpressionSyntax || body is ArrowExpressionClauseSyntax; |
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 => 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.
fixed
{ | ||
return; | ||
} | ||
|
||
if (parameter.Name == "") | ||
var method = (IMethodSymbol)parameter.ContainingSymbol; | ||
if (method == 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.
null check not needed.
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.
fixed
method.IsAbstract || | ||
method.IsExtern || | ||
method.PartialImplementationPart != null || | ||
method.ContainingType?.TypeKind == TypeKind.Interface) |
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.
?. not needed.
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.
fixed
@@ -107,16 +104,12 @@ internal abstract partial class AbstractInitializeParameterCodeRefactoringProvid | |||
if (bodyOpt != null) | |||
{ | |||
blockStatementOpt = semanticModel.GetOperation(bodyOpt, cancellationToken) as IBlockOperation; | |||
if (blockStatementOpt == 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.
this is just paranoia until IOperation is complete.
SyntaxFactory.ReturnStatement(DirectCast(singleLineLambda.Body, ExpressionSyntax))) | ||
return SyntaxFactory.List(ImmutableArray.Create(convertedStatement)) | ||
Else | ||
Debug.Fail($"Unexpected {NameOf(functionDeclaration)} type") |
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.
throw.
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.
fixed
Looking awesome! |
I found a bug: in VB lambdas the null checks don't get ordered correctly and are offered even if there is an existing one. The IOperation wireup must be wrong there. |
All fixed... I think this is ready |
@@ -1282,7 +1282,6 @@ End Module", | |||
Module Program | |||
Sub Main | |||
Dim a = Sub(x As Integer) | |||
|
|||
If True 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.
these deletions are good. this PR also fixes a VB formatting bug around elastic trivia and lambdas.
return InitializeParameterHelpers.TryConvertExpressionBodyToStatement(body, | ||
semicolonToken: SyntaxFactory.Token(SyntaxKind.SemicolonToken), | ||
createReturnStatementForExpression: false, | ||
statement: out var _); |
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 exercises this codepath when TryConvertExpressionBodyToStatement returns false?
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:
roslyn/src/EditorFeatures/CSharpTest/InitializeParameter/AddParameterCheckTests.cs
Line 1170 in 957882b
public async Task TestMissingInArrowExpression1() |
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.
great. thanks!
{ | ||
var bodyOpt = GetBody(functionDeclaration); | ||
if (bodyOpt == null) | ||
return 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.
curlies always required.
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.
:( ok
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.
done
case LocalFunctionStatementSyntax localFunction: | ||
return (SyntaxNode)localFunction.Body ?? localFunction.ExpressionBody; | ||
case AnonymousFunctionExpressionSyntax anonymousFunction: | ||
return (SyntaxNode)anonymousFunction.Body; |
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.
this cast should not be 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.
it's not, i wanted it to look the same as on previous lines... i'll remove 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.
removed
retest windows_coreclr_debug_prtest please |
retest windows_debug_spanish_unit32_prtest please |
@@ -462,6 +462,150 @@ public C(string s) | |||
Option(CSharpCodeStyleOptions.PreferExpressionBodiedConstructors, CSharpCodeStyleOptions.WhenPossibleWithSuggestionEnforcement))); | |||
} | |||
|
|||
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInitializeParameter)] |
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.
💡 Would be good to see the new tests in this PR have a WorkItem
attribute pointing to #20983.
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 in d5dbed4
else if (condition is IIsPatternOperation isPatternOperation && | ||
isPatternOperation.Pattern is IConstantPatternOperation constantPattern) | ||
{ | ||
if (IsNullCheck(constantPattern.Value, isPatternOperation.Value, parameter)) |
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.
💡
// Look for code of the form "if (p is 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.
added in 9876619
@@ -370,6 +370,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting | |||
Private Function TopLevelStatement(statement As StatementSyntax) As Boolean | |||
Return TypeOf statement Is MethodStatementSyntax OrElse | |||
TypeOf statement Is SubNewStatementSyntax OrElse | |||
TypeOf statement Is LambdaHeaderSyntax OrElse |
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.
🎉
Return containingMember | ||
Public Shared Function IsFunctionDeclaration(node As SyntaxNode) As Boolean | ||
Return TypeOf node Is MethodBlockBaseSyntax OrElse | ||
TypeOf node Is LambdaExpressionSyntax |
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.
💭 The code could be simplified to ISyntaxFactsService
if we could resolve the difference between TypeOf node Is MethodBlockBaseSyntax
(which is VisualBasicSyntaxFactsService.IsBlockBody
) and node is BaseMethodDeclarationSyntax
(which is a bit different). The rest is just IsAnonymousFunction||IsLocalFunction
(where the latter always returns false for VB).
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 would love to simplify this more but there's nothing I could come up with that would make this simpler (unless I created a new API, which of course would result in more code overall, not less)
|| node is LocalFunctionStatementSyntax | ||
|| node is AnonymousFunctionExpressionSyntax; | ||
|
||
public static IBlockOperation GetBlockOperation(SyntaxNode functionDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken) |
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 some of the helpers could be unified, but it wasn't obvious how it would work so this seems fine for now.
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 tried a few approaches but nothing actually made things simpler. I don't think there's a unified way to get a block operation from a method-level declaration, local function or anonymous function, is there? Unfortunately I think this has to implemented separately.
@sharwell any thing pending here ? LGTM approved to merge for 15.8.preview3 |
I think this is my first PR 🎉 Thanks! |
fixes #20983