-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Account for missing keyword in fn/struct definition #45997
Changes from all commits
1737d69
547873a
7c0387e
c82e9e8
df357b2
4e2d1b9
0e93b75
f103342
0e241d0
cf9283e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1156,6 +1156,7 @@ impl<'a> Parser<'a> { | |
None => token::CloseDelim(self.token_cursor.frame.delim), | ||
}) | ||
} | ||
|
||
fn look_ahead_span(&self, dist: usize) -> Span { | ||
if dist == 0 { | ||
return self.span | ||
|
@@ -4268,7 +4269,16 @@ impl<'a> Parser<'a> { | |
let mut stmts = vec![]; | ||
|
||
while !self.eat(&token::CloseDelim(token::Brace)) { | ||
if let Some(stmt) = self.parse_full_stmt(false)? { | ||
let stmt = match self.parse_full_stmt(false) { | ||
Err(mut err) => { | ||
err.emit(); | ||
self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Break); | ||
self.eat(&token::CloseDelim(token::Brace)); | ||
break; | ||
} | ||
Ok(stmt) => stmt, | ||
}; | ||
if let Some(stmt) = stmt { | ||
stmts.push(stmt); | ||
} else if self.token == token::Eof { | ||
break; | ||
|
@@ -4277,7 +4287,6 @@ impl<'a> Parser<'a> { | |
continue; | ||
}; | ||
} | ||
|
||
Ok(P(ast::Block { | ||
stmts, | ||
id: ast::DUMMY_NODE_ID, | ||
|
@@ -5325,18 +5334,45 @@ impl<'a> Parser<'a> { | |
Ok((class_name, ItemKind::Union(vdata, generics), None)) | ||
} | ||
|
||
fn consume_block(&mut self, delim: token::DelimToken) { | ||
let mut brace_depth = 0; | ||
if !self.eat(&token::OpenDelim(delim)) { | ||
return; | ||
} | ||
loop { | ||
if self.eat(&token::OpenDelim(delim)) { | ||
brace_depth += 1; | ||
} else if self.eat(&token::CloseDelim(delim)) { | ||
if brace_depth == 0 { | ||
return; | ||
} else { | ||
brace_depth -= 1; | ||
continue; | ||
} | ||
} else if self.eat(&token::Eof) || self.eat(&token::CloseDelim(token::NoDelim)) { | ||
return; | ||
} else { | ||
self.bump(); | ||
} | ||
} | ||
} | ||
|
||
pub fn parse_record_struct_body(&mut self) -> PResult<'a, Vec<StructField>> { | ||
let mut fields = Vec::new(); | ||
if self.eat(&token::OpenDelim(token::Brace)) { | ||
while self.token != token::CloseDelim(token::Brace) { | ||
fields.push(self.parse_struct_decl_field().map_err(|e| { | ||
let field = self.parse_struct_decl_field().map_err(|e| { | ||
self.recover_stmt(); | ||
self.eat(&token::CloseDelim(token::Brace)); | ||
e | ||
})?); | ||
}); | ||
match field { | ||
Ok(field) => fields.push(field), | ||
Err(mut err) => { | ||
err.emit(); | ||
} | ||
} | ||
} | ||
|
||
self.bump(); | ||
self.eat(&token::CloseDelim(token::Brace)); | ||
} else { | ||
let token_str = self.this_token_to_string(); | ||
return Err(self.fatal(&format!("expected `where`, or `{{` after struct \ | ||
|
@@ -5384,8 +5420,15 @@ impl<'a> Parser<'a> { | |
self.bump(); | ||
} | ||
token::CloseDelim(token::Brace) => {} | ||
token::DocComment(_) => return Err(self.span_fatal_err(self.span, | ||
Error::UselessDocComment)), | ||
token::DocComment(_) => { | ||
let mut err = self.span_fatal_err(self.span, Error::UselessDocComment); | ||
self.bump(); // consume the doc comment | ||
if self.eat(&token::Comma) || self.token == token::CloseDelim(token::Brace) { | ||
err.emit(); | ||
} else { | ||
return Err(err); | ||
} | ||
} | ||
_ => return Err(self.span_fatal_help(self.span, | ||
&format!("expected `,`, or `}}`, found `{}`", self.this_token_to_string()), | ||
"struct fields should be separated by commas")), | ||
|
@@ -6236,7 +6279,65 @@ impl<'a> Parser<'a> { | |
return Ok(Some(macro_def)); | ||
} | ||
|
||
self.parse_macro_use_or_failure(attrs,macros_allowed,attributes_allowed,lo,visibility) | ||
// Verify wether we have encountered a struct or method definition where the user forgot to | ||
// add the `struct` or `fn` keyword after writing `pub`: `pub S {}` | ||
if visibility == Visibility::Public && | ||
self.check_ident() && | ||
self.look_ahead(1, |t| *t != token::Not) | ||
{ | ||
// Space between `pub` keyword and the identifier | ||
// | ||
// pub S {} | ||
// ^^^ `sp` points here | ||
let sp = self.prev_span.between(self.span); | ||
let full_sp = self.prev_span.to(self.span); | ||
let ident_sp = self.span; | ||
if self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace)) { | ||
// possible public struct definition where `struct` was forgotten | ||
let ident = self.parse_ident().unwrap(); | ||
let msg = format!("add `struct` here to parse `{}` as a public struct", | ||
ident); | ||
let mut err = self.diagnostic() | ||
.struct_span_err(sp, "missing `struct` for struct definition"); | ||
err.span_suggestion_short(sp, &msg, " struct ".into()); | ||
return Err(err); | ||
} else if self.look_ahead(1, |t| *t == token::OpenDelim(token::Paren)) { | ||
let ident = self.parse_ident().unwrap(); | ||
self.consume_block(token::Paren); | ||
let (kw, kw_name, ambiguous) = if self.check(&token::RArrow) || | ||
self.check(&token::OpenDelim(token::Brace)) | ||
{ | ||
("fn", "method", false) | ||
} else if self.check(&token::Colon) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @estebank sorry for the enormous necropost, but do you know what the idea behind this was? It matches |
||
let kw = "struct"; | ||
(kw, kw, false) | ||
} else { | ||
("fn` or `struct", "method or struct", true) | ||
}; | ||
|
||
let msg = format!("missing `{}` for {} definition", kw, kw_name); | ||
let mut err = self.diagnostic().struct_span_err(sp, &msg); | ||
if !ambiguous { | ||
let suggestion = format!("add `{}` here to parse `{}` as a public {}", | ||
kw, | ||
ident, | ||
kw_name); | ||
err.span_suggestion_short(sp, &suggestion, format!(" {} ", kw)); | ||
} else { | ||
if let Ok(snippet) = self.sess.codemap().span_to_snippet(ident_sp) { | ||
err.span_suggestion( | ||
full_sp, | ||
"if you meant to call a macro, write instead", | ||
format!("{}!", snippet)); | ||
} else { | ||
err.help("if you meant to call a macro, remove the `pub` \ | ||
and add a trailing `!` after the identifier"); | ||
} | ||
} | ||
return Err(err); | ||
} | ||
} | ||
self.parse_macro_use_or_failure(attrs, macros_allowed, attributes_allowed, lo, visibility) | ||
} | ||
|
||
/// Parse a foreign item. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,5 +17,5 @@ struct X { | |
} | ||
|
||
fn main() { | ||
let y = X {a = 1}; | ||
let y = X {a: 1}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,5 +16,5 @@ struct X { | |
} | ||
|
||
fn main() { | ||
let y = X {a = 1}; | ||
let y = X {a: 1}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,4 +16,4 @@ struct Foo { | |
pub(crate) () foo: usize, | ||
} | ||
|
||
|
||
fn main() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
pub foo(s: usize) { bar() } | ||
//~^ ERROR missing `fn` for method definition | ||
|
||
fn main() { | ||
foo(2); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
error: missing `fn` for method definition | ||
--> $DIR/pub-ident-fn-2.rs:11:4 | ||
| | ||
11 | pub foo(s: usize) { bar() } | ||
| ^ | ||
help: add `fn` here to parse `foo` as a public method | ||
| | ||
11 | pub fn foo(s: usize) { bar() } | ||
| ^^ | ||
|
||
error: aborting due to previous error | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
pub S(); | ||
//~^ ERROR missing `fn` or `struct` for method or struct definition | ||
|
||
fn main() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
error: missing `fn` or `struct` for method or struct definition | ||
--> $DIR/pub-ident-fn-or-struct-2.rs:11:4 | ||
| | ||
11 | pub S(); | ||
| ---^- help: if you meant to call a macro, write instead: `S!` | ||
|
||
error: aborting due to previous error | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
pub S (foo) bar | ||
//~^ ERROR missing `fn` or `struct` for method or struct definition | ||
|
||
fn main() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
error: missing `fn` or `struct` for method or struct definition | ||
--> $DIR/pub-ident-fn-or-struct.rs:11:4 | ||
| | ||
11 | pub S (foo) bar | ||
| ---^- help: if you meant to call a macro, write instead: `S!` | ||
|
||
error: aborting due to previous error | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
pub foo(s: usize) -> bool { true } | ||
//~^ ERROR missing `fn` for method definition | ||
|
||
fn main() { | ||
foo(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.
Isn't it possible these days to have paths to macros? e.g.,
foo::bar!
?cc @jseyfried @petrochenkov
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 way the fall back happens, if the code isn't either
pub foo (
orpub foo {
, the current macro handling will happen. In the case ofpub foo::bar
this new branch will not be executed. As long asparse_macro_use_or_failure
does the right thing with paths, this should be fine :)