diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index caa554c4aa669..34c7137b8676d 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -155,7 +155,10 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::attrs::EiiImpl { span: self.lower_span(*span), inner_span: self.lower_span(*inner_span), - impl_marked_unsafe: self.lower_safety(*impl_safety, hir::Safety::Safe).is_unsafe(), + impl_unsafe_span: match *impl_safety { + Safety::Unsafe(span) => Some(self.lower_span(span)), + Safety::Safe(_) | Safety::Default => None, + }, is_default: *is_default, resolution, } diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 15c4714624a63..28016aec48a50 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -305,7 +305,7 @@ impl AttributeParser for NakedParser { if other_attr.word_is(sym::target_feature) { if !cx.features().naked_functions_target_feature() { feature_err( - &cx.sess(), + cx.sess(), sym::naked_functions_target_feature, other_attr.span(), "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions", @@ -396,7 +396,7 @@ impl AttributeParser for UsedParser { Some(sym::compiler) => { if !cx.features().used_with_arg() { feature_err( - &cx.sess(), + cx.sess(), sym::used_with_arg, cx.attr_span, "`#[used(compiler)]` is currently unstable", @@ -408,7 +408,7 @@ impl AttributeParser for UsedParser { Some(sym::linker) => { if !cx.features().used_with_arg() { feature_err( - &cx.sess(), + cx.sess(), sym::used_with_arg, cx.attr_span, "`#[used(linker)]` is currently unstable", @@ -503,7 +503,7 @@ fn parse_tf_attribute( let Some(value_str) = cx.expect_string_literal(value) else { return features; }; - for feature in value_str.as_str().split(",") { + for feature in value_str.as_str().split(',') { features.push((Symbol::intern(feature), item.span())); } } @@ -602,7 +602,7 @@ impl SingleAttributeParser for InstrumentFnParser { const STABILITY: AttributeStability = unstable!(instrument_fn); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let instrument = match args { + match args { ArgParser::NameValue(nv) => match nv.value_as_str() { Some(sym::on) => Some(AttributeKind::InstrumentFn(InstrumentFnAttr::On)), Some(sym::off) => Some(AttributeKind::InstrumentFn(InstrumentFnAttr::Off)), @@ -621,8 +621,7 @@ impl SingleAttributeParser for InstrumentFnParser { cx.adcx().expected_specific_argument_strings(span, &[sym::on, sym::off]); None } - }; - instrument + } } } @@ -673,14 +672,7 @@ impl SingleAttributeParser for SanitizeParser { let is_on = match value.value_as_str() { Some(sym::on) => true, Some(sym::off) => false, - Some(_) => { - cx.adcx().expected_specific_argument_strings( - value.value_span, - &[sym::on, sym::off], - ); - return; - } - None => { + _ => { cx.adcx().expected_specific_argument_strings( value.value_span, &[sym::on, sym::off], @@ -737,7 +729,6 @@ impl SingleAttributeParser for SanitizeParser { sym::realtime, ], ); - continue; } } } @@ -809,9 +800,7 @@ impl SingleAttributeParser for PatchableFunctionEntryParser { } for item in meta_item_list.mixed() { - let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else { - return None; - }; + let (ident, value) = cx.expect_name_value(item, item.span(), None)?; let attrib_to_write = match ident.name { sym::prefix_nops => { diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index d401aafed7bec..25835c1ed8b04 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -24,7 +24,7 @@ fn get( if let Some(value_str) = v.value_as_ident() { Some(value_str) } else { - cx.adcx().expected_string_literal(v.value_span, Some(&v.value_as_lit())); + cx.adcx().expected_string_literal(v.value_span, Some(v.value_as_lit())); None } } diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index 2486b5e3a65ed..a3a15978d2a37 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -49,7 +49,7 @@ pub(crate) enum Mode { } impl Mode { - fn as_str(&self) -> &'static str { + fn as_str(self) -> &'static str { match self { Self::RustcOnUnimplemented => "rustc_on_unimplemented", Self::DiagnosticOnUnimplemented => "diagnostic::on_unimplemented", @@ -61,7 +61,7 @@ impl Mode { } } - fn expected_options(&self) -> &'static str { + fn expected_options(self) -> &'static str { const DEFAULT: &str = "at least one of the `message`, `note` and `label` options are expected"; const DIAGNOSTIC_ON_TYPE_ERROR_EXPECTED_OPTIONS: &str = @@ -70,16 +70,16 @@ impl Mode { Self::RustcOnUnimplemented => { "see " } - Self::DiagnosticOnUnimplemented => DEFAULT, - Self::DiagnosticOnConst => DEFAULT, - Self::DiagnosticOnMove => DEFAULT, - Self::DiagnosticOnUnknown => DEFAULT, - Self::DiagnosticOnUnmatchedArgs => DEFAULT, + Self::DiagnosticOnUnimplemented + | Self::DiagnosticOnConst + | Self::DiagnosticOnMove + | Self::DiagnosticOnUnknown + | Self::DiagnosticOnUnmatchedArgs => DEFAULT, Self::DiagnosticOnTypeError => DIAGNOSTIC_ON_TYPE_ERROR_EXPECTED_OPTIONS, } } - fn allowed_options(&self) -> &'static str { + fn allowed_options(self) -> &'static str { const DEFAULT: &str = "only `message`, `note` and `label` are allowed as options"; const DIAGNOSTIC_ON_TYPE_ERROR_ALLOWED_OPTIONS: &str = "only `note` is allowed as option for `diagnostic::on_type_error`"; @@ -87,16 +87,16 @@ impl Mode { Self::RustcOnUnimplemented => { "see " } - Self::DiagnosticOnUnimplemented => DEFAULT, - Self::DiagnosticOnConst => DEFAULT, - Self::DiagnosticOnMove => DEFAULT, - Self::DiagnosticOnUnknown => DEFAULT, - Self::DiagnosticOnUnmatchedArgs => DEFAULT, + Self::DiagnosticOnUnimplemented + | Self::DiagnosticOnConst + | Self::DiagnosticOnMove + | Self::DiagnosticOnUnknown + | Self::DiagnosticOnUnmatchedArgs => DEFAULT, Self::DiagnosticOnTypeError => DIAGNOSTIC_ON_TYPE_ERROR_ALLOWED_OPTIONS, } } - fn allowed_format_arguments(&self) -> &'static str { + fn allowed_format_arguments(self) -> &'static str { match self { Self::RustcOnUnimplemented => { "see for allowed format arguments" @@ -356,12 +356,11 @@ fn parse_directive_items<'p>( if is_root { let items = or_malformed!(item.args().as_list()?); let mut iter = items.mixed(); - let filter: &MetaItemOrLitParser = match iter.next() { - Some(c) => c, - None => { - cx.emit_err(InvalidOnClause::Empty { span }); - continue; - } + let filter = if let Some(c) = iter.next() { + c + } else { + cx.emit_err(InvalidOnClause::Empty { span }); + continue; }; let filter = parse_filter(filter); diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatched_args.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatched_args.rs index 18f980bfebbe8..df8cee63506cc 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatched_args.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatched_args.rs @@ -17,6 +17,8 @@ impl AttributeParser for OnUnmatchedArgsParser { AttributeStability::Stable, // Unstable, stability checked manually in the parser |this, cx, args| { if !cx.features().diagnostic_on_unmatched_args() { + // `UnknownDiagnosticAttribute` is emitted in rustc_resolve/macros.rs + args.ignore_args(); return; } diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs index 0a18b69dda0ed..64c5ed704f8c5 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs @@ -45,7 +45,7 @@ impl AttributeParser for OpaqueParser { AllowedTargets::AllowListWarnRest(&[Allow(Target::MacroDef)]); fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { - if let Some(_) = self.attr_span { Some(AttributeKind::Opaque) } else { None } + if self.attr_span.is_some() { Some(AttributeKind::Opaque) } else { None } } } diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs index d3b632fae99d9..45d93e215c781 100644 --- a/compiler/rustc_attr_parsing/src/attributes/inline.rs +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -50,13 +50,13 @@ impl SingleAttributeParser for InlineParser { } _ => { cx.adcx().expected_specific_argument(l.span(), &[sym::always, sym::never]); - return None; + None } } } ArgParser::NameValue(_) => { cx.adcx().warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); - return None; + None } } } diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 07713590ff2e9..935265924d7da 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -144,13 +144,13 @@ impl CombineAttributeParser for LinkParser { let mut verbatim = None; if let Some((modifiers, span)) = modifiers { for modifier in modifiers.as_str().split(',') { - let (modifier, value): (Symbol, bool) = match modifier.strip_prefix(&['+', '-']) { - Some(m) => (Symbol::intern(m), modifier.starts_with('+')), - None => { + let (modifier, value): (Symbol, bool) = + if let Some(m) = modifier.strip_prefix(['+', '-']) { + (Symbol::intern(m), modifier.starts_with('+')) + } else { cx.emit_err(InvalidLinkModifier { span }); continue; - } - }; + }; macro report_unstable_modifier($feature: ident) { if !features.$feature() { @@ -196,9 +196,14 @@ impl CombineAttributeParser for LinkParser { cx.emit_err(WholeArchiveNeedsStatic { span }); } - (sym::as_dash_needed, Some(NativeLibKind::Dylib { as_needed })) - | (sym::as_dash_needed, Some(NativeLibKind::Framework { as_needed })) - | (sym::as_dash_needed, Some(NativeLibKind::RawDylib { as_needed })) => { + ( + sym::as_dash_needed, + Some( + NativeLibKind::Dylib { as_needed } + | NativeLibKind::Framework { as_needed } + | NativeLibKind::RawDylib { as_needed }, + ), + ) => { report_unstable_modifier!(native_link_modifiers_as_needed); assign_modifier(as_needed) } @@ -642,14 +647,13 @@ pub(crate) struct LinkageParser; impl SingleAttributeParser for LinkageParser { const PATH: &[Symbol] = &[sym::linkage]; const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ - Allow(Target::Fn), + Allow(Target::Fn), // const fn denied in check_attr Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: true })), Allow(Target::Method(MethodKind::TraitImpl)), Allow(Target::Static), - Allow(Target::ForeignStatic), + Allow(Target::ForeignStatic), // extern static mut denied in check_attr Allow(Target::ForeignFn), - Warn(Target::Method(MethodKind::Trait { body: false })), // Not inherited ]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: [ "available_externally", @@ -667,9 +671,7 @@ impl SingleAttributeParser for LinkageParser { fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { let name_value = cx.expect_name_value(args, cx.attr_span, Some(sym::linkage))?; - let Some(value) = cx.expect_string_literal(name_value) else { - return None; - }; + let value = cx.expect_string_literal(name_value)?; // Use the names from src/llvm/docs/LangRef.rst here. Most types are only // applicable to variable declarations and may not really make sense for diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index f49a7e674d560..17123f67cf276 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -149,12 +149,12 @@ impl SingleAttributeParser for MacroExportParser { cx.adcx().warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS); return None; }; - match l.meta_item_no_args().and_then(|i| i.path().word_sym()) { - Some(sym::local_inner_macros) => true, - _ => { - cx.adcx().warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS); - return None; - } + if l.meta_item_no_args().is_some_and(|m| m.path().word_is(sym::local_inner_macros)) + { + true + } else { + cx.adcx().warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS); + return None; } } ArgParser::NameValue(nv) => { diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 0adae406df5c6..885b35d353ef7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -247,7 +247,7 @@ impl OnDuplicate { this: unused, other: used, name: Symbol::intern( - &P::PATH.into_iter().map(|i| i.to_string()).collect::>().join(".."), + &P::PATH.iter().map(|i| i.to_string()).collect::>().join(".."), ), }); } diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index dfb7ce5e7b2f4..bb0da73df015c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -59,7 +59,7 @@ impl CombineAttributeParser for ReprParser { cx.adcx().expected_identifier(param.span()); continue; }; - reprs.extend(parse_repr(cx, &item).map(|r| (r, param.span()))); + reprs.extend(parse_repr(cx, item).map(|r| (r, param.span()))); } reprs } @@ -144,7 +144,7 @@ fn parse_repr(cx: &mut AcceptContext<'_, '_>, param: &MetaItemParser) -> Option< Some(sym::simd) => { if cx.features.is_some_and(|feats| !feats.repr_simd()) { feature_err( - &cx.sess(), + cx.sess(), sym::repr_simd, param.span(), "SIMD types are experimental and possibly buggy", diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 0cfb68d523f9b..1ff0cb26ac8d9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -309,10 +309,10 @@ pub(crate) fn parse_stability( let word = param.path().word(); match word.map(|i| i.name) { Some(sym::feature) => { - insert_value_into_option_or_error(cx, ¶m, &mut feature, word.unwrap())? + insert_value_into_option_or_error(cx, param, &mut feature, word.unwrap())? } Some(sym::since) => { - insert_value_into_option_or_error(cx, ¶m, &mut since, word.unwrap())? + insert_value_into_option_or_error(cx, param, &mut since, word.unwrap())? } _ => { cx.adcx().expected_specific_argument(param_span, &[sym::feature, sym::since]); @@ -376,13 +376,13 @@ pub(crate) fn parse_unstability( let word = param.path().word(); match word.map(|i| i.name) { Some(sym::feature) => { - insert_value_into_option_or_error(cx, ¶m, &mut feature, word.unwrap())? + insert_value_into_option_or_error(cx, param, &mut feature, word.unwrap())? } Some(sym::reason) => { - insert_value_into_option_or_error(cx, ¶m, &mut reason, word.unwrap())? + insert_value_into_option_or_error(cx, param, &mut reason, word.unwrap())? } Some(sym::issue) => { - insert_value_into_option_or_error(cx, ¶m, &mut issue, word.unwrap())?; + insert_value_into_option_or_error(cx, param, &mut issue, word.unwrap())?; // These unwraps are safe because `insert_value_into_option_or_error` ensures the meta item // is a name/value pair string literal. @@ -406,10 +406,10 @@ pub(crate) fn parse_unstability( }; } Some(sym::implied_by) => { - insert_value_into_option_or_error(cx, ¶m, &mut implied_by, word.unwrap())? + insert_value_into_option_or_error(cx, param, &mut implied_by, word.unwrap())? } Some(sym::old_name) => { - insert_value_into_option_or_error(cx, ¶m, &mut old_name, word.unwrap())? + insert_value_into_option_or_error(cx, param, &mut old_name, word.unwrap())? } _ => { cx.adcx().expected_specific_argument( @@ -493,10 +493,10 @@ impl CombineAttributeParser for UnstableRemovedParser { return None; }; match word.name { - sym::feature => insert_value_into_option_or_error(cx, ¶m, &mut feature, word)?, - sym::since => insert_value_into_option_or_error(cx, ¶m, &mut since, word)?, - sym::reason => insert_value_into_option_or_error(cx, ¶m, &mut reason, word)?, - sym::link => insert_value_into_option_or_error(cx, ¶m, &mut link, word)?, + sym::feature => insert_value_into_option_or_error(cx, param, &mut feature, word)?, + sym::since => insert_value_into_option_or_error(cx, param, &mut since, word)?, + sym::reason => insert_value_into_option_or_error(cx, param, &mut reason, word)?, + sym::link => insert_value_into_option_or_error(cx, param, &mut link, word)?, _ => { cx.adcx().expected_specific_argument( param.span(), diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index a2348eef975cf..69bdccb85c5cd 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -53,7 +53,7 @@ pub(crate) struct RustcParenSugarParser; impl NoArgsAttributeParser for RustcParenSugarParser { const PATH: &[Symbol] = &[sym::rustc_paren_sugar]; const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Trait)]); - const STABILITY: AttributeStability = unstable!(rustc_attrs); + const STABILITY: AttributeStability = unstable!(unboxed_closures); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcParenSugar; } diff --git a/compiler/rustc_attr_parsing/src/attributes/unroll.rs b/compiler/rustc_attr_parsing/src/attributes/unroll.rs index 50c73ca041cdd..3438fc044ec55 100644 --- a/compiler/rustc_attr_parsing/src/attributes/unroll.rs +++ b/compiler/rustc_attr_parsing/src/attributes/unroll.rs @@ -47,7 +47,7 @@ impl SingleAttributeParser for UnrollParser { ArgParser::NameValue(_) => { let inner_span = cx.inner_span; cx.adcx().expected_list_or_no_args(inner_span); - return None; + None } } } diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs index 47a8ac4ce51a8..b58489712fa52 100644 --- a/compiler/rustc_attr_parsing/src/attributes/util.rs +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -12,7 +12,7 @@ use crate::session_diagnostics::LimitInvalid; /// Parse a rustc version number written inside string literal in an attribute, /// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are -/// not accepted in this position, unlike when parsing CFG_RELEASE. +/// not accepted in this position, unlike when parsing `CFG_RELEASE`. pub fn parse_version(s: Symbol) -> Option { let mut components = s.as_str().split('-'); let d = components.next()?; @@ -70,7 +70,7 @@ impl AcceptContext<'_, '_> { IntErrorKind::Zero => { panic!("zero is a valid `limit` so should have returned Ok() when parsing") } - kind => panic!("unimplemented IntErrorKind variant: {:?}", kind), + kind => panic!("unimplemented IntErrorKind variant: {kind:?}"), }, }; diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index ed926330ed289..c4b2a5b509051 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -660,9 +660,8 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { // don't `uninterpolate` the token to avoid suggesting anything butchered or questionable // when macro metavariables are involved. let snapshot = self.parser.create_snapshot_for_diagnostic(); - let stmt = self.parser.parse_stmt_without_recovery(false, ForceCollect::No, false); - match stmt { - Ok(Some(stmt)) => { + match self.parser.parse_stmt_without_recovery(false, ForceCollect::No, false) { + Ok(stmt) => { // The user tried to write something like // `#[deprecated(note = concat!("a", "b"))]`. err.descr = stmt.kind.descr().to_string(); @@ -692,7 +691,6 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { }); } } - Ok(None) => {} Err(e) => { e.cancel(); self.parser.restore_snapshot(snapshot); diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 60854d67ec946..2fb89939181e6 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -126,9 +126,8 @@ impl CfgEval<'_> { Annotatable::ForeignItem(self.flat_map_foreign_item(item).pop().unwrap()) } Annotatable::Stmt(_) => { - let stmt = parser - .parse_stmt_without_recovery(false, ForceCollect::Yes, false)? - .unwrap(); + let stmt = + parser.parse_stmt_without_recovery(false, ForceCollect::Yes, false)?; Annotatable::Stmt(Box::new(self.flat_map_stmt(stmt).pop().unwrap())) } Annotatable::Expr(_) => { diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 691b13f03e965..0f9c3c8534785 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -198,16 +198,10 @@ fn process_builtin_attrs( codegen_fn_attrs.import_linkage = linkage; if tcx.is_mutable_static(did.into()) { - let mut diag = tcx.dcx().struct_span_err( + tcx.dcx().span_delayed_bug( *span, - "extern mutable statics are not allowed with `#[linkage]`", + "`extern { #[linkage] static mut ...` is checked in check_attr}", ); - diag.note( - "marking the extern static mutable would allow changing which \ - symbol the static references rather than make the target of the \ - symbol mutable", - ); - diag.emit(); } } else { codegen_fn_attrs.linkage = linkage; diff --git a/compiler/rustc_data_structures/src/svh.rs b/compiler/rustc_data_structures/src/svh.rs index 2c4e00c824d80..67594f6dae79d 100644 --- a/compiler/rustc_data_structures/src/svh.rs +++ b/compiler/rustc_data_structures/src/svh.rs @@ -11,17 +11,8 @@ use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash}; use crate::fingerprint::Fingerprint; -#[derive( - Copy, - Clone, - PartialEq, - Eq, - Debug, - Encodable_NoContext, - Decodable_NoContext, - Hash, - StableHash -)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Encodable_NoContext, Decodable_NoContext, StableHash)] pub struct Svh { hash: Fingerprint, } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 0c775e22ccf23..4f55e0506e799 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1120,9 +1120,7 @@ pub fn parse_ast_fragment<'a>( let mut stmts = SmallVec::new(); // Won't make progress on a `}`. while this.token != token::Eof && this.token != token::CloseBrace { - if let Some(stmt) = this.parse_full_stmt(AttemptLocalParseRecovery::Yes)? { - stmts.push(stmt); - } + stmts.push(this.parse_full_stmt(AttemptLocalParseRecovery::Yes)?); } AstFragment::Stmts(stmts) } diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs index 827538f58f980..b4b9db5d93f48 100644 --- a/compiler/rustc_hir/src/arena.rs +++ b/compiler/rustc_hir/src/arena.rs @@ -6,10 +6,10 @@ macro_rules! arena_types { $macro!([ // HIR types [] asm_template: rustc_ast::InlineAsmTemplatePiece, - [] attribute: rustc_hir::Attribute, - [] owner_info: rustc_hir::OwnerInfo<'tcx>, + [] attribute: crate::Attribute, + [] owner_info: crate::OwnerInfo<'tcx>, [] macro_def: rustc_ast::MacroDef, - [] delegation_info: rustc_hir::DelegationInfo, + [] delegation_info: crate::DelegationInfo, ]); ) } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index db492475a3aa4..bb0d7227bd171 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -12,7 +12,6 @@ use rustc_ast::{AttrStyle, Path, ast}; use rustc_data_structures::Limit; use rustc_data_structures::fx::FxIndexMap; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; -use rustc_hir::LangItem; use rustc_macros::{Decodable, Encodable, PrintAttribute, StableHash}; use rustc_span::def_id::DefId; use rustc_span::hygiene::Transparency; @@ -22,7 +21,7 @@ use thin_vec::ThinVec; use crate::attrs::diagnostic::*; use crate::attrs::pretty_printing::PrintAttribute; -use crate::{DefaultBodyStability, PartialConstStability, RustcVersion, Stability}; +use crate::{DefaultBodyStability, LangItem, PartialConstStability, RustcVersion, Stability}; #[derive(Copy, Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] pub enum EiiImplResolution { @@ -40,7 +39,7 @@ pub enum EiiImplResolution { #[derive(Copy, Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] pub struct EiiImpl { pub resolution: EiiImplResolution, - pub impl_marked_unsafe: bool, + pub impl_unsafe_span: Option, pub span: Span, pub inner_span: Span, pub is_default: bool, @@ -370,19 +369,8 @@ pub enum PeImportNameType { Undecorated, } -#[derive( - Copy, - Clone, - Debug, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - Encodable, - Decodable, - PrintAttribute -)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Encodable, Decodable, PrintAttribute)] #[derive(StableHash)] pub enum NativeLibKind { /// Static library (e.g. `libfoo.a` on Linux or `foo.lib` on Windows/MSVC) diff --git a/compiler/rustc_hir/src/attrs/mod.rs b/compiler/rustc_hir/src/attrs/mod.rs index b3c1deaa48fa8..1e863a71640c4 100644 --- a/compiler/rustc_hir/src/attrs/mod.rs +++ b/compiler/rustc_hir/src/attrs/mod.rs @@ -85,13 +85,13 @@ macro_rules! find_attr { 'done: { for i in $attributes_list { #[allow(unused_imports)] - use rustc_hir::attrs::AttributeKind::*; - let i: &rustc_hir::Attribute = i; + use $crate::attrs::AttributeKind::*; + let i: &$crate::Attribute = i; match i { - rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => { + $crate::Attribute::Parsed($pattern) $(if $guard)? => { break 'done Some($e); } - rustc_hir::Attribute::Unparsed(..) => {} + $crate::Attribute::Unparsed(..) => {} // In lint emitting, there's a specific exception for this warning. // It's not usually emitted from inside macros from other crates // (see https://github.com/rust-lang/rust/issues/110613) diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 96e89a8d8cdb6..d1c4e158a7959 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -114,71 +114,65 @@ pub trait HirTyCtxt<'hir> { fn hir_foreign_item(&self, id: ForeignItemId) -> &'hir ForeignItem<'hir>; } -// Used when no tcx is actually available, forcing manual implementation of nested visitors. +/// Used when no tcx is actually available, forcing manual implementation of nested visitors. impl<'hir> HirTyCtxt<'hir> for ! { fn hir_node(&self, _: HirId) -> Node<'hir> { - unreachable!(); + *self } fn hir_body(&self, _: BodyId) -> &'hir Body<'hir> { - unreachable!(); + *self } fn hir_item(&self, _: ItemId) -> &'hir Item<'hir> { - unreachable!(); + *self } fn hir_trait_item(&self, _: TraitItemId) -> &'hir TraitItem<'hir> { - unreachable!(); + *self } fn hir_impl_item(&self, _: ImplItemId) -> &'hir ImplItem<'hir> { - unreachable!(); + *self } fn hir_foreign_item(&self, _: ForeignItemId) -> &'hir ForeignItem<'hir> { - unreachable!(); + *self } } -pub mod nested_filter { - use super::HirTyCtxt; - - /// Specifies what nested things a visitor wants to visit. By "nested - /// things", we are referring to bits of HIR that are not directly embedded - /// within one another but rather indirectly, through a table in the crate. - /// This is done to control dependencies during incremental compilation: the - /// non-inline bits of HIR can be tracked and hashed separately. - /// - /// The most common choice is `OnlyBodies`, which will cause the visitor to - /// visit fn bodies for fns that it encounters, and closure bodies, but - /// skip over nested item-like things. - /// - /// See the comments at [`rustc_hir::intravisit`] for more details on the overall - /// visit strategy. - pub trait NestedFilter<'hir> { - type MaybeTyCtxt: HirTyCtxt<'hir>; - - /// Whether the visitor visits nested "item-like" things. - /// E.g., item, impl-item. - const INTER: bool; - /// Whether the visitor visits "intra item-like" things. - /// E.g., function body, closure, `AnonConst` - const INTRA: bool; - } - - /// Do not visit any nested things. When you add a new - /// "non-nested" thing, you will want to audit such uses to see if - /// they remain valid. - /// - /// Use this if you are only walking some particular kind of tree - /// (i.e., a type, or fn signature) and you don't want to thread a - /// `tcx` around. - pub struct None(()); - impl NestedFilter<'_> for None { - type MaybeTyCtxt = !; - const INTER: bool = false; - const INTRA: bool = false; - } +/// Specifies what nested things a visitor wants to visit. By "nested +/// things", we are referring to bits of HIR that are not directly embedded +/// within one another but rather indirectly, through a table in the crate. +/// This is done to control dependencies during incremental compilation: the +/// non-inline bits of HIR can be tracked and hashed separately. +/// +/// The most common choice is `OnlyBodies`, which will cause the visitor to +/// visit fn bodies for fns that it encounters, and closure bodies, but +/// skip over nested item-like things. +/// +/// See the [module level documentation](self) for more details on the overall +/// visit strategy. +pub trait NestedFilter<'hir> { + type MaybeTyCtxt: HirTyCtxt<'hir>; + + /// Whether the visitor visits nested "item-like" things. + /// E.g., item, impl-item. + const INTER: bool; + /// Whether the visitor visits "intra item-like" things. + /// E.g., function body, closure, `AnonConst` + const INTRA: bool; +} + +/// Do not visit any nested things. When you add a new +/// "non-nested" thing, you will want to audit such uses to see if +/// they remain valid. +/// +/// Use this if you are only walking some particular kind of tree +/// (i.e., a type, or fn signature) and you don't want to thread a +/// `tcx` around. +pub struct IgnoreNested(()); +impl NestedFilter<'_> for IgnoreNested { + type MaybeTyCtxt = !; + const INTER: bool = false; + const INTRA: bool = false; } -use nested_filter::NestedFilter; - /// Each method of the Visitor trait is a hook to be potentially /// overridden. Each method's default implementation recursively visits /// the substructure of the input via the corresponding `walk` method; @@ -215,7 +209,7 @@ pub trait Visitor<'v>: Sized { /// `visit_nested_XXX` methods. If a new `visit_nested_XXX` variant is /// added in the future, it will cause a panic which can be detected /// and fixed appropriately. - type NestedFilter: NestedFilter<'v> = nested_filter::None; + type NestedFilter: NestedFilter<'v> = IgnoreNested; /// The result type of the `visit_*` methods. Can be either `()`, /// or `ControlFlow`. @@ -226,16 +220,16 @@ pub trait Visitor<'v>: Sized { fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { panic!( "maybe_tcx must be implemented or consider using \ - `type NestedFilter = nested_filter::None` (the default)" + `type NestedFilter = Skip` (the default)" ); } /// Invoked when a nested item is encountered. By default, when - /// `Self::NestedFilter` is `nested_filter::None`, this method does + /// `Self::NestedFilter` is `Skip`, this method does /// nothing. **You probably don't want to override this method** -- /// instead, override [`Self::NestedFilter`] or use the "shallow" or /// "deep" visit patterns described at - /// [`rustc_hir::intravisit`]. The only reason to override + /// [`intravisit`](self). The only reason to override /// this method is if you want a nested pattern but cannot supply a /// `TyCtxt`; see `maybe_tcx` for advice. fn visit_nested_item(&mut self, id: ItemId) -> Self::Result { diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 9b976d21d5365..4e3b27e0b2442 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -15,8 +15,6 @@ #![recursion_limit = "256"] // tidy-alphabetical-end -extern crate self as rustc_hir; - mod arena; pub mod attrs; pub mod canonical_symbols; diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 605b6e3751ef3..6c393ed3dfc6b 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -947,9 +947,6 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { let attrs = tcx.get_all_attrs(def_id); let paren_sugar = find_attr!(attrs, RustcParenSugar); - if paren_sugar && !tcx.features().unboxed_closures() { - tcx.dcx().emit_err(diagnostics::ParenSugarAttribute { span: item.span }); - } // Only regular traits can be marker. let is_marker = !is_alias && find_attr!(attrs, Marker); diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index a6e274ceb4bc6..0ea353cf14cee 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -862,16 +862,6 @@ pub(crate) struct EnumDiscriminantOverflowed { pub wrapped_discr: String, } -#[derive(Diagnostic)] -#[diag( - "the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation" -)] -#[help("add `#![feature(unboxed_closures)]` to the crate attributes to use it")] -pub(crate) struct ParenSugarAttribute { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag("use of SIMD type{$snip} in FFI is highly experimental and may result in invalid code")] #[help("add `#![feature(simd_ffi)]` to the crate attributes to enable")] diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index b084b412417c5..d7852fc0e4f69 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -840,7 +840,7 @@ pub(crate) enum RedefiningRuntimeSymbolsDiag<'tcx> { "invalid definition of the runtime `{$symbol_name}` symbol used by the standard library" )] #[note( - "expected `{$expected_fn_sig}` + "expected `{$expected_fn_sig}` (for the current target) found `{$found_fn_sig}`" )] #[help( @@ -851,7 +851,7 @@ pub(crate) enum RedefiningRuntimeSymbolsDiag<'tcx> { "suspicious definition of the runtime `{$symbol_name}` symbol used by the standard library" )] #[note( - "expected `{$expected_fn_sig}` + "expected `{$expected_fn_sig}` (for the current target) found `{$found_fn_sig}`" )] #[help( diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 6949090781cf6..767fa647612af 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -149,9 +149,8 @@ impl From for LintExpectationId { /// Setting for how to handle a lint. /// /// See: -#[derive( - Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, Encodable, Decodable, StableHash -)] +#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] +#[derive(Encodable, Decodable, StableHash)] pub enum Level { /// The `allow` level will not issue any message. Allow, diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 391bd41be828c..f460fd42cb31c 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -217,9 +217,8 @@ pub struct DepKindVTable<'tcx> { /// some independent path or string that persists between runs without /// the need to be mapped or unmapped. (This ensures we can serialize /// them even in the absence of a tcx.) -#[derive( - Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable, StableHash -)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Encodable, Decodable, StableHash)] pub struct WorkProductId { hash: Fingerprint, } diff --git a/compiler/rustc_middle/src/hir/nested_filter.rs b/compiler/rustc_middle/src/hir/nested_filter.rs index e18c28a8eac01..304af22a87ea7 100644 --- a/compiler/rustc_middle/src/hir/nested_filter.rs +++ b/compiler/rustc_middle/src/hir/nested_filter.rs @@ -1,4 +1,4 @@ -use rustc_hir::intravisit::nested_filter::NestedFilter; +use rustc_hir::intravisit::NestedFilter; use crate::ty::TyCtxt; diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 9798138b48b80..94d3c7dccfc6c 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3331,13 +3331,9 @@ impl<'a> Parser<'a> { self.restore_snapshot(pre_pat_snapshot); match self.parse_stmt_without_recovery(true, ForceCollect::No, false) { // Consume statements for as long as possible. - Ok(Some(stmt)) => { + Ok(stmt) => { stmts.push(stmt); } - Ok(None) => { - self.restore_snapshot(start_snapshot); - break; - } // We couldn't parse either yet another statement missing it's // enclosing block nor the next arm's pattern or closing brace. Err(stmt_err) => { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index fc539405249a6..ef62316abf941 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -987,12 +987,9 @@ impl<'a> Parser<'a> { let initial_semicolon = self.token.span; while self.eat(exp!(Semi)) { - let _ = self - .parse_stmt_without_recovery(false, ForceCollect::No, false) - .unwrap_or_else(|e| { - e.cancel(); - None - }); + if let Err(e) = self.parse_stmt_without_recovery(false, ForceCollect::No, false) { + e.cancel(); + } } expect_err diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index e4602abf7779f..9f9545c194082 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -139,7 +139,7 @@ impl<'a> Parser<'a> { this.parse_block().map(|block| WithTokens::new(block)) })?)) } - NonterminalKind::Stmt => match self.parse_stmt(ForceCollect::Yes)? { + NonterminalKind::Stmt => match self.parse_stmt_nonterminal(ForceCollect::Yes) { Some(stmt) => Ok(ParseNtResult::Stmt(Box::new(stmt))), None => { Err(self.dcx().create_err(UnexpectedNonterminal::Statement(self.token.span))) diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 27a047f598513..6dddc8b2407dc 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -27,18 +27,22 @@ use crate::diagnostics::{self, MalformedLoopLabel}; use crate::exp; impl<'a> Parser<'a> { - /// Parses a statement. This stops just before trailing semicolons on everything but items. + /// Parses a statement nonterminal, which has a peculiar syntax preserved for backward + /// compatibility. The parsing stops just before trailing semicolons on everything but items. /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed. /// /// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of /// whether or not we have attributes. // Public for rustfmt usage. - pub fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option> { - Ok(self.parse_stmt_without_recovery(false, force_collect, false).unwrap_or_else(|e| { - e.emit(); - self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); - None - })) + pub fn parse_stmt_nonterminal(&mut self, force_collect: ForceCollect) -> Option { + match self.parse_stmt_without_recovery(false, force_collect, false) { + Ok(stmt) => Some(stmt), + Err(e) => { + e.emit(); + self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); + None + } + } } /// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of @@ -49,19 +53,18 @@ impl<'a> Parser<'a> { capture_semi: bool, force_collect: ForceCollect, force_full_expr: bool, - ) -> PResult<'a, Option> { + ) -> PResult<'a, Stmt> { let pre_attr_pos = self.collect_pos(); let attrs = self.parse_outer_attributes()?; let lo = self.token.span; - if let Some(stmt) = self.eat_metavar_seq(MetaVarKind::Stmt, |this| { + if let Some(mut stmt) = self.eat_metavar_seq(MetaVarKind::Stmt, |this| { this.parse_stmt_without_recovery(false, ForceCollect::Yes, false) }) { - let mut stmt = stmt.expect("an actual statement"); stmt.visit_attrs(|stmt_attrs| { attrs.prepend_to_nt_inner(stmt_attrs); }); - return Ok(Some(stmt)); + return Ok(stmt); } if self.token.is_keyword(kw::Mut) && self.is_keyword_ahead(1, &[kw::Let]) { @@ -161,9 +164,12 @@ impl<'a> Parser<'a> { self.mk_stmt(lo.to(item.span), StmtKind::Item(Box::new(item))) } else if self.eat(exp!(Semi)) { // Do not attempt to parse an expression if we're done here. - self.error_outer_attrs(attrs); + self.error_outer_attrs(attrs)?; self.mk_stmt(lo, StmtKind::Empty) - } else if self.token != token::CloseBrace { + } else if self.token == token::CloseBrace { + self.error_outer_attrs(attrs)?; + self.dcx().span_bug(self.token.span, "don't parse a statement if you see `}`"); + } else { // Remainder are line-expr stmts. This is similar to the `parse_stmt_path_start` case // above. let restrictions = @@ -185,13 +191,10 @@ impl<'a> Parser<'a> { .emit_err(diagnostics::AssignmentElseNotAllowed { span: e.span.to(bl.span) }); } self.mk_stmt(lo.to(e.span), StmtKind::Expr(e)) - } else { - self.error_outer_attrs(attrs); - return Ok(None); }; self.maybe_augment_stashed_expr_in_pats_with_suggestions(&stmt); - Ok(Some(stmt)) + Ok(stmt) } fn parse_stmt_path_start(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> { @@ -272,20 +275,21 @@ impl<'a> Parser<'a> { /// Error on outer attributes in this context. /// Also error if the previous token was a doc comment. - fn error_outer_attrs(&self, attrs: AttrWrapper) { - if !attrs.is_empty() - && let attrs @ [.., last] = &*attrs.take_for_recovery(self.psess) - { - if last.is_doc_comment() { - self.dcx().emit_err(diagnostics::DocCommentDoesNotDocumentAnything { - span: last.span, - missing_comma: None, - }); - } else if attrs.iter().any(|a| a.style == AttrStyle::Outer) { - self.dcx() - .emit_err(diagnostics::ExpectedStatementAfterOuterAttr { span: last.span }); - } + fn error_outer_attrs(&self, attrs: AttrWrapper) -> PResult<'a, ()> { + if attrs.is_empty() { + return Ok(()); } + let attrs = attrs.take_for_recovery(self.psess); + let last = attrs.last().unwrap(); + Err(if last.is_doc_comment() { + self.dcx().create_err(diagnostics::DocCommentDoesNotDocumentAnything { + span: last.span, + missing_comma: None, + }) + } else { + assert_eq!(last.style, AttrStyle::Outer); + self.dcx().create_err(diagnostics::ExpectedStatementAfterOuterAttr { span: last.span }) + }) } fn recover_stmt_local_after_let( @@ -521,6 +525,10 @@ impl<'a> Parser<'a> { let sp = self.token.span; let mut err = self.dcx().struct_span_err(sp, msg); self.label_expected_raw_ref(&mut err); + err.span_label(sp, "expected `{`"); + if self.token == token::CloseBrace { + return err; + } let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon @@ -547,13 +555,13 @@ impl<'a> Parser<'a> { // since we want to protect against: // `if 1 1 + 1 {` being suggested as `if { 1 } 1 + 1 {` // + + - Ok(Some(_)) + Ok(_) if (!self.token.is_keyword(kw::Else) && self.look_ahead(1, |t| t == &token::OpenBrace)) || do_not_suggest_help => {} // Do not suggest `if foo println!("") {;}` (as would be seen in test for #46836). - Ok(Some(Stmt { kind: StmtKind::Empty, .. })) => {} - Ok(Some(stmt)) => { + Ok(Stmt { kind: StmtKind::Empty, .. }) => {} + Ok(stmt) => { let stmt_own_line = self.psess.source_map().is_line_before_span_empty(sp); let stmt_span = if stmt_own_line && self.eat(exp!(Semi)) { // Expand the span to include the semicolon. @@ -571,9 +579,7 @@ impl<'a> Parser<'a> { Err(e) => { e.delay_as_bug(); } - _ => {} } - err.span_label(sp, "expected `{`"); err } @@ -773,17 +779,12 @@ impl<'a> Parser<'a> { let guar = err.emit(); self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore); - Some(self.mk_stmt_err(self.token.span, guar)) + self.mk_stmt_err(self.token.span, guar) } Ok(stmt) => stmt, Err(err) => return Err(err), }; - if let Some(stmt) = stmt { - stmts.push(stmt); - } else { - // Found only `;` or `}`. - continue; - }; + stmts.push(stmt); } Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span))) } @@ -943,10 +944,7 @@ impl<'a> Parser<'a> { } /// Parses a statement, including the trailing semicolon. - pub fn parse_full_stmt( - &mut self, - recover: AttemptLocalParseRecovery, - ) -> PResult<'a, Option> { + pub fn parse_full_stmt(&mut self, recover: AttemptLocalParseRecovery) -> PResult<'a, Stmt> { // Skip looking for a trailing semicolon when we have a metavar seq. if let Some(stmt) = self.eat_metavar_seq(MetaVarKind::Stmt, |this| { // Why pass `true` for `force_full_expr`? Statement expressions are less expressive @@ -959,14 +957,10 @@ impl<'a> Parser<'a> { // will reparse successfully. this.parse_stmt_without_recovery(false, ForceCollect::No, true) }) { - let stmt = stmt.expect("an actual statement"); - return Ok(Some(stmt)); + return Ok(stmt); } - let Some(mut stmt) = self.parse_stmt_without_recovery(true, ForceCollect::No, false)? - else { - return Ok(None); - }; + let mut stmt = self.parse_stmt_without_recovery(true, ForceCollect::No, false)?; let mut eat_semi = true; let mut add_semi_to_stmt = false; @@ -1086,7 +1080,7 @@ impl<'a> Parser<'a> { StmtKind::Expr(_) | StmtKind::MacCall(_) => {} StmtKind::Let(local) => { if self.try_recover_let_missing_semi(local).is_some() { - return Ok(Some(stmt)); + return Ok(stmt); } if let Err(mut e) = self.expect_semi() { // We might be at the `,` in `let x = foo;`. Try to recover. @@ -1159,7 +1153,7 @@ impl<'a> Parser<'a> { } stmt.span = stmt.span.to(self.prev_token.span); - Ok(Some(stmt)) + Ok(stmt) } pub(super) fn mk_block( diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index f9b508476689e..6115811b18af6 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -233,6 +233,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::OnTypeError { directive, .. } => { self.check_diagnostic_on_type_error(hir_id, directive.as_deref()) } + AttributeKind::Linkage(_linkage, span) => { + self.check_linkage(*span, hir_id, target, item) + } // All of the following attributes have no specific checks. // tidy-alphabetical-start @@ -268,7 +271,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::LinkName { .. } => (), AttributeKind::LinkOrdinal { .. } => (), AttributeKind::LinkSection { .. } => (), - AttributeKind::Linkage(..) => (), AttributeKind::LoopMatch(..) => {} AttributeKind::MacroEscape => (), AttributeKind::MacroUse { .. } => (), @@ -473,7 +475,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } fn check_eii_impl(&self, impls: &[EiiImpl], target: Target) { - for EiiImpl { span, inner_span, resolution, impl_marked_unsafe, is_default: _ } in impls { + for EiiImpl { span, inner_span, resolution, impl_unsafe_span, is_default: _ } in impls { match target { Target::Fn | Target::Static => {} _ => { @@ -481,35 +483,48 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - let needs_unsafe = match resolution { - EiiImplResolution::Macro(eii_macro) => { - find_attr!(self.tcx, *eii_macro, EiiDeclaration(EiiDecl { impl_unsafe, .. }) if *impl_unsafe) - } - EiiImplResolution::Known(foreign_item_did) => { - let foreign_item_did = *foreign_item_did; - self.tcx - .externally_implementable_items(foreign_item_did.krate) - .get(&foreign_item_did) - .map(|(decl, _)| decl.impl_unsafe) - .unwrap_or(false) - } - EiiImplResolution::Error(_) => false, + let impl_unsafe = match resolution { + EiiImplResolution::Macro(eii_macro) => find_attr!( + self.tcx, + *eii_macro, + EiiDeclaration(EiiDecl { impl_unsafe, .. }) => *impl_unsafe + ), + EiiImplResolution::Known(foreign_item_did) => self + .tcx + .externally_implementable_items(foreign_item_did.krate) + .get(foreign_item_did) + .map(|(decl, _)| decl.impl_unsafe), + EiiImplResolution::Error(_) => None, + }; + let Some(needs_unsafe) = impl_unsafe else { + continue; }; - if needs_unsafe && !impl_marked_unsafe { - let name = match resolution { - EiiImplResolution::Macro(eii_macro) => self.tcx.item_name(*eii_macro), - EiiImplResolution::Known(def_id) => self.tcx.item_name(*def_id), - EiiImplResolution::Error(_) => unreachable!(), - }; - self.dcx().emit_err(diagnostics::EiiImplRequiresUnsafe { - span: *span, - name, - suggestion: diagnostics::EiiImplRequiresUnsafeSuggestion { - left: inner_span.shrink_to_lo(), - right: inner_span.shrink_to_hi(), - }, - }); + let name = match resolution { + EiiImplResolution::Macro(eii_macro) => self.tcx.item_name(*eii_macro), + EiiImplResolution::Known(def_id) => self.tcx.item_name(*def_id), + EiiImplResolution::Error(_) => unreachable!(), + }; + + match (needs_unsafe, *impl_unsafe_span) { + (true, None) => { + self.dcx().emit_err(diagnostics::EiiImplRequiresUnsafe { + span: *span, + name, + suggestion: diagnostics::EiiImplRequiresUnsafeSuggestion { + left: inner_span.shrink_to_lo(), + right: inner_span.shrink_to_hi(), + }, + }); + } + (false, Some(unsafe_span)) => { + self.dcx().emit_err(diagnostics::EiiImplCannotBeUnsafe { + impl_span: *span, + unsafe_span, + name, + }); + } + _ => {} } } } @@ -1627,6 +1642,31 @@ impl<'tcx> CheckAttrVisitor<'tcx> { .emit_err(diagnostics::BothOptimizeNoneAndInline { optimize_span, inline_span }); } } + + fn check_linkage( + &self, + span: Span, + hir_id: HirId, + target: Target, + item: Option<&'tcx Item<'tcx>>, + ) { + // FIXME(eii) Once eii is no longer so experimental, suggest doing + // linkage stuff with externally implementable items instead + match target { + Target::ForeignStatic + if self.tcx.is_mutable_static(hir_id.expect_owner().def_id.into()) => + { + self.tcx.dcx().emit_err(diagnostics::StaticMutLinkage { span }); + } + Target::Fn + if let Item { kind: ItemKind::Fn { sig, .. }, .. } = item.unwrap() + && matches!(sig.header.constness, Constness::Const { .. }) => + { + self.tcx.dcx().emit_err(diagnostics::ConstFnLinkage { span }); + } + _ => {} + } + } } impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index 34fa0b264319d..cdb14737ad90d 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -515,10 +515,10 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NoMainErr { if self.add_teach_note { diag.note(msg!("if you don't know the basics of Rust, you can go look to the Rust Book to get started: https://doc.rust-lang.org/book/")); } + diag } } - pub(crate) struct DuplicateLangItem { pub local_span: Option, pub lang_item_name: Symbol, @@ -1082,6 +1082,16 @@ pub(crate) struct EiiImplRequiresUnsafeSuggestion { pub right: Span, } +#[derive(Diagnostic)] +#[diag("`{$name}` is not unsafe to implement")] +pub(crate) struct EiiImplCannotBeUnsafe { + #[primary_span] + pub impl_span: Span, + #[label("`unsafe` is not allowed here")] + pub unsafe_span: Span, + pub name: Symbol, +} + #[derive(Diagnostic)] #[diag("`#[{$name}]` {$kind} required, but not found")] pub(crate) struct EiiWithoutImpl { @@ -1190,3 +1200,23 @@ pub(crate) struct OnTypeErrorMalformedFormatLiterals { pub(crate) struct OnTypeErrorNotExactlyOneGeneric { pub count: usize, } + +#[derive(Diagnostic)] +#[diag("extern mutable statics are incompatible with the `linkage` attribute")] +#[note( + "the `linkage` attribute on extern statics generates a symbol that contains the address of \ + another static. Making the extern static mutable would allow changing the address, rather \ + than the static the address is pointing to" +)] +pub(crate) struct StaticMutLinkage { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag("`const fn` are incompatible with the `linkage` attribute")] +#[note("`const fn` may be called at compile time, which happens before linking")] +pub(crate) struct ConstFnLinkage { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 318d0b60e240d..b8cf472309ba5 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -2126,10 +2126,8 @@ impl fmt::Debug for SourceFile { /// /// When `SourceFile`s are exported in crate metadata, the `StableSourceFileId` /// is updated to incorporate the `StableCrateId` of the exporting crate. -#[derive( - Debug, Clone, Copy, Hash, PartialEq, Eq, StableHash, Encodable, Decodable, Default, PartialOrd, - Ord -)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Default, Ord)] +#[derive(StableHash, Encodable, Decodable)] pub struct StableSourceFileId(Hash128); impl StableSourceFileId { diff --git a/compiler/rustc_target/src/asm/amdgpu.rs b/compiler/rustc_target/src/asm/amdgpu.rs index cc6f612cdb8b5..0f24ae5dea225 100644 --- a/compiler/rustc_target/src/asm/amdgpu.rs +++ b/compiler/rustc_target/src/asm/amdgpu.rs @@ -1,5 +1,6 @@ use std::fmt; +use rustc_macros::{Decodable, Encodable, StableHash}; use rustc_span::Symbol; use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; @@ -9,19 +10,8 @@ use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; /// Amdgpu register classes /// /// The number is the size of the register class in bits. -#[derive( - Copy, - Clone, - rustc_macros::Encodable, - rustc_macros::Decodable, - Debug, - Eq, - PartialEq, - PartialOrd, - Hash, - rustc_macros::StableHash -)] -#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] +#[derive(StableHash, Encodable, Decodable)] pub enum AmdgpuInlineAsmRegClass { Sgpr(u16), Vgpr(u16), @@ -267,18 +257,8 @@ impl AmdgpuInlineAsmRegClass { /// Start index of a register. /// /// Together with the register size this gives the range occupied by a register. -#[derive( - Copy, - Clone, - rustc_macros::Encodable, - rustc_macros::Decodable, - Debug, - Eq, - PartialEq, - PartialOrd, - Hash, - rustc_macros::StableHash -)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] +#[derive(Encodable, Decodable, StableHash)] enum AmdgpuRegStart { /// Low 16-bit of the register at this index Low(u16), @@ -288,19 +268,8 @@ enum AmdgpuRegStart { Full(u16), } -#[derive( - Copy, - Clone, - rustc_macros::Encodable, - rustc_macros::Decodable, - Debug, - Eq, - PartialEq, - PartialOrd, - Hash, - rustc_macros::StableHash -)] -#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] +#[derive(Encodable, Decodable, StableHash)] pub struct AmdgpuInlineAsmReg { class: AmdgpuInlineAsmRegClass, range: AmdgpuRegStart, diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 58996703023ce..5190baaab8a26 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1368,7 +1368,7 @@ impl Box { /// /// unsafe { /// let non_null = NonNull::new(alloc(Layout::new::()).cast::()) - /// .expect("allocation failed"); + /// .expect("alloc should have successfully allocated memory"); /// // In general .write is required to avoid attempting to destruct /// // the (uninitialized) previous contents of `non_null`. /// non_null.write(5); diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index e6e6fcf5420f3..b340cf9566f2e 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -84,7 +84,7 @@ use crate::vec::Vec; /// /// // We are certain that our string doesn't have 0 bytes in the middle, /// // so we can .expect() -/// let c_to_print = CString::new("Hello, world!").expect("CString::new failed"); +/// let c_to_print = CString::new("Hello, world!").expect("we provided a string without NUL bytes, so CString::new should not fail"); /// unsafe { /// my_printer(c_to_print.as_ptr()); /// } @@ -242,7 +242,7 @@ impl CString { /// /// extern "C" { fn puts(s: *const c_char); } /// - /// let to_print = CString::new("Hello!").expect("CString::new failed"); + /// let to_print = CString::new("Hello!").expect("we provided a string without NUL bytes, so CString::new should not fail"); /// unsafe { /// puts(to_print.as_ptr()); /// } @@ -466,12 +466,12 @@ impl CString { /// use std::ffi::CString; /// /// let valid_utf8 = vec![b'f', b'o', b'o']; - /// let cstring = CString::new(valid_utf8).expect("CString::new failed"); - /// assert_eq!(cstring.into_string().expect("into_string() call failed"), "foo"); + /// let cstring = CString::new(valid_utf8).expect("we provided bytes that do not have a NUL byte, so CString::new should not fail"); + /// assert_eq!(cstring.into_string().expect("we provided bytes that are valid UTF-8, so `into_string` should not fail"), "foo"); /// /// let invalid_utf8 = vec![b'f', 0xff, b'o', b'o']; - /// let cstring = CString::new(invalid_utf8).expect("CString::new failed"); - /// let err = cstring.into_string().err().expect("into_string().err() failed"); + /// let cstring = CString::new(invalid_utf8).expect("we provided bytes that do not have a NUL byte, so CString::new should not fail"); + /// let err = cstring.into_string().expect_err("we provided bytes that are invalid UTF-8, so `into_string` should fail"); /// assert_eq!(err.utf8_error().valid_up_to(), 1); /// ``` #[stable(feature = "cstring_into", since = "1.7.0")] @@ -577,7 +577,7 @@ impl CString { /// let c_string = CString::from(c"foo"); /// let cstr = c_string.as_c_str(); /// assert_eq!(cstr, - /// CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed")); + /// CStr::from_bytes_with_nul(b"foo\0").expect("we provided bytes that has one NUL byte exactly at the end, so CStr::from_bytes_with_nul should not fail")); /// ``` #[inline] #[must_use] @@ -660,7 +660,7 @@ impl CString { /// use std::ffi::CString; /// assert_eq!( /// CString::from_vec_with_nul(b"abc\0".to_vec()) - /// .expect("CString::from_vec_with_nul failed"), + /// .expect("we provided bytes that has one NUL byte exactly at the end, so CString::from_vec_with_nul should not fail"), /// c"abc".to_owned() /// ); /// ``` diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 6c19ba816050d..cc321660e6ea4 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1314,7 +1314,7 @@ impl String { /// /// Ok(output) /// } - /// # process_data("rust").expect("why is the test harness OOMing on 4 bytes?"); + /// # process_data("rust").expect("reserving capacity for 12 bytes should never fail"); /// ``` #[stable(feature = "try_reserve", since = "1.57.0")] pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { @@ -1355,7 +1355,7 @@ impl String { /// /// Ok(output) /// } - /// # process_data("rust").expect("why is the test harness OOMing on 4 bytes?"); + /// # process_data("rust").expect("reserving capacity for 12 bytes should never fail"); /// ``` #[stable(feature = "try_reserve", since = "1.57.0")] pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index 939d3df26ba24..4332e7d28c58b 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -116,7 +116,7 @@ pub macro unreachable_2021 { /// convert unwinds to aborts, so using this function isn't necessary for FFI. #[unstable(feature = "abort_unwind", issue = "130338")] #[rustc_nounwind] -pub fn abort_unwind R, R>(f: F) -> R { +pub fn abort_on_unwind R, R>(f: F) -> R { f() } diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index e8394feb73f5d..a07a5fb6290ca 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -285,7 +285,7 @@ where } #[unstable(feature = "abort_unwind", issue = "130338")] -pub use core::panic::abort_unwind; +pub use core::panic::abort_on_unwind; /// Invokes a closure, capturing the cause of an unwinding panic if one occurs. /// diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 1e7de695ddae7..35d17d56ddd49 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -166,8 +166,8 @@ fn lang_start_internal( // prevent std from accidentally introducing a panic to these functions. Another is from // user code from `main` or, more nefariously, as described in e.g. issue #86030. // - // We use `catch_unwind` with `handle_rt_panic` instead of `abort_unwind` to make the error in - // case of a panic a bit nicer. + // We use `catch_unwind` with `handle_rt_panic` instead of `abort_on_unwind` to make the error + // in case of a panic a bit nicer. panic::catch_unwind(move || { // SAFETY: Only called once during runtime initialization. unsafe { init(argc, argv, sigpipe) }; diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 25a5238f538ee..1a74dff9d23d8 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -390279b302ca98ae270f434100ae3730531d1246 +da86f4d0726be475afbbffe40cb2f65741c51ad3 diff --git a/src/doc/rustc-dev-guide/src/ast-validation.md b/src/doc/rustc-dev-guide/src/ast-validation.md index 8f10bbecf21d0..45a9d77252815 100644 --- a/src/doc/rustc-dev-guide/src/ast-validation.md +++ b/src/doc/rustc-dev-guide/src/ast-validation.md @@ -1,28 +1,27 @@ # AST validation _AST validation_ is a separate AST pass that visits each -item in the tree and performs simple checks. This pass -doesn't perform any complex analysis, type checking or +item in the tree and performs simple checks. +This pass doesn't perform any complex analysis, type checking or name resolution. -Before performing any validation, the compiler first expands -the macros. Then this pass performs validations to check -that each AST item is in the correct state. And when this pass -is done, the compiler runs the crate resolution pass. +Before performing any validation, the compiler first expands the macros. +Then this pass performs validations to check that each AST item is in the correct state. +And when this pass is done, the compiler runs the crate resolution pass. ## Validations Validations are defined in `AstValidator` type, which -itself is located in `rustc_ast_passes` crate. This -type implements various simple checks which emit errors +itself is located in `rustc_ast_passes` crate. +This type implements various simple checks which emit errors when certain language rules are broken. In addition, `AstValidator` implements `Visitor` trait that defines how to visit AST items (which can be functions, traits, enums, etc). -For each item, visitor performs specific checks. For -example, when visiting a function declaration, +For each item, visitor performs specific checks. +For example, when visiting a function declaration, `AstValidator` checks that the function has: * no more than `u16::MAX` parameters; diff --git a/src/doc/rustc-dev-guide/src/attributes.md b/src/doc/rustc-dev-guide/src/attributes.md index 1056a257c2ee8..e9703d2c6e209 100644 --- a/src/doc/rustc-dev-guide/src/attributes.md +++ b/src/doc/rustc-dev-guide/src/attributes.md @@ -25,6 +25,35 @@ For more information on these attributes, see the chapter about [attribute parsi [attr-parsing-chapter]: ./hir/attribute-parsing.md +### Note on adding new builtin attributes + +
+ +**Warning: Name resolution ambiguity potential when adding new builtin attributes** + +Please note that adding **new builtin attributes** (whose name is not reserved, i.e. a new builtin +attribute whose name does not start with `rustc`), even if *unstable*-gated, can introduce breakage +from name resolution ambiguity in stable code if (1) the stable code has a macro of the same name +which gets re-exported, or (2) or a proc-macro derive helper attribute of the same name. + +Typically, the builtin attributes probably has to start out as `#[rustc_foo]` instead of `#[foo]` to +avoid colliding with user-defined macros and proc-macro helper attributes. +Then, prior to stabilization, +a rename to `#[foo]` should be done separately with a crater run to assess fallout, +with a deliberate breakage FCP proposal for T-lang to consider. + +Remember also that crater is *not* exhaustive and does not contain all existing stable code. + +See: +- [Built-in attributes are treated differently vs prelude attributes, unstable built-in attributes + can name-collide with stable macro, and built-in attributes can break back-compat + #134963](https://github.com/rust-lang/rust/issues/134963) and backlinks within this issue, + including design discussions on how to fix this kind of breakage hazard. +- [Broken build after updating: coverage is ambiguous; ambiguous because of a name conflict with a + builtin attribute #121157](https://github.com/rust-lang/rust/issues/121157). +- [Regression: align is ambiguous #143834](https://github.com/rust-lang/rust/issues/143834). +
+ ## 'Non-builtin'/'active' attributes These attributes are defined by a crate - either the standard library, or a proc-macro crate. diff --git a/src/doc/rustc-dev-guide/src/const-eval.md b/src/doc/rustc-dev-guide/src/const-eval.md index a3fee034ec6ed..8ffd7d5ba47de 100644 --- a/src/doc/rustc-dev-guide/src/const-eval.md +++ b/src/doc/rustc-dev-guide/src/const-eval.md @@ -1,8 +1,9 @@ # Constant Evaluation -Constant evaluation is the process of computing values at compile time. For a -specific item (constant/static/array length) this happens after the MIR for the -item is borrow-checked and optimized. In many cases trying to const evaluate an +Constant evaluation is the process of computing values at compile time. +For a specific item (constant/static/array length) this happens after the MIR for the +item is borrow-checked and optimized. +In many cases trying to const evaluate an item will trigger the computation of its MIR for the first time. Prominent examples are: @@ -11,14 +12,12 @@ Prominent examples are: * Array length * needs to be known to reserve stack or heap space * Enum variant discriminants - * needs to be known to prevent two variants from having the same - discriminant + * needs to be known to prevent two variants from having the same discriminant * Patterns * need to be known to check for overlapping patterns Additionally constant evaluation can be used to reduce the workload or binary -size at runtime by precomputing complex operations at compile time and only -storing the result. +size at runtime by precomputing complex operations at compile time and only storing the result. All uses of constant evaluation can either be categorized as "influencing the type system" (array lengths, enum variant discriminants, const generic parameters), or as solely being @@ -37,7 +36,8 @@ They're the wrappers of the `const_eval` query. The `const_eval_*` functions use a [`ParamEnv`](./typing-parameter-envs.md) of environment in which the constant is evaluated (e.g. the function within which the constant is used) -and a [`GlobalId`]. The `GlobalId` is made up of an `Instance` referring to a constant +and a [`GlobalId`]. +The `GlobalId` is made up of an `Instance` referring to a constant or static or of an `Instance` of a function and an index into the function's `Promoted` table. Constant evaluation returns an [`EvalToValTreeResult`] for type system constants diff --git a/src/doc/rustc-dev-guide/src/notification-groups/gpu-target.md b/src/doc/rustc-dev-guide/src/notification-groups/gpu-target.md index 101c793525246..9acc94a174ed5 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/gpu-target.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/gpu-target.md @@ -3,14 +3,13 @@ **Github Label:** None
**Ping command:** `@rustbot ping gpu-target` -This notification group deals with linker related issues and their integration -within the compiler. +This notification group deals with linker-related issues and their integration within the compiler. The group also has an associated Zulip stream ([`#t-compiler/gpgpu-backend`]) where people can go to ask questions and discuss GPU-related topics and issues. -if you're interested in participating, feel free to sign up for this group! To -do so, open a PR against the [rust-lang/team] repository and add your GitHub +if you're interested in participating, feel free to sign up for this group! +To do so, open a PR against the [rust-lang/team] repository and add your GitHub user to [this file][gpu-target-team]. [`#t-compiler/gpgpu-backend`]: https://rust-lang.zulipchat.com/#narrow/channel/422870-t-compiler.2Fgpgpu-backend diff --git a/src/doc/rustc-dev-guide/src/notification-groups/wasi.md b/src/doc/rustc-dev-guide/src/notification-groups/wasi.md index 93962a54fdfb7..a38528e2d9b4e 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/wasi.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/wasi.md @@ -14,8 +14,8 @@ where people can go to pose questions and discuss WASI-specific topics. So, if you are interested in participating, please sign up for the WASI group! To do so, open a PR against the [rust-lang/team] repository. -Just [follow this example][eg], but change the username to your own! +Just follow [this example], but change the username to your own! [`#t-compiler/wasm`]: https://rust-lang.zulipchat.com/#narrow/stream/463513-t-compiler.2Fwasm [rust-lang/team]: https://github.com/rust-lang/team -[eg]: https://github.com/rust-lang/team/pull/1580 +[this example]: https://github.com/rust-lang/team/pull/1580 diff --git a/src/doc/rustc-dev-guide/src/notification-groups/wasm.md b/src/doc/rustc-dev-guide/src/notification-groups/wasm.md index 12e457546ba93..f903bc7114a71 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/wasm.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/wasm.md @@ -10,14 +10,12 @@ WebAssembly-related issues as well as suggestions on how to resolve interesting questions regarding our WASM support. The group also has an associated Zulip channel ([`#t-compiler/wasm`]) -where people can go to pose questions and discuss WASM-specific -topics. +where people can go to pose questions and discuss WASM-specific topics. -So, if you are interested in participating, please sign up for the -WASM group! To do so, open a PR against the [rust-lang/team] -repository. Just [follow this example][eg], but change the username to -your own! +So, if you are interested in participating, please sign up for the WASM group! +To do so, open a PR against the [rust-lang/team] repository. +Just follow [this example], but change the username to your own! [`#t-compiler/wasm`]: https://rust-lang.zulipchat.com/#narrow/stream/463513-t-compiler.2Fwasm [rust-lang/team]: https://github.com/rust-lang/team -[eg]: https://github.com/rust-lang/team/pull/1581 +[this example]: https://github.com/rust-lang/team/pull/1581 diff --git a/src/doc/rustc-dev-guide/src/pat-exhaustive-checking.md b/src/doc/rustc-dev-guide/src/pat-exhaustive-checking.md index e953931aa78c2..b7edb51ac8545 100644 --- a/src/doc/rustc-dev-guide/src/pat-exhaustive-checking.md +++ b/src/doc/rustc-dev-guide/src/pat-exhaustive-checking.md @@ -1,7 +1,7 @@ # Pattern and exhaustiveness checking -In Rust, pattern matching and bindings have a few very helpful properties. The -compiler will check that bindings are irrefutable when made and that match arms +In Rust, pattern matching and bindings have a few very helpful properties. +The compiler will check that bindings are irrefutable when made and that match arms are exhaustive. ## Pattern usefulness @@ -35,8 +35,7 @@ match x { } ``` -Thus usefulness is used for two purposes: -detecting unreachable code (which is useful to the user), +Thus usefulness is used for two purposes: detecting unreachable code (which is useful to the user), and ensuring that matches are exhaustive (which is important for soundness, because a match expression can return a value). @@ -88,22 +87,26 @@ That file contains a detailed description of the algorithm. ### Constructors and fields In the value `Pair(Some(0), true)`, `Pair` is called the constructor of the value, and `Some(0)` and -`true` are its fields. Every matchable value can be decomposed in this way. Examples of -constructors are: `Some`, `None`, `(,)` (the 2-tuple constructor), `Foo {..}` (the constructor for -a struct `Foo`), and `2` (the constructor for the number `2`). - -Each constructor takes a fixed number of fields; this is called its arity. `Pair` and `(,)` have -arity 2, `Some` has arity 1, `None` and `42` have arity 0. Each type has a known set of -constructors. Some types have many constructors (like `u64`) or even an infinitely many (like `&str` -and `&[T]`). - -Patterns are similar: `Pair(Some(_), _)` has constructor `Pair` and two fields. The difference is -that we get some extra pattern-only constructors, namely: the wildcard `_`, variable bindings, -integer ranges like `0..=10`, and variable-length slices like `[_, .., _]`. We treat or-patterns -separately. +`true` are its fields. +Every matchable value can be decomposed in this way. +Examples of constructors are: +`Some`, `None`, `(,)` (the 2-tuple constructor), `Foo {..}` (the constructor for a struct `Foo`), +and `2` (the constructor for the number `2`). + +Each constructor takes a fixed number of fields; this is called its arity. +`Pair` and `(,)` have arity 2, `Some` has arity 1, `None` and `42` have arity 0. +Each type has a known set of constructors. +Some types have many constructors (like `u64`) or even an infinitely many (like `&str` and `&[T]`). + +Patterns are similar: `Pair(Some(_), _)` has constructor `Pair` and two fields. +The difference is that we get some extra pattern-only constructors, namely: +the wildcard `_`, variable bindings, +integer ranges like `0..=10`, and variable-length slices like `[_, .., _]`. +We treat or-patterns separately. Now to check if a value `v` matches a pattern `p`, we check if `v`'s constructor matches `p`'s -constructor, then recursively compare their fields if necessary. A few representative examples: +constructor, then recursively compare their fields if necessary. +A few representative examples: - `matches!(v, _) := true` - `matches!((v0, v1), (p0, p1)) := matches!(v0, p0) && matches!(v1, p1)` @@ -114,8 +117,9 @@ constructor, then recursively compare their fields if necessary. A few represent - `matches!([v0], [p0, .., p1]) := false` (incompatible lengths) - `matches!([v0, v1, v2], [p0, .., p1]) := matches!(v0, p0) && matches!(v2, p1)` -This concept is absolutely central to pattern analysis. The [`constructor`] module provides -functions to extract, list and manipulate constructors. This is a useful enough concept that +This concept is absolutely central to pattern analysis. +The [`constructor`] module provides functions to extract, list, and manipulate constructors. +This is a useful enough concept that variations of it can be found in other places of the compiler, like in the MIR-lowering of a match expression and in some clippy lints. @@ -125,7 +129,8 @@ The pattern-only constructors (`_`, ranges and variable-length slices) each stan normal constructors, e.g. `_: Option` stands for the set {`None`, `Some`} and `[_, .., _]` stands for the infinite set {`[,]`, `[,,]`, `[,,,]`, ...} of the slice constructors of arity >= 2. -In order to manage these constructors, we keep them as grouped as possible. For example: +In order to manage these constructors, we keep them as grouped as possible. +For example: ```rust match (0, false) { @@ -137,7 +142,8 @@ match (0, false) { In this example, all of `0`, `1`, .., `49` match the same arms, and thus can be treated as a group. In fact, in this match, the only ranges we need to consider are: `0..50`, `50..=100`, -`101..=150`,`151..=200` and `201..`. Similarly: +`101..=150`,`151..=200` and `201..`. +Similarly: ```rust enum Direction { North, South, East, West } @@ -156,10 +162,11 @@ time. ### Usefulness vs reachability in the presence of empty types -This is likely the subtlest aspect of exhaustiveness. To be fully precise, a match doesn't operate -on a value, it operates on a place. In certain unsafe circumstances, it is possible for a place to -not contain valid data for its type. This has subtle consequences for empty types. Take the -following: +This is likely the subtlest aspect of exhaustiveness. +To be fully precise, a match doesn't operate on a value; it operates on a place. +In certain unsafe circumstances, it is possible for a place to not contain valid data for its type. +This has subtle consequences for empty types. +Take the following: ```rust enum Void {} @@ -172,10 +179,11 @@ unsafe { } ``` -In this example, `ptr` is a valid pointer pointing to a place with invalid data. The `_` pattern -does not look at the contents of the place `*ptr`, so this code is ok and the arm is taken. In other -words, despite the place we are inspecting being of type `Void`, there is a reachable arm. If the -arm had a binding however: +In this example, `ptr` is a valid pointer pointing to a place with invalid data. +The `_` pattern does not look at the contents of the place `*ptr`, +so this code is ok and the arm is taken. +In other words, despite the place we are inspecting being of type `Void`, there is a reachable arm. +If the arm had a binding however: ```rust # #[derive(Copy, Clone)] @@ -189,25 +197,31 @@ match *ptr { # } ``` -Here the binding loads the value of type `Void` from the `*ptr` place. In this example, this causes -UB since the data is not valid. In the general case, this asserts validity of the data at `*ptr`. +Here the binding loads the value of type `Void` from the `*ptr` place. +In this example, this causes UB since the data is not valid. +In the general case, this asserts validity of the data at `*ptr`. Either way, this arm will never be taken. -Finally, let's consider the empty match `match *ptr {}`. If we consider this exhaustive, then -having invalid data at `*ptr` is invalid. In other words, the empty match is semantically -equivalent to the `_a => ...` match. In the interest of explicitness, we prefer the case with an -arm, hence we won't tell the user to remove the `_a` arm. In other words, the `_a` arm is -unreachable yet not redundant. This is why we lint on redundant arms rather than unreachable +Finally, let's consider the empty match `match *ptr {}`. +If we consider this exhaustive, then having invalid data at `*ptr` is invalid. +In other words, the empty match is semantically equivalent to the `_a => ...` match. +In the interest of explicitness, we prefer the case with an +arm, hence we won't tell the user to remove the `_a` arm. +In other words, the `_a` arm is unreachable yet not redundant. +This is why we lint on redundant arms rather than unreachable arms, despite the fact that the lint says "unreachable". These considerations only affects certain places, namely those that can contain non-valid data -without UB. These are: pointer dereferences, reference dereferences, and union field accesses. We -track during exhaustiveness checking whether a given place is known to contain valid data. +without UB. +These are: pointer dereferences, reference dereferences, and union field accesses. +We track during exhaustiveness checking whether a given place is known to contain valid data. Having said all that, the current implementation of exhaustiveness checking does not follow the -above considerations. On stable, empty types are for the most part treated as non-empty. The -[`exhaustive_patterns`] feature errs on the other end: it allows omitting arms that could be -reachable in unsafe situations. The [`never_patterns`] experimental feature aims to fix this and +above considerations. +On stable, empty types are for the most part treated as non-empty. +The [`exhaustive_patterns`] feature errs on the other end: it allows omitting arms that could be +reachable in unsafe situations. +The [`never_patterns`] experimental feature aims to fix this and permit the correct behavior of empty types in patterns. [`check_match`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_build/thir/pattern/check_match/index.html diff --git a/src/doc/rustc-dev-guide/src/profiling/with-perf.md b/src/doc/rustc-dev-guide/src/profiling/with-perf.md index 3285a486eb452..becaec831230c 100644 --- a/src/doc/rustc-dev-guide/src/profiling/with-perf.md +++ b/src/doc/rustc-dev-guide/src/profiling/with-perf.md @@ -17,10 +17,9 @@ This is a guide for how to profile rustc with [perf](https://perf.wiki.kernel.or ## Gathering a perf profile -perf is an excellent tool on linux that can be used to gather and -analyze all kinds of information. Mostly it is used to figure out -where a program spends its time. It can also be used for other sorts -of events, though, like cache misses and so forth. +perf is an excellent tool on Linux that can be used to gather and analyze all kinds of information. +It is mostly used to figure out where a program spends its time. +It can also be used for other sorts of events, though, like cache misses and so forth. ### The basics @@ -30,26 +29,26 @@ The basic `perf` command is this: perf record -F99 --call-graph dwarf XXX ``` -The `-F99` tells perf to sample at 99 Hz, which avoids generating too -much data for longer runs (why 99 Hz you ask? It is often chosen -because it is unlikely to be in lockstep with other periodic -activity). The `--call-graph dwarf` tells perf to get call-graph -information from debuginfo, which is accurate. The `XXX` is the -command you want to profile. So, for example, you might do: +The `-F99` tells perf to sample at 99 Hz, which avoids generating too much data for longer runs. + +> 99 Hz is often chosen because it is unlikely to be in lockstep with other periodic activity. + +The `--call-graph dwarf` tells perf to get call-graph information from debuginfo, which is accurate. +The `XXX` is the command you want to profile. +So, for example, you might do: ```bash perf record -F99 --call-graph dwarf cargo + rustc ``` -to run `cargo` -- here `` should be the name of the toolchain -you made in the beginning. But there are some things to be aware of: +to run `cargo` -- here `` should be the name of the toolchain you made in the beginning. +But there are some things to be aware of: -- You probably don't want to profile the time spend building - dependencies. So something like `cargo build; cargo clean -p $C` may - be helpful (where `$C` is the crate name) - - Though usually I just do `touch src/lib.rs` and rebuild instead. =) -- You probably don't want incremental messing about with your - profile. So something like `CARGO_INCREMENTAL=0` can be helpful. +- You probably don't want to profile the time spent building dependencies. + So something like `cargo build; cargo clean --package $crate` may be helpful + - Though usually I just do `touch src/lib.rs` and rebuild instead =) +- You probably don't want incremental messing about with your profile. + So something like `CARGO_INCREMENTAL=0` can be helpful. In case to avoid the issue of `addr2line xxx/elf: could not read first record` when reading collected data from `cargo`, you may need use the latest version of `addr2line`: @@ -61,20 +60,20 @@ cargo install addr2line --features="bin" ### Gathering a perf profile from a `perf.rust-lang.org` test Often we want to analyze a specific test from `perf.rust-lang.org`. -The easiest way to do that is to use the [rustc-perf][rustc-perf] +The easiest way to do that is to use the [rustc-perf] benchmarking suite, this approach is described [here](with-rustc-perf.md). -Instead of using the benchmark suite CLI, you can also profile the benchmarks manually. First, -you need to clone the [rustc-perf][rustc-perf] repository: +Instead of using the benchmark suite CLI, you can also profile the benchmarks manually. +First, you need to clone the [rustc-perf] repository: ```bash $ git clone https://github.com/rust-lang/rustc-perf ``` -and then find the source code of the test that you want to profile. Sources for the tests -are found in [the `collector/compile-benchmarks` directory][compile-time dir] -and [the `collector/runtime-benchmarks` directory][runtime dir]. So let's -go into the directory of a specific test; we'll use `clap-rs` as an example: +and then find the source code of the test that you want to profile. +Sources for the tests are found in [the `collector/compile-benchmarks` directory][compile-time dir] +and [the `collector/runtime-benchmarks` directory][runtime dir]. +So let's go into the directory of a specific test; we'll use `clap-rs` as an example: [rustc-perf]: https://github.com/rust-lang/rustc-perf [compile-time dir]: https://github.com/rust-lang/rustc-perf/tree/master/collector/compile-benchmarks @@ -84,9 +83,8 @@ go into the directory of a specific test; we'll use `clap-rs` as an example: cd collector/compile-benchmarks/clap-3.1.6 ``` -In this case, let's say we want to profile the `cargo check` -performance. In that case, I would first run some basic commands to -build the dependencies: +In this case, let's say we want to profile the `cargo check` performance. +In that case, I would first run some basic commands to build the dependencies: ```bash # Setup: first clean out any old results and build the dependencies: @@ -94,11 +92,11 @@ cargo + clean CARGO_INCREMENTAL=0 cargo + check ``` -(Again, `` should be replaced with the name of the -toolchain we made in the first step.) +(Again, `` should be replaced with the name of the toolchain we made in the first step.) Next: we want record the execution time for *just* the clap-rs crate, -running cargo check. I tend to use `cargo rustc` for this, since it +running cargo check. +I tend to use `cargo rustc` for this, since it also allows me to add explicit flags, which we'll do later on. ```bash @@ -106,26 +104,26 @@ touch src/lib.rs CARGO_INCREMENTAL=0 perf record -F99 --call-graph dwarf cargo rustc --profile check --lib ``` -Note that final command: it's a doozy! It uses the `cargo rustc` -command, which executes rustc with (potentially) additional options; +Note that final command: it's a doozy! +It uses the `cargo rustc` command, which executes rustc with (potentially) additional options; the `--profile check` and `--lib` options specify that we are doing a `cargo check` execution, and that this is a library (not a binary). -At this point, we can use `perf` tooling to analyze the results. For example: +At this point, we can use `perf` tooling to analyze the results. +For example: ```bash perf report ``` -will open up an interactive TUI program. In simple cases, that can be -helpful. For more detailed examination, the [`perf-focus` tool][pf] -can be helpful; it is covered below. +This opens up an interactive TUI program. +In simple cases, that can be helpful. +For more detailed examination, the [`perf-focus` tool][pf] can be helpful; it is covered below. -**A note of caution.** Each of the rustc-perf tests is its own special - snowflake. In particular, some of them are not libraries, in which - case you would want to do `touch src/main.rs` and avoid passing - `--lib`. I'm not sure how best to tell which test is which to be - honest. +**A note of caution.** Each of the rustc-perf tests is its own special snowflake. + In particular, some of them are not libraries, in which + case you would want to do `touch src/main.rs` and avoid passing `--lib`. + I'm not sure how best to tell which test is which to be honest. ### Gathering NLL data @@ -141,23 +139,22 @@ CARGO_INCREMENTAL=0 perf record -F99 --call-graph dwarf cargo rustc --profile ch ## Analyzing a perf profile with `perf focus` -Once you've gathered a perf profile, we want to get some information -about it. For this, I personally use [perf focus][pf]. It's a kind of -simple but useful tool that lets you answer queries like: +Once you've gathered a perf profile, we want to get some information about it. +For this, I personally use [perf focus][pf]. +It's a kind of simple but useful tool that lets you answer queries like: - "how much time was spent in function F" (no matter where it was called from) - "how much time was spent in function F when it was called from G" - "how much time was spent in function F *excluding* time spent in G" - "what functions does F call and how much time does it spend in them" -To understand how it works, you have to know just a bit about -perf. Basically, perf works by *sampling* your process on a regular -basis (or whenever some event occurs). For each sample, perf gathers a -backtrace. `perf focus` lets you write a regular expression that tests +To understand how it works, you have to know just a bit about perf. +Basically, perf works by *sampling* your process on a regular basis (or whenever some event occurs). +For each sample, perf gathers a backtrace. +`perf focus` lets you write a regular expression that tests which functions appear in that backtrace, and then tells you which -percentage of samples had a backtrace that met the regular -expression. It's probably easiest to explain by walking through how I -would analyze NLL performance. +percentage of samples had a backtrace that met the regular expression. +It's probably easiest to explain by walking through how I would analyze NLL performance. ### Installing `perf-focus` @@ -169,9 +166,9 @@ cargo install perf-focus ### Example: How much time is spent in MIR borrowck? -Let's say we've gathered the NLL data for a test. We'd like to know -how much time it is spending in the MIR borrow-checker. The "main" -function of the MIR borrowck is called `do_mir_borrowck`, so we can do +Let's say we've gathered the NLL data for a test. +We'd like to know how much time it is spending in the MIR borrow-checker. +The "main" function of the MIR borrowck is called `do_mir_borrowck`, so we can do this command: ```bash @@ -183,37 +180,38 @@ Percentage : 29% ``` The `'{do_mir_borrowck}'` argument is called the **matcher**. It -specifies the test to be applied on the backtrace. In this case, the -`{X}` indicates that there must be *some* function on the backtrace -that meets the regular expression `X`. In this case, that regex is -just the name of the function we want (in fact, it's a subset of the name; -the full name includes a bunch of other stuff, like the module -path). In this mode, perf-focus just prints out the percentage of +specifies the test to be applied on the backtrace. +In this case, the `{X}` indicates that there must be *some* function on the backtrace +that meets the regular expression `X`. +In this case, that regex is just the name of the function we want +(in fact, it's a subset of the name; +the full name includes a bunch of other stuff, like the module path). +In this mode, perf-focus just prints out the percentage of samples where `do_mir_borrowck` was on the stack: in this case, 29%. **A note about c++filt.** To get the data from `perf`, `perf focus` - currently executes `perf script` (perhaps there is a better - way...). I've sometimes found that `perf script` outputs C++ mangled + currently executes `perf script` (perhaps there is a better way...). + I've sometimes found that `perf script` outputs C++ mangled names. This is annoying. You can tell by running `perf script | head` yourself — if you see names like `5rustc6middle` instead of - `rustc::middle`, then you have the same problem. You can solve this - by doing: + `rustc::middle`, then you have the same problem. + You can solve this by doing: ```bash perf script | c++filt | perf focus --from-stdin ... ``` This will pipe the output from `perf script` through `c++filt` and -should mostly convert those names into a more friendly format. The -`--from-stdin` flag to `perf focus` tells it to get its data from -stdin, rather than executing `perf focus`. We should make this more -convenient (at worst, maybe add a `c++filt` option to `perf focus`, or +should mostly convert those names into a more friendly format. +The `--from-stdin` flag to `perf focus` tells it to get its data from +stdin, rather than executing `perf focus`. +We should make this more convenient (at worst, maybe add a `c++filt` option to `perf focus`, or just always use it — it's pretty harmless). ### Example: How much time does MIR borrowck spend solving traits? -Perhaps we'd like to know how much time MIR borrowck spends in the -trait checker. We can ask this using a more complex regex: +Perhaps we'd like to know how much time MIR borrowck spends in the trait checker. +We can ask this using a more complex regex: ```bash $ perf focus '{do_mir_borrowck}..{^rustc::traits}' @@ -225,23 +223,21 @@ Percentage : 0% Here we used the `..` operator to ask "how often do we have `do_mir_borrowck` on the stack and then, later, some function whose -name begins with `rustc::traits`?" (basically, code in that module). It -turns out the answer is "almost never" — only 12 samples fit that -description (if you ever see *no* samples, that often indicates your -query is messed up). +name begins with `rustc::traits`?" (basically, code in that module). +It turns out the answer is "almost never" — only 12 samples fit that +description (if you ever see *no* samples, that often indicates your query is messed up). -If you're curious, you can find out exactly which samples by using the -`--print-match` option. This will print out the full backtrace for -each sample. The `|` at the front of the line indicates the part that -the regular expression matched. +If you're curious, you can find out exactly which samples by using the `--print-match` option. +This will print out the full backtrace for each sample. +The `|` at the front of the line indicates the part that the regular expression matched. ### Example: Where does MIR borrowck spend its time? -Often we want to do more "explorational" queries. Like, we know that -MIR borrowck is 29% of the time, but where does that time get spent? -For that, the `--tree-callees` option is often the best tool. You -usually also want to give `--tree-min-percent` or -`--tree-max-depth`. The result looks like this: +Often we want to do more "explorational" queries. +Like, we know that MIR borrowck is 29% of the time, but where does that time get spent? +For that, the `--tree-callees` option is often the best tool. +You usually also want to give `--tree-min-percent` or `--tree-max-depth`. +The result looks like this: ```bash $ perf focus '{do_mir_borrowck}' --tree-callees --tree-min-percent 3 @@ -265,33 +261,30 @@ Tree What happens with `--tree-callees` is that - we find each sample matching the regular expression -- we look at the code that occurs *after* the regex match and try - to build up a call tree +- we look at the code that occurs *after* the regex match and try to build up a call tree -The `--tree-min-percent 3` option says "only show me things that take -more than 3% of the time". Without this, the tree often gets really -noisy and includes random stuff like the innards of -malloc. `--tree-max-depth` can be useful too, it just limits how many -levels we print. +The `--tree-min-percent 3` option says "only show me things that take more than 3% of the time". +Without this, +the tree often gets really noisy and includes random stuff like the innards of malloc. +`--tree-max-depth` can be useful too, it just limits how many levels we print. For each line, we display the percent of time in that function altogether ("total") and the percent of time spent in **just that -function and not some callee of that function** (self). Usually -"total" is the more interesting number, but not always. +function and not some callee of that function** (self). +Usually "total" is the more interesting number, but not always. ### Relative percentages By default, all in perf-focus are relative to the **total program execution**. This is useful to help you keep perspective — often as we drill down to find hot spots, we can lose sight of the fact that, -in terms of overall program execution, this "hot spot" is actually not -important. It also ensures that percentages between different queries -are easily compared against one another. +in terms of overall program execution, this "hot spot" is actually not important. +It also ensures that percentages between different queries are easily compared against one another. That said, sometimes it's useful to get relative percentages, so `perf -focus` offers a `--relative` option. In this case, the percentages are -listed only for samples that match (vs all samples). So for example we -could get our percentages relative to the borrowck itself +focus` offers a `--relative` option. +In this case, the percentages are listed only for samples that match (vs all samples). +So for example we could get our percentages relative to the borrowck itself like so: ```bash @@ -310,6 +303,7 @@ Tree ``` Here you see that `compute_regions` came up as "47% total" — that -means that 47% of `do_mir_borrowck` is spent in that function. Before, +means that 47% of `do_mir_borrowck` is spent in that function. +Before, we saw 20% — that's because `do_mir_borrowck` itself is only 43% of the total time (and `.47 * .43 = .20`). diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-gui-test-suite.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-gui-test-suite.md index d18021bf278d9..951a541548b23 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-gui-test-suite.md +++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-gui-test-suite.md @@ -5,7 +5,9 @@ This page is about the test suite named `rustdoc-gui` used to test the "GUI" of `rustdoc` (i.e., the HTML/JS/CSS as rendered in a browser). For other rustdoc-specific test suites, see [Rustdoc test suites]. -These use a NodeJS-based tool called [`browser-UI-test`] that uses [puppeteer] to run tests in a headless browser and check rendering and interactivity. For information on how to write this form of test, see [`tests/rustdoc-gui/README.md`][rustdoc-gui-readme] as well as [the description of the `.goml` format][goml-script] +These use a NodeJS-based tool called [`browser-UI-test`] that uses [puppeteer] to run tests in a headless browser and check rendering and interactivity. +For information on how to write this form of test, +see [`tests/rustdoc-gui/README.md`][rustdoc-gui-readme] as well as [the description of the `.goml` format][goml-script]. [Rustdoc test suites]: ../tests/compiletest.md#rustdoc-test-suites [`browser-UI-test`]: https://github.com/GuillaumeGomez/browser-UI-test/ diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md index e88d2703e3a23..ac2b4da5c652e 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md +++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md @@ -1,16 +1,13 @@ # Rustdoc search -Rustdoc Search is two programs: `search_index.rs` -and `search.js`. The first generates a nasty JSON -file with a full list of items and function signatures +Rustdoc Search is two programs: `search_index.rs` and `search.js`. +The first generates a nasty JSON file with a full list of items and function signatures in the crates in the doc bundle, and the second reads -it, turns it into some in-memory structures, and -scans them linearly to search. +it, turns it into some in-memory structures, and scans them linearly to search. ## Search index format -`search.js` calls this Raw, because it turns it into -a more normal object tree after loading it. +`search.js` calls this Raw, because it turns it into a more normal object tree after loading it. For space savings, it's also written without newlines or spaces. ```json @@ -44,8 +41,7 @@ For space savings, it's also written without newlines or spaces. ] ``` -[`src/librustdoc/html/static/js/rustdoc.d.ts`] -defines an actual schema in a TypeScript `type`. +[`src/librustdoc/html/static/js/rustdoc.d.ts`] defines an actual schema in a TypeScript `type`. | Key | Name | Description | | --- | -------------------- | ------------ | @@ -75,10 +71,8 @@ It makes a lot of compromises: * The `rustdoc` compiler runs on one crate at a time, so each crate has an essentially separate search index. - It [merges] them by having each crate on one line - and looking at the first quoted string. -* Names in the search index are given - in their original case and with underscores. + It [merges] them by having each crate on one line and looking at the first quoted string. +* Names in the search index are given in their original case and with underscores. When the search index is loaded, `search.js` stores the original names for display, but also folds them to lowercase and strips underscores for search. @@ -156,10 +150,8 @@ for (i, entry) in search_index.iter().enumerate() { } ``` -This is valid because everything has a parent module -(even if it's just the crate itself), -and is easy to assemble because the rustdoc generator sorts by path -before serializing. +This is valid because everything has a parent module (even if it's just the crate itself), +and is easy to assemble because the rustdoc generator sorts by path before serializing. Doing this allows rustdoc to not only make the search index smaller, but reuse the same string representing the parent path across multiple in-memory items. @@ -233,21 +225,21 @@ work through a "sandwich workload" of three steps: Reducing the amount of data downloaded here will almost always increase latency, by delaying the decision of what to download behind other work and/or adding -data dependencies where something can't be downloaded without first downloading -something else. In this case, we can't start downloading descriptions until +data dependencies where something can't be downloaded without first downloading something else. +In this case, we can't start downloading descriptions until after the search is done, because that's what allows it to decide *which* descriptions to download (it needs to sort the results then truncate to 200). To do this, two columns are stored in the search index, building on both Roaring Bitmaps and on VLQ Hex. -* `e` is an index of **e**mpty descriptions. It's a [roaring bitmap] of - each item (the crate itself is item 0, the rest start at 1). +* `e` is an index of **e**mpty descriptions. + It's a [roaring bitmap] of each item (the crate itself is item 0, the rest start at 1). * `D` is a shard list, stored in [VLQ hex] as flat list of integers. Each integer gives you the number of descriptions in the shard. As the decoder walks the index, it checks if the description is empty. - if it's not, then it's in the "current" shard. When all items are - exhausted, it goes on to the next shard. + if it's not, then it's in the "current" shard. + When all items are exhausted, it goes on to the next shard. Inside each shard is a newline-delimited list of descriptions, wrapped in a JSONP-style function call. @@ -280,8 +272,7 @@ Because of zigzag encoding, `` ` `` is +0, `a` is -0 (which is not used), ## Searching by name -Searching by name works by looping through the search index -and running these functions on each: +Searching by name works by looping through the search index and running these functions on each: * [`editDistance`] is always used to determine a match (unless quotes are specified, which would use simple equality instead). @@ -295,20 +286,19 @@ and running these functions on each: If it returns anything other than -1, the result is added, even if `editDistance` exceeds its threshold, and the index is stored for ranking. -* [`checkPath`] is used if, and only if, a parent path is specified - in the query. For example, `vec` has no parent path, but `vec::vec` does. +* [`checkPath`] is used if, and only if, a parent path is specified in the query. + For example, `vec` has no parent path, but `vec::vec` does. Within checkPath, editDistance and indexOf are used, and the path query has its own heuristic threshold, too. If it's not within the threshold, the entry is rejected, even if the first two pass. - If it's within the threshold, the path distance is stored - for ranking. + If it's within the threshold, the path distance is stored for ranking. * [`checkType`] is used only if there's a type filter, - like the struct in `struct:vec`. If it fails, + like the struct in `struct:vec`. + If it fails, the entry is rejected. -If all four criteria pass -(plus the crate filter, which isn't technically part of the query), +If all four criteria pass (plus the crate filter, which isn't technically part of the query), the results are sorted by [`sortResults`]. [`editDistance`]: https://github.com/rust-lang/rust/blob/79b710c13968a1a48d94431d024d2b1677940866/src/librustdoc/html/static/js/search.js#L137 @@ -336,17 +326,18 @@ is going to match the same things `T, u32` matches (though rustdoc will detect this particular problem and warn about it). Then, when actually looping over each item, -the bloom filter will probably reject entries that don't have every -type mentioned in the query. +the bloom filter will probably reject entries that don't have every type mentioned in the query. For example, the bloom query allows a query of `i32 -> u32` to match a function with the type `i32, u32 -> bool`, but unification will reject it later. The unification filter ensures that: -* Bag semantics are respected. If you query says `i32, i32`, +* Bag semantics are respected. + If your query says `i32, i32`, then the function has to mention *two* i32s, not just one. -* Nesting semantics are respected. If your query says `vec