Skip to content

Make unknown/renamed/removed lints passed via command line respect lint levels #115387

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 2 additions & 9 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,6 @@ lint_builtin_unused_doc_comment = unused doc comment
lint_builtin_while_true = denote infinite loops with `loop {"{"} ... {"}"}`
.suggestion = use `loop`

lint_check_name_deprecated = lint name `{$lint_name}` is deprecated and does not have an effect anymore. Use: {$new_name}

lint_check_name_removed = lint `{$lint_name}` has been removed: {$reason}

lint_check_name_renamed = lint `{$lint_name}` has been renamed to `{$replace}`

lint_check_name_unknown = unknown lint: `{$lint_name}`
.help = did you mean: `{$suggestion}`

lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}`

lint_command_line_source = `forbid` lint level was set on command line
Expand All @@ -187,6 +178,7 @@ lint_default_source = `forbid` lint level is the default for {$id}
lint_deprecated_lint_name =
lint name `{$name}` is deprecated and may not have an effect in the future.
.suggestion = change it to
.help = change it to {$replace}

lint_diag_out_of_impl =
diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls
Expand Down Expand Up @@ -528,6 +520,7 @@ lint_unknown_gated_lint =
lint_unknown_lint =
unknown lint: `{$name}`
.suggestion = did you mean
.help = did you mean: `{$replace}`

lint_unknown_tool_in_scoped_lint = unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}`
.help = add `#![register_tool({$tool_name})]` to the crate root
Expand Down
67 changes: 0 additions & 67 deletions compiler/rustc_lint/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@

use self::TargetLint::*;

use crate::errors::{
CheckNameDeprecated, CheckNameRemoved, CheckNameRenamed, CheckNameUnknown,
CheckNameUnknownTool, RequestedLevel, UnsupportedGroup,
};
use crate::levels::LintLevelsBuilder;
use crate::passes::{EarlyLintPassObject, LateLintPassObject};
use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
Expand Down Expand Up @@ -330,58 +326,6 @@ impl LintStore {
}
}

/// 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,
registered_tools: &RegisteredTools,
) {
let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
if lint_name_only == crate::WARNINGS.name_lower() && matches!(level, Level::ForceWarn(_)) {
sess.emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
return;
}
match self.check_lint_name(lint_name_only, tool_name, registered_tools) {
CheckLintNameResult::Renamed(replace) => {
sess.emit_warning(CheckNameRenamed {
lint_name,
replace: &replace,
sub: RequestedLevel { level, lint_name },
});
}
CheckLintNameResult::Removed(reason) => {
sess.emit_warning(CheckNameRemoved {
lint_name,
reason: &reason,
sub: RequestedLevel { level, lint_name },
});
}
CheckLintNameResult::NoLint(suggestion) => {
sess.emit_err(CheckNameUnknown {
lint_name,
suggestion,
sub: RequestedLevel { level, lint_name },
});
}
CheckLintNameResult::Tool(Err((Some(_), new_name))) => {
sess.emit_warning(CheckNameDeprecated {
lint_name,
new_name: &new_name,
sub: RequestedLevel { level, lint_name },
});
}
CheckLintNameResult::NoTool => {
sess.emit_err(CheckNameUnknownTool {
tool_name: tool_name.unwrap(),
sub: RequestedLevel { level, lint_name },
});
}
_ => {}
};
}

/// True if this symbol represents a lint group name.
pub fn is_lint_group(&self, lint_name: Symbol) -> bool {
debug!(
Expand Down Expand Up @@ -1402,14 +1346,3 @@ impl<'tcx> LayoutOfHelpers<'tcx> for LateContext<'tcx> {
err
}
}

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),
}
}
54 changes: 1 addition & 53 deletions compiler/rustc_lint/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use crate::fluent_generated as fluent;
use rustc_errors::{
AddToDiagnostic, Diagnostic, ErrorGuaranteed, Handler, IntoDiagnostic, SubdiagnosticMessage,
};
use rustc_errors::{AddToDiagnostic, Diagnostic, SubdiagnosticMessage};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_session::lint::Level;
use rustc_span::{Span, Symbol};
Expand Down Expand Up @@ -102,60 +100,10 @@ pub struct UnsupportedGroup {
pub lint_group: String,
}

pub struct CheckNameUnknown<'a> {
pub lint_name: &'a str,
pub suggestion: Option<Symbol>,
pub sub: RequestedLevel<'a>,
}

impl IntoDiagnostic<'_> for CheckNameUnknown<'_> {
fn into_diagnostic(
self,
handler: &Handler,
) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
let mut diag = handler.struct_err(fluent::lint_check_name_unknown);
diag.code(rustc_errors::error_code!(E0602));
if let Some(suggestion) = self.suggestion {
diag.help(fluent::lint_help);
diag.set_arg("suggestion", suggestion);
}
diag.set_arg("lint_name", self.lint_name);
diag.subdiagnostic(self.sub);
diag
}
}

#[derive(Diagnostic)]
#[diag(lint_check_name_unknown_tool, code = "E0602")]
pub struct CheckNameUnknownTool<'a> {
pub tool_name: Symbol,
#[subdiagnostic]
pub sub: RequestedLevel<'a>,
}

#[derive(Diagnostic)]
#[diag(lint_check_name_renamed)]
pub struct CheckNameRenamed<'a> {
pub lint_name: &'a str,
pub replace: &'a str,
#[subdiagnostic]
pub sub: RequestedLevel<'a>,
}

#[derive(Diagnostic)]
#[diag(lint_check_name_removed)]
pub struct CheckNameRemoved<'a> {
pub lint_name: &'a str,
pub reason: &'a str,
#[subdiagnostic]
pub sub: RequestedLevel<'a>,
}

#[derive(Diagnostic)]
#[diag(lint_check_name_deprecated)]
pub struct CheckNameDeprecated<'a> {
pub lint_name: &'a str,
pub new_name: &'a str,
#[subdiagnostic]
pub sub: RequestedLevel<'a>,
}
95 changes: 73 additions & 22 deletions compiler/rustc_lint/src/levels.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
use crate::errors::{CheckNameUnknownTool, RequestedLevel, UnsupportedGroup};
use crate::lints::{
DeprecatedLintNameFromCommandLine, RemovedLintFromCommandLine, RenamedLintFromCommandLine,
UnknownLintFromCommandLine,
};
use crate::{
builtin::MISSING_DOCS,
context::{CheckLintNameResult, LintStore},
Expand Down Expand Up @@ -552,12 +557,55 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {

fn add_command_line(&mut self) {
for &(ref lint_name, level) in &self.sess.opts.lint_opts {
self.store.check_lint_name_cmdline(self.sess, &lint_name, level, self.registered_tools);
// Checks the validity of lint names derived from the command line.
let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
if lint_name_only == crate::WARNINGS.name_lower()
&& matches!(level, Level::ForceWarn(_))
{
self.sess.emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
}
match self.store.check_lint_name(lint_name_only, tool_name, self.registered_tools) {
CheckLintNameResult::Renamed(ref replace) => {
let name = lint_name.as_str();
let suggestion = RenamedLintSuggestion::WithoutSpan { replace };
let requested_level = RequestedLevel { level, lint_name };
let lint = RenamedLintFromCommandLine { name, suggestion, requested_level };
self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
}
CheckLintNameResult::Removed(ref reason) => {
let name = lint_name.as_str();
let requested_level = RequestedLevel { level, lint_name };
let lint = RemovedLintFromCommandLine { name, reason, requested_level };
self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
}
CheckLintNameResult::NoLint(suggestion) => {
let name = lint_name.clone();
let suggestion =
suggestion.map(|replace| UnknownLintSuggestion::WithoutSpan { replace });
let requested_level = RequestedLevel { level, lint_name };
let lint = UnknownLintFromCommandLine { name, suggestion, requested_level };
self.emit_lint(UNKNOWN_LINTS, lint);
}
CheckLintNameResult::Tool(Err((Some(_), ref replace))) => {
let name = lint_name.clone();
let requested_level = RequestedLevel { level, lint_name };
let lint = DeprecatedLintNameFromCommandLine { name, replace, requested_level };
self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
}
CheckLintNameResult::NoTool => {
self.sess.emit_err(CheckNameUnknownTool {
tool_name: tool_name.unwrap(),
sub: RequestedLevel { level, lint_name },
});
}
_ => {}
};

let orig_level = level;
let lint_flag_val = Symbol::intern(lint_name);

let Ok(ids) = self.store.find_lints(&lint_name) else {
// errors handled in check_lint_name_cmdline above
// errors already handled above
continue;
};
for id in ids {
Expand Down Expand Up @@ -915,24 +963,18 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {

_ if !self.warn_about_weird_lints => {}

CheckLintNameResult::Renamed(new_name) => {
CheckLintNameResult::Renamed(ref replace) => {
let suggestion =
RenamedLintSuggestion { suggestion: sp, replace: new_name.as_str() };
RenamedLintSuggestion::WithSpan { suggestion: sp, replace };
let name = tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
self.emit_spanned_lint(
RENAMED_AND_REMOVED_LINTS,
sp.into(),
RenamedLint { name: name.as_str(), suggestion },
);
let lint = RenamedLint { name: name.as_str(), suggestion };
self.emit_spanned_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint);
}

CheckLintNameResult::Removed(reason) => {
CheckLintNameResult::Removed(ref reason) => {
let name = tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
self.emit_spanned_lint(
RENAMED_AND_REMOVED_LINTS,
sp.into(),
RemovedLint { name: name.as_str(), reason: reason.as_str() },
);
let lint = RemovedLint { name: name.as_str(), reason };
self.emit_spanned_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint);
}

CheckLintNameResult::NoLint(suggestion) => {
Expand All @@ -941,13 +983,11 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
} else {
name.to_string()
};
let suggestion = suggestion
.map(|replace| UnknownLintSuggestion { suggestion: sp, replace });
self.emit_spanned_lint(
UNKNOWN_LINTS,
sp.into(),
UnknownLint { name, suggestion },
);
let suggestion = suggestion.map(|replace| {
UnknownLintSuggestion::WithSpan { suggestion: sp, replace }
});
let lint = UnknownLint { name, suggestion };
self.emit_spanned_lint(UNKNOWN_LINTS, sp.into(), lint);
}
}
// If this lint was renamed, apply the new lint instead of ignoring the attribute.
Expand Down Expand Up @@ -1092,3 +1132,14 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers { shallow_lint_levels_on, lint_expectations, ..*providers };
}

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),
}
}
Loading