Skip to content

Commit

Permalink
Lint the use of async as an identifier
Browse files Browse the repository at this point in the history
  • Loading branch information
oli-obk committed Jul 14, 2018
1 parent 22d21b1 commit 15a8a66
Show file tree
Hide file tree
Showing 10 changed files with 297 additions and 107 deletions.
176 changes: 93 additions & 83 deletions src/librustc/lint/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
use self::TargetLint::*;

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

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

Expand Down Expand Up @@ -139,6 +139,7 @@ impl LintStore {
pub fn new() -> LintStore {
LintStore {
lints: vec![],
pre_expansion_passes: Some(vec![]),
early_passes: Some(vec![]),
late_passes: Some(vec![]),
by_name: FxHashMap(),
Expand All @@ -165,6 +166,15 @@ impl LintStore {
self.early_passes.as_mut().unwrap().push(pass);
}

pub fn register_pre_expansion_pass(
&mut self,
sess: Option<&Session>,
pass: EarlyLintPassObject,
) {
self.push_pass(sess, false, &pass);
self.pre_expansion_passes.as_mut().unwrap().push(pass);
}

pub fn register_late_pass(&mut self,
sess: Option<&Session>,
from_plugin: bool,
Expand Down Expand Up @@ -332,28 +342,6 @@ impl LintStore {
}
}

impl<'a, PassObject: LintPassObject> LintSession<'a, PassObject> {
/// Creates a new `LintSession`, by moving out the `LintStore`'s initial
/// lint levels and pass objects. These can be restored using the `restore`
/// method.
fn new(store: &'a RwLock<LintStore>) -> LintSession<'a, PassObject> {
let mut s = store.borrow_mut();
let passes = PassObject::take_passes(&mut *s);
drop(s);
LintSession {
lints: store.borrow(),
passes,
}
}

/// Restores the levels back to the original lint store.
fn restore(self, store: &RwLock<LintStore>) {
drop(self.lints);
let mut s = store.borrow_mut();
PassObject::restore_passes(&mut *s, self.passes);
}
}

/// Context for lint checking after type checking.
pub struct LateContext<'a, 'tcx: 'a> {
/// Type context we're checking in.
Expand Down Expand Up @@ -405,30 +393,11 @@ macro_rules! run_lints { ($cx:expr, $f:ident, $($args:expr),*) => ({
$cx.lint_sess_mut().passes = Some(passes);
}) }

pub trait LintPassObject: Sized {
fn take_passes(store: &mut LintStore) -> Option<Vec<Self>>;
fn restore_passes(store: &mut LintStore, passes: Option<Vec<Self>>);
}

impl LintPassObject for EarlyLintPassObject {
fn take_passes(store: &mut LintStore) -> Option<Vec<Self>> {
store.early_passes.take()
}
pub trait LintPassObject: Sized {}

fn restore_passes(store: &mut LintStore, passes: Option<Vec<Self>>) {
store.early_passes = passes;
}
}

impl LintPassObject for LateLintPassObject {
fn take_passes(store: &mut LintStore) -> Option<Vec<Self>> {
store.late_passes.take()
}
impl LintPassObject for EarlyLintPassObject {}

fn restore_passes(store: &mut LintStore, passes: Option<Vec<Self>>) {
store.late_passes = passes;
}
}
impl LintPassObject for LateLintPassObject {}


pub trait LintContext<'tcx>: Sized {
Expand Down Expand Up @@ -515,14 +484,21 @@ pub trait LintContext<'tcx>: Sized {


impl<'a> EarlyContext<'a> {
fn new(sess: &'a Session,
krate: &'a ast::Crate) -> EarlyContext<'a> {
fn new(
sess: &'a Session,
krate: &'a ast::Crate,
passes: Option<Vec<EarlyLintPassObject>>,
buffered: LintBuffer,
) -> EarlyContext<'a> {
EarlyContext {
sess,
krate,
lint_sess: LintSession::new(&sess.lint_store),
lint_sess: LintSession {
lints: sess.lint_store.borrow(),
passes,
},
builder: LintLevelSets::builder(sess),
buffered: sess.buffered_lints.borrow_mut().take().unwrap(),
buffered,
}
}

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

fn visit_mac_def(&mut self, _mac: &'a ast::MacroDef, id: ast::NodeId) {
fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) {
run_lints!(self, check_mac_def, mac, id);
self.check_id(id);
}

fn visit_mac(&mut self, mac: &'ast ast::Mac) {
run_lints!(self, check_mac, mac);
}
}


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

let krate = tcx.hir.krate();
let passes = tcx.sess.lint_store.borrow_mut().late_passes.take();

let passes = {
let mut cx = LateContext {
tcx,
tables: &ty::TypeckTables::empty(None),
param_env: ty::ParamEnv::empty(),
access_levels,
lint_sess: LintSession {
passes,
lints: tcx.sess.lint_store.borrow(),
},
last_ast_node_with_lint_attrs: ast::CRATE_NODE_ID,
generics: None,
};

let mut cx = LateContext {
tcx,
tables: &ty::TypeckTables::empty(None),
param_env: ty::ParamEnv::empty(),
access_levels,
lint_sess: LintSession::new(&tcx.sess.lint_store),
last_ast_node_with_lint_attrs: ast::CRATE_NODE_ID,
generics: None,
};

// Visit the whole crate.
cx.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |cx| {
// since the root module isn't visited as an item (because it isn't an
// item), warn for it here.
run_lints!(cx, check_crate, krate);
// Visit the whole crate.
cx.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |cx| {
// since the root module isn't visited as an item (because it isn't an
// item), warn for it here.
run_lints!(cx, check_crate, krate);

hir_visit::walk_crate(cx, krate);
hir_visit::walk_crate(cx, krate);

run_lints!(cx, check_crate_post, krate);
});
run_lints!(cx, check_crate_post, krate);
});
cx.lint_sess.passes
};

// Put the lint store levels and passes back in the session.
cx.lint_sess.restore(&tcx.sess.lint_store);
tcx.sess.lint_store.borrow_mut().late_passes = passes;
}

pub fn check_ast_crate(sess: &Session, krate: &ast::Crate) {
let mut cx = EarlyContext::new(sess, krate);
pub fn check_ast_crate(
sess: &Session,
krate: &ast::Crate,
pre_expansion: bool,
) {
let (passes, buffered) = if pre_expansion {
(
sess.lint_store.borrow_mut().pre_expansion_passes.take(),
LintBuffer::new(),
)
} else {
(
sess.lint_store.borrow_mut().early_passes.take(),
sess.buffered_lints.borrow_mut().take().unwrap(),
)
};
let (passes, buffered) = {
let mut cx = EarlyContext::new(sess, krate, passes, buffered);

// Visit the whole crate.
cx.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |cx| {
// since the root module isn't visited as an item (because it isn't an
// item), warn for it here.
run_lints!(cx, check_crate, krate);
// Visit the whole crate.
cx.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |cx| {
// since the root module isn't visited as an item (because it isn't an
// item), warn for it here.
run_lints!(cx, check_crate, krate);

ast_visit::walk_crate(cx, krate);
ast_visit::walk_crate(cx, krate);

run_lints!(cx, check_crate_post, krate);
});
run_lints!(cx, check_crate_post, krate);
});
(cx.lint_sess.passes, cx.buffered)
};

// Put the lint store levels and passes back in the session.
cx.lint_sess.restore(&sess.lint_store);
if pre_expansion {
sess.lint_store.borrow_mut().pre_expansion_passes = passes;
} else {
sess.lint_store.borrow_mut().early_passes = passes;
}

// All of the buffered lints should have been emitted at this point.
// If not, that means that we somehow buffered a lint for a node id
Expand All @@ -1107,7 +1117,7 @@ pub fn check_ast_crate(sess: &Session, krate: &ast::Crate) {
// unused_macro lint) anymore. So we only run this check
// when we're not in rustdoc mode. (see issue #47639)
if !sess.opts.actually_rustdoc {
for (_id, lints) in cx.buffered.map {
for (_id, lints) in buffered.map {
for early_lint in lints {
sess.delay_span_bug(early_lint.span, "failed to process buffered lint here");
}
Expand Down
4 changes: 4 additions & 0 deletions src/librustc/lint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,8 @@ pub trait EarlyLintPass: LintPass {
fn check_lifetime(&mut self, _: &EarlyContext, _: &ast::Lifetime) { }
fn check_path(&mut self, _: &EarlyContext, _: &ast::Path, _: ast::NodeId) { }
fn check_attribute(&mut self, _: &EarlyContext, _: &ast::Attribute) { }
fn check_mac_def(&mut self, _: &EarlyContext, _: &ast::MacroDef, _id: ast::NodeId) { }
fn check_mac(&mut self, _: &EarlyContext, _: &ast::Mac) { }

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



/// Identifies a lint known to the compiler.
#[derive(Clone, Copy, Debug)]
pub struct LintId {
Expand Down
6 changes: 5 additions & 1 deletion src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,10 @@ where
return Err(CompileIncomplete::Stopped);
}

time(sess, "pre ast expansion lint checks", || {
lint::check_ast_crate(sess, &krate, true)
});

let mut resolver = Resolver::new(
sess,
cstore,
Expand Down Expand Up @@ -1134,7 +1138,7 @@ where
});

time(sess, "early lint checks", || {
lint::check_ast_crate(sess, &krate)
lint::check_ast_crate(sess, &krate, false)
});

// Discard hygiene data, which isn't required after lowering to HIR.
Expand Down
68 changes: 68 additions & 0 deletions src/librustc_lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ use lint::{LintPass, LateLintPass, EarlyLintPass, EarlyContext};

use std::collections::HashSet;

use syntax::tokenstream::{TokenTree, TokenStream};
use syntax::ast;
use syntax::attr;
use syntax::codemap::Spanned;
Expand Down Expand Up @@ -1784,3 +1785,70 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnameableTestFunctions {
};
}
}

declare_lint! {
pub ASYNC_IDENTS,
Allow,
"detects `async` being used as an identifier"
}

/// Checks for uses of `async` as an identifier
#[derive(Clone)]
pub struct Async2018;

impl LintPass for Async2018 {
fn get_lints(&self) -> LintArray {
lint_array!()
}
}

impl Async2018 {
fn check_tokens(&mut self, cx: &EarlyContext, tokens: TokenStream) {
for tt in tokens.into_trees() {
match tt {
TokenTree::Token(span, tok) => match tok.ident() {
// only report non-raw idents
Some((ident, false)) if ident.as_str() == "async" => {
self.report(cx, span.substitute_dummy(ident.span))
},
_ => {},
}
TokenTree::Delimited(_, ref delim) => {
self.check_tokens(cx, delim.tts.clone().into())
},
}
}
}
fn report(&mut self, cx: &EarlyContext, span: Span) {
// don't lint `r#async`
if cx.sess.parse_sess.raw_identifier_spans.borrow().contains(&span) {
return;
}
let mut lint = cx.struct_span_lint(
ASYNC_IDENTS,
span,
"`async` is a keyword in the 2018 edition",
);
lint.span_suggestion_with_applicability(
span,
"you can use a raw identifier to stay compatible",
"r#async".to_string(),
Applicability::MachineApplicable,
);
lint.emit()
}
}

impl EarlyLintPass for Async2018 {
fn check_mac_def(&mut self, cx: &EarlyContext, mac_def: &ast::MacroDef, _id: ast::NodeId) {
self.check_tokens(cx, mac_def.stream());
}
fn check_mac(&mut self, cx: &EarlyContext, mac: &ast::Mac) {
self.check_tokens(cx, mac.node.tts.clone().into());
}
fn check_ident(&mut self, cx: &EarlyContext, ident: ast::Ident) {
if ident.as_str() == "async" {
self.report(cx, ident.span);
}
}
}
Loading

0 comments on commit 15a8a66

Please sign in to comment.