summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_lint/src/builtin.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
commitd1b2d29528b7794b41e66fc2136e395a02f8529b (patch)
treea4a17504b260206dec3cf55b2dca82929a348ac2 /compiler/rustc_lint/src/builtin.rs
parentReleasing progress-linux version 1.72.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.tar.xz
rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.zip
Merging upstream version 1.73.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_lint/src/builtin.rs')
-rw-r--r--compiler/rustc_lint/src/builtin.rs704
1 files changed, 137 insertions, 567 deletions
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index b821933e9..4b6917fdf 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -24,24 +24,22 @@ use crate::fluent_generated as fluent;
use crate::{
errors::BuiltinEllipsisInclusiveRangePatterns,
lints::{
- BuiltinAnonymousParams, BuiltinBoxPointers, BuiltinClashingExtern,
- BuiltinClashingExternSub, BuiltinConstNoMangle, BuiltinDeprecatedAttrLink,
- BuiltinDeprecatedAttrLinkSuggestion, BuiltinDeprecatedAttrUsed, BuiltinDerefNullptr,
- BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives,
- BuiltinExplicitOutlivesSuggestion, BuiltinIncompleteFeatures,
- BuiltinIncompleteFeaturesHelp, BuiltinIncompleteFeaturesNote, BuiltinKeywordIdents,
+ BuiltinAnonymousParams, BuiltinBoxPointers, BuiltinConstNoMangle,
+ BuiltinDeprecatedAttrLink, BuiltinDeprecatedAttrLinkSuggestion, BuiltinDeprecatedAttrUsed,
+ BuiltinDerefNullptr, BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives,
+ BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, BuiltinIncompleteFeatures,
+ BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents,
BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns,
BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds,
BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause,
BuiltinUnexpectedCliConfigName, BuiltinUnexpectedCliConfigValue,
- BuiltinUngatedAsyncFnTrackCaller, BuiltinUnnameableTestItems, BuiltinUnpermittedTypeInit,
+ BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit,
BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe,
BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
BuiltinWhileTrue, SuggestChangingAssocTypes,
},
- types::{transparent_newtype_field, CItemKind},
- EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext,
+ EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext,
};
use hir::IsAsync;
use rustc_ast::attr;
@@ -49,29 +47,29 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree};
use rustc_ast::visit::{FnCtxt, FnKind};
use rustc_ast::{self as ast, *};
use rustc_ast_pretty::pprust::{self, expr_to_string};
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{Applicability, DecorateLint, MultiSpan};
use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, GateIssue, Stability};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID};
+use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
use rustc_hir::intravisit::FnKind as HirFnKind;
-use rustc_hir::{Body, FnDecl, ForeignItemKind, GenericParamKind, Node, PatKind, PredicateOrigin};
+use rustc_hir::{Body, FnDecl, GenericParamKind, Node, PatKind, PredicateOrigin};
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::layout::{LayoutError, LayoutOf};
+use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::GenericArgKind;
+use rustc_middle::ty::ToPredicate;
use rustc_middle::ty::TypeVisitableExt;
-use rustc_middle::ty::{self, Instance, Ty, TyCtxt, VariantDef};
+use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef};
use rustc_session::config::ExpectedValues;
use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason};
use rustc_span::edition::Edition;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, InnerSpan, Span};
-use rustc_target::abi::{Abi, FIRST_VARIANT};
+use rustc_target::abi::Abi;
use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt};
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_trait_selection::traits::{self, misc::type_allowed_to_implement_copy};
use crate::nonstandard_style::{method_context, MethodLateContext};
@@ -181,9 +179,11 @@ impl<'tcx> LateLintPass<'tcx> for BoxPointers {
| hir::ItemKind::TyAlias(..)
| hir::ItemKind::Enum(..)
| hir::ItemKind::Struct(..)
- | hir::ItemKind::Union(..) => {
- self.check_heap_type(cx, it.span, cx.tcx.type_of(it.owner_id).subst_identity())
- }
+ | hir::ItemKind::Union(..) => self.check_heap_type(
+ cx,
+ it.span,
+ cx.tcx.type_of(it.owner_id).instantiate_identity(),
+ ),
_ => (),
}
@@ -194,7 +194,7 @@ impl<'tcx> LateLintPass<'tcx> for BoxPointers {
self.check_heap_type(
cx,
field.span,
- cx.tcx.type_of(field.def_id).subst_identity(),
+ cx.tcx.type_of(field.def_id).instantiate_identity(),
);
}
}
@@ -459,10 +459,7 @@ declare_lint! {
report_in_external_macro
}
-pub struct MissingDoc {
- /// Stack of whether `#[doc(hidden)]` is set at each level which has lint attributes.
- doc_hidden_stack: Vec<bool>,
-}
+pub struct MissingDoc;
impl_lint_pass!(MissingDoc => [MISSING_DOCS]);
@@ -491,14 +488,6 @@ fn has_doc(attr: &ast::Attribute) -> bool {
}
impl MissingDoc {
- pub fn new() -> MissingDoc {
- MissingDoc { doc_hidden_stack: vec![false] }
- }
-
- fn doc_hidden(&self) -> bool {
- *self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
- }
-
fn check_missing_docs_attrs(
&self,
cx: &LateContext<'_>,
@@ -512,11 +501,6 @@ impl MissingDoc {
return;
}
- // `#[doc(hidden)]` disables missing_docs check.
- if self.doc_hidden() {
- return;
- }
-
// Only check publicly-visible items, using the result from the privacy pass.
// It's an option so the crate root can also use this function (it doesn't
// have a `NodeId`).
@@ -539,23 +523,6 @@ impl MissingDoc {
}
impl<'tcx> LateLintPass<'tcx> for MissingDoc {
- #[inline]
- fn enter_lint_attrs(&mut self, _cx: &LateContext<'_>, attrs: &[ast::Attribute]) {
- let doc_hidden = self.doc_hidden()
- || attrs.iter().any(|attr| {
- attr.has_name(sym::doc)
- && match attr.meta_item_list() {
- None => false,
- Some(l) => attr::list_contains_name(&l, sym::hidden),
- }
- });
- self.doc_hidden_stack.push(doc_hidden);
- }
-
- fn exit_lint_attrs(&mut self, _: &LateContext<'_>, _attrs: &[ast::Attribute]) {
- self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
- }
-
fn check_crate(&mut self, cx: &LateContext<'_>) {
self.check_missing_docs_attrs(cx, CRATE_DEF_ID, "the", "crate");
}
@@ -591,7 +558,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
// If the method is an impl for an item with docs_hidden, don't doc.
MethodLateContext::PlainImpl => {
let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id());
- let impl_ty = cx.tcx.type_of(parent).subst_identity();
+ let impl_ty = cx.tcx.type_of(parent).instantiate_identity();
let outerdef = match impl_ty.kind() {
ty::Adt(def, _) => Some(def.did()),
ty::Foreign(def_id) => Some(*def_id),
@@ -700,7 +667,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
// and recommending Copy might be a bad idea.
for field in def.all_fields() {
let did = field.did;
- if cx.tcx.type_of(did).subst_identity().is_unsafe_ptr() {
+ if cx.tcx.type_of(did).instantiate_identity().is_unsafe_ptr() {
return;
}
}
@@ -708,6 +675,9 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
if ty.is_copy_modulo_regions(cx.tcx, param_env) {
return;
}
+ if type_implements_negative_copy_modulo_regions(cx.tcx, ty, param_env) {
+ return;
+ }
// We shouldn't recommend implementing `Copy` on stateful things,
// such as iterators.
@@ -743,6 +713,24 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
}
}
+/// Check whether a `ty` has a negative `Copy` implementation, ignoring outlives constraints.
+fn type_implements_negative_copy_modulo_regions<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ ty: Ty<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+) -> bool {
+ let trait_ref = ty::TraitRef::new(tcx, tcx.require_lang_item(hir::LangItem::Copy, None), [ty]);
+ let pred = ty::TraitPredicate { trait_ref, polarity: ty::ImplPolarity::Negative };
+ let obligation = traits::Obligation {
+ cause: traits::ObligationCause::dummy(),
+ param_env,
+ recursion_depth: 0,
+ predicate: ty::Binder::dummy(pred).to_predicate(tcx),
+ };
+
+ tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation)
+}
+
declare_lint! {
/// The `missing_debug_implementations` lint detects missing
/// implementations of [`fmt::Debug`] for public types.
@@ -776,9 +764,7 @@ declare_lint! {
}
#[derive(Default)]
-pub struct MissingDebugImplementations {
- impling_types: Option<LocalDefIdSet>,
-}
+pub(crate) struct MissingDebugImplementations;
impl_lint_pass!(MissingDebugImplementations => [MISSING_DEBUG_IMPLEMENTATIONS]);
@@ -793,25 +779,20 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations {
_ => return,
}
- let Some(debug) = cx.tcx.get_diagnostic_item(sym::Debug) else {
- return
- };
-
- if self.impling_types.is_none() {
- let mut impls = LocalDefIdSet::default();
- cx.tcx.for_each_impl(debug, |d| {
- if let Some(ty_def) = cx.tcx.type_of(d).subst_identity().ty_adt_def() {
- if let Some(def_id) = ty_def.did().as_local() {
- impls.insert(def_id);
- }
- }
- });
-
- self.impling_types = Some(impls);
- debug!("{:?}", self.impling_types);
+ // Avoid listing trait impls if the trait is allowed.
+ let (level, _) = cx.tcx.lint_level_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id());
+ if level == Level::Allow {
+ return;
}
- if !self.impling_types.as_ref().unwrap().contains(&item.owner_id.def_id) {
+ let Some(debug) = cx.tcx.get_diagnostic_item(sym::Debug) else { return };
+
+ let has_impl = cx
+ .tcx
+ .non_blanket_impls_for_ty(debug, cx.tcx.type_of(item.owner_id).instantiate_identity())
+ .next()
+ .is_some();
+ if !has_impl {
cx.emit_spanned_lint(
MISSING_DEBUG_IMPLEMENTATIONS,
item.span,
@@ -1259,8 +1240,8 @@ impl<'tcx> LateLintPass<'tcx> for UnstableFeatures {
declare_lint! {
/// The `ungated_async_fn_track_caller` lint warns when the
- /// `#[track_caller]` attribute is used on an async function, method, or
- /// closure, without enabling the corresponding unstable feature flag.
+ /// `#[track_caller]` attribute is used on an async function
+ /// without enabling the corresponding unstable feature flag.
///
/// ### Example
///
@@ -1274,13 +1255,13 @@ declare_lint! {
/// ### Explanation
///
/// The attribute must be used in conjunction with the
- /// [`closure_track_caller` feature flag]. Otherwise, the `#[track_caller]`
+ /// [`async_fn_track_caller` feature flag]. Otherwise, the `#[track_caller]`
/// annotation will function as a no-op.
///
- /// [`closure_track_caller` feature flag]: https://doc.rust-lang.org/beta/unstable-book/language-features/closure-track-caller.html
+ /// [`async_fn_track_caller` feature flag]: https://doc.rust-lang.org/beta/unstable-book/language-features/async-fn-track-caller.html
UNGATED_ASYNC_FN_TRACK_CALLER,
Warn,
- "enabling track_caller on an async fn is a no-op unless the closure_track_caller feature is enabled"
+ "enabling track_caller on an async fn is a no-op unless the async_fn_track_caller feature is enabled"
}
declare_lint_pass!(
@@ -1300,7 +1281,7 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller {
def_id: LocalDefId,
) {
if fn_kind.asyncness() == IsAsync::Async
- && !cx.tcx.features().closure_track_caller
+ && !cx.tcx.features().async_fn_track_caller
// Now, check if the function has the `#[track_caller]` attribute
&& let Some(attr) = cx.tcx.get_attr(def_id, sym::track_caller)
{
@@ -1458,17 +1439,20 @@ impl TypeAliasBounds {
impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
- let hir::ItemKind::TyAlias(ty, type_alias_generics) = &item.kind else {
- return
- };
- if cx.tcx.type_of(item.owner_id.def_id).skip_binder().has_opaque_types() {
- // Bounds are respected for `type X = impl Trait` and `type X = (impl Trait, Y);`
+ let hir::ItemKind::TyAlias(hir_ty, type_alias_generics) = &item.kind else { return };
+
+ if cx.tcx.features().lazy_type_alias {
+ // Bounds of lazy type aliases are respected.
return;
}
- if cx.tcx.type_of(item.owner_id).skip_binder().has_inherent_projections() {
- // Bounds are respected for `type X = … Type::Inherent …`
+
+ let ty = cx.tcx.type_of(item.owner_id).skip_binder();
+ if ty.has_opaque_types() || 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;`.
return;
}
+
// There must not be a where clause
if type_alias_generics.predicates.is_empty() {
return;
@@ -1493,7 +1477,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
if !where_spans.is_empty() {
let sub = (!suggested_changing_assoc_types).then(|| {
suggested_changing_assoc_types = true;
- SuggestChangingAssocTypes { ty }
+ SuggestChangingAssocTypes { ty: hir_ty }
});
cx.emit_spanned_lint(
TYPE_ALIAS_BOUNDS,
@@ -1509,7 +1493,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
let suggestion = BuiltinTypeAliasGenericBoundsSuggestion { suggestions: inline_sugg };
let sub = (!suggested_changing_assoc_types).then(|| {
suggested_changing_assoc_types = true;
- SuggestChangingAssocTypes { ty }
+ SuggestChangingAssocTypes { ty: hir_ty }
});
cx.emit_spanned_lint(
TYPE_ALIAS_BOUNDS,
@@ -1531,9 +1515,10 @@ declare_lint_pass!(
impl<'tcx> LateLintPass<'tcx> for UnusedBrokenConst {
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
match it.kind {
- hir::ItemKind::Const(_, body_id) => {
+ hir::ItemKind::Const(_, _, body_id) => {
let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id();
// trigger the query once for all constants since that will already report the errors
+ // FIXME(generic_const_items): Does this work properly with generic const items?
cx.tcx.ensure().const_eval_poly(def_id);
}
hir::ItemKind::Static(_, _, body_id) => {
@@ -1718,7 +1703,7 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
let end = expr_to_string(&end);
let replace = match start {
Some(start) => format!("&({}..={})", expr_to_string(&start), end),
- None => format!("&(..={})", end),
+ None => format!("&(..={end})"),
};
if join.edition() >= Edition::Edition2021 {
cx.sess().emit_err(BuiltinEllipsisInclusiveRangePatterns {
@@ -1767,82 +1752,6 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
}
declare_lint! {
- /// The `unnameable_test_items` lint detects [`#[test]`][test] functions
- /// that are not able to be run by the test harness because they are in a
- /// position where they are not nameable.
- ///
- /// [test]: https://doc.rust-lang.org/reference/attributes/testing.html#the-test-attribute
- ///
- /// ### Example
- ///
- /// ```rust,test
- /// fn main() {
- /// #[test]
- /// fn foo() {
- /// // This test will not fail because it does not run.
- /// assert_eq!(1, 2);
- /// }
- /// }
- /// ```
- ///
- /// {{produces}}
- ///
- /// ### Explanation
- ///
- /// In order for the test harness to run a test, the test function must be
- /// located in a position where it can be accessed from the crate root.
- /// This generally means it must be defined in a module, and not anywhere
- /// else such as inside another function. The compiler previously allowed
- /// this without an error, so a lint was added as an alert that a test is
- /// not being used. Whether or not this should be allowed has not yet been
- /// decided, see [RFC 2471] and [issue #36629].
- ///
- /// [RFC 2471]: https://github.com/rust-lang/rfcs/pull/2471#issuecomment-397414443
- /// [issue #36629]: https://github.com/rust-lang/rust/issues/36629
- UNNAMEABLE_TEST_ITEMS,
- Warn,
- "detects an item that cannot be named being marked as `#[test_case]`",
- report_in_external_macro
-}
-
-pub struct UnnameableTestItems {
- boundary: Option<hir::OwnerId>, // Id of the item under which things are not nameable
- items_nameable: bool,
-}
-
-impl_lint_pass!(UnnameableTestItems => [UNNAMEABLE_TEST_ITEMS]);
-
-impl UnnameableTestItems {
- pub fn new() -> Self {
- Self { boundary: None, items_nameable: true }
- }
-}
-
-impl<'tcx> LateLintPass<'tcx> for UnnameableTestItems {
- fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
- if self.items_nameable {
- if let hir::ItemKind::Mod(..) = it.kind {
- } else {
- self.items_nameable = false;
- self.boundary = Some(it.owner_id);
- }
- return;
- }
-
- let attrs = cx.tcx.hir().attrs(it.hir_id());
- if let Some(attr) = attr::find_by_name(attrs, sym::rustc_test_marker) {
- cx.emit_spanned_lint(UNNAMEABLE_TEST_ITEMS, attr.span, BuiltinUnnameableTestItems);
- }
- }
-
- fn check_item_post(&mut self, _cx: &LateContext<'_>, it: &hir::Item<'_>) {
- if !self.items_nameable && self.boundary == Some(it.owner_id) {
- self.items_nameable = true;
- }
- }
-}
-
-declare_lint! {
/// The `keyword_idents` lint detects edition keywords being used as an
/// identifier.
///
@@ -2147,8 +2056,8 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
match predicate.bounded_ty.kind {
hir::TyKind::Path(hir::QPath::Resolved(None, path)) => {
let Res::Def(DefKind::TyParam, def_id) = path.res else {
- continue;
- };
+ continue;
+ };
let index = ty_generics.param_def_id_to_index[&def_id];
(
Self::lifetimes_outliving_type(inferred_outlives, index),
@@ -2297,30 +2206,63 @@ declare_lint! {
"incomplete features that may function improperly in some or all cases"
}
+declare_lint! {
+ /// The `internal_features` lint detects unstable features enabled with
+ /// the [`feature` attribute] that are internal to the compiler or standard
+ /// library.
+ ///
+ /// [`feature` attribute]: https://doc.rust-lang.org/nightly/unstable-book/
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// #![feature(rustc_attrs)]
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// These features are an implementation detail of the compiler and standard
+ /// library and are not supposed to be used in user code.
+ pub INTERNAL_FEATURES,
+ Warn,
+ "internal features are not supposed to be used"
+}
+
declare_lint_pass!(
/// Check for used feature gates in `INCOMPLETE_FEATURES` in `rustc_feature/src/active.rs`.
- IncompleteFeatures => [INCOMPLETE_FEATURES]
+ IncompleteInternalFeatures => [INCOMPLETE_FEATURES, INTERNAL_FEATURES]
);
-impl EarlyLintPass for IncompleteFeatures {
+impl EarlyLintPass for IncompleteInternalFeatures {
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
- let features = cx.sess().features_untracked();
+ let features = cx.builder.features();
features
.declared_lang_features
.iter()
.map(|(name, span, _)| (name, span))
.chain(features.declared_lib_features.iter().map(|(name, span)| (name, span)))
- .filter(|(&name, _)| features.incomplete(name))
+ .filter(|(&name, _)| features.incomplete(name) || features.internal(name))
.for_each(|(&name, &span)| {
let note = rustc_feature::find_feature_issue(name, GateIssue::Language)
- .map(|n| BuiltinIncompleteFeaturesNote { n });
- let help =
- HAS_MIN_FEATURES.contains(&name).then_some(BuiltinIncompleteFeaturesHelp);
- cx.emit_spanned_lint(
- INCOMPLETE_FEATURES,
- span,
- BuiltinIncompleteFeatures { name, note, help },
- );
+ .map(|n| BuiltinFeatureIssueNote { n });
+
+ if features.incomplete(name) {
+ let help =
+ HAS_MIN_FEATURES.contains(&name).then_some(BuiltinIncompleteFeaturesHelp);
+ cx.emit_spanned_lint(
+ INCOMPLETE_FEATURES,
+ span,
+ BuiltinIncompleteFeatures { name, note, help },
+ );
+ } else {
+ cx.emit_spanned_lint(
+ INTERNAL_FEATURES,
+ span,
+ BuiltinInternalFeatures { name, note },
+ );
+ }
});
}
}
@@ -2459,12 +2401,12 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
cx: &LateContext<'tcx>,
ty: Ty<'tcx>,
variant: &VariantDef,
- substs: ty::SubstsRef<'tcx>,
+ args: ty::GenericArgsRef<'tcx>,
descr: &str,
init: InitKind,
) -> Option<InitError> {
let mut field_err = variant.fields.iter().find_map(|field| {
- ty_find_init_error(cx, field.ty(cx.tcx, substs), init).map(|mut err| {
+ ty_find_init_error(cx, field.ty(cx.tcx, args), init).map(|mut err| {
if !field.did.is_local() {
err
} else if err.span.is_none() {
@@ -2541,14 +2483,14 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
Some("raw pointers must be initialized".into())
}
// Recurse and checks for some compound types. (but not unions)
- Adt(adt_def, substs) if !adt_def.is_union() => {
+ Adt(adt_def, args) if !adt_def.is_union() => {
// Handle structs.
if adt_def.is_struct() {
return variant_find_init_error(
cx,
ty,
adt_def.non_enum_variant(),
- substs,
+ args,
"struct field",
init,
);
@@ -2558,7 +2500,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
let mut potential_variants = adt_def.variants().iter().filter_map(|variant| {
let definitely_inhabited = match variant
.inhabited_predicate(cx.tcx, *adt_def)
- .subst(cx.tcx, substs)
+ .instantiate(cx.tcx, args)
.apply_any_module(cx.tcx, cx.param_env)
{
// Entirely skip uninhabited variants.
@@ -2570,7 +2512,10 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
Some((variant, definitely_inhabited))
});
let Some(first_variant) = potential_variants.next() else {
- return Some(InitError::from("enums with no inhabited variants have no valid value").spanned(span));
+ return Some(
+ InitError::from("enums with no inhabited variants have no valid value")
+ .spanned(span),
+ );
};
// So we have at least one potentially inhabited variant. Might we have two?
let Some(second_variant) = potential_variants.next() else {
@@ -2579,7 +2524,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
cx,
ty,
&first_variant.0,
- substs,
+ args,
"field of the only potentially inhabited enum variant",
init,
);
@@ -2649,381 +2594,6 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
}
declare_lint! {
- /// The `clashing_extern_declarations` lint detects when an `extern fn`
- /// has been declared with the same name but different types.
- ///
- /// ### Example
- ///
- /// ```rust
- /// mod m {
- /// extern "C" {
- /// fn foo();
- /// }
- /// }
- ///
- /// extern "C" {
- /// fn foo(_: u32);
- /// }
- /// ```
- ///
- /// {{produces}}
- ///
- /// ### Explanation
- ///
- /// Because two symbols of the same name cannot be resolved to two
- /// different functions at link time, and one function cannot possibly
- /// have two types, a clashing extern declaration is almost certainly a
- /// mistake. Check to make sure that the `extern` definitions are correct
- /// and equivalent, and possibly consider unifying them in one location.
- ///
- /// This lint does not run between crates because a project may have
- /// dependencies which both rely on the same extern function, but declare
- /// it in a different (but valid) way. For example, they may both declare
- /// an opaque type for one or more of the arguments (which would end up
- /// distinct types), or use types that are valid conversions in the
- /// language the `extern fn` is defined in. In these cases, the compiler
- /// can't say that the clashing declaration is incorrect.
- pub CLASHING_EXTERN_DECLARATIONS,
- Warn,
- "detects when an extern fn has been declared with the same name but different types"
-}
-
-pub struct ClashingExternDeclarations {
- /// Map of function symbol name to the first-seen hir id for that symbol name.. If seen_decls
- /// contains an entry for key K, it means a symbol with name K has been seen by this lint and
- /// the symbol should be reported as a clashing declaration.
- // FIXME: Technically, we could just store a &'tcx str here without issue; however, the
- // `impl_lint_pass` macro doesn't currently support lints parametric over a lifetime.
- seen_decls: FxHashMap<Symbol, hir::OwnerId>,
-}
-
-/// Differentiate between whether the name for an extern decl came from the link_name attribute or
-/// just from declaration itself. This is important because we don't want to report clashes on
-/// symbol name if they don't actually clash because one or the other links against a symbol with a
-/// different name.
-enum SymbolName {
- /// The name of the symbol + the span of the annotation which introduced the link name.
- Link(Symbol, Span),
- /// No link name, so just the name of the symbol.
- Normal(Symbol),
-}
-
-impl SymbolName {
- fn get_name(&self) -> Symbol {
- match self {
- SymbolName::Link(s, _) | SymbolName::Normal(s) => *s,
- }
- }
-}
-
-impl ClashingExternDeclarations {
- pub(crate) fn new() -> Self {
- ClashingExternDeclarations { seen_decls: FxHashMap::default() }
- }
-
- /// Insert a new foreign item into the seen set. If a symbol with the same name already exists
- /// for the item, return its HirId without updating the set.
- fn insert(&mut self, tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> Option<hir::OwnerId> {
- let did = fi.owner_id.to_def_id();
- let instance = Instance::new(did, ty::List::identity_for_item(tcx, did));
- let name = Symbol::intern(tcx.symbol_name(instance).name);
- if let Some(&existing_id) = self.seen_decls.get(&name) {
- // Avoid updating the map with the new entry when we do find a collision. We want to
- // make sure we're always pointing to the first definition as the previous declaration.
- // This lets us avoid emitting "knock-on" diagnostics.
- Some(existing_id)
- } else {
- self.seen_decls.insert(name, fi.owner_id)
- }
- }
-
- /// Get the name of the symbol that's linked against for a given extern declaration. That is,
- /// the name specified in a #[link_name = ...] attribute if one was specified, else, just the
- /// symbol's name.
- fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> SymbolName {
- if let Some((overridden_link_name, overridden_link_name_span)) =
- tcx.codegen_fn_attrs(fi.owner_id).link_name.map(|overridden_link_name| {
- // FIXME: Instead of searching through the attributes again to get span
- // information, we could have codegen_fn_attrs also give span information back for
- // where the attribute was defined. However, until this is found to be a
- // bottleneck, this does just fine.
- (overridden_link_name, tcx.get_attr(fi.owner_id, sym::link_name).unwrap().span)
- })
- {
- SymbolName::Link(overridden_link_name, overridden_link_name_span)
- } else {
- SymbolName::Normal(fi.ident.name)
- }
- }
-
- /// Checks whether two types are structurally the same enough that the declarations shouldn't
- /// clash. We need this so we don't emit a lint when two modules both declare an extern struct,
- /// with the same members (as the declarations shouldn't clash).
- fn structurally_same_type<'tcx>(
- cx: &LateContext<'tcx>,
- a: Ty<'tcx>,
- b: Ty<'tcx>,
- ckind: CItemKind,
- ) -> bool {
- fn structurally_same_type_impl<'tcx>(
- seen_types: &mut FxHashSet<(Ty<'tcx>, Ty<'tcx>)>,
- cx: &LateContext<'tcx>,
- a: Ty<'tcx>,
- b: Ty<'tcx>,
- ckind: CItemKind,
- ) -> bool {
- debug!("structurally_same_type_impl(cx, a = {:?}, b = {:?})", a, b);
- let tcx = cx.tcx;
-
- // Given a transparent newtype, reach through and grab the inner
- // type unless the newtype makes the type non-null.
- let non_transparent_ty = |mut ty: Ty<'tcx>| -> Ty<'tcx> {
- loop {
- if let ty::Adt(def, substs) = *ty.kind() {
- let is_transparent = def.repr().transparent();
- let is_non_null = crate::types::nonnull_optimization_guaranteed(tcx, def);
- debug!(
- "non_transparent_ty({:?}) -- type is transparent? {}, type is non-null? {}",
- ty, is_transparent, is_non_null
- );
- if is_transparent && !is_non_null {
- debug_assert_eq!(def.variants().len(), 1);
- let v = &def.variant(FIRST_VARIANT);
- // continue with `ty`'s non-ZST field,
- // otherwise `ty` is a ZST and we can return
- if let Some(field) = transparent_newtype_field(tcx, v) {
- ty = field.ty(tcx, substs);
- continue;
- }
- }
- }
- debug!("non_transparent_ty -> {:?}", ty);
- return ty;
- }
- };
-
- let a = non_transparent_ty(a);
- let b = non_transparent_ty(b);
-
- if !seen_types.insert((a, b)) {
- // We've encountered a cycle. There's no point going any further -- the types are
- // structurally the same.
- true
- } else if a == b {
- // All nominally-same types are structurally same, too.
- true
- } else {
- // Do a full, depth-first comparison between the two.
- use rustc_type_ir::sty::TyKind::*;
- let a_kind = a.kind();
- let b_kind = b.kind();
-
- let compare_layouts = |a, b| -> Result<bool, LayoutError<'tcx>> {
- debug!("compare_layouts({:?}, {:?})", a, b);
- let a_layout = &cx.layout_of(a)?.layout.abi();
- let b_layout = &cx.layout_of(b)?.layout.abi();
- debug!(
- "comparing layouts: {:?} == {:?} = {}",
- a_layout,
- b_layout,
- a_layout == b_layout
- );
- Ok(a_layout == b_layout)
- };
-
- #[allow(rustc::usage_of_ty_tykind)]
- let is_primitive_or_pointer = |kind: &ty::TyKind<'_>| {
- kind.is_primitive() || matches!(kind, RawPtr(..) | Ref(..))
- };
-
- ensure_sufficient_stack(|| {
- match (a_kind, b_kind) {
- (Adt(a_def, _), Adt(b_def, _)) => {
- // We can immediately rule out these types as structurally same if
- // their layouts differ.
- match compare_layouts(a, b) {
- Ok(false) => return false,
- _ => (), // otherwise, continue onto the full, fields comparison
- }
-
- // Grab a flattened representation of all fields.
- let a_fields = a_def.variants().iter().flat_map(|v| v.fields.iter());
- let b_fields = b_def.variants().iter().flat_map(|v| v.fields.iter());
-
- // Perform a structural comparison for each field.
- a_fields.eq_by(
- b_fields,
- |&ty::FieldDef { did: a_did, .. },
- &ty::FieldDef { did: b_did, .. }| {
- structurally_same_type_impl(
- seen_types,
- cx,
- tcx.type_of(a_did).subst_identity(),
- tcx.type_of(b_did).subst_identity(),
- ckind,
- )
- },
- )
- }
- (Array(a_ty, a_const), Array(b_ty, b_const)) => {
- // For arrays, we also check the constness of the type.
- a_const.kind() == b_const.kind()
- && structurally_same_type_impl(seen_types, cx, *a_ty, *b_ty, ckind)
- }
- (Slice(a_ty), Slice(b_ty)) => {
- structurally_same_type_impl(seen_types, cx, *a_ty, *b_ty, ckind)
- }
- (RawPtr(a_tymut), RawPtr(b_tymut)) => {
- a_tymut.mutbl == b_tymut.mutbl
- && structurally_same_type_impl(
- seen_types, cx, a_tymut.ty, b_tymut.ty, ckind,
- )
- }
- (Ref(_a_region, a_ty, a_mut), Ref(_b_region, b_ty, b_mut)) => {
- // For structural sameness, we don't need the region to be same.
- a_mut == b_mut
- && structurally_same_type_impl(seen_types, cx, *a_ty, *b_ty, ckind)
- }
- (FnDef(..), FnDef(..)) => {
- let a_poly_sig = a.fn_sig(tcx);
- let b_poly_sig = b.fn_sig(tcx);
-
- // We don't compare regions, but leaving bound regions around ICEs, so
- // we erase them.
- let a_sig = tcx.erase_late_bound_regions(a_poly_sig);
- let b_sig = tcx.erase_late_bound_regions(b_poly_sig);
-
- (a_sig.abi, a_sig.unsafety, a_sig.c_variadic)
- == (b_sig.abi, b_sig.unsafety, b_sig.c_variadic)
- && a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| {
- structurally_same_type_impl(seen_types, cx, *a, *b, ckind)
- })
- && structurally_same_type_impl(
- seen_types,
- cx,
- a_sig.output(),
- b_sig.output(),
- ckind,
- )
- }
- (Tuple(a_substs), Tuple(b_substs)) => {
- a_substs.iter().eq_by(b_substs.iter(), |a_ty, b_ty| {
- structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind)
- })
- }
- // For these, it's not quite as easy to define structural-sameness quite so easily.
- // For the purposes of this lint, take the conservative approach and mark them as
- // not structurally same.
- (Dynamic(..), Dynamic(..))
- | (Error(..), Error(..))
- | (Closure(..), Closure(..))
- | (Generator(..), Generator(..))
- | (GeneratorWitness(..), GeneratorWitness(..))
- | (Alias(ty::Projection, ..), Alias(ty::Projection, ..))
- | (Alias(ty::Inherent, ..), Alias(ty::Inherent, ..))
- | (Alias(ty::Opaque, ..), Alias(ty::Opaque, ..)) => false,
-
- // These definitely should have been caught above.
- (Bool, Bool) | (Char, Char) | (Never, Never) | (Str, Str) => unreachable!(),
-
- // An Adt and a primitive or pointer type. This can be FFI-safe if non-null
- // enum layout optimisation is being applied.
- (Adt(..), other_kind) | (other_kind, Adt(..))
- if is_primitive_or_pointer(other_kind) =>
- {
- let (primitive, adt) =
- if is_primitive_or_pointer(a.kind()) { (a, b) } else { (b, a) };
- if let Some(ty) = crate::types::repr_nullable_ptr(cx, adt, ckind) {
- ty == primitive
- } else {
- compare_layouts(a, b).unwrap_or(false)
- }
- }
- // Otherwise, just compare the layouts. This may fail to lint for some
- // incompatible types, but at the very least, will stop reads into
- // uninitialised memory.
- _ => compare_layouts(a, b).unwrap_or(false),
- }
- })
- }
- }
- let mut seen_types = FxHashSet::default();
- structurally_same_type_impl(&mut seen_types, cx, a, b, ckind)
- }
-}
-
-impl_lint_pass!(ClashingExternDeclarations => [CLASHING_EXTERN_DECLARATIONS]);
-
-impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations {
- #[instrument(level = "trace", skip(self, cx))]
- fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, this_fi: &hir::ForeignItem<'_>) {
- if let ForeignItemKind::Fn(..) = this_fi.kind {
- let tcx = cx.tcx;
- if let Some(existing_did) = self.insert(tcx, this_fi) {
- let existing_decl_ty = tcx.type_of(existing_did).skip_binder();
- let this_decl_ty = tcx.type_of(this_fi.owner_id).subst_identity();
- debug!(
- "ClashingExternDeclarations: Comparing existing {:?}: {:?} to this {:?}: {:?}",
- existing_did, existing_decl_ty, this_fi.owner_id, this_decl_ty
- );
- // Check that the declarations match.
- if !Self::structurally_same_type(
- cx,
- existing_decl_ty,
- this_decl_ty,
- CItemKind::Declaration,
- ) {
- let orig_fi = tcx.hir().expect_foreign_item(existing_did);
- let orig = Self::name_of_extern_decl(tcx, orig_fi);
-
- // We want to ensure that we use spans for both decls that include where the
- // name was defined, whether that was from the link_name attribute or not.
- let get_relevant_span =
- |fi: &hir::ForeignItem<'_>| match Self::name_of_extern_decl(tcx, fi) {
- SymbolName::Normal(_) => fi.span,
- SymbolName::Link(_, annot_span) => fi.span.to(annot_span),
- };
-
- // Finally, emit the diagnostic.
- let this = this_fi.ident.name;
- let orig = orig.get_name();
- let previous_decl_label = get_relevant_span(orig_fi);
- let mismatch_label = get_relevant_span(this_fi);
- let sub = BuiltinClashingExternSub {
- tcx,
- expected: existing_decl_ty,
- found: this_decl_ty,
- };
- let decorator = if orig == this {
- BuiltinClashingExtern::SameName {
- this,
- orig,
- previous_decl_label,
- mismatch_label,
- sub,
- }
- } else {
- BuiltinClashingExtern::DiffName {
- this,
- orig,
- previous_decl_label,
- mismatch_label,
- sub,
- }
- };
- tcx.emit_spanned_lint(
- CLASHING_EXTERN_DECLARATIONS,
- this_fi.hir_id(),
- get_relevant_span(this_fi),
- decorator,
- );
- }
- }
- }
- }
-}
-
-declare_lint! {
/// The `deref_nullptr` lint detects when an null pointer is dereferenced,
/// which causes [undefined behavior].
///
@@ -3181,7 +2751,7 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
let mut chars = possible_label.chars();
let Some(c) = chars.next() else {
// Empty string means a leading ':' in this section, which is not a label
- break
+ break;
};
// A label starts with an alphabetic character or . or _ and continues with alphanumeric characters, _, or $
if (c.is_alphabetic() || matches!(c, '.' | '_'))