summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_passes
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--compiler/rustc_passes/src/check_attr.rs82
-rw-r--r--compiler/rustc_passes/src/dead.rs22
-rw-r--r--compiler/rustc_passes/src/entry.rs56
-rw-r--r--compiler/rustc_passes/src/errors.rs201
-rw-r--r--compiler/rustc_passes/src/hir_id_validator.rs2
-rw-r--r--compiler/rustc_passes/src/hir_stats.rs540
-rw-r--r--compiler/rustc_passes/src/lib.rs2
-rw-r--r--compiler/rustc_passes/src/lib_features.rs25
-rw-r--r--compiler/rustc_passes/src/liveness.rs220
-rw-r--r--compiler/rustc_passes/src/naked_functions.rs96
-rw-r--r--compiler/rustc_passes/src/stability.rs176
11 files changed, 1017 insertions, 405 deletions
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index a2ac329f2..b3f15ba7c 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -16,6 +16,7 @@ use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID};
use rustc_hir::{MethodKind, Target};
use rustc_middle::hir::nested_filter;
+use rustc_middle::middle::resolve_lifetime::ObjectLifetimeDefault;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::lint::builtin::{
@@ -130,6 +131,7 @@ impl CheckAttrVisitor<'_> {
| sym::rustc_if_this_changed
| sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(&attr),
sym::cmse_nonsecure_entry => self.check_cmse_nonsecure_entry(attr, span, target),
+ sym::collapse_debuginfo => self.check_collapse_debuginfo(attr, span, target),
sym::const_trait => self.check_const_trait(attr, span, target),
sym::must_not_suspend => self.check_must_not_suspend(&attr, span, target),
sym::must_use => self.check_must_use(hir_id, &attr, span, target),
@@ -146,6 +148,7 @@ impl CheckAttrVisitor<'_> {
| sym::stable
| sym::rustc_allowed_through_unstable_modules
| sym::rustc_promotable => self.check_stability_promotable(&attr, span, target),
+ sym::link_ordinal => self.check_link_ordinal(&attr, span, target),
_ => true,
};
is_valid &= attr_is_valid;
@@ -171,6 +174,7 @@ impl CheckAttrVisitor<'_> {
sym::no_implicit_prelude => {
self.check_generic_attr(hir_id, attr, target, &[Target::Mod])
}
+ sym::rustc_object_lifetime_default => self.check_object_lifetime_default(hir_id),
_ => {}
}
@@ -209,7 +213,14 @@ impl CheckAttrVisitor<'_> {
}
// FIXME(@lcnr): this doesn't belong here.
- if matches!(target, Target::Closure | Target::Fn | Target::Method(_) | Target::ForeignFn) {
+ if matches!(
+ target,
+ Target::Closure
+ | Target::Fn
+ | Target::Method(_)
+ | Target::ForeignFn
+ | Target::ForeignStatic
+ ) {
self.tcx.ensure().codegen_fn_attrs(self.tcx.hir().local_def_id(hir_id));
}
@@ -402,6 +413,38 @@ impl CheckAttrVisitor<'_> {
}
}
+ /// Debugging aid for `object_lifetime_default` query.
+ fn check_object_lifetime_default(&self, hir_id: HirId) {
+ let tcx = self.tcx;
+ if let Some(generics) = tcx.hir().get_generics(tcx.hir().local_def_id(hir_id)) {
+ for p in generics.params {
+ let hir::GenericParamKind::Type { .. } = p.kind else { continue };
+ let param_id = tcx.hir().local_def_id(p.hir_id);
+ let default = tcx.object_lifetime_default(param_id);
+ let repr = match default {
+ ObjectLifetimeDefault::Empty => "BaseDefault".to_owned(),
+ ObjectLifetimeDefault::Static => "'static".to_owned(),
+ ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(),
+ ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(),
+ };
+ tcx.sess.span_err(p.span, &repr);
+ }
+ }
+ }
+
+ /// Checks if `#[collapse_debuginfo]` is applied to a macro.
+ fn check_collapse_debuginfo(&self, attr: &Attribute, span: Span, target: Target) -> bool {
+ match target {
+ Target::MacroDef => true,
+ _ => {
+ self.tcx
+ .sess
+ .emit_err(errors::CollapseDebuginfo { attr_span: attr.span, defn_span: span });
+ false
+ }
+ }
+ }
+
/// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid.
fn check_track_caller(
&self,
@@ -632,6 +675,7 @@ impl CheckAttrVisitor<'_> {
| Target::GlobalAsm
| Target::TyAlias
| Target::OpaqueTy
+ | Target::ImplTraitPlaceholder
| Target::Enum
| Target::Variant
| Target::Struct
@@ -644,7 +688,9 @@ impl CheckAttrVisitor<'_> {
| Target::ForeignStatic
| Target::ForeignTy
| Target::GenericParam(..)
- | Target::MacroDef => None,
+ | Target::MacroDef
+ | Target::PatField
+ | Target::ExprField => None,
} {
tcx.sess.emit_err(errors::DocAliasBadLocation { span, attr_str, location });
return false;
@@ -1620,6 +1666,8 @@ impl CheckAttrVisitor<'_> {
E0552,
"unrecognized representation hint"
)
+ .help("valid reprs are `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, \
+ `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`")
.emit();
continue;
@@ -1886,6 +1934,16 @@ impl CheckAttrVisitor<'_> {
}
}
+ fn check_link_ordinal(&self, attr: &Attribute, _span: Span, target: Target) -> bool {
+ match target {
+ Target::ForeignFn | Target::ForeignStatic => true,
+ _ => {
+ self.tcx.sess.emit_err(errors::LinkOrdinal { attr_span: attr.span });
+ false
+ }
+ }
+ }
+
fn check_deprecated(&self, hir_id: HirId, attr: &Attribute, _span: Span, target: Target) {
match target {
Target::Closure | Target::Expression | Target::Statement | Target::Arm => {
@@ -2048,14 +2106,14 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
intravisit::walk_expr(self, expr)
}
- fn visit_variant(
- &mut self,
- variant: &'tcx hir::Variant<'tcx>,
- generics: &'tcx hir::Generics<'tcx>,
- item_id: HirId,
- ) {
+ fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) {
+ self.check_attributes(field.hir_id, field.span, Target::ExprField, None);
+ intravisit::walk_expr_field(self, field)
+ }
+
+ fn visit_variant(&mut self, variant: &'tcx hir::Variant<'tcx>) {
self.check_attributes(variant.id, variant.span, Target::Variant, None);
- intravisit::walk_variant(self, variant, generics, item_id)
+ intravisit::walk_variant(self, variant)
}
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
@@ -2063,6 +2121,11 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
intravisit::walk_param(self, param);
}
+
+ fn visit_pat_field(&mut self, field: &'tcx hir::PatField<'tcx>) {
+ self.check_attributes(field.hir_id, field.span, Target::PatField, None);
+ intravisit::walk_pat_field(self, field);
+ }
}
fn is_c_like_enum(item: &Item<'_>) -> bool {
@@ -2092,6 +2155,7 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
sym::automatically_derived,
sym::start,
sym::rustc_main,
+ sym::unix_sigpipe,
sym::derive,
sym::test,
sym::test_case,
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 1e2fbeb38..f141d7bee 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -226,19 +226,16 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
lhs: &hir::Pat<'_>,
res: Res,
pats: &[hir::Pat<'_>],
- dotdot: Option<usize>,
+ dotdot: hir::DotDotPos,
) {
let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
ty::Adt(adt, _) => adt.variant_of_res(res),
_ => span_bug!(lhs.span, "non-ADT in tuple struct pattern"),
};
- let first_n = pats.iter().enumerate().take(dotdot.unwrap_or(pats.len()));
+ let dotdot = dotdot.as_opt_usize().unwrap_or(pats.len());
+ let first_n = pats.iter().enumerate().take(dotdot);
let missing = variant.fields.len() - pats.len();
- let last_n = pats
- .iter()
- .enumerate()
- .skip(dotdot.unwrap_or(pats.len()))
- .map(|(idx, pat)| (idx + missing, pat));
+ let last_n = pats.iter().enumerate().skip(dotdot).map(|(idx, pat)| (idx + missing, pat));
for (idx, pat) in first_n.chain(last_n) {
if let PatKind::Wild = pat.kind {
continue;
@@ -368,14 +365,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
self.maybe_typeck_results = old_maybe_typeck_results;
}
- fn visit_variant_data(
- &mut self,
- def: &'tcx hir::VariantData<'tcx>,
- _: Symbol,
- _: &hir::Generics<'_>,
- _: hir::HirId,
- _: rustc_span::Span,
- ) {
+ fn visit_variant_data(&mut self, def: &'tcx hir::VariantData<'tcx>) {
let tcx = self.tcx;
let has_repr_c = self.repr_has_repr_c;
let has_repr_simd = self.repr_has_repr_simd;
@@ -457,7 +447,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
}
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
- if let TyKind::OpaqueDef(item_id, _) = ty.kind {
+ if let TyKind::OpaqueDef(item_id, _, _) = ty.kind {
let item = self.tcx.hir().item(item_id);
intravisit::walk_item(self, item);
}
diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs
index 7381019a6..cd10170d3 100644
--- a/compiler/rustc_passes/src/entry.rs
+++ b/compiler/rustc_passes/src/entry.rs
@@ -1,11 +1,11 @@
-use rustc_ast::{entry::EntryPointType, Attribute};
+use rustc_ast::entry::EntryPointType;
use rustc_errors::struct_span_err;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
use rustc_hir::{ItemId, Node, CRATE_HIR_ID};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{DefIdTree, TyCtxt};
-use rustc_session::config::{CrateType, EntryFnType};
+use rustc_session::config::{sigpipe, CrateType, EntryFnType};
use rustc_session::parse::feature_err;
use rustc_span::symbol::sym;
use rustc_span::{Span, Symbol, DUMMY_SP};
@@ -71,14 +71,12 @@ fn entry_point_type(ctxt: &EntryContext<'_>, id: ItemId, at_root: bool) -> Entry
}
}
-fn err_if_attr_found(ctxt: &EntryContext<'_>, attrs: &[Attribute], sym: Symbol) {
+fn err_if_attr_found(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol, details: &str) {
+ let attrs = ctxt.tcx.hir().attrs(id.hir_id());
if let Some(attr) = ctxt.tcx.sess.find_by_name(attrs, sym) {
ctxt.tcx
.sess
- .struct_span_err(
- attr.span,
- &format!("`{}` attribute can only be used on functions", sym),
- )
+ .struct_span_err(attr.span, &format!("`{}` attribute {}", sym, details))
.emit();
}
}
@@ -87,14 +85,16 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
let at_root = ctxt.tcx.opt_local_parent(id.def_id) == Some(CRATE_DEF_ID);
match entry_point_type(ctxt, id, at_root) {
- EntryPointType::None => (),
+ EntryPointType::None => {
+ err_if_attr_found(ctxt, id, sym::unix_sigpipe, "can only be used on `fn main()`");
+ }
_ if !matches!(ctxt.tcx.def_kind(id.def_id), DefKind::Fn) => {
- let attrs = ctxt.tcx.hir().attrs(id.hir_id());
- err_if_attr_found(ctxt, attrs, sym::start);
- err_if_attr_found(ctxt, attrs, sym::rustc_main);
+ err_if_attr_found(ctxt, id, sym::start, "can only be used on functions");
+ err_if_attr_found(ctxt, id, sym::rustc_main, "can only be used on functions");
}
EntryPointType::MainNamed => (),
EntryPointType::OtherMain => {
+ err_if_attr_found(ctxt, id, sym::unix_sigpipe, "can only be used on root `fn main()`");
ctxt.non_main_fns.push(ctxt.tcx.def_span(id.def_id));
}
EntryPointType::RustcMainAttr => {
@@ -116,6 +116,7 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
}
}
EntryPointType::Start => {
+ err_if_attr_found(ctxt, id, sym::unix_sigpipe, "can only be used on `fn main()`");
if ctxt.start_fn.is_none() {
ctxt.start_fn = Some((id.def_id, ctxt.tcx.def_span(id.def_id.to_def_id())));
} else {
@@ -136,8 +137,9 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, EntryFnType)> {
if let Some((def_id, _)) = visitor.start_fn {
Some((def_id.to_def_id(), EntryFnType::Start))
- } else if let Some((def_id, _)) = visitor.attr_main_fn {
- Some((def_id.to_def_id(), EntryFnType::Main))
+ } else if let Some((local_def_id, _)) = visitor.attr_main_fn {
+ let def_id = local_def_id.to_def_id();
+ Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx, def_id) }))
} else {
if let Some(main_def) = tcx.resolutions(()).main_def && let Some(def_id) = main_def.opt_fn_def_id() {
// non-local main imports are handled below
@@ -161,13 +163,39 @@ fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId,
)
.emit();
}
- return Some((def_id, EntryFnType::Main));
+ return Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx, def_id) }));
}
no_main_err(tcx, visitor);
None
}
}
+fn sigpipe(tcx: TyCtxt<'_>, def_id: DefId) -> u8 {
+ if let Some(attr) = tcx.get_attr(def_id, sym::unix_sigpipe) {
+ match (attr.value_str(), attr.meta_item_list()) {
+ (Some(sym::inherit), None) => sigpipe::INHERIT,
+ (Some(sym::sig_ign), None) => sigpipe::SIG_IGN,
+ (Some(sym::sig_dfl), None) => sigpipe::SIG_DFL,
+ (_, Some(_)) => {
+ // Keep going so that `fn emit_malformed_attribute()` can print
+ // an excellent error message
+ sigpipe::DEFAULT
+ }
+ _ => {
+ tcx.sess
+ .struct_span_err(
+ attr.span,
+ "valid values for `#[unix_sigpipe = \"...\"]` are `inherit`, `sig_ign`, or `sig_dfl`",
+ )
+ .emit();
+ sigpipe::DEFAULT
+ }
+ }
+ } else {
+ sigpipe::DEFAULT
+ }
+}
+
fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) {
let sp = tcx.def_span(CRATE_DEF_ID);
if *tcx.sess.parse_sess.reached_eof.borrow() {
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 5feb0e295..96cc8ae98 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -3,37 +3,37 @@ use rustc_macros::{LintDiagnostic, SessionDiagnostic, SessionSubdiagnostic};
use rustc_span::{Span, Symbol};
#[derive(LintDiagnostic)]
-#[lint(passes::outer_crate_level_attr)]
+#[diag(passes::outer_crate_level_attr)]
pub struct OuterCrateLevelAttr;
#[derive(LintDiagnostic)]
-#[lint(passes::inner_crate_level_attr)]
+#[diag(passes::inner_crate_level_attr)]
pub struct InnerCrateLevelAttr;
#[derive(LintDiagnostic)]
-#[lint(passes::ignored_attr_with_macro)]
+#[diag(passes::ignored_attr_with_macro)]
pub struct IgnoredAttrWithMacro<'a> {
pub sym: &'a str,
}
#[derive(LintDiagnostic)]
-#[lint(passes::ignored_attr)]
+#[diag(passes::ignored_attr)]
pub struct IgnoredAttr<'a> {
pub sym: &'a str,
}
#[derive(LintDiagnostic)]
-#[lint(passes::inline_ignored_function_prototype)]
+#[diag(passes::inline_ignored_function_prototype)]
pub struct IgnoredInlineAttrFnProto;
#[derive(LintDiagnostic)]
-#[lint(passes::inline_ignored_constants)]
-#[warn_]
+#[diag(passes::inline_ignored_constants)]
+#[warning]
#[note]
pub struct IgnoredInlineAttrConstants;
#[derive(SessionDiagnostic)]
-#[error(passes::inline_not_fn_or_closure, code = "E0518")]
+#[diag(passes::inline_not_fn_or_closure, code = "E0518")]
pub struct InlineNotFnOrClosure {
#[primary_span]
pub attr_span: Span,
@@ -42,19 +42,19 @@ pub struct InlineNotFnOrClosure {
}
#[derive(LintDiagnostic)]
-#[lint(passes::no_coverage_ignored_function_prototype)]
+#[diag(passes::no_coverage_ignored_function_prototype)]
pub struct IgnoredNoCoverageFnProto;
#[derive(LintDiagnostic)]
-#[lint(passes::no_coverage_propagate)]
+#[diag(passes::no_coverage_propagate)]
pub struct IgnoredNoCoveragePropagate;
#[derive(LintDiagnostic)]
-#[lint(passes::no_coverage_fn_defn)]
+#[diag(passes::no_coverage_fn_defn)]
pub struct IgnoredNoCoverageFnDefn;
#[derive(SessionDiagnostic)]
-#[error(passes::no_coverage_not_coverable, code = "E0788")]
+#[diag(passes::no_coverage_not_coverable, code = "E0788")]
pub struct IgnoredNoCoverageNotCoverable {
#[primary_span]
pub attr_span: Span,
@@ -63,7 +63,7 @@ pub struct IgnoredNoCoverageNotCoverable {
}
#[derive(SessionDiagnostic)]
-#[error(passes::should_be_applied_to_fn)]
+#[diag(passes::should_be_applied_to_fn)]
pub struct AttrShouldBeAppliedToFn {
#[primary_span]
pub attr_span: Span,
@@ -72,14 +72,14 @@ pub struct AttrShouldBeAppliedToFn {
}
#[derive(SessionDiagnostic)]
-#[error(passes::naked_tracked_caller, code = "E0736")]
+#[diag(passes::naked_tracked_caller, code = "E0736")]
pub struct NakedTrackedCaller {
#[primary_span]
pub attr_span: Span,
}
#[derive(SessionDiagnostic)]
-#[error(passes::should_be_applied_to_fn, code = "E0739")]
+#[diag(passes::should_be_applied_to_fn, code = "E0739")]
pub struct TrackedCallerWrongLocation {
#[primary_span]
pub attr_span: Span,
@@ -88,7 +88,7 @@ pub struct TrackedCallerWrongLocation {
}
#[derive(SessionDiagnostic)]
-#[error(passes::should_be_applied_to_struct_enum, code = "E0701")]
+#[diag(passes::should_be_applied_to_struct_enum, code = "E0701")]
pub struct NonExhaustiveWrongLocation {
#[primary_span]
pub attr_span: Span,
@@ -97,7 +97,7 @@ pub struct NonExhaustiveWrongLocation {
}
#[derive(SessionDiagnostic)]
-#[error(passes::should_be_applied_to_trait)]
+#[diag(passes::should_be_applied_to_trait)]
pub struct AttrShouldBeAppliedToTrait {
#[primary_span]
pub attr_span: Span,
@@ -106,11 +106,11 @@ pub struct AttrShouldBeAppliedToTrait {
}
#[derive(LintDiagnostic)]
-#[lint(passes::target_feature_on_statement)]
+#[diag(passes::target_feature_on_statement)]
pub struct TargetFeatureOnStatement;
#[derive(SessionDiagnostic)]
-#[error(passes::should_be_applied_to_static)]
+#[diag(passes::should_be_applied_to_static)]
pub struct AttrShouldBeAppliedToStatic {
#[primary_span]
pub attr_span: Span,
@@ -119,7 +119,7 @@ pub struct AttrShouldBeAppliedToStatic {
}
#[derive(SessionDiagnostic)]
-#[error(passes::doc_expect_str)]
+#[diag(passes::doc_expect_str)]
pub struct DocExpectStr<'a> {
#[primary_span]
pub attr_span: Span,
@@ -127,7 +127,7 @@ pub struct DocExpectStr<'a> {
}
#[derive(SessionDiagnostic)]
-#[error(passes::doc_alias_empty)]
+#[diag(passes::doc_alias_empty)]
pub struct DocAliasEmpty<'a> {
#[primary_span]
pub span: Span,
@@ -135,7 +135,7 @@ pub struct DocAliasEmpty<'a> {
}
#[derive(SessionDiagnostic)]
-#[error(passes::doc_alias_bad_char)]
+#[diag(passes::doc_alias_bad_char)]
pub struct DocAliasBadChar<'a> {
#[primary_span]
pub span: Span,
@@ -144,7 +144,7 @@ pub struct DocAliasBadChar<'a> {
}
#[derive(SessionDiagnostic)]
-#[error(passes::doc_alias_start_end)]
+#[diag(passes::doc_alias_start_end)]
pub struct DocAliasStartEnd<'a> {
#[primary_span]
pub span: Span,
@@ -152,7 +152,7 @@ pub struct DocAliasStartEnd<'a> {
}
#[derive(SessionDiagnostic)]
-#[error(passes::doc_alias_bad_location)]
+#[diag(passes::doc_alias_bad_location)]
pub struct DocAliasBadLocation<'a> {
#[primary_span]
pub span: Span,
@@ -161,7 +161,7 @@ pub struct DocAliasBadLocation<'a> {
}
#[derive(SessionDiagnostic)]
-#[error(passes::doc_alias_not_an_alias)]
+#[diag(passes::doc_alias_not_an_alias)]
pub struct DocAliasNotAnAlias<'a> {
#[primary_span]
pub span: Span,
@@ -169,42 +169,42 @@ pub struct DocAliasNotAnAlias<'a> {
}
#[derive(LintDiagnostic)]
-#[lint(passes::doc_alias_duplicated)]
+#[diag(passes::doc_alias_duplicated)]
pub struct DocAliasDuplicated {
#[label]
pub first_defn: Span,
}
#[derive(SessionDiagnostic)]
-#[error(passes::doc_alias_not_string_literal)]
+#[diag(passes::doc_alias_not_string_literal)]
pub struct DocAliasNotStringLiteral {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
-#[error(passes::doc_alias_malformed)]
+#[diag(passes::doc_alias_malformed)]
pub struct DocAliasMalformed {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
-#[error(passes::doc_keyword_empty_mod)]
+#[diag(passes::doc_keyword_empty_mod)]
pub struct DocKeywordEmptyMod {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
-#[error(passes::doc_keyword_not_mod)]
+#[diag(passes::doc_keyword_not_mod)]
pub struct DocKeywordNotMod {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
-#[error(passes::doc_keyword_invalid_ident)]
+#[diag(passes::doc_keyword_invalid_ident)]
pub struct DocKeywordInvalidIdent {
#[primary_span]
pub span: Span,
@@ -212,21 +212,21 @@ pub struct DocKeywordInvalidIdent {
}
#[derive(SessionDiagnostic)]
-#[error(passes::doc_fake_variadic_not_valid)]
+#[diag(passes::doc_fake_variadic_not_valid)]
pub struct DocFakeVariadicNotValid {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
-#[error(passes::doc_keyword_only_impl)]
+#[diag(passes::doc_keyword_only_impl)]
pub struct DocKeywordOnlyImpl {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
-#[error(passes::doc_inline_conflict)]
+#[diag(passes::doc_inline_conflict)]
#[help]
pub struct DocKeywordConflict {
#[primary_span]
@@ -234,7 +234,7 @@ pub struct DocKeywordConflict {
}
#[derive(LintDiagnostic)]
-#[lint(passes::doc_inline_only_use)]
+#[diag(passes::doc_inline_only_use)]
#[note]
pub struct DocInlineOnlyUse {
#[label]
@@ -244,7 +244,7 @@ pub struct DocInlineOnlyUse {
}
#[derive(SessionDiagnostic)]
-#[error(passes::doc_attr_not_crate_level)]
+#[diag(passes::doc_attr_not_crate_level)]
pub struct DocAttrNotCrateLevel<'a> {
#[primary_span]
pub span: Span,
@@ -252,27 +252,27 @@ pub struct DocAttrNotCrateLevel<'a> {
}
#[derive(LintDiagnostic)]
-#[lint(passes::doc_test_unknown)]
+#[diag(passes::doc_test_unknown)]
pub struct DocTestUnknown {
pub path: String,
}
#[derive(LintDiagnostic)]
-#[lint(passes::doc_test_takes_list)]
+#[diag(passes::doc_test_takes_list)]
pub struct DocTestTakesList;
#[derive(LintDiagnostic)]
-#[lint(passes::doc_primitive)]
+#[diag(passes::doc_primitive)]
pub struct DocPrimitive;
#[derive(LintDiagnostic)]
-#[lint(passes::doc_test_unknown_any)]
+#[diag(passes::doc_test_unknown_any)]
pub struct DocTestUnknownAny {
pub path: String,
}
#[derive(LintDiagnostic)]
-#[lint(passes::doc_test_unknown_spotlight)]
+#[diag(passes::doc_test_unknown_spotlight)]
#[note]
#[note(passes::no_op_note)]
pub struct DocTestUnknownSpotlight {
@@ -282,7 +282,7 @@ pub struct DocTestUnknownSpotlight {
}
#[derive(LintDiagnostic)]
-#[lint(passes::doc_test_unknown_include)]
+#[diag(passes::doc_test_unknown_include)]
pub struct DocTestUnknownInclude {
pub path: String,
pub value: String,
@@ -292,11 +292,11 @@ pub struct DocTestUnknownInclude {
}
#[derive(LintDiagnostic)]
-#[lint(passes::doc_invalid)]
+#[diag(passes::doc_invalid)]
pub struct DocInvalid;
#[derive(SessionDiagnostic)]
-#[error(passes::pass_by_value)]
+#[diag(passes::pass_by_value)]
pub struct PassByValue {
#[primary_span]
pub attr_span: Span,
@@ -305,7 +305,7 @@ pub struct PassByValue {
}
#[derive(SessionDiagnostic)]
-#[error(passes::allow_incoherent_impl)]
+#[diag(passes::allow_incoherent_impl)]
pub struct AllowIncoherentImpl {
#[primary_span]
pub attr_span: Span,
@@ -314,7 +314,7 @@ pub struct AllowIncoherentImpl {
}
#[derive(SessionDiagnostic)]
-#[error(passes::has_incoherent_inherent_impl)]
+#[diag(passes::has_incoherent_inherent_impl)]
pub struct HasIncoherentInherentImpl {
#[primary_span]
pub attr_span: Span,
@@ -323,21 +323,21 @@ pub struct HasIncoherentInherentImpl {
}
#[derive(LintDiagnostic)]
-#[lint(passes::must_use_async)]
+#[diag(passes::must_use_async)]
pub struct MustUseAsync {
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
-#[lint(passes::must_use_no_effect)]
+#[diag(passes::must_use_no_effect)]
pub struct MustUseNoEffect {
pub article: &'static str,
pub target: rustc_hir::Target,
}
#[derive(SessionDiagnostic)]
-#[error(passes::must_not_suspend)]
+#[diag(passes::must_not_suspend)]
pub struct MustNotSuspend {
#[primary_span]
pub attr_span: Span,
@@ -346,24 +346,24 @@ pub struct MustNotSuspend {
}
#[derive(LintDiagnostic)]
-#[lint(passes::cold)]
-#[warn_]
+#[diag(passes::cold)]
+#[warning]
pub struct Cold {
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
-#[lint(passes::link)]
-#[warn_]
+#[diag(passes::link)]
+#[warning]
pub struct Link {
#[label]
pub span: Option<Span>,
}
#[derive(LintDiagnostic)]
-#[lint(passes::link_name)]
-#[warn_]
+#[diag(passes::link_name)]
+#[warning]
pub struct LinkName<'a> {
#[help]
pub attr_span: Option<Span>,
@@ -373,7 +373,7 @@ pub struct LinkName<'a> {
}
#[derive(SessionDiagnostic)]
-#[error(passes::no_link)]
+#[diag(passes::no_link)]
pub struct NoLink {
#[primary_span]
pub attr_span: Span,
@@ -382,7 +382,7 @@ pub struct NoLink {
}
#[derive(SessionDiagnostic)]
-#[error(passes::export_name)]
+#[diag(passes::export_name)]
pub struct ExportName {
#[primary_span]
pub attr_span: Span,
@@ -391,7 +391,7 @@ pub struct ExportName {
}
#[derive(SessionDiagnostic)]
-#[error(passes::rustc_layout_scalar_valid_range_not_struct)]
+#[diag(passes::rustc_layout_scalar_valid_range_not_struct)]
pub struct RustcLayoutScalarValidRangeNotStruct {
#[primary_span]
pub attr_span: Span,
@@ -400,14 +400,14 @@ pub struct RustcLayoutScalarValidRangeNotStruct {
}
#[derive(SessionDiagnostic)]
-#[error(passes::rustc_layout_scalar_valid_range_arg)]
+#[diag(passes::rustc_layout_scalar_valid_range_arg)]
pub struct RustcLayoutScalarValidRangeArg {
#[primary_span]
pub attr_span: Span,
}
#[derive(SessionDiagnostic)]
-#[error(passes::rustc_legacy_const_generics_only)]
+#[diag(passes::rustc_legacy_const_generics_only)]
pub struct RustcLegacyConstGenericsOnly {
#[primary_span]
pub attr_span: Span,
@@ -416,7 +416,7 @@ pub struct RustcLegacyConstGenericsOnly {
}
#[derive(SessionDiagnostic)]
-#[error(passes::rustc_legacy_const_generics_index)]
+#[diag(passes::rustc_legacy_const_generics_index)]
pub struct RustcLegacyConstGenericsIndex {
#[primary_span]
pub attr_span: Span,
@@ -425,7 +425,7 @@ pub struct RustcLegacyConstGenericsIndex {
}
#[derive(SessionDiagnostic)]
-#[error(passes::rustc_legacy_const_generics_index_exceed)]
+#[diag(passes::rustc_legacy_const_generics_index_exceed)]
pub struct RustcLegacyConstGenericsIndexExceed {
#[primary_span]
#[label]
@@ -434,30 +434,30 @@ pub struct RustcLegacyConstGenericsIndexExceed {
}
#[derive(SessionDiagnostic)]
-#[error(passes::rustc_legacy_const_generics_index_negative)]
+#[diag(passes::rustc_legacy_const_generics_index_negative)]
pub struct RustcLegacyConstGenericsIndexNegative {
#[primary_span]
pub invalid_args: Vec<Span>,
}
#[derive(SessionDiagnostic)]
-#[error(passes::rustc_dirty_clean)]
+#[diag(passes::rustc_dirty_clean)]
pub struct RustcDirtyClean {
#[primary_span]
pub span: Span,
}
#[derive(LintDiagnostic)]
-#[lint(passes::link_section)]
-#[warn_]
+#[diag(passes::link_section)]
+#[warning]
pub struct LinkSection {
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
-#[lint(passes::no_mangle_foreign)]
-#[warn_]
+#[diag(passes::no_mangle_foreign)]
+#[warning]
#[note]
pub struct NoMangleForeign {
#[label]
@@ -468,40 +468,40 @@ pub struct NoMangleForeign {
}
#[derive(LintDiagnostic)]
-#[lint(passes::no_mangle)]
-#[warn_]
+#[diag(passes::no_mangle)]
+#[warning]
pub struct NoMangle {
#[label]
pub span: Span,
}
#[derive(SessionDiagnostic)]
-#[error(passes::repr_ident, code = "E0565")]
+#[diag(passes::repr_ident, code = "E0565")]
pub struct ReprIdent {
#[primary_span]
pub span: Span,
}
#[derive(LintDiagnostic)]
-#[lint(passes::repr_conflicting, code = "E0566")]
+#[diag(passes::repr_conflicting, code = "E0566")]
pub struct ReprConflicting;
#[derive(SessionDiagnostic)]
-#[error(passes::used_static)]
+#[diag(passes::used_static)]
pub struct UsedStatic {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
-#[error(passes::used_compiler_linker)]
+#[diag(passes::used_compiler_linker)]
pub struct UsedCompilerLinker {
#[primary_span]
pub spans: Vec<Span>,
}
#[derive(SessionDiagnostic)]
-#[error(passes::allow_internal_unstable)]
+#[diag(passes::allow_internal_unstable)]
pub struct AllowInternalUnstable {
#[primary_span]
pub attr_span: Span,
@@ -510,14 +510,14 @@ pub struct AllowInternalUnstable {
}
#[derive(SessionDiagnostic)]
-#[error(passes::debug_visualizer_placement)]
+#[diag(passes::debug_visualizer_placement)]
pub struct DebugVisualizerPlacement {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
-#[error(passes::debug_visualizer_invalid)]
+#[diag(passes::debug_visualizer_invalid)]
#[note(passes::note_1)]
#[note(passes::note_2)]
#[note(passes::note_3)]
@@ -527,7 +527,7 @@ pub struct DebugVisualizerInvalid {
}
#[derive(SessionDiagnostic)]
-#[error(passes::rustc_allow_const_fn_unstable)]
+#[diag(passes::rustc_allow_const_fn_unstable)]
pub struct RustcAllowConstFnUnstable {
#[primary_span]
pub attr_span: Span,
@@ -536,7 +536,7 @@ pub struct RustcAllowConstFnUnstable {
}
#[derive(SessionDiagnostic)]
-#[error(passes::rustc_std_internal_symbol)]
+#[diag(passes::rustc_std_internal_symbol)]
pub struct RustcStdInternalSymbol {
#[primary_span]
pub attr_span: Span,
@@ -545,35 +545,42 @@ pub struct RustcStdInternalSymbol {
}
#[derive(SessionDiagnostic)]
-#[error(passes::const_trait)]
+#[diag(passes::const_trait)]
pub struct ConstTrait {
#[primary_span]
pub attr_span: Span,
}
#[derive(SessionDiagnostic)]
-#[error(passes::stability_promotable)]
+#[diag(passes::link_ordinal)]
+pub struct LinkOrdinal {
+ #[primary_span]
+ pub attr_span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(passes::stability_promotable)]
pub struct StabilityPromotable {
#[primary_span]
pub attr_span: Span,
}
#[derive(LintDiagnostic)]
-#[lint(passes::deprecated)]
+#[diag(passes::deprecated)]
pub struct Deprecated;
#[derive(LintDiagnostic)]
-#[lint(passes::macro_use)]
+#[diag(passes::macro_use)]
pub struct MacroUse {
pub name: Symbol,
}
#[derive(LintDiagnostic)]
-#[lint(passes::macro_export)]
+#[diag(passes::macro_export)]
pub struct MacroExport;
#[derive(LintDiagnostic)]
-#[lint(passes::plugin_registrar)]
+#[diag(passes::plugin_registrar)]
pub struct PluginRegistrar;
#[derive(SessionSubdiagnostic)]
@@ -587,7 +594,7 @@ pub enum UnusedNote {
}
#[derive(LintDiagnostic)]
-#[lint(passes::unused)]
+#[diag(passes::unused)]
pub struct Unused {
#[suggestion(applicability = "machine-applicable")]
pub attr_span: Span,
@@ -596,7 +603,7 @@ pub struct Unused {
}
#[derive(SessionDiagnostic)]
-#[error(passes::non_exported_macro_invalid_attrs, code = "E0518")]
+#[diag(passes::non_exported_macro_invalid_attrs, code = "E0518")]
pub struct NonExportedMacroInvalidAttrs {
#[primary_span]
#[label]
@@ -604,19 +611,18 @@ pub struct NonExportedMacroInvalidAttrs {
}
#[derive(LintDiagnostic)]
-#[lint(passes::unused_duplicate)]
+#[diag(passes::unused_duplicate)]
pub struct UnusedDuplicate {
- #[primary_span]
#[suggestion(code = "", applicability = "machine-applicable")]
pub this: Span,
#[note]
pub other: Span,
- #[warn_]
+ #[warning]
pub warning: Option<()>,
}
#[derive(SessionDiagnostic)]
-#[error(passes::unused_multiple)]
+#[diag(passes::unused_multiple)]
pub struct UnusedMultiple {
#[primary_span]
#[suggestion(code = "", applicability = "machine-applicable")]
@@ -627,7 +633,7 @@ pub struct UnusedMultiple {
}
#[derive(SessionDiagnostic)]
-#[error(passes::rustc_lint_opt_ty)]
+#[diag(passes::rustc_lint_opt_ty)]
pub struct RustcLintOptTy {
#[primary_span]
pub attr_span: Span,
@@ -636,10 +642,19 @@ pub struct RustcLintOptTy {
}
#[derive(SessionDiagnostic)]
-#[error(passes::rustc_lint_opt_deny_field_access)]
+#[diag(passes::rustc_lint_opt_deny_field_access)]
pub struct RustcLintOptDenyFieldAccess {
#[primary_span]
pub attr_span: Span,
#[label]
pub span: Span,
}
+
+#[derive(SessionDiagnostic)]
+#[diag(passes::collapse_debuginfo)]
+pub struct CollapseDebuginfo {
+ #[primary_span]
+ pub attr_span: Span,
+ #[label]
+ pub defn_span: Span,
+}
diff --git a/compiler/rustc_passes/src/hir_id_validator.rs b/compiler/rustc_passes/src/hir_id_validator.rs
index 212ea9e57..3bb8c0bb4 100644
--- a/compiler/rustc_passes/src/hir_id_validator.rs
+++ b/compiler/rustc_passes/src/hir_id_validator.rs
@@ -103,7 +103,7 @@ impl<'a, 'hir> HirIdValidator<'a, 'hir> {
self.error(|| {
format!(
"ItemLocalIds not assigned densely in {}. \
- Max ItemLocalId = {}, missing IDs = {:?}; seens IDs = {:?}",
+ Max ItemLocalId = {}, missing IDs = {:#?}; seens IDs = {:#?}",
self.hir_map.def_path(owner).to_string_no_crate_verbose(),
max,
missing_items,
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index a3be827a7..0be2fc053 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -21,75 +21,169 @@ enum Id {
None,
}
-struct NodeData {
+struct NodeStats {
count: usize,
size: usize,
}
+impl NodeStats {
+ fn new() -> NodeStats {
+ NodeStats { count: 0, size: 0 }
+ }
+}
+
+struct Node {
+ stats: NodeStats,
+ subnodes: FxHashMap<&'static str, NodeStats>,
+}
+
+impl Node {
+ fn new() -> Node {
+ Node { stats: NodeStats::new(), subnodes: FxHashMap::default() }
+ }
+}
+
+/// This type measures the size of AST and HIR nodes, by implementing the AST
+/// and HIR `Visitor` traits. But we don't measure every visited type because
+/// that could cause double counting.
+///
+/// For example, `ast::Visitor` has `visit_ident`, but `Ident`s are always
+/// stored inline within other AST nodes, so we don't implement `visit_ident`
+/// here. In contrast, we do implement `visit_expr` because `ast::Expr` is
+/// always stored as `P<ast::Expr>`, and every such expression should be
+/// measured separately.
+///
+/// In general, a `visit_foo` method should be implemented here if the
+/// corresponding `Foo` type is always stored on its own, e.g.: `P<Foo>`,
+/// `Box<Foo>`, `Vec<Foo>`, `Box<[Foo]>`.
+///
+/// There are some types in the AST and HIR tree that the visitors do not have
+/// a `visit_*` method for, and so we cannot measure these, which is
+/// unfortunate.
struct StatCollector<'k> {
krate: Option<Map<'k>>,
- data: FxHashMap<&'static str, NodeData>,
+ nodes: FxHashMap<&'static str, Node>,
seen: FxHashSet<Id>,
}
pub fn print_hir_stats(tcx: TyCtxt<'_>) {
let mut collector = StatCollector {
krate: Some(tcx.hir()),
- data: FxHashMap::default(),
+ nodes: FxHashMap::default(),
seen: FxHashSet::default(),
};
tcx.hir().walk_toplevel_module(&mut collector);
tcx.hir().walk_attributes(&mut collector);
- collector.print("HIR STATS");
+ collector.print("HIR STATS", "hir-stats");
}
-pub fn print_ast_stats(krate: &ast::Crate, title: &str) {
+pub fn print_ast_stats(krate: &ast::Crate, title: &str, prefix: &str) {
+ use rustc_ast::visit::Visitor;
+
let mut collector =
- StatCollector { krate: None, data: FxHashMap::default(), seen: FxHashSet::default() };
- ast_visit::walk_crate(&mut collector, krate);
- collector.print(title);
+ StatCollector { krate: None, nodes: FxHashMap::default(), seen: FxHashSet::default() };
+ collector.visit_crate(krate);
+ collector.print(title, prefix);
}
impl<'k> StatCollector<'k> {
- fn record<T>(&mut self, label: &'static str, id: Id, node: &T) {
+ // Record a top-level node.
+ fn record<T>(&mut self, label: &'static str, id: Id, val: &T) {
+ self.record_inner(label, None, id, val);
+ }
+
+ // Record a two-level entry, with a top-level enum type and a variant.
+ fn record_variant<T>(&mut self, label1: &'static str, label2: &'static str, id: Id, val: &T) {
+ self.record_inner(label1, Some(label2), id, val);
+ }
+
+ fn record_inner<T>(
+ &mut self,
+ label1: &'static str,
+ label2: Option<&'static str>,
+ id: Id,
+ val: &T,
+ ) {
if id != Id::None && !self.seen.insert(id) {
return;
}
- let entry = self.data.entry(label).or_insert(NodeData { count: 0, size: 0 });
+ let node = self.nodes.entry(label1).or_insert(Node::new());
+ node.stats.count += 1;
+ node.stats.size = std::mem::size_of_val(val);
- entry.count += 1;
- entry.size = std::mem::size_of_val(node);
+ if let Some(label2) = label2 {
+ let subnode = node.subnodes.entry(label2).or_insert(NodeStats::new());
+ subnode.count += 1;
+ subnode.size = std::mem::size_of_val(val);
+ }
}
- fn print(&self, title: &str) {
- let mut stats: Vec<_> = self.data.iter().collect();
-
- stats.sort_by_key(|&(_, ref d)| d.count * d.size);
+ fn print(&self, title: &str, prefix: &str) {
+ let mut nodes: Vec<_> = self.nodes.iter().collect();
+ nodes.sort_by_key(|&(_, ref node)| node.stats.count * node.stats.size);
- let mut total_size = 0;
+ let total_size = nodes.iter().map(|(_, node)| node.stats.count * node.stats.size).sum();
- eprintln!("\n{}\n", title);
+ eprintln!("{} {}", prefix, title);
+ eprintln!(
+ "{} {:<18}{:>18}{:>14}{:>14}",
+ prefix, "Name", "Accumulated Size", "Count", "Item Size"
+ );
+ eprintln!("{} ----------------------------------------------------------------", prefix);
- eprintln!("{:<18}{:>18}{:>14}{:>14}", "Name", "Accumulated Size", "Count", "Item Size");
- eprintln!("----------------------------------------------------------------");
+ let percent = |m, n| (m * 100) as f64 / n as f64;
- for (label, data) in stats {
+ for (label, node) in nodes {
+ let size = node.stats.count * node.stats.size;
eprintln!(
- "{:<18}{:>18}{:>14}{:>14}",
+ "{} {:<18}{:>10} ({:4.1}%){:>14}{:>14}",
+ prefix,
label,
- to_readable_str(data.count * data.size),
- to_readable_str(data.count),
- to_readable_str(data.size)
+ to_readable_str(size),
+ percent(size, total_size),
+ to_readable_str(node.stats.count),
+ to_readable_str(node.stats.size)
);
-
- total_size += data.count * data.size;
+ if !node.subnodes.is_empty() {
+ let mut subnodes: Vec<_> = node.subnodes.iter().collect();
+ subnodes.sort_by_key(|&(_, ref subnode)| subnode.count * subnode.size);
+
+ for (label, subnode) in subnodes {
+ let size = subnode.count * subnode.size;
+ eprintln!(
+ "{} - {:<18}{:>10} ({:4.1}%){:>14}",
+ prefix,
+ label,
+ to_readable_str(size),
+ percent(size, total_size),
+ to_readable_str(subnode.count),
+ );
+ }
+ }
}
- eprintln!("----------------------------------------------------------------");
- eprintln!("{:<18}{:>18}\n", "Total", to_readable_str(total_size));
+ eprintln!("{} ----------------------------------------------------------------", prefix);
+ eprintln!("{} {:<18}{:>10}", prefix, "Total", to_readable_str(total_size));
+ eprintln!("{}", prefix);
}
}
+// Used to avoid boilerplate for types with many variants.
+macro_rules! record_variants {
+ (
+ ($self:ident, $val:expr, $kind:expr, $id:expr, $mod:ident, $ty:ty, $tykind:ident),
+ [$($variant:ident),*]
+ ) => {
+ match $kind {
+ $(
+ $mod::$tykind::$variant { .. } => {
+ $self.record_variant(stringify!($ty), stringify!($variant), $id, $val)
+ }
+ )*
+ }
+ };
+}
+
impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
fn visit_param(&mut self, param: &'v hir::Param<'v>) {
self.record("Param", Id::Node(param.hir_id), param);
@@ -122,12 +216,46 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
}
fn visit_item(&mut self, i: &'v hir::Item<'v>) {
- self.record("Item", Id::Node(i.hir_id()), i);
+ record_variants!(
+ (self, i, i.kind, Id::Node(i.hir_id()), hir, Item, ItemKind),
+ [
+ ExternCrate,
+ Use,
+ Static,
+ Const,
+ Fn,
+ Macro,
+ Mod,
+ ForeignMod,
+ GlobalAsm,
+ TyAlias,
+ OpaqueTy,
+ Enum,
+ Struct,
+ Union,
+ Trait,
+ TraitAlias,
+ Impl
+ ]
+ );
hir_visit::walk_item(self, i)
}
+ fn visit_body(&mut self, b: &'v hir::Body<'v>) {
+ self.record("Body", Id::None, b);
+ hir_visit::walk_body(self, b);
+ }
+
+ fn visit_mod(&mut self, m: &'v hir::Mod<'v>, _s: Span, n: HirId) {
+ self.record("Mod", Id::None, m);
+ hir_visit::walk_mod(self, m, n)
+ }
+
fn visit_foreign_item(&mut self, i: &'v hir::ForeignItem<'v>) {
- self.record("ForeignItem", Id::Node(i.hir_id()), i);
+ record_variants!(
+ (self, i, i.kind, Id::Node(i.hir_id()), hir, ForeignItem, ForeignItemKind),
+ [Fn, Static, Type]
+ );
hir_visit::walk_foreign_item(self, i)
}
@@ -142,7 +270,10 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
}
fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
- self.record("Stmt", Id::Node(s.hir_id), s);
+ record_variants!(
+ (self, s, s.kind, Id::Node(s.hir_id), hir, Stmt, StmtKind),
+ [Local, Item, Expr, Semi]
+ );
hir_visit::walk_stmt(self, s)
}
@@ -152,50 +283,135 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
}
fn visit_pat(&mut self, p: &'v hir::Pat<'v>) {
- self.record("Pat", Id::Node(p.hir_id), p);
+ record_variants!(
+ (self, p, p.kind, Id::Node(p.hir_id), hir, Pat, PatKind),
+ [Wild, Binding, Struct, TupleStruct, Or, Path, Tuple, Box, Ref, Lit, Range, Slice]
+ );
hir_visit::walk_pat(self, p)
}
- fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
- self.record("Expr", Id::Node(ex.hir_id), ex);
- hir_visit::walk_expr(self, ex)
+ fn visit_pat_field(&mut self, f: &'v hir::PatField<'v>) {
+ self.record("PatField", Id::Node(f.hir_id), f);
+ hir_visit::walk_pat_field(self, f)
+ }
+
+ fn visit_expr(&mut self, e: &'v hir::Expr<'v>) {
+ record_variants!(
+ (self, e, e.kind, Id::Node(e.hir_id), hir, Expr, ExprKind),
+ [
+ Box, ConstBlock, Array, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type,
+ DropTemps, Let, If, Loop, Match, Closure, Block, Assign, AssignOp, Field, Index,
+ Path, AddrOf, Break, Continue, Ret, InlineAsm, Struct, Repeat, Yield, Err
+ ]
+ );
+ hir_visit::walk_expr(self, e)
+ }
+
+ fn visit_let_expr(&mut self, lex: &'v hir::Let<'v>) {
+ self.record("Let", Id::Node(lex.hir_id), lex);
+ hir_visit::walk_let_expr(self, lex)
+ }
+
+ fn visit_expr_field(&mut self, f: &'v hir::ExprField<'v>) {
+ self.record("ExprField", Id::Node(f.hir_id), f);
+ hir_visit::walk_expr_field(self, f)
}
fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
- self.record("Ty", Id::Node(t.hir_id), t);
+ record_variants!(
+ (self, t, t.kind, Id::Node(t.hir_id), hir, Ty, TyKind),
+ [
+ Slice,
+ Array,
+ Ptr,
+ Rptr,
+ BareFn,
+ Never,
+ Tup,
+ Path,
+ OpaqueDef,
+ TraitObject,
+ Typeof,
+ Infer,
+ Err
+ ]
+ );
hir_visit::walk_ty(self, t)
}
+ fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) {
+ self.record("GenericParam", Id::Node(p.hir_id), p);
+ hir_visit::walk_generic_param(self, p)
+ }
+
+ fn visit_generics(&mut self, g: &'v hir::Generics<'v>) {
+ self.record("Generics", Id::None, g);
+ hir_visit::walk_generics(self, g)
+ }
+
+ fn visit_where_predicate(&mut self, p: &'v hir::WherePredicate<'v>) {
+ record_variants!(
+ (self, p, p, Id::None, hir, WherePredicate, WherePredicate),
+ [BoundPredicate, RegionPredicate, EqPredicate]
+ );
+ hir_visit::walk_where_predicate(self, p)
+ }
+
fn visit_fn(
&mut self,
fk: hir_visit::FnKind<'v>,
fd: &'v hir::FnDecl<'v>,
b: hir::BodyId,
- s: Span,
+ _: Span,
id: hir::HirId,
) {
self.record("FnDecl", Id::None, fd);
- hir_visit::walk_fn(self, fk, fd, b, s, id)
+ hir_visit::walk_fn(self, fk, fd, b, id)
}
- fn visit_where_predicate(&mut self, predicate: &'v hir::WherePredicate<'v>) {
- self.record("WherePredicate", Id::None, predicate);
- hir_visit::walk_where_predicate(self, predicate)
+ fn visit_use(&mut self, p: &'v hir::Path<'v>, hir_id: hir::HirId) {
+ // This is `visit_use`, but the type is `Path` so record it that way.
+ self.record("Path", Id::None, p);
+ hir_visit::walk_use(self, p, hir_id)
}
fn visit_trait_item(&mut self, ti: &'v hir::TraitItem<'v>) {
- self.record("TraitItem", Id::Node(ti.hir_id()), ti);
+ record_variants!(
+ (self, ti, ti.kind, Id::Node(ti.hir_id()), hir, TraitItem, TraitItemKind),
+ [Const, Fn, Type]
+ );
hir_visit::walk_trait_item(self, ti)
}
+ fn visit_trait_item_ref(&mut self, ti: &'v hir::TraitItemRef) {
+ self.record("TraitItemRef", Id::Node(ti.id.hir_id()), ti);
+ hir_visit::walk_trait_item_ref(self, ti)
+ }
+
fn visit_impl_item(&mut self, ii: &'v hir::ImplItem<'v>) {
- self.record("ImplItem", Id::Node(ii.hir_id()), ii);
+ record_variants!(
+ (self, ii, ii.kind, Id::Node(ii.hir_id()), hir, ImplItem, ImplItemKind),
+ [Const, Fn, TyAlias]
+ );
hir_visit::walk_impl_item(self, ii)
}
- fn visit_param_bound(&mut self, bounds: &'v hir::GenericBound<'v>) {
- self.record("GenericBound", Id::None, bounds);
- hir_visit::walk_param_bound(self, bounds)
+ fn visit_foreign_item_ref(&mut self, fi: &'v hir::ForeignItemRef) {
+ self.record("ForeignItemRef", Id::Node(fi.id.hir_id()), fi);
+ hir_visit::walk_foreign_item_ref(self, fi)
+ }
+
+ fn visit_impl_item_ref(&mut self, ii: &'v hir::ImplItemRef) {
+ self.record("ImplItemRef", Id::Node(ii.id.hir_id()), ii);
+ hir_visit::walk_impl_item_ref(self, ii)
+ }
+
+ fn visit_param_bound(&mut self, b: &'v hir::GenericBound<'v>) {
+ record_variants!(
+ (self, b, b, Id::None, hir, GenericBound, GenericBound),
+ [Trait, LangItemTrait, Outlives]
+ );
+ hir_visit::walk_param_bound(self, b)
}
fn visit_field_def(&mut self, s: &'v hir::FieldDef<'v>) {
@@ -203,14 +419,22 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
hir_visit::walk_field_def(self, s)
}
- fn visit_variant(
- &mut self,
- v: &'v hir::Variant<'v>,
- g: &'v hir::Generics<'v>,
- item_id: hir::HirId,
- ) {
+ fn visit_variant(&mut self, v: &'v hir::Variant<'v>) {
self.record("Variant", Id::None, v);
- hir_visit::walk_variant(self, v, g, item_id)
+ hir_visit::walk_variant(self, v)
+ }
+
+ fn visit_generic_arg(&mut self, ga: &'v hir::GenericArg<'v>) {
+ record_variants!(
+ (self, ga, ga, Id::Node(ga.hir_id()), hir, GenericArg, GenericArg),
+ [Lifetime, Type, Const, Infer]
+ );
+ match ga {
+ hir::GenericArg::Lifetime(lt) => self.visit_lifetime(lt),
+ hir::GenericArg::Type(ty) => self.visit_ty(ty),
+ hir::GenericArg::Const(ct) => self.visit_anon_const(&ct.value),
+ hir::GenericArg::Infer(inf) => self.visit_infer(inf),
+ }
}
fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
@@ -218,19 +442,19 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
hir_visit::walk_lifetime(self, lifetime)
}
- fn visit_qpath(&mut self, qpath: &'v hir::QPath<'v>, id: hir::HirId, span: Span) {
- self.record("QPath", Id::None, qpath);
- hir_visit::walk_qpath(self, qpath, id, span)
- }
-
fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) {
self.record("Path", Id::None, path);
hir_visit::walk_path(self, path)
}
- fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v hir::PathSegment<'v>) {
+ fn visit_path_segment(&mut self, path_segment: &'v hir::PathSegment<'v>) {
self.record("PathSegment", Id::None, path_segment);
- hir_visit::walk_path_segment(self, path_span, path_segment)
+ hir_visit::walk_path_segment(self, path_segment)
+ }
+
+ fn visit_generic_args(&mut self, ga: &'v hir::GenericArgs<'v>) {
+ self.record("GenericArgs", Id::None, ga);
+ hir_visit::walk_generic_args(self, ga)
}
fn visit_assoc_type_binding(&mut self, type_binding: &'v hir::TypeBinding<'v>) {
@@ -241,16 +465,45 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
self.record("Attribute", Id::Attr(attr.id), attr);
}
+
+ fn visit_inline_asm(&mut self, asm: &'v hir::InlineAsm<'v>, id: HirId) {
+ self.record("InlineAsm", Id::None, asm);
+ hir_visit::walk_inline_asm(self, asm, id);
+ }
}
impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) {
- self.record("ForeignItem", Id::None, i);
+ record_variants!(
+ (self, i, i.kind, Id::None, ast, ForeignItem, ForeignItemKind),
+ [Static, Fn, TyAlias, MacCall]
+ );
ast_visit::walk_foreign_item(self, i)
}
fn visit_item(&mut self, i: &'v ast::Item) {
- self.record("Item", Id::None, i);
+ record_variants!(
+ (self, i, i.kind, Id::None, ast, Item, ItemKind),
+ [
+ ExternCrate,
+ Use,
+ Static,
+ Const,
+ Fn,
+ Mod,
+ ForeignMod,
+ GlobalAsm,
+ TyAlias,
+ Enum,
+ Struct,
+ Union,
+ Trait,
+ TraitAlias,
+ Impl,
+ MacCall,
+ MacroDef
+ ]
+ );
ast_visit::walk_item(self, i)
}
@@ -265,47 +518,119 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
}
fn visit_stmt(&mut self, s: &'v ast::Stmt) {
- self.record("Stmt", Id::None, s);
+ record_variants!(
+ (self, s, s.kind, Id::None, ast, Stmt, StmtKind),
+ [Local, Item, Expr, Semi, Empty, MacCall]
+ );
ast_visit::walk_stmt(self, s)
}
+ fn visit_param(&mut self, p: &'v ast::Param) {
+ self.record("Param", Id::None, p);
+ ast_visit::walk_param(self, p)
+ }
+
fn visit_arm(&mut self, a: &'v ast::Arm) {
self.record("Arm", Id::None, a);
ast_visit::walk_arm(self, a)
}
fn visit_pat(&mut self, p: &'v ast::Pat) {
- self.record("Pat", Id::None, p);
+ record_variants!(
+ (self, p, p.kind, Id::None, ast, Pat, PatKind),
+ [
+ Wild,
+ Ident,
+ Struct,
+ TupleStruct,
+ Or,
+ Path,
+ Tuple,
+ Box,
+ Ref,
+ Lit,
+ Range,
+ Slice,
+ Rest,
+ Paren,
+ MacCall
+ ]
+ );
ast_visit::walk_pat(self, p)
}
- fn visit_expr(&mut self, ex: &'v ast::Expr) {
- self.record("Expr", Id::None, ex);
- ast_visit::walk_expr(self, ex)
+ fn visit_expr(&mut self, e: &'v ast::Expr) {
+ record_variants!(
+ (self, e, e.kind, Id::None, ast, Expr, ExprKind),
+ [
+ Box, Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let,
+ If, While, ForLoop, Loop, Match, Closure, Block, Async, Await, TryBlock, Assign,
+ AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
+ InlineAsm, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, Err
+ ]
+ );
+ ast_visit::walk_expr(self, e)
}
fn visit_ty(&mut self, t: &'v ast::Ty) {
- self.record("Ty", Id::None, t);
+ record_variants!(
+ (self, t, t.kind, Id::None, ast, Ty, TyKind),
+ [
+ Slice,
+ Array,
+ Ptr,
+ Rptr,
+ BareFn,
+ Never,
+ Tup,
+ Path,
+ TraitObject,
+ ImplTrait,
+ Paren,
+ Typeof,
+ Infer,
+ ImplicitSelf,
+ MacCall,
+ Err,
+ CVarArgs
+ ]
+ );
+
ast_visit::walk_ty(self, t)
}
- fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, s: Span, _: NodeId) {
+ fn visit_generic_param(&mut self, g: &'v ast::GenericParam) {
+ self.record("GenericParam", Id::None, g);
+ ast_visit::walk_generic_param(self, g)
+ }
+
+ fn visit_where_predicate(&mut self, p: &'v ast::WherePredicate) {
+ record_variants!(
+ (self, p, p, Id::None, ast, WherePredicate, WherePredicate),
+ [BoundPredicate, RegionPredicate, EqPredicate]
+ );
+ ast_visit::walk_where_predicate(self, p)
+ }
+
+ fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, _: Span, _: NodeId) {
self.record("FnDecl", Id::None, fk.decl());
- ast_visit::walk_fn(self, fk, s)
+ ast_visit::walk_fn(self, fk)
}
- fn visit_assoc_item(&mut self, item: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) {
- let label = match ctxt {
- ast_visit::AssocCtxt::Trait => "TraitItem",
- ast_visit::AssocCtxt::Impl => "ImplItem",
- };
- self.record(label, Id::None, item);
- ast_visit::walk_assoc_item(self, item, ctxt);
+ fn visit_assoc_item(&mut self, i: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) {
+ record_variants!(
+ (self, i, i.kind, Id::None, ast, AssocItem, AssocItemKind),
+ [Const, Fn, TyAlias, MacCall]
+ );
+ ast_visit::walk_assoc_item(self, i, ctxt);
}
- fn visit_param_bound(&mut self, bounds: &'v ast::GenericBound, _ctxt: BoundKind) {
- self.record("GenericBound", Id::None, bounds);
- ast_visit::walk_param_bound(self, bounds)
+ fn visit_param_bound(&mut self, b: &'v ast::GenericBound, _ctxt: BoundKind) {
+ record_variants!(
+ (self, b, b, Id::None, ast, GenericBound, GenericBound),
+ [Trait, Outlives]
+ );
+ ast_visit::walk_param_bound(self, b)
}
fn visit_field_def(&mut self, s: &'v ast::FieldDef) {
@@ -318,27 +643,52 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
ast_visit::walk_variant(self, v)
}
- fn visit_lifetime(&mut self, lifetime: &'v ast::Lifetime, _: ast_visit::LifetimeCtxt) {
- self.record("Lifetime", Id::None, lifetime);
- ast_visit::walk_lifetime(self, lifetime)
+ // `UseTree` has one inline use (in `ast::ItemKind::Use`) and one
+ // non-inline use (in `ast::UseTreeKind::Nested). The former case is more
+ // common, so we don't implement `visit_use_tree` and tolerate the missed
+ // coverage in the latter case.
+
+ // `PathSegment` has one inline use (in `ast::ExprKind::MethodCall`) and
+ // one non-inline use (in `ast::Path::segments`). The latter case is more
+ // common than the former case, so we implement this visitor and tolerate
+ // the double counting in the former case.
+ fn visit_path_segment(&mut self, path_segment: &'v ast::PathSegment) {
+ self.record("PathSegment", Id::None, path_segment);
+ ast_visit::walk_path_segment(self, path_segment)
}
- fn visit_mac_call(&mut self, mac: &'v ast::MacCall) {
- self.record("MacCall", Id::None, mac);
- ast_visit::walk_mac(self, mac)
+ // `GenericArgs` has one inline use (in `ast::AssocConstraint::gen_args`) and one
+ // non-inline use (in `ast::PathSegment::args`). The latter case is more
+ // common, so we implement `visit_generic_args` and tolerate the double
+ // counting in the former case.
+ fn visit_generic_args(&mut self, g: &'v ast::GenericArgs) {
+ record_variants!(
+ (self, g, g, Id::None, ast, GenericArgs, GenericArgs),
+ [AngleBracketed, Parenthesized]
+ );
+ ast_visit::walk_generic_args(self, g)
}
- fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v ast::PathSegment) {
- self.record("PathSegment", Id::None, path_segment);
- ast_visit::walk_path_segment(self, path_span, path_segment)
+ fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
+ record_variants!(
+ (self, attr, attr.kind, Id::None, ast, Attribute, AttrKind),
+ [Normal, DocComment]
+ );
+ ast_visit::walk_attribute(self, attr)
}
- fn visit_assoc_constraint(&mut self, constraint: &'v ast::AssocConstraint) {
- self.record("AssocConstraint", Id::None, constraint);
- ast_visit::walk_assoc_constraint(self, constraint)
+ fn visit_expr_field(&mut self, f: &'v ast::ExprField) {
+ self.record("ExprField", Id::None, f);
+ ast_visit::walk_expr_field(self, f)
}
- fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
- self.record("Attribute", Id::None, attr);
+ fn visit_crate(&mut self, krate: &'v ast::Crate) {
+ self.record("Crate", Id::None, krate);
+ ast_visit::walk_crate(self, krate)
+ }
+
+ fn visit_inline_asm(&mut self, asm: &'v ast::InlineAsm) {
+ self.record("InlineAsm", Id::None, asm);
+ ast_visit::walk_inline_asm(self, asm)
}
}
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs
index 7b2f83958..39ebb8db2 100644
--- a/compiler/rustc_passes/src/lib.rs
+++ b/compiler/rustc_passes/src/lib.rs
@@ -8,7 +8,7 @@
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(iter_intersperse)]
#![feature(let_chains)]
-#![feature(let_else)]
+#![cfg_attr(bootstrap, feature(let_else))]
#![feature(map_try_insert)]
#![feature(min_specialization)]
#![feature(try_blocks)]
diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs
index e05994f13..04173c792 100644
--- a/compiler/rustc_passes/src/lib_features.rs
+++ b/compiler/rustc_passes/src/lib_features.rs
@@ -5,6 +5,7 @@
//! collect them instead.
use rustc_ast::{Attribute, MetaItemKind};
+use rustc_attr::{rust_version_symbol, VERSION_PLACEHOLDER};
use rustc_errors::struct_span_err;
use rustc_hir::intravisit::Visitor;
use rustc_middle::hir::nested_filter;
@@ -29,11 +30,16 @@ impl<'tcx> LibFeatureCollector<'tcx> {
}
fn extract(&self, attr: &Attribute) -> Option<(Symbol, Option<Symbol>, Span)> {
- let stab_attrs =
- [sym::stable, sym::unstable, sym::rustc_const_stable, sym::rustc_const_unstable];
+ let stab_attrs = [
+ sym::stable,
+ sym::unstable,
+ sym::rustc_const_stable,
+ sym::rustc_const_unstable,
+ sym::rustc_default_body_unstable,
+ ];
// Find a stability attribute: one of #[stable(…)], #[unstable(…)],
- // #[rustc_const_stable(…)], or #[rustc_const_unstable(…)].
+ // #[rustc_const_stable(…)], #[rustc_const_unstable(…)] or #[rustc_default_body_unstable].
if let Some(stab_attr) = stab_attrs.iter().find(|stab_attr| attr.has_name(**stab_attr)) {
let meta_kind = attr.meta_kind();
if let Some(MetaItemKind::List(ref metas)) = meta_kind {
@@ -49,12 +55,21 @@ impl<'tcx> LibFeatureCollector<'tcx> {
}
}
}
+
+ if let Some(s) = since && s.as_str() == VERSION_PLACEHOLDER {
+ since = Some(rust_version_symbol());
+ }
+
if let Some(feature) = feature {
// This additional check for stability is to make sure we
// don't emit additional, irrelevant errors for malformed
// attributes.
- let is_unstable =
- matches!(*stab_attr, sym::unstable | sym::rustc_const_unstable);
+ let is_unstable = matches!(
+ *stab_attr,
+ sym::unstable
+ | sym::rustc_const_unstable
+ | sym::rustc_default_body_unstable
+ );
if since.is_some() || is_unstable {
return Some((feature, since, attr.span));
}
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index 461dd52b9..6a4cd79cd 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -89,16 +89,15 @@ use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::*;
-use rustc_hir::def_id::LocalDefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{Expr, HirId, HirIdMap, HirIdSet};
use rustc_index::vec::IndexVec;
-use rustc_middle::hir::nested_filter;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, DefIdTree, RootVariableMinCaptureList, Ty, TyCtxt};
use rustc_session::lint;
use rustc_span::symbol::{kw, sym, Symbol};
-use rustc_span::Span;
+use rustc_span::{BytePos, Span};
use std::collections::VecDeque;
use std::io;
@@ -139,12 +138,54 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String {
}
}
-fn check_mod_liveness(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
- tcx.hir().visit_item_likes_in_module(module_def_id, &mut IrMaps::new(tcx));
+fn check_liveness(tcx: TyCtxt<'_>, def_id: DefId) {
+ let local_def_id = match def_id.as_local() {
+ None => return,
+ Some(def_id) => def_id,
+ };
+
+ // Don't run unused pass for #[derive()]
+ let parent = tcx.local_parent(local_def_id);
+ if let DefKind::Impl = tcx.def_kind(parent)
+ && tcx.has_attr(parent.to_def_id(), sym::automatically_derived)
+ {
+ return;
+ }
+
+ // Don't run unused pass for #[naked]
+ if tcx.has_attr(def_id, sym::naked) {
+ return;
+ }
+
+ let mut maps = IrMaps::new(tcx);
+ let body_id = tcx.hir().body_owned_by(local_def_id);
+ let hir_id = tcx.hir().body_owner(body_id);
+ let body = tcx.hir().body(body_id);
+
+ if let Some(upvars) = tcx.upvars_mentioned(def_id) {
+ for &var_hir_id in upvars.keys() {
+ let var_name = tcx.hir().name(var_hir_id);
+ maps.add_variable(Upvar(var_hir_id, var_name));
+ }
+ }
+
+ // gather up the various local variables, significant expressions,
+ // and so forth:
+ maps.visit_body(body);
+
+ // compute liveness
+ let mut lsets = Liveness::new(&mut maps, local_def_id);
+ let entry_ln = lsets.compute(&body, hir_id);
+ lsets.log_liveness(entry_ln, body_id.hir_id);
+
+ // check for various error conditions
+ lsets.visit_body(body);
+ lsets.warn_about_unused_upvars(entry_ln);
+ lsets.warn_about_unused_args(body, entry_ln);
}
pub fn provide(providers: &mut Providers) {
- *providers = Providers { check_mod_liveness, ..*providers };
+ *providers = Providers { check_liveness, ..*providers };
}
// ______________________________________________________________________
@@ -188,6 +229,19 @@ enum VarKind {
Upvar(HirId, Symbol),
}
+struct CollectLitsVisitor<'tcx> {
+ lit_exprs: Vec<&'tcx hir::Expr<'tcx>>,
+}
+
+impl<'tcx> Visitor<'tcx> for CollectLitsVisitor<'tcx> {
+ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+ if let hir::ExprKind::Lit(_) = expr.kind {
+ self.lit_exprs.push(expr);
+ }
+ intravisit::walk_expr(self, expr);
+ }
+}
+
struct IrMaps<'tcx> {
tcx: TyCtxt<'tcx>,
live_node_map: HirIdMap<LiveNode>,
@@ -316,56 +370,6 @@ impl<'tcx> IrMaps<'tcx> {
}
impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
- type NestedFilter = nested_filter::OnlyBodies;
-
- fn nested_visit_map(&mut self) -> Self::Map {
- self.tcx.hir()
- }
-
- fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) {
- debug!("visit_body {:?}", body.id());
-
- // swap in a new set of IR maps for this body
- let mut maps = IrMaps::new(self.tcx);
- let hir_id = maps.tcx.hir().body_owner(body.id());
- let local_def_id = maps.tcx.hir().local_def_id(hir_id);
- let def_id = local_def_id.to_def_id();
-
- // Don't run unused pass for #[derive()]
- let parent = self.tcx.local_parent(local_def_id);
- if let DefKind::Impl = self.tcx.def_kind(parent)
- && self.tcx.has_attr(parent.to_def_id(), sym::automatically_derived)
- {
- return;
- }
-
- // Don't run unused pass for #[naked]
- if self.tcx.has_attr(def_id, sym::naked) {
- return;
- }
-
- if let Some(upvars) = maps.tcx.upvars_mentioned(def_id) {
- for &var_hir_id in upvars.keys() {
- let var_name = maps.tcx.hir().name(var_hir_id);
- maps.add_variable(Upvar(var_hir_id, var_name));
- }
- }
-
- // gather up the various local variables, significant expressions,
- // and so forth:
- intravisit::walk_body(&mut maps, body);
-
- // compute liveness
- let mut lsets = Liveness::new(&mut maps, local_def_id);
- let entry_ln = lsets.compute(&body, hir_id);
- lsets.log_liveness(entry_ln, body.id().hir_id);
-
- // check for various error conditions
- lsets.visit_body(body);
- lsets.warn_about_unused_upvars(entry_ln);
- lsets.warn_about_unused_args(body, entry_ln);
- }
-
fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
self.add_from_pat(&local.pat);
if local.els.is_some() {
@@ -1035,9 +1039,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
self.propagate_through_expr(&f, succ)
}
- hir::ExprKind::MethodCall(.., ref args, _) => {
+ hir::ExprKind::MethodCall(.., receiver, ref args, _) => {
let succ = self.check_is_ty_uninhabited(expr, succ);
- self.propagate_through_exprs(args, succ)
+ let succ = self.propagate_through_exprs(args, succ);
+ self.propagate_through_expr(receiver, succ)
}
hir::ExprKind::Tup(ref exprs) => self.propagate_through_exprs(exprs, succ),
@@ -1342,7 +1347,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
- self.check_unused_vars_in_pat(&local.pat, None, |spans, hir_id, ln, var| {
+ self.check_unused_vars_in_pat(&local.pat, None, None, |spans, hir_id, ln, var| {
if local.init.is_some() {
self.warn_about_dead_assign(spans, hir_id, ln, var);
}
@@ -1357,7 +1362,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
}
fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
- self.check_unused_vars_in_pat(&arm.pat, None, |_, _, _, _| {});
+ self.check_unused_vars_in_pat(&arm.pat, None, None, |_, _, _, _| {});
intravisit::walk_arm(self, arm);
}
}
@@ -1396,7 +1401,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) {
}
hir::ExprKind::Let(let_expr) => {
- this.check_unused_vars_in_pat(let_expr.pat, None, |_, _, _, _| {});
+ this.check_unused_vars_in_pat(let_expr.pat, None, None, |_, _, _, _| {});
}
// no correctness conditions related to liveness
@@ -1517,13 +1522,18 @@ impl<'tcx> Liveness<'_, 'tcx> {
fn warn_about_unused_args(&self, body: &hir::Body<'_>, entry_ln: LiveNode) {
for p in body.params {
- self.check_unused_vars_in_pat(&p.pat, Some(entry_ln), |spans, hir_id, ln, var| {
- if !self.live_on_entry(ln, var) {
- self.report_unused_assign(hir_id, spans, var, |name| {
- format!("value passed to `{}` is never read", name)
- });
- }
- });
+ self.check_unused_vars_in_pat(
+ &p.pat,
+ Some(entry_ln),
+ Some(body),
+ |spans, hir_id, ln, var| {
+ if !self.live_on_entry(ln, var) {
+ self.report_unused_assign(hir_id, spans, var, |name| {
+ format!("value passed to `{}` is never read", name)
+ });
+ }
+ },
+ );
}
}
@@ -1531,6 +1541,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
&self,
pat: &hir::Pat<'_>,
entry_ln: Option<LiveNode>,
+ opt_body: Option<&hir::Body<'_>>,
on_used_on_entry: impl Fn(Vec<Span>, HirId, LiveNode, Variable),
) {
// In an or-pattern, only consider the variable; any later patterns must have the same
@@ -1549,6 +1560,8 @@ impl<'tcx> Liveness<'_, 'tcx> {
.or_insert_with(|| (ln, var, vec![id_and_sp]));
});
+ let can_remove = matches!(&pat.kind, hir::PatKind::Struct(_, _, true));
+
for (_, (ln, var, hir_ids_and_spans)) in vars {
if self.used_on_entry(ln, var) {
let id = hir_ids_and_spans[0].0;
@@ -1556,16 +1569,20 @@ impl<'tcx> Liveness<'_, 'tcx> {
hir_ids_and_spans.into_iter().map(|(_, _, ident_span)| ident_span).collect();
on_used_on_entry(spans, id, ln, var);
} else {
- self.report_unused(hir_ids_and_spans, ln, var);
+ self.report_unused(hir_ids_and_spans, ln, var, can_remove, pat, opt_body);
}
}
}
+ #[instrument(skip(self), level = "INFO")]
fn report_unused(
&self,
hir_ids_and_spans: Vec<(HirId, Span, Span)>,
ln: LiveNode,
var: Variable,
+ can_remove: bool,
+ pat: &hir::Pat<'_>,
+ opt_body: Option<&hir::Body<'_>>,
) {
let first_hir_id = hir_ids_and_spans[0].0;
@@ -1590,6 +1607,32 @@ impl<'tcx> Liveness<'_, 'tcx> {
.emit();
},
)
+ } else if can_remove {
+ self.ir.tcx.struct_span_lint_hir(
+ lint::builtin::UNUSED_VARIABLES,
+ first_hir_id,
+ hir_ids_and_spans.iter().map(|(_, pat_span, _)| *pat_span).collect::<Vec<_>>(),
+ |lint| {
+ let mut err = lint.build(&format!("unused variable: `{}`", name));
+ err.multipart_suggestion(
+ "try removing the field",
+ hir_ids_and_spans
+ .iter()
+ .map(|(_, pat_span, _)| {
+ let span = self
+ .ir
+ .tcx
+ .sess
+ .source_map()
+ .span_extend_to_next_char(*pat_span, ',', true);
+ (span.with_hi(BytePos(span.hi().0 + 1)), String::new())
+ })
+ .collect(),
+ Applicability::MachineApplicable,
+ );
+ err.emit();
+ },
+ );
} else {
let (shorthands, non_shorthands): (Vec<_>, Vec<_>) =
hir_ids_and_spans.iter().copied().partition(|(hir_id, _, ident_span)| {
@@ -1643,6 +1686,9 @@ impl<'tcx> Liveness<'_, 'tcx> {
.collect::<Vec<_>>(),
|lint| {
let mut err = lint.build(&format!("unused variable: `{}`", name));
+ if self.has_added_lit_match_name_span(&name, opt_body, &mut err) {
+ err.span_label(pat.span, "unused variable");
+ }
err.multipart_suggestion(
"if this is intentional, prefix it with an underscore",
non_shorthands,
@@ -1656,6 +1702,42 @@ impl<'tcx> Liveness<'_, 'tcx> {
}
}
+ fn has_added_lit_match_name_span(
+ &self,
+ name: &str,
+ opt_body: Option<&hir::Body<'_>>,
+ err: &mut rustc_errors::DiagnosticBuilder<'_, ()>,
+ ) -> bool {
+ let mut has_litstring = false;
+ let Some(opt_body) = opt_body else {return false;};
+ let mut visitor = CollectLitsVisitor { lit_exprs: vec![] };
+ intravisit::walk_body(&mut visitor, opt_body);
+ for lit_expr in visitor.lit_exprs {
+ let hir::ExprKind::Lit(litx) = &lit_expr.kind else { continue };
+ let rustc_ast::LitKind::Str(syb, _) = litx.node else{ continue; };
+ let name_str: &str = syb.as_str();
+ let mut name_pa = String::from("{");
+ name_pa.push_str(&name);
+ name_pa.push('}');
+ if name_str.contains(&name_pa) {
+ err.span_label(
+ lit_expr.span,
+ "you might have meant to use string interpolation in this string literal",
+ );
+ err.multipart_suggestion(
+ "string interpolation only works in `format!` invocations",
+ vec![
+ (lit_expr.span.shrink_to_lo(), "format!(".to_string()),
+ (lit_expr.span.shrink_to_hi(), ")".to_string()),
+ ],
+ Applicability::MachineApplicable,
+ );
+ has_litstring = true;
+ }
+ }
+ has_litstring
+ }
+
fn warn_about_dead_assign(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) {
if !self.live_on_exit(ln, var) {
self.report_unused_assign(hir_id, spans, var, |name| {
diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs
index 20765abf3..607973446 100644
--- a/compiler/rustc_passes/src/naked_functions.rs
+++ b/compiler/rustc_passes/src/naked_functions.rs
@@ -1,11 +1,12 @@
//! Checks validity of naked functions.
-use rustc_ast::{Attribute, InlineAsmOptions};
+use rustc_ast::InlineAsmOptions;
use rustc_errors::{struct_span_err, Applicability};
use rustc_hir as hir;
+use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
-use rustc_hir::intravisit::{FnKind, Visitor};
-use rustc_hir::{ExprKind, HirId, InlineAsmOperand, StmtKind};
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{ExprKind, InlineAsmOperand, StmtKind};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::lint::builtin::UNDEFINED_NAKED_FUNCTION_ABI;
@@ -13,71 +14,58 @@ use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::spec::abi::Abi;
-fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
- tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckNakedFunctions { tcx });
-}
-
pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers { check_mod_naked_functions, ..*providers };
}
-struct CheckNakedFunctions<'tcx> {
- tcx: TyCtxt<'tcx>,
-}
-
-impl<'tcx> Visitor<'tcx> for CheckNakedFunctions<'tcx> {
- fn visit_fn(
- &mut self,
- fk: FnKind<'_>,
- _fd: &'tcx hir::FnDecl<'tcx>,
- body_id: hir::BodyId,
- span: Span,
- hir_id: HirId,
- ) {
- let ident_span;
- let fn_header;
-
- match fk {
- FnKind::Closure => {
- // Closures with a naked attribute are rejected during attribute
- // check. Don't validate them any further.
- return;
- }
- FnKind::ItemFn(ident, _, ref header, ..) => {
- ident_span = ident.span;
- fn_header = header;
- }
-
- FnKind::Method(ident, ref sig, ..) => {
- ident_span = ident.span;
- fn_header = &sig.header;
- }
+fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
+ let items = tcx.hir_module_items(module_def_id);
+ for def_id in items.definitions() {
+ if !matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) {
+ continue;
}
- let attrs = self.tcx.hir().attrs(hir_id);
- let naked = attrs.iter().any(|attr| attr.has_name(sym::naked));
- if naked {
- let body = self.tcx.hir().body(body_id);
- check_abi(self.tcx, hir_id, fn_header.abi, ident_span);
- check_no_patterns(self.tcx, body.params);
- check_no_parameters_use(self.tcx, body);
- check_asm(self.tcx, body, span);
- check_inline(self.tcx, attrs);
+ let naked = tcx.has_attr(def_id.to_def_id(), sym::naked);
+ if !naked {
+ continue;
}
+
+ let (fn_header, body_id) = match tcx.hir().get_by_def_id(def_id) {
+ hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. })
+ | hir::Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)),
+ ..
+ })
+ | hir::Node::ImplItem(hir::ImplItem {
+ kind: hir::ImplItemKind::Fn(sig, body_id),
+ ..
+ }) => (sig.header, *body_id),
+ _ => continue,
+ };
+
+ let body = tcx.hir().body(body_id);
+ check_abi(tcx, def_id, fn_header.abi);
+ check_no_patterns(tcx, body.params);
+ check_no_parameters_use(tcx, body);
+ check_asm(tcx, def_id, body);
+ check_inline(tcx, def_id);
}
}
/// Check that the function isn't inlined.
-fn check_inline(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
- for attr in attrs.iter().filter(|attr| attr.has_name(sym::inline)) {
+fn check_inline(tcx: TyCtxt<'_>, def_id: LocalDefId) {
+ let attrs = tcx.get_attrs(def_id.to_def_id(), sym::inline);
+ for attr in attrs {
tcx.sess.struct_span_err(attr.span, "naked functions cannot be inlined").emit();
}
}
/// Checks that function uses non-Rust ABI.
-fn check_abi(tcx: TyCtxt<'_>, hir_id: HirId, abi: Abi, fn_ident_span: Span) {
+fn check_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, abi: Abi) {
if abi == Abi::Rust {
- tcx.struct_span_lint_hir(UNDEFINED_NAKED_FUNCTION_ABI, hir_id, fn_ident_span, |lint| {
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+ let span = tcx.def_span(def_id);
+ tcx.struct_span_lint_hir(UNDEFINED_NAKED_FUNCTION_ABI, hir_id, span, |lint| {
lint.build("Rust ABI is unsupported in naked functions").emit();
});
}
@@ -88,7 +76,7 @@ fn check_no_patterns(tcx: TyCtxt<'_>, params: &[hir::Param<'_>]) {
for param in params {
match param.pat.kind {
hir::PatKind::Wild
- | hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, _, None) => {}
+ | hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, _, None) => {}
_ => {
tcx.sess
.struct_span_err(
@@ -141,7 +129,7 @@ impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> {
}
/// Checks that function body contains a single inline assembly block.
-fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>, fn_span: Span) {
+fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<'tcx>) {
let mut this = CheckInlineAssembly { tcx, items: Vec::new() };
this.visit_body(body);
if let [(ItemKind::Asm | ItemKind::Err, _)] = this.items[..] {
@@ -149,7 +137,7 @@ fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>, fn_span: Span
} else {
let mut diag = struct_span_err!(
tcx.sess,
- fn_span,
+ tcx.def_span(def_id),
E0787,
"naked functions must contain a single asm block"
);
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index ca6a2ac3d..9ba127609 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -1,8 +1,10 @@
//! A pass that annotates every item and method with its stability level,
//! propagating default levels lexically from parent to children ast nodes.
-use attr::StabilityLevel;
-use rustc_attr::{self as attr, ConstStability, Stability, Unstable, UnstableReason};
+use rustc_attr::{
+ self as attr, rust_version_symbol, ConstStability, Stability, StabilityLevel, Unstable,
+ UnstableReason, VERSION_PLACEHOLDER,
+};
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_errors::{struct_span_err, Applicability};
use rustc_hir as hir;
@@ -10,7 +12,7 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
use rustc_hir::hir_id::CRATE_HIR_ID;
use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::{FieldDef, Generics, HirId, Item, ItemKind, TraitRef, Ty, TyKind, Variant};
+use rustc_hir::{FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant};
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::privacy::AccessLevels;
use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, Index};
@@ -161,7 +163,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
return;
}
- let (stab, const_stab) = attr::find_stability(&self.tcx.sess, attrs, item_sp);
+ let (stab, const_stab, body_stab) = attr::find_stability(&self.tcx.sess, attrs, item_sp);
let mut const_span = None;
let const_stab = const_stab.map(|(const_stab, const_span_node)| {
@@ -209,6 +211,13 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
}
}
+ if let Some((body_stab, _span)) = body_stab {
+ // FIXME: check that this item can have body stability
+
+ self.index.default_body_stab_map.insert(def_id, body_stab);
+ debug!(?self.index.default_body_stab_map);
+ }
+
let stab = stab.map(|(stab, span)| {
// Error if prohibited, or can't inherit anything from a container.
if kind == AnnotationKind::Prohibited
@@ -434,7 +443,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
);
}
- fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) {
+ fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) {
self.annotate(
self.tcx.hir().local_def_id(var.id),
var.span,
@@ -452,12 +461,12 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
AnnotationKind::Required,
InheritDeprecation::Yes,
InheritConstStability::No,
- InheritStability::No,
+ InheritStability::Yes,
|_| {},
);
}
- intravisit::walk_variant(v, var, g, item_id)
+ intravisit::walk_variant(v, var)
},
)
}
@@ -590,9 +599,12 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> {
intravisit::walk_impl_item(self, ii);
}
- fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) {
+ fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) {
self.check_missing_stability(self.tcx.hir().local_def_id(var.id), var.span);
- intravisit::walk_variant(self, var, g, item_id);
+ if let Some(ctor_hir_id) = var.data.ctor_hir_id() {
+ self.check_missing_stability(self.tcx.hir().local_def_id(ctor_hir_id), var.span);
+ }
+ intravisit::walk_variant(self, var);
}
fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) {
@@ -613,6 +625,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index {
let mut index = Index {
stab_map: Default::default(),
const_stab_map: Default::default(),
+ default_body_stab_map: Default::default(),
depr_map: Default::default(),
implications: Default::default(),
};
@@ -673,6 +686,9 @@ pub(crate) fn provide(providers: &mut Providers) {
stability_implications: |tcx, _| tcx.stability().implications.clone(),
lookup_stability: |tcx, id| tcx.stability().local_stability(id.expect_local()),
lookup_const_stability: |tcx, id| tcx.stability().local_const_stability(id.expect_local()),
+ lookup_default_body_stability: |tcx, id| {
+ tcx.stability().local_default_body_stability(id.expect_local())
+ },
lookup_deprecation_entry: |tcx, id| {
tcx.stability().local_deprecation_entry(id.expect_local())
},
@@ -723,7 +739,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
let features = self.tcx.features();
if features.staged_api {
let attrs = self.tcx.hir().attrs(item.hir_id());
- let (stab, const_stab) = attr::find_stability(&self.tcx.sess, attrs, item.span);
+ let (stab, const_stab, _) =
+ attr::find_stability(&self.tcx.sess, attrs, item.span);
// If this impl block has an #[unstable] attribute, give an
// error if all involved types and traits are stable, because
@@ -816,7 +833,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
// added, such as `core::intrinsics::transmute`
let parents = path.segments.iter().rev().skip(1);
for path_segment in parents {
- if let Some(def_id) = path_segment.res.as_ref().and_then(Res::opt_def_id) {
+ if let Some(def_id) = path_segment.res.opt_def_id() {
// use `None` for id to prevent deprecation check
self.tcx.check_stability_allow_unstable(
def_id,
@@ -949,51 +966,90 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
remaining_lib_features.remove(&sym::libc);
remaining_lib_features.remove(&sym::test);
- // We always collect the lib features declared in the current crate, even if there are
- // no unknown features, because the collection also does feature attribute validation.
- let local_defined_features = tcx.lib_features(());
- let mut all_lib_features: FxHashMap<_, _> =
- local_defined_features.to_vec().iter().map(|el| *el).collect();
- let mut implications = tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE).clone();
- for &cnum in tcx.crates(()) {
- implications.extend(tcx.stability_implications(cnum));
- all_lib_features.extend(tcx.defined_lib_features(cnum).iter().map(|el| *el));
- }
-
- // Check that every feature referenced by an `implied_by` exists (for features defined in the
- // local crate).
- for (implied_by, feature) in tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE) {
- // Only `implied_by` needs to be checked, `feature` is guaranteed to exist.
- if !all_lib_features.contains_key(implied_by) {
- let span = local_defined_features
- .stable
- .get(feature)
- .map(|(_, span)| span)
- .or_else(|| local_defined_features.unstable.get(feature))
- .expect("feature that implied another does not exist");
- tcx.sess
- .struct_span_err(
- *span,
- format!("feature `{implied_by}` implying `{feature}` does not exist"),
- )
- .emit();
- }
- }
-
- if !remaining_lib_features.is_empty() {
- for (feature, since) in all_lib_features.iter() {
+ /// For each feature in `defined_features`..
+ ///
+ /// - If it is in `remaining_lib_features` (those features with `#![feature(..)]` attributes in
+ /// the current crate), check if it is stable (or partially stable) and thus an unnecessary
+ /// attribute.
+ /// - If it is in `remaining_implications` (a feature that is referenced by an `implied_by`
+ /// from the current crate), then remove it from the remaining implications.
+ ///
+ /// Once this function has been invoked for every feature (local crate and all extern crates),
+ /// then..
+ ///
+ /// - If features remain in `remaining_lib_features`, then the user has enabled a feature that
+ /// does not exist.
+ /// - If features remain in `remaining_implications`, the `implied_by` refers to a feature that
+ /// does not exist.
+ ///
+ /// By structuring the code in this way: checking the features defined from each crate one at a
+ /// time, less loading from metadata is performed and thus compiler performance is improved.
+ fn check_features<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ remaining_lib_features: &mut FxIndexMap<&Symbol, Span>,
+ remaining_implications: &mut FxHashMap<Symbol, Symbol>,
+ defined_features: &[(Symbol, Option<Symbol>)],
+ all_implications: &FxHashMap<Symbol, Symbol>,
+ ) {
+ for (feature, since) in defined_features {
if let Some(since) = since && let Some(span) = remaining_lib_features.get(&feature) {
// Warn if the user has enabled an already-stable lib feature.
- if let Some(implies) = implications.get(&feature) {
+ if let Some(implies) = all_implications.get(&feature) {
unnecessary_partially_stable_feature_lint(tcx, *span, *feature, *implies, *since);
} else {
unnecessary_stable_feature_lint(tcx, *span, *feature, *since);
}
+
+ }
+ remaining_lib_features.remove(feature);
+
+ // `feature` is the feature doing the implying, but `implied_by` is the feature with
+ // the attribute that establishes this relationship. `implied_by` is guaranteed to be a
+ // feature defined in the local crate because `remaining_implications` is only the
+ // implications from this crate.
+ remaining_implications.remove(feature);
+
+ if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
+ break;
}
- remaining_lib_features.remove(&feature);
- if remaining_lib_features.is_empty() {
+ }
+ }
+
+ // All local crate implications need to have the feature that implies it confirmed to exist.
+ let mut remaining_implications =
+ tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE).clone();
+
+ // We always collect the lib features declared in the current crate, even if there are
+ // no unknown features, because the collection also does feature attribute validation.
+ let local_defined_features = tcx.lib_features(()).to_vec();
+ if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() {
+ // Loading the implications of all crates is unavoidable to be able to emit the partial
+ // stabilization diagnostic, but it can be avoided when there are no
+ // `remaining_lib_features`.
+ let mut all_implications = remaining_implications.clone();
+ for &cnum in tcx.crates(()) {
+ all_implications.extend(tcx.stability_implications(cnum));
+ }
+
+ check_features(
+ tcx,
+ &mut remaining_lib_features,
+ &mut remaining_implications,
+ local_defined_features.as_slice(),
+ &all_implications,
+ );
+
+ for &cnum in tcx.crates(()) {
+ if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
break;
}
+ check_features(
+ tcx,
+ &mut remaining_lib_features,
+ &mut remaining_implications,
+ tcx.defined_lib_features(cnum).to_vec().as_slice(),
+ &all_implications,
+ );
}
}
@@ -1001,6 +1057,22 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
struct_span_err!(tcx.sess, span, E0635, "unknown feature `{}`", feature).emit();
}
+ for (implied_by, feature) in remaining_implications {
+ let local_defined_features = tcx.lib_features(());
+ let span = local_defined_features
+ .stable
+ .get(&feature)
+ .map(|(_, span)| span)
+ .or_else(|| local_defined_features.unstable.get(&feature))
+ .expect("feature that implied another does not exist");
+ tcx.sess
+ .struct_span_err(
+ *span,
+ format!("feature `{implied_by}` implying `{feature}` does not exist"),
+ )
+ .emit();
+ }
+
// FIXME(#44232): the `used_features` table no longer exists, so we
// don't lint about unused features. We should re-enable this one day!
}
@@ -1035,7 +1107,15 @@ fn unnecessary_partially_stable_feature_lint(
});
}
-fn unnecessary_stable_feature_lint(tcx: TyCtxt<'_>, span: Span, feature: Symbol, since: Symbol) {
+fn unnecessary_stable_feature_lint(
+ tcx: TyCtxt<'_>,
+ span: Span,
+ feature: Symbol,
+ mut since: Symbol,
+) {
+ if since.as_str() == VERSION_PLACEHOLDER {
+ since = rust_version_symbol();
+ }
tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| {
lint.build(&format!(
"the feature `{feature}` has been stable since {since} and no longer requires an \