Rollup merge of #86639 - eholk:lint-tool, r=petrochenkov
Support lint tool names in rustc command line options When rustc is running without a lint tool such as clippy enabled, options for lints such as `clippy::foo` are meant to be ignored. This was already working for those specified by attrs, such as `#![allow(clippy::foo)]`, but this did not work for command line arguments like `-A clippy::foo`. This PR fixes that issue. Note that we discovered this issue while discussing https://github.com/rust-lang/cargo/issues/5034. Fixes #86628.
This commit is contained in:
commit
c2d3f5f772
7 changed files with 123 additions and 34 deletions
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
use self::TargetLint::*;
|
use self::TargetLint::*;
|
||||||
|
|
||||||
use crate::levels::LintLevelsBuilder;
|
use crate::levels::{is_known_lint_tool, LintLevelsBuilder};
|
||||||
use crate::passes::{EarlyLintPassObject, LateLintPassObject};
|
use crate::passes::{EarlyLintPassObject, LateLintPassObject};
|
||||||
use rustc_ast as ast;
|
use rustc_ast as ast;
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
|
@ -129,6 +129,8 @@ pub enum CheckLintNameResult<'a> {
|
||||||
Ok(&'a [LintId]),
|
Ok(&'a [LintId]),
|
||||||
/// Lint doesn't exist. Potentially contains a suggestion for a correct lint name.
|
/// Lint doesn't exist. Potentially contains a suggestion for a correct lint name.
|
||||||
NoLint(Option<Symbol>),
|
NoLint(Option<Symbol>),
|
||||||
|
/// The lint refers to a tool that has not been registered.
|
||||||
|
NoTool,
|
||||||
/// The lint is either renamed or removed. This is the warning
|
/// The lint is either renamed or removed. This is the warning
|
||||||
/// message, and an optional new name (`None` if removed).
|
/// message, and an optional new name (`None` if removed).
|
||||||
Warning(String, Option<String>),
|
Warning(String, Option<String>),
|
||||||
|
@ -321,9 +323,17 @@ impl LintStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks the validity of lint names derived from the command line
|
/// Checks the validity of lint names derived from the command line.
|
||||||
pub fn check_lint_name_cmdline(&self, sess: &Session, lint_name: &str, level: Level) {
|
pub fn check_lint_name_cmdline(
|
||||||
let db = match self.check_lint_name(lint_name, None) {
|
&self,
|
||||||
|
sess: &Session,
|
||||||
|
lint_name: &str,
|
||||||
|
level: Level,
|
||||||
|
crate_attrs: &[ast::Attribute],
|
||||||
|
) {
|
||||||
|
let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
|
||||||
|
|
||||||
|
let db = match self.check_lint_name(sess, lint_name_only, tool_name, crate_attrs) {
|
||||||
CheckLintNameResult::Ok(_) => None,
|
CheckLintNameResult::Ok(_) => None,
|
||||||
CheckLintNameResult::Warning(ref msg, _) => Some(sess.struct_warn(msg)),
|
CheckLintNameResult::Warning(ref msg, _) => Some(sess.struct_warn(msg)),
|
||||||
CheckLintNameResult::NoLint(suggestion) => {
|
CheckLintNameResult::NoLint(suggestion) => {
|
||||||
|
@ -345,6 +355,13 @@ impl LintStore {
|
||||||
))),
|
))),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
|
CheckLintNameResult::NoTool => Some(struct_span_err!(
|
||||||
|
sess,
|
||||||
|
DUMMY_SP,
|
||||||
|
E0602,
|
||||||
|
"unknown lint tool: `{}`",
|
||||||
|
tool_name.unwrap()
|
||||||
|
)),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(mut db) = db {
|
if let Some(mut db) = db {
|
||||||
|
@ -387,9 +404,17 @@ impl LintStore {
|
||||||
/// printing duplicate warnings.
|
/// printing duplicate warnings.
|
||||||
pub fn check_lint_name(
|
pub fn check_lint_name(
|
||||||
&self,
|
&self,
|
||||||
|
sess: &Session,
|
||||||
lint_name: &str,
|
lint_name: &str,
|
||||||
tool_name: Option<Symbol>,
|
tool_name: Option<Symbol>,
|
||||||
|
crate_attrs: &[ast::Attribute],
|
||||||
) -> CheckLintNameResult<'_> {
|
) -> CheckLintNameResult<'_> {
|
||||||
|
if let Some(tool_name) = tool_name {
|
||||||
|
if !is_known_lint_tool(tool_name, sess, crate_attrs) {
|
||||||
|
return CheckLintNameResult::NoTool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let complete_name = if let Some(tool_name) = tool_name {
|
let complete_name = if let Some(tool_name) = tool_name {
|
||||||
format!("{}::{}", tool_name, lint_name)
|
format!("{}::{}", tool_name, lint_name)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1005,3 +1030,14 @@ impl<'tcx> LayoutOf for LateContext<'tcx> {
|
||||||
self.tcx.layout_of(self.param_env.and(ty))
|
self.tcx.layout_of(self.param_env.and(ty))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {
|
||||||
|
match lint_name.split_once("::") {
|
||||||
|
Some((tool_name, lint_name)) => {
|
||||||
|
let tool_name = Symbol::intern(tool_name);
|
||||||
|
|
||||||
|
(Some(tool_name), lint_name)
|
||||||
|
}
|
||||||
|
None => (None, lint_name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ impl<'s> LintLevelsBuilder<'s> {
|
||||||
self.sets.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid);
|
self.sets.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid);
|
||||||
|
|
||||||
for &(ref lint_name, level) in &sess.opts.lint_opts {
|
for &(ref lint_name, level) in &sess.opts.lint_opts {
|
||||||
store.check_lint_name_cmdline(sess, &lint_name, level);
|
store.check_lint_name_cmdline(sess, &lint_name, level, self.crate_attrs);
|
||||||
let orig_level = level;
|
let orig_level = level;
|
||||||
|
|
||||||
// If the cap is less than this specified level, e.g., if we've got
|
// If the cap is less than this specified level, e.g., if we've got
|
||||||
|
@ -111,7 +111,7 @@ impl<'s> LintLevelsBuilder<'s> {
|
||||||
}
|
}
|
||||||
|
|
||||||
for lint_name in &sess.opts.force_warns {
|
for lint_name in &sess.opts.force_warns {
|
||||||
store.check_lint_name_cmdline(sess, lint_name, Level::ForceWarn);
|
store.check_lint_name_cmdline(sess, lint_name, Level::ForceWarn, self.crate_attrs);
|
||||||
let lints = store
|
let lints = store
|
||||||
.find_lints(lint_name)
|
.find_lints(lint_name)
|
||||||
.unwrap_or_else(|_| bug!("A valid lint failed to produce a lint ids"));
|
.unwrap_or_else(|_| bug!("A valid lint failed to produce a lint ids"));
|
||||||
|
@ -322,33 +322,14 @@ impl<'s> LintLevelsBuilder<'s> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let tool_name = if meta_item.path.segments.len() > 1 {
|
let tool_ident = if meta_item.path.segments.len() > 1 {
|
||||||
let tool_ident = meta_item.path.segments[0].ident;
|
Some(meta_item.path.segments.remove(0).ident)
|
||||||
if !is_known_lint_tool(tool_ident.name, sess, &self.crate_attrs) {
|
|
||||||
let mut err = struct_span_err!(
|
|
||||||
sess,
|
|
||||||
tool_ident.span,
|
|
||||||
E0710,
|
|
||||||
"unknown tool name `{}` found in scoped lint: `{}`",
|
|
||||||
tool_ident.name,
|
|
||||||
pprust::path_to_string(&meta_item.path),
|
|
||||||
);
|
|
||||||
if sess.is_nightly_build() {
|
|
||||||
err.help(&format!(
|
|
||||||
"add `#![register_tool({})]` to the crate root",
|
|
||||||
tool_ident.name
|
|
||||||
));
|
|
||||||
}
|
|
||||||
err.emit();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(meta_item.path.segments.remove(0).ident.name)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
let tool_name = tool_ident.map(|ident| ident.name);
|
||||||
let name = pprust::path_to_string(&meta_item.path);
|
let name = pprust::path_to_string(&meta_item.path);
|
||||||
let lint_result = store.check_lint_name(&name, tool_name);
|
let lint_result = store.check_lint_name(sess, &name, tool_name, self.crate_attrs);
|
||||||
match &lint_result {
|
match &lint_result {
|
||||||
CheckLintNameResult::Ok(ids) => {
|
CheckLintNameResult::Ok(ids) => {
|
||||||
let src = LintLevelSource::Node(
|
let src = LintLevelSource::Node(
|
||||||
|
@ -365,7 +346,8 @@ impl<'s> LintLevelsBuilder<'s> {
|
||||||
CheckLintNameResult::Tool(result) => {
|
CheckLintNameResult::Tool(result) => {
|
||||||
match *result {
|
match *result {
|
||||||
Ok(ids) => {
|
Ok(ids) => {
|
||||||
let complete_name = &format!("{}::{}", tool_name.unwrap(), name);
|
let complete_name =
|
||||||
|
&format!("{}::{}", tool_ident.unwrap().name, name);
|
||||||
let src = LintLevelSource::Node(
|
let src = LintLevelSource::Node(
|
||||||
Symbol::intern(complete_name),
|
Symbol::intern(complete_name),
|
||||||
sp,
|
sp,
|
||||||
|
@ -420,6 +402,26 @@ impl<'s> LintLevelsBuilder<'s> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&CheckLintNameResult::NoTool => {
|
||||||
|
let mut err = struct_span_err!(
|
||||||
|
sess,
|
||||||
|
tool_ident.map_or(DUMMY_SP, |ident| ident.span),
|
||||||
|
E0710,
|
||||||
|
"unknown tool name `{}` found in scoped lint: `{}::{}`",
|
||||||
|
tool_name.unwrap(),
|
||||||
|
tool_name.unwrap(),
|
||||||
|
pprust::path_to_string(&meta_item.path),
|
||||||
|
);
|
||||||
|
if sess.is_nightly_build() {
|
||||||
|
err.help(&format!(
|
||||||
|
"add `#![register_tool({})]` to the crate root",
|
||||||
|
tool_name.unwrap()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
err.emit();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
_ if !self.warn_about_weird_lints => {}
|
_ if !self.warn_about_weird_lints => {}
|
||||||
|
|
||||||
CheckLintNameResult::Warning(msg, renamed) => {
|
CheckLintNameResult::Warning(msg, renamed) => {
|
||||||
|
@ -451,8 +453,8 @@ impl<'s> LintLevelsBuilder<'s> {
|
||||||
let (level, src) =
|
let (level, src) =
|
||||||
self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess);
|
self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess);
|
||||||
struct_lint_level(self.sess, lint, level, src, Some(sp.into()), |lint| {
|
struct_lint_level(self.sess, lint, level, src, Some(sp.into()), |lint| {
|
||||||
let name = if let Some(tool_name) = tool_name {
|
let name = if let Some(tool_ident) = tool_ident {
|
||||||
format!("{}::{}", tool_name, name)
|
format!("{}::{}", tool_ident.name, name)
|
||||||
} else {
|
} else {
|
||||||
name.to_string()
|
name.to_string()
|
||||||
};
|
};
|
||||||
|
@ -475,7 +477,9 @@ impl<'s> LintLevelsBuilder<'s> {
|
||||||
if let CheckLintNameResult::Warning(_, Some(new_name)) = lint_result {
|
if let CheckLintNameResult::Warning(_, Some(new_name)) = lint_result {
|
||||||
// Ignore any errors or warnings that happen because the new name is inaccurate
|
// Ignore any errors or warnings that happen because the new name is inaccurate
|
||||||
// NOTE: `new_name` already includes the tool name, so we don't have to add it again.
|
// NOTE: `new_name` already includes the tool name, so we don't have to add it again.
|
||||||
if let CheckLintNameResult::Ok(ids) = store.check_lint_name(&new_name, None) {
|
if let CheckLintNameResult::Ok(ids) =
|
||||||
|
store.check_lint_name(sess, &new_name, None, self.crate_attrs)
|
||||||
|
{
|
||||||
let src = LintLevelSource::Node(Symbol::intern(&new_name), sp, reason);
|
let src = LintLevelSource::Node(Symbol::intern(&new_name), sp, reason);
|
||||||
for &id in ids {
|
for &id in ids {
|
||||||
self.check_gated_lint(id, attr.span);
|
self.check_gated_lint(id, attr.span);
|
||||||
|
@ -578,7 +582,7 @@ impl<'s> LintLevelsBuilder<'s> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_known_lint_tool(m_item: Symbol, sess: &Session, attrs: &[ast::Attribute]) -> bool {
|
pub fn is_known_lint_tool(m_item: Symbol, sess: &Session, attrs: &[ast::Attribute]) -> bool {
|
||||||
if [sym::clippy, sym::rustc, sym::rustdoc].contains(&m_item) {
|
if [sym::clippy, sym::rustc, sym::rustdoc].contains(&m_item) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -497,3 +497,6 @@ fn register_internals(store: &mut LintStore) {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
24
compiler/rustc_lint/src/tests.rs
Normal file
24
compiler/rustc_lint/src/tests.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
use crate::context::parse_lint_and_tool_name;
|
||||||
|
use rustc_span::{with_default_session_globals, Symbol};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_lint_no_tool() {
|
||||||
|
with_default_session_globals(|| assert_eq!(parse_lint_and_tool_name("foo"), (None, "foo")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_lint_with_tool() {
|
||||||
|
with_default_session_globals(|| {
|
||||||
|
assert_eq!(parse_lint_and_tool_name("clippy::foo"), (Some(Symbol::intern("clippy")), "foo"))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_lint_multiple_path() {
|
||||||
|
with_default_session_globals(|| {
|
||||||
|
assert_eq!(
|
||||||
|
parse_lint_and_tool_name("clippy::foo::bar"),
|
||||||
|
(Some(Symbol::intern("clippy")), "foo::bar")
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
7
src/test/ui/lint/command-line-register-lint-tool.rs
Normal file
7
src/test/ui/lint/command-line-register-lint-tool.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// compile-flags: -A known_tool::foo
|
||||||
|
// check-pass
|
||||||
|
|
||||||
|
#![feature(register_tool)]
|
||||||
|
#![register_tool(known_tool)]
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,4 @@
|
||||||
|
// compile-flags: -A unknown_tool::foo
|
||||||
|
// error-pattern: unknown lint tool: `unknown_tool`
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,11 @@
|
||||||
|
error[E0602]: unknown lint tool: `unknown_tool`
|
||||||
|
|
|
||||||
|
= note: requested on the command line with `-A unknown_tool::foo`
|
||||||
|
|
||||||
|
error[E0602]: unknown lint tool: `unknown_tool`
|
||||||
|
|
|
||||||
|
= note: requested on the command line with `-A unknown_tool::foo`
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0602`.
|
Loading…
Add table
Add a link
Reference in a new issue