1
Fork 0

Auto merge of #93678 - steffahn:better_unsafe_diagnostics, r=nagisa

Improve `unused_unsafe` lint

I’m going to add some motivation and explanation below, particularly pointing the changes in behavior from this PR.

_Edit:_ Looking for existing issues, looks like this PR fixes #88260.

_Edit2:_ Now also contains code that closes #90776.
This commit is contained in:
bors 2022-02-20 21:15:11 +00:00
commit 45e2c2881d
11 changed files with 3210 additions and 245 deletions

View file

@ -3,6 +3,10 @@ use rustc_hir::intravisit::nested_filter::NestedFilter;
/// Do not visit nested item-like things, but visit nested things
/// that are inside of an item-like.
///
/// Notably, possible occurrences of bodies in non-item-like things
/// include: closures/generators, inline `const {}` blocks, and
/// constant arguments of types, e.g. in `let _: [(); /* HERE */];`.
///
/// **This is the most common choice.** A very common pattern is
/// to use `visit_all_item_likes()` as an outer loop,
/// and to have the visitor that visits the contents of each item

View file

@ -202,6 +202,77 @@ impl<'a> LintDiagnosticBuilder<'a> {
}
}
pub fn explain_lint_level_source<'s>(
sess: &'s Session,
lint: &'static Lint,
level: Level,
src: LintLevelSource,
err: &mut DiagnosticBuilder<'s>,
) {
let name = lint.name_lower();
match src {
LintLevelSource::Default => {
sess.diag_note_once(
err,
DiagnosticMessageId::from(lint),
&format!("`#[{}({})]` on by default", level.as_str(), name),
);
}
LintLevelSource::CommandLine(lint_flag_val, orig_level) => {
let flag = match orig_level {
Level::Warn => "-W",
Level::Deny => "-D",
Level::Forbid => "-F",
Level::Allow => "-A",
Level::ForceWarn => "--force-warn",
};
let hyphen_case_lint_name = name.replace('_', "-");
if lint_flag_val.as_str() == name {
sess.diag_note_once(
err,
DiagnosticMessageId::from(lint),
&format!(
"requested on the command line with `{} {}`",
flag, hyphen_case_lint_name
),
);
} else {
let hyphen_case_flag_val = lint_flag_val.as_str().replace('_', "-");
sess.diag_note_once(
err,
DiagnosticMessageId::from(lint),
&format!(
"`{} {}` implied by `{} {}`",
flag, hyphen_case_lint_name, flag, hyphen_case_flag_val
),
);
}
}
LintLevelSource::Node(lint_attr_name, src, reason) => {
if let Some(rationale) = reason {
err.note(rationale.as_str());
}
sess.diag_span_note_once(
err,
DiagnosticMessageId::from(lint),
src,
"the lint level is defined here",
);
if lint_attr_name.as_str() != name {
let level_str = level.as_str();
sess.diag_note_once(
err,
DiagnosticMessageId::from(lint),
&format!(
"`#[{}({})]` implied by `#[{}({})]`",
level_str, name, level_str, lint_attr_name
),
);
}
}
}
}
pub fn struct_lint_level<'s, 'd>(
sess: &'s Session,
lint: &'static Lint,
@ -277,69 +348,9 @@ pub fn struct_lint_level<'s, 'd>(
}
}
let name = lint.name_lower();
match src {
LintLevelSource::Default => {
sess.diag_note_once(
&mut err,
DiagnosticMessageId::from(lint),
&format!("`#[{}({})]` on by default", level.as_str(), name),
);
}
LintLevelSource::CommandLine(lint_flag_val, orig_level) => {
let flag = match orig_level {
Level::Warn => "-W",
Level::Deny => "-D",
Level::Forbid => "-F",
Level::Allow => "-A",
Level::ForceWarn => "--force-warn",
};
let hyphen_case_lint_name = name.replace('_', "-");
if lint_flag_val.as_str() == name {
sess.diag_note_once(
&mut err,
DiagnosticMessageId::from(lint),
&format!(
"requested on the command line with `{} {}`",
flag, hyphen_case_lint_name
),
);
} else {
let hyphen_case_flag_val = lint_flag_val.as_str().replace('_', "-");
sess.diag_note_once(
&mut err,
DiagnosticMessageId::from(lint),
&format!(
"`{} {}` implied by `{} {}`",
flag, hyphen_case_lint_name, flag, hyphen_case_flag_val
),
);
}
}
LintLevelSource::Node(lint_attr_name, src, reason) => {
if let Some(rationale) = reason {
err.note(rationale.as_str());
}
sess.diag_span_note_once(
&mut err,
DiagnosticMessageId::from(lint),
src,
"the lint level is defined here",
);
if lint_attr_name.as_str() != name {
let level_str = level.as_str();
sess.diag_note_once(
&mut err,
DiagnosticMessageId::from(lint),
&format!(
"`#[{}({})]` implied by `#[{}({})]`",
level_str, name, level_str, lint_attr_name
),
);
}
}
}
explain_lint_level_source(sess, lint, level, src, &mut err);
let name = lint.name_lower();
let is_force_warn = matches!(level, Level::ForceWarn);
err.code(DiagnosticId::Lint { name, has_future_breakage, is_force_warn });

View file

@ -2,7 +2,7 @@
use crate::mir::{Body, Promoted};
use crate::ty::{self, Ty, TyCtxt};
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::stable_map::FxHashMap;
use rustc_data_structures::vec_map::VecMap;
use rustc_errors::ErrorReported;
use rustc_hir as hir;
@ -114,13 +114,44 @@ pub struct UnsafetyViolation {
pub details: UnsafetyViolationDetails,
}
#[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)]
#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
pub enum UnusedUnsafe {
/// `unsafe` block contains no unsafe operations
/// > ``unnecessary `unsafe` block``
Unused,
/// `unsafe` block nested under another (used) `unsafe` block
/// > ``… because it's nested under this `unsafe` block``
InUnsafeBlock(hir::HirId),
/// `unsafe` block nested under `unsafe fn`
/// > ``… because it's nested under this `unsafe fn` ``
///
/// the second HirId here indicates the first usage of the `unsafe` block,
/// which allows retrival of the LintLevelSource for why that operation would
/// have been permitted without the block
InUnsafeFn(hir::HirId, hir::HirId),
}
#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
pub enum UsedUnsafeBlockData {
SomeDisallowedInUnsafeFn,
// the HirId here indicates the first usage of the `unsafe` block
// (i.e. the one that's first encountered in the MIR traversal of the unsafety check)
AllAllowedInUnsafeFn(hir::HirId),
}
#[derive(TyEncodable, TyDecodable, HashStable, Debug)]
pub struct UnsafetyCheckResult {
/// Violations that are propagated *upwards* from this function.
pub violations: Lrc<[UnsafetyViolation]>,
/// `unsafe` blocks in this function, along with whether they are used. This is
/// used for the "unused_unsafe" lint.
pub unsafe_blocks: Lrc<[(hir::HirId, bool)]>,
pub violations: Vec<UnsafetyViolation>,
/// Used `unsafe` blocks in this function. This is used for the "unused_unsafe" lint.
///
/// The keys are the used `unsafe` blocks, the UnusedUnsafeKind indicates whether
/// or not any of the usages happen at a place that doesn't allow `unsafe_op_in_unsafe_fn`.
pub used_unsafe_blocks: FxHashMap<hir::HirId, UsedUnsafeBlockData>,
/// This is `Some` iff the item is not a closure.
pub unused_unsafes: Option<Vec<(hir::HirId, UnusedUnsafe)>>,
}
rustc_index::newtype_index! {