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
Moving class with syntax errors to another file #23723
Comments
So: private class Nested()
{
throw new System.NotImplementedException();
} Is one of those "very broken" syntactic cases. Like "the parser goes off the rails" syntactic cases :) We could attempt to make the parser a it better here (For example, seeing () and tryign to skip it better). But that would only be a spot fix. If you had something like "private class Nested(int i)" then it would be equally bad. Similarly, we could try to replace the "class" with "void" and treat this like a method. But that would be hard with lookahead (given that generics might be involved). Now, once you have a really busted syntax tree, all bets are off with how features are going to behave. In a case like thsi, i'd probably best prefer that move-type not even be offered as it will screw things up majorly. The problem is trying to figure out how to suppress this feature here. We don't want to suppress move-type just because a tiny, non-problematic syntax error (like a missing semicolon deep in some code in the class). but we do want to suppress when the type is totally broken. The issue is knowing which is which. Perhaps a good check would be "does the class have missing { and } tokens". If so, then don't offer the fix. |
PRs welcome. I could walk someone through how to do this. |
Note: because we may eventually do primary constructors, we could consider having the parser try to parse out a parameter list after it's type arguent list. That parameter list could then be attached as a skipped node to the name or ParseMethodDeclaration has the following in it: else if (this.CurrentToken.Kind == SyntaxKind.ColonToken)
{
// Use else if, rather than if, because if we see both a constructor initializer and a constraint clause, we're too lost to recover.
var colonToken = this.CurrentToken;
ConstructorInitializerSyntax initializer = this.ParseConstructorInitializer();
initializer = this.AddErrorToFirstToken(initializer, ErrorCode.ERR_UnexpectedToken, colonToken.Text);
paramList = AddTrailingSkippedSyntax(paramList, initializer);
// CONSIDER: Parsing an invalid constructor initializer could, conceivably, get us way
// off track. If this becomes a problem, an alternative approach would be to generalize
// EatTokenWithPrejudice in such a way that we can just skip everything until we recognize
// our context again (perhaps an open brace).
} This is to deal with someone accidentally writing: public void MyClass(int i, int j) : this(i) { } Here the person meant to write a constructor, but had a return type accidenty there. We don't want the ": this(i)" to throw everything off so we look for it, parse it, and just attach it as bogus syntax in the tree. I think doing hte same for a parameter list on a class/struct declaration would be feasible and would help here. |
Yes, I found this worth mentioning because I know there was a working prototype where parameters after type were valid syntax. Not sure how interesting is to fix this particular case, but it's a bit unfortunate that the parser gets that off when there even isn't anything unbalanced. |
Error recovery is exceptionally difficult. For all the parser knows, these parens could have been anything. Maybe they're the start of a lambda. Maybe they're a parameter list to a method. Maybe it's a tuple. When it seems extremely strange stuff in a very unexpected place in the tree, it's really hard to tell what to do :) this is truly a 'panic mode' scenario where recovery (especially without keywords) is super hard. it's always possible to put in one-off fixes. But the general problem is actually 'unsolved' as far as the theory and literature are concerned. There are further techniques and approaches we can take (i did a lot of research in this space over the last couple of decades). However:
The general problem of "people take chunks of code that are legal in one context (liek expressions/statements) and place them into random other locations (like after a declaration name) don't tend to appear that often. and, if they do, they're fairly ephemeral. I.e. the user will edit things to fix it up. It's usually very unlikely that someone both does this, and then performs high level refactorings. So investing in this space isn't very bang/bucky. |
Version Used: 15.6.0 Preview 1
Steps to Reproduce:
Invoke Move type to Program.Nested.cs on the
class Nested()
line.Actual Behavior:
Program.cs:
Program.Nested.cs:
Expected Behavior:
I understand this is syntactically broken scenario, but I think the result is perhaps unnecessarily bad. Taking the whole
and putting it into a new file was the expected though I agree naive behavior in this case. Having only
in the nested file would be also fine, but the extra
}
and random#endregion
seems to be coming out of nowhere - done this in a larger class, should I be worrying it got moved from somewhere it shouldn't have?Context:
I wanted to move a method to a new file the same way I can move nested classes, so I just changed
void
toclass
to get the refactoring. Maybe the Move to file could work on members too?The text was updated successfully, but these errors were encountered: