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
Refactoring feature to suggest to use recursive patterns #32386
Conversation
return b is | ||
{ | ||
P1: 1, P2: 2 | ||
}; |
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.
we should probably try to preserve 'single line'-ness the 'after' doesn't seem better here due to it now adding 3 lines.
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 FYI, but i do think this is critical.
{ | ||
Name: string.Empty, ContainingNamespace: 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.
the formatting here seems wonky. where it doesn't seem to match up with original like i woudl expect.
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.
here's my thinking. if the original was single-line, we should attempt to preserve single-line. If the original is multi-line, we can be multiline too. When multi-line, we should keep properties on a single line, if their patterns are single line. But if they are multi-line, then they should go across multiple lines as well. So this should be:
return b is
{
MetadataName: string.Empty,
ContainingType: null,
ContainingNamespace:
{
Name: string.Empty, ContainingNamespace: null
}
};
Name: ""System"", Kind: SymbolKind.Namespace | ||
} | ||
|
||
declContainer |
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.
what is this random name?
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 the var declContainer
pattern in "before" which has become the pattern designation.
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 reads super weirdly. especially with the multiple newlines, and no clear indication about which part of hte pattern this corresponds to.
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.
how would you expect this to be formatted (a multi-line property pattern + designation)?
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.
another possibility: type + multi-line property pattern + designation
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. it points to the exact same object from the source.
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 having both a newline and a blank line here is bad. it looks likke declContainer is completed separated from teh pattern. We really shoudl not do this. It should at least be:
return type is
{
Name: ""ValueTuple"",
ContainingSymbol: { Name: ""System"", Kind: SymbolKind.Namespace } declContainer
}
However, since declContainre isn't used, we should remove it as well.
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.
Could we modify formatting engine to NOT insert new lines in certain places like,
- an empty property pattern clause
{}
does not need to break in multiple lines - we should not insert a new line before a variable designation ever
Or more rules? lookin at tests, only these two would result in a way better source text.
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.
Also, I'm not sure if we should deal with indentation when breaking lists into multiple lines.
I think unless we want to explicitly perverse or move around trivia, we don't need to do anything on formatting in this feature. These all should go to formatting rules, 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.
- if you are not preserving trivia and you are using ElasticTrivia, then yes, you just need to update formatting rules to do something sensible there.
- if you are preserving trivia, then it's likely you'll need to write some custom logic so that hte preserved trivia still looks good.
I'm fine with any approach you take that ends up with a good final result for the user :) '1' would be a good place to start from to see how far that gets us.
bool M() | ||
{ | ||
return type.Name == ""ValueTuple"" && | ||
type.ContainingSymbol is var declContainer && |
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.
semantics seem like they may have changed slightly (which is probably ok). here, the is var ...
will always succeed, and you can null on the checks ref below. but in the converted form, it can't null ref 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.
we preserve the variable, in case it's used anywhere else (see previous comment), but semantics haven't changed because the var pattern is &&'ed with others in the source.
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.
- "we preserve the variable, in case it's used anywhere else (see previous comment)," we should probably remove the variable if not used.
- i'm not quite getting your explanation. previous code could have a null ref exception right? (i.e. on declContainer.Kind). But hte new code won't because it won't do the inner checks if ContainingSymbol is null. Isn't that how it works?
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.
regarding null checks, yes it would be added by the compiler. we might also reorder expressions but property patterns are assumed to be idempotent by the compiler to allow reordering in the generated code. this is kinda a reversed use of that assumption. there will be semantic changes if it doesn't hold.
IsGlobalNamespace: true | ||
} | ||
|
||
, Name: ""System"", Kind: SymbolKind.Namespace |
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 formatting seems off.
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 the default formatting from SyntaxFactory, is this in the stable form or it can change?
either way we probably need to do a formatting pass for the feature.
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.
To be pednatic. SyntaxFactory does not formatting. It just adds elastic trivia to things. So this is how the formatting engine is handling elastic formatting here currently.
- we probably need to make hte formatter better here.
- even if the formatter is better, this feature will likely need specialized trivia handling to try to do a good job for this sort of conversion.
, Name: ""System"", Kind: SymbolKind.Namespace | ||
} | ||
|
||
declContainer |
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.
?
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 the pattern designation which comes from the var pattern. see comment above.
...table/UseRecursivePatterns/CSharpUseRecursivePatternsCodeRefactoringProvider.AnalyzedNode.cs
Outdated
Show resolved
Hide resolved
...table/UseRecursivePatterns/CSharpUseRecursivePatternsCodeRefactoringProvider.AnalyzedNode.cs
Outdated
Show resolved
Hide resolved
public override bool VisitPatternMatch(PatternMatch node) => SyntaxFactory.AreEquivalent(_expression, node.Expression); | ||
public override bool VisitSourcePattern(SourcePattern node) => false; | ||
public override bool VisitTypePattern(TypePattern node) => false; | ||
public override bool VisitVarPattern(VarPattern node) => 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.
these all need explanations. for example, why _expression could not be found in a constant pattern
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.
because the expression we're trying to find would not possibly appear in a constant pattern. will add a comment.
else | ||
{ | ||
return new Conjuction(this.Left, rightSub); | ||
} |
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.
needs explanation (probably with examples).
Thanks! |
...table/UseRecursivePatterns/CSharpUseRecursivePatternsCodeRefactoringProvider.AnalyzedNode.cs
Outdated
Show resolved
Hide resolved
Correct. Code should strive for simplicity, maintainability and clarity. When necessary, for performance reasons, we can take extra steps to trim things down. Otherwise, it's putting in unnecessary effort for gains that will not benefit people.
Define 'lot of allocations'. The general idea is this: If your system is producing a veritable torrential downpour of allocations, then avoiding a handful of allocs isn't really buying anything.
That doesn't seem to be the case. You're adding an entirely new type, and having to have this code use it, with the understanding of how this argument is processed. There is conceptual complexity here that seems like overkill given how little actual impact hte existing code would have.
The main reason this is brought up is because this is essentially a form of premature optimization. And expending effort there is just not worthwhile when it won't actually improve things and when many other areas actually would benefit from a focus on performance. There are limited resources to make code cahnges (and that includes the resources to do PR reviews). So tying up those resources should be done in a manner that produces the most bang/buck at the end of the day. |
Turns out we don't need a Visitor after all, because we already have access to the node declaration itself, we can do all phases (reducer and rewriter) using just methods on the base. I intend to remove the Visitor class and move everything to AnalyzedNode hierarchy. Please let me know if you think it's better to keep it. |
I'd have to see. But my gut tells me it would be nicer to not have it |
The only concern is that every kind of transformations (namely, reduction and rewrite) are mixed in a single class. Perhaps it would help to separate out files for each node, but Visitor is def redundant here. |
This is the rough sketch of the change: alrz@1b7df05 |
...table/UseRecursivePatterns/CSharpUseRecursivePatternsCodeRefactoringProvider.AnalyzedNode.cs
Outdated
Show resolved
Hide resolved
Probably not. In general, we're ok with individual options handling a few cases, as long as they all seem to reasonably fit the same 'theme'. |
1fa5819
to
f3af169
Compare
ContainingSymbol: NamespaceSymbol { IsGlobalNamespace: true }, | ||
Name: ""System"", | ||
Kind: 0 | ||
} declContainer |
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.
Could we use a reducer to remove unused variables via Simplifier annotation?
MetadataImportOptions: otherMetadataImportOptions, | ||
ReferencesSupersedeLowerVersions: otherReferencesSupersedeLowerVersions | ||
} && | ||
this.OutputKind.IsNetModule() == other.OutputKind.IsNetModule(); |
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 expect the formatter indent this..
@sharwell This is ready for review. Note: there's some formatting changes to get a reasonable output from the refactoring. |
@alrz do you still want to move forward with this? |
@CyrusNajmabadi I think this is only blocked on #40361 and a few formatting changes in this PR should be also unnecessary. |
Closing this for now, maybe I'll come back to it later. this seems like a rather complicated feature for little benefit to end user, at least at its current state. |
@alrz If we happen to get some free time, do you mind if @CyrusNajmabadi and/or myself reopen this pull request and finish the work by adding commits to the top of your branch? |
Of course, if you're happy with the feature feel free to reopen. let me know if there's anything I can do. Thanks. |
I plan to split this into a few more focused refactorings/analyzers. Keeping this open for reference. |
I think we should change this to operate on IOperation nodes instead of sytnax, for the following reasons: We need a simple and fast equality function. We use AreEqivalent too many times in the current impl, with IOperation it could be trivially done via a reference comparison. Plus, some edge cases demand for a more reliable equality check, In the current impl we have to separately handle different symbols with identical names. We are snipping out lots of code and keeping the syntax around wouldn't help much. And it could be done via a separate syntax pass if needed, like we did with a few other refactorings. And finally, there's a chance we can share the logic with those features as well, all of which use IOperation nodes. |
Since we aggressively make patterns out of any expression, I think we need a way for user to choose subparts of an expression to be replaced. I want to consider the selection span for that purpose, if any. Otherwise we apply it to the whole expression. |
No description provided.