Skip to content
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

Closed
wants to merge 34 commits into from

Conversation

alrz
Copy link
Member

@alrz alrz commented Jan 11, 2019

No description provided.

@alrz alrz requested a review from a team as a code owner January 11, 2019 05:32
@alrz
Copy link
Member Author

alrz commented Jan 11, 2019

/cc @CyrusNajmabadi @jcouv

return b is
{
P1: 1, P2: 2
};
Copy link
Member

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.

Copy link
Member

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
}
};
Copy link
Member

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.

Copy link
Member

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
Copy link
Member

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?

Copy link
Member Author

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.

Copy link
Member

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.

Copy link
Member Author

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)?

Copy link
Member Author

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

Copy link
Member Author

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.

Copy link
Member

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.

Copy link
Member Author

@alrz alrz Jan 29, 2019

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.

Copy link
Member Author

@alrz alrz Jan 29, 2019

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?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. 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.
  2. 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 &&
Copy link
Member

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?

Copy link
Member Author

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. "we preserve the variable, in case it's used anywhere else (see previous comment)," we should probably remove the variable if not used.
  2. 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?

Copy link
Member Author

@alrz alrz Jan 11, 2019

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this formatting seems off.

Copy link
Member Author

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.

Copy link
Member

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.

  1. we probably need to make hte formatter better here.
  2. 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Member Author

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.

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;
Copy link
Member

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

Copy link
Member Author

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);
}
Copy link
Member

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).

@CyrusNajmabadi
Copy link
Member

  1. It's really cool you're doing this! I'm sure it will be taken as a refactoring.
  2. The approach is too different for me to wrap my weak and puny brain around. Can you do a writeup (ideally with comments in code) walking through both the high level ideas of patterns/analyzed-nodes/reducers/etc. Then have some examples in appropriate places showing what's going on?

Thanks!

@CyrusNajmabadi
Copy link
Member

I must say I have never understood the bar for performance improvements, the fact that it doesn't run that much (but not too rarely either) is enough reason to not care?

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.

but it would eliminate a lot of allocations in the the local run.

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.

Considering that this addition would add zero complexity

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.

(read this as just a general question, not an argument)

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.

@jinujoseph jinujoseph added Area-IDE PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it. Community The pull request was submitted by a contributor who is not a Microsoft employee. labels Jan 15, 2019
@jinujoseph jinujoseph added this to InQueue in IDE: CommunityPR via automation Jan 15, 2019
@alrz alrz changed the title Refactoring feature for using recursive patterns Refactoring feature to suggest to use recursive patterns Jan 18, 2019
@alrz
Copy link
Member Author

alrz commented Jan 21, 2019

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.

@CyrusNajmabadi
Copy link
Member

I'd have to see. But my gut tells me it would be nicer to not have it

@alrz
Copy link
Member Author

alrz commented Jan 21, 2019

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.

@alrz
Copy link
Member Author

alrz commented Jan 21, 2019

This is the rough sketch of the change: alrz@1b7df05

@CyrusNajmabadi
Copy link
Member

is that important enough to give a different option based on what we're doing?

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'.

@alrz alrz requested review from a team as code owners January 25, 2019 15:29
@alrz alrz changed the base branch from features/recursive-patterns to master January 25, 2019 15:29
ContainingSymbol: NamespaceSymbol { IsGlobalNamespace: true },
Name: ""System"",
Kind: 0
} declContainer
Copy link
Member Author

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();
Copy link
Member Author

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..

@alrz
Copy link
Member Author

alrz commented Nov 16, 2019

@sharwell This is ready for review.

Note: there's some formatting changes to get a reasonable output from the refactoring.

@CyrusNajmabadi
Copy link
Member

@alrz do you still want to move forward with this?

@alrz
Copy link
Member Author

alrz commented Mar 8, 2020

@CyrusNajmabadi I think this is only blocked on #40361 and a few formatting changes in this PR should be also unnecessary.

@alrz
Copy link
Member Author

alrz commented Mar 11, 2020

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 alrz closed this Mar 11, 2020
IDE: CommunityPR automation moved this from BuddyAssigned to Completed Mar 11, 2020
@sharwell
Copy link
Member

@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?

@alrz
Copy link
Member Author

alrz commented Mar 16, 2020

Of course, if you're happy with the feature feel free to reopen. let me know if there's anything I can do. Thanks.

@sharwell sharwell reopened this Mar 16, 2020
@alrz
Copy link
Member Author

alrz commented Apr 20, 2020

I plan to split this into a few more focused refactorings/analyzers. Keeping this open for reference.

@alrz alrz marked this pull request as draft April 21, 2020 08:13
@alrz
Copy link
Member Author

alrz commented May 2, 2020

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.

@alrz
Copy link
Member Author

alrz commented May 2, 2020

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.

Base automatically changed from master to main March 3, 2021 23:51
@jinujoseph jinujoseph moved this from Completed-166 to Completed 2020 in IDE: CommunityPR Apr 2, 2021
@alrz alrz closed this May 20, 2021
IDE: CommunityPR automation moved this from Completed 2020 to May-2021 May 20, 2021
@jinujoseph jinujoseph moved this from May-2021 to Completed 2021 in IDE: CommunityPR Jan 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-IDE Community The pull request was submitted by a contributor who is not a Microsoft employee. Investigating
Projects
IDE: CommunityPR
  
Completed 2021
Development

Successfully merging this pull request may close these issues.

None yet

5 participants