move rustc::lint::{context, passes} to rustc_lint.

Also do some cleanup of the interface.
This commit is contained in:
Mazdak Farrokhzad 2020-01-09 07:52:01 +01:00
parent 8c12c424e2
commit f58db20362
24 changed files with 505 additions and 513 deletions

View file

@ -3789,6 +3789,7 @@ dependencies = [
"rustc_error_codes",
"rustc_errors",
"rustc_hir",
"rustc_lint",
"rustc_metadata",
"rustc_span",
"syntax",

View file

@ -72,8 +72,6 @@
#[macro_use]
extern crate log;
#[macro_use]
extern crate syntax;
#[macro_use]
extern crate smallvec;
#[cfg(test)]

View file

@ -1,30 +1,15 @@
use std::cmp;
use crate::ich::StableHashingContext;
use crate::lint::context::CheckLintNameResult;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId};
use rustc_errors::{pluralize, Applicability, DiagnosticBuilder, DiagnosticId};
use rustc_hir::HirId;
pub use rustc_session::lint::{builtin, Level, Lint, LintId, LintPass};
use rustc_session::{DiagnosticMessageId, Session};
use rustc_span::hygiene::MacroKind;
use rustc_span::source_map::{DesugaringKind, ExpnKind, MultiSpan};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use syntax::ast;
use syntax::attr;
use syntax::print::pprust;
use syntax::sess::feature_err;
use rustc_error_codes::*;
mod context;
mod passes;
pub use context::add_elided_lifetime_in_path_suggestion;
pub use context::{EarlyContext, LateContext, LintContext, LintStore};
pub use passes::{EarlyLintPass, EarlyLintPassObject, LateLintPass, LateLintPassObject};
use rustc_span::{Span, Symbol};
/// How a lint level was set.
#[derive(Clone, Copy, PartialEq, Eq, HashStable)]
@ -43,11 +28,11 @@ pub enum LintSource {
pub type LevelSource = (Level, LintSource);
pub struct LintLevelSets {
list: Vec<LintSet>,
lint_cap: Level,
pub list: Vec<LintSet>,
pub lint_cap: Level,
}
enum LintSet {
pub enum LintSet {
CommandLine {
// -A,-W,-D flags, a `Symbol` for the flag itself and `Level` for which
// flag.
@ -133,381 +118,9 @@ pub fn get_lint_id_level(
}
}
pub struct LintLevelsBuilder<'a> {
sess: &'a Session,
sets: LintLevelSets,
id_to_set: FxHashMap<HirId, u32>,
cur: u32,
warn_about_weird_lints: bool,
}
pub struct BuilderPush {
prev: u32,
pub changed: bool,
}
impl<'a> LintLevelsBuilder<'a> {
pub fn new(sess: &'a Session, warn_about_weird_lints: bool, store: &LintStore) -> Self {
let mut builder = LintLevelsBuilder {
sess,
sets: LintLevelSets::new(),
cur: 0,
id_to_set: Default::default(),
warn_about_weird_lints,
};
builder.process_command_line(sess, store);
assert_eq!(builder.sets.list.len(), 1);
builder
}
fn process_command_line(&mut self, sess: &Session, store: &LintStore) {
let mut specs = FxHashMap::default();
self.sets.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid);
for &(ref lint_name, level) in &sess.opts.lint_opts {
store.check_lint_name_cmdline(sess, &lint_name, level);
// If the cap is less than this specified level, e.g., if we've got
// `--cap-lints allow` but we've also got `-D foo` then we ignore
// this specification as the lint cap will set it to allow anyway.
let level = cmp::min(level, self.sets.lint_cap);
let lint_flag_val = Symbol::intern(lint_name);
let ids = match store.find_lints(&lint_name) {
Ok(ids) => ids,
Err(_) => continue, // errors handled in check_lint_name_cmdline above
};
for id in ids {
let src = LintSource::CommandLine(lint_flag_val);
specs.insert(id, (level, src));
}
}
self.sets.list.push(LintSet::CommandLine { specs });
}
/// Pushes a list of AST lint attributes onto this context.
///
/// This function will return a `BuilderPush` object which should be passed
/// to `pop` when this scope for the attributes provided is exited.
///
/// This function will perform a number of tasks:
///
/// * It'll validate all lint-related attributes in `attrs`
/// * It'll mark all lint-related attributes as used
/// * Lint levels will be updated based on the attributes provided
/// * Lint attributes are validated, e.g., a #[forbid] can't be switched to
/// #[allow]
///
/// Don't forget to call `pop`!
pub fn push(&mut self, attrs: &[ast::Attribute], store: &LintStore) -> BuilderPush {
let mut specs = FxHashMap::default();
let sess = self.sess;
let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input");
for attr in attrs {
let level = match Level::from_symbol(attr.name_or_empty()) {
None => continue,
Some(lvl) => lvl,
};
let meta = unwrap_or!(attr.meta(), continue);
attr::mark_used(attr);
let mut metas = unwrap_or!(meta.meta_item_list(), continue);
if metas.is_empty() {
// FIXME (#55112): issue unused-attributes lint for `#[level()]`
continue;
}
// Before processing the lint names, look for a reason (RFC 2383)
// at the end.
let mut reason = None;
let tail_li = &metas[metas.len() - 1];
if let Some(item) = tail_li.meta_item() {
match item.kind {
ast::MetaItemKind::Word => {} // actual lint names handled later
ast::MetaItemKind::NameValue(ref name_value) => {
if item.path == sym::reason {
// found reason, reslice meta list to exclude it
metas = &metas[0..metas.len() - 1];
// FIXME (#55112): issue unused-attributes lint if we thereby
// don't have any lint names (`#[level(reason = "foo")]`)
if let ast::LitKind::Str(rationale, _) = name_value.kind {
if !self.sess.features_untracked().lint_reasons {
feature_err(
&self.sess.parse_sess,
sym::lint_reasons,
item.span,
"lint reasons are experimental",
)
.emit();
}
reason = Some(rationale);
} else {
bad_attr(name_value.span)
.span_label(name_value.span, "reason must be a string literal")
.emit();
}
} else {
bad_attr(item.span)
.span_label(item.span, "bad attribute argument")
.emit();
}
}
ast::MetaItemKind::List(_) => {
bad_attr(item.span).span_label(item.span, "bad attribute argument").emit();
}
}
}
for li in metas {
let meta_item = match li.meta_item() {
Some(meta_item) if meta_item.is_word() => meta_item,
_ => {
let sp = li.span();
let mut err = bad_attr(sp);
let mut add_label = true;
if let Some(item) = li.meta_item() {
if let ast::MetaItemKind::NameValue(_) = item.kind {
if item.path == sym::reason {
err.span_label(sp, "reason in lint attribute must come last");
add_label = false;
}
}
}
if add_label {
err.span_label(sp, "bad attribute argument");
}
err.emit();
continue;
}
};
let tool_name = if meta_item.path.segments.len() > 1 {
let tool_ident = meta_item.path.segments[0].ident;
if !attr::is_known_lint_tool(tool_ident) {
struct_span_err!(
sess,
tool_ident.span,
E0710,
"an unknown tool name found in scoped lint: `{}`",
pprust::path_to_string(&meta_item.path),
)
.emit();
continue;
}
Some(tool_ident.name)
} else {
None
};
let name = meta_item.path.segments.last().expect("empty lint name").ident.name;
match store.check_lint_name(&name.as_str(), tool_name) {
CheckLintNameResult::Ok(ids) => {
let src = LintSource::Node(name, li.span(), reason);
for id in ids {
specs.insert(*id, (level, src));
}
}
CheckLintNameResult::Tool(result) => {
match result {
Ok(ids) => {
let complete_name = &format!("{}::{}", tool_name.unwrap(), name);
let src = LintSource::Node(
Symbol::intern(complete_name),
li.span(),
reason,
);
for id in ids {
specs.insert(*id, (level, src));
}
}
Err((Some(ids), new_lint_name)) => {
let lint = builtin::RENAMED_AND_REMOVED_LINTS;
let (lvl, src) =
self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess);
let msg = format!(
"lint name `{}` is deprecated \
and may not have an effect in the future. \
Also `cfg_attr(cargo-clippy)` won't be necessary anymore",
name
);
struct_lint_level(
self.sess,
lint,
lvl,
src,
Some(li.span().into()),
&msg,
)
.span_suggestion(
li.span(),
"change it to",
new_lint_name.to_string(),
Applicability::MachineApplicable,
)
.emit();
let src = LintSource::Node(
Symbol::intern(&new_lint_name),
li.span(),
reason,
);
for id in ids {
specs.insert(*id, (level, src));
}
}
Err((None, _)) => {
// If Tool(Err(None, _)) is returned, then either the lint does not
// exist in the tool or the code was not compiled with the tool and
// therefore the lint was never added to the `LintStore`. To detect
// this is the responsibility of the lint tool.
}
}
}
_ if !self.warn_about_weird_lints => {}
CheckLintNameResult::Warning(msg, renamed) => {
let lint = builtin::RENAMED_AND_REMOVED_LINTS;
let (level, src) =
self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess);
let mut err = struct_lint_level(
self.sess,
lint,
level,
src,
Some(li.span().into()),
&msg,
);
if let Some(new_name) = renamed {
err.span_suggestion(
li.span(),
"use the new name",
new_name,
Applicability::MachineApplicable,
);
}
err.emit();
}
CheckLintNameResult::NoLint(suggestion) => {
let lint = builtin::UNKNOWN_LINTS;
let (level, src) =
self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess);
let msg = format!("unknown lint: `{}`", name);
let mut db = struct_lint_level(
self.sess,
lint,
level,
src,
Some(li.span().into()),
&msg,
);
if let Some(suggestion) = suggestion {
db.span_suggestion(
li.span(),
"did you mean",
suggestion.to_string(),
Applicability::MachineApplicable,
);
}
db.emit();
}
}
}
}
for (id, &(level, ref src)) in specs.iter() {
if level == Level::Forbid {
continue;
}
let forbid_src = match self.sets.get_lint_id_level(*id, self.cur, None) {
(Some(Level::Forbid), src) => src,
_ => continue,
};
let forbidden_lint_name = match forbid_src {
LintSource::Default => id.to_string(),
LintSource::Node(name, _, _) => name.to_string(),
LintSource::CommandLine(name) => name.to_string(),
};
let (lint_attr_name, lint_attr_span) = match *src {
LintSource::Node(name, span, _) => (name, span),
_ => continue,
};
let mut diag_builder = struct_span_err!(
self.sess,
lint_attr_span,
E0453,
"{}({}) overruled by outer forbid({})",
level.as_str(),
lint_attr_name,
forbidden_lint_name
);
diag_builder.span_label(lint_attr_span, "overruled by previous forbid");
match forbid_src {
LintSource::Default => {}
LintSource::Node(_, forbid_source_span, reason) => {
diag_builder.span_label(forbid_source_span, "`forbid` level set here");
if let Some(rationale) = reason {
diag_builder.note(&rationale.as_str());
}
}
LintSource::CommandLine(_) => {
diag_builder.note("`forbid` lint level was set on command line");
}
}
diag_builder.emit();
// don't set a separate error for every lint in the group
break;
}
let prev = self.cur;
if specs.len() > 0 {
self.cur = self.sets.list.len() as u32;
self.sets.list.push(LintSet::Node { specs: specs, parent: prev });
}
BuilderPush { prev: prev, changed: prev != self.cur }
}
/// Called after `push` when the scope of a set of attributes are exited.
pub fn pop(&mut self, push: BuilderPush) {
self.cur = push.prev;
}
/// Used to emit a lint-related diagnostic based on the current state of
/// this lint context.
pub fn struct_lint(
&self,
lint: &'static Lint,
span: Option<MultiSpan>,
msg: &str,
) -> DiagnosticBuilder<'a> {
let (level, src) = self.sets.get_lint_level(lint, self.cur, None, self.sess);
struct_lint_level(self.sess, lint, level, src, span, msg)
}
/// Registers the ID provided with the current set of lints stored in
/// this context.
pub fn register_id(&mut self, id: HirId) {
self.id_to_set.insert(id, self.cur);
}
pub fn build(self) -> LintLevelSets {
self.sets
}
pub fn build_map(self) -> LintLevelMap {
LintLevelMap { sets: self.sets, id_to_set: self.id_to_set }
}
}
pub struct LintLevelMap {
sets: LintLevelSets,
id_to_set: FxHashMap<HirId, u32>,
pub sets: LintLevelSets,
pub id_to_set: FxHashMap<HirId, u32>,
}
impl LintLevelMap {
@ -712,3 +325,45 @@ pub fn in_external_macro(sess: &Session, span: Span) -> bool {
ExpnKind::Macro(..) => true, // definitely a plugin
}
}
pub fn add_elided_lifetime_in_path_suggestion(
sess: &Session,
db: &mut DiagnosticBuilder<'_>,
n: usize,
path_span: Span,
incl_angl_brckt: bool,
insertion_span: Span,
anon_lts: String,
) {
let (replace_span, suggestion) = if incl_angl_brckt {
(insertion_span, anon_lts)
} else {
// When possible, prefer a suggestion that replaces the whole
// `Path<T>` expression with `Path<'_, T>`, rather than inserting `'_, `
// at a point (which makes for an ugly/confusing label)
if let Ok(snippet) = sess.source_map().span_to_snippet(path_span) {
// But our spans can get out of whack due to macros; if the place we think
// we want to insert `'_` isn't even within the path expression's span, we
// should bail out of making any suggestion rather than panicking on a
// subtract-with-overflow or string-slice-out-out-bounds (!)
// FIXME: can we do better?
if insertion_span.lo().0 < path_span.lo().0 {
return;
}
let insertion_index = (insertion_span.lo().0 - path_span.lo().0) as usize;
if insertion_index > snippet.len() {
return;
}
let (before, after) = snippet.split_at(insertion_index);
(path_span, format!("{}{}{}", before, anon_lts, after))
} else {
(insertion_span, anon_lts)
}
};
db.span_suggestion(
replace_span,
&format!("indicate the anonymous lifetime{}", pluralize!(n)),
suggestion,
Applicability::MachineApplicable,
);
}

View file

@ -37,7 +37,6 @@
use rustc::dep_graph::DepGraph;
use rustc::hir::map::definitions::{DefKey, DefPathData, Definitions};
use rustc::hir::map::Map;
use rustc::lint::builtin;
use rustc::{bug, span_bug};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashSet;
@ -51,7 +50,7 @@
use rustc_hir::{ConstArg, GenericArg, ParamName};
use rustc_index::vec::IndexVec;
use rustc_session::config::nightly_options;
use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer};
use rustc_session::lint::{builtin, BuiltinLintDiagnostics, LintBuffer};
use rustc_session::node_id::NodeMap;
use rustc_session::Session;
use rustc_span::hygiene::ExpnId;

View file

@ -23,9 +23,7 @@
pub extern crate rustc_plugin_impl as plugin;
//use rustc_resolve as resolve;
use rustc::lint;
use rustc::lint::Lint;
use rustc::lint::{Lint, LintId};
use rustc::middle::cstore::MetadataLoader;
use rustc::session::config::nightly_options;
use rustc::session::config::{ErrorOutputType, Input, OutputType, PrintRequest};
@ -41,6 +39,7 @@
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_interface::util::get_builtin_codegen_backend;
use rustc_interface::{interface, Queries};
use rustc_lint::LintStore;
use rustc_metadata::locator;
use rustc_save_analysis as save;
use rustc_save_analysis::DumpHandler;
@ -811,7 +810,7 @@ fn print_wall_help() {
);
}
fn describe_lints(sess: &Session, lint_store: &lint::LintStore, loaded_plugins: bool) {
fn describe_lints(sess: &Session, lint_store: &LintStore, loaded_plugins: bool) {
println!(
"
Available lint options:
@ -832,8 +831,8 @@ fn sort_lints(sess: &Session, mut lints: Vec<&'static Lint>) -> Vec<&'static Lin
}
fn sort_lint_groups(
lints: Vec<(&'static str, Vec<lint::LintId>, bool)>,
) -> Vec<(&'static str, Vec<lint::LintId>)> {
lints: Vec<(&'static str, Vec<LintId>, bool)>,
) -> Vec<(&'static str, Vec<LintId>)> {
let mut lints: Vec<_> = lints.into_iter().map(|(x, y, _)| (x, y)).collect();
lints.sort_by_key(|l| l.0);
lints
@ -892,7 +891,7 @@ fn sort_lint_groups(
println!(" {} {}", padded("----"), "---------");
println!(" {} {}", padded("warnings"), "all lints that are set to issue warnings");
let print_lint_groups = |lints: Vec<(&'static str, Vec<lint::LintId>)>| {
let print_lint_groups = |lints: Vec<(&'static str, Vec<LintId>)>| {
for (name, to) in lints {
let name = name.to_lowercase().replace("_", "-");
let desc = to

View file

@ -12,6 +12,7 @@
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::OnDrop;
use rustc_errors::registry::Registry;
use rustc_lint::LintStore;
use rustc_parse::new_parser_from_source_str;
use rustc_span::edition;
use rustc_span::source_map::{FileLoader, FileName, SourceMap};
@ -36,7 +37,7 @@ pub struct Compiler {
pub(crate) output_dir: Option<PathBuf>,
pub(crate) output_file: Option<PathBuf>,
pub(crate) crate_name: Option<String>,
pub(crate) register_lints: Option<Box<dyn Fn(&Session, &mut lint::LintStore) + Send + Sync>>,
pub(crate) register_lints: Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>,
pub(crate) override_queries:
Option<fn(&Session, &mut ty::query::Providers<'_>, &mut ty::query::Providers<'_>)>,
}
@ -136,7 +137,7 @@ pub struct Config {
///
/// Note that if you find a Some here you probably want to call that function in the new
/// function being registered.
pub register_lints: Option<Box<dyn Fn(&Session, &mut lint::LintStore) + Send + Sync>>,
pub register_lints: Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>,
/// This is a callback from the driver that is called just after we have populated
/// the list of queries.

View file

@ -27,6 +27,7 @@
use rustc_expand::base::ExtCtxt;
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_incremental;
use rustc_lint::LintStore;
use rustc_mir as mir;
use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str};
use rustc_passes::{self, hir_stats, layout_test};
@ -100,7 +101,7 @@ fn count_nodes(krate: &ast::Crate) -> usize {
/// Returns `None` if we're aborting after handling -W help.
pub fn configure_and_expand(
sess: Lrc<Session>,
lint_store: Lrc<lint::LintStore>,
lint_store: Lrc<LintStore>,
metadata_loader: Box<MetadataLoaderDyn>,
krate: ast::Crate,
crate_name: &str,
@ -150,10 +151,10 @@ pub fn to_resolver_outputs(resolver: Rc<RefCell<BoxedResolver>>) -> ResolverOutp
pub fn register_plugins<'a>(
sess: &'a Session,
metadata_loader: &'a dyn MetadataLoader,
register_lints: impl Fn(&Session, &mut lint::LintStore),
register_lints: impl Fn(&Session, &mut LintStore),
mut krate: ast::Crate,
crate_name: &str,
) -> Result<(ast::Crate, Lrc<lint::LintStore>)> {
) -> Result<(ast::Crate, Lrc<LintStore>)> {
krate = sess.time("attributes_injection", || {
rustc_builtin_macros::cmdline_attrs::inject(
krate,
@ -214,7 +215,7 @@ pub fn register_plugins<'a>(
fn configure_and_expand_inner<'a>(
sess: &'a Session,
lint_store: &'a lint::LintStore,
lint_store: &'a LintStore,
mut krate: ast::Crate,
crate_name: &str,
resolver_arenas: &'a ResolverArenas<'a>,
@ -420,7 +421,7 @@ fn configure_and_expand_inner<'a>(
pub fn lower_to_hir<'res, 'tcx>(
sess: &'tcx Session,
lint_store: &lint::LintStore,
lint_store: &LintStore,
resolver: &'res mut Resolver<'_>,
dep_graph: &'res DepGraph,
krate: &'res ast::Crate,
@ -705,7 +706,7 @@ pub fn print_stats(&self) {
pub fn create_global_ctxt<'tcx>(
compiler: &'tcx Compiler,
lint_store: Lrc<lint::LintStore>,
lint_store: Lrc<LintStore>,
hir_forest: &'tcx map::Forest<'tcx>,
mut resolver_outputs: ResolverOutputs,
outputs: OutputFilenames,

View file

@ -4,8 +4,6 @@
use rustc::arena::Arena;
use rustc::dep_graph::DepGraph;
use rustc::hir::map;
use rustc::lint;
use rustc::lint::LintStore;
use rustc::session::config::{OutputFilenames, OutputType};
use rustc::session::Session;
use rustc::ty::steal::Steal;
@ -15,6 +13,7 @@
use rustc_data_structures::sync::{Lrc, Once, WorkerLocal};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_incremental::DepGraphFuture;
use rustc_lint::LintStore;
use std::any::Any;
use std::cell::{Ref, RefCell, RefMut};
use std::mem;
@ -133,7 +132,7 @@ pub fn register_plugins(&self) -> Result<&Query<(ast::Crate, Lrc<LintStore>)>> {
let crate_name = self.crate_name()?.peek().clone();
let krate = self.parse()?.take();
let empty: &(dyn Fn(&Session, &mut lint::LintStore) + Sync + Send) = &|_, _| {};
let empty: &(dyn Fn(&Session, &mut LintStore) + Sync + Send) = &|_, _| {};
let result = passes::register_plugins(
self.session(),
&*self.codegen_backend().metadata_loader(),

View file

@ -1,4 +1,4 @@
use rustc::lint::{LateContext, LateLintPass, LintContext};
use crate::{LateContext, LateLintPass, LintContext};
use rustc::ty;
use rustc::ty::adjustment::{Adjust, Adjustment};
use rustc_errors::Applicability;

View file

@ -21,8 +21,8 @@
//! If you define a new `LateLintPass`, you will also need to add it to the
//! `late_lint_methods!` invocation in `lib.rs`.
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc::hir::map::Map;
use rustc::lint::{self, EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc::traits::misc::can_type_implement_copy;
use rustc::ty::{self, layout::VariantIdx, Ty, TyCtxt};
use rustc_data_structures::fx::FxHashSet;
@ -51,7 +51,7 @@
use std::fmt::Write;
// hardwired lints from librustc
pub use lint::builtin::*;
pub use rustc_session::lint::builtin::*;
declare_lint! {
WHILE_TRUE,

View file

@ -16,17 +16,18 @@
use self::TargetLint::*;
use crate::hir::map::definitions::{DefPathData, DisambiguatedDefPathData};
use crate::lint::passes::{EarlyLintPassObject, LateLintPassObject};
use crate::lint::LintLevelsBuilder;
use crate::middle::privacy::AccessLevels;
use crate::middle::stability;
use crate::ty::layout::{LayoutError, LayoutOf, TyLayout};
use crate::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt};
use crate::levels::LintLevelsBuilder;
use crate::passes::{EarlyLintPassObject, LateLintPassObject};
use rustc::hir::map::definitions::{DefPathData, DisambiguatedDefPathData};
use rustc::lint::add_elided_lifetime_in_path_suggestion;
use rustc::middle::privacy::AccessLevels;
use rustc::middle::stability;
use rustc::ty::layout::{LayoutError, LayoutOf, TyLayout};
use rustc::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync;
use rustc_error_codes::*;
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, DefId};
use rustc_session::lint::BuiltinLintDiagnostics;
@ -467,48 +468,6 @@ impl LintPassObject for EarlyLintPassObject {}
impl LintPassObject for LateLintPassObject {}
pub fn add_elided_lifetime_in_path_suggestion(
sess: &Session,
db: &mut DiagnosticBuilder<'_>,
n: usize,
path_span: Span,
incl_angl_brckt: bool,
insertion_span: Span,
anon_lts: String,
) {
let (replace_span, suggestion) = if incl_angl_brckt {
(insertion_span, anon_lts)
} else {
// When possible, prefer a suggestion that replaces the whole
// `Path<T>` expression with `Path<'_, T>`, rather than inserting `'_, `
// at a point (which makes for an ugly/confusing label)
if let Ok(snippet) = sess.source_map().span_to_snippet(path_span) {
// But our spans can get out of whack due to macros; if the place we think
// we want to insert `'_` isn't even within the path expression's span, we
// should bail out of making any suggestion rather than panicking on a
// subtract-with-overflow or string-slice-out-out-bounds (!)
// FIXME: can we do better?
if insertion_span.lo().0 < path_span.lo().0 {
return;
}
let insertion_index = (insertion_span.lo().0 - path_span.lo().0) as usize;
if insertion_index > snippet.len() {
return;
}
let (before, after) = snippet.split_at(insertion_index);
(path_span, format!("{}{}{}", before, anon_lts, after))
} else {
(insertion_span, anon_lts)
}
};
db.span_suggestion(
replace_span,
&format!("indicate the anonymous lifetime{}", pluralize!(n)),
suggestion,
Applicability::MachineApplicable,
);
}
pub trait LintContext: Sized {
type PassObject: LintPassObject;

View file

@ -14,10 +14,9 @@
//! upon. As the ast is traversed, this keeps track of the current lint level
//! for all lint attributes.
use rustc::lint::{EarlyContext, LintStore};
use rustc::lint::{EarlyLintPass, EarlyLintPassObject};
use rustc::lint::{LintContext, LintPass};
use rustc_session::lint::LintBuffer;
use crate::context::{EarlyContext, LintContext, LintStore};
use crate::passes::{EarlyLintPass, EarlyLintPassObject};
use rustc_session::lint::{LintBuffer, LintPass};
use rustc_session::Session;
use rustc_span::Span;
use syntax::ast;
@ -291,7 +290,7 @@ impl EarlyLintPass for EarlyLintPassObjects<'_> {
)
}
early_lint_methods!(early_lint_pass_impl, []);
crate::early_lint_methods!(early_lint_pass_impl, []);
fn early_lint_crate<T: EarlyLintPass>(
sess: &Session,

View file

@ -1,8 +1,7 @@
//! Some lints that are only useful in the compiler or crates that use compiler internals, such as
//! Clippy.
use crate::lint::{EarlyContext, LateContext, LintContext};
use crate::lint::{EarlyLintPass, LateLintPass};
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_hir::{GenericArg, HirId, MutTy, Mutability, Path, PathSegment, QPath, Ty, TyKind};

View file

@ -14,8 +14,8 @@
//! upon. As the ast is traversed, this keeps track of the current lint level
//! for all lint attributes.
use crate::{passes::LateLintPassObject, LateContext, LateLintPass, LintStore};
use rustc::hir::map::Map;
use rustc::lint::{LateContext, LateLintPass, LateLintPassObject, LintStore};
use rustc::ty::{self, TyCtxt};
use rustc_data_structures::sync::{join, par_iter, ParallelIterator};
use rustc_hir as hir;
@ -347,7 +347,7 @@ impl<'a, $hir> LateLintPass<'a, $hir> for LateLintPassObjects<'_> {
)
}
late_lint_methods!(late_lint_pass_impl, [], ['tcx]);
crate::late_lint_methods!(late_lint_pass_impl, [], ['tcx]);
fn late_lint_mod_pass<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>(
tcx: TyCtxt<'tcx>,

View file

@ -1,14 +1,27 @@
use super::late::unerased_lint_store;
use crate::context::{CheckLintNameResult, LintStore};
use crate::late::unerased_lint_store;
use rustc::hir::map::Map;
use rustc::lint::{LintLevelMap, LintLevelsBuilder, LintStore};
use rustc::lint::struct_lint_level;
use rustc::lint::{LintLevelMap, LintLevelSets, LintSet, LintSource};
use rustc::ty::query::Providers;
use rustc::ty::TyCtxt;
use rustc_data_structures::fx::FxHashMap;
use rustc_error_codes::*;
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_hir::hir_id::HirId;
use rustc_hir::intravisit;
use rustc_session::lint::{builtin, Level, Lint};
use rustc_session::Session;
use rustc_span::{sym, MultiSpan, Symbol};
use syntax::ast;
use syntax::attr;
use syntax::print::pprust;
use syntax::sess::feature_err;
use syntax::unwrap_or;
pub use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintId};
use std::cmp;
fn lint_levels(tcx: TyCtxt<'_>, cnum: CrateNum) -> &LintLevelMap {
assert_eq!(cnum, LOCAL_CRATE);
@ -28,6 +41,378 @@ fn lint_levels(tcx: TyCtxt<'_>, cnum: CrateNum) -> &LintLevelMap {
tcx.arena.alloc(builder.levels.build_map())
}
pub struct LintLevelsBuilder<'a> {
sess: &'a Session,
sets: LintLevelSets,
id_to_set: FxHashMap<HirId, u32>,
cur: u32,
warn_about_weird_lints: bool,
}
pub struct BuilderPush {
prev: u32,
pub changed: bool,
}
impl<'a> LintLevelsBuilder<'a> {
pub fn new(sess: &'a Session, warn_about_weird_lints: bool, store: &LintStore) -> Self {
let mut builder = LintLevelsBuilder {
sess,
sets: LintLevelSets::new(),
cur: 0,
id_to_set: Default::default(),
warn_about_weird_lints,
};
builder.process_command_line(sess, store);
assert_eq!(builder.sets.list.len(), 1);
builder
}
fn process_command_line(&mut self, sess: &Session, store: &LintStore) {
let mut specs = FxHashMap::default();
self.sets.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid);
for &(ref lint_name, level) in &sess.opts.lint_opts {
store.check_lint_name_cmdline(sess, &lint_name, level);
// If the cap is less than this specified level, e.g., if we've got
// `--cap-lints allow` but we've also got `-D foo` then we ignore
// this specification as the lint cap will set it to allow anyway.
let level = cmp::min(level, self.sets.lint_cap);
let lint_flag_val = Symbol::intern(lint_name);
let ids = match store.find_lints(&lint_name) {
Ok(ids) => ids,
Err(_) => continue, // errors handled in check_lint_name_cmdline above
};
for id in ids {
let src = LintSource::CommandLine(lint_flag_val);
specs.insert(id, (level, src));
}
}
self.sets.list.push(LintSet::CommandLine { specs });
}
/// Pushes a list of AST lint attributes onto this context.
///
/// This function will return a `BuilderPush` object which should be passed
/// to `pop` when this scope for the attributes provided is exited.
///
/// This function will perform a number of tasks:
///
/// * It'll validate all lint-related attributes in `attrs`
/// * It'll mark all lint-related attributes as used
/// * Lint levels will be updated based on the attributes provided
/// * Lint attributes are validated, e.g., a #[forbid] can't be switched to
/// #[allow]
///
/// Don't forget to call `pop`!
pub fn push(&mut self, attrs: &[ast::Attribute], store: &LintStore) -> BuilderPush {
let mut specs = FxHashMap::default();
let sess = self.sess;
let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input");
for attr in attrs {
let level = match Level::from_symbol(attr.name_or_empty()) {
None => continue,
Some(lvl) => lvl,
};
let meta = unwrap_or!(attr.meta(), continue);
attr::mark_used(attr);
let mut metas = unwrap_or!(meta.meta_item_list(), continue);
if metas.is_empty() {
// FIXME (#55112): issue unused-attributes lint for `#[level()]`
continue;
}
// Before processing the lint names, look for a reason (RFC 2383)
// at the end.
let mut reason = None;
let tail_li = &metas[metas.len() - 1];
if let Some(item) = tail_li.meta_item() {
match item.kind {
ast::MetaItemKind::Word => {} // actual lint names handled later
ast::MetaItemKind::NameValue(ref name_value) => {
if item.path == sym::reason {
// found reason, reslice meta list to exclude it
metas = &metas[0..metas.len() - 1];
// FIXME (#55112): issue unused-attributes lint if we thereby
// don't have any lint names (`#[level(reason = "foo")]`)
if let ast::LitKind::Str(rationale, _) = name_value.kind {
if !self.sess.features_untracked().lint_reasons {
feature_err(
&self.sess.parse_sess,
sym::lint_reasons,
item.span,
"lint reasons are experimental",
)
.emit();
}
reason = Some(rationale);
} else {
bad_attr(name_value.span)
.span_label(name_value.span, "reason must be a string literal")
.emit();
}
} else {
bad_attr(item.span)
.span_label(item.span, "bad attribute argument")
.emit();
}
}
ast::MetaItemKind::List(_) => {
bad_attr(item.span).span_label(item.span, "bad attribute argument").emit();
}
}
}
for li in metas {
let meta_item = match li.meta_item() {
Some(meta_item) if meta_item.is_word() => meta_item,
_ => {
let sp = li.span();
let mut err = bad_attr(sp);
let mut add_label = true;
if let Some(item) = li.meta_item() {
if let ast::MetaItemKind::NameValue(_) = item.kind {
if item.path == sym::reason {
err.span_label(sp, "reason in lint attribute must come last");
add_label = false;
}
}
}
if add_label {
err.span_label(sp, "bad attribute argument");
}
err.emit();
continue;
}
};
let tool_name = if meta_item.path.segments.len() > 1 {
let tool_ident = meta_item.path.segments[0].ident;
if !attr::is_known_lint_tool(tool_ident) {
struct_span_err!(
sess,
tool_ident.span,
E0710,
"an unknown tool name found in scoped lint: `{}`",
pprust::path_to_string(&meta_item.path),
)
.emit();
continue;
}
Some(tool_ident.name)
} else {
None
};
let name = meta_item.path.segments.last().expect("empty lint name").ident.name;
match store.check_lint_name(&name.as_str(), tool_name) {
CheckLintNameResult::Ok(ids) => {
let src = LintSource::Node(name, li.span(), reason);
for id in ids {
specs.insert(*id, (level, src));
}
}
CheckLintNameResult::Tool(result) => {
match result {
Ok(ids) => {
let complete_name = &format!("{}::{}", tool_name.unwrap(), name);
let src = LintSource::Node(
Symbol::intern(complete_name),
li.span(),
reason,
);
for id in ids {
specs.insert(*id, (level, src));
}
}
Err((Some(ids), new_lint_name)) => {
let lint = builtin::RENAMED_AND_REMOVED_LINTS;
let (lvl, src) =
self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess);
let msg = format!(
"lint name `{}` is deprecated \
and may not have an effect in the future. \
Also `cfg_attr(cargo-clippy)` won't be necessary anymore",
name
);
struct_lint_level(
self.sess,
lint,
lvl,
src,
Some(li.span().into()),
&msg,
)
.span_suggestion(
li.span(),
"change it to",
new_lint_name.to_string(),
Applicability::MachineApplicable,
)
.emit();
let src = LintSource::Node(
Symbol::intern(&new_lint_name),
li.span(),
reason,
);
for id in ids {
specs.insert(*id, (level, src));
}
}
Err((None, _)) => {
// If Tool(Err(None, _)) is returned, then either the lint does not
// exist in the tool or the code was not compiled with the tool and
// therefore the lint was never added to the `LintStore`. To detect
// this is the responsibility of the lint tool.
}
}
}
_ if !self.warn_about_weird_lints => {}
CheckLintNameResult::Warning(msg, renamed) => {
let lint = builtin::RENAMED_AND_REMOVED_LINTS;
let (level, src) =
self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess);
let mut err = struct_lint_level(
self.sess,
lint,
level,
src,
Some(li.span().into()),
&msg,
);
if let Some(new_name) = renamed {
err.span_suggestion(
li.span(),
"use the new name",
new_name,
Applicability::MachineApplicable,
);
}
err.emit();
}
CheckLintNameResult::NoLint(suggestion) => {
let lint = builtin::UNKNOWN_LINTS;
let (level, src) =
self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess);
let msg = format!("unknown lint: `{}`", name);
let mut db = struct_lint_level(
self.sess,
lint,
level,
src,
Some(li.span().into()),
&msg,
);
if let Some(suggestion) = suggestion {
db.span_suggestion(
li.span(),
"did you mean",
suggestion.to_string(),
Applicability::MachineApplicable,
);
}
db.emit();
}
}
}
}
for (id, &(level, ref src)) in specs.iter() {
if level == Level::Forbid {
continue;
}
let forbid_src = match self.sets.get_lint_id_level(*id, self.cur, None) {
(Some(Level::Forbid), src) => src,
_ => continue,
};
let forbidden_lint_name = match forbid_src {
LintSource::Default => id.to_string(),
LintSource::Node(name, _, _) => name.to_string(),
LintSource::CommandLine(name) => name.to_string(),
};
let (lint_attr_name, lint_attr_span) = match *src {
LintSource::Node(name, span, _) => (name, span),
_ => continue,
};
let mut diag_builder = struct_span_err!(
self.sess,
lint_attr_span,
E0453,
"{}({}) overruled by outer forbid({})",
level.as_str(),
lint_attr_name,
forbidden_lint_name
);
diag_builder.span_label(lint_attr_span, "overruled by previous forbid");
match forbid_src {
LintSource::Default => {}
LintSource::Node(_, forbid_source_span, reason) => {
diag_builder.span_label(forbid_source_span, "`forbid` level set here");
if let Some(rationale) = reason {
diag_builder.note(&rationale.as_str());
}
}
LintSource::CommandLine(_) => {
diag_builder.note("`forbid` lint level was set on command line");
}
}
diag_builder.emit();
// don't set a separate error for every lint in the group
break;
}
let prev = self.cur;
if specs.len() > 0 {
self.cur = self.sets.list.len() as u32;
self.sets.list.push(LintSet::Node { specs: specs, parent: prev });
}
BuilderPush { prev: prev, changed: prev != self.cur }
}
/// Called after `push` when the scope of a set of attributes are exited.
pub fn pop(&mut self, push: BuilderPush) {
self.cur = push.prev;
}
/// Used to emit a lint-related diagnostic based on the current state of
/// this lint context.
pub fn struct_lint(
&self,
lint: &'static Lint,
span: Option<MultiSpan>,
msg: &str,
) -> DiagnosticBuilder<'a> {
let (level, src) = self.sets.get_lint_level(lint, self.cur, None, self.sess);
struct_lint_level(self.sess, lint, level, src, span, msg)
}
/// Registers the ID provided with the current set of lints stored in
/// this context.
pub fn register_id(&mut self, id: HirId) {
self.id_to_set.insert(id, self.cur);
}
pub fn build(self) -> LintLevelSets {
self.sets
}
pub fn build_map(self) -> LintLevelMap {
LintLevelMap { sets: self.sets, id_to_set: self.id_to_set }
}
}
struct LintLevelMapBuilder<'a, 'tcx> {
levels: LintLevelsBuilder<'tcx>,
tcx: TyCtxt<'tcx>,

View file

@ -15,6 +15,7 @@
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(crate_visibility_modifier)]
#![feature(never_type)]
#![feature(nll)]
#![recursion_limit = "256"]
@ -25,33 +26,29 @@
mod array_into_iter;
pub mod builtin;
mod context;
mod early;
mod internal;
mod late;
mod levels;
mod non_ascii_idents;
mod nonstandard_style;
mod passes;
mod redundant_semicolon;
mod types;
mod unused;
use rustc::lint;
use rustc::lint::builtin::{
BARE_TRAIT_OBJECTS, ELIDED_LIFETIMES_IN_PATHS, EXPLICIT_OUTLIVES_REQUIREMENTS,
INTRA_DOC_LINK_RESOLUTION_FAILURE, MISSING_DOC_CODE_EXAMPLES, PRIVATE_DOC_TESTS,
};
use rustc::lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
use rustc::ty::query::Providers;
use rustc::ty::TyCtxt;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_session::lint::{LintArray, LintPass};
use rustc_session::lint::builtin::{
BARE_TRAIT_OBJECTS, ELIDED_LIFETIMES_IN_PATHS, EXPLICIT_OUTLIVES_REQUIREMENTS,
INTRA_DOC_LINK_RESOLUTION_FAILURE, MISSING_DOC_CODE_EXAMPLES, PRIVATE_DOC_TESTS,
};
use rustc_span::Span;
use syntax::ast;
use lint::LintId;
use array_into_iter::ArrayIntoIter;
use builtin::*;
use internal::*;
@ -61,10 +58,15 @@
use types::*;
use unused::*;
/// Useful for other parts of the compiler.
/// Useful for other parts of the compiler / Clippy.
pub use builtin::SoftLints;
pub use context::{EarlyContext, LateContext, LintContext, LintStore};
pub use early::check_ast_crate;
pub use late::check_crate;
pub use passes::{EarlyLintPass, LateLintPass};
pub use rustc_session::lint::Level::{self, *};
pub use rustc_session::lint::{BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId};
pub use rustc_session::lint::{LintArray, LintPass};
pub fn provide(providers: &mut Providers<'_>) {
levels::provide(providers);
@ -181,8 +183,8 @@ macro_rules! declare_combined_late_pass {
late_lint_mod_passes!(declare_combined_late_pass, [BuiltinCombinedModuleLateLintPass]);
pub fn new_lint_store(no_interleave_lints: bool, internal_lints: bool) -> lint::LintStore {
let mut lint_store = lint::LintStore::new();
pub fn new_lint_store(no_interleave_lints: bool, internal_lints: bool) -> LintStore {
let mut lint_store = LintStore::new();
register_builtins(&mut lint_store, no_interleave_lints);
if internal_lints {
@ -195,7 +197,7 @@ pub fn new_lint_store(no_interleave_lints: bool, internal_lints: bool) -> lint::
/// Tell the `LintStore` about all the built-in lints (the ones
/// defined in this crate and the ones defined in
/// `rustc::lint::builtin`).
fn register_builtins(store: &mut lint::LintStore, no_interleave_lints: bool) {
fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
macro_rules! add_lint_group {
($name:expr, $($lint:ident),*) => (
store.register_group(false, $name, None, vec![$(LintId::of($lint)),*]);
@ -392,7 +394,7 @@ macro_rules! register_passes {
store.register_removed("plugin_as_library", "plugins have been deprecated and retired");
}
fn register_internals(store: &mut lint::LintStore) {
fn register_internals(store: &mut LintStore) {
store.register_lints(&DefaultHashTypes::get_lints());
store.register_early_pass(|| box DefaultHashTypes::new());
store.register_lints(&LintPassImpl::get_lints());

View file

@ -1,4 +1,4 @@
use rustc::lint::{EarlyContext, EarlyLintPass, LintContext};
use crate::{EarlyContext, EarlyLintPass, LintContext};
use syntax::ast;
declare_lint! {

View file

@ -1,4 +1,4 @@
use rustc::lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc::ty;
use rustc_errors::Applicability;
use rustc_hir as hir;

View file

@ -18,23 +18,15 @@
//! example) requires more effort. See `emit_lint` and `GatherNodeLevels`
//! in `context.rs`.
pub use self::Level::*;
pub use crate::lint::LintSource::{self, *};
use crate::context::{EarlyContext, LateContext};
use rustc_data_structures::sync;
use rustc_hir as hir;
use rustc_session::lint::builtin::HardwiredLints;
use rustc_session::lint::LintPass;
use rustc_span::Span;
use syntax::ast;
pub use crate::lint::context::{
add_elided_lifetime_in_path_suggestion, CheckLintNameResult, EarlyContext, LateContext,
LintContext, LintStore,
};
pub use rustc_session::lint::{builtin, LintArray, LintPass};
pub use rustc_session::lint::{BufferedEarlyLint, FutureIncompatibleInfo, Level, Lint, LintId};
#[macro_export]
macro_rules! late_lint_methods {
($macro:path, $args:tt, [$hir:tt]) => (
@ -169,6 +161,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for $name {
expand_combined_late_lint_pass_methods!([$($passes),*], $methods);
}
#[allow(rustc::lint_pass_impl_without_macro)]
impl LintPass for $name {
fn name(&self) -> &'static str {
panic!()
@ -296,6 +289,7 @@ impl EarlyLintPass for $name {
expand_combined_early_lint_pass_methods!([$($passes),*], $methods);
}
#[allow(rustc::lint_pass_impl_without_macro)]
impl LintPass for $name {
fn name(&self) -> &'static str {
panic!()

View file

@ -1,4 +1,4 @@
use rustc::lint::{EarlyContext, EarlyLintPass, LintContext};
use crate::{EarlyContext, EarlyLintPass, LintContext};
use rustc_errors::Applicability;
use syntax::ast::{ExprKind, Stmt, StmtKind};

View file

@ -1,6 +1,6 @@
#![allow(non_snake_case)]
use rustc::lint::{LateContext, LateLintPass, LintContext};
use crate::{LateContext, LateLintPass, LintContext};
use rustc::mir::interpret::{sign_extend, truncate};
use rustc::ty::layout::{self, IntegerExt, LayoutOf, SizeSkeleton, VariantIdx};
use rustc::ty::subst::SubstsRef;

View file

@ -1,5 +1,4 @@
use rustc::lint::builtin::UNUSED_ATTRIBUTES;
use rustc::lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc::ty::adjustment;
use rustc::ty::{self, Ty};
use rustc_data_structures::fx::FxHashMap;
@ -8,6 +7,7 @@
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_session::lint::builtin::UNUSED_ATTRIBUTES;
use rustc_span::symbol::Symbol;
use rustc_span::symbol::{kw, sym};
use rustc_span::{BytePos, Span};

View file

@ -14,6 +14,7 @@ doctest = false
rustc = { path = "../librustc" }
rustc_errors = { path = "../librustc_errors" }
rustc_hir = { path = "../librustc_hir" }
rustc_lint = { path = "../librustc_lint" }
rustc_metadata = { path = "../librustc_metadata" }
syntax = { path = "../libsyntax" }
rustc_span = { path = "../librustc_span" }

View file

@ -9,7 +9,7 @@
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
#![feature(nll)]
use rustc::lint::LintStore;
use rustc_lint::LintStore;
pub mod build;
pub mod load;