about summary refs log tree commit diff
path: root/compiler/rustc_lint/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_lint/src')
-rw-r--r--compiler/rustc_lint/src/array_into_iter.rs3
-rw-r--r--compiler/rustc_lint/src/builtin.rs18
-rw-r--r--compiler/rustc_lint/src/context.rs7
-rw-r--r--compiler/rustc_lint/src/enum_intrinsics_non_enums.rs106
-rw-r--r--compiler/rustc_lint/src/internal.rs59
-rw-r--r--compiler/rustc_lint/src/lib.rs8
-rw-r--r--compiler/rustc_lint/src/unused.rs2
7 files changed, 194 insertions, 9 deletions
diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs
index d147148ac71..d8883b0e66d 100644
--- a/compiler/rustc_lint/src/array_into_iter.rs
+++ b/compiler/rustc_lint/src/array_into_iter.rs
@@ -134,9 +134,8 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
                     Applicability::MachineApplicable,
                 );
                 if self.for_expr_span == expr.span {
-                    let expr_span = expr.span.ctxt().outer_expn_data().call_site;
                     diag.span_suggestion(
-                        receiver_arg.span.shrink_to_hi().to(expr_span.shrink_to_hi()),
+                        receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
                         "or remove `.into_iter()` to iterate by value",
                         String::new(),
                         Applicability::MaybeIncorrect,
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 57c1c8f3ecb..c228ecb03fd 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -657,6 +657,24 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
             return;
         }
 
+        // If the method is an impl for an item with docs_hidden, don't doc.
+        if method_context(cx, impl_item.hir_id()) == MethodLateContext::PlainImpl {
+            let parent = cx.tcx.hir().get_parent_did(impl_item.hir_id());
+            let impl_ty = cx.tcx.type_of(parent);
+            let outerdef = match impl_ty.kind() {
+                ty::Adt(def, _) => Some(def.did),
+                ty::Foreign(def_id) => Some(*def_id),
+                _ => None,
+            };
+            let is_hidden = match outerdef {
+                Some(id) => cx.tcx.is_doc_hidden(id),
+                None => false,
+            };
+            if is_hidden {
+                return;
+            }
+        }
+
         let (article, desc) = cx.tcx.article_and_description(impl_item.def_id.to_def_id());
         self.check_missing_docs_attrs(cx, impl_item.def_id, impl_item.span, article, desc);
     }
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index d235b220944..e34a92aba25 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -144,7 +144,11 @@ impl LintStore {
         &self.lints
     }
 
-    pub fn get_lint_groups<'t>(&'t self) -> Vec<(&'static str, Vec<LintId>, bool)> {
+    pub fn get_lint_groups<'t>(
+        &'t self,
+    ) -> impl Iterator<Item = (&'static str, Vec<LintId>, bool)> + 't {
+        // This function is not used in a way which observes the order of lints.
+        #[cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))]
         self.lint_groups
             .iter()
             .filter(|(_, LintGroup { depr, .. })| {
@@ -154,7 +158,6 @@ impl LintStore {
             .map(|(k, LintGroup { lint_ids, from_plugin, .. })| {
                 (*k, lint_ids.clone(), *from_plugin)
             })
-            .collect()
     }
 
     pub fn register_early_pass(
diff --git a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
new file mode 100644
index 00000000000..876245747f6
--- /dev/null
+++ b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
@@ -0,0 +1,106 @@
+use crate::{context::LintContext, LateContext, LateLintPass};
+use rustc_hir as hir;
+use rustc_middle::ty::{fold::TypeFoldable, Ty};
+use rustc_span::{symbol::sym, Span};
+
+declare_lint! {
+    /// The `enum_intrinsics_non_enums` lint detects calls to
+    /// intrinsic functions that require an enum ([`core::mem::discriminant`],
+    /// [`core::mem::variant_count`]), but are called with a non-enum type.
+    ///
+    /// [`core::mem::discriminant`]: https://doc.rust-lang.org/core/mem/fn.discriminant.html
+    /// [`core::mem::variant_count`]: https://doc.rust-lang.org/core/mem/fn.variant_count.html
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(enum_intrinsics_non_enums)]
+    /// core::mem::discriminant::<i32>(&123);
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// In order to accept any enum, the `mem::discriminant` and
+    /// `mem::variant_count` functions are generic over a type `T`.
+    /// This makes it technically possible for `T` to be a non-enum,
+    /// in which case the return value is unspecified.
+    ///
+    /// This lint prevents such incorrect usage of these functions.
+    ENUM_INTRINSICS_NON_ENUMS,
+    Deny,
+    "detects calls to `core::mem::discriminant` and `core::mem::variant_count` with non-enum types"
+}
+
+declare_lint_pass!(EnumIntrinsicsNonEnums => [ENUM_INTRINSICS_NON_ENUMS]);
+
+/// Returns `true` if we know for sure that the given type is not an enum. Note that for cases where
+/// the type is generic, we can't be certain if it will be an enum so we have to assume that it is.
+fn is_non_enum(t: Ty<'_>) -> bool {
+    !t.is_enum() && !t.potentially_needs_subst()
+}
+
+fn enforce_mem_discriminant(
+    cx: &LateContext<'_>,
+    func_expr: &hir::Expr<'_>,
+    expr_span: Span,
+    args_span: Span,
+) {
+    let ty_param = cx.typeck_results().node_substs(func_expr.hir_id).type_at(0);
+    if is_non_enum(ty_param) {
+        cx.struct_span_lint(ENUM_INTRINSICS_NON_ENUMS, expr_span, |builder| {
+            builder
+                .build(
+                    "the return value of `mem::discriminant` is \
+                        unspecified when called with a non-enum type",
+                )
+                .span_note(
+                    args_span,
+                    &format!(
+                        "the argument to `discriminant` should be a \
+                            reference to an enum, but it was passed \
+                            a reference to a `{}`, which is not an enum.",
+                        ty_param,
+                    ),
+                )
+                .emit();
+        });
+    }
+}
+
+fn enforce_mem_variant_count(cx: &LateContext<'_>, func_expr: &hir::Expr<'_>, span: Span) {
+    let ty_param = cx.typeck_results().node_substs(func_expr.hir_id).type_at(0);
+    if is_non_enum(ty_param) {
+        cx.struct_span_lint(ENUM_INTRINSICS_NON_ENUMS, span, |builder| {
+            builder
+                .build(
+                    "the return value of `mem::variant_count` is \
+                        unspecified when called with a non-enum type",
+                )
+                .note(&format!(
+                    "the type parameter of `variant_count` should \
+                            be an enum, but it was instantiated with \
+                            the type `{}`, which is not an enum.",
+                    ty_param,
+                ))
+                .emit();
+        });
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for EnumIntrinsicsNonEnums {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
+        if let hir::ExprKind::Call(ref func, ref args) = expr.kind {
+            if let hir::ExprKind::Path(ref qpath) = func.kind {
+                if let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() {
+                    if cx.tcx.is_diagnostic_item(sym::mem_discriminant, def_id) {
+                        enforce_mem_discriminant(cx, func, expr.span, args[0].span);
+                    } else if cx.tcx.is_diagnostic_item(sym::mem_variant_count, def_id) {
+                        enforce_mem_variant_count(cx, func, expr.span);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index 50a0d211a36..e9dcc120200 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -5,10 +5,7 @@ use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}
 use rustc_ast as ast;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
-use rustc_hir::{
-    GenericArg, HirId, Item, ItemKind, MutTy, Mutability, Node, Path, PathSegment, QPath, Ty,
-    TyKind,
-};
+use rustc_hir::*;
 use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::hygiene::{ExpnKind, MacroKind};
@@ -52,6 +49,60 @@ impl LateLintPass<'_> for DefaultHashTypes {
 }
 
 declare_tool_lint! {
+    pub rustc::POTENTIAL_QUERY_INSTABILITY,
+    Allow,
+    "require explicit opt-in when using potentially unstable methods or functions",
+    report_in_external_macro: true
+}
+
+declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY]);
+
+impl LateLintPass<'_> for QueryStability {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        // FIXME(rustdoc): This lint uses typecheck results, causing rustdoc to
+        // error if there are resolution failures.
+        //
+        // As internal lints are currently always run if there are `unstable_options`,
+        // they are added to the lint store of rustdoc. Internal lints are also
+        // not used via the `lint_mod` query. Crate lints run outside of a query
+        // so rustdoc currently doesn't disable them.
+        //
+        // Instead of relying on this, either change crate lints to a query disabled by
+        // rustdoc, only run internal lints if the user is explicitly opting in
+        // or figure out a different way to avoid running lints for rustdoc.
+        if cx.tcx.sess.opts.actually_rustdoc {
+            return;
+        }
+
+        let (def_id, span) = match expr.kind {
+            ExprKind::Path(ref path) if let Some(def_id) = cx.qpath_res(path, expr.hir_id).opt_def_id() => {
+                (def_id, expr.span)
+            }
+            ExprKind::MethodCall(_, span, _, _) if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) => {
+                (def_id, span)
+            },
+            _ => return,
+        };
+
+        let substs = cx.typeck_results().node_substs(expr.hir_id);
+        if let Ok(Some(instance)) = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs) {
+            let def_id = instance.def_id();
+            if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) {
+                cx.struct_span_lint(POTENTIAL_QUERY_INSTABILITY, span, |lint| {
+                    let msg = format!(
+                        "using `{}` can result in unstable query results",
+                        cx.tcx.item_name(def_id)
+                    );
+                    lint.build(&msg)
+                        .note("if you believe this case to be fine, allow this lint and add a comment explaining your rationale")
+                        .emit();
+                })
+            }
+        }
+    }
+}
+
+declare_tool_lint! {
     pub rustc::USAGE_OF_TY_TYKIND,
     Allow,
     "usage of `ty::TyKind` outside of the `ty::sty` module",
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 17eb0054584..f83eaabdfef 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -31,12 +31,14 @@
 #![feature(box_patterns)]
 #![feature(crate_visibility_modifier)]
 #![feature(format_args_capture)]
+#![feature(if_let_guard)]
 #![feature(iter_order_by)]
 #![feature(iter_zip)]
 #![feature(never_type)]
 #![feature(nll)]
 #![feature(control_flow_enum)]
 #![recursion_limit = "256"]
+#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))]
 
 #[macro_use]
 extern crate rustc_middle;
@@ -47,6 +49,7 @@ mod array_into_iter;
 pub mod builtin;
 mod context;
 mod early;
+mod enum_intrinsics_non_enums;
 mod internal;
 mod late;
 mod levels;
@@ -76,6 +79,7 @@ use rustc_span::Span;
 
 use array_into_iter::ArrayIntoIter;
 use builtin::*;
+use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
 use internal::*;
 use methods::*;
 use non_ascii_idents::*;
@@ -168,6 +172,7 @@ macro_rules! late_lint_passes {
                 TemporaryCStringAsPtr: TemporaryCStringAsPtr,
                 NonPanicFmt: NonPanicFmt,
                 NoopMethodCall: NoopMethodCall,
+                EnumIntrinsicsNonEnums: EnumIntrinsicsNonEnums,
                 InvalidAtomicOrdering: InvalidAtomicOrdering,
                 NamedAsmLabels: NamedAsmLabels,
             ]
@@ -481,6 +486,8 @@ fn register_internals(store: &mut LintStore) {
     store.register_early_pass(|| Box::new(LintPassImpl));
     store.register_lints(&DefaultHashTypes::get_lints());
     store.register_late_pass(|| Box::new(DefaultHashTypes));
+    store.register_lints(&QueryStability::get_lints());
+    store.register_late_pass(|| Box::new(QueryStability));
     store.register_lints(&ExistingDocKeyword::get_lints());
     store.register_late_pass(|| Box::new(ExistingDocKeyword));
     store.register_lints(&TyTyKind::get_lints());
@@ -491,6 +498,7 @@ fn register_internals(store: &mut LintStore) {
         None,
         vec![
             LintId::of(DEFAULT_HASH_TYPES),
+            LintId::of(POTENTIAL_QUERY_INSTABILITY),
             LintId::of(USAGE_OF_TY_TYKIND),
             LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO),
             LintId::of(TY_PASS_BY_REFERENCE),
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 48b955e41ac..da1edcf6fe3 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -670,7 +670,7 @@ declare_lint! {
     ///
     /// ### Explanation
     ///
-    /// The parenthesis are not needed, and should be removed. This is the
+    /// The parentheses are not needed, and should be removed. This is the
     /// preferred style for writing these expressions.
     pub(super) UNUSED_PARENS,
     Warn,