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

Emit error for pattern arguments in trait methods #53051

Merged
merged 7 commits into from
Aug 13, 2018

Conversation

varkor
Copy link
Member

@varkor varkor commented Aug 4, 2018

The error and check for this already existed, but the parser didn't try to parse trait method arguments as patterns, so the error was never emitted. This surfaces the error, so we get better errors than simple parse errors.

This improves the error message described in #53046.

r? @petrochenkov

@varkor varkor force-pushed the trait-method-pattern-arguments-error branch from 288699e to 934bca5 Compare August 4, 2018 01:22
@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Aug 4, 2018
@varkor varkor force-pushed the trait-method-pattern-arguments-error branch from 934bca5 to 023a714 Compare August 4, 2018 01:23
@rust-lang rust-lang deleted a comment from rust-highfive Aug 4, 2018
@rust-highfive
Copy link
Collaborator

The job x86_64-gnu-llvm-5.0 of your PR failed on Travis (raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.

[00:03:54] travis_fold:start:tidy
travis_time:start:tidy
tidy check
[00:03:54] tidy error: /checkout/src/libsyntax/parse/parser.rs:1776: line longer than 100 chars
[00:03:55] some tidy checks failed
[00:03:55] 
[00:03:55] 
[00:03:55] command did not execute successfully: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-tools-bin/tidy" "/checkout/src" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "--no-vendor" "--quiet"
[00:03:55] 
[00:03:55] 
[00:03:55] failed to run: /checkout/obj/build/bootstrap/debug/bootstrap test src/tools/tidy
[00:03:55] Build completed unsuccessfully in 0:00:55
[00:03:55] Build completed unsuccessfully in 0:00:55
[00:03:55] Makefile:79: recipe for target 'tidy' failed
[00:03:55] make: *** [tidy] Error 1

The command "stamp sh -x -c "$RUN_SCRIPT"" exited with 2.
travis_time:start:122cc6fc
$ date && (curl -fs --head https://google.com | grep ^Date: | sed 's/Date: //g' || true)
---
travis_time:end:029e1de0:start=1533346142846456894,finish=1533346142855326628,duration=8869734
travis_fold:end:after_failure.3
travis_fold:start:after_failure.4
travis_time:start:033c5d48
$ ln -s . checkout && for CORE in obj/cores/core.*; do EXE=$(echo $CORE | sed 's|obj/cores/core\.[0-9]*\.!checkout!\(.*\)|\1|;y|!|/|'); if [ -f "$EXE" ]; then printf travis_fold":start:crashlog\n\033[31;1m%s\033[0m\n" "$CORE"; gdb -q -c "$CORE" "$EXE" -iex 'set auto-load off' -iex 'dir src/' -iex 'set sysroot .' -ex bt -ex q; echo travis_fold":"end:crashlog; fi; done || true
travis_fold:end:after_failure.4
travis_fold:start:after_failure.5
travis_time:start:1a066b50
travis_time:start:1a066b50
$ cat ./obj/build/x86_64-unknown-linux-gnu/native/asan/build/lib/asan/clang_rt.asan-dynamic-i386.vers || true
cat: ./obj/build/x86_64-unknown-linux-gnu/native/asan/build/lib/asan/clang_rt.asan-dynamic-i386.vers: No such file or directory
travis_fold:end:after_failure.5
travis_fold:start:after_failure.6
travis_time:start:15a6aec8
$ dmesg | grep -i kill

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @TimNN. (Feature Requests)

Copy link
Contributor

@estebank estebank left a comment

Choose a reason for hiding this comment

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

Can you add a suggestion to replace the tuple with an underscore, or a name which is the underscore concatenate of the tuple idents?

Great improvements I'm just worried about interactions with bad code being edited (like fn foo(u8 x: usize).

err.cancel();
// Recover from attempting to parse the argument as a pattern. This means
// the type is alone, with no name, e.g. `fn foo(u32)`.
mem::replace(self, parser_snapshot_before_pat);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be checked without rewinding state?proactively looking if a path could be parsed? I'm not against the rewind method in exceptional cases, but it should be a tool of last resort.

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 had also wanted to avoid this, but I wasn't quite sure how to go about it. The problem I had was that parse_pat and parse_ty can succeed on the same strings. Ideally we'd parse_pat (which should be a superset of parse_ty), try to parse a : and then convert the pattern to a type if it failed, because we effectively have all the information we need at that stage. Is there any way to reinterpret a pattern as a type, or similar? (Or perhaps there's a simpler method I'm overlooking.)

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm away from a computer now, but I'd think you could inspect the successfully parsed Pat to see if it is a single typed argument.

Copy link
Member Author

Choose a reason for hiding this comment

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

That seems plausible. I'll give it a go soon.

Copy link
Contributor

Choose a reason for hiding this comment

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

Rewinding is rare, so it's not very useful to optimize for that, but doing the state saving for every single fn parameter in traits doesn't look good.
I think doing minimal lookahead and avoiding state saving for the common case ident: Type would be enough.

Copy link
Member Author

Choose a reason for hiding this comment

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

Okay, I'll try that.

@varkor varkor force-pushed the trait-method-pattern-arguments-error branch from 023a714 to d35252f Compare August 4, 2018 10:16
pat,
id: ast::DUMMY_NODE_ID,
})
let is_named_argument = self.is_named_argument();
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you move this line into the Err(mut err) clause where it's needed?
(In the Ok(..) case it return nonsensical result anyway.)

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, not sure why I left it out there...

let mut err = struct_span_err!(self.session, span, E0642,
"patterns aren't allowed in methods without bodies");
err.span_suggestion(span,
"use an underscore to ignore the name", "_".to_owned());
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the point of this recommendation?
If pattern doesn't work, then it's much more likely that the user will want some name describing the pattern as a whole, rather than no name at all.

Copy link
Member Author

Choose a reason for hiding this comment

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

It was a response to #53051 (review). This suggestion only appears somewhere where the name is irrelevant and can't be used anywhere else anyway, so a name is unnecessary. I considered converting the pattern somehow, but I couldn't find an existing method to do something similar, and it seemed like more effort than was worthwhile.

Copy link
Contributor

Choose a reason for hiding this comment

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

Synthesizing a meaningful name from code like fn foo((x, y): (i32, i32)); can be impossible to get right (should it suggest x_y? x_and_y? something else?), which is why I suggested using _, but the wording could be changed slightly to give this argument a name or use an underscore to ignore it, instead of a tuple pattern.

@petrochenkov petrochenkov added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 5, 2018
@petrochenkov
Copy link
Contributor

Wait a second, this PR has language implications as well.

Arbitrary patterns are now accepted in trait methods with body, a feature that we can't provide without backtracking in parser.

trait Tr {
    fn f((a, b): (u8, u8)) {} // OK
}

This PR needs to enforce the restrictions that previously existed on all trait methods, with or without body.

@varkor
Copy link
Member Author

varkor commented Aug 5, 2018

Arbitrary patterns are now accepted in trait methods with body

Oops, is that the case? I didn't realise it would have that implication. There should probably be a test for that. I'll fix that and add a test.

Copy link
Contributor

@estebank estebank left a comment

Choose a reason for hiding this comment

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

Couple of smaller nitpicks, otherwise lgtm. Leaving @petrochenkov for final r.

// If we see `ident :`, then we know that the argument is just of the
// form `type`, which means we won't need to recover from parsing a
// pattern and so we don't need to store a parser snapshot.
let parser_snapshot_before_pat = if
Copy link
Contributor

Choose a reason for hiding this comment

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

Creative :)

// in methods without bodies
}
```
"##,
Copy link
Contributor

Choose a reason for hiding this comment

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

Last nitpick, could you expand the documentation to show the "fixed" code, using maybe x_and_y as argument name?

|
LL | fn bar((x, y): (i32, i32)) {} //~ ERROR patterns aren't allowed in trait methods
| ^^^^^^
help: give this argument a name or use an underscore to ignore it, instead of a tuple pattern
Copy link
Contributor

Choose a reason for hiding this comment

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

reword to "give this argument a name or use an underscore to ignore it instead of using a tuple pattern"

});
}
} else {
let mut err = struct_span_err!(self.session, span, E0642,
Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, now this is a breaking change 😄
Some limited set of pattern (defined by fn is_named_argument) was previously accepted by parser in methods with body.
Something like

trait Tr {
    fn f(&ident: Type) {} // OK
    fn g(&&ident: Type) {} // OK
    fn h(mut ident: Type) {} // OK
    fn k($pat: Type) {} // OK
}

Note, that this compatibility check needs to happen in parser due to $pat that can look like an arbitrary complex pattern in AST validation, but still needs to be accepted.

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh no! I thought I accounted for that with the branches, but it turns out not! (Why did it even pass? Do we not have any tests for that?)

So will I need to change parser.rs to fix this, or can I just move the check for the body?

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think AST validation needs any changes (it already does things right), all the relevant stuff is in the parser.
It would probably be better to:

  • Check for self.is_named_argument() and parse in the old way if it's true.
  • Otherwise, parse PAT: TYPE with state saving and immediately report a non-fatal error in case of success.
  • Otherwise, backtrack and parse TYPE.

Copy link
Contributor

Choose a reason for hiding this comment

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

If AST validation reports an unwanted duplicate error, then patterns could be converted into _ in the parser.

"patterns aren't allowed in trait methods");
let suggestion = "give this argument a name or use an \
underscore to ignore it instead of using a \
tuple pattern";
Copy link
Contributor

Choose a reason for hiding this comment

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

The mention of tuple pattern is overly specific, I'd remove the "instead" part, or used "instead of using a pattern"

@varkor varkor force-pushed the trait-method-pattern-arguments-error branch from 3d6eba1 to 96b34c1 Compare August 9, 2018 22:23
@varkor
Copy link
Member Author

varkor commented Aug 9, 2018

Okay, the errors are now emitted during parsing instead, as per @petrochenkov's suggestion in #53051 (comment).

@varkor varkor force-pushed the trait-method-pattern-arguments-error branch from 96b34c1 to afe415d Compare August 9, 2018 22:58
// pattern and so we don't need to store a parser snapshot.
let parser_snapshot_before_pat = if
self.look_ahead(1, |t| t.is_ident()) &&
self.look_ahead(2, |t| t == &token::Colon) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This would still store a copy of the parser for fn foo(&mut x) {} and fn bar(&self), 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.

Yes.

Copy link
Contributor

Choose a reason for hiding this comment

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

self is methods is treated separately outside of parse_arg_general, so it's not affected.

@rust-highfive

This comment has been minimized.

@@ -342,10 +342,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.session.buffer_lint(
lint::builtin::PATTERNS_IN_FNS_WITHOUT_BODY,
trait_item.id, span,
"patterns aren't allowed in methods without bodies");
"patterns aren't allowed in trait methods");
Copy link
Contributor

Choose a reason for hiding this comment

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

Patterns are allowed in trait methods with bodies, the old message was correct.

@petrochenkov
Copy link
Contributor

Could you add examples from #53051 (comment) as a test for catching future regressions?

});
(pat, ty)

// If we see `ident :`, then we know that the argument is not just of the
Copy link
Contributor

Choose a reason for hiding this comment

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

ident : no longer needs to be treated specially because it falls under self.is_named_argument() above.

Copy link
Contributor

Choose a reason for hiding this comment

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

In other words, we are already in cold code here and can make the snapshot unconditionally.

@petrochenkov
Copy link
Contributor

LGTM, beside the few remaining comments above.

The error and check for this already existed, but the parser didn't try to parse trait method arguments as patterns, so the error was never emitted. This surfaces the error, so we get better errors than simple parse errors.
@varkor varkor force-pushed the trait-method-pattern-arguments-error branch from bb12fb0 to 5c814e2 Compare August 11, 2018 20:26
@petrochenkov
Copy link
Contributor

@bors r+

@bors
Copy link
Contributor

bors commented Aug 12, 2018

📌 Commit 5c814e2 has been approved by petrochenkov

@bors
Copy link
Contributor

bors commented Aug 12, 2018

🌲 The tree is currently closed for pull requests below priority 10, this pull request will be tested once the tree is reopened

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Aug 12, 2018
kennytm added a commit to kennytm/rust that referenced this pull request Aug 12, 2018
…ts-error, r=petrochenkov

Emit error for pattern arguments in trait methods

The error and check for this already existed, but the parser didn't try to parse trait method arguments as patterns, so the error was never emitted. This surfaces the error, so we get better errors than simple parse errors.

This improves the error message described in rust-lang#53046.
@bors
Copy link
Contributor

bors commented Aug 13, 2018

⌛ Testing commit 5c814e2 with merge ab93561...

bors added a commit that referenced this pull request Aug 13, 2018
…=petrochenkov

Emit error for pattern arguments in trait methods

The error and check for this already existed, but the parser didn't try to parse trait method arguments as patterns, so the error was never emitted. This surfaces the error, so we get better errors than simple parse errors.

This improves the error message described in #53046.

r? @petrochenkov
@bors
Copy link
Contributor

bors commented Aug 13, 2018

☀️ Test successful - status-appveyor, status-travis
Approved by: petrochenkov
Pushing ab93561 to master...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants