Skip to content

Commit c2d3f5f

Browse files
authored
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 rust-lang/cargo#5034. Fixes #86628.
2 parents d2b04f0 + 4a83a93 commit c2d3f5f

7 files changed

+123
-34
lines changed

compiler/rustc_lint/src/context.rs

+40-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
1717
use self::TargetLint::*;
1818

19-
use crate::levels::LintLevelsBuilder;
19+
use crate::levels::{is_known_lint_tool, LintLevelsBuilder};
2020
use crate::passes::{EarlyLintPassObject, LateLintPassObject};
2121
use rustc_ast as ast;
2222
use rustc_data_structures::fx::FxHashMap;
@@ -129,6 +129,8 @@ pub enum CheckLintNameResult<'a> {
129129
Ok(&'a [LintId]),
130130
/// Lint doesn't exist. Potentially contains a suggestion for a correct lint name.
131131
NoLint(Option<Symbol>),
132+
/// The lint refers to a tool that has not been registered.
133+
NoTool,
132134
/// The lint is either renamed or removed. This is the warning
133135
/// message, and an optional new name (`None` if removed).
134136
Warning(String, Option<String>),
@@ -321,9 +323,17 @@ impl LintStore {
321323
}
322324
}
323325

324-
/// Checks the validity of lint names derived from the command line
325-
pub fn check_lint_name_cmdline(&self, sess: &Session, lint_name: &str, level: Level) {
326-
let db = match self.check_lint_name(lint_name, None) {
326+
/// Checks the validity of lint names derived from the command line.
327+
pub fn check_lint_name_cmdline(
328+
&self,
329+
sess: &Session,
330+
lint_name: &str,
331+
level: Level,
332+
crate_attrs: &[ast::Attribute],
333+
) {
334+
let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
335+
336+
let db = match self.check_lint_name(sess, lint_name_only, tool_name, crate_attrs) {
327337
CheckLintNameResult::Ok(_) => None,
328338
CheckLintNameResult::Warning(ref msg, _) => Some(sess.struct_warn(msg)),
329339
CheckLintNameResult::NoLint(suggestion) => {
@@ -345,6 +355,13 @@ impl LintStore {
345355
))),
346356
_ => None,
347357
},
358+
CheckLintNameResult::NoTool => Some(struct_span_err!(
359+
sess,
360+
DUMMY_SP,
361+
E0602,
362+
"unknown lint tool: `{}`",
363+
tool_name.unwrap()
364+
)),
348365
};
349366

350367
if let Some(mut db) = db {
@@ -387,9 +404,17 @@ impl LintStore {
387404
/// printing duplicate warnings.
388405
pub fn check_lint_name(
389406
&self,
407+
sess: &Session,
390408
lint_name: &str,
391409
tool_name: Option<Symbol>,
410+
crate_attrs: &[ast::Attribute],
392411
) -> CheckLintNameResult<'_> {
412+
if let Some(tool_name) = tool_name {
413+
if !is_known_lint_tool(tool_name, sess, crate_attrs) {
414+
return CheckLintNameResult::NoTool;
415+
}
416+
}
417+
393418
let complete_name = if let Some(tool_name) = tool_name {
394419
format!("{}::{}", tool_name, lint_name)
395420
} else {
@@ -1005,3 +1030,14 @@ impl<'tcx> LayoutOf for LateContext<'tcx> {
10051030
self.tcx.layout_of(self.param_env.and(ty))
10061031
}
10071032
}
1033+
1034+
pub fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {
1035+
match lint_name.split_once("::") {
1036+
Some((tool_name, lint_name)) => {
1037+
let tool_name = Symbol::intern(tool_name);
1038+
1039+
(Some(tool_name), lint_name)
1040+
}
1041+
None => (None, lint_name),
1042+
}
1043+
}

compiler/rustc_lint/src/levels.rs

+34-30
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ impl<'s> LintLevelsBuilder<'s> {
8989
self.sets.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid);
9090

9191
for &(ref lint_name, level) in &sess.opts.lint_opts {
92-
store.check_lint_name_cmdline(sess, &lint_name, level);
92+
store.check_lint_name_cmdline(sess, &lint_name, level, self.crate_attrs);
9393
let orig_level = level;
9494

9595
// If the cap is less than this specified level, e.g., if we've got
@@ -111,7 +111,7 @@ impl<'s> LintLevelsBuilder<'s> {
111111
}
112112

113113
for lint_name in &sess.opts.force_warns {
114-
store.check_lint_name_cmdline(sess, lint_name, Level::ForceWarn);
114+
store.check_lint_name_cmdline(sess, lint_name, Level::ForceWarn, self.crate_attrs);
115115
let lints = store
116116
.find_lints(lint_name)
117117
.unwrap_or_else(|_| bug!("A valid lint failed to produce a lint ids"));
@@ -322,33 +322,14 @@ impl<'s> LintLevelsBuilder<'s> {
322322
continue;
323323
}
324324
};
325-
let tool_name = if meta_item.path.segments.len() > 1 {
326-
let tool_ident = meta_item.path.segments[0].ident;
327-
if !is_known_lint_tool(tool_ident.name, sess, &self.crate_attrs) {
328-
let mut err = struct_span_err!(
329-
sess,
330-
tool_ident.span,
331-
E0710,
332-
"unknown tool name `{}` found in scoped lint: `{}`",
333-
tool_ident.name,
334-
pprust::path_to_string(&meta_item.path),
335-
);
336-
if sess.is_nightly_build() {
337-
err.help(&format!(
338-
"add `#![register_tool({})]` to the crate root",
339-
tool_ident.name
340-
));
341-
}
342-
err.emit();
343-
continue;
344-
}
345-
346-
Some(meta_item.path.segments.remove(0).ident.name)
325+
let tool_ident = if meta_item.path.segments.len() > 1 {
326+
Some(meta_item.path.segments.remove(0).ident)
347327
} else {
348328
None
349329
};
330+
let tool_name = tool_ident.map(|ident| ident.name);
350331
let name = pprust::path_to_string(&meta_item.path);
351-
let lint_result = store.check_lint_name(&name, tool_name);
332+
let lint_result = store.check_lint_name(sess, &name, tool_name, self.crate_attrs);
352333
match &lint_result {
353334
CheckLintNameResult::Ok(ids) => {
354335
let src = LintLevelSource::Node(
@@ -365,7 +346,8 @@ impl<'s> LintLevelsBuilder<'s> {
365346
CheckLintNameResult::Tool(result) => {
366347
match *result {
367348
Ok(ids) => {
368-
let complete_name = &format!("{}::{}", tool_name.unwrap(), name);
349+
let complete_name =
350+
&format!("{}::{}", tool_ident.unwrap().name, name);
369351
let src = LintLevelSource::Node(
370352
Symbol::intern(complete_name),
371353
sp,
@@ -420,6 +402,26 @@ impl<'s> LintLevelsBuilder<'s> {
420402
}
421403
}
422404

405+
&CheckLintNameResult::NoTool => {
406+
let mut err = struct_span_err!(
407+
sess,
408+
tool_ident.map_or(DUMMY_SP, |ident| ident.span),
409+
E0710,
410+
"unknown tool name `{}` found in scoped lint: `{}::{}`",
411+
tool_name.unwrap(),
412+
tool_name.unwrap(),
413+
pprust::path_to_string(&meta_item.path),
414+
);
415+
if sess.is_nightly_build() {
416+
err.help(&format!(
417+
"add `#![register_tool({})]` to the crate root",
418+
tool_name.unwrap()
419+
));
420+
}
421+
err.emit();
422+
continue;
423+
}
424+
423425
_ if !self.warn_about_weird_lints => {}
424426

425427
CheckLintNameResult::Warning(msg, renamed) => {
@@ -451,8 +453,8 @@ impl<'s> LintLevelsBuilder<'s> {
451453
let (level, src) =
452454
self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess);
453455
struct_lint_level(self.sess, lint, level, src, Some(sp.into()), |lint| {
454-
let name = if let Some(tool_name) = tool_name {
455-
format!("{}::{}", tool_name, name)
456+
let name = if let Some(tool_ident) = tool_ident {
457+
format!("{}::{}", tool_ident.name, name)
456458
} else {
457459
name.to_string()
458460
};
@@ -475,7 +477,9 @@ impl<'s> LintLevelsBuilder<'s> {
475477
if let CheckLintNameResult::Warning(_, Some(new_name)) = lint_result {
476478
// Ignore any errors or warnings that happen because the new name is inaccurate
477479
// NOTE: `new_name` already includes the tool name, so we don't have to add it again.
478-
if let CheckLintNameResult::Ok(ids) = store.check_lint_name(&new_name, None) {
480+
if let CheckLintNameResult::Ok(ids) =
481+
store.check_lint_name(sess, &new_name, None, self.crate_attrs)
482+
{
479483
let src = LintLevelSource::Node(Symbol::intern(&new_name), sp, reason);
480484
for &id in ids {
481485
self.check_gated_lint(id, attr.span);
@@ -578,7 +582,7 @@ impl<'s> LintLevelsBuilder<'s> {
578582
}
579583
}
580584

581-
fn is_known_lint_tool(m_item: Symbol, sess: &Session, attrs: &[ast::Attribute]) -> bool {
585+
pub fn is_known_lint_tool(m_item: Symbol, sess: &Session, attrs: &[ast::Attribute]) -> bool {
582586
if [sym::clippy, sym::rustc, sym::rustdoc].contains(&m_item) {
583587
return true;
584588
}

compiler/rustc_lint/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -497,3 +497,6 @@ fn register_internals(store: &mut LintStore) {
497497
],
498498
);
499499
}
500+
501+
#[cfg(test)]
502+
mod tests;

compiler/rustc_lint/src/tests.rs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use crate::context::parse_lint_and_tool_name;
2+
use rustc_span::{with_default_session_globals, Symbol};
3+
4+
#[test]
5+
fn parse_lint_no_tool() {
6+
with_default_session_globals(|| assert_eq!(parse_lint_and_tool_name("foo"), (None, "foo")));
7+
}
8+
9+
#[test]
10+
fn parse_lint_with_tool() {
11+
with_default_session_globals(|| {
12+
assert_eq!(parse_lint_and_tool_name("clippy::foo"), (Some(Symbol::intern("clippy")), "foo"))
13+
});
14+
}
15+
16+
#[test]
17+
fn parse_lint_multiple_path() {
18+
with_default_session_globals(|| {
19+
assert_eq!(
20+
parse_lint_and_tool_name("clippy::foo::bar"),
21+
(Some(Symbol::intern("clippy")), "foo::bar")
22+
)
23+
});
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// compile-flags: -A known_tool::foo
2+
// check-pass
3+
4+
#![feature(register_tool)]
5+
#![register_tool(known_tool)]
6+
7+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// compile-flags: -A unknown_tool::foo
2+
// error-pattern: unknown lint tool: `unknown_tool`
3+
4+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0602]: unknown lint tool: `unknown_tool`
2+
|
3+
= note: requested on the command line with `-A unknown_tool::foo`
4+
5+
error[E0602]: unknown lint tool: `unknown_tool`
6+
|
7+
= note: requested on the command line with `-A unknown_tool::foo`
8+
9+
error: aborting due to 2 previous errors
10+
11+
For more information about this error, try `rustc --explain E0602`.

0 commit comments

Comments
 (0)