Auto merge of #3768 - rust-lang:rustup-2024-07-27, r=RalfJung
Automatic Rustup
This commit is contained in:
commit
a6796c10a1
439 changed files with 6858 additions and 6195 deletions
31
Cargo.lock
31
Cargo.lock
|
|
@ -573,7 +573,7 @@ checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
|
|||
|
||||
[[package]]
|
||||
name = "clippy"
|
||||
version = "0.1.81"
|
||||
version = "0.1.82"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"clippy_config",
|
||||
|
|
@ -594,13 +594,13 @@ dependencies = [
|
|||
"termize",
|
||||
"tokio",
|
||||
"toml 0.7.8",
|
||||
"ui_test 0.23.0",
|
||||
"ui_test 0.24.0",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clippy_config"
|
||||
version = "0.1.81"
|
||||
version = "0.1.82"
|
||||
dependencies = [
|
||||
"rustc-semver",
|
||||
"serde",
|
||||
|
|
@ -623,7 +623,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clippy_lints"
|
||||
version = "0.1.81"
|
||||
version = "0.1.82"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"cargo_metadata 0.18.1",
|
||||
|
|
@ -648,7 +648,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clippy_utils"
|
||||
version = "0.1.81"
|
||||
version = "0.1.82"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"clippy_config",
|
||||
|
|
@ -969,7 +969,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "declare_clippy_lint"
|
||||
version = "0.1.81"
|
||||
version = "0.1.82"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"quote",
|
||||
|
|
@ -2977,6 +2977,16 @@ dependencies = [
|
|||
"pad",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettydiff"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abec3fb083c10660b3854367697da94c674e9e82aa7511014dc958beeb7215e9"
|
||||
dependencies = [
|
||||
"owo-colors",
|
||||
"pad",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.20+deprecated"
|
||||
|
|
@ -5825,7 +5835,7 @@ dependencies = [
|
|||
"indicatif",
|
||||
"lazy_static",
|
||||
"levenshtein",
|
||||
"prettydiff",
|
||||
"prettydiff 0.6.4",
|
||||
"regex",
|
||||
"rustc_version",
|
||||
"rustfix 0.6.1",
|
||||
|
|
@ -5836,9 +5846,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ui_test"
|
||||
version = "0.23.0"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29e5f4ffcbab82453958fbf59990e981b8e8a177dcd60c2bd8f9b52c3036a6e1"
|
||||
checksum = "bc1c6c78d55482388711c8d417b8e547263046a607512278fed274c54633bbe4"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.11.4",
|
||||
"anyhow",
|
||||
|
|
@ -5852,14 +5862,13 @@ dependencies = [
|
|||
"indicatif",
|
||||
"lazy_static",
|
||||
"levenshtein",
|
||||
"prettydiff",
|
||||
"prettydiff 0.7.0",
|
||||
"regex",
|
||||
"rustc_version",
|
||||
"rustfix 0.8.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"spanned",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
id: NodeId,
|
||||
hir_id: hir::HirId,
|
||||
ident: &mut Ident,
|
||||
attrs: Option<&'hir [Attribute]>,
|
||||
attrs: &'hir [Attribute],
|
||||
vis_span: Span,
|
||||
i: &ItemKind,
|
||||
) -> hir::ItemKind<'hir> {
|
||||
|
|
@ -488,7 +488,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
id: NodeId,
|
||||
vis_span: Span,
|
||||
ident: &mut Ident,
|
||||
attrs: Option<&'hir [Attribute]>,
|
||||
attrs: &'hir [Attribute],
|
||||
) -> hir::ItemKind<'hir> {
|
||||
let path = &tree.prefix;
|
||||
let segments = prefix.segments.iter().chain(path.segments.iter()).cloned().collect();
|
||||
|
|
@ -566,7 +566,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
// `ItemLocalId` and the new owner. (See `lower_node_id`)
|
||||
let kind =
|
||||
this.lower_use_tree(use_tree, &prefix, id, vis_span, &mut ident, attrs);
|
||||
if let Some(attrs) = attrs {
|
||||
if !attrs.is_empty() {
|
||||
this.attrs.insert(hir::ItemLocalId::ZERO, attrs);
|
||||
}
|
||||
|
||||
|
|
@ -1525,8 +1525,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
continue;
|
||||
}
|
||||
let is_param = *is_param.get_or_insert_with(compute_is_param);
|
||||
if !is_param {
|
||||
self.dcx().emit_err(MisplacedRelaxTraitBound { span: bound.span() });
|
||||
if !is_param && !self.tcx.features().more_maybe_bounds {
|
||||
self.tcx
|
||||
.sess
|
||||
.create_feature_err(
|
||||
MisplacedRelaxTraitBound { span: bound.span() },
|
||||
sym::more_maybe_bounds,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -913,15 +913,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
ret
|
||||
}
|
||||
|
||||
fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> Option<&'hir [Attribute]> {
|
||||
fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> &'hir [Attribute] {
|
||||
if attrs.is_empty() {
|
||||
None
|
||||
&[]
|
||||
} else {
|
||||
debug_assert_eq!(id.owner, self.current_hir_id_owner);
|
||||
let ret = self.arena.alloc_from_iter(attrs.iter().map(|a| self.lower_attr(a)));
|
||||
debug_assert!(!ret.is_empty());
|
||||
self.attrs.insert(id.local_id, ret);
|
||||
Some(ret)
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1216,6 +1216,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
itctx,
|
||||
TraitBoundModifiers::NONE,
|
||||
);
|
||||
let bound = (bound, hir::TraitBoundModifier::None);
|
||||
let bounds = this.arena.alloc_from_iter([bound]);
|
||||
let lifetime_bound = this.elided_dyn_bound(t.span);
|
||||
(bounds, lifetime_bound)
|
||||
|
|
@ -1348,21 +1349,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// We can safely ignore constness here since AST validation
|
||||
// takes care of rejecting invalid modifier combinations and
|
||||
// const trait bounds in trait object types.
|
||||
GenericBound::Trait(ty, modifiers) => match modifiers.polarity {
|
||||
BoundPolarity::Positive | BoundPolarity::Negative(_) => {
|
||||
Some(this.lower_poly_trait_ref(
|
||||
ty,
|
||||
itctx,
|
||||
// Still, don't pass along the constness here; we don't want to
|
||||
// synthesize any host effect args, it'd only cause problems.
|
||||
TraitBoundModifiers {
|
||||
constness: BoundConstness::Never,
|
||||
..*modifiers
|
||||
},
|
||||
))
|
||||
}
|
||||
BoundPolarity::Maybe(_) => None,
|
||||
},
|
||||
GenericBound::Trait(ty, modifiers) => {
|
||||
// Still, don't pass along the constness here; we don't want to
|
||||
// synthesize any host effect args, it'd only cause problems.
|
||||
let modifiers = TraitBoundModifiers {
|
||||
constness: BoundConstness::Never,
|
||||
..*modifiers
|
||||
};
|
||||
let trait_ref = this.lower_poly_trait_ref(ty, itctx, modifiers);
|
||||
let polarity = this.lower_trait_bound_modifiers(modifiers);
|
||||
Some((trait_ref, polarity))
|
||||
}
|
||||
GenericBound::Outlives(lifetime) => {
|
||||
if lifetime_bound.is_none() {
|
||||
lifetime_bound = Some(this.lower_lifetime(lifetime));
|
||||
|
|
@ -2688,6 +2685,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
trait_ref: hir::TraitRef { path, hir_ref_id: hir_id },
|
||||
span: self.lower_span(span),
|
||||
};
|
||||
let principal = (principal, hir::TraitBoundModifier::None);
|
||||
|
||||
// The original ID is taken by the `PolyTraitRef`,
|
||||
// so the `Ty` itself needs a different one.
|
||||
|
|
|
|||
|
|
@ -1345,14 +1345,28 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
match bound {
|
||||
GenericBound::Trait(trait_ref, modifiers) => {
|
||||
match (ctxt, modifiers.constness, modifiers.polarity) {
|
||||
(BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_)) => {
|
||||
self.dcx().emit_err(errors::OptionalTraitSupertrait {
|
||||
span: trait_ref.span,
|
||||
path_str: pprust::path_to_string(&trait_ref.trait_ref.path),
|
||||
});
|
||||
(BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_))
|
||||
if !self.features.more_maybe_bounds =>
|
||||
{
|
||||
self.session
|
||||
.create_feature_err(
|
||||
errors::OptionalTraitSupertrait {
|
||||
span: trait_ref.span,
|
||||
path_str: pprust::path_to_string(&trait_ref.trait_ref.path),
|
||||
},
|
||||
sym::more_maybe_bounds,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
(BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_)) => {
|
||||
self.dcx().emit_err(errors::OptionalTraitObject { span: trait_ref.span });
|
||||
(BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_))
|
||||
if !self.features.more_maybe_bounds =>
|
||||
{
|
||||
self.session
|
||||
.create_feature_err(
|
||||
errors::OptionalTraitObject { span: trait_ref.span },
|
||||
sym::more_maybe_bounds,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
(
|
||||
BoundKind::TraitObject,
|
||||
|
|
|
|||
|
|
@ -1442,9 +1442,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
|||
// See `tests/ui/moves/needs-clone-through-deref.rs`
|
||||
return false;
|
||||
}
|
||||
// We don't want to suggest `.clone()` in a move closure, since the value has already been captured.
|
||||
if self.in_move_closure(expr) {
|
||||
return false;
|
||||
}
|
||||
// We also don't want to suggest cloning a closure itself, since the value has already been captured.
|
||||
if let hir::ExprKind::Closure(_) = expr.kind {
|
||||
return false;
|
||||
}
|
||||
// Try to find predicates on *generic params* that would allow copying `ty`
|
||||
let mut suggestion =
|
||||
if let Some(symbol) = tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
|
||||
|
|
|
|||
|
|
@ -205,6 +205,8 @@ declare_features! (
|
|||
(unstable, lifetime_capture_rules_2024, "1.76.0", None),
|
||||
/// Allows `#[link(..., cfg(..))]`; perma-unstable per #37406
|
||||
(unstable, link_cfg, "1.14.0", None),
|
||||
/// Allows using `?Trait` trait bounds in more contexts.
|
||||
(internal, more_maybe_bounds, "CURRENT_RUSTC_VERSION", None),
|
||||
/// Allows the `multiple_supertrait_upcastable` lint.
|
||||
(unstable, multiple_supertrait_upcastable, "1.69.0", None),
|
||||
/// Allow negative trait bounds. This is an internal-only feature for testing the trait solver!
|
||||
|
|
|
|||
|
|
@ -2832,7 +2832,11 @@ pub enum TyKind<'hir> {
|
|||
OpaqueDef(ItemId, &'hir [GenericArg<'hir>], bool),
|
||||
/// A trait object type `Bound1 + Bound2 + Bound3`
|
||||
/// where `Bound` is a trait or a lifetime.
|
||||
TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax),
|
||||
TraitObject(
|
||||
&'hir [(PolyTraitRef<'hir>, TraitBoundModifier)],
|
||||
&'hir Lifetime,
|
||||
TraitObjectSyntax,
|
||||
),
|
||||
/// Unused for now.
|
||||
Typeof(&'hir AnonConst),
|
||||
/// `TyKind::Infer` means the type should be inferred instead of it having been
|
||||
|
|
|
|||
|
|
@ -902,7 +902,9 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul
|
|||
try_visit!(visitor.visit_array_length(length));
|
||||
}
|
||||
TyKind::TraitObject(bounds, ref lifetime, _syntax) => {
|
||||
walk_list!(visitor, visit_poly_trait_ref, bounds);
|
||||
for (bound, _modifier) in bounds {
|
||||
try_visit!(visitor.visit_poly_trait_ref(bound));
|
||||
}
|
||||
try_visit!(visitor.visit_lifetime(lifetime));
|
||||
}
|
||||
TyKind::Typeof(ref expression) => try_visit!(visitor.visit_anon_const(expression)),
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ language_item_table! {
|
|||
StructuralPeq, sym::structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None;
|
||||
Copy, sym::copy, copy_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
Clone, sym::clone, clone_trait, Target::Trait, GenericRequirement::None;
|
||||
CloneFn, sym::clone_fn, clone_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
Sync, sym::sync, sync_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
DiscriminantKind, sym::discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None;
|
||||
/// The associated item of the `DiscriminantKind` trait.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$ty_param_name}`
|
||||
hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$qself}`
|
||||
.label = ambiguous associated {$assoc_kind} `{$assoc_name}`
|
||||
|
||||
hir_analysis_ambiguous_lifetime_bound =
|
||||
|
|
@ -12,16 +12,21 @@ hir_analysis_assoc_item_is_private = {$kind} `{$name}` is private
|
|||
.label = private {$kind}
|
||||
.defined_here_label = the {$kind} is defined here
|
||||
|
||||
hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$ty_param_name}`
|
||||
hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$qself}`
|
||||
|
||||
hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identically_named ->
|
||||
[true] an
|
||||
*[false] a similarly named
|
||||
} associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}`
|
||||
hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_name}` not found
|
||||
hir_analysis_assoc_item_not_found_other_sugg = `{$ty_param_name}` has the following associated {$assoc_kind}
|
||||
hir_analysis_assoc_item_not_found_other_sugg = `{$qself}` has the following associated {$assoc_kind}
|
||||
hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg =
|
||||
consider fully qualifying{$identically_named ->
|
||||
[true] {""}
|
||||
*[false] {" "}and renaming
|
||||
} the associated {$assoc_kind}
|
||||
hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg = change the associated {$assoc_kind} name to use `{$suggested_name}` from `{$trait_name}`
|
||||
hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = and also change the associated {$assoc_kind} name
|
||||
hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = ...and changing the associated {$assoc_kind} name
|
||||
hir_analysis_assoc_item_not_found_similar_sugg = there is an associated {$assoc_kind} with a similar name
|
||||
|
||||
hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got}
|
||||
|
|
|
|||
|
|
@ -831,7 +831,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GATArgsCollector<'tcx> {
|
|||
|
||||
fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool {
|
||||
match ty.kind {
|
||||
hir::TyKind::TraitObject([trait_ref], ..) => match trait_ref.trait_ref.path.segments {
|
||||
hir::TyKind::TraitObject([(trait_ref, _)], ..) => match trait_ref.trait_ref.path.segments {
|
||||
[s] => s.res.opt_def_id() == Some(trait_def_id.to_def_id()),
|
||||
_ => false,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -652,7 +652,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
|
|||
debug!(?bounds, ?lifetime, "TraitObject");
|
||||
let scope = Scope::TraitRefBoundary { s: self.scope };
|
||||
self.with(scope, |this| {
|
||||
for bound in bounds {
|
||||
for (bound, _) in bounds {
|
||||
this.visit_poly_trait_ref_inner(
|
||||
bound,
|
||||
NonLifetimeBinderAllowed::Deny("trait object types"),
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ pub struct AmbiguousAssocItem<'a> {
|
|||
pub span: Span,
|
||||
pub assoc_kind: &'static str,
|
||||
pub assoc_name: Ident,
|
||||
pub ty_param_name: &'a str,
|
||||
pub qself: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
@ -75,7 +75,7 @@ pub struct AssocItemNotFound<'a> {
|
|||
pub span: Span,
|
||||
pub assoc_name: Ident,
|
||||
pub assoc_kind: &'static str,
|
||||
pub ty_param_name: &'a str,
|
||||
pub qself: &'a str,
|
||||
#[subdiagnostic]
|
||||
pub label: Option<AssocItemNotFoundLabel<'a>>,
|
||||
#[subdiagnostic]
|
||||
|
|
@ -126,13 +126,32 @@ pub enum AssocItemNotFoundSugg<'a> {
|
|||
assoc_kind: &'static str,
|
||||
suggested_name: Symbol,
|
||||
},
|
||||
#[suggestion(hir_analysis_assoc_item_not_found_other_sugg, code = "{suggested_name}")]
|
||||
#[multipart_suggestion(
|
||||
hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg,
|
||||
style = "verbose"
|
||||
)]
|
||||
SimilarInOtherTraitQPath {
|
||||
#[suggestion_part(code = "<")]
|
||||
lo: Span,
|
||||
#[suggestion_part(code = " as {trait_ref}>")]
|
||||
mi: Span,
|
||||
#[suggestion_part(code = "{suggested_name}")]
|
||||
hi: Option<Span>,
|
||||
trait_ref: String,
|
||||
suggested_name: Symbol,
|
||||
identically_named: bool,
|
||||
#[applicability]
|
||||
applicability: Applicability,
|
||||
},
|
||||
#[suggestion(
|
||||
hir_analysis_assoc_item_not_found_other_sugg,
|
||||
code = "{suggested_name}",
|
||||
applicability = "maybe-incorrect"
|
||||
)]
|
||||
Other {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
#[applicability]
|
||||
applicability: Applicability,
|
||||
ty_param_name: &'a str,
|
||||
qself: &'a str,
|
||||
assoc_kind: &'static str,
|
||||
suggested_name: Symbol,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,19 +6,17 @@ use rustc_hir as hir;
|
|||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::print::PrintTraitRefExt as _;
|
||||
use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt};
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{ErrorGuaranteed, Span, Symbol};
|
||||
use rustc_span::{sym, ErrorGuaranteed, Span, Symbol};
|
||||
use rustc_trait_selection::traits;
|
||||
use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::bounds::Bounds;
|
||||
use crate::errors;
|
||||
use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter};
|
||||
|
||||
use super::RegionInferReason;
|
||||
use crate::hir_ty_lowering::HirTyLowerer;
|
||||
use crate::hir_ty_lowering::{AssocItemQSelf, OnlySelfBounds, PredicateFilter, RegionInferReason};
|
||||
|
||||
impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||
/// Add a `Sized` bound to the `bounds` if appropriate.
|
||||
|
|
@ -75,10 +73,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
}
|
||||
}
|
||||
|
||||
let mut unique_bounds = FxIndexSet::default();
|
||||
let mut seen_repeat = false;
|
||||
for unbound in &unbounds {
|
||||
if let Res::Def(DefKind::Trait, unbound_def_id) = unbound.trait_ref.path.res {
|
||||
seen_repeat |= !unique_bounds.insert(unbound_def_id);
|
||||
}
|
||||
}
|
||||
if unbounds.len() > 1 {
|
||||
self.dcx().emit_err(errors::MultipleRelaxedDefaultBounds {
|
||||
let err = errors::MultipleRelaxedDefaultBounds {
|
||||
spans: unbounds.iter().map(|ptr| ptr.span).collect(),
|
||||
});
|
||||
};
|
||||
if seen_repeat {
|
||||
self.dcx().emit_err(err);
|
||||
} else if !tcx.features().more_maybe_bounds {
|
||||
self.tcx().sess.create_feature_err(err, sym::more_maybe_bounds).emit();
|
||||
};
|
||||
}
|
||||
|
||||
let mut seen_sized_unbound = false;
|
||||
|
|
@ -288,8 +298,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
// one that does define it.
|
||||
self.probe_single_bound_for_assoc_item(
|
||||
|| traits::supertraits(tcx, trait_ref),
|
||||
trait_ref.skip_binder().print_only_trait_name(),
|
||||
None,
|
||||
AssocItemQSelf::Trait(trait_ref.def_id()),
|
||||
assoc_kind,
|
||||
constraint.ident,
|
||||
path_span,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::errors::{
|
|||
ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits,
|
||||
};
|
||||
use crate::fluent_generated as fluent;
|
||||
use crate::hir_ty_lowering::HirTyLowerer;
|
||||
use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer};
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_data_structures::unord::UnordMap;
|
||||
|
|
@ -11,9 +11,9 @@ use rustc_errors::MultiSpan;
|
|||
use rustc_errors::{
|
||||
codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::{self as hir, Node};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::query::Key;
|
||||
use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _};
|
||||
|
|
@ -116,8 +116,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
pub(super) fn complain_about_assoc_item_not_found<I>(
|
||||
&self,
|
||||
all_candidates: impl Fn() -> I,
|
||||
ty_param_name: &str,
|
||||
ty_param_def_id: Option<LocalDefId>,
|
||||
qself: AssocItemQSelf,
|
||||
assoc_kind: ty::AssocKind,
|
||||
assoc_name: Ident,
|
||||
span: Span,
|
||||
|
|
@ -139,7 +138,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
);
|
||||
}
|
||||
|
||||
let assoc_kind_str = super::assoc_kind_str(assoc_kind);
|
||||
let assoc_kind_str = assoc_kind_str(assoc_kind);
|
||||
let qself_str = qself.to_string(tcx);
|
||||
|
||||
// The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a
|
||||
// valid span, so we point at the whole path segment instead.
|
||||
|
|
@ -149,7 +149,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
span: if is_dummy { span } else { assoc_name.span },
|
||||
assoc_name,
|
||||
assoc_kind: assoc_kind_str,
|
||||
ty_param_name,
|
||||
qself: &qself_str,
|
||||
label: None,
|
||||
sugg: None,
|
||||
};
|
||||
|
|
@ -219,19 +219,28 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
suggested_name,
|
||||
identically_named: suggested_name == assoc_name.name,
|
||||
});
|
||||
let hir = tcx.hir();
|
||||
if let Some(def_id) = ty_param_def_id
|
||||
&& let parent = hir.get_parent_item(tcx.local_def_id_to_hir_id(def_id))
|
||||
&& let Some(generics) = hir.get_generics(parent.def_id)
|
||||
if let AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span) = qself
|
||||
// Not using `self.item_def_id()` here as that would yield the opaque type itself if we're
|
||||
// inside an opaque type while we're interested in the overarching type alias (TAIT).
|
||||
// FIXME: However, for trait aliases, this incorrectly returns the enclosing module...
|
||||
&& let item_def_id =
|
||||
tcx.hir().get_parent_item(tcx.local_def_id_to_hir_id(ty_param_def_id))
|
||||
// FIXME: ...which obviously won't have any generics.
|
||||
&& let Some(generics) = tcx.hir().get_generics(item_def_id.def_id)
|
||||
{
|
||||
if generics.bounds_for_param(def_id).flat_map(|pred| pred.bounds.iter()).any(
|
||||
|b| match b {
|
||||
// FIXME: Suggest adding supertrait bounds if we have a `Self` type param.
|
||||
// FIXME(trait_alias): Suggest adding `Self: Trait` to
|
||||
// `trait Alias = where Self::Proj:;` with `trait Trait { type Proj; }`.
|
||||
if generics
|
||||
.bounds_for_param(ty_param_def_id)
|
||||
.flat_map(|pred| pred.bounds.iter())
|
||||
.any(|b| match b {
|
||||
hir::GenericBound::Trait(t, ..) => {
|
||||
t.trait_ref.trait_def_id() == Some(best_trait)
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
) {
|
||||
})
|
||||
{
|
||||
// The type param already has a bound for `trait_name`, we just need to
|
||||
// change the associated item.
|
||||
err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait {
|
||||
|
|
@ -242,48 +251,60 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
return self.dcx().emit_err(err);
|
||||
}
|
||||
|
||||
let mut err = self.dcx().create_err(err);
|
||||
if suggest_constraining_type_param(
|
||||
tcx,
|
||||
generics,
|
||||
&mut err,
|
||||
&ty_param_name,
|
||||
&trait_name,
|
||||
None,
|
||||
None,
|
||||
) && suggested_name != assoc_name.name
|
||||
let trait_args = &ty::GenericArgs::identity_for_item(tcx, best_trait)[1..];
|
||||
let mut trait_ref = trait_name.clone();
|
||||
let applicability = if let [arg, args @ ..] = trait_args {
|
||||
use std::fmt::Write;
|
||||
write!(trait_ref, "</* {arg}").unwrap();
|
||||
args.iter().try_for_each(|arg| write!(trait_ref, ", {arg}")).unwrap();
|
||||
trait_ref += " */>";
|
||||
Applicability::HasPlaceholders
|
||||
} else {
|
||||
Applicability::MaybeIncorrect
|
||||
};
|
||||
|
||||
let identically_named = suggested_name == assoc_name.name;
|
||||
|
||||
if let DefKind::TyAlias = tcx.def_kind(item_def_id)
|
||||
&& !tcx.type_alias_is_lazy(item_def_id)
|
||||
{
|
||||
// We suggested constraining a type parameter, but the associated item on it
|
||||
// was also not an exact match, so we also suggest changing it.
|
||||
err.span_suggestion_verbose(
|
||||
assoc_name.span,
|
||||
fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg,
|
||||
err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTraitQPath {
|
||||
lo: ty_param_span.shrink_to_lo(),
|
||||
mi: ty_param_span.shrink_to_hi(),
|
||||
hi: (!identically_named).then_some(assoc_name.span),
|
||||
trait_ref,
|
||||
identically_named,
|
||||
suggested_name,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
applicability,
|
||||
});
|
||||
} else {
|
||||
let mut err = self.dcx().create_err(err);
|
||||
if suggest_constraining_type_param(
|
||||
tcx, generics, &mut err, &qself_str, &trait_ref, None, None,
|
||||
) && !identically_named
|
||||
{
|
||||
// We suggested constraining a type parameter, but the associated item on it
|
||||
// was also not an exact match, so we also suggest changing it.
|
||||
err.span_suggestion_verbose(
|
||||
assoc_name.span,
|
||||
fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg,
|
||||
suggested_name,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
return err.emit();
|
||||
}
|
||||
return err.emit();
|
||||
}
|
||||
return self.dcx().emit_err(err);
|
||||
}
|
||||
}
|
||||
|
||||
// If we still couldn't find any associated item, and only one associated item exists,
|
||||
// suggests using it.
|
||||
// suggest using it.
|
||||
if let [candidate_name] = all_candidate_names.as_slice() {
|
||||
// This should still compile, except on `#![feature(associated_type_defaults)]`
|
||||
// where it could suggests `type A = Self::A`, thus recursing infinitely.
|
||||
let applicability =
|
||||
if assoc_kind == ty::AssocKind::Type && tcx.features().associated_type_defaults {
|
||||
Applicability::Unspecified
|
||||
} else {
|
||||
Applicability::MaybeIncorrect
|
||||
};
|
||||
|
||||
err.sugg = Some(errors::AssocItemNotFoundSugg::Other {
|
||||
span: assoc_name.span,
|
||||
applicability,
|
||||
ty_param_name,
|
||||
qself: &qself_str,
|
||||
assoc_kind: assoc_kind_str,
|
||||
suggested_name: *candidate_name,
|
||||
});
|
||||
|
|
@ -349,10 +370,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
|
||||
self.dcx().emit_err(errors::AssocKindMismatch {
|
||||
span,
|
||||
expected: super::assoc_kind_str(expected),
|
||||
got: super::assoc_kind_str(got),
|
||||
expected: assoc_kind_str(expected),
|
||||
got: assoc_kind_str(got),
|
||||
expected_because_label,
|
||||
assoc_kind: super::assoc_kind_str(assoc_item.kind),
|
||||
assoc_kind: assoc_kind_str(assoc_item.kind),
|
||||
def_span: tcx.def_span(assoc_item.def_id),
|
||||
bound_on_assoc_const_label,
|
||||
wrap_in_braces_sugg,
|
||||
|
|
@ -698,7 +719,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
&self,
|
||||
associated_types: FxIndexMap<Span, FxIndexSet<DefId>>,
|
||||
potential_assoc_types: Vec<usize>,
|
||||
trait_bounds: &[hir::PolyTraitRef<'_>],
|
||||
trait_bounds: &[(hir::PolyTraitRef<'_>, hir::TraitBoundModifier)],
|
||||
) {
|
||||
if associated_types.values().all(|v| v.is_empty()) {
|
||||
return;
|
||||
|
|
@ -744,12 +765,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
// related to issue #91997, turbofishes added only when in an expr or pat
|
||||
let mut in_expr_or_pat = false;
|
||||
if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) {
|
||||
let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id));
|
||||
let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.0.trait_ref.hir_ref_id));
|
||||
in_expr_or_pat = match grandparent {
|
||||
Node::Expr(_) | Node::Pat(_) => true,
|
||||
hir::Node::Expr(_) | hir::Node::Pat(_) => true,
|
||||
_ => false,
|
||||
};
|
||||
match bound.trait_ref.path.segments {
|
||||
match bound.0.trait_ref.path.segments {
|
||||
// FIXME: `trait_ref.path.span` can point to a full path with multiple
|
||||
// segments, even though `trait_ref.path.segments` is of length `1`. Work
|
||||
// around that bug here, even though it should be fixed elsewhere.
|
||||
|
|
@ -790,7 +811,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
// and we can then use their span to indicate this to the user.
|
||||
let bound_names = trait_bounds
|
||||
.iter()
|
||||
.filter_map(|poly_trait_ref| {
|
||||
.filter_map(|(poly_trait_ref, _)| {
|
||||
let path = poly_trait_ref.trait_ref.path.segments.last()?;
|
||||
let args = path.args?;
|
||||
|
||||
|
|
@ -1612,3 +1633,11 @@ fn generics_args_err_extend<'a>(
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn assoc_kind_str(kind: ty::AssocKind) -> &'static str {
|
||||
match kind {
|
||||
ty::AssocKind::Fn => "function",
|
||||
ty::AssocKind::Const => "constant",
|
||||
ty::AssocKind::Type => "type",
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
.ok()
|
||||
.is_some_and(|s| s.trim_end().ends_with('<'));
|
||||
|
||||
let is_global = poly_trait_ref.trait_ref.path.is_global();
|
||||
let is_global = poly_trait_ref.0.trait_ref.path.is_global();
|
||||
|
||||
let mut sugg = vec![(
|
||||
self_ty.span.shrink_to_lo(),
|
||||
|
|
@ -176,7 +176,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
let mut is_downgradable = true;
|
||||
let is_object_safe = match self_ty.kind {
|
||||
hir::TyKind::TraitObject(objects, ..) => {
|
||||
objects.iter().all(|o| match o.trait_ref.path.res {
|
||||
objects.iter().all(|(o, _)| match o.trait_ref.path.res {
|
||||
Res::Def(DefKind::Trait, id) => {
|
||||
if Some(id) == owner {
|
||||
// For recursive traits, don't downgrade the error. (#119652)
|
||||
|
|
|
|||
|
|
@ -55,7 +55,6 @@ use rustc_trait_selection::infer::InferCtxtExt;
|
|||
use rustc_trait_selection::traits::wf::object_region_bounds;
|
||||
use rustc_trait_selection::traits::{self, ObligationCtxt};
|
||||
|
||||
use std::fmt::Display;
|
||||
use std::slice;
|
||||
|
||||
/// A path segment that is semantically allowed to have generic arguments.
|
||||
|
|
@ -193,6 +192,25 @@ pub trait HirTyLowerer<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The "qualified self" of an associated item path.
|
||||
///
|
||||
/// For diagnostic purposes only.
|
||||
enum AssocItemQSelf {
|
||||
Trait(DefId),
|
||||
TyParam(LocalDefId, Span),
|
||||
SelfTyAlias,
|
||||
}
|
||||
|
||||
impl AssocItemQSelf {
|
||||
fn to_string(&self, tcx: TyCtxt<'_>) -> String {
|
||||
match *self {
|
||||
Self::Trait(def_id) => tcx.def_path_str(def_id),
|
||||
Self::TyParam(def_id, _) => tcx.hir().ty_param_name(def_id).to_string(),
|
||||
Self::SelfTyAlias => kw::SelfUpper.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// New-typed boolean indicating whether explicit late-bound lifetimes
|
||||
/// are present in a set of generic arguments.
|
||||
///
|
||||
|
|
@ -802,6 +820,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
fn probe_single_ty_param_bound_for_assoc_ty(
|
||||
&self,
|
||||
ty_param_def_id: LocalDefId,
|
||||
ty_param_span: Span,
|
||||
assoc_name: Ident,
|
||||
span: Span,
|
||||
) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> {
|
||||
|
|
@ -811,19 +830,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
let predicates = &self.probe_ty_param_bounds(span, ty_param_def_id, assoc_name).predicates;
|
||||
debug!("predicates={:#?}", predicates);
|
||||
|
||||
let param_name = tcx.hir().ty_param_name(ty_param_def_id);
|
||||
self.probe_single_bound_for_assoc_item(
|
||||
|| {
|
||||
traits::transitive_bounds_that_define_assoc_item(
|
||||
tcx,
|
||||
predicates
|
||||
.iter()
|
||||
.filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))),
|
||||
assoc_name,
|
||||
)
|
||||
let trait_refs = predicates
|
||||
.iter()
|
||||
.filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref)));
|
||||
traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name)
|
||||
},
|
||||
param_name,
|
||||
Some(ty_param_def_id),
|
||||
AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span),
|
||||
ty::AssocKind::Type,
|
||||
assoc_name,
|
||||
span,
|
||||
|
|
@ -835,12 +849,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
///
|
||||
/// This fails if there is no such bound in the list of candidates or if there are multiple
|
||||
/// candidates in which case it reports ambiguity.
|
||||
#[instrument(level = "debug", skip(self, all_candidates, ty_param_name, constraint), ret)]
|
||||
#[instrument(level = "debug", skip(self, all_candidates, qself, constraint), ret)]
|
||||
fn probe_single_bound_for_assoc_item<I>(
|
||||
&self,
|
||||
all_candidates: impl Fn() -> I,
|
||||
ty_param_name: impl Display,
|
||||
ty_param_def_id: Option<LocalDefId>,
|
||||
qself: AssocItemQSelf,
|
||||
assoc_kind: ty::AssocKind,
|
||||
assoc_name: Ident,
|
||||
span: Span,
|
||||
|
|
@ -858,8 +871,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
let Some(bound) = matching_candidates.next() else {
|
||||
let reported = self.complain_about_assoc_item_not_found(
|
||||
all_candidates,
|
||||
&ty_param_name.to_string(),
|
||||
ty_param_def_id,
|
||||
qself,
|
||||
assoc_kind,
|
||||
assoc_name,
|
||||
span,
|
||||
|
|
@ -872,13 +884,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
if let Some(bound2) = matching_candidates.next() {
|
||||
debug!(?bound2);
|
||||
|
||||
let assoc_kind_str = assoc_kind_str(assoc_kind);
|
||||
let ty_param_name = &ty_param_name.to_string();
|
||||
let assoc_kind_str = errors::assoc_kind_str(assoc_kind);
|
||||
let qself_str = qself.to_string(tcx);
|
||||
let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem {
|
||||
span,
|
||||
assoc_kind: assoc_kind_str,
|
||||
assoc_name,
|
||||
ty_param_name,
|
||||
qself: &qself_str,
|
||||
});
|
||||
// Provide a more specific error code index entry for equality bindings.
|
||||
err.code(
|
||||
|
|
@ -929,7 +941,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
err.span_suggestion_verbose(
|
||||
span.with_hi(assoc_name.span.lo()),
|
||||
"use fully-qualified syntax to disambiguate",
|
||||
format!("<{ty_param_name} as {}>::", bound.print_only_trait_path()),
|
||||
format!("<{qself_str} as {}>::", bound.print_only_trait_path()),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
|
|
@ -943,7 +955,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
if !where_bounds.is_empty() {
|
||||
err.help(format!(
|
||||
"consider introducing a new type parameter `T` and adding `where` constraints:\
|
||||
\n where\n T: {ty_param_name},\n{}",
|
||||
\n where\n T: {qself_str},\n{}",
|
||||
where_bounds.join(",\n"),
|
||||
));
|
||||
}
|
||||
|
|
@ -997,11 +1009,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
let tcx = self.tcx();
|
||||
|
||||
let assoc_ident = assoc_segment.ident;
|
||||
let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind {
|
||||
path.res
|
||||
} else {
|
||||
Res::Err
|
||||
};
|
||||
|
||||
// Check if we have an enum variant or an inherent associated type.
|
||||
let mut variant_resolution = None;
|
||||
|
|
@ -1038,6 +1045,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
}
|
||||
}
|
||||
|
||||
let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind {
|
||||
path.res
|
||||
} else {
|
||||
Res::Err
|
||||
};
|
||||
|
||||
// Find the type of the associated item, and the trait where the associated
|
||||
// item is declared.
|
||||
let bound = match (&qself_ty.kind(), qself_res) {
|
||||
|
|
@ -1056,8 +1069,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
ty::Binder::dummy(trait_ref.instantiate_identity()),
|
||||
)
|
||||
},
|
||||
kw::SelfUpper,
|
||||
None,
|
||||
AssocItemQSelf::SelfTyAlias,
|
||||
ty::AssocKind::Type,
|
||||
assoc_ident,
|
||||
span,
|
||||
|
|
@ -1069,6 +1081,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did),
|
||||
) => self.probe_single_ty_param_bound_for_assoc_ty(
|
||||
param_did.expect_local(),
|
||||
qself.span,
|
||||
assoc_ident,
|
||||
span,
|
||||
)?,
|
||||
|
|
@ -2522,11 +2535,3 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
Some(r)
|
||||
}
|
||||
}
|
||||
|
||||
fn assoc_kind_str(kind: ty::AssocKind) -> &'static str {
|
||||
match kind {
|
||||
ty::AssocKind::Fn => "function",
|
||||
ty::AssocKind::Const => "constant",
|
||||
ty::AssocKind::Type => "type",
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
&self,
|
||||
span: Span,
|
||||
hir_id: hir::HirId,
|
||||
hir_trait_bounds: &[hir::PolyTraitRef<'tcx>],
|
||||
hir_trait_bounds: &[(hir::PolyTraitRef<'tcx>, hir::TraitBoundModifier)],
|
||||
lifetime: &hir::Lifetime,
|
||||
borrowed: bool,
|
||||
representation: DynKind,
|
||||
|
|
@ -37,7 +37,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
let mut bounds = Bounds::default();
|
||||
let mut potential_assoc_types = Vec::new();
|
||||
let dummy_self = self.tcx().types.trait_object_dummy_self;
|
||||
for trait_bound in hir_trait_bounds.iter().rev() {
|
||||
for (trait_bound, modifier) in hir_trait_bounds.iter().rev() {
|
||||
if *modifier == hir::TraitBoundModifier::Maybe {
|
||||
continue;
|
||||
}
|
||||
if let GenericArgCountResult {
|
||||
correct:
|
||||
Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }),
|
||||
|
|
@ -249,7 +252,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
let args = tcx.mk_args(&args);
|
||||
|
||||
let span = i.bottom().1;
|
||||
let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| {
|
||||
let empty_generic_args = hir_trait_bounds.iter().any(|(hir_bound, _)| {
|
||||
hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
|
||||
&& hir_bound.span.contains(span)
|
||||
});
|
||||
|
|
|
|||
|
|
@ -300,13 +300,16 @@ impl<'a> State<'a> {
|
|||
self.word_space("dyn");
|
||||
}
|
||||
let mut first = true;
|
||||
for bound in bounds {
|
||||
for (bound, modifier) in bounds {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
self.nbsp();
|
||||
self.word_space("+");
|
||||
}
|
||||
if *modifier == TraitBoundModifier::Maybe {
|
||||
self.word("?");
|
||||
}
|
||||
self.print_poly_trait_ref(bound);
|
||||
}
|
||||
if !lifetime.is_elided() {
|
||||
|
|
|
|||
|
|
@ -144,13 +144,18 @@ lint_builtin_special_module_name_used_main = found module declaration for main.r
|
|||
|
||||
lint_builtin_trivial_bounds = {$predicate_kind_name} bound {$predicate} does not depend on any type or lifetime parameters
|
||||
|
||||
lint_builtin_type_alias_bounds_help = use fully disambiguated paths (i.e., `<T as Trait>::Assoc`) to refer to associated types in type aliases
|
||||
|
||||
lint_builtin_type_alias_generic_bounds = bounds on generic parameters are not enforced in type aliases
|
||||
.suggestion = the bound will not be checked when the type alias is used, and should be removed
|
||||
|
||||
lint_builtin_type_alias_where_clause = where clauses are not enforced in type aliases
|
||||
.suggestion = the clause will not be checked when the type alias is used, and should be removed
|
||||
lint_builtin_type_alias_bounds_enable_feat_help = add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
|
||||
lint_builtin_type_alias_bounds_label = will not be checked at usage sites of the type alias
|
||||
lint_builtin_type_alias_bounds_limitation_note = this is a known limitation of the type checker that may be lifted in a future edition.
|
||||
see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
|
||||
lint_builtin_type_alias_bounds_param_bounds = bounds on generic parameters in type aliases are not enforced
|
||||
.suggestion = remove {$count ->
|
||||
[one] this bound
|
||||
*[other] these bounds
|
||||
}
|
||||
lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg = fully qualify this associated type
|
||||
lint_builtin_type_alias_bounds_where_clause = where clauses on type aliases are not enforced
|
||||
.suggestion = remove this where clause
|
||||
|
||||
lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed
|
||||
lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
|
|
|||
|
|
@ -31,12 +31,11 @@ use crate::{
|
|||
BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents,
|
||||
BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
|
||||
BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns,
|
||||
BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds,
|
||||
BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause,
|
||||
BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds,
|
||||
BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit,
|
||||
BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe,
|
||||
BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
|
||||
BuiltinWhileTrue, InvalidAsmLabel, SuggestChangingAssocTypes,
|
||||
BuiltinWhileTrue, InvalidAsmLabel,
|
||||
},
|
||||
EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext,
|
||||
};
|
||||
|
|
@ -1391,64 +1390,80 @@ declare_lint! {
|
|||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// The trait bounds in a type alias are currently ignored, and should not
|
||||
/// be included to avoid confusion. This was previously allowed
|
||||
/// unintentionally; this may become a hard error in the future.
|
||||
/// Trait and lifetime bounds on generic parameters and in where clauses of
|
||||
/// type aliases are not checked at usage sites of the type alias. Moreover,
|
||||
/// they are not thoroughly checked for correctness at their definition site
|
||||
/// either similar to the aliased type.
|
||||
///
|
||||
/// This is a known limitation of the type checker that may be lifted in a
|
||||
/// future edition. Permitting such bounds in light of this was unintentional.
|
||||
///
|
||||
/// While these bounds may have secondary effects such as enabling the use of
|
||||
/// "shorthand" associated type paths[^1] and affecting the default trait
|
||||
/// object lifetime[^2] of trait object types passed to the type alias, this
|
||||
/// should not have been allowed until the aforementioned restrictions of the
|
||||
/// type checker have been lifted.
|
||||
///
|
||||
/// Using such bounds is highly discouraged as they are actively misleading.
|
||||
///
|
||||
/// [^1]: I.e., paths of the form `T::Assoc` where `T` is a type parameter
|
||||
/// bounded by trait `Trait` which defines an associated type called `Assoc`
|
||||
/// as opposed to a fully qualified path of the form `<T as Trait>::Assoc`.
|
||||
/// [^2]: <https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes>
|
||||
TYPE_ALIAS_BOUNDS,
|
||||
Warn,
|
||||
"bounds in type aliases are not enforced"
|
||||
}
|
||||
|
||||
declare_lint_pass!(
|
||||
/// Lint for trait and lifetime bounds in type aliases being mostly ignored.
|
||||
/// They are relevant when using associated types, but otherwise neither checked
|
||||
/// at definition site nor enforced at use site.
|
||||
TypeAliasBounds => [TYPE_ALIAS_BOUNDS]
|
||||
);
|
||||
declare_lint_pass!(TypeAliasBounds => [TYPE_ALIAS_BOUNDS]);
|
||||
|
||||
impl TypeAliasBounds {
|
||||
pub(crate) fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool {
|
||||
match *qpath {
|
||||
hir::QPath::TypeRelative(ty, _) => {
|
||||
// If this is a type variable, we found a `T::Assoc`.
|
||||
match ty.kind {
|
||||
hir::TyKind::Path(hir::QPath::Resolved(None, path)) => {
|
||||
matches!(path.res, Res::Def(DefKind::TyParam, _))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
hir::QPath::Resolved(..) | hir::QPath::LangItem(..) => false,
|
||||
pub(crate) fn affects_object_lifetime_defaults(pred: &hir::WherePredicate<'_>) -> bool {
|
||||
// Bounds of the form `T: 'a` with `T` type param affect object lifetime defaults.
|
||||
if let hir::WherePredicate::BoundPredicate(pred) = pred
|
||||
&& pred.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Outlives(_)))
|
||||
&& pred.bound_generic_params.is_empty() // indeed, even if absent from the RHS
|
||||
&& pred.bounded_ty.as_generic_param().is_some()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
|
||||
let hir::ItemKind::TyAlias(hir_ty, type_alias_generics) = &item.kind else { return };
|
||||
let hir::ItemKind::TyAlias(hir_ty, generics) = item.kind else { return };
|
||||
|
||||
// There must not be a where clause.
|
||||
if generics.predicates.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bounds of lazy type aliases and TAITs are respected.
|
||||
if cx.tcx.type_alias_is_lazy(item.owner_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let ty = cx.tcx.type_of(item.owner_id).skip_binder();
|
||||
if ty.has_inherent_projections() {
|
||||
// Bounds of type aliases that contain opaque types or inherent projections are
|
||||
// respected. E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`, `type X =
|
||||
// Type::Inherent;`.
|
||||
// FIXME(generic_const_exprs): Revisit this before stabilization.
|
||||
// See also `tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs`.
|
||||
let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
|
||||
if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION)
|
||||
&& cx.tcx.features().generic_const_exprs
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// There must not be a where clause
|
||||
if type_alias_generics.predicates.is_empty() {
|
||||
return;
|
||||
}
|
||||
// NOTE(inherent_associated_types): While we currently do take some bounds in type
|
||||
// aliases into consideration during IAT *selection*, we don't perform full use+def
|
||||
// site wfchecking for such type aliases. Therefore TAB should still trigger.
|
||||
// See also `tests/ui/associated-inherent-types/type-alias-bounds.rs`.
|
||||
|
||||
let mut where_spans = Vec::new();
|
||||
let mut inline_spans = Vec::new();
|
||||
let mut inline_sugg = Vec::new();
|
||||
for p in type_alias_generics.predicates {
|
||||
|
||||
for p in generics.predicates {
|
||||
let span = p.span();
|
||||
if p.in_where_clause() {
|
||||
where_spans.push(span);
|
||||
|
|
@ -1460,37 +1475,57 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
|
|||
}
|
||||
}
|
||||
|
||||
let mut suggested_changing_assoc_types = false;
|
||||
if !where_spans.is_empty() {
|
||||
let sub = (!suggested_changing_assoc_types).then(|| {
|
||||
suggested_changing_assoc_types = true;
|
||||
SuggestChangingAssocTypes { ty: hir_ty }
|
||||
});
|
||||
let mut ty = Some(hir_ty);
|
||||
let enable_feat_help = cx.tcx.sess.is_nightly_build();
|
||||
|
||||
if let [.., label_sp] = *where_spans {
|
||||
cx.emit_span_lint(
|
||||
TYPE_ALIAS_BOUNDS,
|
||||
where_spans,
|
||||
BuiltinTypeAliasWhereClause {
|
||||
suggestion: type_alias_generics.where_clause_span,
|
||||
sub,
|
||||
BuiltinTypeAliasBounds {
|
||||
in_where_clause: true,
|
||||
label: label_sp,
|
||||
enable_feat_help,
|
||||
suggestions: vec![(generics.where_clause_span, String::new())],
|
||||
preds: generics.predicates,
|
||||
ty: ty.take(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if !inline_spans.is_empty() {
|
||||
let suggestion = BuiltinTypeAliasGenericBoundsSuggestion { suggestions: inline_sugg };
|
||||
let sub = (!suggested_changing_assoc_types).then(|| {
|
||||
suggested_changing_assoc_types = true;
|
||||
SuggestChangingAssocTypes { ty: hir_ty }
|
||||
});
|
||||
if let [.., label_sp] = *inline_spans {
|
||||
cx.emit_span_lint(
|
||||
TYPE_ALIAS_BOUNDS,
|
||||
inline_spans,
|
||||
BuiltinTypeAliasGenericBounds { suggestion, sub },
|
||||
BuiltinTypeAliasBounds {
|
||||
in_where_clause: false,
|
||||
label: label_sp,
|
||||
enable_feat_help,
|
||||
suggestions: inline_sugg,
|
||||
preds: generics.predicates,
|
||||
ty,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ShorthandAssocTyCollector {
|
||||
pub(crate) qselves: Vec<Span>,
|
||||
}
|
||||
|
||||
impl hir::intravisit::Visitor<'_> for ShorthandAssocTyCollector {
|
||||
fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, _: Span) {
|
||||
// Look for "type-parameter shorthand-associated-types". I.e., paths of the
|
||||
// form `T::Assoc` with `T` type param. These are reliant on trait bounds.
|
||||
if let hir::QPath::TypeRelative(qself, _) = qpath
|
||||
&& qself.as_generic_param().is_some()
|
||||
{
|
||||
self.qselves.push(qself.span);
|
||||
}
|
||||
hir::intravisit::walk_qpath(self, qpath, id)
|
||||
}
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `trivial_bounds` lint detects trait bounds that don't depend on
|
||||
/// any type parameters.
|
||||
|
|
|
|||
|
|
@ -2,14 +2,16 @@
|
|||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
use std::num::NonZero;
|
||||
|
||||
use crate::errors::RequestedLevel;
|
||||
use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds};
|
||||
use crate::errors::{OverruledAttributeSub, RequestedLevel};
|
||||
use crate::fluent_generated as fluent;
|
||||
use crate::LateContext;
|
||||
use rustc_errors::{
|
||||
codes::*, Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString,
|
||||
ElidedLifetimeInPathSubdiag, EmissionGuarantee, LintDiagnostic, MultiSpan, SubdiagMessageOp,
|
||||
Subdiagnostic, SuggestionStyle,
|
||||
};
|
||||
use rustc_hir::{def::Namespace, def_id::DefId};
|
||||
use rustc_hir::{self as hir, def::Namespace, def_id::DefId};
|
||||
use rustc_macros::{LintDiagnostic, Subdiagnostic};
|
||||
use rustc_middle::ty::{
|
||||
inhabitedness::InhabitedPredicate, Clause, PolyExistentialTraitRef, Ty, TyCtxt,
|
||||
|
|
@ -22,10 +24,6 @@ use rustc_span::{
|
|||
Span, Symbol,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
builtin::InitError, builtin::TypeAliasBounds, errors::OverruledAttributeSub, LateContext,
|
||||
};
|
||||
|
||||
// array_into_iter.rs
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_shadowed_into_iter)]
|
||||
|
|
@ -263,62 +261,6 @@ pub struct BuiltinUnreachablePub<'a> {
|
|||
pub help: Option<()>,
|
||||
}
|
||||
|
||||
pub struct SuggestChangingAssocTypes<'a, 'b> {
|
||||
pub ty: &'a rustc_hir::Ty<'b>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> Subdiagnostic for SuggestChangingAssocTypes<'a, 'b> {
|
||||
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
|
||||
self,
|
||||
diag: &mut Diag<'_, G>,
|
||||
_f: &F,
|
||||
) {
|
||||
// Access to associates types should use `<T as Bound>::Assoc`, which does not need a
|
||||
// bound. Let's see if this type does that.
|
||||
|
||||
// We use a HIR visitor to walk the type.
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
struct WalkAssocTypes<'a, 'b, G: EmissionGuarantee> {
|
||||
err: &'a mut Diag<'b, G>,
|
||||
}
|
||||
impl<'a, 'b, G: EmissionGuarantee> Visitor<'_> for WalkAssocTypes<'a, 'b, G> {
|
||||
fn visit_qpath(
|
||||
&mut self,
|
||||
qpath: &rustc_hir::QPath<'_>,
|
||||
id: rustc_hir::HirId,
|
||||
span: Span,
|
||||
) {
|
||||
if TypeAliasBounds::is_type_variable_assoc(qpath) {
|
||||
self.err.span_help(span, fluent::lint_builtin_type_alias_bounds_help);
|
||||
}
|
||||
intravisit::walk_qpath(self, qpath, id)
|
||||
}
|
||||
}
|
||||
|
||||
// Let's go for a walk!
|
||||
let mut visitor = WalkAssocTypes { err: diag };
|
||||
visitor.visit_ty(self.ty);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_builtin_type_alias_where_clause)]
|
||||
pub struct BuiltinTypeAliasWhereClause<'a, 'b> {
|
||||
#[suggestion(code = "", applicability = "machine-applicable")]
|
||||
pub suggestion: Span,
|
||||
#[subdiagnostic]
|
||||
pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_builtin_type_alias_generic_bounds)]
|
||||
pub struct BuiltinTypeAliasGenericBounds<'a, 'b> {
|
||||
#[subdiagnostic]
|
||||
pub suggestion: BuiltinTypeAliasGenericBoundsSuggestion,
|
||||
#[subdiagnostic]
|
||||
pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_macro_expr_fragment_specifier_2024_migration)]
|
||||
pub struct MacroExprFragment2024 {
|
||||
|
|
@ -326,21 +268,72 @@ pub struct MacroExprFragment2024 {
|
|||
pub suggestion: Span,
|
||||
}
|
||||
|
||||
pub struct BuiltinTypeAliasGenericBoundsSuggestion {
|
||||
pub struct BuiltinTypeAliasBounds<'a, 'hir> {
|
||||
pub in_where_clause: bool,
|
||||
pub label: Span,
|
||||
pub enable_feat_help: bool,
|
||||
pub suggestions: Vec<(Span, String)>,
|
||||
pub preds: &'hir [hir::WherePredicate<'hir>],
|
||||
pub ty: Option<&'a hir::Ty<'hir>>,
|
||||
}
|
||||
|
||||
impl Subdiagnostic for BuiltinTypeAliasGenericBoundsSuggestion {
|
||||
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
|
||||
self,
|
||||
diag: &mut Diag<'_, G>,
|
||||
_f: &F,
|
||||
) {
|
||||
diag.multipart_suggestion(
|
||||
fluent::lint_suggestion,
|
||||
self.suggestions,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_, '_> {
|
||||
fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
|
||||
diag.primary_message(if self.in_where_clause {
|
||||
fluent::lint_builtin_type_alias_bounds_where_clause
|
||||
} else {
|
||||
fluent::lint_builtin_type_alias_bounds_param_bounds
|
||||
});
|
||||
diag.span_label(self.label, fluent::lint_builtin_type_alias_bounds_label);
|
||||
diag.note(fluent::lint_builtin_type_alias_bounds_limitation_note);
|
||||
if self.enable_feat_help {
|
||||
diag.help(fluent::lint_builtin_type_alias_bounds_enable_feat_help);
|
||||
}
|
||||
|
||||
// We perform the walk in here instead of in `<TypeAliasBounds as LateLintPass>` to
|
||||
// avoid doing throwaway work in case the lint ends up getting suppressed.
|
||||
let mut collector = ShorthandAssocTyCollector { qselves: Vec::new() };
|
||||
if let Some(ty) = self.ty {
|
||||
hir::intravisit::Visitor::visit_ty(&mut collector, ty);
|
||||
}
|
||||
|
||||
let affect_object_lifetime_defaults = self
|
||||
.preds
|
||||
.iter()
|
||||
.filter(|pred| pred.in_where_clause() == self.in_where_clause)
|
||||
.any(|pred| TypeAliasBounds::affects_object_lifetime_defaults(pred));
|
||||
|
||||
// If there are any shorthand assoc tys, then the bounds can't be removed automatically.
|
||||
// The user first needs to fully qualify the assoc tys.
|
||||
let applicability = if !collector.qselves.is_empty() || affect_object_lifetime_defaults {
|
||||
Applicability::MaybeIncorrect
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
};
|
||||
|
||||
diag.arg("count", self.suggestions.len());
|
||||
diag.multipart_suggestion(fluent::lint_suggestion, self.suggestions, applicability);
|
||||
|
||||
// Suggest fully qualifying paths of the form `T::Assoc` with `T` type param via
|
||||
// `<T as /* Trait */>::Assoc` to remove their reliance on any type param bounds.
|
||||
//
|
||||
// Instead of attempting to figure out the necessary trait ref, just use a
|
||||
// placeholder. Since we don't record type-dependent resolutions for non-body
|
||||
// items like type aliases, we can't simply deduce the corresp. trait from
|
||||
// the HIR path alone without rerunning parts of HIR ty lowering here
|
||||
// (namely `probe_single_ty_param_bound_for_assoc_ty`) which is infeasible.
|
||||
//
|
||||
// (We could employ some simple heuristics but that's likely not worth it).
|
||||
for qself in collector.qselves {
|
||||
diag.multipart_suggestion(
|
||||
fluent::lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg,
|
||||
vec![
|
||||
(qself.shrink_to_lo(), "<".into()),
|
||||
(qself.shrink_to_hi(), " as /* Trait */>".into()),
|
||||
],
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -429,7 +429,7 @@ fn ty_has_local_parent(
|
|||
path_has_local_parent(ty_path, cx, impl_parent, impl_parent_parent)
|
||||
}
|
||||
TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => path_has_local_parent(
|
||||
principle_poly_trait_ref.trait_ref.path,
|
||||
principle_poly_trait_ref.0.trait_ref.path,
|
||||
cx,
|
||||
impl_parent,
|
||||
impl_parent_parent,
|
||||
|
|
@ -527,7 +527,7 @@ fn self_ty_kind_for_diagnostic(ty: &rustc_hir::Ty<'_>, tcx: TyCtxt<'_>) -> (Span
|
|||
.to_string(),
|
||||
),
|
||||
TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => {
|
||||
let path = &principle_poly_trait_ref.trait_ref.path;
|
||||
let path = &principle_poly_trait_ref.0.trait_ref.path;
|
||||
(
|
||||
path_span_without_args(path),
|
||||
path.res
|
||||
|
|
|
|||
|
|
@ -113,9 +113,11 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
|
|||
|
||||
fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) {
|
||||
let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { return };
|
||||
for bound in &bounds[..] {
|
||||
for (bound, modifier) in &bounds[..] {
|
||||
let def_id = bound.trait_ref.trait_def_id();
|
||||
if cx.tcx.lang_items().drop_trait() == def_id {
|
||||
if cx.tcx.lang_items().drop_trait() == def_id
|
||||
&& *modifier != hir::TraitBoundModifier::Maybe
|
||||
{
|
||||
let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { return };
|
||||
cx.emit_span_lint(DYN_DROP, bound.span, DropGlue { tcx: cx.tcx, def_id });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1864,9 +1864,9 @@ impl<'tcx> Ty<'tcx> {
|
|||
// Definitely absolutely not copy.
|
||||
ty::Ref(_, _, hir::Mutability::Mut) => false,
|
||||
|
||||
// Thin pointers & thin shared references are pure-clone-copy, but for
|
||||
// anything with custom metadata it might be more complicated.
|
||||
ty::Ref(_, _, hir::Mutability::Not) | ty::RawPtr(..) => false,
|
||||
// The standard library has a blanket Copy impl for shared references and raw pointers,
|
||||
// for all unsized types.
|
||||
ty::Ref(_, _, hir::Mutability::Not) | ty::RawPtr(..) => true,
|
||||
|
||||
ty::Coroutine(..) | ty::CoroutineWitness(..) => false,
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
use crate::simplify::simplify_duplicate_switch_targets;
|
||||
use crate::take_array;
|
||||
use rustc_ast::attr;
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::layout;
|
||||
|
|
@ -271,8 +272,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> {
|
|||
return;
|
||||
}
|
||||
|
||||
let trait_def_id = self.tcx.trait_of_item(fn_def_id);
|
||||
if trait_def_id.is_none() || trait_def_id != self.tcx.lang_items().clone_trait() {
|
||||
if !self.tcx.is_lang_item(fn_def_id, LangItem::CloneFn) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use std::iter;
|
|||
|
||||
use crate::{
|
||||
abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator,
|
||||
mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify,
|
||||
instsimplify, mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify,
|
||||
};
|
||||
use rustc_middle::mir::patch::MirPatch;
|
||||
use rustc_mir_dataflow::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle};
|
||||
|
|
@ -154,6 +154,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body<
|
|||
&deref_separator::Derefer,
|
||||
&remove_noop_landing_pads::RemoveNoopLandingPads,
|
||||
&simplify::SimplifyCfg::MakeShim,
|
||||
&instsimplify::InstSimplify,
|
||||
&abort_unwinding_calls::AbortUnwindingCalls,
|
||||
&add_call_guards::CriticalCallEdges,
|
||||
],
|
||||
|
|
@ -434,6 +435,9 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -
|
|||
match self_ty.kind() {
|
||||
ty::FnDef(..) | ty::FnPtr(_) => builder.copy_shim(),
|
||||
ty::Closure(_, args) => builder.tuple_like_shim(dest, src, args.as_closure().upvar_tys()),
|
||||
ty::CoroutineClosure(_, args) => {
|
||||
builder.tuple_like_shim(dest, src, args.as_coroutine_closure().upvar_tys())
|
||||
}
|
||||
ty::Tuple(..) => builder.tuple_like_shim(dest, src, self_ty.tuple_fields()),
|
||||
ty::Coroutine(coroutine_def_id, args) => {
|
||||
assert_eq!(tcx.coroutine_movability(*coroutine_def_id), hir::Movability::Movable);
|
||||
|
|
|
|||
|
|
@ -217,7 +217,10 @@ where
|
|||
// impl Copy/Clone for Closure where Self::TupledUpvars: Copy/Clone
|
||||
ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]),
|
||||
|
||||
ty::CoroutineClosure(..) => Err(NoSolution),
|
||||
// impl Copy/Clone for CoroutineClosure where Self::TupledUpvars: Copy/Clone
|
||||
ty::CoroutineClosure(_, args) => {
|
||||
Ok(vec![ty::Binder::dummy(args.as_coroutine_closure().tupled_upvars_ty())])
|
||||
}
|
||||
|
||||
// only when `coroutine_clone` is enabled and the coroutine is movable
|
||||
// impl Copy/Clone for Coroutine where T: Copy/Clone forall T in (upvars, witnesses)
|
||||
|
|
|
|||
|
|
@ -457,14 +457,3 @@ impl<'a> Parser<'a> {
|
|||
Err(self.dcx().create_err(err))
|
||||
}
|
||||
}
|
||||
|
||||
/// The attributes are complete if all attributes are either a doc comment or a
|
||||
/// builtin attribute other than `cfg_attr`.
|
||||
pub fn is_complete(attrs: &[ast::Attribute]) -> bool {
|
||||
attrs.iter().all(|attr| {
|
||||
attr.is_doc_comment()
|
||||
|| attr.ident().is_some_and(|ident| {
|
||||
ident.name != sym::cfg_attr && rustc_feature::is_builtin_attr_name(ident.name)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,10 +60,6 @@ impl AttrWrapper {
|
|||
pub fn is_empty(&self) -> bool {
|
||||
self.attrs.is_empty()
|
||||
}
|
||||
|
||||
pub fn is_complete(&self) -> bool {
|
||||
crate::parser::attr::is_complete(&self.attrs)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if `attrs` contains a `cfg` or `cfg_attr` attribute
|
||||
|
|
@ -114,17 +110,15 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
|
|||
replace_ranges.sort_by_key(|(range, _)| range.start);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
for [(range, tokens), (next_range, next_tokens)] in replace_ranges.array_windows() {
|
||||
assert!(
|
||||
range.end <= next_range.start || range.end >= next_range.end,
|
||||
"Replace ranges should either be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})",
|
||||
range,
|
||||
tokens,
|
||||
next_range,
|
||||
next_tokens,
|
||||
);
|
||||
}
|
||||
for [(range, tokens), (next_range, next_tokens)] in replace_ranges.array_windows() {
|
||||
assert!(
|
||||
range.end <= next_range.start || range.end >= next_range.end,
|
||||
"Replace ranges should either be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})",
|
||||
range,
|
||||
tokens,
|
||||
next_range,
|
||||
next_tokens,
|
||||
);
|
||||
}
|
||||
|
||||
// Process the replace ranges, starting from the highest start
|
||||
|
|
@ -137,9 +131,9 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
|
|||
// `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }`
|
||||
//
|
||||
// By starting processing from the replace range with the greatest
|
||||
// start position, we ensure that any replace range which encloses
|
||||
// another replace range will capture the *replaced* tokens for the inner
|
||||
// range, not the original tokens.
|
||||
// start position, we ensure that any (outer) replace range which
|
||||
// encloses another (inner) replace range will fully overwrite the
|
||||
// inner range's replacement.
|
||||
for (range, target) in replace_ranges.into_iter().rev() {
|
||||
assert!(!range.is_empty(), "Cannot replace an empty range: {range:?}");
|
||||
|
||||
|
|
@ -199,20 +193,20 @@ impl<'a> Parser<'a> {
|
|||
force_collect: ForceCollect,
|
||||
f: impl FnOnce(&mut Self, ast::AttrVec) -> PResult<'a, (R, bool)>,
|
||||
) -> PResult<'a, R> {
|
||||
// Skip collection when nothing could observe the collected tokens, i.e.
|
||||
// all of the following conditions hold.
|
||||
// - We are not force collecting tokens (because force collection
|
||||
// requires tokens by definition).
|
||||
if matches!(force_collect, ForceCollect::No)
|
||||
// - None of our outer attributes require tokens.
|
||||
&& attrs.is_complete()
|
||||
// - Our target doesn't support custom inner attributes (custom
|
||||
// We must collect if anything could observe the collected tokens, i.e.
|
||||
// if any of the following conditions hold.
|
||||
// - We are force collecting tokens (because force collection requires
|
||||
// tokens by definition).
|
||||
let needs_collection = matches!(force_collect, ForceCollect::Yes)
|
||||
// - Any of our outer attributes require tokens.
|
||||
|| needs_tokens(&attrs.attrs)
|
||||
// - Our target supports custom inner attributes (custom
|
||||
// inner attribute invocation might require token capturing).
|
||||
&& !R::SUPPORTS_CUSTOM_INNER_ATTRS
|
||||
// - We are not in `capture_cfg` mode (which requires tokens if
|
||||
|| R::SUPPORTS_CUSTOM_INNER_ATTRS
|
||||
// - We are in `capture_cfg` mode (which requires tokens if
|
||||
// the parsed node has `#[cfg]` or `#[cfg_attr]` attributes).
|
||||
&& !self.capture_cfg
|
||||
{
|
||||
|| self.capture_cfg;
|
||||
if !needs_collection {
|
||||
return Ok(f(self, attrs.attrs)?.0);
|
||||
}
|
||||
|
||||
|
|
@ -250,28 +244,28 @@ impl<'a> Parser<'a> {
|
|||
return Ok(ret);
|
||||
}
|
||||
|
||||
// This is similar to the "skip collection" check at the start of this
|
||||
// function, but now that we've parsed an AST node we have more
|
||||
// This is similar to the `needs_collection` check at the start of this
|
||||
// function, but now that we've parsed an AST node we have complete
|
||||
// information available. (If we return early here that means the
|
||||
// setup, such as cloning the token cursor, was unnecessary. That's
|
||||
// hard to avoid.)
|
||||
//
|
||||
// Skip collection when nothing could observe the collected tokens, i.e.
|
||||
// all of the following conditions hold.
|
||||
// - We are not force collecting tokens.
|
||||
if matches!(force_collect, ForceCollect::No)
|
||||
// - None of our outer *or* inner attributes require tokens.
|
||||
// (`attrs` was just outer attributes, but `ret.attrs()` is outer
|
||||
// and inner attributes. That makes this check more precise than
|
||||
// `attrs.is_complete()` at the start of the function, and we can
|
||||
// skip the subsequent check on `R::SUPPORTS_CUSTOM_INNER_ATTRS`.
|
||||
&& crate::parser::attr::is_complete(ret.attrs())
|
||||
// - We are not in `capture_cfg` mode, or we are but there are no
|
||||
// `#[cfg]` or `#[cfg_attr]` attributes. (During normal
|
||||
// non-`capture_cfg` parsing, we don't need any special capturing
|
||||
// for those attributes, because they're builtin.)
|
||||
&& (!self.capture_cfg || !has_cfg_or_cfg_attr(ret.attrs()))
|
||||
{
|
||||
// We must collect if anything could observe the collected tokens, i.e.
|
||||
// if any of the following conditions hold.
|
||||
// - We are force collecting tokens.
|
||||
let needs_collection = matches!(force_collect, ForceCollect::Yes)
|
||||
// - Any of our outer *or* inner attributes require tokens.
|
||||
// (`attr.attrs` was just outer attributes, but `ret.attrs()` is
|
||||
// outer and inner attributes. So this check is more precise than
|
||||
// the earlier `needs_tokens` check, and we don't need to
|
||||
// check `R::SUPPORTS_CUSTOM_INNER_ATTRS`.)
|
||||
|| needs_tokens(ret.attrs())
|
||||
// - We are in `capture_cfg` mode and there are `#[cfg]` or
|
||||
// `#[cfg_attr]` attributes. (During normal non-`capture_cfg`
|
||||
// parsing, we don't need any special capturing for those
|
||||
// attributes, because they're builtin.)
|
||||
|| (self.capture_cfg && has_cfg_or_cfg_attr(ret.attrs()));
|
||||
if !needs_collection {
|
||||
return Ok(ret);
|
||||
}
|
||||
|
||||
|
|
@ -297,11 +291,13 @@ impl<'a> Parser<'a> {
|
|||
// with `None`, which means the relevant tokens will be removed. (More
|
||||
// details below.)
|
||||
let mut inner_attr_replace_ranges = Vec::new();
|
||||
for inner_attr in ret.attrs().iter().filter(|a| a.style == ast::AttrStyle::Inner) {
|
||||
if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&inner_attr.id) {
|
||||
inner_attr_replace_ranges.push((attr_range, None));
|
||||
} else {
|
||||
self.dcx().span_delayed_bug(inner_attr.span, "Missing token range for attribute");
|
||||
for attr in ret.attrs() {
|
||||
if attr.style == ast::AttrStyle::Inner {
|
||||
if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&attr.id) {
|
||||
inner_attr_replace_ranges.push((attr_range, None));
|
||||
} else {
|
||||
self.dcx().span_delayed_bug(attr.span, "Missing token range for attribute");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -337,8 +333,7 @@ impl<'a> Parser<'a> {
|
|||
// When parsing `m`:
|
||||
// - `start_pos..end_pos` is `0..34` (`mod m`, excluding the `#[cfg_eval]` attribute).
|
||||
// - `inner_attr_replace_ranges` is empty.
|
||||
// - `replace_range_start..replace_ranges_end` has two entries.
|
||||
// - One to delete the inner attribute (`17..27`), obtained when parsing `g` (see above).
|
||||
// - `replace_range_start..replace_ranges_end` has one entry.
|
||||
// - One `AttrsTarget` (added below when parsing `g`) to replace all of `g` (`3..33`,
|
||||
// including its outer attribute), with:
|
||||
// - `attrs`: includes the outer and the inner attr.
|
||||
|
|
@ -369,12 +364,10 @@ impl<'a> Parser<'a> {
|
|||
|
||||
// What is the status here when parsing the example code at the top of this method?
|
||||
//
|
||||
// When parsing `g`, we add two entries:
|
||||
// When parsing `g`, we add one entry:
|
||||
// - The `start_pos..end_pos` (`3..33`) entry has a new `AttrsTarget` with:
|
||||
// - `attrs`: includes the outer and the inner attr.
|
||||
// - `tokens`: lazy tokens for `g` (with its inner attr deleted).
|
||||
// - `inner_attr_replace_ranges` contains the one entry to delete the inner attr's
|
||||
// tokens (`17..27`).
|
||||
//
|
||||
// When parsing `m`, we do nothing here.
|
||||
|
||||
|
|
@ -384,7 +377,6 @@ impl<'a> Parser<'a> {
|
|||
let start_pos = if has_outer_attrs { attrs.start_pos } else { start_pos };
|
||||
let target = AttrsTarget { attrs: ret.attrs().iter().cloned().collect(), tokens };
|
||||
self.capture_state.replace_ranges.push((start_pos..end_pos, Some(target)));
|
||||
self.capture_state.replace_ranges.extend(inner_attr_replace_ranges);
|
||||
} else if matches!(self.capture_state.capturing, Capturing::No) {
|
||||
// Only clear the ranges once we've finished capturing entirely, i.e. we've finished
|
||||
// the outermost call to this method.
|
||||
|
|
@ -461,6 +453,19 @@ fn make_attr_token_stream(
|
|||
AttrTokenStream::new(stack_top.inner)
|
||||
}
|
||||
|
||||
/// Tokens are needed if:
|
||||
/// - any non-single-segment attributes (other than doc comments) are present; or
|
||||
/// - any `cfg_attr` attributes are present;
|
||||
/// - any single-segment, non-builtin attributes are present.
|
||||
fn needs_tokens(attrs: &[ast::Attribute]) -> bool {
|
||||
attrs.iter().any(|attr| match attr.ident() {
|
||||
None => !attr.is_doc_comment(),
|
||||
Some(ident) => {
|
||||
ident.name == sym::cfg_attr || !rustc_feature::is_builtin_attr_name(ident.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Some types are used a lot. Make sure they don't unintentionally get bigger.
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
mod size_asserts {
|
||||
|
|
|
|||
|
|
@ -2483,12 +2483,15 @@ impl<'a> Parser<'a> {
|
|||
/// `check_pub` adds additional `pub` to the checks in case users place it
|
||||
/// wrongly, can be used to ensure `pub` never comes after `default`.
|
||||
pub(super) fn check_fn_front_matter(&mut self, check_pub: bool, case: Case) -> bool {
|
||||
const ALL_QUALS: &[Symbol] =
|
||||
&[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern];
|
||||
|
||||
// We use an over-approximation here.
|
||||
// `const const`, `fn const` won't parse, but we're not stepping over other syntax either.
|
||||
// `pub` is added in case users got confused with the ordering like `async pub fn`,
|
||||
// only if it wasn't preceded by `default` as `default pub` is invalid.
|
||||
let quals: &[Symbol] = if check_pub {
|
||||
&[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern]
|
||||
ALL_QUALS
|
||||
} else {
|
||||
&[kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern]
|
||||
};
|
||||
|
|
@ -2518,9 +2521,9 @@ impl<'a> Parser<'a> {
|
|||
|| self.check_keyword_case(kw::Extern, case)
|
||||
&& self.look_ahead(1, |t| t.can_begin_string_literal())
|
||||
&& (self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, case)) ||
|
||||
// this branch is only for better diagnostic in later, `pub` is not allowed here
|
||||
// this branch is only for better diagnostics; `pub`, `unsafe`, etc. are not allowed here
|
||||
(self.may_recover()
|
||||
&& self.look_ahead(2, |t| t.is_keyword(kw::Pub))
|
||||
&& self.look_ahead(2, |t| ALL_QUALS.iter().any(|&kw| t.is_keyword(kw)))
|
||||
&& self.look_ahead(3, |t| t.is_keyword_case(kw::Fn, case))))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,24 @@ impl Publicness {
|
|||
}
|
||||
}
|
||||
|
||||
fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool {
|
||||
fn adt_of<'tcx>(ty: &hir::Ty<'tcx>) -> Option<(LocalDefId, DefKind)> {
|
||||
match ty.kind {
|
||||
TyKind::Path(hir::QPath::Resolved(_, path)) => {
|
||||
if let Res::Def(def_kind, def_id) = path.res
|
||||
&& let Some(local_def_id) = def_id.as_local()
|
||||
{
|
||||
Some((local_def_id, def_kind))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
TyKind::Slice(ty) | TyKind::Array(ty, _) => adt_of(ty),
|
||||
TyKind::Ptr(ty) | TyKind::Ref(_, ty) => adt_of(ty.ty),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: LocalDefId) -> bool {
|
||||
// treat PhantomData and positional ZST as public,
|
||||
// we don't want to lint types which only have them,
|
||||
// cause it's a common way to use such types to check things like well-formedness
|
||||
|
|
@ -79,10 +96,7 @@ fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool {
|
|||
/// for enum and union, just check they are public,
|
||||
/// and doesn't solve types like &T for now, just skip them
|
||||
fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> Publicness {
|
||||
if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
|
||||
&& let Res::Def(def_kind, def_id) = path.res
|
||||
&& def_id.is_local()
|
||||
{
|
||||
if let Some((def_id, def_kind)) = adt_of(ty) {
|
||||
return match def_kind {
|
||||
DefKind::Enum | DefKind::Union => {
|
||||
let ty_is_public = tcx.visibility(def_id).is_public();
|
||||
|
|
@ -565,10 +579,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
|||
}
|
||||
|
||||
fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId, impl_item_id: LocalDefId) -> bool {
|
||||
if let TyKind::Path(hir::QPath::Resolved(_, path)) =
|
||||
self.tcx.hir().item(impl_id).expect_impl().self_ty.kind
|
||||
&& let Res::Def(def_kind, def_id) = path.res
|
||||
&& let Some(local_def_id) = def_id.as_local()
|
||||
if let Some((local_def_id, def_kind)) =
|
||||
adt_of(self.tcx.hir().item(impl_id).expect_impl().self_ty)
|
||||
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
|
||||
{
|
||||
if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id
|
||||
|
|
@ -915,7 +927,7 @@ fn create_and_seed_worklist(
|
|||
match tcx.def_kind(id) {
|
||||
DefKind::Impl { .. } => false,
|
||||
DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer),
|
||||
DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(),
|
||||
DefKind::Struct => struct_all_fields_are_public(tcx, id) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(),
|
||||
_ => true
|
||||
})
|
||||
.map(|id| (id, ComesFromAllowExpect::No))
|
||||
|
|
|
|||
|
|
@ -1448,7 +1448,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
);
|
||||
|
||||
if macro_kind == MacroKind::Bang && ident.name == sym::macro_rules {
|
||||
err.subdiagnostic(MaybeMissingMacroRulesName { span: ident.span });
|
||||
let label_span = ident.span.shrink_to_hi();
|
||||
let mut spans = MultiSpan::from_span(label_span);
|
||||
spans.push_span_label(label_span, "put a macro name here");
|
||||
err.subdiagnostic(MaybeMissingMacroRulesName { spans: spans });
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -669,7 +669,7 @@ pub(crate) struct MacroSuggMovePosition {
|
|||
#[note(resolve_missing_macro_rules_name)]
|
||||
pub(crate) struct MaybeMissingMacroRulesName {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
pub(crate) spans: MultiSpan,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
|
|
|
|||
|
|
@ -557,6 +557,7 @@ symbols! {
|
|||
clobber_abi,
|
||||
clone,
|
||||
clone_closures,
|
||||
clone_fn,
|
||||
clone_from,
|
||||
closure,
|
||||
closure_lifetime_binder,
|
||||
|
|
@ -1229,6 +1230,7 @@ symbols! {
|
|||
modifiers,
|
||||
module,
|
||||
module_path,
|
||||
more_maybe_bounds,
|
||||
more_qualified_paths,
|
||||
more_struct_aliases,
|
||||
movbe_target_feature,
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
|
|||
}
|
||||
|
||||
hir::TyKind::TraitObject(bounds, ..) => {
|
||||
for bound in bounds {
|
||||
for (bound, _) in bounds {
|
||||
self.current_index.shift_in(1);
|
||||
self.visit_poly_trait_ref(bound);
|
||||
self.current_index.shift_out(1);
|
||||
|
|
|
|||
|
|
@ -607,7 +607,7 @@ impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> {
|
|||
_,
|
||||
) = t.kind
|
||||
{
|
||||
for ptr in poly_trait_refs {
|
||||
for (ptr, _) in poly_trait_refs {
|
||||
if Some(self.1) == ptr.trait_ref.trait_def_id() {
|
||||
self.0.push(ptr.span);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -437,7 +437,7 @@ pub fn report_object_safety_error<'tcx>(
|
|||
if tcx.parent_hir_node(hir_id).fn_sig().is_some() {
|
||||
// Do not suggest `impl Trait` when dealing with things like super-traits.
|
||||
err.span_suggestion_verbose(
|
||||
ty.span.until(trait_ref.span),
|
||||
ty.span.until(trait_ref.0.span),
|
||||
"consider using an opaque type instead",
|
||||
"impl ",
|
||||
Applicability::MaybeIncorrect,
|
||||
|
|
|
|||
|
|
@ -3040,11 +3040,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
match ty.kind {
|
||||
hir::TyKind::TraitObject(traits, _, _) => {
|
||||
let (span, kw) = match traits {
|
||||
[first, ..] if first.span.lo() == ty.span.lo() => {
|
||||
[(first, _), ..] if first.span.lo() == ty.span.lo() => {
|
||||
// Missing `dyn` in front of trait object.
|
||||
(ty.span.shrink_to_lo(), "dyn ")
|
||||
}
|
||||
[first, ..] => (ty.span.until(first.span), ""),
|
||||
[(first, _), ..] => (ty.span.until(first.span), ""),
|
||||
[] => span_bug!(ty.span, "trait object with no traits: {ty:?}"),
|
||||
};
|
||||
let needs_parens = traits.len() != 1;
|
||||
|
|
|
|||
|
|
@ -2262,8 +2262,21 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME(async_closures): These are never clone, for now.
|
||||
ty::CoroutineClosure(_, _) => None,
|
||||
ty::CoroutineClosure(_, args) => {
|
||||
// (*) binder moved here
|
||||
let ty = self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty());
|
||||
if let ty::Infer(ty::TyVar(_)) = ty.kind() {
|
||||
// Not yet resolved.
|
||||
Ambiguous
|
||||
} else {
|
||||
Where(
|
||||
obligation
|
||||
.predicate
|
||||
.rebind(args.as_coroutine_closure().upvar_tys().to_vec()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// `Copy` and `Clone` are automatically implemented for an anonymous adt
|
||||
// if all of its fields are `Copy` and `Clone`
|
||||
ty::Adt(adt, args) if adt.is_anonymous() => {
|
||||
|
|
|
|||
|
|
@ -241,10 +241,6 @@ pub trait TypeVisitableExt<I: Interner>: TypeVisitable<I> {
|
|||
self.has_type_flags(TypeFlags::HAS_ALIAS)
|
||||
}
|
||||
|
||||
fn has_inherent_projections(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_TY_INHERENT)
|
||||
}
|
||||
|
||||
fn has_opaque_types(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_TY_OPAQUE)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -395,7 +395,7 @@
|
|||
#metrics = false
|
||||
|
||||
# Specify the location of the Android NDK. Used when targeting Android.
|
||||
#android-ndk = "/path/to/android-ndk-r25b"
|
||||
#android-ndk = "/path/to/android-ndk-r26d"
|
||||
|
||||
# =============================================================================
|
||||
# General install configuration options
|
||||
|
|
|
|||
|
|
@ -160,6 +160,9 @@ pub trait Clone: Sized {
|
|||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[must_use = "cloning is often expensive and is not expected to have side effects"]
|
||||
// Clone::clone is special because the compiler generates MIR to implement it for some types.
|
||||
// See InstanceKind::CloneShim.
|
||||
#[cfg_attr(not(bootstrap), lang = "clone_fn")]
|
||||
fn clone(&self) -> Self;
|
||||
|
||||
/// Performs copy-assignment from `source`.
|
||||
|
|
|
|||
|
|
@ -402,6 +402,7 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A helper used to print list-like items with no special formatting.
|
||||
struct DebugInner<'a, 'b: 'a> {
|
||||
fmt: &'a mut fmt::Formatter<'b>,
|
||||
result: fmt::Result,
|
||||
|
|
@ -578,7 +579,8 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> {
|
|||
/// ```
|
||||
#[stable(feature = "debug_builders", since = "1.2.0")]
|
||||
pub fn finish(&mut self) -> fmt::Result {
|
||||
self.inner.result.and_then(|_| self.inner.fmt.write_str("}"))
|
||||
self.inner.result = self.inner.result.and_then(|_| self.inner.fmt.write_str("}"));
|
||||
self.inner.result
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -721,7 +723,8 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> {
|
|||
/// ```
|
||||
#[stable(feature = "debug_builders", since = "1.2.0")]
|
||||
pub fn finish(&mut self) -> fmt::Result {
|
||||
self.inner.result.and_then(|_| self.inner.fmt.write_str("]"))
|
||||
self.inner.result = self.inner.result.and_then(|_| self.inner.fmt.write_str("]"));
|
||||
self.inner.result
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1002,11 +1005,12 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
|
|||
/// ```
|
||||
#[stable(feature = "debug_builders", since = "1.2.0")]
|
||||
pub fn finish(&mut self) -> fmt::Result {
|
||||
self.result.and_then(|_| {
|
||||
self.result = self.result.and_then(|_| {
|
||||
assert!(!self.has_key, "attempted to finish a map with a partial entry");
|
||||
|
||||
self.fmt.write_str("}")
|
||||
})
|
||||
});
|
||||
self.result
|
||||
}
|
||||
|
||||
fn is_pretty(&self) -> bool {
|
||||
|
|
|
|||
|
|
@ -823,7 +823,7 @@ pub trait Iterator {
|
|||
///
|
||||
/// Given an element the closure must return `true` or `false`. The returned
|
||||
/// iterator will yield only the elements for which the closure returns
|
||||
/// true.
|
||||
/// `true`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
|
|||
|
|
@ -127,7 +127,6 @@
|
|||
#![feature(const_hash)]
|
||||
#![feature(const_heap)]
|
||||
#![feature(const_index_range_slice_index)]
|
||||
#![feature(const_int_from_str)]
|
||||
#![feature(const_intrinsic_copy)]
|
||||
#![feature(const_intrinsic_forget)]
|
||||
#![feature(const_ipv4)]
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ pub enum IntErrorKind {
|
|||
impl ParseIntError {
|
||||
/// Outputs the detailed cause of parsing an integer failing.
|
||||
#[must_use]
|
||||
#[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")]
|
||||
#[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[stable(feature = "int_error_matching", since = "1.55.0")]
|
||||
pub const fn kind(&self) -> &IntErrorKind {
|
||||
&self.kind
|
||||
|
|
|
|||
|
|
@ -1386,6 +1386,7 @@ from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 }
|
|||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
#[unstable(issue = "none", feature = "std_internals")]
|
||||
#[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub const fn can_not_overflow<T>(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool {
|
||||
radix <= 16 && digits.len() <= mem::size_of::<T>() * 2 - is_signed_ty as usize
|
||||
}
|
||||
|
|
@ -1435,7 +1436,7 @@ macro_rules! from_str_radix {
|
|||
#[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str_radix(\"A\", 16), Ok(10));")]
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")]
|
||||
#[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> {
|
||||
use self::IntErrorKind::*;
|
||||
use self::ParseIntError as PIE;
|
||||
|
|
@ -1565,7 +1566,7 @@ macro_rules! from_str_radix_size_impl {
|
|||
#[doc = concat!("assert_eq!(", stringify!($size), "::from_str_radix(\"A\", 16), Ok(10));")]
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")]
|
||||
#[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub const fn from_str_radix(src: &str, radix: u32) -> Result<$size, ParseIntError> {
|
||||
match <$t>::from_str_radix(src, radix) {
|
||||
Ok(x) => Ok(x as $size),
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
#![feature(const_hash)]
|
||||
#![feature(const_heap)]
|
||||
#![feature(const_intrinsic_copy)]
|
||||
#![feature(const_int_from_str)]
|
||||
#![feature(const_maybe_uninit_as_mut_ptr)]
|
||||
#![feature(const_nonnull_new)]
|
||||
#![feature(const_pointer_is_aligned)]
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ trait Copy {}
|
|||
#[lang = "freeze"]
|
||||
auto trait Freeze {}
|
||||
|
||||
impl<T: ?Sized> Copy for *mut T {}
|
||||
|
||||
#[lang = "drop_in_place"]
|
||||
#[inline]
|
||||
#[allow(unconditional_recursion)]
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ trait Copy {}
|
|||
#[lang = "freeze"]
|
||||
auto trait Freeze {}
|
||||
|
||||
impl<T: ?Sized> Copy for *mut T {}
|
||||
|
||||
#[lang = "drop_in_place"]
|
||||
#[inline]
|
||||
#[allow(unconditional_recursion)]
|
||||
|
|
|
|||
|
|
@ -247,10 +247,8 @@ pub(crate) fn ndk_compiler(compiler: Language, triple: &str, ndk: &Path) -> Path
|
|||
triple.to_string()
|
||||
};
|
||||
|
||||
// API 19 is the earliest API level supported by NDK r25b but AArch64 and x86_64 support
|
||||
// begins at API level 21.
|
||||
let api_level =
|
||||
if triple.contains("aarch64") || triple.contains("x86_64") { "21" } else { "19" };
|
||||
// The earliest API supported by NDK r26d is 21.
|
||||
let api_level = "21";
|
||||
let compiler = format!("{}{}-{}", triple_translated, api_level, compiler.clang());
|
||||
let host_tag = if cfg!(target_os = "macos") {
|
||||
// The NDK uses universal binaries, so this is correct even on ARM.
|
||||
|
|
@ -258,7 +256,7 @@ pub(crate) fn ndk_compiler(compiler: Language, triple: &str, ndk: &Path) -> Path
|
|||
} else if cfg!(target_os = "windows") {
|
||||
"windows-x86_64"
|
||||
} else {
|
||||
// NDK r25b only has official releases for macOS, Windows and Linux.
|
||||
// NDK r26d only has official releases for macOS, Windows and Linux.
|
||||
// Try the Linux directory everywhere else, on the assumption that the OS has an
|
||||
// emulation layer that can cope (e.g. BSDs).
|
||||
"linux-x86_64"
|
||||
|
|
|
|||
|
|
@ -210,4 +210,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
|
|||
severity: ChangeSeverity::Info,
|
||||
summary: "the `wasm-component-ld` tool is now built as part of `build.extended` and can be a member of `build.tools`",
|
||||
},
|
||||
ChangeInfo {
|
||||
change_id: 120593,
|
||||
severity: ChangeSeverity::Info,
|
||||
summary: "Removed android-ndk r25b support in favor of android-ndk r26d.",
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ RUN sh /scripts/android-base-apt-get.sh
|
|||
|
||||
COPY scripts/android-ndk.sh /scripts/
|
||||
RUN . /scripts/android-ndk.sh && \
|
||||
download_ndk android-ndk-r25b-linux.zip
|
||||
download_ndk android-ndk-r26d-linux.zip
|
||||
|
||||
RUN dpkg --add-architecture i386 && \
|
||||
apt-get update && \
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
emulator emulator-linux-5264690.zip 48c1cda2bdf3095d9d9d5c010fbfb3d6d673e3ea
|
||||
patcher;v4 3534162-studio.sdk-patcher.zip 046699c5e2716ae11d77e0bad814f7f33fab261e
|
||||
platform-tools platform-tools_r28.0.2-linux.zip 46a4c02a9b8e4e2121eddf6025da3c979bf02e28
|
||||
platforms;android-18 android-18_r03.zip e6b09b3505754cbbeb4a5622008b907262ee91cb
|
||||
system-images;android-18;default;armeabi-v7a sys-img/android/armeabi-v7a-18_r05.zip 580b583720f7de671040d5917c8c9db0c7aa03fd
|
||||
platform-tools platform-tools_r34.0.5-linux.zip 96097475cf7b279fdd8f218f5d043ffe94104ec3
|
||||
platforms;android-21 android-21_r02.zip 53536556059bb29ae82f414fd2e14bc335a4eb4c
|
||||
system-images;android-21;default;armeabi-v7a sys-img/android/armeabi-v7a-21_r04.zip 8c606f81306564b65e41303d2603e4c42ded0d10
|
||||
tools sdk-tools-linux-4333796.zip 8c7c28554a32318461802c1291d76fccfafde054
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ RUN sh /scripts/android-base-apt-get.sh
|
|||
# ndk
|
||||
COPY scripts/android-ndk.sh /scripts/
|
||||
RUN . /scripts/android-ndk.sh && \
|
||||
download_ndk android-ndk-r25b-linux.zip
|
||||
download_ndk android-ndk-r26d-linux.zip
|
||||
|
||||
# env
|
||||
ENV TARGETS=arm-linux-androideabi
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export SHELL=/bin/bash
|
|||
# the emulator date is set to unix epoch (in armeabi-v7a-18 image). Using
|
||||
# classic engine the emulator starts with the current date and the tests run
|
||||
# fine. If another image is used, this need to be evaluated again.
|
||||
nohup nohup emulator @armeabi-v7a-18 \
|
||||
nohup nohup emulator @armeabi-v7a-21 \
|
||||
-engine classic -no-window -partition-size 2047 0<&- &>/dev/null &
|
||||
|
||||
exec "$@"
|
||||
|
|
|
|||
|
|
@ -97,9 +97,15 @@ def find_run_type(ctx: GitHubCtx) -> Optional[WorkflowRunType]:
|
|||
"refs/heads/automation/bors/try"
|
||||
)
|
||||
|
||||
# Unrolled branch from a rollup for testing perf
|
||||
# This should **not** allow custom try jobs
|
||||
is_unrolled_perf_build = ctx.ref == "refs/heads/try-perf"
|
||||
|
||||
if try_build:
|
||||
jobs = get_custom_jobs(ctx)
|
||||
return TryRunType(custom_jobs=jobs)
|
||||
custom_jobs = []
|
||||
if not is_unrolled_perf_build:
|
||||
custom_jobs = get_custom_jobs(ctx)
|
||||
return TryRunType(custom_jobs=custom_jobs)
|
||||
|
||||
if ctx.ref == "refs/heads/auto":
|
||||
return AutoRunType()
|
||||
|
|
|
|||
|
|
@ -1858,7 +1858,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
|
|||
}
|
||||
TyKind::Path(_) => clean_qpath(ty, cx),
|
||||
TyKind::TraitObject(bounds, ref lifetime, _) => {
|
||||
let bounds = bounds.iter().map(|bound| clean_poly_trait_ref(bound, cx)).collect();
|
||||
let bounds = bounds.iter().map(|(bound, _)| clean_poly_trait_ref(bound, cx)).collect();
|
||||
let lifetime =
|
||||
if !lifetime.is_elided() { Some(clean_lifetime(*lifetime, cx)) } else { None };
|
||||
DynTrait(bounds, lifetime)
|
||||
|
|
|
|||
|
|
@ -2932,7 +2932,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
|
|||
}
|
||||
|
||||
// Update document title to maintain a meaningful browser history
|
||||
searchState.title = "Results for " + query.original + " - Rust";
|
||||
searchState.title = "\"" + query.original + "\" Search - Rust";
|
||||
|
||||
// Because searching is incremental by character, only the most
|
||||
// recent search query is added to the browser history.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ uibless = "test --test compile-test -- -- --bless"
|
|||
bless = "test -- -- --bless"
|
||||
dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
|
||||
lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- "
|
||||
collect-metadata = "test --test dogfood --features internal -- run_metadata_collection_lint --ignored"
|
||||
collect-metadata = "test --test dogfood --features internal -- collect_metadata"
|
||||
|
||||
[build]
|
||||
# -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests
|
||||
|
|
|
|||
28
src/tools/clippy/.github/workflows/lintcheck.yml
vendored
28
src/tools/clippy/.github/workflows/lintcheck.yml
vendored
|
|
@ -53,18 +53,18 @@ jobs:
|
|||
id: cache-json
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: lintcheck-logs/lintcheck_crates_logs.json
|
||||
path: lintcheck-logs/ci_crates_logs.json
|
||||
key: ${{ steps.key.outputs.key }}
|
||||
|
||||
- name: Run lintcheck
|
||||
if: steps.cache-json.outputs.cache-hit != 'true'
|
||||
run: ./target/debug/lintcheck --format json --warn-all
|
||||
run: ./target/debug/lintcheck --format json --warn-all --crates-toml ./lintcheck/ci_crates.toml
|
||||
|
||||
- name: Upload base JSON
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: base
|
||||
path: lintcheck-logs/lintcheck_crates_logs.json
|
||||
path: lintcheck-logs/ci_crates_logs.json
|
||||
|
||||
# Runs lintcheck on the PR and stores the results as an artifact
|
||||
head:
|
||||
|
|
@ -86,13 +86,13 @@ jobs:
|
|||
run: cargo build --manifest-path=lintcheck/Cargo.toml
|
||||
|
||||
- name: Run lintcheck
|
||||
run: ./target/debug/lintcheck --format json --warn-all
|
||||
run: ./target/debug/lintcheck --format json --warn-all --crates-toml ./lintcheck/ci_crates.toml
|
||||
|
||||
- name: Upload head JSON
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: head
|
||||
path: lintcheck-logs/lintcheck_crates_logs.json
|
||||
path: lintcheck-logs/ci_crates_logs.json
|
||||
|
||||
# Retrieves the head and base JSON results and prints the diff to the GH actions step summary
|
||||
diff:
|
||||
|
|
@ -115,4 +115,20 @@ jobs:
|
|||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Diff results
|
||||
run: ./target/debug/lintcheck diff {base,head}/lintcheck_crates_logs.json >> $GITHUB_STEP_SUMMARY
|
||||
# GH's summery has a maximum size of 1024k:
|
||||
# https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary
|
||||
# That's why we first log to file and then to the summary and logs
|
||||
run: |
|
||||
./target/debug/lintcheck diff {base,head}/ci_crates_logs.json --truncate >> truncated_diff.md
|
||||
head -c 1024000 truncated_diff.md >> $GITHUB_STEP_SUMMARY
|
||||
cat truncated_diff.md
|
||||
./target/debug/lintcheck diff {base,head}/ci_crates_logs.json >> full_diff.md
|
||||
|
||||
- name: Upload full diff
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: diff
|
||||
if-no-files-found: ignore
|
||||
path: |
|
||||
full_diff.md
|
||||
truncated_diff.md
|
||||
|
|
|
|||
|
|
@ -6,11 +6,53 @@ document.
|
|||
|
||||
## Unreleased / Beta / In Rust Nightly
|
||||
|
||||
[ca3b3937...master](https://github.com/rust-lang/rust-clippy/compare/ca3b3937...master)
|
||||
[c9139bd5...master](https://github.com/rust-lang/rust-clippy/compare/c9139bd5...master)
|
||||
|
||||
## Rust 1.80
|
||||
|
||||
Current stable, released 2024-07-25
|
||||
|
||||
[View all 68 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-04-18T22%3A50%3A22Z..2024-05-30T08%3A26%3A18Z+base%3Amaster)
|
||||
|
||||
### New Lints
|
||||
|
||||
* Added [`while_float`] to `nursery`
|
||||
[#12765](https://github.com/rust-lang/rust-clippy/pull/12765)
|
||||
* Added [`macro_metavars_in_unsafe`] to `suspicious`
|
||||
[#12107](https://github.com/rust-lang/rust-clippy/pull/12107)
|
||||
* Added [`renamed_function_params`] to `restriction`
|
||||
[#11540](https://github.com/rust-lang/rust-clippy/pull/11540)
|
||||
* Added [`doc_lazy_continuation`] to `style`
|
||||
[#12770](https://github.com/rust-lang/rust-clippy/pull/12770)
|
||||
|
||||
### Moves and Deprecations
|
||||
|
||||
* Moved [`assigning_clones`] to `pedantic` (From `perf` now allow-by-default)
|
||||
[#12779](https://github.com/rust-lang/rust-clippy/pull/12779)
|
||||
* Moved [`single_char_pattern`] to `pedantic` (From `perf` now allow-by-default)
|
||||
[#11852](https://github.com/rust-lang/rust-clippy/pull/11852)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [`panic`]: Added [`allow-panic-in-tests`] configuration to allow the lint in tests
|
||||
[#12803](https://github.com/rust-lang/rust-clippy/pull/12803)
|
||||
* [`missing_const_for_fn`]: Now respects the [`msrv`] configuration
|
||||
[#12713](https://github.com/rust-lang/rust-clippy/pull/12713)
|
||||
* [`missing_panics_doc`]: No longer lints on compile-time panics
|
||||
[#12790](https://github.com/rust-lang/rust-clippy/pull/12790)
|
||||
* [`collapsible_match`]: Now considers the [`msrv`] configuration for the suggestion
|
||||
[#12745](https://github.com/rust-lang/rust-clippy/pull/12745)
|
||||
* [`useless_vec`]: Added [`allow-useless-vec-in-tests`] configuration to allow the lint in tests
|
||||
[#12725](https://github.com/rust-lang/rust-clippy/pull/12725)
|
||||
|
||||
### Suggestion Fixes/Improvements
|
||||
|
||||
* [`single_match`], [`single_match_else`]: Suggestions are now machine-applicable
|
||||
[#12726](https://github.com/rust-lang/rust-clippy/pull/12726)
|
||||
|
||||
## Rust 1.79
|
||||
|
||||
Current stable, released 2024-06-13
|
||||
Released 2024-06-13
|
||||
|
||||
[View all 102 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-03-08T11%3A13%3A58Z..2024-04-18T15%3A50%3A50Z+base%3Amaster)
|
||||
|
||||
|
|
@ -5712,6 +5754,7 @@ Released 2018-09-13
|
|||
[`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none
|
||||
[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
|
||||
[`path_ends_with_ext`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_ends_with_ext
|
||||
[`pathbuf_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#pathbuf_init_then_push
|
||||
[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
|
||||
[`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false
|
||||
[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "clippy"
|
||||
version = "0.1.81"
|
||||
version = "0.1.82"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
|
|
@ -30,11 +30,10 @@ color-print = "0.3.4"
|
|||
anstream = "0.6.0"
|
||||
|
||||
[dev-dependencies]
|
||||
ui_test = "0.23"
|
||||
ui_test = "0.24"
|
||||
regex = "1.5.5"
|
||||
toml = "0.7.3"
|
||||
walkdir = "2.3"
|
||||
# This is used by the `collect-metadata` alias.
|
||||
filetime = "0.2.9"
|
||||
itertools = "0.12"
|
||||
|
||||
|
|
@ -63,3 +62,7 @@ rustc_private = true
|
|||
[[test]]
|
||||
name = "compile-test"
|
||||
harness = false
|
||||
|
||||
[[test]]
|
||||
name = "dogfood"
|
||||
harness = false
|
||||
|
|
|
|||
|
|
@ -458,9 +458,8 @@ pub struct ManualStrip {
|
|||
}
|
||||
|
||||
impl ManualStrip {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self { msrv: conf.msrv.clone() }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -689,7 +688,6 @@ for some users. Adding a configuration is done in the following steps:
|
|||
]);
|
||||
|
||||
// New manual definition struct
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct StructName {}
|
||||
|
||||
impl_lint_pass!(StructName => [
|
||||
|
|
@ -700,7 +698,6 @@ for some users. Adding a configuration is done in the following steps:
|
|||
2. Next add the configuration value and a corresponding creation method like
|
||||
this:
|
||||
```rust
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct StructName {
|
||||
configuration_ident: Type,
|
||||
}
|
||||
|
|
@ -708,9 +705,9 @@ for some users. Adding a configuration is done in the following steps:
|
|||
// ...
|
||||
|
||||
impl StructName {
|
||||
pub fn new(configuration_ident: Type) -> Self {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
configuration_ident,
|
||||
configuration_ident: conf.configuration_ident,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -726,8 +723,7 @@ for some users. Adding a configuration is done in the following steps:
|
|||
store.register_*_pass(|| box module::StructName);
|
||||
|
||||
// New registration with configuration value
|
||||
let configuration_ident = conf.configuration_ident.clone();
|
||||
store.register_*_pass(move || box module::StructName::new(configuration_ident));
|
||||
store.register_*_pass(move || box module::StructName::new(conf));
|
||||
```
|
||||
|
||||
Congratulations the work is almost done. The configuration value can now be
|
||||
|
|
|
|||
|
|
@ -455,7 +455,7 @@ default configuration of Clippy. By default, any configuration will replace the
|
|||
* `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
|
||||
* `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
|
||||
|
||||
**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DevOps", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "WebGL", "WebGL2", "WebGPU", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]`
|
||||
**Default Value:** `["TiB", "CoreGraphics", "CoffeeScript", "TeX", "Direct2D", "PiB", "DirectX", "NetBSD", "OAuth", "NaN", "OpenType", "WebGL2", "WebTransport", "JavaScript", "OpenSSL", "OpenSSH", "EiB", "PureScript", "OpenAL", "MiB", "WebAssembly", "MinGW", "CoreFoundation", "WebGPU", "ClojureScript", "CamelCase", "OpenDNS", "NaNs", "OpenMP", "GitLab", "KiB", "sRGB", "CoreText", "macOS", "TypeScript", "GiB", "OpenExr", "YCbCr", "OpenTelemetry", "OpenBSD", "FreeBSD", "GPLv2", "PostScript", "WebP", "LaTeX", "TensorFlow", "AccessKit", "TrueType", "OpenStreetMap", "OpenGL", "DevOps", "OCaml", "WebRTC", "WebGL", "BibLaTeX", "GitHub", "GraphQL", "iOS", "Direct3D", "BibTeX", "DirectWrite", "GPLv3", "IPv6", "WebSocket", "IPv4", "ECMAScript"]`
|
||||
|
||||
---
|
||||
**Affected lints:**
|
||||
|
|
@ -679,6 +679,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
|
|||
* [`cast_abs_to_unsigned`](https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned)
|
||||
* [`checked_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions)
|
||||
* [`cloned_instead_of_copied`](https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied)
|
||||
* [`collapsible_match`](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match)
|
||||
* [`collapsible_str_replace`](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace)
|
||||
* [`deprecated_cfg_attr`](https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr)
|
||||
* [`derivable_impls`](https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ reason = "this function does not add a link to our documentation, please use the
|
|||
path = "rustc_lint::context::LintContext::span_lint"
|
||||
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead"
|
||||
|
||||
|
||||
[[disallowed-methods]]
|
||||
path = "rustc_middle::ty::context::TyCtxt::node_span_lint"
|
||||
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "clippy_config"
|
||||
version = "0.1.81"
|
||||
version = "0.1.82"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
|
|
|||
|
|
@ -18,23 +18,26 @@ use std::{cmp, env, fmt, fs, io};
|
|||
#[rustfmt::skip]
|
||||
const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
|
||||
"KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
|
||||
"AccessKit",
|
||||
"CoreFoundation", "CoreGraphics", "CoreText",
|
||||
"DevOps",
|
||||
"DirectX",
|
||||
"Direct2D", "Direct3D", "DirectWrite", "DirectX",
|
||||
"ECMAScript",
|
||||
"GPLv2", "GPLv3",
|
||||
"GitHub", "GitLab",
|
||||
"IPv4", "IPv6",
|
||||
"ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
|
||||
"ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript",
|
||||
"WebAssembly",
|
||||
"NaN", "NaNs",
|
||||
"OAuth", "GraphQL",
|
||||
"OCaml",
|
||||
"OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry",
|
||||
"WebGL", "WebGL2", "WebGPU",
|
||||
"OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry",
|
||||
"OpenType",
|
||||
"WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport",
|
||||
"WebP", "OpenExr", "YCbCr", "sRGB",
|
||||
"TensorFlow",
|
||||
"TrueType",
|
||||
"iOS", "macOS", "FreeBSD",
|
||||
"iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD",
|
||||
"TeX", "LaTeX", "BibTeX", "BibLaTeX",
|
||||
"MinGW",
|
||||
"CamelCase",
|
||||
|
|
@ -235,7 +238,7 @@ define_Conf! {
|
|||
///
|
||||
/// A type, say `SomeType`, listed in this configuration has the same behavior of
|
||||
/// `["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`.
|
||||
(arithmetic_side_effects_allowed: FxHashSet<String> = <_>::default()),
|
||||
(arithmetic_side_effects_allowed: Vec<String> = <_>::default()),
|
||||
/// Lint: ARITHMETIC_SIDE_EFFECTS.
|
||||
///
|
||||
/// Suppress checking of the passed type pair names in binary operations like addition or
|
||||
|
|
@ -262,12 +265,12 @@ define_Conf! {
|
|||
/// ```toml
|
||||
/// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]
|
||||
/// ```
|
||||
(arithmetic_side_effects_allowed_unary: FxHashSet<String> = <_>::default()),
|
||||
(arithmetic_side_effects_allowed_unary: Vec<String> = <_>::default()),
|
||||
/// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX, UNNECESSARY_BOX_RETURNS, SINGLE_CALL_FN, NEEDLESS_PASS_BY_REF_MUT.
|
||||
///
|
||||
/// Suppress lints whenever the suggested change would cause breakage for other crates.
|
||||
(avoid_breaking_exported_api: bool = true),
|
||||
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS, ASSIGNING_CLONES, LEGACY_NUMERIC_CONSTANTS, MANUAL_PATTERN_CHAR_COMPARISON, ALLOW_ATTRIBUTES, ALLOW_ATTRIBUTES_WITHOUT_REASON.
|
||||
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS, ASSIGNING_CLONES, LEGACY_NUMERIC_CONSTANTS, MANUAL_PATTERN_CHAR_COMPARISON, ALLOW_ATTRIBUTES, ALLOW_ATTRIBUTES_WITHOUT_REASON, COLLAPSIBLE_MATCH.
|
||||
///
|
||||
/// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml`
|
||||
#[default_text = ""]
|
||||
|
|
@ -311,7 +314,7 @@ define_Conf! {
|
|||
/// default configuration of Clippy. By default, any configuration will replace the default value. For example:
|
||||
/// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
|
||||
/// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
|
||||
(doc_valid_idents: Vec<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect()),
|
||||
(doc_valid_idents: FxHashSet<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect()),
|
||||
/// Lint: TOO_MANY_ARGUMENTS.
|
||||
///
|
||||
/// The maximum number of argument a function or method can have
|
||||
|
|
@ -547,7 +550,7 @@ define_Conf! {
|
|||
/// Lint: PATH_ENDS_WITH_EXT.
|
||||
///
|
||||
/// Additional dotfiles (files or directories starting with a dot) to allow
|
||||
(allowed_dotfiles: FxHashSet<String> = FxHashSet::default()),
|
||||
(allowed_dotfiles: Vec<String> = Vec::default()),
|
||||
/// Lint: MULTIPLE_CRATE_VERSIONS.
|
||||
///
|
||||
/// A list of crate names to allow duplicates of
|
||||
|
|
@ -700,7 +703,6 @@ pub fn lookup_conf_file() -> io::Result<(Option<PathBuf>, Vec<String>)> {
|
|||
fn deserialize(file: &SourceFile) -> TryConf {
|
||||
match toml::de::Deserializer::new(file.src.as_ref().unwrap()).deserialize_map(ConfVisitor(file)) {
|
||||
Ok(mut conf) => {
|
||||
extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS);
|
||||
extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES);
|
||||
extend_vec_if_indicator_present(&mut conf.conf.allowed_prefixes, DEFAULT_ALLOWED_PREFIXES);
|
||||
extend_vec_if_indicator_present(
|
||||
|
|
@ -713,6 +715,11 @@ fn deserialize(file: &SourceFile) -> TryConf {
|
|||
.allowed_idents_below_min_chars
|
||||
.extend(DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string));
|
||||
}
|
||||
if conf.conf.doc_valid_idents.contains("..") {
|
||||
conf.conf
|
||||
.doc_valid_idents
|
||||
.extend(DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string));
|
||||
}
|
||||
|
||||
conf
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ use serde::de::{self, Deserializer, Visitor};
|
|||
use serde::{ser, Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Rename {
|
||||
pub path: String,
|
||||
pub rename: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum DisallowedPath {
|
||||
Simple(String),
|
||||
|
|
@ -22,12 +22,10 @@ impl DisallowedPath {
|
|||
path
|
||||
}
|
||||
|
||||
pub fn reason(&self) -> Option<String> {
|
||||
match self {
|
||||
Self::WithReason {
|
||||
reason: Some(reason), ..
|
||||
} => Some(format!("{reason} (from clippy.toml)")),
|
||||
_ => None,
|
||||
pub fn reason(&self) -> Option<&str> {
|
||||
match &self {
|
||||
Self::WithReason { reason, .. } => reason.as_deref(),
|
||||
Self::Simple(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
|
|||
|
||||
let new_lint = if enable_msrv {
|
||||
format!(
|
||||
"store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(msrv())));\n ",
|
||||
"store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf)));\n ",
|
||||
lint_pass = lint.pass,
|
||||
ctor_arg = if lint.pass == "late" { "_" } else { "" },
|
||||
module_name = lint.name,
|
||||
|
|
@ -274,6 +274,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
|
|||
formatdoc!(
|
||||
r#"
|
||||
use clippy_config::msrvs::{{self, Msrv}};
|
||||
use clippy_config::Conf;
|
||||
{pass_import}
|
||||
use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
|
@ -301,9 +302,8 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
|
|||
}}
|
||||
|
||||
impl {name_camel} {{
|
||||
#[must_use]
|
||||
pub fn new(msrv: Msrv) -> Self {{
|
||||
Self {{ msrv }}
|
||||
pub fn new(conf: &'static Conf) -> Self {{
|
||||
Self {{ msrv: conf.msrv.clone() }}
|
||||
}}
|
||||
}}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "clippy_lints"
|
||||
version = "0.1.81"
|
||||
version = "0.1.82"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
|
|
@ -47,7 +48,16 @@ impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]);
|
|||
|
||||
pub struct AbsolutePaths {
|
||||
pub absolute_paths_max_segments: u64,
|
||||
pub absolute_paths_allowed_crates: FxHashSet<String>,
|
||||
pub absolute_paths_allowed_crates: &'static FxHashSet<String>,
|
||||
}
|
||||
|
||||
impl AbsolutePaths {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
absolute_paths_max_segments: conf.absolute_paths_max_segments,
|
||||
absolute_paths_allowed_crates: &conf.absolute_paths_allowed_crates,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for AbsolutePaths {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{trim_span, walk_span_to_context};
|
||||
use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits};
|
||||
|
|
@ -34,8 +35,10 @@ pub struct AlmostCompleteRange {
|
|||
msrv: Msrv,
|
||||
}
|
||||
impl AlmostCompleteRange {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
msrv: conf.msrv.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl EarlyLintPass for AlmostCompleteRange {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
|
|
@ -67,9 +68,10 @@ pub struct ApproxConstant {
|
|||
}
|
||||
|
||||
impl ApproxConstant {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
msrv: conf.msrv.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_lit(&self, cx: &LateContext<'_>, lit: &LitKind, e: &Expr<'_>) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::is_from_proc_macro;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use clippy_utils::{is_from_proc_macro, last_path_segment};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::print::with_forced_trimmed_paths;
|
||||
|
|
@ -42,12 +42,11 @@ declare_lint_pass!(ArcWithNonSendSync => [ARC_WITH_NON_SEND_SYNC]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if !expr.span.from_expansion()
|
||||
&& let ty = cx.typeck_results().expr_ty(expr)
|
||||
&& is_type_diagnostic_item(cx, ty, sym::Arc)
|
||||
&& let ExprKind::Call(func, [arg]) = expr.kind
|
||||
&& let ExprKind::Path(func_path) = func.kind
|
||||
&& last_path_segment(&func_path).ident.name == sym::new
|
||||
if let ExprKind::Call(func, [arg]) = expr.kind
|
||||
&& let ExprKind::Path(QPath::TypeRelative(func_ty, func_name)) = func.kind
|
||||
&& func_name.ident.name == sym::new
|
||||
&& !expr.span.from_expansion()
|
||||
&& is_type_diagnostic_item(cx, cx.typeck_results().node_type(func_ty.hir_id), sym::Arc)
|
||||
&& let arg_ty = cx.typeck_results().expr_ty(arg)
|
||||
// make sure that the type is not and does not contain any type parameters
|
||||
&& arg_ty.walk().all(|arg| {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::mir::{enclosing_mir, PossibleBorrowerMap};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
|
|
@ -57,9 +58,10 @@ pub struct AssigningClones {
|
|||
}
|
||||
|
||||
impl AssigningClones {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
msrv: conf.msrv.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ mod useless_attribute;
|
|||
mod utils;
|
||||
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use rustc_ast::{Attribute, MetaItemKind, NestedMetaItem};
|
||||
use rustc_hir::{ImplItem, Item, ItemKind, TraitItem};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
||||
|
|
@ -499,7 +500,6 @@ declare_clippy_lint! {
|
|||
"duplicated attribute"
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Attributes {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
|
@ -517,9 +517,10 @@ impl_lint_pass!(Attributes => [
|
|||
]);
|
||||
|
||||
impl Attributes {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
msrv: conf.msrv.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -589,7 +590,15 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
|
|||
}
|
||||
|
||||
pub struct EarlyAttributes {
|
||||
pub msrv: Msrv,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl EarlyAttributes {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
msrv: conf.msrv.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(EarlyAttributes => [
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use clippy_config::types::DisallowedPath;
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{match_def_path, paths};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use clippy_utils::{create_disallowed_map, match_def_path, paths};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::def_id::{DefId, DefIdMap};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::mir::CoroutineLayout;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
|
|
@ -172,31 +172,19 @@ declare_clippy_lint! {
|
|||
|
||||
impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AwaitHolding {
|
||||
conf_invalid_types: Vec<DisallowedPath>,
|
||||
def_ids: FxHashMap<DefId, DisallowedPath>,
|
||||
def_ids: DefIdMap<(&'static str, Option<&'static str>)>,
|
||||
}
|
||||
|
||||
impl AwaitHolding {
|
||||
pub(crate) fn new(conf_invalid_types: Vec<DisallowedPath>) -> Self {
|
||||
pub(crate) fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
conf_invalid_types,
|
||||
def_ids: FxHashMap::default(),
|
||||
def_ids: create_disallowed_map(tcx, &conf.await_holding_invalid_types),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for AwaitHolding {
|
||||
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
|
||||
for conf in &self.conf_invalid_types {
|
||||
let segs: Vec<_> = conf.path().split("::").collect();
|
||||
for id in clippy_utils::def_path_def_ids(cx, &segs) {
|
||||
self.def_ids.insert(id, conf.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
|
||||
if let hir::ExprKind::Closure(hir::Closure {
|
||||
kind: hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)),
|
||||
|
|
@ -258,25 +246,22 @@ impl AwaitHolding {
|
|||
);
|
||||
},
|
||||
);
|
||||
} else if let Some(disallowed) = self.def_ids.get(&adt.did()) {
|
||||
emit_invalid_type(cx, ty_cause.source_info.span, disallowed);
|
||||
} else if let Some(&(path, reason)) = self.def_ids.get(&adt.did()) {
|
||||
emit_invalid_type(cx, ty_cause.source_info.span, path, reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedPath) {
|
||||
fn emit_invalid_type(cx: &LateContext<'_>, span: Span, path: &'static str, reason: Option<&'static str>) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
AWAIT_HOLDING_INVALID_TYPE,
|
||||
span,
|
||||
format!(
|
||||
"`{}` may not be held across an await point per `clippy.toml`",
|
||||
disallowed.path()
|
||||
),
|
||||
format!("holding a disallowed type across an await point `{path}`"),
|
||||
|diag| {
|
||||
if let Some(reason) = disallowed.reason() {
|
||||
if let Some(reason) = reason {
|
||||
diag.note(reason);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -49,35 +49,31 @@ declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) {
|
||||
if !e.span.from_expansion()
|
||||
&& let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind
|
||||
&& !addrof_target.span.from_expansion()
|
||||
if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind
|
||||
&& let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind
|
||||
&& !deref_target.span.from_expansion()
|
||||
&& !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..))
|
||||
&& !e.span.from_expansion()
|
||||
&& !deref_target.span.from_expansion()
|
||||
&& !addrof_target.span.from_expansion()
|
||||
&& let ref_ty = cx.typeck_results().expr_ty(deref_target)
|
||||
&& let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind()
|
||||
&& get_parent_expr(cx, e).map_or(true, |parent| {
|
||||
match parent.kind {
|
||||
// `*&*foo` should lint `deref_addrof` instead.
|
||||
ExprKind::Unary(UnOp::Deref, _) => is_lint_allowed(cx, DEREF_ADDROF, parent.hir_id),
|
||||
// `&*foo` creates a distinct temporary from `foo`
|
||||
ExprKind::AddrOf(_, Mutability::Mut, _) => !matches!(
|
||||
deref_target.kind,
|
||||
ExprKind::Path(..)
|
||||
| ExprKind::Field(..)
|
||||
| ExprKind::Index(..)
|
||||
| ExprKind::Unary(UnOp::Deref, ..)
|
||||
),
|
||||
_ => true,
|
||||
}
|
||||
})
|
||||
&& !is_from_proc_macro(cx, e)
|
||||
{
|
||||
if let Some(parent_expr) = get_parent_expr(cx, e) {
|
||||
if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..))
|
||||
&& !is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// modification to `&mut &*x` is different from `&mut x`
|
||||
if matches!(
|
||||
deref_target.kind,
|
||||
ExprKind::Path(..) | ExprKind::Field(..) | ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, ..)
|
||||
) && matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if is_from_proc_macro(cx, e) {
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
BORROW_DEREF_REF,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ mod multiple_crate_versions;
|
|||
mod wildcard_dependencies;
|
||||
|
||||
use cargo_metadata::MetadataCommand;
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_lint_allowed;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
|
|
@ -204,8 +205,8 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct Cargo {
|
||||
pub allowed_duplicate_crates: FxHashSet<String>,
|
||||
pub ignore_publish: bool,
|
||||
allowed_duplicate_crates: &'static FxHashSet<String>,
|
||||
ignore_publish: bool,
|
||||
}
|
||||
|
||||
impl_lint_pass!(Cargo => [
|
||||
|
|
@ -217,6 +218,15 @@ impl_lint_pass!(Cargo => [
|
|||
LINT_GROUPS_PRIORITY,
|
||||
]);
|
||||
|
||||
impl Cargo {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
allowed_duplicate_crates: &conf.allowed_duplicate_crates,
|
||||
ignore_publish: conf.cargo_ignore_publish,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for Cargo {
|
||||
fn check_crate(&mut self, cx: &LateContext<'_>) {
|
||||
static NO_DEPS_LINTS: &[&Lint] = &[
|
||||
|
|
@ -253,7 +263,7 @@ impl LateLintPass<'_> for Cargo {
|
|||
{
|
||||
match MetadataCommand::new().exec() {
|
||||
Ok(metadata) => {
|
||||
multiple_crate_versions::check(cx, &metadata, &self.allowed_duplicate_crates);
|
||||
multiple_crate_versions::check(cx, &metadata, self.allowed_duplicate_crates);
|
||||
},
|
||||
Err(e) => {
|
||||
for lint in WITH_DEPS_LINTS {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,21 @@
|
|||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::in_constant;
|
||||
use clippy_utils::source::{snippet_opt, snippet_with_applicability};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::is_isize_or_usize;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, QPath, TyKind};
|
||||
use rustc_hir::{Expr, QPath, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, FloatTy, Ty, UintTy};
|
||||
use rustc_middle::ty::{self, FloatTy, Ty};
|
||||
use rustc_span::hygiene;
|
||||
|
||||
use super::{utils, CAST_LOSSLESS};
|
||||
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
cast_op: &Expr<'_>,
|
||||
cast_from_expr: &Expr<'_>,
|
||||
cast_from: Ty<'_>,
|
||||
cast_to: Ty<'_>,
|
||||
cast_to_hir: &rustc_hir::Ty<'_>,
|
||||
|
|
@ -23,64 +25,54 @@ pub(super) fn check(
|
|||
return;
|
||||
}
|
||||
|
||||
// The suggestion is to use a function call, so if the original expression
|
||||
// has parens on the outside, they are no longer needed.
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let opt = snippet_opt(cx, cast_op.span.source_callsite());
|
||||
let sugg = opt.as_ref().map_or_else(
|
||||
|| {
|
||||
app = Applicability::HasPlaceholders;
|
||||
".."
|
||||
},
|
||||
|snip| {
|
||||
if should_strip_parens(cast_op, snip) {
|
||||
&snip[1..snip.len() - 1]
|
||||
} else {
|
||||
snip.as_str()
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Display the type alias instead of the aliased type. Fixes #11285
|
||||
//
|
||||
// FIXME: Once `lazy_type_alias` is stabilized(?) we should use `rustc_middle` types instead,
|
||||
// this will allow us to display the right type with `cast_from` as well.
|
||||
let cast_to_fmt = if let TyKind::Path(QPath::Resolved(None, path)) = cast_to_hir.kind
|
||||
// It's a bit annoying but the turbofish is optional for types. A type in an `as` cast
|
||||
// shouldn't have these if they're primitives, which are the only things we deal with.
|
||||
//
|
||||
// This could be removed for performance if this check is determined to have a pretty major
|
||||
// effect.
|
||||
&& path.segments.iter().all(|segment| segment.args.is_none())
|
||||
{
|
||||
snippet_with_applicability(cx, cast_to_hir.span, "..", &mut app)
|
||||
} else {
|
||||
cast_to.to_string().into()
|
||||
};
|
||||
|
||||
let message = if cast_from.is_bool() {
|
||||
format!("casting `{cast_from}` to `{cast_to_fmt}` is more cleanly stated with `{cast_to_fmt}::from(_)`")
|
||||
} else {
|
||||
format!("casting `{cast_from}` to `{cast_to_fmt}` may become silently lossy if you later change the type")
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
CAST_LOSSLESS,
|
||||
expr.span,
|
||||
message,
|
||||
"try",
|
||||
format!("{cast_to_fmt}::from({sugg})"),
|
||||
app,
|
||||
format!("casts from `{cast_from}` to `{cast_to}` can be expressed infallibly using `From`"),
|
||||
|diag| {
|
||||
diag.help("an `as` cast can become silently lossy if the types change in the future");
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let from_sugg = Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "<from>", &mut applicability);
|
||||
let Some(ty) = snippet_opt(cx, hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt())) else {
|
||||
return;
|
||||
};
|
||||
match cast_to_hir.kind {
|
||||
TyKind::Infer => {
|
||||
diag.span_suggestion_verbose(
|
||||
expr.span,
|
||||
"use `Into::into` instead",
|
||||
format!("{}.into()", from_sugg.maybe_par()),
|
||||
applicability,
|
||||
);
|
||||
},
|
||||
// Don't suggest `A<_>::B::From(x)` or `macro!()::from(x)`
|
||||
kind if matches!(kind, TyKind::Path(QPath::Resolved(_, path)) if path.segments.iter().any(|s| s.args.is_some()))
|
||||
|| !cast_to_hir.span.eq_ctxt(expr.span) =>
|
||||
{
|
||||
diag.span_suggestion_verbose(
|
||||
expr.span,
|
||||
format!("use `<{ty}>::from` instead"),
|
||||
format!("<{ty}>::from({from_sugg})"),
|
||||
applicability,
|
||||
);
|
||||
},
|
||||
_ => {
|
||||
diag.span_suggestion_verbose(
|
||||
expr.span,
|
||||
format!("use `{ty}::from` instead"),
|
||||
format!("{ty}::from({from_sugg})"),
|
||||
applicability,
|
||||
);
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool {
|
||||
// Do not suggest using From in consts/statics until it is valid to do so (see #2267).
|
||||
//
|
||||
// If destination is u128, do not lint because source type cannot be larger
|
||||
// If source is bool, still lint due to the lint message differing (refers to style)
|
||||
if in_constant(cx, expr.hir_id) || (!cast_from.is_bool() && matches!(cast_to.kind(), ty::Uint(UintTy::U128))) {
|
||||
if in_constant(cx, expr.hir_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -110,12 +102,3 @@ fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn should_strip_parens(cast_expr: &Expr<'_>, snip: &str) -> bool {
|
||||
if let ExprKind::Binary(_, _, _) = cast_expr.kind {
|
||||
if snip.starts_with('(') && snip.ends_with(')') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ mod utils;
|
|||
mod zero_ptr;
|
||||
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::is_hir_ty_cfg_dependant;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
|
|
@ -658,11 +659,11 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let _: (0.0_f32 / 0.0) as u64;
|
||||
/// let _ = (0.0_f32 / 0.0) as u64;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// let _: = 0_u64;
|
||||
/// let _ = 0_u64;
|
||||
/// ```
|
||||
#[clippy::version = "1.66.0"]
|
||||
pub CAST_NAN_TO_INT,
|
||||
|
|
@ -722,9 +723,10 @@ pub struct Casts {
|
|||
}
|
||||
|
||||
impl Casts {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
msrv: conf.msrv.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -761,45 +763,45 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Cast(cast_expr, cast_to_hir) = expr.kind {
|
||||
if let ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind {
|
||||
if is_hir_ty_cfg_dependant(cx, cast_to_hir) {
|
||||
return;
|
||||
}
|
||||
let (cast_from, cast_to) = (
|
||||
cx.typeck_results().expr_ty(cast_expr),
|
||||
cx.typeck_results().expr_ty(cast_from_expr),
|
||||
cx.typeck_results().expr_ty(expr),
|
||||
);
|
||||
|
||||
if !expr.span.from_expansion() && unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
|
||||
if !expr.span.from_expansion() && unnecessary_cast::check(cx, expr, cast_from_expr, cast_from, cast_to) {
|
||||
return;
|
||||
}
|
||||
cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv);
|
||||
ptr_cast_constness::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
|
||||
as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to);
|
||||
fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
|
||||
fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
|
||||
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
|
||||
zero_ptr::check(cx, expr, cast_expr, cast_to_hir);
|
||||
cast_slice_from_raw_parts::check(cx, expr, cast_from_expr, cast_to, &self.msrv);
|
||||
ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, &self.msrv);
|
||||
as_ptr_cast_mut::check(cx, expr, cast_from_expr, cast_to);
|
||||
fn_to_numeric_cast_any::check(cx, expr, cast_from_expr, cast_from, cast_to);
|
||||
fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to);
|
||||
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to);
|
||||
zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
|
||||
|
||||
if cast_to.is_numeric() {
|
||||
cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir.span);
|
||||
cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span);
|
||||
if cast_from.is_numeric() {
|
||||
cast_possible_wrap::check(cx, expr, cast_from, cast_to);
|
||||
cast_precision_loss::check(cx, expr, cast_from, cast_to);
|
||||
cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
|
||||
cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
|
||||
cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to);
|
||||
cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to);
|
||||
cast_abs_to_unsigned::check(cx, expr, cast_from_expr, cast_from, cast_to, &self.msrv);
|
||||
cast_nan_to_int::check(cx, expr, cast_from_expr, cast_from, cast_to);
|
||||
}
|
||||
cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir, &self.msrv);
|
||||
cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
|
||||
cast_lossless::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir, &self.msrv);
|
||||
cast_enum_constructor::check(cx, expr, cast_from_expr, cast_from);
|
||||
}
|
||||
|
||||
as_underscore::check(cx, expr, cast_to_hir);
|
||||
|
||||
if self.msrv.meets(msrvs::PTR_FROM_REF) {
|
||||
ref_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
|
||||
ref_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
|
||||
} else if self.msrv.meets(msrvs::BORROW_AS_PTR) {
|
||||
borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
|
||||
borrow_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use clippy_utils::sugg::Sugg;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, Mutability};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
|
||||
|
||||
use super::PTR_CAST_CONSTNESS;
|
||||
|
||||
|
|
@ -24,6 +24,7 @@ pub(super) fn check<'tcx>(
|
|||
(Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not)
|
||||
)
|
||||
&& from_ty == to_ty
|
||||
&& !from_ty.has_erased_regions()
|
||||
{
|
||||
let sugg = Sugg::hir(cx, cast_expr, "_");
|
||||
let constness = match *to_mutbl {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
//! lint on manually implemented checked conversions that could be transformed into `try_from`
|
||||
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{in_constant, is_integer_literal, SpanlessEq};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
|
@ -40,9 +41,10 @@ pub struct CheckedConversions {
|
|||
}
|
||||
|
||||
impl CheckedConversions {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
msrv: conf.msrv.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -50,61 +52,54 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
|
||||
if !self.msrv.meets(msrvs::TRY_FROM) {
|
||||
return;
|
||||
}
|
||||
|
||||
let result = if !in_constant(cx, item.hir_id)
|
||||
if let ExprKind::Binary(op, lhs, rhs) = item.kind
|
||||
&& let (lt1, gt1, op2) = match op.node {
|
||||
BinOpKind::Le => (lhs, rhs, None),
|
||||
BinOpKind::Ge => (rhs, lhs, None),
|
||||
BinOpKind::And
|
||||
if let ExprKind::Binary(op1, lhs1, rhs1) = lhs.kind
|
||||
&& let ExprKind::Binary(op2, lhs2, rhs2) = rhs.kind
|
||||
&& let Some((lt1, gt1)) = read_le_ge(op1.node, lhs1, rhs1)
|
||||
&& let Some((lt2, gt2)) = read_le_ge(op2.node, lhs2, rhs2) =>
|
||||
{
|
||||
(lt1, gt1, Some((lt2, gt2)))
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
&& !in_external_macro(cx.sess(), item.span)
|
||||
&& let ExprKind::Binary(op, left, right) = &item.kind
|
||||
&& !in_constant(cx, item.hir_id)
|
||||
&& self.msrv.meets(msrvs::TRY_FROM)
|
||||
&& let Some(cv) = match op2 {
|
||||
// todo: check for case signed -> larger unsigned == only x >= 0
|
||||
None => check_upper_bound(lt1, gt1).filter(|cv| cv.cvt == ConversionType::FromUnsigned),
|
||||
Some((lt2, gt2)) => {
|
||||
let upper_lower = |lt1, gt1, lt2, gt2| {
|
||||
check_upper_bound(lt1, gt1)
|
||||
.zip(check_lower_bound(lt2, gt2))
|
||||
.and_then(|(l, r)| l.combine(r, cx))
|
||||
};
|
||||
upper_lower(lt1, gt1, lt2, gt2).or_else(|| upper_lower(lt2, gt2, lt1, gt1))
|
||||
},
|
||||
}
|
||||
&& let Some(to_type) = cv.to_type
|
||||
{
|
||||
match op.node {
|
||||
BinOpKind::Ge | BinOpKind::Le => single_check(item),
|
||||
BinOpKind::And => double_check(cx, left, right),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(cv) = result {
|
||||
if let Some(to_type) = cv.to_type {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut applicability);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
CHECKED_CONVERSIONS,
|
||||
item.span,
|
||||
"checked cast can be simplified",
|
||||
"try",
|
||||
format!("{to_type}::try_from({snippet}).is_ok()"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut applicability);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
CHECKED_CONVERSIONS,
|
||||
item.span,
|
||||
"checked cast can be simplified",
|
||||
"try",
|
||||
format!("{to_type}::try_from({snippet}).is_ok()"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
/// Searches for a single check from unsigned to _ is done
|
||||
/// todo: check for case signed -> larger unsigned == only x >= 0
|
||||
fn single_check<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
|
||||
check_upper_bound(expr).filter(|cv| cv.cvt == ConversionType::FromUnsigned)
|
||||
}
|
||||
|
||||
/// Searches for a combination of upper & lower bound checks
|
||||
fn double_check<'a>(cx: &LateContext<'_>, left: &'a Expr<'_>, right: &'a Expr<'_>) -> Option<Conversion<'a>> {
|
||||
let upper_lower = |l, r| {
|
||||
let upper = check_upper_bound(l);
|
||||
let lower = check_lower_bound(r);
|
||||
|
||||
upper.zip(lower).and_then(|(l, r)| l.combine(r, cx))
|
||||
};
|
||||
|
||||
upper_lower(left, right).or_else(|| upper_lower(right, left))
|
||||
}
|
||||
|
||||
/// Contains the result of a tried conversion check
|
||||
#[derive(Clone, Debug)]
|
||||
struct Conversion<'a> {
|
||||
|
|
@ -121,6 +116,19 @@ enum ConversionType {
|
|||
FromUnsigned,
|
||||
}
|
||||
|
||||
/// Attempts to read either `<=` or `>=` with a normalized operand order.
|
||||
fn read_le_ge<'tcx>(
|
||||
op: BinOpKind,
|
||||
lhs: &'tcx Expr<'tcx>,
|
||||
rhs: &'tcx Expr<'tcx>,
|
||||
) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
|
||||
match op {
|
||||
BinOpKind::Le => Some((lhs, rhs)),
|
||||
BinOpKind::Ge => Some((rhs, lhs)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Conversion<'a> {
|
||||
/// Combine multiple conversions if the are compatible
|
||||
pub fn combine(self, other: Self, cx: &LateContext<'_>) -> Option<Conversion<'a>> {
|
||||
|
|
@ -188,29 +196,17 @@ impl ConversionType {
|
|||
}
|
||||
|
||||
/// Check for `expr <= (to_type::MAX as from_type)`
|
||||
fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
|
||||
if let ExprKind::Binary(ref op, left, right) = &expr.kind
|
||||
&& let Some((candidate, check)) = normalize_le_ge(op, left, right)
|
||||
&& let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX")
|
||||
{
|
||||
Conversion::try_new(candidate, from, to)
|
||||
fn check_upper_bound<'tcx>(lt: &'tcx Expr<'tcx>, gt: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
|
||||
if let Some((from, to)) = get_types_from_cast(gt, INTS, "max_value", "MAX") {
|
||||
Conversion::try_new(lt, from, to)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Check for `expr >= 0|(to_type::MIN as from_type)`
|
||||
fn check_lower_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
|
||||
fn check_function<'a>(candidate: &'a Expr<'a>, check: &'a Expr<'a>) -> Option<Conversion<'a>> {
|
||||
(check_lower_bound_zero(candidate, check)).or_else(|| (check_lower_bound_min(candidate, check)))
|
||||
}
|
||||
|
||||
// First of we need a binary containing the expression & the cast
|
||||
if let ExprKind::Binary(ref op, left, right) = &expr.kind {
|
||||
normalize_le_ge(op, right, left).and_then(|(l, r)| check_function(l, r))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
fn check_lower_bound<'tcx>(lt: &'tcx Expr<'tcx>, gt: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
|
||||
check_lower_bound_zero(gt, lt).or_else(|| check_lower_bound_min(gt, lt))
|
||||
}
|
||||
|
||||
/// Check for `expr >= 0`
|
||||
|
|
@ -309,15 +305,6 @@ fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Will return the expressions as if they were expr1 <= expr2
|
||||
fn normalize_le_ge<'a>(op: &BinOp, left: &'a Expr<'a>, right: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> {
|
||||
match op.node {
|
||||
BinOpKind::Le => Some((left, right)),
|
||||
BinOpKind::Ge => Some((right, left)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Constants
|
||||
const UINTS: &[&str] = &["u8", "u16", "u32", "u64", "usize"];
|
||||
const SINTS: &[&str] = &["i8", "i16", "i32", "i64", "isize"];
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//! calculate cognitive complexity and warn about overly complex functions
|
||||
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::source::{IntoSpan, SpanRangeExt};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
|
|
@ -39,10 +40,9 @@ pub struct CognitiveComplexity {
|
|||
}
|
||||
|
||||
impl CognitiveComplexity {
|
||||
#[must_use]
|
||||
pub fn new(limit: u64) -> Self {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
limit: LimitStack::new(limit),
|
||||
limit: LimitStack::new(conf.cognitive_complexity_threshold),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,20 +93,14 @@ declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]);
|
|||
|
||||
impl EarlyLintPass for CollapsibleIf {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
|
||||
if !expr.span.from_expansion() {
|
||||
check_if(cx, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) {
|
||||
if let ast::ExprKind::If(check, then, else_) = &expr.kind {
|
||||
if let Some(else_) = else_ {
|
||||
check_collapsible_maybe_if_let(cx, then.span, else_);
|
||||
} else if let ast::ExprKind::Let(..) = check.kind {
|
||||
// Prevent triggering on `if let a = b { if c { .. } }`.
|
||||
} else {
|
||||
check_collapsible_no_if_let(cx, expr, check, then);
|
||||
if let ast::ExprKind::If(cond, then, else_) = &expr.kind
|
||||
&& !expr.span.from_expansion()
|
||||
{
|
||||
if let Some(else_) = else_ {
|
||||
check_collapsible_maybe_if_let(cx, then.span, else_);
|
||||
} else if !matches!(cond.kind, ast::ExprKind::Let(..)) {
|
||||
check_collapsible_no_if_let(cx, expr, cond, then);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -189,13 +183,10 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &
|
|||
|
||||
/// If the block contains only one expression, return it.
|
||||
fn expr_block(block: &ast::Block) -> Option<&ast::Expr> {
|
||||
let mut it = block.stmts.iter();
|
||||
|
||||
if let (Some(stmt), None) = (it.next(), it.next()) {
|
||||
match stmt.kind {
|
||||
ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => Some(expr),
|
||||
_ => None,
|
||||
}
|
||||
if let [stmt] = &*block.stmts
|
||||
&& let ast::StmtKind::Expr(expr) | ast::StmtKind::Semi(expr) = &stmt.kind
|
||||
{
|
||||
Some(expr)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,9 +59,9 @@ static COLLECTIONS: [Symbol; 9] = [
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
|
||||
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) {
|
||||
// Look for local variables whose type is a container. Search surrounding bock for read access.
|
||||
if match_acceptable_type(cx, local, &COLLECTIONS)
|
||||
&& let PatKind::Binding(_, local_id, _, _) = local.pat.kind
|
||||
// Look for local variables whose type is a container. Search surrounding block for read access.
|
||||
if let PatKind::Binding(_, local_id, _, _) = local.pat.kind
|
||||
&& match_acceptable_type(cx, local, &COLLECTIONS)
|
||||
&& let Some(enclosing_block) = get_enclosing_block(cx, local.hir_id)
|
||||
&& has_no_read_access(cx, local_id, enclosing_block)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
|
||||
use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, IntoSpan, SpanRangeExt};
|
||||
use clippy_utils::ty::{needs_ordered_drop, InteriorMut};
|
||||
|
|
@ -11,6 +12,7 @@ use core::ops::ControlFlow;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{intravisit, BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::hygiene::walk_chain;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
|
|
@ -159,15 +161,13 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct CopyAndPaste<'tcx> {
|
||||
ignore_interior_mutability: Vec<String>,
|
||||
interior_mut: InteriorMut<'tcx>,
|
||||
}
|
||||
|
||||
impl CopyAndPaste<'_> {
|
||||
pub fn new(ignore_interior_mutability: Vec<String>) -> Self {
|
||||
impl<'tcx> CopyAndPaste<'tcx> {
|
||||
pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
ignore_interior_mutability,
|
||||
interior_mut: InteriorMut::default(),
|
||||
interior_mut: InteriorMut::new(tcx, &conf.ignore_interior_mutability),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -180,10 +180,6 @@ impl_lint_pass!(CopyAndPaste<'_> => [
|
|||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CopyAndPaste<'tcx> {
|
||||
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
|
||||
self.interior_mut = InteriorMut::new(cx, &self.ignore_interior_mutability);
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) {
|
||||
let (conds, blocks) = if_sequence(expr);
|
||||
|
|
|
|||
|
|
@ -53,10 +53,9 @@ declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]);
|
|||
|
||||
impl EarlyLintPass for CrateInMacroDef {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
if item.attrs.iter().any(is_macro_export)
|
||||
&& let ItemKind::MacroDef(macro_def) = &item.kind
|
||||
&& let tts = macro_def.body.tokens.clone()
|
||||
&& let Some(span) = contains_unhygienic_crate_reference(&tts)
|
||||
if let ItemKind::MacroDef(macro_def) = &item.kind
|
||||
&& item.attrs.iter().any(is_macro_export)
|
||||
&& let Some(span) = contains_unhygienic_crate_reference(¯o_def.body.tokens)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_in_test;
|
||||
use clippy_utils::macros::{macro_backtrace, MacroCall};
|
||||
|
|
@ -33,7 +34,6 @@ declare_clippy_lint! {
|
|||
"`dbg!` macro is intended as a debugging tool"
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DbgMacro {
|
||||
allow_dbg_in_tests: bool,
|
||||
/// Tracks the `dbg!` macro callsites that are already checked.
|
||||
|
|
@ -45,9 +45,9 @@ pub struct DbgMacro {
|
|||
impl_lint_pass!(DbgMacro => [DBG_MACRO]);
|
||||
|
||||
impl DbgMacro {
|
||||
pub fn new(allow_dbg_in_tests: bool) -> Self {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
DbgMacro {
|
||||
allow_dbg_in_tests,
|
||||
allow_dbg_in_tests: conf.allow_dbg_in_tests,
|
||||
checked_dbg_call_site: FxHashSet::default(),
|
||||
prev_ctxt: SyntaxContext::root(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -598,6 +598,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::partialeq_to_none::PARTIALEQ_TO_NONE_INFO,
|
||||
crate::pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE_INFO,
|
||||
crate::pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF_INFO,
|
||||
crate::pathbuf_init_then_push::PATHBUF_INIT_THEN_PUSH_INFO,
|
||||
crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO,
|
||||
crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO,
|
||||
crate::precedence::PRECEDENCE_INFO,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::indent_of;
|
||||
use clippy_utils::{is_default_equivalent, peel_blocks};
|
||||
|
|
@ -60,9 +61,10 @@ pub struct DerivableImpls {
|
|||
}
|
||||
|
||||
impl DerivableImpls {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
DerivableImpls { msrv }
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
DerivableImpls {
|
||||
msrv: conf.msrv.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use clippy_config::types::DisallowedPath;
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::create_disallowed_map;
|
||||
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
|
||||
use clippy_utils::macros::macro_backtrace;
|
||||
use rustc_ast::Attribute;
|
||||
|
|
@ -9,6 +10,7 @@ use rustc_hir::{
|
|||
Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{ExpnId, MacroKind, Span};
|
||||
|
||||
|
|
@ -57,27 +59,24 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct DisallowedMacros {
|
||||
conf_disallowed: Vec<DisallowedPath>,
|
||||
disallowed: DefIdMap<usize>,
|
||||
disallowed: DefIdMap<(&'static str, Option<&'static str>)>,
|
||||
seen: FxHashSet<ExpnId>,
|
||||
|
||||
// Track the most recently seen node that can have a `derive` attribute.
|
||||
// Needed to use the correct lint level.
|
||||
derive_src: Option<OwnerId>,
|
||||
}
|
||||
|
||||
impl DisallowedMacros {
|
||||
pub fn new(conf_disallowed: Vec<DisallowedPath>) -> Self {
|
||||
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
conf_disallowed,
|
||||
disallowed: DefIdMap::default(),
|
||||
disallowed: create_disallowed_map(tcx, &conf.disallowed_macros),
|
||||
seen: FxHashSet::default(),
|
||||
derive_src: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn check(&mut self, cx: &LateContext<'_>, span: Span, derive_src: Option<OwnerId>) {
|
||||
if self.conf_disallowed.is_empty() {
|
||||
if self.disallowed.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -86,11 +85,10 @@ impl DisallowedMacros {
|
|||
return;
|
||||
}
|
||||
|
||||
if let Some(&index) = self.disallowed.get(&mac.def_id) {
|
||||
let conf = &self.conf_disallowed[index];
|
||||
let msg = format!("use of a disallowed macro `{}`", conf.path());
|
||||
if let Some(&(path, reason)) = self.disallowed.get(&mac.def_id) {
|
||||
let msg = format!("use of a disallowed macro `{path}`");
|
||||
let add_note = |diag: &mut Diag<'_, _>| {
|
||||
if let Some(reason) = conf.reason() {
|
||||
if let Some(reason) = reason {
|
||||
diag.note(reason);
|
||||
}
|
||||
};
|
||||
|
|
@ -116,15 +114,6 @@ impl DisallowedMacros {
|
|||
impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]);
|
||||
|
||||
impl LateLintPass<'_> for DisallowedMacros {
|
||||
fn check_crate(&mut self, cx: &LateContext<'_>) {
|
||||
for (index, conf) in self.conf_disallowed.iter().enumerate() {
|
||||
let segs: Vec<_> = conf.path().split("::").collect();
|
||||
for id in clippy_utils::def_path_def_ids(cx, &segs) {
|
||||
self.disallowed.insert(id, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
self.check(cx, expr.span, None);
|
||||
// `$t + $t` can have the context of $t, check also the span of the binary operator
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use clippy_config::types::DisallowedPath;
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::create_disallowed_map;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||
use rustc_hir::def_id::DefIdMap;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -55,17 +57,14 @@ declare_clippy_lint! {
|
|||
"use of a disallowed method call"
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DisallowedMethods {
|
||||
conf_disallowed: Vec<DisallowedPath>,
|
||||
disallowed: DefIdMap<usize>,
|
||||
disallowed: DefIdMap<(&'static str, Option<&'static str>)>,
|
||||
}
|
||||
|
||||
impl DisallowedMethods {
|
||||
pub fn new(conf_disallowed: Vec<DisallowedPath>) -> Self {
|
||||
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
conf_disallowed,
|
||||
disallowed: DefIdMap::default(),
|
||||
disallowed: create_disallowed_map(tcx, &conf.disallowed_methods),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -73,15 +72,6 @@ impl DisallowedMethods {
|
|||
impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
|
||||
fn check_crate(&mut self, cx: &LateContext<'_>) {
|
||||
for (index, conf) in self.conf_disallowed.iter().enumerate() {
|
||||
let segs: Vec<_> = conf.path().split("::").collect();
|
||||
for id in clippy_utils::def_path_def_ids(cx, &segs) {
|
||||
self.disallowed.insert(id, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let (id, span) = match &expr.kind {
|
||||
ExprKind::Path(path)
|
||||
|
|
@ -95,14 +85,18 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
|
|||
},
|
||||
_ => return,
|
||||
};
|
||||
if let Some(&index) = self.disallowed.get(&id) {
|
||||
let conf = &self.conf_disallowed[index];
|
||||
let msg = format!("use of a disallowed method `{}`", conf.path());
|
||||
span_lint_and_then(cx, DISALLOWED_METHODS, span, msg, |diag| {
|
||||
if let Some(reason) = conf.reason() {
|
||||
diag.note(reason);
|
||||
}
|
||||
});
|
||||
if let Some(&(path, reason)) = self.disallowed.get(&id) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DISALLOWED_METHODS,
|
||||
span,
|
||||
format!("use of a disallowed method `{path}`"),
|
||||
|diag| {
|
||||
if let Some(reason) = reason {
|
||||
diag.note(reason);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_in_test;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::{Pat, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Symbol;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -24,15 +26,14 @@ declare_clippy_lint! {
|
|||
"usage of a disallowed/placeholder name"
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DisallowedNames {
|
||||
disallow: FxHashSet<String>,
|
||||
disallow: FxHashSet<Symbol>,
|
||||
}
|
||||
|
||||
impl DisallowedNames {
|
||||
pub fn new(disallowed_names: &[String]) -> Self {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
disallow: disallowed_names.iter().cloned().collect(),
|
||||
disallow: conf.disallowed_names.iter().map(|x| Symbol::intern(x)).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -42,7 +43,7 @@ impl_lint_pass!(DisallowedNames => [DISALLOWED_NAMES]);
|
|||
impl<'tcx> LateLintPass<'tcx> for DisallowedNames {
|
||||
fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
|
||||
if let PatKind::Binding(.., ident, _) = pat.kind
|
||||
&& self.disallow.contains(&ident.name.to_string())
|
||||
&& self.disallow.contains(&ident.name)
|
||||
&& !is_in_test(cx.tcx, pat.hir_id)
|
||||
{
|
||||
span_lint(
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_ast::ast;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
|
|
@ -44,19 +45,20 @@ declare_clippy_lint! {
|
|||
"usage of non-allowed Unicode scripts"
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DisallowedScriptIdents {
|
||||
whitelist: FxHashSet<Script>,
|
||||
}
|
||||
|
||||
impl DisallowedScriptIdents {
|
||||
pub fn new(whitelist: &[String]) -> Self {
|
||||
let whitelist = whitelist
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.filter_map(Script::from_full_name)
|
||||
.collect();
|
||||
Self { whitelist }
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
whitelist: conf
|
||||
.allowed_scripts
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.filter_map(Script::from_full_name)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
use clippy_config::types::DisallowedPath;
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::def_id::DefIdMap;
|
||||
use rustc_hir::{Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
|
|
@ -49,60 +50,56 @@ declare_clippy_lint! {
|
|||
"use of disallowed types"
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DisallowedTypes {
|
||||
conf_disallowed: Vec<DisallowedPath>,
|
||||
def_ids: FxHashMap<DefId, usize>,
|
||||
prim_tys: FxHashMap<PrimTy, usize>,
|
||||
def_ids: DefIdMap<(&'static str, Option<&'static str>)>,
|
||||
prim_tys: FxHashMap<PrimTy, (&'static str, Option<&'static str>)>,
|
||||
}
|
||||
|
||||
impl DisallowedTypes {
|
||||
pub fn new(conf_disallowed: Vec<DisallowedPath>) -> Self {
|
||||
Self {
|
||||
conf_disallowed,
|
||||
def_ids: FxHashMap::default(),
|
||||
prim_tys: FxHashMap::default(),
|
||||
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
let mut def_ids = DefIdMap::default();
|
||||
let mut prim_tys = FxHashMap::default();
|
||||
for x in &conf.disallowed_types {
|
||||
let path: Vec<_> = x.path().split("::").collect::<Vec<_>>();
|
||||
let reason = x.reason();
|
||||
for res in clippy_utils::def_path_res(tcx, &path) {
|
||||
match res {
|
||||
Res::Def(_, id) => {
|
||||
def_ids.insert(id, (x.path(), reason));
|
||||
},
|
||||
Res::PrimTy(ty) => {
|
||||
prim_tys.insert(ty, (x.path(), reason));
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
Self { def_ids, prim_tys }
|
||||
}
|
||||
|
||||
fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) {
|
||||
match res {
|
||||
Res::Def(_, did) => {
|
||||
if let Some(&index) = self.def_ids.get(did) {
|
||||
emit(cx, &cx.tcx.def_path_str(*did), span, &self.conf_disallowed[index]);
|
||||
let (path, reason) = match res {
|
||||
Res::Def(_, did) if let Some(&x) = self.def_ids.get(did) => x,
|
||||
Res::PrimTy(prim) if let Some(&x) = self.prim_tys.get(prim) => x,
|
||||
_ => return,
|
||||
};
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DISALLOWED_TYPES,
|
||||
span,
|
||||
format!("use of a disallowed type `{path}`"),
|
||||
|diag| {
|
||||
if let Some(reason) = reason {
|
||||
diag.note(reason);
|
||||
}
|
||||
},
|
||||
Res::PrimTy(prim) => {
|
||||
if let Some(&index) = self.prim_tys.get(prim) {
|
||||
emit(cx, prim.name_str(), span, &self.conf_disallowed[index]);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
|
||||
fn check_crate(&mut self, cx: &LateContext<'_>) {
|
||||
for (index, conf) in self.conf_disallowed.iter().enumerate() {
|
||||
let segs: Vec<_> = conf.path().split("::").collect();
|
||||
|
||||
for res in clippy_utils::def_path_res(cx, &segs) {
|
||||
match res {
|
||||
Res::Def(_, id) => {
|
||||
self.def_ids.insert(id, index);
|
||||
},
|
||||
Res::PrimTy(ty) => {
|
||||
self.prim_tys.insert(ty, index);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
||||
if let ItemKind::Use(path, UseKind::Single) = &item.kind {
|
||||
for res in &path.res {
|
||||
|
|
@ -121,17 +118,3 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
|
|||
self.check_res_emit(cx, &poly.trait_ref.path.res, poly.trait_ref.path.span);
|
||||
}
|
||||
}
|
||||
|
||||
fn emit(cx: &LateContext<'_>, name: &str, span: Span, conf: &DisallowedPath) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DISALLOWED_TYPES,
|
||||
span,
|
||||
format!("`{name}` is not allowed according to config"),
|
||||
|diag| {
|
||||
if let Some(reason) = conf.reason() {
|
||||
diag.note(reason);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
mod lazy_continuation;
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::attrs::is_doc_hidden;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
|
||||
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
|
||||
|
|
@ -421,17 +422,16 @@ declare_clippy_lint! {
|
|||
"require every line of a paragraph to be indented and marked"
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Documentation {
|
||||
valid_idents: FxHashSet<String>,
|
||||
valid_idents: &'static FxHashSet<String>,
|
||||
check_private_items: bool,
|
||||
}
|
||||
|
||||
impl Documentation {
|
||||
pub fn new(valid_idents: &[String], check_private_items: bool) -> Self {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
valid_idents: valid_idents.iter().cloned().collect(),
|
||||
check_private_items,
|
||||
valid_idents: &conf.doc_valid_idents,
|
||||
check_private_items: conf.check_private_items,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -452,7 +452,7 @@ impl_lint_pass!(Documentation => [
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for Documentation {
|
||||
fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {
|
||||
let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
|
||||
let Some(headers) = check_attrs(cx, self.valid_idents, attrs) else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -40,35 +40,29 @@ declare_lint_pass!(DoubleParens => [DOUBLE_PARENS]);
|
|||
|
||||
impl EarlyLintPass for DoubleParens {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
let msg: &str = "consider removing unnecessary double parentheses";
|
||||
|
||||
match expr.kind {
|
||||
ExprKind::Paren(ref in_paren) => match in_paren.kind {
|
||||
ExprKind::Paren(_) | ExprKind::Tup(_) => {
|
||||
span_lint(cx, DOUBLE_PARENS, expr.span, msg);
|
||||
},
|
||||
_ => {},
|
||||
let span = match &expr.kind {
|
||||
ExprKind::Paren(in_paren) if matches!(in_paren.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => expr.span,
|
||||
ExprKind::Call(_, params)
|
||||
if let [param] = &**params
|
||||
&& let ExprKind::Paren(_) = param.kind =>
|
||||
{
|
||||
param.span
|
||||
},
|
||||
ExprKind::Call(_, ref params) => {
|
||||
if params.len() == 1 {
|
||||
let param = ¶ms[0];
|
||||
if let ExprKind::Paren(_) = param.kind {
|
||||
span_lint(cx, DOUBLE_PARENS, param.span, msg);
|
||||
}
|
||||
}
|
||||
ExprKind::MethodCall(call)
|
||||
if let [arg] = &*call.args
|
||||
&& let ExprKind::Paren(_) = arg.kind =>
|
||||
{
|
||||
arg.span
|
||||
},
|
||||
ExprKind::MethodCall(ref call) => {
|
||||
if let [ref arg] = call.args[..] {
|
||||
if let ExprKind::Paren(_) = arg.kind {
|
||||
span_lint(cx, DOUBLE_PARENS, arg.span, msg);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
_ => return,
|
||||
};
|
||||
if !expr.span.from_expansion() {
|
||||
span_lint(
|
||||
cx,
|
||||
DOUBLE_PARENS,
|
||||
span,
|
||||
"consider removing unnecessary double parentheses",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue