Some fixes for patterns in IDE#26474
Some fixes for patterns in IDE#26474jcouv wants to merge 19 commits intodotnet:features/recursive-patternsfrom
Conversation
| { | ||
| // This occurs in error cases. | ||
| Debug.Assert(recursive.HasAnyErrors); | ||
| //Debug.Assert(recursive.HasAnyErrors); |
There was a problem hiding this comment.
I won't merge this change. It's to unblock manual validation in IDE.
Filed #26467 #Closed
There was a problem hiding this comment.
Should a colon be displayed in the completion? Prop:
Should typing enter or tab automatically enter the colon?
| } | ||
| } | ||
|
|
||
| return false; |
There was a problem hiding this comment.
can this reuse the same logic that is and as keywords use? those also show up generally after an expression #Closed
There was a problem hiding this comment.
Excellent idea. Thanks #Closed
| } | ||
|
|
||
| // `switch` follows expressions in switch expressions | ||
| internal static bool IsAfterExpression(CSharpSyntaxContext context) |
There was a problem hiding this comment.
consider also verifying "P1" and "P2" is absent here #Closed
There was a problem hiding this comment.
consider also verifying "P2" is absent here #Resolved
There was a problem hiding this comment.
consider adding a test that has a comma inside a nested pattern, such as:
this is Program { P2: (1, $$ or this is Program { P2: { something: 1, $$ we don't want P1 here, but we do want after the closing ) or } and a comma
There was a problem hiding this comment.
consider adding a test where the properties are private but inside Program and therefore accessible #Closed
There was a problem hiding this comment.
what about setter only properties? should they be offered or not? #Closed
There was a problem hiding this comment.
The shouldn't be offered (just like we don't offer read-only properties in object initializers). Fixed.
Thanks! #Closed
There was a problem hiding this comment.
is there a test where this type (Program) is explicitly different from the switch expression type? #Closed
There was a problem hiding this comment.
Add PropertiesInRecursivePattern_WithOtherType #Closed
There was a problem hiding this comment.
consider adding a nested test where P2 itself is a field #Closed
There was a problem hiding this comment.
Added PropertiesInRecursivePattern_NestedInField #Closed
There was a problem hiding this comment.
you're not actually testing that P1 is absent, which seems like it was the purpose of this test #Closed
There was a problem hiding this comment.
is there an IsKind overload that takes two arguments? so you could get rid of the || #Closed
There was a problem hiding this comment.
nit: I would probably consider making these separate variables instead of reassigning, but up to you #ByDesign
There was a problem hiding this comment.
or just inlining this directly: var members = semanticModel.LookupSymbols(position, type).Where #Resolved
There was a problem hiding this comment.
nit: Please use 'var'. #ByDesign
There was a problem hiding this comment.
I get an error in assignment below if I use var here. Leaving as-is.
In reply to: 185140445 [](ancestors = 185140445)
There was a problem hiding this comment.
why is this separate? #Closed
There was a problem hiding this comment.
nit: should be untestedMember #Closed
There was a problem hiding this comment.
consider naming TryGetPatternType #Closed
There was a problem hiding this comment.
can this be static? #Closed
| case IPropertySymbol propertySymbol: | ||
| return (propertySymbol.Type, token.GetLocation()); | ||
| case IFieldSymbol fieldSymbol: | ||
| return (fieldSymbol.Type, token.GetLocation()); |
There was a problem hiding this comment.
is returning this location really needed here? is it not suffiient to just find the token in the calling method and just use that locatoin? isn't the location just used for things like simplification?
| context.IsStatementContext || | ||
| context.IsGlobalStatementContext; | ||
| context.IsGlobalStatementContext || | ||
| context.IsIsOrAsContext; // ie. following an expression |
There was a problem hiding this comment.
this seems super bizarre. If you really want to reuse this property, then call it IsIsOrAsOrSwitchContext #Closed
| return false; | ||
| } | ||
|
|
||
| // PROTOTYPE(recursive-patterns): Rename this method |
There was a problem hiding this comment.
possibly: FollowsCompleteExpression #Closed
| public static bool IsColonInCasePatternSwitchLabel(this SyntaxToken token) | ||
| { | ||
| return token.Kind() == SyntaxKind.ColonToken && token.Parent is CasePatternSwitchLabelSyntax; | ||
| } |
| { | ||
| AddSuppressWrappingIfOnSingleLineOperation(list, switchExpressionArm.GetFirstToken(), switchExpressionArm.GetLastToken()); | ||
| return; | ||
| } |
There was a problem hiding this comment.
do you need a similar suppressoin for the entire switch expression? #Closed
|
|
||
| if (currentToken.IsPattern()) | ||
| { | ||
| return null; |
There was a problem hiding this comment.
Thanks. I've added a suppression on switch expression and pattern case. Then I removed this special case (on similar one below, at line 49). #Closed
There was a problem hiding this comment.
Hum, after I removed this, I get a newline before the brace when typing _ = e is {.
It's not showing up in tests for some reason, only in IDE at the moment.
There was a problem hiding this comment.
@CyrusNajmabadi
Would you know a good way to test the formatting-as-you-type experience? That will help me capture and investigate behaviors.
In particular, I need to find out what generates a newline when typing x is {. Then I can see what prevents the same from happening when typing if (true) { }, and do the same.
|
|
||
| if (currentToken.IsPattern()) | ||
| { | ||
| return null; |
There was a problem hiding this comment.
why? this doesn't seem right to me. We would want to adjust newlines. We'd just all want that be overridden by a supression if it was on a single line to start with. But if this is spread over multiple lines, we would want this fixed up, right?
| { | ||
| _ = this is { | ||
| P1: 1, | ||
| P2: 2 |
There was a problem hiding this comment.
indentation seems wrong here.
There was a problem hiding this comment.
I've tried various things in IndentBlockFormattingRules, but none seems to have any effect.
@CyrusNajmabadi @heejaechang Any tips on this?
There was a problem hiding this comment.
this kind of thing (expression level), we do not force indentation. looks like it is doing right thing. it is maintaining relative indentation from "_"
if test was written
_ = this is {
P1 : 1 ...
it will maintain relative indentation between _ and P1.
same way something like below is formatted.
method(1,
2,
4);
and format, you will see relative indentation of 2 and 4 are maintained aginst method
There was a problem hiding this comment.
The latest commit I pushed finally has some effect. But it's super weird. Only the first arm of the switch expression got indented... I'll continue poking around.
| var endToken = arms.Last().GetLastToken(includeZeroWidth: true); | ||
| var span = CommonFormattingHelpers.GetSpanIncludingTrailingAndLeadingTriviaOfAdjacentTokens(startToken, endToken); | ||
|
|
||
| AddIndentBlockOperation(list, switchExpression.OpenBraceToken, startToken, endToken, IndentBlockOption.RelativeToFirstTokenOnBaseTokenLine); |
There was a problem hiding this comment.
do you want to really force this? that means users can't put it anywhere else?
There was a problem hiding this comment.
Yes, I think so. The switch expression should be formatted strictly, like a switch statement (except a bit simpler).
| // } | ||
| // ``` | ||
| // Each sub-pattern gets a new line | ||
| return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.ForceLines); |
There was a problem hiding this comment.
we usually never use force line in general formatting rule. that means, user can never put lines between propertysubpatternSyntax.
There was a problem hiding this comment.
is this syntax type only exist in your feature branch?
PropertySubpatternSyntax
There was a problem hiding this comment.
Yes, this syntax is in features/recursive-patterns (and also in features/build-demo).
There was a problem hiding this comment.
This is analogous to the colon in switch labels (see below, line 124).
I'm mistaken. The handling of switch labels below is doing PreserveLines. I'll fix.
| { | ||
| _ = this switch | ||
| { | ||
| { P1: 1 } => true, |
There was a problem hiding this comment.
I think you dont have new line rule for ","
| } | ||
|
|
||
| if (token.Parent.IsKind(SyntaxKind.PropertySubpattern) == false || | ||
| token.Parent.Parent.IsKind(SyntaxKind.PropertyPattern) == false) |
There was a problem hiding this comment.
== false and || feels weird.... why not just if token.parent.iskind() && token.parent.parent.iskind() ? token : default? #Closed
There was a problem hiding this comment.
the parent of a PropertySubpattern can also be a DeconstructionPattern:
(1, 2) { P1: 1, P2: 2 }| // PROTOTYPE(NullableReferenceTypes): | ||
| // All the cases above should be replaced by using the semantic model's GetTypeInfo (ConvertedType) once that is integrated | ||
|
|
||
| return default; |
There was a problem hiding this comment.
i feel like there's a bunch of a logic shared here and between the work i'm doing in my PR where i update the TypeInferenceService... i don't have a great suggestion on how to unify things though :-/
There was a problem hiding this comment.
The logic for TryGetPatternType can be removed once Neal's PR for GetTypeInfo gets merged.
There will still be some holes. For instance, when typing a property pattern inside a positional pattern and the positional pattern isn't completely typed yet, so the Deconstruct method may not be fully resolved yet. But I think we'll fix that from compiler side (best overload candidate should be returned in that case, for IDE to use (just as in SignatureHelp).
Beyond that, I don't expect much need for inference from IDE side in patterns.
| if (token.Parent is PropertySubpatternSyntax subpattern) | ||
| { | ||
| return new HashSet<string>(subpattern.SubPatterns.Select( | ||
| p => p.NameColon?.Name?.Identifier.ValueText).Where(s => !string.IsNullOrEmpty(s))); |
There was a problem hiding this comment.
if you have subpatterns, can you not have a NameColon or Name? Are those really optional? if so... i'm curious why....
There was a problem hiding this comment.
See error syntax, which shows no NameColon.
I don't know about no Name, but I preferred to be defensive.
| { | ||
| // is Goo { $$ | ||
| // is Goo { P1: 0, $$ | ||
| var propertyPattern = (PropertyPatternSyntax)token.Parent.Parent; |
There was a problem hiding this comment.
This can also be a DeconstructionPatternSyntax
| } | ||
|
|
||
| [Fact(Skip = "PROTOTYPE(patterns2)")] | ||
| public async Task PropertiesInRecursivePattern_WithPositionalPattern() |
There was a problem hiding this comment.
Please fix this. You need to take into account that the parent of a PropertySubpattern can also be a DeconstructionPattern, not just PropertyPattern, everywhere you expect that to be the case.
then make sure it works both with and without a type just like with a PropertyPattern:
this is C (1, 2) { P1: 1, P2: 2 };
this is (1, 2) { P1: 1, P2: 2 };Because this is a different SyntaxNode and you need to have separate cases to get the type in each one, I would expect additional tests such as
PropertiesInRecursivePattern_UseStaticTypeFromIs_DeconstructionPattern
PropertiesInRecursivePattern_UseStaticTypeFromSwitchStatement_DeconstructionPattern and
PropertiesInRecursivePattern_WithOtherType_DeconstructionPattern
There was a problem hiding this comment.
I don't plan to expand the cases handled by the completion provider in this PR. As noted, we have a design to supercede this approach, using GetTypeInfo. I'm waiting for that to be merged.
There was a problem hiding this comment.
Note: even when you do that, you will still have to update the code to get the DeconstructionPattern in order to use GetTypeInfo on it. Right now I found 2 places in the code that assume the parent can only be a PropertyPattern and bail out if it's not.
|
I'm going to close this PR in favor of:
|
Addressed:
GetTypeInfoAPI from the compiler)switchkeyword offered for switch expression.UpgradeProjectis properly offered.e is {gets a newline before braceNot yet addressed:
e switch {thenreturnproduces badly indented closing brace. Formatting the document doesn't fix the issue. So that's likely a formatting problem.