diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index 172e27d56d485..4b3e53d931f13 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -564,6 +564,15 @@ impl Handler { self.bump_err_count(); self.panic_if_treat_err_as_bug(); } + pub fn mut_span_err<'a, S: Into>(&'a self, + sp: S, + msg: &str) + -> DiagnosticBuilder<'a> { + let mut result = DiagnosticBuilder::new(self, Level::Error, msg); + result.set_span(sp); + self.bump_err_count(); + result + } pub fn span_err_with_code>(&self, sp: S, msg: &str, code: &str) { self.emit_with_code(&sp.into(), msg, code, Error); self.bump_err_count(); diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 126e8816d0559..9443df6321bd0 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -520,12 +520,21 @@ impl<'a> Parser<'a> { self.bug("ident interpolation not converted to real token"); } _ => { - let mut err = self.fatal(&format!("expected identifier, found `{}`", - self.this_token_to_string())); - if self.token == token::Underscore { - err.note("`_` is a wildcard pattern, not an identifier"); - } - Err(err) + let last_token = self.last_token.clone().map(|t| *t); + Err(match last_token { + Some(token::DocComment(_)) => self.span_fatal_help(self.last_span, + "found a documentation comment that doesn't document anything", + "doc comments must come before what they document, maybe a comment was \ + intended with `//`?"), + _ => { + let mut err = self.fatal(&format!("expected identifier, found `{}`", + self.this_token_to_string())); + if self.token == token::Underscore { + err.note("`_` is a wildcard pattern, not an identifier"); + } + err + } + }) } } } @@ -927,6 +936,7 @@ impl<'a> Parser<'a> { // Stash token for error recovery (sometimes; clone is not necessarily cheap). self.last_token = if self.token.is_ident() || self.token.is_path() || + self.token.is_doc_comment() || self.token == token::Comma { Some(Box::new(self.token.clone())) } else { @@ -1018,6 +1028,11 @@ impl<'a> Parser<'a> { pub fn span_err(&self, sp: Span, m: &str) { self.sess.span_diagnostic.span_err(sp, m) } + pub fn span_err_help(&self, sp: Span, m: &str, h: &str) { + let mut err = self.sess.span_diagnostic.mut_span_err(sp, m); + err.help(h); + err.emit(); + } pub fn span_bug(&self, sp: Span, m: &str) -> ! { self.sess.span_diagnostic.span_bug(sp, m) } @@ -4021,8 +4036,14 @@ impl<'a> Parser<'a> { None => { let unused_attrs = |attrs: &[_], s: &mut Self| { if attrs.len() > 0 { - s.span_err(s.span, - "expected statement after outer attribute"); + let last_token = s.last_token.clone().map(|t| *t); + match last_token { + Some(token::DocComment(_)) => s.span_err_help(s.last_span, + "found a documentation comment that doesn't document anything", + "doc comments must come before what they document, maybe a \ + comment was intended with `//`?"), + _ => s.span_err(s.span, "expected statement after outer attribute"), + } } }; @@ -5127,14 +5148,13 @@ impl<'a> Parser<'a> { self.bump(); } token::CloseDelim(token::Brace) => {} - _ => { - let span = self.span; - let token_str = self.this_token_to_string(); - return Err(self.span_fatal_help(span, - &format!("expected `,`, or `}}`, found `{}`", - token_str), - "struct fields should be separated by commas")) - } + token::DocComment(_) => return Err(self.span_fatal_help(self.span, + "found a documentation comment that doesn't document anything", + "doc comments must come before what they document, maybe a comment was \ + intended with `//`?")), + _ => return Err(self.span_fatal_help(self.span, + &format!("expected `,`, or `}}`, found `{}`", self.this_token_to_string()), + "struct fields should be separated by commas")), } Ok(a_var) } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 6fdc9b714d347..dc0fb02ea45d9 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -203,7 +203,7 @@ impl Token { pub fn is_lit(&self) -> bool { match *self { Literal(_, _) => true, - _ => false, + _ => false, } } @@ -215,6 +215,14 @@ impl Token { } } + /// Returns `true` if the token is a documentation comment. + pub fn is_doc_comment(&self) -> bool { + match *self { + DocComment(..) => true, + _ => false, + } + } + /// Returns `true` if the token is interpolated. pub fn is_interpolated(&self) -> bool { match *self { diff --git a/src/test/parse-fail/doc-after-struct-field.rs b/src/test/parse-fail/doc-after-struct-field.rs new file mode 100644 index 0000000000000..1aa6af5b78f5b --- /dev/null +++ b/src/test/parse-fail/doc-after-struct-field.rs @@ -0,0 +1,20 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z continue-parse-after-error +struct X { + a: u8 /** document a */, + //~^ ERROR found a documentation comment that doesn't document anything + //~| HELP maybe a comment was intended +} + +fn main() { + let y = X {a = 1}; +} diff --git a/src/test/parse-fail/doc-before-extern-rbrace.rs b/src/test/parse-fail/doc-before-extern-rbrace.rs index 9e825193dc0d8..70da47ba9b4f0 100644 --- a/src/test/parse-fail/doc-before-extern-rbrace.rs +++ b/src/test/parse-fail/doc-before-extern-rbrace.rs @@ -12,5 +12,5 @@ extern { /// hi + //~^ ERROR expected item after doc comment } -//~^^ ERROR expected item after doc comment diff --git a/src/test/parse-fail/doc-before-fn-rbrace.rs b/src/test/parse-fail/doc-before-fn-rbrace.rs new file mode 100644 index 0000000000000..bcf32612c8f4c --- /dev/null +++ b/src/test/parse-fail/doc-before-fn-rbrace.rs @@ -0,0 +1,16 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z continue-parse-after-error +fn main() { + /// document + //~^ ERROR found a documentation comment that doesn't document anything + //~| HELP maybe a comment was intended +} diff --git a/src/test/parse-fail/doc-before-identifier.rs b/src/test/parse-fail/doc-before-identifier.rs new file mode 100644 index 0000000000000..8f1fad91b1f99 --- /dev/null +++ b/src/test/parse-fail/doc-before-identifier.rs @@ -0,0 +1,18 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z continue-parse-after-error +fn /// document +foo() {} +//~^^ ERROR expected identifier, found `/// document` + +fn main() { + foo(); +} diff --git a/src/test/parse-fail/doc-before-mod-rbrace.rs b/src/test/parse-fail/doc-before-mod-rbrace.rs new file mode 100644 index 0000000000000..d38d1876384a8 --- /dev/null +++ b/src/test/parse-fail/doc-before-mod-rbrace.rs @@ -0,0 +1,15 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z continue-parse-after-error +mod Foo { + /// document + //~^ ERROR expected item after doc comment +} diff --git a/src/test/parse-fail/doc-before-rbrace.rs b/src/test/parse-fail/doc-before-rbrace.rs index 295d5ae432ecb..48ea2b5aea12c 100644 --- a/src/test/parse-fail/doc-before-rbrace.rs +++ b/src/test/parse-fail/doc-before-rbrace.rs @@ -12,5 +12,6 @@ fn main() { println!("Hi"); /// hi + //~^ ERROR found a documentation comment that doesn't document anything + //~| HELP maybe a comment was intended } -//~^ ERROR expected statement diff --git a/src/test/parse-fail/doc-before-semi.rs b/src/test/parse-fail/doc-before-semi.rs index 6a8906953be09..71104b8caae19 100644 --- a/src/test/parse-fail/doc-before-semi.rs +++ b/src/test/parse-fail/doc-before-semi.rs @@ -12,6 +12,7 @@ fn main() { /// hi + //~^ ERROR found a documentation comment that doesn't document anything + //~| HELP maybe a comment was intended ; - //~^ ERROR expected statement } diff --git a/src/test/parse-fail/doc-before-struct-rbrace-1.rs b/src/test/parse-fail/doc-before-struct-rbrace-1.rs new file mode 100644 index 0000000000000..5ba83190c8e50 --- /dev/null +++ b/src/test/parse-fail/doc-before-struct-rbrace-1.rs @@ -0,0 +1,21 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z continue-parse-after-error +struct X { + a: u8, + /// document + //~^ ERROR found a documentation comment that doesn't document anything + //~| HELP maybe a comment was intended +} + +fn main() { + let y = X {a = 1}; +} diff --git a/src/test/parse-fail/doc-before-struct-rbrace-2.rs b/src/test/parse-fail/doc-before-struct-rbrace-2.rs new file mode 100644 index 0000000000000..643e4aa17a1ac --- /dev/null +++ b/src/test/parse-fail/doc-before-struct-rbrace-2.rs @@ -0,0 +1,20 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z continue-parse-after-error +struct X { + a: u8 /// document + //~^ ERROR found a documentation comment that doesn't document anything + //~| HELP maybe a comment was intended +} + +fn main() { + let y = X {a = 1}; +}