Auto merge of #139336 - matthiaskrgr:rollup-zsi8pgf, r=matthiaskrgr
Rollup of 9 pull requests Successful merges: - #138017 (Tighten up assignment operator representations.) - #138462 (Dedup `&mut *` reborrow suggestion in loops) - #138610 (impl !PartialOrd for HirId) - #138767 (Allow boolean literals in `check-cfg`) - #139068 (io: Avoid marking some bytes as uninit) - #139255 (Remove unused variables generated in merged doctests) - #139270 (Add a mailmap entry for myself) - #139303 (Put Noratrieb on vacation) - #139312 (add Marco Ieni to mailmap) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
4fd8c04da0
81 changed files with 807 additions and 599 deletions
|
@ -981,6 +981,75 @@ impl BinOpKind {
|
|||
|
||||
pub type BinOp = Spanned<BinOpKind>;
|
||||
|
||||
// Sometimes `BinOpKind` and `AssignOpKind` need the same treatment. The
|
||||
// operations covered by `AssignOpKind` are a subset of those covered by
|
||||
// `BinOpKind`, so it makes sense to convert `AssignOpKind` to `BinOpKind`.
|
||||
impl From<AssignOpKind> for BinOpKind {
|
||||
fn from(op: AssignOpKind) -> BinOpKind {
|
||||
match op {
|
||||
AssignOpKind::AddAssign => BinOpKind::Add,
|
||||
AssignOpKind::SubAssign => BinOpKind::Sub,
|
||||
AssignOpKind::MulAssign => BinOpKind::Mul,
|
||||
AssignOpKind::DivAssign => BinOpKind::Div,
|
||||
AssignOpKind::RemAssign => BinOpKind::Rem,
|
||||
AssignOpKind::BitXorAssign => BinOpKind::BitXor,
|
||||
AssignOpKind::BitAndAssign => BinOpKind::BitAnd,
|
||||
AssignOpKind::BitOrAssign => BinOpKind::BitOr,
|
||||
AssignOpKind::ShlAssign => BinOpKind::Shl,
|
||||
AssignOpKind::ShrAssign => BinOpKind::Shr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
|
||||
pub enum AssignOpKind {
|
||||
/// The `+=` operator (addition)
|
||||
AddAssign,
|
||||
/// The `-=` operator (subtraction)
|
||||
SubAssign,
|
||||
/// The `*=` operator (multiplication)
|
||||
MulAssign,
|
||||
/// The `/=` operator (division)
|
||||
DivAssign,
|
||||
/// The `%=` operator (modulus)
|
||||
RemAssign,
|
||||
/// The `^=` operator (bitwise xor)
|
||||
BitXorAssign,
|
||||
/// The `&=` operator (bitwise and)
|
||||
BitAndAssign,
|
||||
/// The `|=` operator (bitwise or)
|
||||
BitOrAssign,
|
||||
/// The `<<=` operator (shift left)
|
||||
ShlAssign,
|
||||
/// The `>>=` operator (shift right)
|
||||
ShrAssign,
|
||||
}
|
||||
|
||||
impl AssignOpKind {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
use AssignOpKind::*;
|
||||
match self {
|
||||
AddAssign => "+=",
|
||||
SubAssign => "-=",
|
||||
MulAssign => "*=",
|
||||
DivAssign => "/=",
|
||||
RemAssign => "%=",
|
||||
BitXorAssign => "^=",
|
||||
BitAndAssign => "&=",
|
||||
BitOrAssign => "|=",
|
||||
ShlAssign => "<<=",
|
||||
ShrAssign => ">>=",
|
||||
}
|
||||
}
|
||||
|
||||
/// AssignOps are always by value.
|
||||
pub fn is_by_value(self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub type AssignOp = Spanned<AssignOpKind>;
|
||||
|
||||
/// Unary operator.
|
||||
///
|
||||
/// Note that `&data` is not an operator, it's an `AddrOf` expression.
|
||||
|
@ -1593,7 +1662,7 @@ pub enum ExprKind {
|
|||
/// An assignment with an operator.
|
||||
///
|
||||
/// E.g., `a += 1`.
|
||||
AssignOp(BinOp, P<Expr>, P<Expr>),
|
||||
AssignOp(AssignOp, P<Expr>, P<Expr>),
|
||||
/// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct field.
|
||||
Field(P<Expr>, Ident),
|
||||
/// An indexing operation (e.g., `foo[2]`).
|
||||
|
|
|
@ -570,6 +570,14 @@ impl MetaItemInner {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the bool if `self` is a boolean `MetaItemInner::Literal`.
|
||||
pub fn boolean_literal(&self) -> Option<bool> {
|
||||
match self {
|
||||
MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => Some(*b),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `MetaItem` if `self` is a `MetaItemInner::MetaItem` or if it's
|
||||
/// `MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(_), .. })`.
|
||||
pub fn meta_item_or_bool(&self) -> Option<&MetaItemInner> {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use rustc_span::kw;
|
||||
|
||||
use crate::ast::{self, BinOpKind, RangeLimits};
|
||||
use crate::ast::{self, AssignOpKind, BinOpKind, RangeLimits};
|
||||
use crate::token::{self, Token};
|
||||
|
||||
/// Associative operator.
|
||||
|
@ -9,7 +9,7 @@ pub enum AssocOp {
|
|||
/// A binary op.
|
||||
Binary(BinOpKind),
|
||||
/// `?=` where ? is one of the assignable BinOps
|
||||
AssignOp(BinOpKind),
|
||||
AssignOp(AssignOpKind),
|
||||
/// `=`
|
||||
Assign,
|
||||
/// `as`
|
||||
|
@ -44,16 +44,16 @@ impl AssocOp {
|
|||
token::Or => Some(Binary(BinOpKind::BitOr)),
|
||||
token::Shl => Some(Binary(BinOpKind::Shl)),
|
||||
token::Shr => Some(Binary(BinOpKind::Shr)),
|
||||
token::PlusEq => Some(AssignOp(BinOpKind::Add)),
|
||||
token::MinusEq => Some(AssignOp(BinOpKind::Sub)),
|
||||
token::StarEq => Some(AssignOp(BinOpKind::Mul)),
|
||||
token::SlashEq => Some(AssignOp(BinOpKind::Div)),
|
||||
token::PercentEq => Some(AssignOp(BinOpKind::Rem)),
|
||||
token::CaretEq => Some(AssignOp(BinOpKind::BitXor)),
|
||||
token::AndEq => Some(AssignOp(BinOpKind::BitAnd)),
|
||||
token::OrEq => Some(AssignOp(BinOpKind::BitOr)),
|
||||
token::ShlEq => Some(AssignOp(BinOpKind::Shl)),
|
||||
token::ShrEq => Some(AssignOp(BinOpKind::Shr)),
|
||||
token::PlusEq => Some(AssignOp(AssignOpKind::AddAssign)),
|
||||
token::MinusEq => Some(AssignOp(AssignOpKind::SubAssign)),
|
||||
token::StarEq => Some(AssignOp(AssignOpKind::MulAssign)),
|
||||
token::SlashEq => Some(AssignOp(AssignOpKind::DivAssign)),
|
||||
token::PercentEq => Some(AssignOp(AssignOpKind::RemAssign)),
|
||||
token::CaretEq => Some(AssignOp(AssignOpKind::BitXorAssign)),
|
||||
token::AndEq => Some(AssignOp(AssignOpKind::BitAndAssign)),
|
||||
token::OrEq => Some(AssignOp(AssignOpKind::BitOrAssign)),
|
||||
token::ShlEq => Some(AssignOp(AssignOpKind::ShlAssign)),
|
||||
token::ShrEq => Some(AssignOp(AssignOpKind::ShrAssign)),
|
||||
token::Lt => Some(Binary(BinOpKind::Lt)),
|
||||
token::Le => Some(Binary(BinOpKind::Le)),
|
||||
token::Ge => Some(Binary(BinOpKind::Ge)),
|
||||
|
|
|
@ -274,7 +274,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
}
|
||||
ExprKind::Assign(el, er, span) => self.lower_expr_assign(el, er, *span, e.span),
|
||||
ExprKind::AssignOp(op, el, er) => hir::ExprKind::AssignOp(
|
||||
self.lower_binop(*op),
|
||||
self.lower_assign_op(*op),
|
||||
self.lower_expr(el),
|
||||
self.lower_expr(er),
|
||||
),
|
||||
|
@ -443,6 +443,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
Spanned { node: b.node, span: self.lower_span(b.span) }
|
||||
}
|
||||
|
||||
fn lower_assign_op(&mut self, a: AssignOp) -> AssignOp {
|
||||
Spanned { node: a.node, span: self.lower_span(a.span) }
|
||||
}
|
||||
|
||||
fn lower_legacy_const_generics(
|
||||
&mut self,
|
||||
mut f: Expr,
|
||||
|
|
|
@ -274,22 +274,22 @@ impl<'a> State<'a> {
|
|||
|
||||
fn print_expr_binary(
|
||||
&mut self,
|
||||
op: ast::BinOp,
|
||||
op: ast::BinOpKind,
|
||||
lhs: &ast::Expr,
|
||||
rhs: &ast::Expr,
|
||||
fixup: FixupContext,
|
||||
) {
|
||||
let binop_prec = op.node.precedence();
|
||||
let binop_prec = op.precedence();
|
||||
let left_prec = lhs.precedence();
|
||||
let right_prec = rhs.precedence();
|
||||
|
||||
let (mut left_needs_paren, right_needs_paren) = match op.node.fixity() {
|
||||
let (mut left_needs_paren, right_needs_paren) = match op.fixity() {
|
||||
Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec),
|
||||
Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec),
|
||||
Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec),
|
||||
};
|
||||
|
||||
match (&lhs.kind, op.node) {
|
||||
match (&lhs.kind, op) {
|
||||
// These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is
|
||||
// the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead
|
||||
// of `(x as i32) < ...`. We need to convince it _not_ to do that.
|
||||
|
@ -312,7 +312,7 @@ impl<'a> State<'a> {
|
|||
|
||||
self.print_expr_cond_paren(lhs, left_needs_paren, fixup.leftmost_subexpression());
|
||||
self.space();
|
||||
self.word_space(op.node.as_str());
|
||||
self.word_space(op.as_str());
|
||||
self.print_expr_cond_paren(rhs, right_needs_paren, fixup.subsequent_subexpression());
|
||||
}
|
||||
|
||||
|
@ -410,7 +410,7 @@ impl<'a> State<'a> {
|
|||
self.print_expr_method_call(seg, receiver, args, fixup);
|
||||
}
|
||||
ast::ExprKind::Binary(op, lhs, rhs) => {
|
||||
self.print_expr_binary(*op, lhs, rhs, fixup);
|
||||
self.print_expr_binary(op.node, lhs, rhs, fixup);
|
||||
}
|
||||
ast::ExprKind::Unary(op, expr) => {
|
||||
self.print_expr_unary(*op, expr, fixup);
|
||||
|
@ -605,8 +605,7 @@ impl<'a> State<'a> {
|
|||
fixup.leftmost_subexpression(),
|
||||
);
|
||||
self.space();
|
||||
self.word(op.node.as_str());
|
||||
self.word_space("=");
|
||||
self.word_space(op.node.as_str());
|
||||
self.print_expr_cond_paren(
|
||||
rhs,
|
||||
rhs.precedence() < ExprPrecedence::Assign,
|
||||
|
|
|
@ -181,7 +181,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
let closure = self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
|
||||
|
||||
let mut is_loop_move = false;
|
||||
let mut in_pattern = false;
|
||||
let mut seen_spans = FxIndexSet::default();
|
||||
|
||||
for move_site in &move_site_vec {
|
||||
|
@ -204,7 +203,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
self.suggest_ref_or_clone(
|
||||
mpi,
|
||||
&mut err,
|
||||
&mut in_pattern,
|
||||
move_spans,
|
||||
moved_place.as_ref(),
|
||||
&mut has_suggest_reborrow,
|
||||
|
@ -256,15 +254,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
let place = &self.move_data.move_paths[mpi].place;
|
||||
let ty = place.ty(self.body, self.infcx.tcx).ty;
|
||||
|
||||
// If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
|
||||
// Same for if we're in a loop, see #101119.
|
||||
if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) {
|
||||
if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
|
||||
// We have a `&mut` ref, we need to reborrow on each iteration (#62112).
|
||||
self.suggest_reborrow(&mut err, span, moved_place);
|
||||
}
|
||||
}
|
||||
|
||||
if self.infcx.param_env.caller_bounds().iter().any(|c| {
|
||||
c.as_trait_clause().is_some_and(|pred| {
|
||||
pred.skip_binder().self_ty() == ty && self.infcx.tcx.is_fn_trait(pred.def_id())
|
||||
|
@ -330,7 +319,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
&self,
|
||||
mpi: MovePathIndex,
|
||||
err: &mut Diag<'infcx>,
|
||||
in_pattern: &mut bool,
|
||||
move_spans: UseSpans<'tcx>,
|
||||
moved_place: PlaceRef<'tcx>,
|
||||
has_suggest_reborrow: &mut bool,
|
||||
|
@ -545,7 +533,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
&& !move_span.is_dummy()
|
||||
&& !self.infcx.tcx.sess.source_map().is_imported(move_span)
|
||||
{
|
||||
*in_pattern = true;
|
||||
let mut sugg = vec![(pat.span.shrink_to_lo(), "ref ".to_string())];
|
||||
if let Some(pat) = finder.parent_pat {
|
||||
sugg.insert(0, (pat.span.shrink_to_lo(), "ref ".to_string()));
|
||||
|
|
|
@ -959,9 +959,9 @@ fn link_natively(
|
|||
}
|
||||
}
|
||||
|
||||
let (level, src) = codegen_results.crate_info.lint_levels.linker_messages;
|
||||
let level = codegen_results.crate_info.lint_levels.linker_messages;
|
||||
let lint = |msg| {
|
||||
lint_level(sess, LINKER_MESSAGES, level, src, None, |diag| {
|
||||
lint_level(sess, LINKER_MESSAGES, level, None, |diag| {
|
||||
LinkerOutput { inner: msg }.decorate_lint(diag)
|
||||
})
|
||||
};
|
||||
|
|
|
@ -34,7 +34,7 @@ use rustc_hir::CRATE_HIR_ID;
|
|||
use rustc_hir::def_id::CrateNum;
|
||||
use rustc_macros::{Decodable, Encodable, HashStable};
|
||||
use rustc_middle::dep_graph::WorkProduct;
|
||||
use rustc_middle::lint::LintLevelSource;
|
||||
use rustc_middle::lint::LevelAndSource;
|
||||
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
|
||||
use rustc_middle::middle::dependency_format::Dependencies;
|
||||
use rustc_middle::middle::exported_symbols::SymbolExportKind;
|
||||
|
@ -45,7 +45,6 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
|
|||
use rustc_session::Session;
|
||||
use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT};
|
||||
use rustc_session::cstore::{self, CrateSource};
|
||||
use rustc_session::lint::Level;
|
||||
use rustc_session::lint::builtin::LINKER_MESSAGES;
|
||||
use rustc_session::utils::NativeLibKind;
|
||||
use rustc_span::Symbol;
|
||||
|
@ -341,7 +340,7 @@ impl CodegenResults {
|
|||
/// Instead, encode exactly the information we need.
|
||||
#[derive(Copy, Clone, Debug, Encodable, Decodable)]
|
||||
pub struct CodegenLintLevels {
|
||||
linker_messages: (Level, LintLevelSource),
|
||||
linker_messages: LevelAndSource,
|
||||
}
|
||||
|
||||
impl CodegenLintLevels {
|
||||
|
|
|
@ -546,7 +546,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
|
|||
rustc_session::lint::builtin::LONG_RUNNING_CONST_EVAL,
|
||||
hir_id,
|
||||
)
|
||||
.0
|
||||
.level
|
||||
.is_error();
|
||||
let span = ecx.cur_span();
|
||||
ecx.tcx.emit_node_span_lint(
|
||||
|
|
|
@ -715,7 +715,7 @@ fn print_crate_info(
|
|||
// lint is unstable and feature gate isn't active, don't print
|
||||
continue;
|
||||
}
|
||||
let level = lint_levels.lint_level(lint).0;
|
||||
let level = lint_levels.lint_level(lint).level;
|
||||
println_info!("{}={}", lint.name_lower(), level.as_str());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,13 +91,13 @@ fn annotation_level_for_level(level: Level) -> annotate_snippets::Level {
|
|||
Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => {
|
||||
annotate_snippets::Level::Error
|
||||
}
|
||||
Level::ForceWarning(_) | Level::Warning => annotate_snippets::Level::Warning,
|
||||
Level::ForceWarning | Level::Warning => annotate_snippets::Level::Warning,
|
||||
Level::Note | Level::OnceNote => annotate_snippets::Level::Note,
|
||||
Level::Help | Level::OnceHelp => annotate_snippets::Level::Help,
|
||||
// FIXME(#59346): Not sure how to map this level
|
||||
Level::FailureNote => annotate_snippets::Level::Error,
|
||||
Level::Allow => panic!("Should not call with Allow"),
|
||||
Level::Expect(_) => panic!("Should not call with Expect"),
|
||||
Level::Expect => panic!("Should not call with Expect"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::thread::panicking;
|
|||
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_error_messages::{FluentValue, fluent_value_from_str_list_sep_by_and};
|
||||
use rustc_lint_defs::Applicability;
|
||||
use rustc_lint_defs::{Applicability, LintExpectationId};
|
||||
use rustc_macros::{Decodable, Encodable};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{DUMMY_SP, Span, Symbol};
|
||||
|
@ -296,6 +296,7 @@ pub struct DiagInner {
|
|||
|
||||
pub messages: Vec<(DiagMessage, Style)>,
|
||||
pub code: Option<ErrCode>,
|
||||
pub lint_id: Option<LintExpectationId>,
|
||||
pub span: MultiSpan,
|
||||
pub children: Vec<Subdiag>,
|
||||
pub suggestions: Suggestions,
|
||||
|
@ -324,6 +325,7 @@ impl DiagInner {
|
|||
pub fn new_with_messages(level: Level, messages: Vec<(DiagMessage, Style)>) -> Self {
|
||||
DiagInner {
|
||||
level,
|
||||
lint_id: None,
|
||||
messages,
|
||||
code: None,
|
||||
span: MultiSpan::new(),
|
||||
|
@ -346,7 +348,7 @@ impl DiagInner {
|
|||
match self.level {
|
||||
Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => true,
|
||||
|
||||
Level::ForceWarning(_)
|
||||
Level::ForceWarning
|
||||
| Level::Warning
|
||||
| Level::Note
|
||||
| Level::OnceNote
|
||||
|
@ -354,7 +356,7 @@ impl DiagInner {
|
|||
| Level::OnceHelp
|
||||
| Level::FailureNote
|
||||
| Level::Allow
|
||||
| Level::Expect(_) => false,
|
||||
| Level::Expect => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -365,7 +367,7 @@ impl DiagInner {
|
|||
|
||||
pub(crate) fn is_force_warn(&self) -> bool {
|
||||
match self.level {
|
||||
Level::ForceWarning(_) => {
|
||||
Level::ForceWarning => {
|
||||
assert!(self.is_lint.is_some());
|
||||
true
|
||||
}
|
||||
|
@ -1259,6 +1261,17 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
|
|||
self
|
||||
} }
|
||||
|
||||
with_fn! { with_lint_id,
|
||||
/// Add an argument.
|
||||
#[rustc_lint_diagnostics]
|
||||
pub fn lint_id(
|
||||
&mut self,
|
||||
id: LintExpectationId,
|
||||
) -> &mut Self {
|
||||
self.lint_id = Some(id);
|
||||
self
|
||||
} }
|
||||
|
||||
with_fn! { with_primary_message,
|
||||
/// Add a primary message.
|
||||
#[rustc_lint_diagnostics]
|
||||
|
|
|
@ -144,7 +144,7 @@ impl Emitter for JsonEmitter {
|
|||
//
|
||||
// So to avoid ICEs and confused users we "upgrade" the lint level for
|
||||
// those `FutureBreakageItem` to warn.
|
||||
if matches!(diag.level, crate::Level::Allow | crate::Level::Expect(..)) {
|
||||
if matches!(diag.level, crate::Level::Allow | crate::Level::Expect) {
|
||||
diag.level = crate::Level::Warning;
|
||||
}
|
||||
FutureBreakageItem {
|
||||
|
|
|
@ -905,8 +905,8 @@ impl<'a> DiagCtxtHandle<'a> {
|
|||
DelayedBug => {
|
||||
return self.inner.borrow_mut().emit_diagnostic(diag, self.tainted_with_errors);
|
||||
}
|
||||
ForceWarning(_) | Warning | Note | OnceNote | Help | OnceHelp | FailureNote | Allow
|
||||
| Expect(_) => None,
|
||||
ForceWarning | Warning | Note | OnceNote | Help | OnceHelp | FailureNote | Allow
|
||||
| Expect => None,
|
||||
};
|
||||
|
||||
// FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic
|
||||
|
@ -1045,7 +1045,7 @@ impl<'a> DiagCtxtHandle<'a> {
|
|||
// Use `ForceWarning` rather than `Warning` to guarantee emission, e.g. with a
|
||||
// configuration like `--cap-lints allow --force-warn bare_trait_objects`.
|
||||
inner.emit_diagnostic(
|
||||
DiagInner::new(ForceWarning(None), DiagMessage::Str(warnings)),
|
||||
DiagInner::new(ForceWarning, DiagMessage::Str(warnings)),
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
@ -1450,7 +1450,7 @@ impl<'a> DiagCtxtHandle<'a> {
|
|||
#[rustc_lint_diagnostics]
|
||||
#[track_caller]
|
||||
pub fn struct_expect(self, msg: impl Into<DiagMessage>, id: LintExpectationId) -> Diag<'a, ()> {
|
||||
Diag::new(self, Expect(id), msg)
|
||||
Diag::new(self, Expect, msg).with_lint_id(id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1510,7 +1510,7 @@ impl DiagCtxtInner {
|
|||
// Future breakages aren't emitted if they're `Level::Allow` or
|
||||
// `Level::Expect`, but they still need to be constructed and
|
||||
// stashed below, so they'll trigger the must_produce_diag check.
|
||||
assert_matches!(diagnostic.level, Error | Warning | Allow | Expect(_));
|
||||
assert_matches!(diagnostic.level, Error | Warning | Allow | Expect);
|
||||
self.future_breakage_diagnostics.push(diagnostic.clone());
|
||||
}
|
||||
|
||||
|
@ -1558,7 +1558,7 @@ impl DiagCtxtInner {
|
|||
};
|
||||
}
|
||||
}
|
||||
ForceWarning(None) => {} // `ForceWarning(Some(...))` is below, with `Expect`
|
||||
ForceWarning if diagnostic.lint_id.is_none() => {} // `ForceWarning(Some(...))` is below, with `Expect`
|
||||
Warning => {
|
||||
if !self.flags.can_emit_warnings {
|
||||
// We are not emitting warnings.
|
||||
|
@ -1580,9 +1580,9 @@ impl DiagCtxtInner {
|
|||
}
|
||||
return None;
|
||||
}
|
||||
Expect(expect_id) | ForceWarning(Some(expect_id)) => {
|
||||
self.fulfilled_expectations.insert(expect_id);
|
||||
if let Expect(_) = diagnostic.level {
|
||||
Expect | ForceWarning => {
|
||||
self.fulfilled_expectations.insert(diagnostic.lint_id.unwrap());
|
||||
if let Expect = diagnostic.level {
|
||||
// Nothing emitted here for expected lints.
|
||||
TRACK_DIAGNOSTIC(diagnostic, &mut |_| None);
|
||||
self.suppressed_expected_diag = true;
|
||||
|
@ -1631,7 +1631,7 @@ impl DiagCtxtInner {
|
|||
|
||||
if is_error {
|
||||
self.deduplicated_err_count += 1;
|
||||
} else if matches!(diagnostic.level, ForceWarning(_) | Warning) {
|
||||
} else if matches!(diagnostic.level, ForceWarning | Warning) {
|
||||
self.deduplicated_warn_count += 1;
|
||||
}
|
||||
self.has_printed = true;
|
||||
|
@ -1899,9 +1899,9 @@ pub enum Level {
|
|||
/// A `force-warn` lint warning about the code being compiled. Does not prevent compilation
|
||||
/// from finishing.
|
||||
///
|
||||
/// The [`LintExpectationId`] is used for expected lint diagnostics. In all other cases this
|
||||
/// Requires a [`LintExpectationId`] for expected lint diagnostics. In all other cases this
|
||||
/// should be `None`.
|
||||
ForceWarning(Option<LintExpectationId>),
|
||||
ForceWarning,
|
||||
|
||||
/// A warning about the code being compiled. Does not prevent compilation from finishing.
|
||||
/// Will be skipped if `can_emit_warnings` is false.
|
||||
|
@ -1926,8 +1926,8 @@ pub enum Level {
|
|||
/// Only used for lints.
|
||||
Allow,
|
||||
|
||||
/// Only used for lints.
|
||||
Expect(LintExpectationId),
|
||||
/// Only used for lints. Requires a [`LintExpectationId`] for silencing the lints.
|
||||
Expect,
|
||||
}
|
||||
|
||||
impl fmt::Display for Level {
|
||||
|
@ -1943,7 +1943,7 @@ impl Level {
|
|||
Bug | Fatal | Error | DelayedBug => {
|
||||
spec.set_fg(Some(Color::Red)).set_intense(true);
|
||||
}
|
||||
ForceWarning(_) | Warning => {
|
||||
ForceWarning | Warning => {
|
||||
spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows));
|
||||
}
|
||||
Note | OnceNote => {
|
||||
|
@ -1953,7 +1953,7 @@ impl Level {
|
|||
spec.set_fg(Some(Color::Cyan)).set_intense(true);
|
||||
}
|
||||
FailureNote => {}
|
||||
Allow | Expect(_) => unreachable!(),
|
||||
Allow | Expect => unreachable!(),
|
||||
}
|
||||
spec
|
||||
}
|
||||
|
@ -1962,11 +1962,11 @@ impl Level {
|
|||
match self {
|
||||
Bug | DelayedBug => "error: internal compiler error",
|
||||
Fatal | Error => "error",
|
||||
ForceWarning(_) | Warning => "warning",
|
||||
ForceWarning | Warning => "warning",
|
||||
Note | OnceNote => "note",
|
||||
Help | OnceHelp => "help",
|
||||
FailureNote => "failure-note",
|
||||
Allow | Expect(_) => unreachable!(),
|
||||
Allow | Expect => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1977,8 +1977,7 @@ impl Level {
|
|||
// Can this level be used in a subdiagnostic message?
|
||||
fn can_be_subdiag(&self) -> bool {
|
||||
match self {
|
||||
Bug | DelayedBug | Fatal | Error | ForceWarning(_) | FailureNote | Allow
|
||||
| Expect(_) => false,
|
||||
Bug | DelayedBug | Fatal | Error | ForceWarning | FailureNote | Allow | Expect => false,
|
||||
|
||||
Warning | Note | Help | OnceNote | OnceHelp => true,
|
||||
}
|
||||
|
|
|
@ -10,9 +10,9 @@ use rustc_ast::{
|
|||
LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind,
|
||||
};
|
||||
pub use rustc_ast::{
|
||||
AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity,
|
||||
ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto, MetaItemInner, MetaItemLit, Movability,
|
||||
Mutability, UnOp,
|
||||
AssignOp, AssignOpKind, AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind,
|
||||
BoundConstness, BoundPolarity, ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto,
|
||||
MetaItemInner, MetaItemLit, Movability, Mutability, UnOp,
|
||||
};
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
|
@ -2648,7 +2648,7 @@ pub enum ExprKind<'hir> {
|
|||
/// An assignment with an operator.
|
||||
///
|
||||
/// E.g., `a += 1`.
|
||||
AssignOp(BinOp, &'hir Expr<'hir>, &'hir Expr<'hir>),
|
||||
AssignOp(AssignOp, &'hir Expr<'hir>, &'hir Expr<'hir>),
|
||||
/// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct or tuple field.
|
||||
Field(&'hir Expr<'hir>, Ident),
|
||||
/// An indexing operation (`foo[2]`).
|
||||
|
|
|
@ -83,6 +83,12 @@ pub struct HirId {
|
|||
pub local_id: ItemLocalId,
|
||||
}
|
||||
|
||||
// To ensure correctness of incremental compilation,
|
||||
// `HirId` must not implement `Ord` or `PartialOrd`.
|
||||
// See https://github.com/rust-lang/rust/issues/90317.
|
||||
impl !Ord for HirId {}
|
||||
impl !PartialOrd for HirId {}
|
||||
|
||||
impl Debug for HirId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Example: HirId(DefId(0:1 ~ aa[7697]::{use#0}).10)
|
||||
|
@ -116,10 +122,6 @@ impl HirId {
|
|||
pub fn make_owner(owner: LocalDefId) -> Self {
|
||||
Self { owner: OwnerId { def_id: owner }, local_id: ItemLocalId::ZERO }
|
||||
}
|
||||
|
||||
pub fn index(self) -> (usize, usize) {
|
||||
(rustc_index::Idx::index(self.owner.def_id), rustc_index::Idx::index(self.local_id))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for HirId {
|
||||
|
@ -128,18 +130,6 @@ impl fmt::Display for HirId {
|
|||
}
|
||||
}
|
||||
|
||||
impl Ord for HirId {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
(self.index()).cmp(&(other.index()))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for HirId {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
rustc_data_structures::define_stable_id_collections!(HirIdMap, HirIdSet, HirIdMapEntry, HirId);
|
||||
rustc_data_structures::define_id_collections!(
|
||||
ItemLocalMap,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#![feature(debug_closure_helpers)]
|
||||
#![feature(exhaustive_patterns)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(negative_impls)]
|
||||
#![feature(never_type)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(variant_count)]
|
||||
|
|
|
@ -1271,18 +1271,18 @@ impl<'a> State<'a> {
|
|||
self.print_call_post(base_args)
|
||||
}
|
||||
|
||||
fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) {
|
||||
let binop_prec = op.node.precedence();
|
||||
fn print_expr_binary(&mut self, op: hir::BinOpKind, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) {
|
||||
let binop_prec = op.precedence();
|
||||
let left_prec = lhs.precedence();
|
||||
let right_prec = rhs.precedence();
|
||||
|
||||
let (mut left_needs_paren, right_needs_paren) = match op.node.fixity() {
|
||||
let (mut left_needs_paren, right_needs_paren) = match op.fixity() {
|
||||
Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec),
|
||||
Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec),
|
||||
Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec),
|
||||
};
|
||||
|
||||
match (&lhs.kind, op.node) {
|
||||
match (&lhs.kind, op) {
|
||||
// These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is
|
||||
// the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead
|
||||
// of `(x as i32) < ...`. We need to convince it _not_ to do that.
|
||||
|
@ -1297,7 +1297,7 @@ impl<'a> State<'a> {
|
|||
|
||||
self.print_expr_cond_paren(lhs, left_needs_paren);
|
||||
self.space();
|
||||
self.word_space(op.node.as_str());
|
||||
self.word_space(op.as_str());
|
||||
self.print_expr_cond_paren(rhs, right_needs_paren);
|
||||
}
|
||||
|
||||
|
@ -1451,7 +1451,7 @@ impl<'a> State<'a> {
|
|||
self.word(".use");
|
||||
}
|
||||
hir::ExprKind::Binary(op, lhs, rhs) => {
|
||||
self.print_expr_binary(op, lhs, rhs);
|
||||
self.print_expr_binary(op.node, lhs, rhs);
|
||||
}
|
||||
hir::ExprKind::Unary(op, expr) => {
|
||||
self.print_expr_unary(op, expr);
|
||||
|
@ -1572,8 +1572,7 @@ impl<'a> State<'a> {
|
|||
hir::ExprKind::AssignOp(op, lhs, rhs) => {
|
||||
self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign);
|
||||
self.space();
|
||||
self.word(op.node.as_str());
|
||||
self.word_space("=");
|
||||
self.word_space(op.node.as_str());
|
||||
self.print_expr_cond_paren(rhs, rhs.precedence() < ExprPrecedence::Assign);
|
||||
}
|
||||
hir::ExprKind::Field(expr, ident) => {
|
||||
|
|
|
@ -512,7 +512,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
self.check_expr_assign(expr, expected, lhs, rhs, span)
|
||||
}
|
||||
ExprKind::AssignOp(op, lhs, rhs) => {
|
||||
self.check_expr_binop_assign(expr, op, lhs, rhs, expected)
|
||||
self.check_expr_assign_op(expr, op, lhs, rhs, expected)
|
||||
}
|
||||
ExprKind::Unary(unop, oprnd) => self.check_expr_unop(unop, oprnd, expected, expr),
|
||||
ExprKind::AddrOf(kind, mutbl, oprnd) => {
|
||||
|
|
|
@ -3477,30 +3477,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
lhs_ty: Ty<'tcx>,
|
||||
rhs_expr: &'tcx hir::Expr<'tcx>,
|
||||
lhs_expr: &'tcx hir::Expr<'tcx>,
|
||||
op: hir::BinOp,
|
||||
) {
|
||||
match op.node {
|
||||
hir::BinOpKind::Eq => {
|
||||
if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait()
|
||||
&& self
|
||||
.infcx
|
||||
.type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env)
|
||||
.must_apply_modulo_regions()
|
||||
{
|
||||
let sm = self.tcx.sess.source_map();
|
||||
if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span)
|
||||
&& let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span)
|
||||
{
|
||||
err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`"));
|
||||
err.multipart_suggestion(
|
||||
"consider swapping the equality",
|
||||
vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait()
|
||||
&& self
|
||||
.infcx
|
||||
.type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env)
|
||||
.must_apply_modulo_regions()
|
||||
{
|
||||
let sm = self.tcx.sess.source_map();
|
||||
if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span)
|
||||
&& let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span)
|
||||
{
|
||||
err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`"));
|
||||
err.multipart_suggestion(
|
||||
"consider swapping the equality",
|
||||
vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,15 +4,15 @@ use rustc_data_structures::packed::Pu128;
|
|||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{Applicability, Diag, struct_span_code_err};
|
||||
use rustc_infer::traits::ObligationCauseCode;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::adjustment::{
|
||||
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
|
||||
};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_session::errors::ExprParenthesesNeeded;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{Ident, Span, sym};
|
||||
use rustc_span::{Ident, Span, Symbol, sym};
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::{FulfillmentError, Obligation, ObligationCtxt};
|
||||
use tracing::debug;
|
||||
|
@ -24,24 +24,27 @@ use crate::Expectation;
|
|||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
/// Checks a `a <op>= b`
|
||||
pub(crate) fn check_expr_binop_assign(
|
||||
pub(crate) fn check_expr_assign_op(
|
||||
&self,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
op: hir::BinOp,
|
||||
op: hir::AssignOp,
|
||||
lhs: &'tcx hir::Expr<'tcx>,
|
||||
rhs: &'tcx hir::Expr<'tcx>,
|
||||
expected: Expectation<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let (lhs_ty, rhs_ty, return_ty) =
|
||||
self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes, expected);
|
||||
self.check_overloaded_binop(expr, lhs, rhs, Op::AssignOp(op), expected);
|
||||
|
||||
let ty =
|
||||
if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
|
||||
self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, op);
|
||||
self.tcx.types.unit
|
||||
} else {
|
||||
return_ty
|
||||
};
|
||||
let category = BinOpCategory::from(op.node);
|
||||
let ty = if !lhs_ty.is_ty_var()
|
||||
&& !rhs_ty.is_ty_var()
|
||||
&& is_builtin_binop(lhs_ty, rhs_ty, category)
|
||||
{
|
||||
self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, category);
|
||||
self.tcx.types.unit
|
||||
} else {
|
||||
return_ty
|
||||
};
|
||||
|
||||
self.check_lhs_assignable(lhs, E0067, op.span, |err| {
|
||||
if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
|
||||
|
@ -49,7 +52,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.lookup_op_method(
|
||||
(lhs, lhs_deref_ty),
|
||||
Some((rhs, rhs_ty)),
|
||||
Op::Binary(op, IsAssign::Yes),
|
||||
lang_item_for_binop(self.tcx, Op::AssignOp(op)),
|
||||
op.span,
|
||||
expected,
|
||||
)
|
||||
.is_ok()
|
||||
|
@ -60,7 +64,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.lookup_op_method(
|
||||
(lhs, lhs_ty),
|
||||
Some((rhs, rhs_ty)),
|
||||
Op::Binary(op, IsAssign::Yes),
|
||||
lang_item_for_binop(self.tcx, Op::AssignOp(op)),
|
||||
op.span,
|
||||
expected,
|
||||
)
|
||||
.is_err()
|
||||
|
@ -98,7 +103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
expr.hir_id, expr, op, lhs_expr, rhs_expr
|
||||
);
|
||||
|
||||
match BinOpCategory::from(op) {
|
||||
match BinOpCategory::from(op.node) {
|
||||
BinOpCategory::Shortcircuit => {
|
||||
// && and || are a simple case.
|
||||
self.check_expr_coercible_to_type(lhs_expr, tcx.types.bool, None);
|
||||
|
@ -114,14 +119,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// Otherwise, we always treat operators as if they are
|
||||
// overloaded. This is the way to be most flexible w/r/t
|
||||
// types that get inferred.
|
||||
let (lhs_ty, rhs_ty, return_ty) = self.check_overloaded_binop(
|
||||
expr,
|
||||
lhs_expr,
|
||||
rhs_expr,
|
||||
op,
|
||||
IsAssign::No,
|
||||
expected,
|
||||
);
|
||||
let (lhs_ty, rhs_ty, return_ty) =
|
||||
self.check_overloaded_binop(expr, lhs_expr, rhs_expr, Op::BinOp(op), expected);
|
||||
|
||||
// Supply type inference hints if relevant. Probably these
|
||||
// hints should be enforced during select as part of the
|
||||
|
@ -135,16 +134,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// deduce that the result type should be `u32`, even
|
||||
// though we don't know yet what type 2 has and hence
|
||||
// can't pin this down to a specific impl.
|
||||
let category = BinOpCategory::from(op.node);
|
||||
if !lhs_ty.is_ty_var()
|
||||
&& !rhs_ty.is_ty_var()
|
||||
&& is_builtin_binop(lhs_ty, rhs_ty, op)
|
||||
&& is_builtin_binop(lhs_ty, rhs_ty, category)
|
||||
{
|
||||
let builtin_return_ty = self.enforce_builtin_binop_types(
|
||||
lhs_expr.span,
|
||||
lhs_ty,
|
||||
rhs_expr.span,
|
||||
rhs_ty,
|
||||
op,
|
||||
category,
|
||||
);
|
||||
self.demand_eqtype(expr.span, builtin_return_ty, return_ty);
|
||||
builtin_return_ty
|
||||
|
@ -161,16 +161,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
lhs_ty: Ty<'tcx>,
|
||||
rhs_span: Span,
|
||||
rhs_ty: Ty<'tcx>,
|
||||
op: hir::BinOp,
|
||||
category: BinOpCategory,
|
||||
) -> Ty<'tcx> {
|
||||
debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
|
||||
debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, category));
|
||||
|
||||
// Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
|
||||
// (See https://github.com/rust-lang/rust/issues/57447.)
|
||||
let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty));
|
||||
|
||||
let tcx = self.tcx;
|
||||
match BinOpCategory::from(op) {
|
||||
match category {
|
||||
BinOpCategory::Shortcircuit => {
|
||||
self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty);
|
||||
self.demand_suptype(rhs_span, tcx.types.bool, rhs_ty);
|
||||
|
@ -201,17 +201,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
expr: &'tcx hir::Expr<'tcx>,
|
||||
lhs_expr: &'tcx hir::Expr<'tcx>,
|
||||
rhs_expr: &'tcx hir::Expr<'tcx>,
|
||||
op: hir::BinOp,
|
||||
is_assign: IsAssign,
|
||||
op: Op,
|
||||
expected: Expectation<'tcx>,
|
||||
) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
|
||||
debug!(
|
||||
"check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})",
|
||||
expr.hir_id, op, is_assign
|
||||
);
|
||||
debug!("check_overloaded_binop(expr.hir_id={}, op={:?})", expr.hir_id, op);
|
||||
|
||||
let lhs_ty = match is_assign {
|
||||
IsAssign::No => {
|
||||
let lhs_ty = match op {
|
||||
Op::BinOp(_) => {
|
||||
// Find a suitable supertype of the LHS expression's type, by coercing to
|
||||
// a type variable, to pass as the `Self` to the trait, avoiding invariant
|
||||
// trait matching creating lifetime constraints that are too strict.
|
||||
|
@ -221,7 +217,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let fresh_var = self.next_ty_var(lhs_expr.span);
|
||||
self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No)
|
||||
}
|
||||
IsAssign::Yes => {
|
||||
Op::AssignOp(_) => {
|
||||
// rust-lang/rust#52126: We have to use strict
|
||||
// equivalence on the LHS of an assign-op like `+=`;
|
||||
// overwritten or mutably-borrowed places cannot be
|
||||
|
@ -242,7 +238,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let result = self.lookup_op_method(
|
||||
(lhs_expr, lhs_ty),
|
||||
Some((rhs_expr, rhs_ty_var)),
|
||||
Op::Binary(op, is_assign),
|
||||
lang_item_for_binop(self.tcx, op),
|
||||
op.span(),
|
||||
expected,
|
||||
);
|
||||
|
||||
|
@ -252,15 +249,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
rhs_ty_var,
|
||||
Some(lhs_expr),
|
||||
|err, ty| {
|
||||
self.suggest_swapping_lhs_and_rhs(err, ty, lhs_ty, rhs_expr, lhs_expr, op);
|
||||
if let Op::BinOp(binop) = op
|
||||
&& binop.node == hir::BinOpKind::Eq
|
||||
{
|
||||
self.suggest_swapping_lhs_and_rhs(err, ty, lhs_ty, rhs_expr, lhs_expr);
|
||||
}
|
||||
},
|
||||
);
|
||||
let rhs_ty = self.resolve_vars_with_obligations(rhs_ty);
|
||||
|
||||
let return_ty = match result {
|
||||
Ok(method) => {
|
||||
let by_ref_binop = !op.node.is_by_value();
|
||||
if is_assign == IsAssign::Yes || by_ref_binop {
|
||||
let by_ref_binop = !op.is_by_value();
|
||||
if matches!(op, Op::AssignOp(_)) || by_ref_binop {
|
||||
if let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() {
|
||||
let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes);
|
||||
let autoref = Adjustment {
|
||||
|
@ -301,32 +302,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
Ty::new_misc_error(self.tcx)
|
||||
}
|
||||
Err(errors) => {
|
||||
let (_, trait_def_id) =
|
||||
lang_item_for_op(self.tcx, Op::Binary(op, is_assign), op.span);
|
||||
let (_, trait_def_id) = lang_item_for_binop(self.tcx, op);
|
||||
let missing_trait = trait_def_id
|
||||
.map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id)));
|
||||
let mut path = None;
|
||||
let lhs_ty_str = self.tcx.short_string(lhs_ty, &mut path);
|
||||
let rhs_ty_str = self.tcx.short_string(rhs_ty, &mut path);
|
||||
let (mut err, output_def_id) = match is_assign {
|
||||
IsAssign::Yes => {
|
||||
let (mut err, output_def_id) = match op {
|
||||
Op::AssignOp(assign_op) => {
|
||||
let s = assign_op.node.as_str();
|
||||
let mut err = struct_span_code_err!(
|
||||
self.dcx(),
|
||||
expr.span,
|
||||
E0368,
|
||||
"binary assignment operation `{}=` cannot be applied to type `{}`",
|
||||
op.node.as_str(),
|
||||
"binary assignment operation `{}` cannot be applied to type `{}`",
|
||||
s,
|
||||
lhs_ty_str,
|
||||
);
|
||||
err.span_label(
|
||||
lhs_expr.span,
|
||||
format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty_str),
|
||||
format!("cannot use `{}` on type `{}`", s, lhs_ty_str),
|
||||
);
|
||||
self.note_unmet_impls_on_type(&mut err, errors, false);
|
||||
(err, None)
|
||||
}
|
||||
IsAssign::No => {
|
||||
let message = match op.node {
|
||||
Op::BinOp(bin_op) => {
|
||||
let message = match bin_op.node {
|
||||
hir::BinOpKind::Add => {
|
||||
format!("cannot add `{rhs_ty_str}` to `{lhs_ty_str}`")
|
||||
}
|
||||
|
@ -362,8 +363,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
_ => format!(
|
||||
"binary operation `{}` cannot be applied to type `{}`",
|
||||
op.node.as_str(),
|
||||
lhs_ty_str,
|
||||
bin_op.node.as_str(),
|
||||
lhs_ty_str
|
||||
),
|
||||
};
|
||||
let output_def_id = trait_def_id.and_then(|def_id| {
|
||||
|
@ -376,7 +377,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.cloned()
|
||||
});
|
||||
let mut err =
|
||||
struct_span_code_err!(self.dcx(), op.span, E0369, "{message}");
|
||||
struct_span_code_err!(self.dcx(), bin_op.span, E0369, "{message}");
|
||||
if !lhs_expr.span.eq(&rhs_expr.span) {
|
||||
err.span_label(lhs_expr.span, lhs_ty_str.clone());
|
||||
err.span_label(rhs_expr.span, rhs_ty_str);
|
||||
|
@ -409,18 +410,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.lookup_op_method(
|
||||
(lhs_expr, lhs_deref_ty),
|
||||
Some((rhs_expr, rhs_ty)),
|
||||
Op::Binary(op, is_assign),
|
||||
lang_item_for_binop(self.tcx, op),
|
||||
op.span(),
|
||||
expected,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
let msg = format!(
|
||||
"`{}{}` can be used on `{}` if you dereference the left-hand side",
|
||||
op.node.as_str(),
|
||||
match is_assign {
|
||||
IsAssign::Yes => "=",
|
||||
IsAssign::No => "",
|
||||
},
|
||||
"`{}` can be used on `{}` if you dereference the left-hand side",
|
||||
op.as_str(),
|
||||
self.tcx.short_string(lhs_deref_ty, err.long_ty_path()),
|
||||
);
|
||||
err.span_suggestion_verbose(
|
||||
|
@ -442,14 +440,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.lookup_op_method(
|
||||
(lhs_expr, lhs_adjusted_ty),
|
||||
Some((rhs_expr, rhs_adjusted_ty)),
|
||||
Op::Binary(op, is_assign),
|
||||
lang_item_for_binop(self.tcx, op),
|
||||
op.span(),
|
||||
expected,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
let lhs = self.tcx.short_string(lhs_adjusted_ty, err.long_ty_path());
|
||||
let rhs = self.tcx.short_string(rhs_adjusted_ty, err.long_ty_path());
|
||||
let op = op.node.as_str();
|
||||
let op = op.as_str();
|
||||
err.note(format!("an implementation for `{lhs} {op} {rhs}` exists"));
|
||||
|
||||
if let Some(lhs_new_mutbl) = lhs_new_mutbl
|
||||
|
@ -499,7 +498,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
self.lookup_op_method(
|
||||
(lhs_expr, lhs_ty),
|
||||
Some((rhs_expr, rhs_ty)),
|
||||
Op::Binary(op, is_assign),
|
||||
lang_item_for_binop(self.tcx, op),
|
||||
op.span(),
|
||||
expected,
|
||||
)
|
||||
.is_ok()
|
||||
|
@ -511,13 +511,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
// We should suggest `a + b` => `*a + b` if `a` is copy, and suggest
|
||||
// `a += b` => `*a += b` if a is a mut ref.
|
||||
if !op.span.can_be_used_for_suggestions() {
|
||||
if !op.span().can_be_used_for_suggestions() {
|
||||
// Suppress suggestions when lhs and rhs are not in the same span as the error
|
||||
} else if is_assign == IsAssign::Yes
|
||||
} else if let Op::AssignOp(_) = op
|
||||
&& let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty)
|
||||
{
|
||||
suggest_deref_binop(&mut err, lhs_deref_ty);
|
||||
} else if is_assign == IsAssign::No
|
||||
} else if let Op::BinOp(_) = op
|
||||
&& let ty::Ref(region, lhs_deref_ty, mutbl) = lhs_ty.kind()
|
||||
{
|
||||
if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty) {
|
||||
|
@ -572,10 +572,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
if let Some(missing_trait) = missing_trait {
|
||||
if op.node == hir::BinOpKind::Add
|
||||
&& self.check_str_addition(
|
||||
lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, is_assign, op,
|
||||
)
|
||||
if matches!(
|
||||
op,
|
||||
Op::BinOp(Spanned { node: hir::BinOpKind::Add, .. })
|
||||
| Op::AssignOp(Spanned { node: hir::AssignOpKind::AddAssign, .. })
|
||||
) && self
|
||||
.check_str_addition(lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, op)
|
||||
{
|
||||
// This has nothing here because it means we did string
|
||||
// concatenation (e.g., "Hello " + "World!"). This means
|
||||
|
@ -592,7 +594,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.lookup_op_method(
|
||||
(lhs_expr, lhs_ty),
|
||||
Some((rhs_expr, rhs_ty)),
|
||||
Op::Binary(op, is_assign),
|
||||
lang_item_for_binop(self.tcx, op),
|
||||
op.span(),
|
||||
expected,
|
||||
)
|
||||
.unwrap_err();
|
||||
|
@ -642,9 +645,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
// Suggest using `add`, `offset` or `offset_from` for pointer - {integer},
|
||||
// pointer + {integer} or pointer - pointer.
|
||||
if op.span.can_be_used_for_suggestions() {
|
||||
match op.node {
|
||||
hir::BinOpKind::Add if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() => {
|
||||
if op.span().can_be_used_for_suggestions() {
|
||||
match op {
|
||||
Op::BinOp(Spanned { node: hir::BinOpKind::Add, .. })
|
||||
if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() =>
|
||||
{
|
||||
err.multipart_suggestion(
|
||||
"consider using `wrapping_add` or `add` for pointer + {integer}",
|
||||
vec![
|
||||
|
@ -657,7 +662,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
hir::BinOpKind::Sub => {
|
||||
Op::BinOp(Spanned { node: hir::BinOpKind::Sub, .. }) => {
|
||||
if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() {
|
||||
err.multipart_suggestion(
|
||||
"consider using `wrapping_sub` or `sub` for \
|
||||
|
@ -713,8 +718,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
lhs_ty: Ty<'tcx>,
|
||||
rhs_ty: Ty<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
is_assign: IsAssign,
|
||||
op: hir::BinOp,
|
||||
op: Op,
|
||||
) -> bool {
|
||||
let str_concat_note = "string concatenation requires an owned `String` on the left";
|
||||
let rm_borrow_msg = "remove the borrow to obtain an owned `String`";
|
||||
|
@ -733,8 +737,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
r_ty.kind(), ty::Ref(_, inner_ty, _) if *inner_ty.kind() == ty::Str
|
||||
)) =>
|
||||
{
|
||||
if let IsAssign::No = is_assign { // Do not supply this message if `&str += &str`
|
||||
err.span_label(op.span, "`+` cannot be used to concatenate two `&str` strings");
|
||||
if let Op::BinOp(_) = op { // Do not supply this message if `&str += &str`
|
||||
err.span_label(
|
||||
op.span(),
|
||||
"`+` cannot be used to concatenate two `&str` strings"
|
||||
);
|
||||
err.note(str_concat_note);
|
||||
if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
|
||||
err.span_suggestion_verbose(
|
||||
|
@ -758,11 +765,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
if (*l_ty.kind() == ty::Str || is_std_string(l_ty)) && is_std_string(rhs_ty) =>
|
||||
{
|
||||
err.span_label(
|
||||
op.span,
|
||||
op.span(),
|
||||
"`+` cannot be used to concatenate a `&str` with a `String`",
|
||||
);
|
||||
match is_assign {
|
||||
IsAssign::No => {
|
||||
match op {
|
||||
Op::BinOp(_) => {
|
||||
let sugg_msg;
|
||||
let lhs_sugg = if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
|
||||
sugg_msg = "remove the borrow on the left and add one on the right";
|
||||
|
@ -781,7 +788,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
IsAssign::Yes => {
|
||||
Op::AssignOp(_) => {
|
||||
err.note(str_concat_note);
|
||||
}
|
||||
}
|
||||
|
@ -799,7 +806,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
expected: Expectation<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
assert!(op.is_by_value());
|
||||
match self.lookup_op_method((ex, operand_ty), None, Op::Unary(op, ex.span), expected) {
|
||||
match self.lookup_op_method(
|
||||
(ex, operand_ty),
|
||||
None,
|
||||
lang_item_for_unop(self.tcx, op),
|
||||
ex.span,
|
||||
expected,
|
||||
) {
|
||||
Ok(method) => {
|
||||
self.write_method_call_and_enforce_effects(ex.hir_id, ex.span, method);
|
||||
method.sig.output()
|
||||
|
@ -898,21 +911,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
&self,
|
||||
(lhs_expr, lhs_ty): (&'tcx hir::Expr<'tcx>, Ty<'tcx>),
|
||||
opt_rhs: Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)>,
|
||||
op: Op,
|
||||
(opname, trait_did): (Symbol, Option<hir::def_id::DefId>),
|
||||
span: Span,
|
||||
expected: Expectation<'tcx>,
|
||||
) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
|
||||
let span = match op {
|
||||
Op::Binary(op, _) => op.span,
|
||||
Op::Unary(_, span) => span,
|
||||
};
|
||||
let (opname, Some(trait_did)) = lang_item_for_op(self.tcx, op, span) else {
|
||||
let Some(trait_did) = trait_did else {
|
||||
// Bail if the operator trait is not defined.
|
||||
return Err(vec![]);
|
||||
};
|
||||
|
||||
debug!(
|
||||
"lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})",
|
||||
lhs_ty, op, opname, trait_did
|
||||
"lookup_op_method(lhs_ty={:?}, opname={:?}, trait_did={:?})",
|
||||
lhs_ty, opname, trait_did
|
||||
);
|
||||
|
||||
let opname = Ident::with_dummy_span(opname);
|
||||
|
@ -980,37 +990,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn lang_item_for_op(
|
||||
tcx: TyCtxt<'_>,
|
||||
op: Op,
|
||||
span: Span,
|
||||
) -> (rustc_span::Symbol, Option<hir::def_id::DefId>) {
|
||||
fn lang_item_for_binop(tcx: TyCtxt<'_>, op: Op) -> (Symbol, Option<hir::def_id::DefId>) {
|
||||
let lang = tcx.lang_items();
|
||||
if let Op::Binary(op, IsAssign::Yes) = op {
|
||||
match op.node {
|
||||
hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()),
|
||||
hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()),
|
||||
hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()),
|
||||
hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()),
|
||||
hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()),
|
||||
hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()),
|
||||
hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()),
|
||||
hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()),
|
||||
hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()),
|
||||
hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()),
|
||||
hir::BinOpKind::Lt
|
||||
| hir::BinOpKind::Le
|
||||
| hir::BinOpKind::Ge
|
||||
| hir::BinOpKind::Gt
|
||||
| hir::BinOpKind::Eq
|
||||
| hir::BinOpKind::Ne
|
||||
| hir::BinOpKind::And
|
||||
| hir::BinOpKind::Or => {
|
||||
span_bug!(span, "impossible assignment operation: {}=", op.node.as_str())
|
||||
}
|
||||
}
|
||||
} else if let Op::Binary(op, IsAssign::No) = op {
|
||||
match op.node {
|
||||
match op {
|
||||
Op::AssignOp(op) => match op.node {
|
||||
hir::AssignOpKind::AddAssign => (sym::add_assign, lang.add_assign_trait()),
|
||||
hir::AssignOpKind::SubAssign => (sym::sub_assign, lang.sub_assign_trait()),
|
||||
hir::AssignOpKind::MulAssign => (sym::mul_assign, lang.mul_assign_trait()),
|
||||
hir::AssignOpKind::DivAssign => (sym::div_assign, lang.div_assign_trait()),
|
||||
hir::AssignOpKind::RemAssign => (sym::rem_assign, lang.rem_assign_trait()),
|
||||
hir::AssignOpKind::BitXorAssign => (sym::bitxor_assign, lang.bitxor_assign_trait()),
|
||||
hir::AssignOpKind::BitAndAssign => (sym::bitand_assign, lang.bitand_assign_trait()),
|
||||
hir::AssignOpKind::BitOrAssign => (sym::bitor_assign, lang.bitor_assign_trait()),
|
||||
hir::AssignOpKind::ShlAssign => (sym::shl_assign, lang.shl_assign_trait()),
|
||||
hir::AssignOpKind::ShrAssign => (sym::shr_assign, lang.shr_assign_trait()),
|
||||
},
|
||||
Op::BinOp(op) => match op.node {
|
||||
hir::BinOpKind::Add => (sym::add, lang.add_trait()),
|
||||
hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()),
|
||||
hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()),
|
||||
|
@ -1028,20 +1023,24 @@ fn lang_item_for_op(
|
|||
hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()),
|
||||
hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()),
|
||||
hir::BinOpKind::And | hir::BinOpKind::Or => {
|
||||
span_bug!(span, "&& and || are not overloadable")
|
||||
bug!("&& and || are not overloadable")
|
||||
}
|
||||
}
|
||||
} else if let Op::Unary(hir::UnOp::Not, _) = op {
|
||||
(sym::not, lang.not_trait())
|
||||
} else if let Op::Unary(hir::UnOp::Neg, _) = op {
|
||||
(sym::neg, lang.neg_trait())
|
||||
} else {
|
||||
bug!("lookup_op_method: op not supported: {:?}", op)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn lang_item_for_unop(tcx: TyCtxt<'_>, op: hir::UnOp) -> (Symbol, Option<hir::def_id::DefId>) {
|
||||
let lang = tcx.lang_items();
|
||||
match op {
|
||||
hir::UnOp::Not => (sym::not, lang.not_trait()),
|
||||
hir::UnOp::Neg => (sym::neg, lang.neg_trait()),
|
||||
hir::UnOp::Deref => bug!("Deref is not overloadable"),
|
||||
}
|
||||
}
|
||||
|
||||
// Binary operator categories. These categories summarize the behavior
|
||||
// with respect to the builtin operations supported.
|
||||
#[derive(Clone, Copy)]
|
||||
enum BinOpCategory {
|
||||
/// &&, || -- cannot be overridden
|
||||
Shortcircuit,
|
||||
|
@ -1063,44 +1062,58 @@ enum BinOpCategory {
|
|||
Comparison,
|
||||
}
|
||||
|
||||
impl BinOpCategory {
|
||||
fn from(op: hir::BinOp) -> BinOpCategory {
|
||||
match op.node {
|
||||
hir::BinOpKind::Shl | hir::BinOpKind::Shr => BinOpCategory::Shift,
|
||||
|
||||
hir::BinOpKind::Add
|
||||
| hir::BinOpKind::Sub
|
||||
| hir::BinOpKind::Mul
|
||||
| hir::BinOpKind::Div
|
||||
| hir::BinOpKind::Rem => BinOpCategory::Math,
|
||||
|
||||
hir::BinOpKind::BitXor | hir::BinOpKind::BitAnd | hir::BinOpKind::BitOr => {
|
||||
BinOpCategory::Bitwise
|
||||
}
|
||||
|
||||
hir::BinOpKind::Eq
|
||||
| hir::BinOpKind::Ne
|
||||
| hir::BinOpKind::Lt
|
||||
| hir::BinOpKind::Le
|
||||
| hir::BinOpKind::Ge
|
||||
| hir::BinOpKind::Gt => BinOpCategory::Comparison,
|
||||
|
||||
hir::BinOpKind::And | hir::BinOpKind::Or => BinOpCategory::Shortcircuit,
|
||||
impl From<hir::BinOpKind> for BinOpCategory {
|
||||
fn from(op: hir::BinOpKind) -> BinOpCategory {
|
||||
use hir::BinOpKind::*;
|
||||
match op {
|
||||
Shl | Shr => BinOpCategory::Shift,
|
||||
Add | Sub | Mul | Div | Rem => BinOpCategory::Math,
|
||||
BitXor | BitAnd | BitOr => BinOpCategory::Bitwise,
|
||||
Eq | Ne | Lt | Le | Ge | Gt => BinOpCategory::Comparison,
|
||||
And | Or => BinOpCategory::Shortcircuit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the binary operation is an assignment (`a += b`), or not (`a + b`)
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum IsAssign {
|
||||
No,
|
||||
Yes,
|
||||
impl From<hir::AssignOpKind> for BinOpCategory {
|
||||
fn from(op: hir::AssignOpKind) -> BinOpCategory {
|
||||
use hir::AssignOpKind::*;
|
||||
match op {
|
||||
ShlAssign | ShrAssign => BinOpCategory::Shift,
|
||||
AddAssign | SubAssign | MulAssign | DivAssign | RemAssign => BinOpCategory::Math,
|
||||
BitXorAssign | BitAndAssign | BitOrAssign => BinOpCategory::Bitwise,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
/// An assignment op (e.g. `a += b`), or a binary op (e.g. `a + b`).
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum Op {
|
||||
Binary(hir::BinOp, IsAssign),
|
||||
Unary(hir::UnOp, Span),
|
||||
BinOp(hir::BinOp),
|
||||
AssignOp(hir::AssignOp),
|
||||
}
|
||||
|
||||
impl Op {
|
||||
fn span(&self) -> Span {
|
||||
match self {
|
||||
Op::BinOp(op) => op.span,
|
||||
Op::AssignOp(op) => op.span,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Op::BinOp(op) => op.node.as_str(),
|
||||
Op::AssignOp(op) => op.node.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_by_value(&self) -> bool {
|
||||
match self {
|
||||
Op::BinOp(op) => op.node.is_by_value(),
|
||||
Op::AssignOp(op) => op.node.is_by_value(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Dereferences a single level of immutable referencing.
|
||||
|
@ -1127,27 +1140,24 @@ fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> {
|
|||
/// Reason #2 is the killer. I tried for a while to always use
|
||||
/// overloaded logic and just check the types in constants/codegen after
|
||||
/// the fact, and it worked fine, except for SIMD types. -nmatsakis
|
||||
fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool {
|
||||
fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, category: BinOpCategory) -> bool {
|
||||
// Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
|
||||
// (See https://github.com/rust-lang/rust/issues/57447.)
|
||||
let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
|
||||
|
||||
match BinOpCategory::from(op) {
|
||||
match category.into() {
|
||||
BinOpCategory::Shortcircuit => true,
|
||||
|
||||
BinOpCategory::Shift => {
|
||||
lhs.references_error()
|
||||
|| rhs.references_error()
|
||||
|| lhs.is_integral() && rhs.is_integral()
|
||||
}
|
||||
|
||||
BinOpCategory::Math => {
|
||||
lhs.references_error()
|
||||
|| rhs.references_error()
|
||||
|| lhs.is_integral() && rhs.is_integral()
|
||||
|| lhs.is_floating_point() && rhs.is_floating_point()
|
||||
}
|
||||
|
||||
BinOpCategory::Bitwise => {
|
||||
lhs.references_error()
|
||||
|| rhs.references_error()
|
||||
|
@ -1155,7 +1165,6 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool
|
|||
|| lhs.is_floating_point() && rhs.is_floating_point()
|
||||
|| lhs.is_bool() && rhs.is_bool()
|
||||
}
|
||||
|
||||
BinOpCategory::Comparison => {
|
||||
lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar()
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
/// Intermediate format to store the hir_id pointing to the use that resulted in the
|
||||
/// corresponding place being captured and a String which contains the captured value's
|
||||
/// name (i.e: a.b.c)
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
enum UpvarMigrationInfo {
|
||||
/// We previously captured all of `x`, but now we capture some sub-path.
|
||||
CapturingPrecise { source_expr: Option<HirId>, var_name: String },
|
||||
|
@ -1396,14 +1396,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
FxIndexSet::default()
|
||||
};
|
||||
|
||||
// Combine all the captures responsible for needing migrations into one HashSet
|
||||
// Combine all the captures responsible for needing migrations into one IndexSet
|
||||
let mut capture_diagnostic = drop_reorder_diagnostic.clone();
|
||||
for key in auto_trait_diagnostic.keys() {
|
||||
capture_diagnostic.insert(key.clone());
|
||||
}
|
||||
|
||||
let mut capture_diagnostic = capture_diagnostic.into_iter().collect::<Vec<_>>();
|
||||
capture_diagnostic.sort();
|
||||
capture_diagnostic.sort_by_cached_key(|info| match info {
|
||||
UpvarMigrationInfo::CapturingPrecise { source_expr: _, var_name } => {
|
||||
(0, Some(var_name.clone()))
|
||||
}
|
||||
UpvarMigrationInfo::CapturingNothing { use_span: _ } => (1, None),
|
||||
});
|
||||
for captures_info in capture_diagnostic {
|
||||
// Get the auto trait reasons of why migration is needed because of that capture, if there are any
|
||||
let capture_trait_reasons =
|
||||
|
@ -2323,8 +2328,9 @@ fn should_do_rust_2021_incompatible_closure_captures_analysis(
|
|||
return false;
|
||||
}
|
||||
|
||||
let (level, _) =
|
||||
tcx.lint_level_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id);
|
||||
let level = tcx
|
||||
.lint_level_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id)
|
||||
.level;
|
||||
|
||||
!matches!(level, lint::Level::Allow)
|
||||
}
|
||||
|
|
|
@ -157,7 +157,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
|||
self.typeck_results.node_args_mut().remove(e.hir_id);
|
||||
}
|
||||
}
|
||||
hir::ExprKind::Binary(ref op, lhs, rhs) | hir::ExprKind::AssignOp(ref op, lhs, rhs) => {
|
||||
hir::ExprKind::Binary(ref op, lhs, rhs) => {
|
||||
let lhs_ty = self.typeck_results.node_type(lhs.hir_id);
|
||||
let rhs_ty = self.typeck_results.node_type(rhs.hir_id);
|
||||
|
||||
|
@ -165,25 +165,27 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
|||
self.typeck_results.type_dependent_defs_mut().remove(e.hir_id);
|
||||
self.typeck_results.node_args_mut().remove(e.hir_id);
|
||||
|
||||
match e.kind {
|
||||
hir::ExprKind::Binary(..) => {
|
||||
if !op.node.is_by_value() {
|
||||
let mut adjustments = self.typeck_results.adjustments_mut();
|
||||
if let Some(a) = adjustments.get_mut(lhs.hir_id) {
|
||||
a.pop();
|
||||
}
|
||||
if let Some(a) = adjustments.get_mut(rhs.hir_id) {
|
||||
a.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
hir::ExprKind::AssignOp(..)
|
||||
if let Some(a) =
|
||||
self.typeck_results.adjustments_mut().get_mut(lhs.hir_id) =>
|
||||
{
|
||||
if !op.node.is_by_value() {
|
||||
let mut adjustments = self.typeck_results.adjustments_mut();
|
||||
if let Some(a) = adjustments.get_mut(lhs.hir_id) {
|
||||
a.pop();
|
||||
}
|
||||
_ => {}
|
||||
if let Some(a) = adjustments.get_mut(rhs.hir_id) {
|
||||
a.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
hir::ExprKind::AssignOp(_, lhs, rhs) => {
|
||||
let lhs_ty = self.typeck_results.node_type(lhs.hir_id);
|
||||
let rhs_ty = self.typeck_results.node_type(rhs.hir_id);
|
||||
|
||||
if lhs_ty.is_scalar() && rhs_ty.is_scalar() {
|
||||
self.typeck_results.type_dependent_defs_mut().remove(e.hir_id);
|
||||
self.typeck_results.node_args_mut().remove(e.hir_id);
|
||||
|
||||
if let Some(a) = self.typeck_results.adjustments_mut().get_mut(lhs.hir_id) {
|
||||
a.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -204,6 +204,14 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
|
|||
error!("`cfg()` names cannot be after values");
|
||||
}
|
||||
names.push(ident);
|
||||
} else if let Some(boolean) = arg.boolean_literal() {
|
||||
if values_specified {
|
||||
error!("`cfg()` names cannot be after values");
|
||||
}
|
||||
names.push(rustc_span::Ident::new(
|
||||
if boolean { rustc_span::kw::True } else { rustc_span::kw::False },
|
||||
arg.span(),
|
||||
));
|
||||
} else if arg.has_name(sym::any)
|
||||
&& let Some(args) = arg.meta_item_list()
|
||||
{
|
||||
|
|
|
@ -29,6 +29,7 @@ use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
|
|||
use rustc_hir::intravisit::FnKind as HirFnKind;
|
||||
use rustc_hir::{Body, FnDecl, GenericParamKind, PatKind, PredicateOrigin};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::lint::LevelAndSource;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, Upcast, VariantDef};
|
||||
|
@ -694,7 +695,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations {
|
|||
}
|
||||
|
||||
// Avoid listing trait impls if the trait is allowed.
|
||||
let (level, _) = cx.tcx.lint_level_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id());
|
||||
let LevelAndSource { level, .. } =
|
||||
cx.tcx.lint_level_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id());
|
||||
if level == Level::Allow {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -17,13 +17,12 @@ use rustc_hir::def_id::{CrateNum, DefId};
|
|||
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
|
||||
use rustc_hir::{Pat, PatKind};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::lint::LevelAndSource;
|
||||
use rustc_middle::middle::privacy::EffectiveVisibilities;
|
||||
use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
|
||||
use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths};
|
||||
use rustc_middle::ty::{self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode};
|
||||
use rustc_session::lint::{
|
||||
FutureIncompatibleInfo, Level, Lint, LintBuffer, LintExpectationId, LintId,
|
||||
};
|
||||
use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintBuffer, LintExpectationId, LintId};
|
||||
use rustc_session::{LintStoreMarker, Session};
|
||||
use rustc_span::edit_distance::find_best_match_for_names;
|
||||
use rustc_span::{Ident, Span, Symbol, sym};
|
||||
|
@ -573,7 +572,7 @@ pub trait LintContext {
|
|||
}
|
||||
|
||||
/// This returns the lint level for the given lint at the current location.
|
||||
fn get_lint_level(&self, lint: &'static Lint) -> Level;
|
||||
fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource;
|
||||
|
||||
/// This function can be used to manually fulfill an expectation. This can
|
||||
/// be used for lints which contain several spans, and should be suppressed,
|
||||
|
@ -642,8 +641,8 @@ impl<'tcx> LintContext for LateContext<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_lint_level(&self, lint: &'static Lint) -> Level {
|
||||
self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs).0
|
||||
fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource {
|
||||
self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -663,8 +662,8 @@ impl LintContext for EarlyContext<'_> {
|
|||
self.builder.opt_span_lint(lint, span.map(|s| s.into()), decorate)
|
||||
}
|
||||
|
||||
fn get_lint_level(&self, lint: &'static Lint) -> Level {
|
||||
self.builder.lint_level(lint).0
|
||||
fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource {
|
||||
self.builder.lint_level(lint)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -84,10 +84,10 @@ impl LintLevelSets {
|
|||
) -> LevelAndSource {
|
||||
let lint = LintId::of(lint);
|
||||
let (level, mut src) = self.raw_lint_id_level(lint, idx, aux);
|
||||
let level = reveal_actual_level(level, &mut src, sess, lint, |id| {
|
||||
let (level, lint_id) = reveal_actual_level(level, &mut src, sess, lint, |id| {
|
||||
self.raw_lint_id_level(id, idx, aux)
|
||||
});
|
||||
(level, src)
|
||||
LevelAndSource { level, lint_id, src }
|
||||
}
|
||||
|
||||
fn raw_lint_id_level(
|
||||
|
@ -95,17 +95,17 @@ impl LintLevelSets {
|
|||
id: LintId,
|
||||
mut idx: LintStackIndex,
|
||||
aux: Option<&FxIndexMap<LintId, LevelAndSource>>,
|
||||
) -> (Option<Level>, LintLevelSource) {
|
||||
) -> (Option<(Level, Option<LintExpectationId>)>, LintLevelSource) {
|
||||
if let Some(specs) = aux
|
||||
&& let Some(&(level, src)) = specs.get(&id)
|
||||
&& let Some(&LevelAndSource { level, lint_id, src }) = specs.get(&id)
|
||||
{
|
||||
return (Some(level), src);
|
||||
return (Some((level, lint_id)), src);
|
||||
}
|
||||
|
||||
loop {
|
||||
let LintSet { ref specs, parent } = self.list[idx];
|
||||
if let Some(&(level, src)) = specs.get(&id) {
|
||||
return (Some(level), src);
|
||||
if let Some(&LevelAndSource { level, lint_id, src }) = specs.get(&id) {
|
||||
return (Some((level, lint_id)), src);
|
||||
}
|
||||
if idx == COMMAND_LINE {
|
||||
return (None, LintLevelSource::Default);
|
||||
|
@ -131,8 +131,8 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LintId> {
|
|||
})
|
||||
.filter_map(|lint| {
|
||||
let lint_level = map.lint_level_id_at_node(tcx, LintId::of(lint), CRATE_HIR_ID);
|
||||
if matches!(lint_level, (Level::Allow, ..))
|
||||
|| (matches!(lint_level, (.., LintLevelSource::Default)))
|
||||
if matches!(lint_level.level, Level::Allow)
|
||||
|| (matches!(lint_level.src, LintLevelSource::Default))
|
||||
&& lint.default_level(tcx.sess.edition()) == Level::Allow
|
||||
{
|
||||
Some(LintId::of(lint))
|
||||
|
@ -379,13 +379,7 @@ impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> {
|
|||
fn visit_attribute(&mut self, attribute: &'tcx hir::Attribute) {
|
||||
if matches!(
|
||||
Level::from_attr(attribute),
|
||||
Some(
|
||||
Level::Warn
|
||||
| Level::Deny
|
||||
| Level::Forbid
|
||||
| Level::Expect(..)
|
||||
| Level::ForceWarn(..),
|
||||
)
|
||||
Some((Level::Warn | Level::Deny | Level::Forbid | Level::Expect | Level::ForceWarn, _))
|
||||
) {
|
||||
let store = unerased_lint_store(self.tcx.sess);
|
||||
// Lint attributes are always a metalist inside a
|
||||
|
@ -541,9 +535,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
for &(ref lint_name, level) in &self.sess.opts.lint_opts {
|
||||
// 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(_))
|
||||
{
|
||||
if lint_name_only == crate::WARNINGS.name_lower() && matches!(level, Level::ForceWarn) {
|
||||
self.sess
|
||||
.dcx()
|
||||
.emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
|
||||
|
@ -586,7 +578,6 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
_ => {}
|
||||
};
|
||||
|
||||
let orig_level = level;
|
||||
let lint_flag_val = Symbol::intern(lint_name);
|
||||
|
||||
let Ok(ids) = self.store.find_lints(lint_name) else {
|
||||
|
@ -595,15 +586,15 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
};
|
||||
for id in ids {
|
||||
// ForceWarn and Forbid cannot be overridden
|
||||
if let Some((Level::ForceWarn(_) | Level::Forbid, _)) =
|
||||
if let Some(LevelAndSource { level: Level::ForceWarn | Level::Forbid, .. }) =
|
||||
self.current_specs().get(&id)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if self.check_gated_lint(id, DUMMY_SP, true) {
|
||||
let src = LintLevelSource::CommandLine(lint_flag_val, orig_level);
|
||||
self.insert(id, (level, src));
|
||||
let src = LintLevelSource::CommandLine(lint_flag_val, level);
|
||||
self.insert(id, LevelAndSource { level, lint_id: None, src });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -612,8 +603,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
/// Attempts to insert the `id` to `level_src` map entry. If unsuccessful
|
||||
/// (e.g. if a forbid was already inserted on the same scope), then emits a
|
||||
/// diagnostic with no change to `specs`.
|
||||
fn insert_spec(&mut self, id: LintId, (level, src): LevelAndSource) {
|
||||
let (old_level, old_src) = self.provider.get_lint_level(id.lint, self.sess);
|
||||
fn insert_spec(&mut self, id: LintId, LevelAndSource { level, lint_id, src }: LevelAndSource) {
|
||||
let LevelAndSource { level: old_level, src: old_src, .. } =
|
||||
self.provider.get_lint_level(id.lint, self.sess);
|
||||
|
||||
// Setting to a non-forbid level is an error if the lint previously had
|
||||
// a forbid level. Note that this is not necessarily true even with a
|
||||
|
@ -685,7 +677,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
// The lint `unfulfilled_lint_expectations` can't be expected, as it would suppress itself.
|
||||
// Handling expectations of this lint would add additional complexity with little to no
|
||||
// benefit. The expect level for this lint will therefore be ignored.
|
||||
if let Level::Expect(_) = level
|
||||
if let Level::Expect = level
|
||||
&& id == LintId::of(UNFULFILLED_LINT_EXPECTATIONS)
|
||||
{
|
||||
return;
|
||||
|
@ -693,13 +685,16 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
|
||||
match (old_level, level) {
|
||||
// If the new level is an expectation store it in `ForceWarn`
|
||||
(Level::ForceWarn(_), Level::Expect(expectation_id)) => {
|
||||
self.insert(id, (Level::ForceWarn(Some(expectation_id)), old_src))
|
||||
(Level::ForceWarn, Level::Expect) => {
|
||||
self.insert(id, LevelAndSource { level: Level::ForceWarn, lint_id, src: old_src })
|
||||
}
|
||||
// Keep `ForceWarn` level but drop the expectation
|
||||
(Level::ForceWarn(_), _) => self.insert(id, (Level::ForceWarn(None), old_src)),
|
||||
(Level::ForceWarn, _) => self.insert(
|
||||
id,
|
||||
LevelAndSource { level: Level::ForceWarn, lint_id: None, src: old_src },
|
||||
),
|
||||
// Set the lint level as normal
|
||||
_ => self.insert(id, (level, src)),
|
||||
_ => self.insert(id, LevelAndSource { level, lint_id, src }),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -714,7 +709,11 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
if attr.has_name(sym::automatically_derived) {
|
||||
self.insert(
|
||||
LintId::of(SINGLE_USE_LIFETIMES),
|
||||
(Level::Allow, LintLevelSource::Default),
|
||||
LevelAndSource {
|
||||
level: Level::Allow,
|
||||
lint_id: None,
|
||||
src: LintLevelSource::Default,
|
||||
},
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -725,15 +724,22 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
.meta_item_list()
|
||||
.is_some_and(|l| ast::attr::list_contains_name(&l, sym::hidden))
|
||||
{
|
||||
self.insert(LintId::of(MISSING_DOCS), (Level::Allow, LintLevelSource::Default));
|
||||
self.insert(
|
||||
LintId::of(MISSING_DOCS),
|
||||
LevelAndSource {
|
||||
level: Level::Allow,
|
||||
lint_id: None,
|
||||
src: LintLevelSource::Default,
|
||||
},
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let level = match Level::from_attr(attr) {
|
||||
let (level, lint_id) = match Level::from_attr(attr) {
|
||||
None => continue,
|
||||
// This is the only lint level with a `LintExpectationId` that can be created from
|
||||
// an attribute.
|
||||
Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => {
|
||||
Some((Level::Expect, Some(unstable_id))) if let Some(hir_id) = source_hir_id => {
|
||||
let LintExpectationId::Unstable { lint_index: None, attr_id: _ } = unstable_id
|
||||
else {
|
||||
bug!("stable id Level::from_attr")
|
||||
|
@ -745,9 +751,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
lint_index: None,
|
||||
};
|
||||
|
||||
Level::Expect(stable_id)
|
||||
(Level::Expect, Some(stable_id))
|
||||
}
|
||||
Some(lvl) => lvl,
|
||||
Some((lvl, id)) => (lvl, id),
|
||||
};
|
||||
|
||||
let Some(mut metas) = attr.meta_item_list() else { continue };
|
||||
|
@ -795,13 +801,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
}
|
||||
|
||||
for (lint_index, li) in metas.iter_mut().enumerate() {
|
||||
let level = match level {
|
||||
Level::Expect(mut id) => {
|
||||
id.set_lint_index(Some(lint_index as u16));
|
||||
Level::Expect(id)
|
||||
}
|
||||
level => level,
|
||||
};
|
||||
let mut lint_id = lint_id;
|
||||
if let Some(id) = &mut lint_id {
|
||||
id.set_lint_index(Some(lint_index as u16));
|
||||
}
|
||||
|
||||
let sp = li.span();
|
||||
let meta_item = match li {
|
||||
|
@ -933,7 +936,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
let src = LintLevelSource::Node { name, span: sp, reason };
|
||||
for &id in ids {
|
||||
if self.check_gated_lint(id, sp, false) {
|
||||
self.insert_spec(id, (level, src));
|
||||
self.insert_spec(id, LevelAndSource { level, lint_id, src });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -942,7 +945,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
// overriding the lint level but instead add an expectation that can't be
|
||||
// fulfilled. The lint message will include an explanation, that the
|
||||
// `unfulfilled_lint_expectations` lint can't be expected.
|
||||
if let Level::Expect(expect_id) = level {
|
||||
if let (Level::Expect, Some(expect_id)) = (level, lint_id) {
|
||||
// The `unfulfilled_lint_expectations` lint is not part of any lint
|
||||
// groups. Therefore. we only need to check the slice if it contains a
|
||||
// single lint.
|
||||
|
@ -964,7 +967,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
}
|
||||
|
||||
if self.lint_added_lints && !is_crate_node {
|
||||
for (id, &(level, ref src)) in self.current_specs().iter() {
|
||||
for (id, &LevelAndSource { level, ref src, .. }) in self.current_specs().iter() {
|
||||
if !id.lint.crate_level_only {
|
||||
continue;
|
||||
}
|
||||
|
@ -1002,10 +1005,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
|
||||
if self.lint_added_lints {
|
||||
let lint = builtin::UNKNOWN_LINTS;
|
||||
let (level, src) = self.lint_level(builtin::UNKNOWN_LINTS);
|
||||
let level = self.lint_level(builtin::UNKNOWN_LINTS);
|
||||
// FIXME: make this translatable
|
||||
#[allow(rustc::diagnostic_outside_of_impl)]
|
||||
lint_level(self.sess, lint, level, src, Some(span.into()), |lint| {
|
||||
lint_level(self.sess, lint, level, Some(span.into()), |lint| {
|
||||
lint.primary_message(fluent::lint_unknown_gated_lint);
|
||||
lint.arg("name", lint_id.lint.name_lower());
|
||||
lint.note(fluent::lint_note);
|
||||
|
@ -1040,8 +1043,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
span: Option<MultiSpan>,
|
||||
decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
|
||||
) {
|
||||
let (level, src) = self.lint_level(lint);
|
||||
lint_level(self.sess, lint, level, src, span, decorate)
|
||||
let level = self.lint_level(lint);
|
||||
lint_level(self.sess, lint, level, span, decorate)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
|
@ -1051,16 +1054,16 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
span: MultiSpan,
|
||||
decorate: impl for<'a> LintDiagnostic<'a, ()>,
|
||||
) {
|
||||
let (level, src) = self.lint_level(lint);
|
||||
lint_level(self.sess, lint, level, src, Some(span), |lint| {
|
||||
let level = self.lint_level(lint);
|
||||
lint_level(self.sess, lint, level, Some(span), |lint| {
|
||||
decorate.decorate_lint(lint);
|
||||
});
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> LintDiagnostic<'a, ()>) {
|
||||
let (level, src) = self.lint_level(lint);
|
||||
lint_level(self.sess, lint, level, src, None, |lint| {
|
||||
let level = self.lint_level(lint);
|
||||
lint_level(self.sess, lint, level, None, |lint| {
|
||||
decorate.decorate_lint(lint);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -159,12 +159,13 @@ impl EarlyLintPass for NonAsciiIdents {
|
|||
use rustc_span::Span;
|
||||
use unicode_security::GeneralSecurityProfile;
|
||||
|
||||
let check_non_ascii_idents = cx.builder.lint_level(NON_ASCII_IDENTS).0 != Level::Allow;
|
||||
let check_non_ascii_idents = cx.builder.lint_level(NON_ASCII_IDENTS).level != Level::Allow;
|
||||
let check_uncommon_codepoints =
|
||||
cx.builder.lint_level(UNCOMMON_CODEPOINTS).0 != Level::Allow;
|
||||
let check_confusable_idents = cx.builder.lint_level(CONFUSABLE_IDENTS).0 != Level::Allow;
|
||||
cx.builder.lint_level(UNCOMMON_CODEPOINTS).level != Level::Allow;
|
||||
let check_confusable_idents =
|
||||
cx.builder.lint_level(CONFUSABLE_IDENTS).level != Level::Allow;
|
||||
let check_mixed_script_confusables =
|
||||
cx.builder.lint_level(MIXED_SCRIPT_CONFUSABLES).0 != Level::Allow;
|
||||
cx.builder.lint_level(MIXED_SCRIPT_CONFUSABLES).level != Level::Allow;
|
||||
|
||||
if !check_non_ascii_idents
|
||||
&& !check_uncommon_codepoints
|
||||
|
|
|
@ -14,7 +14,7 @@ use rustc_middle::ty::{
|
|||
};
|
||||
use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{Span, Symbol, source_map, sym};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use tracing::debug;
|
||||
use {rustc_ast as ast, rustc_hir as hir};
|
||||
|
||||
|
@ -223,7 +223,7 @@ impl TypeLimits {
|
|||
fn lint_nan<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx hir::Expr<'tcx>,
|
||||
binop: hir::BinOp,
|
||||
binop: hir::BinOpKind,
|
||||
l: &'tcx hir::Expr<'tcx>,
|
||||
r: &'tcx hir::Expr<'tcx>,
|
||||
) {
|
||||
|
@ -262,19 +262,19 @@ fn lint_nan<'tcx>(
|
|||
InvalidNanComparisons::EqNe { suggestion }
|
||||
}
|
||||
|
||||
let lint = match binop.node {
|
||||
let lint = match binop {
|
||||
hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, l) => {
|
||||
eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
|
||||
nan_plus_binop: l_span.until(r_span),
|
||||
float: r_span.shrink_to_hi(),
|
||||
neg: (binop.node == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()),
|
||||
neg: (binop == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()),
|
||||
})
|
||||
}
|
||||
hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, r) => {
|
||||
eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
|
||||
nan_plus_binop: l_span.shrink_to_hi().to(r_span),
|
||||
float: l_span.shrink_to_hi(),
|
||||
neg: (binop.node == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()),
|
||||
neg: (binop == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()),
|
||||
})
|
||||
}
|
||||
hir::BinOpKind::Lt | hir::BinOpKind::Le | hir::BinOpKind::Gt | hir::BinOpKind::Ge
|
||||
|
@ -560,11 +560,11 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
|
|||
}
|
||||
}
|
||||
hir::ExprKind::Binary(binop, ref l, ref r) => {
|
||||
if is_comparison(binop) {
|
||||
if !check_limits(cx, binop, l, r) {
|
||||
if is_comparison(binop.node) {
|
||||
if !check_limits(cx, binop.node, l, r) {
|
||||
cx.emit_span_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
|
||||
} else {
|
||||
lint_nan(cx, e, binop, l, r);
|
||||
lint_nan(cx, e, binop.node, l, r);
|
||||
let cmpop = ComparisonOp::BinOp(binop.node);
|
||||
lint_wide_pointer(cx, e, cmpop, l, r);
|
||||
lint_fn_pointer(cx, e, cmpop, l, r);
|
||||
|
@ -591,8 +591,8 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
|
|||
_ => {}
|
||||
};
|
||||
|
||||
fn is_valid<T: PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool {
|
||||
match binop.node {
|
||||
fn is_valid<T: PartialOrd>(binop: hir::BinOpKind, v: T, min: T, max: T) -> bool {
|
||||
match binop {
|
||||
hir::BinOpKind::Lt => v > min && v <= max,
|
||||
hir::BinOpKind::Le => v >= min && v < max,
|
||||
hir::BinOpKind::Gt => v >= min && v < max,
|
||||
|
@ -602,22 +602,19 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
|
|||
}
|
||||
}
|
||||
|
||||
fn rev_binop(binop: hir::BinOp) -> hir::BinOp {
|
||||
source_map::respan(
|
||||
binop.span,
|
||||
match binop.node {
|
||||
hir::BinOpKind::Lt => hir::BinOpKind::Gt,
|
||||
hir::BinOpKind::Le => hir::BinOpKind::Ge,
|
||||
hir::BinOpKind::Gt => hir::BinOpKind::Lt,
|
||||
hir::BinOpKind::Ge => hir::BinOpKind::Le,
|
||||
_ => return binop,
|
||||
},
|
||||
)
|
||||
fn rev_binop(binop: hir::BinOpKind) -> hir::BinOpKind {
|
||||
match binop {
|
||||
hir::BinOpKind::Lt => hir::BinOpKind::Gt,
|
||||
hir::BinOpKind::Le => hir::BinOpKind::Ge,
|
||||
hir::BinOpKind::Gt => hir::BinOpKind::Lt,
|
||||
hir::BinOpKind::Ge => hir::BinOpKind::Le,
|
||||
_ => binop,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_limits(
|
||||
cx: &LateContext<'_>,
|
||||
binop: hir::BinOp,
|
||||
binop: hir::BinOpKind,
|
||||
l: &hir::Expr<'_>,
|
||||
r: &hir::Expr<'_>,
|
||||
) -> bool {
|
||||
|
@ -659,9 +656,9 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_comparison(binop: hir::BinOp) -> bool {
|
||||
fn is_comparison(binop: hir::BinOpKind) -> bool {
|
||||
matches!(
|
||||
binop.node,
|
||||
binop,
|
||||
hir::BinOpKind::Eq
|
||||
| hir::BinOpKind::Lt
|
||||
| hir::BinOpKind::Le
|
||||
|
|
|
@ -8,7 +8,8 @@ use rustc_data_structures::stable_hasher::{
|
|||
};
|
||||
use rustc_error_messages::{DiagMessage, MultiSpan};
|
||||
use rustc_hir::def::Namespace;
|
||||
use rustc_hir::{HashStableContext, HirId, MissingLifetimeKind};
|
||||
use rustc_hir::def_id::DefPathHash;
|
||||
use rustc_hir::{HashStableContext, HirId, ItemLocalId, MissingLifetimeKind};
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
|
||||
pub use rustc_span::edition::Edition;
|
||||
use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym};
|
||||
|
@ -102,7 +103,7 @@ pub enum Applicability {
|
|||
/// The index values have a type of `u16` to reduce the size of the `LintExpectationId`.
|
||||
/// It's reasonable to assume that no user will define 2^16 attributes on one node or
|
||||
/// have that amount of lints listed. `u16` values should therefore suffice.
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, Encodable, Decodable)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Encodable, Decodable)]
|
||||
pub enum LintExpectationId {
|
||||
/// Used for lints emitted during the `EarlyLintPass`. This id is not
|
||||
/// hash stable and should not be cached.
|
||||
|
@ -156,13 +157,14 @@ impl<HCX: rustc_hir::HashStableContext> HashStable<HCX> for LintExpectationId {
|
|||
}
|
||||
|
||||
impl<HCX: rustc_hir::HashStableContext> ToStableHashKey<HCX> for LintExpectationId {
|
||||
type KeyType = (HirId, u16, u16);
|
||||
type KeyType = (DefPathHash, ItemLocalId, u16, u16);
|
||||
|
||||
#[inline]
|
||||
fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType {
|
||||
fn to_stable_hash_key(&self, hcx: &HCX) -> Self::KeyType {
|
||||
match self {
|
||||
LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => {
|
||||
(*hir_id, *attr_index, *lint_index)
|
||||
let (def_path_hash, lint_idx) = hir_id.to_stable_hash_key(hcx);
|
||||
(def_path_hash, lint_idx, *attr_index, *lint_index)
|
||||
}
|
||||
_ => {
|
||||
unreachable!("HashStable should only be called for a filled `LintExpectationId`")
|
||||
|
@ -199,9 +201,9 @@ pub enum Level {
|
|||
///
|
||||
/// See RFC 2383.
|
||||
///
|
||||
/// The [`LintExpectationId`] is used to later link a lint emission to the actual
|
||||
/// Requires a [`LintExpectationId`] to later link a lint emission to the actual
|
||||
/// expectation. It can be ignored in most cases.
|
||||
Expect(LintExpectationId),
|
||||
Expect,
|
||||
/// The `warn` level will produce a warning if the lint was violated, however the
|
||||
/// compiler will continue with its execution.
|
||||
Warn,
|
||||
|
@ -209,9 +211,9 @@ pub enum Level {
|
|||
/// to ensure that a lint can't be suppressed. This lint level can currently only be set
|
||||
/// via the console and is therefore session specific.
|
||||
///
|
||||
/// The [`LintExpectationId`] is intended to fulfill expectations marked via the
|
||||
/// Requires a [`LintExpectationId`] to fulfill expectations marked via the
|
||||
/// `#[expect]` attribute, that will still be suppressed due to the level.
|
||||
ForceWarn(Option<LintExpectationId>),
|
||||
ForceWarn,
|
||||
/// The `deny` level will produce an error and stop further execution after the lint
|
||||
/// pass is complete.
|
||||
Deny,
|
||||
|
@ -225,9 +227,9 @@ impl Level {
|
|||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Level::Allow => "allow",
|
||||
Level::Expect(_) => "expect",
|
||||
Level::Expect => "expect",
|
||||
Level::Warn => "warn",
|
||||
Level::ForceWarn(_) => "force-warn",
|
||||
Level::ForceWarn => "force-warn",
|
||||
Level::Deny => "deny",
|
||||
Level::Forbid => "forbid",
|
||||
}
|
||||
|
@ -246,24 +248,30 @@ impl Level {
|
|||
}
|
||||
|
||||
/// Converts an `Attribute` to a level.
|
||||
pub fn from_attr(attr: &impl AttributeExt) -> Option<Self> {
|
||||
pub fn from_attr(attr: &impl AttributeExt) -> Option<(Self, Option<LintExpectationId>)> {
|
||||
Self::from_symbol(attr.name_or_empty(), || Some(attr.id()))
|
||||
}
|
||||
|
||||
/// Converts a `Symbol` to a level.
|
||||
pub fn from_symbol(s: Symbol, id: impl FnOnce() -> Option<AttrId>) -> Option<Self> {
|
||||
pub fn from_symbol(
|
||||
s: Symbol,
|
||||
id: impl FnOnce() -> Option<AttrId>,
|
||||
) -> Option<(Self, Option<LintExpectationId>)> {
|
||||
match s {
|
||||
sym::allow => Some(Level::Allow),
|
||||
sym::allow => Some((Level::Allow, None)),
|
||||
sym::expect => {
|
||||
if let Some(attr_id) = id() {
|
||||
Some(Level::Expect(LintExpectationId::Unstable { attr_id, lint_index: None }))
|
||||
Some((
|
||||
Level::Expect,
|
||||
Some(LintExpectationId::Unstable { attr_id, lint_index: None }),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
sym::warn => Some(Level::Warn),
|
||||
sym::deny => Some(Level::Deny),
|
||||
sym::forbid => Some(Level::Forbid),
|
||||
sym::warn => Some((Level::Warn, None)),
|
||||
sym::deny => Some((Level::Deny, None)),
|
||||
sym::forbid => Some((Level::Forbid, None)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -274,8 +282,8 @@ impl Level {
|
|||
Level::Deny => "-D",
|
||||
Level::Forbid => "-F",
|
||||
Level::Allow => "-A",
|
||||
Level::ForceWarn(_) => "--force-warn",
|
||||
Level::Expect(_) => {
|
||||
Level::ForceWarn => "--force-warn",
|
||||
Level::Expect => {
|
||||
unreachable!("the expect level does not have a commandline flag")
|
||||
}
|
||||
}
|
||||
|
@ -283,17 +291,10 @@ impl Level {
|
|||
|
||||
pub fn is_error(self) -> bool {
|
||||
match self {
|
||||
Level::Allow | Level::Expect(_) | Level::Warn | Level::ForceWarn(_) => false,
|
||||
Level::Allow | Level::Expect | Level::Warn | Level::ForceWarn => false,
|
||||
Level::Deny | Level::Forbid => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_expectation_id(&self) -> Option<LintExpectationId> {
|
||||
match self {
|
||||
Level::Expect(id) | Level::ForceWarn(Some(id)) => Some(*id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Specification of a single lint.
|
||||
|
|
|
@ -340,7 +340,7 @@ impl CStore {
|
|||
}
|
||||
let level = tcx
|
||||
.lint_level_at_node(lint::builtin::UNUSED_CRATE_DEPENDENCIES, rustc_hir::CRATE_HIR_ID)
|
||||
.0;
|
||||
.level;
|
||||
if level != lint::Level::Allow {
|
||||
let unused_externs =
|
||||
self.unused_externs.iter().map(|ident| ident.to_ident_string()).collect::<Vec<_>>();
|
||||
|
|
|
@ -51,8 +51,13 @@ impl LintLevelSource {
|
|||
}
|
||||
}
|
||||
|
||||
/// A tuple of a lint level and its source.
|
||||
pub type LevelAndSource = (Level, LintLevelSource);
|
||||
/// Convenience helper for moving things around together that frequently are paired
|
||||
#[derive(Copy, Clone, Debug, HashStable, Encodable, Decodable)]
|
||||
pub struct LevelAndSource {
|
||||
pub level: Level,
|
||||
pub lint_id: Option<LintExpectationId>,
|
||||
pub src: LintLevelSource,
|
||||
}
|
||||
|
||||
/// Return type for the `shallow_lint_levels_on` query.
|
||||
///
|
||||
|
@ -69,14 +74,18 @@ pub struct ShallowLintLevelMap {
|
|||
///
|
||||
/// The return of this function is suitable for diagnostics.
|
||||
pub fn reveal_actual_level(
|
||||
level: Option<Level>,
|
||||
level: Option<(Level, Option<LintExpectationId>)>,
|
||||
src: &mut LintLevelSource,
|
||||
sess: &Session,
|
||||
lint: LintId,
|
||||
probe_for_lint_level: impl FnOnce(LintId) -> (Option<Level>, LintLevelSource),
|
||||
) -> Level {
|
||||
probe_for_lint_level: impl FnOnce(
|
||||
LintId,
|
||||
)
|
||||
-> (Option<(Level, Option<LintExpectationId>)>, LintLevelSource),
|
||||
) -> (Level, Option<LintExpectationId>) {
|
||||
// If `level` is none then we actually assume the default level for this lint.
|
||||
let mut level = level.unwrap_or_else(|| lint.lint.default_level(sess.edition()));
|
||||
let (mut level, mut lint_id) =
|
||||
level.unwrap_or_else(|| (lint.lint.default_level(sess.edition()), None));
|
||||
|
||||
// If we're about to issue a warning, check at the last minute for any
|
||||
// directives against the warnings "lint". If, for example, there's an
|
||||
|
@ -88,16 +97,17 @@ pub fn reveal_actual_level(
|
|||
// future compatibility warning.
|
||||
if level == Level::Warn && lint != LintId::of(FORBIDDEN_LINT_GROUPS) {
|
||||
let (warnings_level, warnings_src) = probe_for_lint_level(LintId::of(builtin::WARNINGS));
|
||||
if let Some(configured_warning_level) = warnings_level {
|
||||
if let Some((configured_warning_level, configured_lint_id)) = warnings_level {
|
||||
if configured_warning_level != Level::Warn {
|
||||
level = configured_warning_level;
|
||||
lint_id = configured_lint_id;
|
||||
*src = warnings_src;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that we never exceed the `--cap-lints` argument unless the source is a --force-warn
|
||||
level = if let LintLevelSource::CommandLine(_, Level::ForceWarn(_)) = src {
|
||||
level = if let LintLevelSource::CommandLine(_, Level::ForceWarn) = src {
|
||||
level
|
||||
} else {
|
||||
cmp::min(level, sess.opts.lint_cap.unwrap_or(Level::Forbid))
|
||||
|
@ -108,7 +118,7 @@ pub fn reveal_actual_level(
|
|||
level = cmp::min(*driver_level, level);
|
||||
}
|
||||
|
||||
level
|
||||
(level, lint_id)
|
||||
}
|
||||
|
||||
impl ShallowLintLevelMap {
|
||||
|
@ -121,11 +131,11 @@ impl ShallowLintLevelMap {
|
|||
tcx: TyCtxt<'_>,
|
||||
id: LintId,
|
||||
start: HirId,
|
||||
) -> (Option<Level>, LintLevelSource) {
|
||||
) -> (Option<(Level, Option<LintExpectationId>)>, LintLevelSource) {
|
||||
if let Some(map) = self.specs.get(&start.local_id)
|
||||
&& let Some(&(level, src)) = map.get(&id)
|
||||
&& let Some(&LevelAndSource { level, lint_id, src }) = map.get(&id)
|
||||
{
|
||||
return (Some(level), src);
|
||||
return (Some((level, lint_id)), src);
|
||||
}
|
||||
|
||||
let mut owner = start.owner;
|
||||
|
@ -137,9 +147,9 @@ impl ShallowLintLevelMap {
|
|||
specs = &tcx.shallow_lint_levels_on(owner).specs;
|
||||
}
|
||||
if let Some(map) = specs.get(&parent.local_id)
|
||||
&& let Some(&(level, src)) = map.get(&id)
|
||||
&& let Some(&LevelAndSource { level, lint_id, src }) = map.get(&id)
|
||||
{
|
||||
return (Some(level), src);
|
||||
return (Some((level, lint_id)), src);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,18 +163,18 @@ impl ShallowLintLevelMap {
|
|||
tcx: TyCtxt<'_>,
|
||||
lint: LintId,
|
||||
cur: HirId,
|
||||
) -> (Level, LintLevelSource) {
|
||||
) -> LevelAndSource {
|
||||
let (level, mut src) = self.probe_for_lint_level(tcx, lint, cur);
|
||||
let level = reveal_actual_level(level, &mut src, tcx.sess, lint, |lint| {
|
||||
let (level, lint_id) = reveal_actual_level(level, &mut src, tcx.sess, lint, |lint| {
|
||||
self.probe_for_lint_level(tcx, lint, cur)
|
||||
});
|
||||
(level, src)
|
||||
LevelAndSource { level, lint_id, src }
|
||||
}
|
||||
}
|
||||
|
||||
impl TyCtxt<'_> {
|
||||
/// Fetch and return the user-visible lint level for the given lint at the given HirId.
|
||||
pub fn lint_level_at_node(self, lint: &'static Lint, id: HirId) -> (Level, LintLevelSource) {
|
||||
pub fn lint_level_at_node(self, lint: &'static Lint, id: HirId) -> LevelAndSource {
|
||||
self.shallow_lint_levels_on(id.owner).lint_level_id_at_node(self, LintId::of(lint), id)
|
||||
}
|
||||
}
|
||||
|
@ -267,8 +277,7 @@ fn explain_lint_level_source(
|
|||
pub fn lint_level(
|
||||
sess: &Session,
|
||||
lint: &'static Lint,
|
||||
level: Level,
|
||||
src: LintLevelSource,
|
||||
level: LevelAndSource,
|
||||
span: Option<MultiSpan>,
|
||||
decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
|
||||
) {
|
||||
|
@ -278,11 +287,12 @@ pub fn lint_level(
|
|||
fn lint_level_impl(
|
||||
sess: &Session,
|
||||
lint: &'static Lint,
|
||||
level: Level,
|
||||
src: LintLevelSource,
|
||||
level: LevelAndSource,
|
||||
span: Option<MultiSpan>,
|
||||
decorate: Box<dyn '_ + for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>)>,
|
||||
) {
|
||||
let LevelAndSource { level, lint_id, src } = level;
|
||||
|
||||
// Check for future incompatibility lints and issue a stronger warning.
|
||||
let future_incompatible = lint.future_incompatible;
|
||||
|
||||
|
@ -301,7 +311,7 @@ pub fn lint_level(
|
|||
return;
|
||||
}
|
||||
}
|
||||
Level::Expect(expect_id) => {
|
||||
Level::Expect => {
|
||||
// This case is special as we actually allow the lint itself in this context, but
|
||||
// we can't return early like in the case for `Level::Allow` because we still
|
||||
// need the lint diagnostic to be emitted to `rustc_error::DiagCtxtInner`.
|
||||
|
@ -309,10 +319,9 @@ pub fn lint_level(
|
|||
// We can also not mark the lint expectation as fulfilled here right away, as it
|
||||
// can still be cancelled in the decorate function. All of this means that we simply
|
||||
// create a `Diag` and continue as we would for warnings.
|
||||
rustc_errors::Level::Expect(expect_id)
|
||||
rustc_errors::Level::Expect
|
||||
}
|
||||
Level::ForceWarn(Some(expect_id)) => rustc_errors::Level::ForceWarning(Some(expect_id)),
|
||||
Level::ForceWarn(None) => rustc_errors::Level::ForceWarning(None),
|
||||
Level::ForceWarn => rustc_errors::Level::ForceWarning,
|
||||
Level::Warn => rustc_errors::Level::Warning,
|
||||
Level::Deny | Level::Forbid => rustc_errors::Level::Error,
|
||||
};
|
||||
|
@ -320,6 +329,9 @@ pub fn lint_level(
|
|||
if let Some(span) = span {
|
||||
err.span(span);
|
||||
}
|
||||
if let Some(lint_id) = lint_id {
|
||||
err.lint_id(lint_id);
|
||||
}
|
||||
|
||||
// If this code originates in a foreign macro, aka something that this crate
|
||||
// did not itself author, then it's likely that there's nothing this crate
|
||||
|
@ -350,7 +362,7 @@ pub fn lint_level(
|
|||
// the compiler. It is therefore not necessary to add any information for the user.
|
||||
// This will therefore directly call the decorate function which will in turn emit
|
||||
// the diagnostic.
|
||||
if let Level::Expect(_) = level {
|
||||
if let Level::Expect = level {
|
||||
decorate(&mut err);
|
||||
err.emit();
|
||||
return;
|
||||
|
@ -419,5 +431,5 @@ pub fn lint_level(
|
|||
explain_lint_level_source(lint, level, src, &mut err);
|
||||
err.emit()
|
||||
}
|
||||
lint_level_impl(sess, lint, level, src, span, Box::new(decorate))
|
||||
lint_level_impl(sess, lint, level, span, Box::new(decorate))
|
||||
}
|
||||
|
|
|
@ -255,7 +255,7 @@ fn late_report_deprecation(
|
|||
// Calculating message for lint involves calling `self.def_path_str`,
|
||||
// which will by default invoke the expensive `visible_parent_map` query.
|
||||
// Skip all that work if the lint is allowed anyway.
|
||||
if tcx.lint_level_at_node(lint, hir_id).0 == Level::Allow {
|
||||
if tcx.lint_level_at_node(lint, hir_id).level == Level::Allow {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1668,6 +1668,42 @@ pub enum BinOp {
|
|||
Offset,
|
||||
}
|
||||
|
||||
// Assignment operators, e.g. `+=`. See comments on the corresponding variants
|
||||
// in `BinOp` for details.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)]
|
||||
pub enum AssignOp {
|
||||
AddAssign,
|
||||
SubAssign,
|
||||
MulAssign,
|
||||
DivAssign,
|
||||
RemAssign,
|
||||
BitXorAssign,
|
||||
BitAndAssign,
|
||||
BitOrAssign,
|
||||
ShlAssign,
|
||||
ShrAssign,
|
||||
}
|
||||
|
||||
// Sometimes `BinOp` and `AssignOp` need the same treatment. The operations
|
||||
// covered by `AssignOp` are a subset of those covered by `BinOp`, so it makes
|
||||
// sense to convert `AssignOp` to `BinOp`.
|
||||
impl From<AssignOp> for BinOp {
|
||||
fn from(op: AssignOp) -> BinOp {
|
||||
match op {
|
||||
AssignOp::AddAssign => BinOp::Add,
|
||||
AssignOp::SubAssign => BinOp::Sub,
|
||||
AssignOp::MulAssign => BinOp::Mul,
|
||||
AssignOp::DivAssign => BinOp::Div,
|
||||
AssignOp::RemAssign => BinOp::Rem,
|
||||
AssignOp::BitXorAssign => BinOp::BitXor,
|
||||
AssignOp::BitAndAssign => BinOp::BitAnd,
|
||||
AssignOp::BitOrAssign => BinOp::BitOr,
|
||||
AssignOp::ShlAssign => BinOp::Shl,
|
||||
AssignOp::ShrAssign => BinOp::Shr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Some nodes are used a lot. Make sure they don't unintentionally get bigger.
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
mod size_asserts {
|
||||
|
|
|
@ -27,7 +27,7 @@ use tracing::instrument;
|
|||
|
||||
use crate::middle::region;
|
||||
use crate::mir::interpret::AllocId;
|
||||
use crate::mir::{self, BinOp, BorrowKind, FakeReadCause, UnOp};
|
||||
use crate::mir::{self, AssignOp, BinOp, BorrowKind, FakeReadCause, UnOp};
|
||||
use crate::thir::visit::for_each_immediate_subpat;
|
||||
use crate::ty::adjustment::PointerCoercion;
|
||||
use crate::ty::layout::IntegerExt;
|
||||
|
@ -403,7 +403,7 @@ pub enum ExprKind<'tcx> {
|
|||
},
|
||||
/// A *non-overloaded* operation assignment, e.g. `lhs += rhs`.
|
||||
AssignOp {
|
||||
op: BinOp,
|
||||
op: AssignOp,
|
||||
lhs: ExprId,
|
||||
rhs: ExprId,
|
||||
},
|
||||
|
|
|
@ -3022,8 +3022,8 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
span: impl Into<MultiSpan>,
|
||||
decorator: impl for<'a> LintDiagnostic<'a, ()>,
|
||||
) {
|
||||
let (level, src) = self.lint_level_at_node(lint, hir_id);
|
||||
lint_level(self.sess, lint, level, src, Some(span.into()), |lint| {
|
||||
let level = self.lint_level_at_node(lint, hir_id);
|
||||
lint_level(self.sess, lint, level, Some(span.into()), |lint| {
|
||||
decorator.decorate_lint(lint);
|
||||
})
|
||||
}
|
||||
|
@ -3040,8 +3040,8 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
span: impl Into<MultiSpan>,
|
||||
decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
|
||||
) {
|
||||
let (level, src) = self.lint_level_at_node(lint, hir_id);
|
||||
lint_level(self.sess, lint, level, src, Some(span.into()), decorate);
|
||||
let level = self.lint_level_at_node(lint, hir_id);
|
||||
lint_level(self.sess, lint, level, Some(span.into()), decorate);
|
||||
}
|
||||
|
||||
/// Find the crate root and the appropriate span where `use` and outer attributes can be
|
||||
|
@ -3108,8 +3108,8 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
id: HirId,
|
||||
decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
|
||||
) {
|
||||
let (level, src) = self.lint_level_at_node(lint, id);
|
||||
lint_level(self.sess, lint, level, src, None, decorate);
|
||||
let level = self.lint_level_at_node(lint, id);
|
||||
lint_level(self.sess, lint, level, None, decorate);
|
||||
}
|
||||
|
||||
pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx [TraitCandidate]> {
|
||||
|
|
|
@ -159,7 +159,7 @@ fn find_capture_matching_projections<'a, 'tcx>(
|
|||
) -> Option<(usize, &'a Capture<'tcx>)> {
|
||||
let hir_projections = convert_to_hir_projections_and_truncate_for_capture(projections);
|
||||
|
||||
upvars.get_by_key_enumerated(var_hir_id.0).find(|(_, capture)| {
|
||||
upvars.get_by_key_enumerated(var_hir_id.0.local_id).find(|(_, capture)| {
|
||||
let possible_ancestor_proj_kinds: Vec<_> =
|
||||
capture.captured_place.place.projections.iter().map(|proj| proj.kind).collect();
|
||||
is_ancestor_or_same_capture(&possible_ancestor_proj_kinds, &hir_projections)
|
||||
|
|
|
@ -78,8 +78,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// because AssignOp is only legal for Copy types
|
||||
// (overloaded ops should be desugared into a call).
|
||||
let result = unpack!(
|
||||
block =
|
||||
this.build_binary_op(block, op, expr_span, lhs_ty, Operand::Copy(lhs), rhs)
|
||||
block = this.build_binary_op(
|
||||
block,
|
||||
op.into(),
|
||||
expr_span,
|
||||
lhs_ty,
|
||||
Operand::Copy(lhs),
|
||||
rhs
|
||||
)
|
||||
);
|
||||
this.cfg.push_assign(block, source_info, lhs, result);
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ use rustc_data_structures::sorted_map::SortedIndexMultiMap;
|
|||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Node};
|
||||
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node};
|
||||
use rustc_index::bit_set::GrowableBitSet;
|
||||
use rustc_index::{Idx, IndexSlice, IndexVec};
|
||||
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
|
||||
|
@ -221,7 +221,7 @@ struct Builder<'a, 'tcx> {
|
|||
coverage_info: Option<coverageinfo::CoverageInfoBuilder>,
|
||||
}
|
||||
|
||||
type CaptureMap<'tcx> = SortedIndexMultiMap<usize, HirId, Capture<'tcx>>;
|
||||
type CaptureMap<'tcx> = SortedIndexMultiMap<usize, ItemLocalId, Capture<'tcx>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Capture<'tcx> {
|
||||
|
@ -853,6 +853,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let capture_tys = upvar_args.upvar_tys();
|
||||
|
||||
let tcx = self.tcx;
|
||||
let mut upvar_owner = None;
|
||||
self.upvars = tcx
|
||||
.closure_captures(self.def_id)
|
||||
.iter()
|
||||
|
@ -866,6 +867,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
|
||||
_ => bug!("Expected an upvar"),
|
||||
};
|
||||
let upvar_base = upvar_owner.get_or_insert(var_id.owner);
|
||||
assert_eq!(*upvar_base, var_id.owner);
|
||||
let var_id = var_id.local_id;
|
||||
|
||||
let mutability = captured_place.mutability;
|
||||
|
||||
|
|
|
@ -195,7 +195,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
|
|||
|
||||
/// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
|
||||
fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
|
||||
self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow
|
||||
self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).level == Level::Allow
|
||||
}
|
||||
|
||||
/// Handle closures/coroutines/inline-consts, which is unsafecked with their parent body.
|
||||
|
@ -292,8 +292,10 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
|
|||
});
|
||||
}
|
||||
BlockSafety::ExplicitUnsafe(hir_id) => {
|
||||
let used =
|
||||
matches!(self.tcx.lint_level_at_node(UNUSED_UNSAFE, hir_id), (Level::Allow, _));
|
||||
let used = matches!(
|
||||
self.tcx.lint_level_at_node(UNUSED_UNSAFE, hir_id).level,
|
||||
Level::Allow
|
||||
);
|
||||
self.in_safety_context(
|
||||
SafetyContext::UnsafeBlock {
|
||||
span: block.span,
|
||||
|
|
|
@ -9,7 +9,7 @@ use rustc_middle::hir::place::{
|
|||
Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind,
|
||||
};
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::{self, BinOp, BorrowKind, UnOp};
|
||||
use rustc_middle::mir::{self, AssignOp, BinOp, BorrowKind, UnOp};
|
||||
use rustc_middle::thir::*;
|
||||
use rustc_middle::ty::adjustment::{
|
||||
Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCoercion,
|
||||
|
@ -489,7 +489,7 @@ impl<'tcx> ThirBuildCx<'tcx> {
|
|||
self.overloaded_operator(expr, Box::new([lhs, rhs]))
|
||||
} else {
|
||||
ExprKind::AssignOp {
|
||||
op: bin_op(op.node),
|
||||
op: assign_op(op.node),
|
||||
lhs: self.mirror_expr(lhs),
|
||||
rhs: self.mirror_expr(rhs),
|
||||
}
|
||||
|
@ -1347,3 +1347,18 @@ fn bin_op(op: hir::BinOpKind) -> BinOp {
|
|||
_ => bug!("no equivalent for ast binop {:?}", op),
|
||||
}
|
||||
}
|
||||
|
||||
fn assign_op(op: hir::AssignOpKind) -> AssignOp {
|
||||
match op {
|
||||
hir::AssignOpKind::AddAssign => AssignOp::AddAssign,
|
||||
hir::AssignOpKind::SubAssign => AssignOp::SubAssign,
|
||||
hir::AssignOpKind::MulAssign => AssignOp::MulAssign,
|
||||
hir::AssignOpKind::DivAssign => AssignOp::DivAssign,
|
||||
hir::AssignOpKind::RemAssign => AssignOp::RemAssign,
|
||||
hir::AssignOpKind::BitXorAssign => AssignOp::BitXorAssign,
|
||||
hir::AssignOpKind::BitAndAssign => AssignOp::BitAndAssign,
|
||||
hir::AssignOpKind::BitOrAssign => AssignOp::BitOrAssign,
|
||||
hir::AssignOpKind::ShlAssign => AssignOp::ShlAssign,
|
||||
hir::AssignOpKind::ShrAssign => AssignOp::ShrAssign,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1025,7 +1025,7 @@ fn find_fallback_pattern_typo<'tcx>(
|
|||
pat: &Pat<'tcx>,
|
||||
lint: &mut UnreachablePattern<'_>,
|
||||
) {
|
||||
if let (Level::Allow, _) = cx.tcx.lint_level_at_node(UNREACHABLE_PATTERNS, hir_id) {
|
||||
if let Level::Allow = cx.tcx.lint_level_at_node(UNREACHABLE_PATTERNS, hir_id).level {
|
||||
// This is because we use `with_no_trimmed_paths` later, so if we never emit the lint we'd
|
||||
// ICE. At the same time, we don't really need to do all of this if we won't emit anything.
|
||||
return;
|
||||
|
|
|
@ -14,10 +14,10 @@ use rustc_ast::util::classify;
|
|||
use rustc_ast::util::parser::{AssocOp, ExprPrecedence, Fixity, prec_let_scrutinee_needs_par};
|
||||
use rustc_ast::visit::{Visitor, walk_expr};
|
||||
use rustc_ast::{
|
||||
self as ast, AnonConst, Arm, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy,
|
||||
ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl, FnRetTy, Label, MacCall,
|
||||
MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind,
|
||||
YieldKind,
|
||||
self as ast, AnonConst, Arm, AssignOp, AssignOpKind, AttrStyle, AttrVec, BinOp, BinOpKind,
|
||||
BlockCheckMode, CaptureBy, ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl,
|
||||
FnRetTy, Label, MacCall, MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind,
|
||||
UnOp, UnsafeBinderCastKind, YieldKind,
|
||||
};
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_errors::{Applicability, Diag, PResult, StashKey, Subdiagnostic};
|
||||
|
@ -359,7 +359,7 @@ impl<'a> Parser<'a> {
|
|||
(
|
||||
Some(
|
||||
AssocOp::Binary(BinOpKind::Shr | BinOpKind::Gt | BinOpKind::Ge)
|
||||
| AssocOp::AssignOp(BinOpKind::Shr),
|
||||
| AssocOp::AssignOp(AssignOpKind::ShrAssign),
|
||||
),
|
||||
_,
|
||||
) if self.restrictions.contains(Restrictions::CONST_EXPR) => {
|
||||
|
@ -3914,8 +3914,8 @@ impl<'a> Parser<'a> {
|
|||
self.dcx().emit_err(errors::LeftArrowOperator { span });
|
||||
}
|
||||
|
||||
fn mk_assign_op(&self, binop: BinOp, lhs: P<Expr>, rhs: P<Expr>) -> ExprKind {
|
||||
ExprKind::AssignOp(binop, lhs, rhs)
|
||||
fn mk_assign_op(&self, assign_op: AssignOp, lhs: P<Expr>, rhs: P<Expr>) -> ExprKind {
|
||||
ExprKind::AssignOp(assign_op, lhs, rhs)
|
||||
}
|
||||
|
||||
fn mk_range(
|
||||
|
|
|
@ -19,8 +19,8 @@ use rustc_middle::middle::privacy::Level;
|
|||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_session::lint;
|
||||
use rustc_session::lint::builtin::DEAD_CODE;
|
||||
use rustc_session::lint::{self, LintExpectationId};
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use crate::errors::{
|
||||
|
@ -696,8 +696,8 @@ fn has_allow_dead_code_or_lang_attr(
|
|||
|
||||
fn has_allow_expect_dead_code(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
||||
let hir_id = tcx.local_def_id_to_hir_id(def_id);
|
||||
let lint_level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).0;
|
||||
matches!(lint_level, lint::Allow | lint::Expect(_))
|
||||
let lint_level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).level;
|
||||
matches!(lint_level, lint::Allow | lint::Expect)
|
||||
}
|
||||
|
||||
fn has_used_like_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
||||
|
@ -915,7 +915,7 @@ fn live_symbols_and_ignored_derived_traits(
|
|||
struct DeadItem {
|
||||
def_id: LocalDefId,
|
||||
name: Symbol,
|
||||
level: lint::Level,
|
||||
level: (lint::Level, Option<LintExpectationId>),
|
||||
}
|
||||
|
||||
struct DeadVisitor<'tcx> {
|
||||
|
@ -959,9 +959,10 @@ impl<'tcx> DeadVisitor<'tcx> {
|
|||
ShouldWarnAboutField::Yes
|
||||
}
|
||||
|
||||
fn def_lint_level(&self, id: LocalDefId) -> lint::Level {
|
||||
fn def_lint_level(&self, id: LocalDefId) -> (lint::Level, Option<LintExpectationId>) {
|
||||
let hir_id = self.tcx.local_def_id_to_hir_id(id);
|
||||
self.tcx.lint_level_at_node(DEAD_CODE, hir_id).0
|
||||
let level = self.tcx.lint_level_at_node(DEAD_CODE, hir_id);
|
||||
(level.level, level.lint_id)
|
||||
}
|
||||
|
||||
// # Panics
|
||||
|
@ -1129,7 +1130,8 @@ impl<'tcx> DeadVisitor<'tcx> {
|
|||
if dead_codes.is_empty() {
|
||||
return;
|
||||
}
|
||||
dead_codes.sort_by_key(|v| v.level);
|
||||
// FIXME: `dead_codes` should probably be morally equivalent to `IndexMap<(Level, LintExpectationId), (DefId, Symbol)>`
|
||||
dead_codes.sort_by_key(|v| v.level.0);
|
||||
for group in dead_codes.chunk_by(|a, b| a.level == b.level) {
|
||||
self.lint_at_single_level(&group, participle, Some(def_id), report_on);
|
||||
}
|
||||
|
|
|
@ -980,7 +980,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
|
|||
// Calculating message for lint involves calling `self.def_path_str`,
|
||||
// which will by default invoke the expensive `visible_parent_map` query.
|
||||
// Skip all that work if the lint is allowed anyway.
|
||||
if self.tcx.lint_level_at_node(DEPRECATED, id).0
|
||||
if self.tcx.lint_level_at_node(DEPRECATED, id).level
|
||||
== lint::Level::Allow
|
||||
{
|
||||
return;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use rustc_middle::lint::LevelAndSource;
|
||||
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
|
||||
use rustc_span::ErrorGuaranteed;
|
||||
use tracing::instrument;
|
||||
|
@ -64,7 +65,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>(
|
|||
scrut_ty: RevealedTy<'tcx>,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
if !matches!(
|
||||
rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level).0,
|
||||
rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level).level,
|
||||
rustc_session::lint::Level::Allow
|
||||
) {
|
||||
let witnesses = collect_nonexhaustive_missing_variants(rcx, pat_column)?;
|
||||
|
@ -88,13 +89,13 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>(
|
|||
// arm. This no longer makes sense so we warn users, to avoid silently breaking their
|
||||
// usage of the lint.
|
||||
for arm in arms {
|
||||
let (lint_level, lint_level_source) =
|
||||
let LevelAndSource { level, src, .. } =
|
||||
rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data);
|
||||
if !matches!(lint_level, rustc_session::lint::Level::Allow) {
|
||||
if !matches!(level, rustc_session::lint::Level::Allow) {
|
||||
let decorator = NonExhaustiveOmittedPatternLintOnArm {
|
||||
lint_span: lint_level_source.span(),
|
||||
lint_span: src.span(),
|
||||
suggest_lint_on_match: rcx.whole_match_span.map(|span| span.shrink_to_lo()),
|
||||
lint_level: lint_level.as_str(),
|
||||
lint_level: level.as_str(),
|
||||
lint_name: "non_exhaustive_omitted_patterns",
|
||||
};
|
||||
|
||||
|
|
|
@ -1700,7 +1700,7 @@ pub fn get_cmd_lint_options(
|
|||
let mut lint_opts_with_position = vec![];
|
||||
let mut describe_lints = false;
|
||||
|
||||
for level in [lint::Allow, lint::Warn, lint::ForceWarn(None), lint::Deny, lint::Forbid] {
|
||||
for level in [lint::Allow, lint::Warn, lint::ForceWarn, lint::Deny, lint::Forbid] {
|
||||
for (arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) {
|
||||
if lint_name == "help" {
|
||||
describe_lints = true;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue