Skip to content

Commit 15a8a66

Browse files
committed
Lint the use of async as an identifier
1 parent 22d21b1 commit 15a8a66

File tree

10 files changed

+297
-107
lines changed

10 files changed

+297
-107
lines changed

src/librustc/lint/context.rs

+93-83
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
use self::TargetLint::*;
2828

2929
use std::slice;
30-
use rustc_data_structures::sync::{RwLock, ReadGuard};
30+
use rustc_data_structures::sync::ReadGuard;
3131
use lint::{EarlyLintPassObject, LateLintPassObject};
3232
use lint::{Level, Lint, LintId, LintPass, LintBuffer};
3333
use lint::builtin::BuiltinLintDiagnostics;
@@ -59,8 +59,8 @@ pub struct LintStore {
5959
lints: Vec<(&'static Lint, bool)>,
6060

6161
/// Trait objects for each lint pass.
62-
/// This is only `None` while performing a lint pass. See the definition
63-
/// of `LintSession::new`.
62+
/// This is only `None` while performing a lint pass.
63+
pre_expansion_passes: Option<Vec<EarlyLintPassObject>>,
6464
early_passes: Option<Vec<EarlyLintPassObject>>,
6565
late_passes: Option<Vec<LateLintPassObject>>,
6666

@@ -139,6 +139,7 @@ impl LintStore {
139139
pub fn new() -> LintStore {
140140
LintStore {
141141
lints: vec![],
142+
pre_expansion_passes: Some(vec![]),
142143
early_passes: Some(vec![]),
143144
late_passes: Some(vec![]),
144145
by_name: FxHashMap(),
@@ -165,6 +166,15 @@ impl LintStore {
165166
self.early_passes.as_mut().unwrap().push(pass);
166167
}
167168

169+
pub fn register_pre_expansion_pass(
170+
&mut self,
171+
sess: Option<&Session>,
172+
pass: EarlyLintPassObject,
173+
) {
174+
self.push_pass(sess, false, &pass);
175+
self.pre_expansion_passes.as_mut().unwrap().push(pass);
176+
}
177+
168178
pub fn register_late_pass(&mut self,
169179
sess: Option<&Session>,
170180
from_plugin: bool,
@@ -332,28 +342,6 @@ impl LintStore {
332342
}
333343
}
334344

335-
impl<'a, PassObject: LintPassObject> LintSession<'a, PassObject> {
336-
/// Creates a new `LintSession`, by moving out the `LintStore`'s initial
337-
/// lint levels and pass objects. These can be restored using the `restore`
338-
/// method.
339-
fn new(store: &'a RwLock<LintStore>) -> LintSession<'a, PassObject> {
340-
let mut s = store.borrow_mut();
341-
let passes = PassObject::take_passes(&mut *s);
342-
drop(s);
343-
LintSession {
344-
lints: store.borrow(),
345-
passes,
346-
}
347-
}
348-
349-
/// Restores the levels back to the original lint store.
350-
fn restore(self, store: &RwLock<LintStore>) {
351-
drop(self.lints);
352-
let mut s = store.borrow_mut();
353-
PassObject::restore_passes(&mut *s, self.passes);
354-
}
355-
}
356-
357345
/// Context for lint checking after type checking.
358346
pub struct LateContext<'a, 'tcx: 'a> {
359347
/// Type context we're checking in.
@@ -405,30 +393,11 @@ macro_rules! run_lints { ($cx:expr, $f:ident, $($args:expr),*) => ({
405393
$cx.lint_sess_mut().passes = Some(passes);
406394
}) }
407395

408-
pub trait LintPassObject: Sized {
409-
fn take_passes(store: &mut LintStore) -> Option<Vec<Self>>;
410-
fn restore_passes(store: &mut LintStore, passes: Option<Vec<Self>>);
411-
}
412-
413-
impl LintPassObject for EarlyLintPassObject {
414-
fn take_passes(store: &mut LintStore) -> Option<Vec<Self>> {
415-
store.early_passes.take()
416-
}
396+
pub trait LintPassObject: Sized {}
417397

418-
fn restore_passes(store: &mut LintStore, passes: Option<Vec<Self>>) {
419-
store.early_passes = passes;
420-
}
421-
}
422-
423-
impl LintPassObject for LateLintPassObject {
424-
fn take_passes(store: &mut LintStore) -> Option<Vec<Self>> {
425-
store.late_passes.take()
426-
}
398+
impl LintPassObject for EarlyLintPassObject {}
427399

428-
fn restore_passes(store: &mut LintStore, passes: Option<Vec<Self>>) {
429-
store.late_passes = passes;
430-
}
431-
}
400+
impl LintPassObject for LateLintPassObject {}
432401

433402

434403
pub trait LintContext<'tcx>: Sized {
@@ -515,14 +484,21 @@ pub trait LintContext<'tcx>: Sized {
515484

516485

517486
impl<'a> EarlyContext<'a> {
518-
fn new(sess: &'a Session,
519-
krate: &'a ast::Crate) -> EarlyContext<'a> {
487+
fn new(
488+
sess: &'a Session,
489+
krate: &'a ast::Crate,
490+
passes: Option<Vec<EarlyLintPassObject>>,
491+
buffered: LintBuffer,
492+
) -> EarlyContext<'a> {
520493
EarlyContext {
521494
sess,
522495
krate,
523-
lint_sess: LintSession::new(&sess.lint_store),
496+
lint_sess: LintSession {
497+
lints: sess.lint_store.borrow(),
498+
passes,
499+
},
524500
builder: LintLevelSets::builder(sess),
525-
buffered: sess.buffered_lints.borrow_mut().take().unwrap(),
501+
buffered,
526502
}
527503
}
528504

@@ -1041,9 +1017,14 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> {
10411017
run_lints!(self, check_attribute, attr);
10421018
}
10431019

1044-
fn visit_mac_def(&mut self, _mac: &'a ast::MacroDef, id: ast::NodeId) {
1020+
fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) {
1021+
run_lints!(self, check_mac_def, mac, id);
10451022
self.check_id(id);
10461023
}
1024+
1025+
fn visit_mac(&mut self, mac: &'ast ast::Mac) {
1026+
run_lints!(self, check_mac, mac);
1027+
}
10471028
}
10481029

10491030

@@ -1054,48 +1035,77 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
10541035
let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE);
10551036

10561037
let krate = tcx.hir.krate();
1038+
let passes = tcx.sess.lint_store.borrow_mut().late_passes.take();
1039+
1040+
let passes = {
1041+
let mut cx = LateContext {
1042+
tcx,
1043+
tables: &ty::TypeckTables::empty(None),
1044+
param_env: ty::ParamEnv::empty(),
1045+
access_levels,
1046+
lint_sess: LintSession {
1047+
passes,
1048+
lints: tcx.sess.lint_store.borrow(),
1049+
},
1050+
last_ast_node_with_lint_attrs: ast::CRATE_NODE_ID,
1051+
generics: None,
1052+
};
10571053

1058-
let mut cx = LateContext {
1059-
tcx,
1060-
tables: &ty::TypeckTables::empty(None),
1061-
param_env: ty::ParamEnv::empty(),
1062-
access_levels,
1063-
lint_sess: LintSession::new(&tcx.sess.lint_store),
1064-
last_ast_node_with_lint_attrs: ast::CRATE_NODE_ID,
1065-
generics: None,
1066-
};
1067-
1068-
// Visit the whole crate.
1069-
cx.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |cx| {
1070-
// since the root module isn't visited as an item (because it isn't an
1071-
// item), warn for it here.
1072-
run_lints!(cx, check_crate, krate);
1054+
// Visit the whole crate.
1055+
cx.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |cx| {
1056+
// since the root module isn't visited as an item (because it isn't an
1057+
// item), warn for it here.
1058+
run_lints!(cx, check_crate, krate);
10731059

1074-
hir_visit::walk_crate(cx, krate);
1060+
hir_visit::walk_crate(cx, krate);
10751061

1076-
run_lints!(cx, check_crate_post, krate);
1077-
});
1062+
run_lints!(cx, check_crate_post, krate);
1063+
});
1064+
cx.lint_sess.passes
1065+
};
10781066

10791067
// Put the lint store levels and passes back in the session.
1080-
cx.lint_sess.restore(&tcx.sess.lint_store);
1068+
tcx.sess.lint_store.borrow_mut().late_passes = passes;
10811069
}
10821070

1083-
pub fn check_ast_crate(sess: &Session, krate: &ast::Crate) {
1084-
let mut cx = EarlyContext::new(sess, krate);
1071+
pub fn check_ast_crate(
1072+
sess: &Session,
1073+
krate: &ast::Crate,
1074+
pre_expansion: bool,
1075+
) {
1076+
let (passes, buffered) = if pre_expansion {
1077+
(
1078+
sess.lint_store.borrow_mut().pre_expansion_passes.take(),
1079+
LintBuffer::new(),
1080+
)
1081+
} else {
1082+
(
1083+
sess.lint_store.borrow_mut().early_passes.take(),
1084+
sess.buffered_lints.borrow_mut().take().unwrap(),
1085+
)
1086+
};
1087+
let (passes, buffered) = {
1088+
let mut cx = EarlyContext::new(sess, krate, passes, buffered);
10851089

1086-
// Visit the whole crate.
1087-
cx.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |cx| {
1088-
// since the root module isn't visited as an item (because it isn't an
1089-
// item), warn for it here.
1090-
run_lints!(cx, check_crate, krate);
1090+
// Visit the whole crate.
1091+
cx.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |cx| {
1092+
// since the root module isn't visited as an item (because it isn't an
1093+
// item), warn for it here.
1094+
run_lints!(cx, check_crate, krate);
10911095

1092-
ast_visit::walk_crate(cx, krate);
1096+
ast_visit::walk_crate(cx, krate);
10931097

1094-
run_lints!(cx, check_crate_post, krate);
1095-
});
1098+
run_lints!(cx, check_crate_post, krate);
1099+
});
1100+
(cx.lint_sess.passes, cx.buffered)
1101+
};
10961102

10971103
// Put the lint store levels and passes back in the session.
1098-
cx.lint_sess.restore(&sess.lint_store);
1104+
if pre_expansion {
1105+
sess.lint_store.borrow_mut().pre_expansion_passes = passes;
1106+
} else {
1107+
sess.lint_store.borrow_mut().early_passes = passes;
1108+
}
10991109

11001110
// All of the buffered lints should have been emitted at this point.
11011111
// If not, that means that we somehow buffered a lint for a node id
@@ -1107,7 +1117,7 @@ pub fn check_ast_crate(sess: &Session, krate: &ast::Crate) {
11071117
// unused_macro lint) anymore. So we only run this check
11081118
// when we're not in rustdoc mode. (see issue #47639)
11091119
if !sess.opts.actually_rustdoc {
1110-
for (_id, lints) in cx.buffered.map {
1120+
for (_id, lints) in buffered.map {
11111121
for early_lint in lints {
11121122
sess.delay_span_bug(early_lint.span, "failed to process buffered lint here");
11131123
}

src/librustc/lint/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,8 @@ pub trait EarlyLintPass: LintPass {
327327
fn check_lifetime(&mut self, _: &EarlyContext, _: &ast::Lifetime) { }
328328
fn check_path(&mut self, _: &EarlyContext, _: &ast::Path, _: ast::NodeId) { }
329329
fn check_attribute(&mut self, _: &EarlyContext, _: &ast::Attribute) { }
330+
fn check_mac_def(&mut self, _: &EarlyContext, _: &ast::MacroDef, _id: ast::NodeId) { }
331+
fn check_mac(&mut self, _: &EarlyContext, _: &ast::Mac) { }
330332

331333
/// Called when entering a syntax node that can have lint attributes such
332334
/// as `#[allow(...)]`. Called with *all* the attributes of that node.
@@ -341,6 +343,8 @@ pub type EarlyLintPassObject = Box<dyn EarlyLintPass + sync::Send + sync::Sync +
341343
pub type LateLintPassObject = Box<dyn for<'a, 'tcx> LateLintPass<'a, 'tcx> + sync::Send
342344
+ sync::Sync + 'static>;
343345

346+
347+
344348
/// Identifies a lint known to the compiler.
345349
#[derive(Clone, Copy, Debug)]
346350
pub struct LintId {

src/librustc_driver/driver.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,10 @@ where
922922
return Err(CompileIncomplete::Stopped);
923923
}
924924

925+
time(sess, "pre ast expansion lint checks", || {
926+
lint::check_ast_crate(sess, &krate, true)
927+
});
928+
925929
let mut resolver = Resolver::new(
926930
sess,
927931
cstore,
@@ -1134,7 +1138,7 @@ where
11341138
});
11351139

11361140
time(sess, "early lint checks", || {
1137-
lint::check_ast_crate(sess, &krate)
1141+
lint::check_ast_crate(sess, &krate, false)
11381142
});
11391143

11401144
// Discard hygiene data, which isn't required after lowering to HIR.

src/librustc_lint/builtin.rs

+68
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ use lint::{LintPass, LateLintPass, EarlyLintPass, EarlyContext};
4141

4242
use std::collections::HashSet;
4343

44+
use syntax::tokenstream::{TokenTree, TokenStream};
4445
use syntax::ast;
4546
use syntax::attr;
4647
use syntax::codemap::Spanned;
@@ -1784,3 +1785,70 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnameableTestFunctions {
17841785
};
17851786
}
17861787
}
1788+
1789+
declare_lint! {
1790+
pub ASYNC_IDENTS,
1791+
Allow,
1792+
"detects `async` being used as an identifier"
1793+
}
1794+
1795+
/// Checks for uses of `async` as an identifier
1796+
#[derive(Clone)]
1797+
pub struct Async2018;
1798+
1799+
impl LintPass for Async2018 {
1800+
fn get_lints(&self) -> LintArray {
1801+
lint_array!()
1802+
}
1803+
}
1804+
1805+
impl Async2018 {
1806+
fn check_tokens(&mut self, cx: &EarlyContext, tokens: TokenStream) {
1807+
for tt in tokens.into_trees() {
1808+
match tt {
1809+
TokenTree::Token(span, tok) => match tok.ident() {
1810+
// only report non-raw idents
1811+
Some((ident, false)) if ident.as_str() == "async" => {
1812+
self.report(cx, span.substitute_dummy(ident.span))
1813+
},
1814+
_ => {},
1815+
}
1816+
TokenTree::Delimited(_, ref delim) => {
1817+
self.check_tokens(cx, delim.tts.clone().into())
1818+
},
1819+
}
1820+
}
1821+
}
1822+
fn report(&mut self, cx: &EarlyContext, span: Span) {
1823+
// don't lint `r#async`
1824+
if cx.sess.parse_sess.raw_identifier_spans.borrow().contains(&span) {
1825+
return;
1826+
}
1827+
let mut lint = cx.struct_span_lint(
1828+
ASYNC_IDENTS,
1829+
span,
1830+
"`async` is a keyword in the 2018 edition",
1831+
);
1832+
lint.span_suggestion_with_applicability(
1833+
span,
1834+
"you can use a raw identifier to stay compatible",
1835+
"r#async".to_string(),
1836+
Applicability::MachineApplicable,
1837+
);
1838+
lint.emit()
1839+
}
1840+
}
1841+
1842+
impl EarlyLintPass for Async2018 {
1843+
fn check_mac_def(&mut self, cx: &EarlyContext, mac_def: &ast::MacroDef, _id: ast::NodeId) {
1844+
self.check_tokens(cx, mac_def.stream());
1845+
}
1846+
fn check_mac(&mut self, cx: &EarlyContext, mac: &ast::Mac) {
1847+
self.check_tokens(cx, mac.node.tts.clone().into());
1848+
}
1849+
fn check_ident(&mut self, cx: &EarlyContext, ident: ast::Ident) {
1850+
if ident.as_str() == "async" {
1851+
self.report(cx, ident.span);
1852+
}
1853+
}
1854+
}

0 commit comments

Comments
 (0)