Skip to content

Commit 505871e

Browse files
committed
Add an AST sanity checking pass and use it to catch some illegal lifetime/label names
1 parent da41920 commit 505871e

File tree

7 files changed

+139
-2
lines changed

7 files changed

+139
-2
lines changed

src/librustc/lint/builtin.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,12 @@ declare_lint! {
204204
"object-unsafe non-principal fragments in object types were erroneously allowed"
205205
}
206206

207+
declare_lint! {
208+
pub LIFETIME_UNDERSCORE,
209+
Warn,
210+
"lifetimes or labels named `'_` were erroneously allowed"
211+
}
212+
207213
/// Does nothing as a lint pass, but registers some `Lint`s
208214
/// which are used by other parts of the compiler.
209215
#[derive(Copy, Clone)]
@@ -242,7 +248,8 @@ impl LintPass for HardwiredLints {
242248
SUPER_OR_SELF_IN_GLOBAL_PATH,
243249
UNSIZED_IN_TUPLE,
244250
OBJECT_UNSAFE_FRAGMENT,
245-
HR_LIFETIME_IN_ASSOC_TYPE
251+
HR_LIFETIME_IN_ASSOC_TYPE,
252+
LIFETIME_UNDERSCORE
246253
)
247254
}
248255
}

src/librustc_driver/driver.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use rustc_privacy;
3838
use rustc_plugin::registry::Registry;
3939
use rustc_plugin as plugin;
4040
use rustc::hir::lowering::lower_crate;
41-
use rustc_passes::{no_asm, loops, consts, rvalues, static_recursion};
41+
use rustc_passes::{ast_sanity, no_asm, loops, consts, rvalues, static_recursion};
4242
use rustc_const_eval::check_match;
4343
use super::Compilation;
4444

@@ -166,6 +166,10 @@ pub fn compile_input(sess: &Session,
166166
"early lint checks",
167167
|| lint::check_ast_crate(sess, &expanded_crate));
168168

169+
time(sess.time_passes(),
170+
"AST sanity checking",
171+
|| ast_sanity::check_crate(sess, &expanded_crate));
172+
169173
let (analysis, resolutions, mut hir_forest) = {
170174
lower_and_resolve(sess, &id, &mut defs, &expanded_crate,
171175
&sess.dep_graph, control.make_glob_map)

src/librustc_lint/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
202202
id: LintId::of(HR_LIFETIME_IN_ASSOC_TYPE),
203203
reference: "issue #33685 <https://github.com/rust-lang/rust/issues/33685>",
204204
},
205+
FutureIncompatibleInfo {
206+
id: LintId::of(LIFETIME_UNDERSCORE),
207+
reference: "RFC 1177 <https://github.com/rust-lang/rfcs/pull/1177>",
208+
},
205209
]);
206210

207211
// We have one lint pass defined specially

src/librustc_passes/ast_sanity.rs

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Sanity check AST before lowering it to HIR
12+
//
13+
// This pass is supposed to catch things that fit into AST data structures,
14+
// but not permitted by the language. It runs after expansion when AST is frozen,
15+
// so it can check for erroneous constructions produced by syntax extensions.
16+
// This pass is supposed to perform only simple checks not requiring name resolution
17+
// or type checking or some other kind of complex analysis.
18+
19+
use rustc::lint;
20+
use rustc::session::Session;
21+
use syntax::ast::*;
22+
use syntax::codemap::Span;
23+
use syntax::errors;
24+
use syntax::parse::token::keywords;
25+
use syntax::visit::{self, Visitor};
26+
27+
struct SanityChecker<'a> {
28+
session: &'a Session,
29+
}
30+
31+
impl<'a> SanityChecker<'a> {
32+
fn err_handler(&self) -> &errors::Handler {
33+
&self.session.parse_sess.span_diagnostic
34+
}
35+
36+
fn check_label(&self, label: Ident, span: Span, id: NodeId) {
37+
if label.name == keywords::StaticLifetime.name() {
38+
self.err_handler().span_err(span, &format!("invalid label name `{}`", label.name));
39+
}
40+
if label.name.as_str() == "'_" {
41+
self.session.add_lint(
42+
lint::builtin::LIFETIME_UNDERSCORE, id, span,
43+
format!("invalid label name `{}`", label.name)
44+
);
45+
}
46+
}
47+
}
48+
49+
impl<'a, 'v> Visitor<'v> for SanityChecker<'a> {
50+
fn visit_lifetime(&mut self, lt: &Lifetime) {
51+
if lt.name.as_str() == "'_" {
52+
self.session.add_lint(
53+
lint::builtin::LIFETIME_UNDERSCORE, lt.id, lt.span,
54+
format!("invalid lifetime name `{}`", lt.name)
55+
);
56+
}
57+
58+
visit::walk_lifetime(self, lt)
59+
}
60+
61+
fn visit_expr(&mut self, expr: &Expr) {
62+
match expr.node {
63+
ExprKind::While(_, _, Some(ident)) | ExprKind::Loop(_, Some(ident)) |
64+
ExprKind::WhileLet(_, _, _, Some(ident)) | ExprKind::ForLoop(_, _, _, Some(ident)) => {
65+
self.check_label(ident, expr.span, expr.id);
66+
}
67+
ExprKind::Break(Some(ident)) | ExprKind::Again(Some(ident)) => {
68+
self.check_label(ident.node, ident.span, expr.id);
69+
}
70+
_ => {}
71+
}
72+
73+
visit::walk_expr(self, expr)
74+
}
75+
}
76+
77+
pub fn check_crate(session: &Session, krate: &Crate) {
78+
visit::walk_crate(&mut SanityChecker { session: session }, krate)
79+
}

src/librustc_passes/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ extern crate rustc_const_math;
3737

3838
pub mod diagnostics;
3939

40+
pub mod ast_sanity;
4041
pub mod consts;
4142
pub mod loops;
4243
pub mod no_asm;

src/test/compile-fail/label-static.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
'static: loop { //~ ERROR invalid label name `'static`
13+
break 'static //~ ERROR invalid label name `'static`
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![deny(lifetime_underscore)]
12+
13+
fn _f<'_>() //~ ERROR invalid lifetime name `'_`
14+
//~^ WARN this was previously accepted
15+
-> &'_ u8 //~ ERROR invalid lifetime name `'_`
16+
//~^ WARN this was previously accepted
17+
{
18+
panic!();
19+
}
20+
21+
fn main() {
22+
'_: loop { //~ ERROR invalid label name `'_`
23+
//~^ WARN this was previously accepted
24+
break '_ //~ ERROR invalid label name `'_`
25+
//~^ WARN this was previously accepted
26+
}
27+
}

0 commit comments

Comments
 (0)