1 // Copyright 2017 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.
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.
13 use errors::DiagnosticBuilder;
16 use lint::context::CheckLintNameResult;
17 use lint::{self, Lint, LintId, Level, LintSource};
21 use syntax::codemap::MultiSpan;
22 use syntax::symbol::Symbol;
23 use util::nodemap::FxHashMap;
25 pub struct LintLevelSets {
32 // -A,-W,-D flags, a `Symbol` for the flag itself and `Level` for which
34 specs: FxHashMap<LintId, (Level, LintSource)>,
38 specs: FxHashMap<LintId, (Level, LintSource)>,
44 pub fn new(sess: &Session) -> LintLevelSets {
45 let mut me = LintLevelSets {
47 lint_cap: Level::Forbid,
49 me.process_command_line(sess);
53 pub fn builder(sess: &Session) -> LintLevelsBuilder {
54 LintLevelsBuilder::new(sess, LintLevelSets::new(sess))
57 fn process_command_line(&mut self, sess: &Session) {
58 let store = sess.lint_store.borrow();
59 let mut specs = FxHashMap();
60 self.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid);
62 for &(ref lint_name, level) in &sess.opts.lint_opts {
63 store.check_lint_name_cmdline(sess, &lint_name, level);
65 // If the cap is less than this specified level, e.g. if we've got
66 // `--cap-lints allow` but we've also got `-D foo` then we ignore
67 // this specification as the lint cap will set it to allow anyway.
68 let level = cmp::min(level, self.lint_cap);
70 let lint_flag_val = Symbol::intern(lint_name);
71 let ids = match store.find_lints(&lint_name) {
73 Err(_) => continue, // errors handled in check_lint_name_cmdline above
76 let src = LintSource::CommandLine(lint_flag_val);
77 specs.insert(id, (level, src));
81 self.list.push(LintSet::CommandLine {
86 fn get_lint_level(&self,
89 aux: Option<&FxHashMap<LintId, (Level, LintSource)>>)
90 -> (Level, LintSource)
92 let (level, mut src) = self.get_lint_id_level(LintId::of(lint), idx, aux);
94 // If `level` is none then we actually assume the default level for this
96 let mut level = level.unwrap_or(lint.default_level);
98 // If we're about to issue a warning, check at the last minute for any
99 // directives against the warnings "lint". If, for example, there's an
100 // `allow(warnings)` in scope then we want to respect that instead.
101 if level == Level::Warn {
102 let (warnings_level, warnings_src) =
103 self.get_lint_id_level(LintId::of(lint::builtin::WARNINGS),
106 if let Some(configured_warning_level) = warnings_level {
107 if configured_warning_level != Level::Warn {
108 level = configured_warning_level;
114 // Ensure that we never exceed the `--cap-lints` argument.
115 level = cmp::min(level, self.lint_cap);
120 fn get_lint_id_level(&self,
123 aux: Option<&FxHashMap<LintId, (Level, LintSource)>>)
124 -> (Option<Level>, LintSource)
126 if let Some(specs) = aux {
127 if let Some(&(level, src)) = specs.get(&id) {
128 return (Some(level), src)
132 match self.list[idx as usize] {
133 LintSet::CommandLine { ref specs } => {
134 if let Some(&(level, src)) = specs.get(&id) {
135 return (Some(level), src)
137 return (None, LintSource::Default)
139 LintSet::Node { ref specs, parent } => {
140 if let Some(&(level, src)) = specs.get(&id) {
141 return (Some(level), src)
150 pub struct LintLevelsBuilder<'a> {
153 id_to_set: FxHashMap<HirId, u32>,
155 warn_about_weird_lints: bool,
158 pub struct BuilderPush {
162 impl<'a> LintLevelsBuilder<'a> {
163 pub fn new(sess: &'a Session, sets: LintLevelSets) -> LintLevelsBuilder<'a> {
164 assert_eq!(sets.list.len(), 1);
169 id_to_set: FxHashMap(),
170 warn_about_weird_lints: sess.buffered_lints.borrow().is_some(),
174 /// Pushes a list of AST lint attributes onto this context.
176 /// This function will return a `BuilderPush` object which should be be
177 /// passed to `pop` when this scope for the attributes provided is exited.
179 /// This function will perform a number of tasks:
181 /// * It'll validate all lint-related attributes in `attrs`
182 /// * It'll mark all lint-related attriutes as used
183 /// * Lint levels will be updated based on the attributes provided
184 /// * Lint attributes are validated, e.g. a #[forbid] can't be switched to
187 /// Don't forget to call `pop`!
188 pub fn push(&mut self, attrs: &[ast::Attribute]) -> BuilderPush {
189 let mut specs = FxHashMap();
190 let store = self.sess.lint_store.borrow();
191 let sess = self.sess;
192 let bad_attr = |span| {
193 span_err!(sess, span, E0452,
194 "malformed lint attribute");
197 let level = match attr.name().and_then(|name| Level::from_str(&name.as_str())) {
202 let meta = unwrap_or!(attr.meta(), continue);
203 attr::mark_used(attr);
205 let metas = if let Some(metas) = meta.meta_item_list() {
213 let word = match li.word() {
220 let name = word.name();
221 match store.check_lint_name(&name.as_str()) {
222 CheckLintNameResult::Ok(ids) => {
223 let src = LintSource::Node(name, li.span);
225 specs.insert(*id, (level, src));
229 _ if !self.warn_about_weird_lints => {}
231 CheckLintNameResult::Warning(ref msg) => {
232 let lint = builtin::RENAMED_AND_REMOVED_LINTS;
233 let (level, src) = self.sets.get_lint_level(lint,
236 lint::struct_lint_level(self.sess,
240 Some(li.span.into()),
244 CheckLintNameResult::NoLint => {
245 let lint = builtin::UNKNOWN_LINTS;
246 let (level, src) = self.sets.get_lint_level(lint,
249 let msg = format!("unknown lint: `{}`", name);
250 let mut db = lint::struct_lint_level(self.sess,
254 Some(li.span.into()),
256 if name.as_str().chars().any(|c| c.is_uppercase()) {
257 let name_lower = name.as_str().to_lowercase();
258 if let CheckLintNameResult::NoLint =
259 store.check_lint_name(&name_lower) {
264 "lowercase the lint name",
276 for (id, &(level, ref src)) in specs.iter() {
277 if level == Level::Forbid {
280 let forbid_src = match self.sets.get_lint_id_level(*id, self.cur, None) {
281 (Some(Level::Forbid), src) => src,
284 let forbidden_lint_name = match forbid_src {
285 LintSource::Default => id.to_string(),
286 LintSource::Node(name, _) => name.to_string(),
287 LintSource::CommandLine(name) => name.to_string(),
289 let (lint_attr_name, lint_attr_span) = match *src {
290 LintSource::Node(name, span) => (name, span),
293 let mut diag_builder = struct_span_err!(self.sess,
296 "{}({}) overruled by outer forbid({})",
299 forbidden_lint_name);
300 diag_builder.span_label(lint_attr_span, "overruled by previous forbid");
302 LintSource::Default => &mut diag_builder,
303 LintSource::Node(_, forbid_source_span) => {
304 diag_builder.span_label(forbid_source_span,
305 "`forbid` level set here")
307 LintSource::CommandLine(_) => {
308 diag_builder.note("`forbid` lint level was set on command line")
311 // don't set a separate error for every lint in the group
317 self.cur = self.sets.list.len() as u32;
318 self.sets.list.push(LintSet::Node {
329 /// Called after `push` when the scope of a set of attributes are exited.
330 pub fn pop(&mut self, push: BuilderPush) {
331 self.cur = push.prev;
334 /// Used to emit a lint-related diagnostic based on the current state of
335 /// this lint context.
336 pub fn struct_lint(&self,
338 span: Option<MultiSpan>,
340 -> DiagnosticBuilder<'a>
342 let (level, src) = self.sets.get_lint_level(lint, self.cur, None);
343 lint::struct_lint_level(self.sess, lint, level, src, span, msg)
346 /// Registers the ID provided with the current set of lints stored in
348 pub fn register_id(&mut self, id: HirId) {
349 self.id_to_set.insert(id, self.cur);
352 pub fn build(self) -> LintLevelSets {
356 pub fn build_map(self) -> LintLevelMap {
359 id_to_set: self.id_to_set,
364 pub struct LintLevelMap {
366 id_to_set: FxHashMap<HirId, u32>,
370 /// If the `id` was previously registered with `register_id` when building
371 /// this `LintLevelMap` this returns the corresponding lint level and source
372 /// of the lint level for the lint provided.
374 /// If the `id` was not previously registered, returns `None`. If `None` is
375 /// returned then the parent of `id` should be acquired and this function
376 /// should be called again.
377 pub fn level_and_source(&self, lint: &'static Lint, id: HirId)
378 -> Option<(Level, LintSource)>
380 self.id_to_set.get(&id).map(|idx| {
381 self.sets.get_lint_level(lint, *idx, None)