diff options
Diffstat (limited to 'compiler/rustc_lint/src')
| -rw-r--r-- | compiler/rustc_lint/src/array_into_iter.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/builtin.rs | 18 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/context.rs | 7 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/enum_intrinsics_non_enums.rs | 106 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/internal.rs | 59 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/lib.rs | 8 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/unused.rs | 2 |
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