From f7d12b4eec92b403909e4ae743fbb77e59ce3994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Wed, 1 Jun 2022 23:13:46 +0200 Subject: [PATCH 01/15] Session object: Decouple e_flags from FileFlags --- compiler/rustc_codegen_ssa/src/back/metadata.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 6aa96f9f40300..4c330c5906ab8 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -130,7 +130,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option { let arch = match sess.target.options.cpu.as_ref() { "mips1" => elf::EF_MIPS_ARCH_1, @@ -149,7 +149,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option { // copied from `mips64el-linux-gnuabi64-gcc foo.c -c` @@ -160,17 +160,18 @@ pub(crate) fn create_object_file(sess: &Session) -> Option { // copied from `riscv64-linux-gnu-gcc foo.c -c`, note though // that the `+d` target feature represents whether the double // float abi is enabled. let e_flags = elf::EF_RISCV_RVC | elf::EF_RISCV_FLOAT_ABI_DOUBLE; - file.flags = FileFlags::Elf { e_flags }; + e_flags } - _ => {} + _ => 0, }; + file.flags = FileFlags::Elf { e_flags }; Some(file) } From 37fd2941a1290a287935988641dc14233ee5e236 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 18 Jun 2022 01:01:19 +0300 Subject: [PATCH 02/15] rustc_target: Remove some redundant target properties --- compiler/rustc_codegen_llvm/src/context.rs | 2 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 2 +- compiler/rustc_codegen_ssa/src/back/link.rs | 4 ++-- compiler/rustc_passes/src/weak_lang_items.rs | 2 +- compiler/rustc_target/src/asm/aarch64.rs | 2 +- compiler/rustc_target/src/spec/fuchsia_base.rs | 1 - compiler/rustc_target/src/spec/mod.rs | 12 ------------ compiler/rustc_target/src/spec/tests/tests_impl.rs | 5 +++++ .../src/spec/wasm32_unknown_emscripten.rs | 1 - 9 files changed, 11 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index b5c31fcebe0c2..c007728095f79 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -906,7 +906,7 @@ impl<'ll> CodegenCx<'ll, '_> { return eh_catch_typeinfo; } let tcx = self.tcx; - assert!(self.sess().target.is_like_emscripten); + assert!(self.sess().target.os == "emscripten"); let eh_catch_typeinfo = match tcx.lang_items().eh_catch_typeinfo() { Some(def_id) => self.get_static(def_id), _ => { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index a18f5b9dd7f9c..9f3647492877c 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -441,7 +441,7 @@ fn try_intrinsic<'ll>( bx.store(bx.const_i32(0), dest, ret_align); } else if wants_msvc_seh(bx.sess()) { codegen_msvc_try(bx, try_func, data, catch_func, dest); - } else if bx.sess().target.is_like_emscripten { + } else if bx.sess().target.os == "emscripten" { codegen_emcc_try(bx, try_func, data, catch_func, dest); } else { codegen_gnu_try(bx, try_func, data, catch_func, dest); diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index e70509f3ecc4b..aaf7e4bb0454d 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2031,7 +2031,7 @@ fn add_order_independent_options( add_link_script(cmd, sess, tmpdir, crate_type); - if sess.target.is_like_fuchsia && crate_type == CrateType::Executable { + if sess.target.os == "fuchsia" && crate_type == CrateType::Executable { let prefix = if sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::ADDRESS) { "asan/" } else { @@ -2051,7 +2051,7 @@ fn add_order_independent_options( cmd.no_crt_objects(); } - if sess.target.is_like_emscripten { + if sess.target.os == "emscripten" { cmd.arg("-s"); cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort { "DISABLE_EXCEPTION_CATCHING=1" diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index 5411946343b74..3291be05807f6 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -17,7 +17,7 @@ pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>, items: &mut lang_items::LanguageItem if items.eh_personality().is_none() { items.missing.push(LangItem::EhPersonality); } - if tcx.sess.target.is_like_emscripten && items.eh_catch_typeinfo().is_none() { + if tcx.sess.target.os == "emscripten" && items.eh_catch_typeinfo().is_none() { items.missing.push(LangItem::EhCatchTypeinfo); } diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs index fba8cc6ef8b4a..25842049413bd 100644 --- a/compiler/rustc_target/src/asm/aarch64.rs +++ b/compiler/rustc_target/src/asm/aarch64.rs @@ -74,7 +74,7 @@ impl AArch64InlineAsmRegClass { } pub fn target_reserves_x18(target: &Target) -> bool { - target.os == "android" || target.is_like_fuchsia || target.is_like_osx || target.is_like_windows + target.os == "android" || target.os == "fuchsia" || target.is_like_osx || target.is_like_windows } fn reserved_x18( diff --git a/compiler/rustc_target/src/spec/fuchsia_base.rs b/compiler/rustc_target/src/spec/fuchsia_base.rs index 04e30ff0c3e6e..b64875e32bdd7 100644 --- a/compiler/rustc_target/src/spec/fuchsia_base.rs +++ b/compiler/rustc_target/src/spec/fuchsia_base.rs @@ -28,7 +28,6 @@ pub fn opts() -> TargetOptions { dynamic_linking: true, executables: true, families: cvs!["unix"], - is_like_fuchsia: true, pre_link_args, pre_link_objects: crt_objects::new(&[ (LinkOutputKind::DynamicNoPicExe, &["Scrt1.o"]), diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 422af66787579..955fb1f3964f7 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1271,12 +1271,6 @@ pub struct TargetOptions { /// - uses SEH-based unwinding, /// - supports control flow guard mechanism. pub is_like_msvc: bool, - /// Whether the target toolchain is like Emscripten's. Only useful for compiling with - /// Emscripten toolchain. - /// Defaults to false. - pub is_like_emscripten: bool, - /// Whether the target toolchain is like Fuchsia's. - pub is_like_fuchsia: bool, /// Whether a target toolchain is like WASM. pub is_like_wasm: bool, /// Version of DWARF to use if not using the default. @@ -1503,9 +1497,7 @@ impl Default for TargetOptions { is_like_osx: false, is_like_solaris: false, is_like_windows: false, - is_like_emscripten: false, is_like_msvc: false, - is_like_fuchsia: false, is_like_wasm: false, dwarf_version: None, linker_is_gnu: true, @@ -2110,8 +2102,6 @@ impl Target { key!(is_like_solaris, bool); key!(is_like_windows, bool); key!(is_like_msvc, bool); - key!(is_like_emscripten, bool); - key!(is_like_fuchsia, bool); key!(is_like_wasm, bool); key!(dwarf_version, Option); key!(linker_is_gnu, bool); @@ -2358,8 +2348,6 @@ impl ToJson for Target { target_option_val!(is_like_solaris); target_option_val!(is_like_windows); target_option_val!(is_like_msvc); - target_option_val!(is_like_emscripten); - target_option_val!(is_like_fuchsia); target_option_val!(is_like_wasm); target_option_val!(dwarf_version); target_option_val!(linker_is_gnu); diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs index 6730319dcfae2..0865ca7ea7df0 100644 --- a/compiler/rustc_target/src/spec/tests/tests_impl.rs +++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs @@ -8,7 +8,12 @@ pub(super) fn test_target(target: Target) { impl Target { fn check_consistency(&self) { + assert_eq!(self.is_like_osx, self.vendor == "apple"); + assert_eq!(self.is_like_solaris, self.os == "solaris" || self.os == "illumos"); + assert_eq!(self.is_like_windows, self.os == "windows" || self.os == "uefi"); + assert_eq!(self.is_like_wasm, self.arch == "wasm32" || self.arch == "wasm64"); assert!(self.is_like_windows || !self.is_like_msvc); + // Check that LLD with the given flavor is treated identically to the linker it emulates. // If your target really needs to deviate from the rules below, except it and document the // reasons. diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs index 975051100b039..4f21b7020eca6 100644 --- a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs @@ -26,7 +26,6 @@ pub fn target() -> Target { // functionality, and a .wasm file. exe_suffix: ".js".into(), linker: None, - is_like_emscripten: true, panic_strategy: PanicStrategy::Unwind, post_link_args, families: cvs!["unix", "wasm"], From 3f12fa7fda96de6687cdd281affcee4a61c35b80 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 26 Nov 2021 21:20:24 +0100 Subject: [PATCH 03/15] Add support for macro in "jump to def" feature --- src/librustdoc/html/format.rs | 1 + src/librustdoc/html/highlight.rs | 126 +++++++++++++++++++++---- src/librustdoc/html/render/span_map.rs | 71 ++++++++++---- 3 files changed, 162 insertions(+), 36 deletions(-) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 5baa53d55545f..29a58810036c6 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -519,6 +519,7 @@ impl clean::GenericArgs { } // Possible errors when computing href link source for a `DefId` +#[derive(PartialEq, Eq)] pub(crate) enum HrefError { /// This item is known to rustdoc, but from a crate that does not have documentation generated. /// diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 480728b179790..209172bb98e67 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -5,15 +5,19 @@ //! //! Use the `render_with_highlighting` to highlight some rust code. -use crate::clean::PrimitiveType; +use crate::clean::{ExternalLocation, PrimitiveType}; use crate::html::escape::Escape; use crate::html::render::Context; use std::collections::VecDeque; use std::fmt::{Display, Write}; +use std::iter::once; +use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::DefId; use rustc_lexer::{LiteralKind, TokenKind}; +use rustc_metadata::creader::{CStore, LoadedMacro}; use rustc_span::edition::Edition; use rustc_span::symbol::Symbol; use rustc_span::{BytePos, Span, DUMMY_SP}; @@ -99,6 +103,7 @@ fn write_code( ) { // This replace allows to fix how the code source with DOS backline characters is displayed. let src = src.replace("\r\n", "\n"); + let mut closing_tag = ""; Classifier::new( &src, edition, @@ -108,8 +113,8 @@ fn write_code( .highlight(&mut |highlight| { match highlight { Highlight::Token { text, class } => string(out, Escape(text), class, &context_info), - Highlight::EnterSpan { class } => enter_span(out, class), - Highlight::ExitSpan => exit_span(out), + Highlight::EnterSpan { class } => closing_tag = enter_span(out, class, &context_info), + Highlight::ExitSpan => exit_span(out, &closing_tag), }; }); } @@ -129,7 +134,7 @@ enum Class { RefKeyWord, Self_(Span), Op, - Macro, + Macro(Span), MacroNonTerminal, String, Number, @@ -153,7 +158,7 @@ impl Class { Class::RefKeyWord => "kw-2", Class::Self_(_) => "self", Class::Op => "op", - Class::Macro => "macro", + Class::Macro(_) => "macro", Class::MacroNonTerminal => "macro-nonterminal", Class::String => "string", Class::Number => "number", @@ -171,8 +176,22 @@ impl Class { /// a "span" (a tuple representing `(lo, hi)` equivalent of `Span`). fn get_span(self) -> Option { match self { - Self::Ident(sp) | Self::Self_(sp) => Some(sp), - _ => None, + Self::Ident(sp) | Self::Self_(sp) | Self::Macro(sp) => Some(sp), + Self::Comment + | Self::DocComment + | Self::Attribute + | Self::KeyWord + | Self::RefKeyWord + | Self::Op + | Self::MacroNonTerminal + | Self::String + | Self::Number + | Self::Bool + | Self::Lifetime + | Self::PreludeTy + | Self::PreludeVal + | Self::QuestionMark + | Self::Decoration(_) => None, } } } @@ -611,7 +630,7 @@ impl<'a> Classifier<'a> { }, TokenKind::Ident | TokenKind::RawIdent if lookahead == Some(TokenKind::Bang) => { self.in_macro = true; - sink(Highlight::EnterSpan { class: Class::Macro }); + sink(Highlight::EnterSpan { class: Class::Macro(self.new_span(before, text)) }); sink(Highlight::Token { text, class: None }); return; } @@ -658,13 +677,18 @@ impl<'a> Classifier<'a> { /// Called when we start processing a span of text that should be highlighted. /// The `Class` argument specifies how it should be highlighted. -fn enter_span(out: &mut Buffer, klass: Class) { - write!(out, "", klass.as_html()); +fn enter_span( + out: &mut Buffer, + klass: Class, + context_info: &Option>, +) -> &'static str { + string_without_closing_tag(out, "", Some(klass), context_info) + .expect("no closing tag to close wrapper...") } /// Called at the end of a span of highlighted text. -fn exit_span(out: &mut Buffer) { - out.write_str(""); +fn exit_span(out: &mut Buffer, closing_tag: &str) { + out.write_str(closing_tag); } /// Called for a span of text. If the text should be highlighted differently @@ -689,13 +713,28 @@ fn string( klass: Option, context_info: &Option>, ) { + if let Some(closing_tag) = string_without_closing_tag(out, text, klass, context_info) { + out.write_str(closing_tag); + } +} + +fn string_without_closing_tag( + out: &mut Buffer, + text: T, + klass: Option, + context_info: &Option>, +) -> Option<&'static str> { let Some(klass) = klass - else { return write!(out, "{}", text) }; + else { + write!(out, "{}", text); + return None; + }; let Some(def_span) = klass.get_span() else { - write!(out, "{}", klass.as_html(), text); - return; + write!(out, "{}", klass.as_html(), text); + return Some(""); }; + let mut text_s = text.to_string(); if text_s.contains("::") { text_s = text_s.split("::").intersperse("::").fold(String::new(), |mut path, t| { @@ -730,8 +769,17 @@ fn string( .map(|s| format!("{}{}", context_info.root_path, s)), LinkFromSrc::External(def_id) => { format::href_with_root_path(*def_id, context, Some(context_info.root_path)) - .ok() .map(|(url, _, _)| url) + .or_else(|e| { + if e == format::HrefError::NotInExternalCache + && matches!(klass, Class::Macro(_)) + { + Ok(generate_macro_def_id_path(context_info, *def_id)) + } else { + Err(e) + } + }) + .ok() } LinkFromSrc::Primitive(prim) => format::href_with_root_path( PrimitiveType::primitive_locations(context.tcx())[prim], @@ -743,11 +791,51 @@ fn string( } }) { - write!(out, "{}", klass.as_html(), href, text_s); - return; + write!(out, "{}", klass.as_html(), href, text_s); + return Some(""); } } - write!(out, "{}", klass.as_html(), text_s); + write!(out, "{}", klass.as_html(), text_s); + Some("") +} + +/// This function is to get the external macro path because they are not in the cache used n +/// `href_with_root_path`. +fn generate_macro_def_id_path(context_info: &ContextInfo<'_, '_, '_>, def_id: DefId) -> String { + let tcx = context_info.context.shared.tcx; + let crate_name = tcx.crate_name(def_id.krate).to_string(); + let cache = &context_info.context.cache(); + + let relative = tcx.def_path(def_id).data.into_iter().filter_map(|elem| { + // extern blocks have an empty name + let s = elem.data.to_string(); + if !s.is_empty() { Some(s) } else { None } + }); + // Check to see if it is a macro 2.0 or built-in macro + let mut path = if matches!( + CStore::from_tcx(tcx).load_macro_untracked(def_id, tcx.sess), + LoadedMacro::MacroDef(def, _) + if matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) + if !ast_def.macro_rules) + ) { + once(crate_name.clone()).chain(relative).collect() + } else { + vec![crate_name.clone(), relative.last().expect("relative was empty")] + }; + + let url_parts = match cache.extern_locations[&def_id.krate] { + ExternalLocation::Remote(ref s) => vec![s.trim_end_matches('/')], + ExternalLocation::Local => vec![context_info.root_path.trim_end_matches('/'), &crate_name], + ExternalLocation::Unknown => panic!("unknown crate"), + }; + + let last = path.pop().unwrap(); + let last = format!("macro.{}.html", last); + if path.is_empty() { + format!("{}/{}", url_parts.join("/"), last) + } else { + format!("{}/{}/{}", url_parts.join("/"), path.join("/"), last) + } } #[cfg(test)] diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 86961dc3bf149..0c60278a82dd9 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -8,7 +8,8 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ExprKind, HirId, Mod, Node}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; -use rustc_span::Span; +use rustc_span::hygiene::MacroKind; +use rustc_span::{BytePos, ExpnKind, Span}; use std::path::{Path, PathBuf}; @@ -63,32 +64,59 @@ struct SpanMapVisitor<'tcx> { impl<'tcx> SpanMapVisitor<'tcx> { /// This function is where we handle `hir::Path` elements and add them into the "span map". - fn handle_path(&mut self, path: &rustc_hir::Path<'_>, path_span: Option) { + fn handle_path(&mut self, path: &rustc_hir::Path<'_>) { let info = match path.res { - // FIXME: For now, we only handle `DefKind` if it's not `DefKind::TyParam` or - // `DefKind::Macro`. Would be nice to support them too alongside the other `DefKind` + // FIXME: For now, we handle `DefKind` if it's not a `DefKind::TyParam`. + // Would be nice to support them too alongside the other `DefKind` // (such as primitive types!). - Res::Def(kind, def_id) if kind != DefKind::TyParam => { - if matches!(kind, DefKind::Macro(_)) { - return; - } - Some(def_id) - } + Res::Def(kind, def_id) if kind != DefKind::TyParam => Some(def_id), Res::Local(_) => None, Res::PrimTy(p) => { // FIXME: Doesn't handle "path-like" primitives like arrays or tuples. - let span = path_span.unwrap_or(path.span); - self.matches.insert(span, LinkFromSrc::Primitive(PrimitiveType::from(p))); + self.matches.insert(path.span, LinkFromSrc::Primitive(PrimitiveType::from(p))); return; } Res::Err => return, _ => return, }; if let Some(span) = self.tcx.hir().res_span(path.res) { - self.matches - .insert(path_span.unwrap_or(path.span), LinkFromSrc::Local(clean::Span::new(span))); + self.matches.insert(path.span, LinkFromSrc::Local(clean::Span::new(span))); } else if let Some(def_id) = info { - self.matches.insert(path_span.unwrap_or(path.span), LinkFromSrc::External(def_id)); + self.matches.insert(path.span, LinkFromSrc::External(def_id)); + } + } + + /// Adds the macro call into the span map. Returns `true` if the `span` was inside a macro + /// expansion, whether or not it was added to the span map. + fn handle_macro(&mut self, span: Span) -> bool { + if span.from_expansion() { + let mut data = span.ctxt().outer_expn_data(); + let mut call_site = data.call_site; + while call_site.from_expansion() { + data = call_site.ctxt().outer_expn_data(); + call_site = data.call_site; + } + + if let ExpnKind::Macro(MacroKind::Bang, macro_name) = data.kind { + let link_from_src = if let Some(macro_def_id) = data.macro_def_id { + if macro_def_id.is_local() { + LinkFromSrc::Local(clean::Span::new(data.def_site)) + } else { + LinkFromSrc::External(macro_def_id) + } + } else { + return true; + }; + let new_span = data.call_site; + let macro_name = macro_name.as_str(); + // The "call_site" includes the whole macro with its "arguments". We only want + // the macro name. + let new_span = new_span.with_hi(new_span.lo() + BytePos(macro_name.len() as u32)); + self.matches.insert(new_span, link_from_src); + } + true + } else { + false } } } @@ -101,7 +129,10 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { } fn visit_path(&mut self, path: &'tcx rustc_hir::Path<'tcx>, _id: HirId) { - self.handle_path(path, None); + if self.handle_macro(path.span) { + return; + } + self.handle_path(path); intravisit::walk_path(self, path); } @@ -143,12 +174,18 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { ); } } + } else if self.handle_macro(expr.span) { + // We don't want to deeper into the macro. + return; } intravisit::walk_expr(self, expr); } fn visit_use(&mut self, path: &'tcx rustc_hir::Path<'tcx>, id: HirId) { - self.handle_path(path, None); + if self.handle_macro(path.span) { + return; + } + self.handle_path(path); intravisit::walk_use(self, path, id); } } From dda980dec07fb7093a153180f19f967ce55fe1f9 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 18 Jun 2022 15:32:26 +0200 Subject: [PATCH 04/15] Rename ContextInfo into HrefContext --- src/librustdoc/html/highlight.rs | 50 ++++++++++++++++---------------- src/librustdoc/html/sources.rs | 2 +- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 209172bb98e67..4c54b569762c6 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -26,7 +26,7 @@ use super::format::{self, Buffer}; use super::render::LinkFromSrc; /// This type is needed in case we want to render links on items to allow to go to their definition. -pub(crate) struct ContextInfo<'a, 'b, 'c> { +pub(crate) struct HrefContext<'a, 'b, 'c> { pub(crate) context: &'a Context<'b>, /// This span contains the current file we're going through. pub(crate) file_span: Span, @@ -48,7 +48,7 @@ pub(crate) fn render_with_highlighting( tooltip: Option<(Option, &str)>, edition: Edition, extra_content: Option, - context_info: Option>, + href_context: Option>, decoration_info: Option, ) { debug!("highlighting: ================\n{}\n==============", src); @@ -66,7 +66,7 @@ pub(crate) fn render_with_highlighting( } write_header(out, class, extra_content); - write_code(out, src, edition, context_info, decoration_info); + write_code(out, src, edition, href_context, decoration_info); write_footer(out, playground_button); } @@ -89,8 +89,8 @@ fn write_header(out: &mut Buffer, class: Option<&str>, extra_content: Option>, + href_context: Option>, decoration_info: Option, ) { // This replace allows to fix how the code source with DOS backline characters is displayed. @@ -107,13 +107,13 @@ fn write_code( Classifier::new( &src, edition, - context_info.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP), + href_context.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP), decoration_info, ) .highlight(&mut |highlight| { match highlight { - Highlight::Token { text, class } => string(out, Escape(text), class, &context_info), - Highlight::EnterSpan { class } => closing_tag = enter_span(out, class, &context_info), + Highlight::Token { text, class } => string(out, Escape(text), class, &href_context), + Highlight::EnterSpan { class } => closing_tag = enter_span(out, class, &href_context), Highlight::ExitSpan => exit_span(out, &closing_tag), }; }); @@ -680,9 +680,9 @@ impl<'a> Classifier<'a> { fn enter_span( out: &mut Buffer, klass: Class, - context_info: &Option>, + href_context: &Option>, ) -> &'static str { - string_without_closing_tag(out, "", Some(klass), context_info) + string_without_closing_tag(out, "", Some(klass), href_context) .expect("no closing tag to close wrapper...") } @@ -711,9 +711,9 @@ fn string( out: &mut Buffer, text: T, klass: Option, - context_info: &Option>, + href_context: &Option>, ) { - if let Some(closing_tag) = string_without_closing_tag(out, text, klass, context_info) { + if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context) { out.write_str(closing_tag); } } @@ -722,7 +722,7 @@ fn string_without_closing_tag( out: &mut Buffer, text: T, klass: Option, - context_info: &Option>, + href_context: &Option>, ) -> Option<&'static str> { let Some(klass) = klass else { @@ -754,10 +754,10 @@ fn string_without_closing_tag( path }); } - if let Some(context_info) = context_info { + if let Some(href_context) = href_context { if let Some(href) = - context_info.context.shared.span_correspondance_map.get(&def_span).and_then(|href| { - let context = context_info.context; + href_context.context.shared.span_correspondance_map.get(&def_span).and_then(|href| { + let context = href_context.context; // FIXME: later on, it'd be nice to provide two links (if possible) for all items: // one to the documentation page and one to the source definition. // FIXME: currently, external items only generate a link to their documentation, @@ -766,15 +766,15 @@ fn string_without_closing_tag( match href { LinkFromSrc::Local(span) => context .href_from_span(*span, true) - .map(|s| format!("{}{}", context_info.root_path, s)), + .map(|s| format!("{}{}", href_context.root_path, s)), LinkFromSrc::External(def_id) => { - format::href_with_root_path(*def_id, context, Some(context_info.root_path)) + format::href_with_root_path(*def_id, context, Some(href_context.root_path)) .map(|(url, _, _)| url) .or_else(|e| { if e == format::HrefError::NotInExternalCache && matches!(klass, Class::Macro(_)) { - Ok(generate_macro_def_id_path(context_info, *def_id)) + Ok(generate_macro_def_id_path(href_context, *def_id)) } else { Err(e) } @@ -784,7 +784,7 @@ fn string_without_closing_tag( LinkFromSrc::Primitive(prim) => format::href_with_root_path( PrimitiveType::primitive_locations(context.tcx())[prim], context, - Some(context_info.root_path), + Some(href_context.root_path), ) .ok() .map(|(url, _, _)| url), @@ -801,10 +801,10 @@ fn string_without_closing_tag( /// This function is to get the external macro path because they are not in the cache used n /// `href_with_root_path`. -fn generate_macro_def_id_path(context_info: &ContextInfo<'_, '_, '_>, def_id: DefId) -> String { - let tcx = context_info.context.shared.tcx; +fn generate_macro_def_id_path(href_context: &HrefContext<'_, '_, '_>, def_id: DefId) -> String { + let tcx = href_context.context.shared.tcx; let crate_name = tcx.crate_name(def_id.krate).to_string(); - let cache = &context_info.context.cache(); + let cache = &href_context.context.cache(); let relative = tcx.def_path(def_id).data.into_iter().filter_map(|elem| { // extern blocks have an empty name @@ -825,7 +825,7 @@ fn generate_macro_def_id_path(context_info: &ContextInfo<'_, '_, '_>, def_id: De let url_parts = match cache.extern_locations[&def_id.krate] { ExternalLocation::Remote(ref s) => vec![s.trim_end_matches('/')], - ExternalLocation::Local => vec![context_info.root_path.trim_end_matches('/'), &crate_name], + ExternalLocation::Local => vec![href_context.root_path.trim_end_matches('/'), &crate_name], ExternalLocation::Unknown => panic!("unknown crate"), }; diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 524c90e1f4d64..bc0a56f2b052f 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -299,7 +299,7 @@ pub(crate) fn print_src( None, edition, Some(line_numbers), - Some(highlight::ContextInfo { context, file_span, root_path }), + Some(highlight::HrefContext { context, file_span, root_path }), decoration_info, ); } From 810254b31e0135d1f2dfe9e3717c3c0f69676a86 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 18 Jun 2022 15:50:37 +0200 Subject: [PATCH 05/15] Improve code readability and documentation --- src/librustdoc/html/highlight.rs | 80 +++++++++++++++++--------- src/librustdoc/html/render/span_map.rs | 68 +++++++++++++--------- 2 files changed, 94 insertions(+), 54 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 4c54b569762c6..11ab3a3f931c3 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -103,7 +103,7 @@ fn write_code( ) { // This replace allows to fix how the code source with DOS backline characters is displayed. let src = src.replace("\r\n", "\n"); - let mut closing_tag = ""; + let mut closing_tags: Vec<&'static str> = Vec::new(); Classifier::new( &src, edition, @@ -113,8 +113,12 @@ fn write_code( .highlight(&mut |highlight| { match highlight { Highlight::Token { text, class } => string(out, Escape(text), class, &href_context), - Highlight::EnterSpan { class } => closing_tag = enter_span(out, class, &href_context), - Highlight::ExitSpan => exit_span(out, &closing_tag), + Highlight::EnterSpan { class } => { + closing_tags.push(enter_span(out, class, &href_context)) + } + Highlight::ExitSpan => { + exit_span(out, closing_tags.pop().expect("ExitSpan without EnterSpan")) + } }; }); } @@ -682,8 +686,10 @@ fn enter_span( klass: Class, href_context: &Option>, ) -> &'static str { - string_without_closing_tag(out, "", Some(klass), href_context) - .expect("no closing tag to close wrapper...") + string_without_closing_tag(out, "", Some(klass), href_context).expect( + "internal error: enter_span was called with Some(klass) but did not return a \ + closing HTML tag", + ) } /// Called at the end of a span of highlighted text. @@ -718,6 +724,15 @@ fn string( } } +/// This function writes `text` into `out` with some modifications depending on `klass`: +/// +/// * If `klass` is `None`, `text` is written into `out` with no modification. +/// * If `klass` is `Some` but `klass.get_span()` is `None`, it writes the text wrapped in a +/// `` with the provided `klass`. +/// * If `klass` is `Some` and has a [`rustc_span::Span`], it then tries to generate a link (`` +/// element) by retrieving the link information from the `span_correspondance_map` that was filled +/// in `span_map.rs::collect_spans_and_sources`. If it cannot retrieve the information, then it's +/// the same as the second point (`klass` is `Some` but doesn't have a [`rustc_span::Span`]). fn string_without_closing_tag( out: &mut Buffer, text: T, @@ -799,42 +814,55 @@ fn string_without_closing_tag( Some("") } -/// This function is to get the external macro path because they are not in the cache used n +/// This function is to get the external macro path because they are not in the cache used in /// `href_with_root_path`. fn generate_macro_def_id_path(href_context: &HrefContext<'_, '_, '_>, def_id: DefId) -> String { let tcx = href_context.context.shared.tcx; let crate_name = tcx.crate_name(def_id.krate).to_string(); - let cache = &href_context.context.cache(); + let cache = href_context.context.cache(); let relative = tcx.def_path(def_id).data.into_iter().filter_map(|elem| { // extern blocks have an empty name let s = elem.data.to_string(); if !s.is_empty() { Some(s) } else { None } }); - // Check to see if it is a macro 2.0 or built-in macro - let mut path = if matches!( - CStore::from_tcx(tcx).load_macro_untracked(def_id, tcx.sess), - LoadedMacro::MacroDef(def, _) - if matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) - if !ast_def.macro_rules) - ) { + // Check to see if it is a macro 2.0 or built-in macro. + // More information in . + let is_macro_2 = match CStore::from_tcx(tcx).load_macro_untracked(def_id, tcx.sess) { + LoadedMacro::MacroDef(def, _) => { + // If `ast_def.macro_rules` is `true`, then it's not a macro 2.0. + matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) if !ast_def.macro_rules) + } + _ => false, + }; + + let mut path = if is_macro_2 { once(crate_name.clone()).chain(relative).collect() } else { - vec![crate_name.clone(), relative.last().expect("relative was empty")] + vec![crate_name.clone(), relative.last().unwrap()] }; + if path.len() < 2 { + // The minimum we can have is the crate name followed by the macro name. If shorter, then + // it means that that `relative` was empty, which is an error. + panic!("macro path cannot be empty!"); + } - let url_parts = match cache.extern_locations[&def_id.krate] { - ExternalLocation::Remote(ref s) => vec![s.trim_end_matches('/')], - ExternalLocation::Local => vec![href_context.root_path.trim_end_matches('/'), &crate_name], - ExternalLocation::Unknown => panic!("unknown crate"), - }; + if let Some(last) = path.last_mut() { + *last = format!("macro.{}.html", last); + } - let last = path.pop().unwrap(); - let last = format!("macro.{}.html", last); - if path.is_empty() { - format!("{}/{}", url_parts.join("/"), last) - } else { - format!("{}/{}/{}", url_parts.join("/"), path.join("/"), last) + match cache.extern_locations[&def_id.krate] { + ExternalLocation::Remote(ref s) => { + // `ExternalLocation::Remote` always end with a `/`. + format!("{}{}", s, path.join("/")) + } + ExternalLocation::Local => { + // `href_context.root_path` always end with a `/`. + format!("{}{}/{}", href_context.root_path, crate_name, path.join("/")) + } + ExternalLocation::Unknown => { + panic!("crate {} not in cache when linkifying macros", crate_name) + } } } diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 0c60278a82dd9..34d590fb2448c 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -88,36 +88,48 @@ impl<'tcx> SpanMapVisitor<'tcx> { /// Adds the macro call into the span map. Returns `true` if the `span` was inside a macro /// expansion, whether or not it was added to the span map. + /// + /// The idea for the macro support is to check if the current `Span` comes from expansion. If + /// so, we loop until we find the macro definition by using `outer_expn_data` in a loop. + /// Finally, we get the information about the macro itself (`span` if "local", `DefId` + /// otherwise) and store it inside the span map. fn handle_macro(&mut self, span: Span) -> bool { - if span.from_expansion() { - let mut data = span.ctxt().outer_expn_data(); - let mut call_site = data.call_site; - while call_site.from_expansion() { - data = call_site.ctxt().outer_expn_data(); - call_site = data.call_site; - } + if !span.from_expansion() { + return false; + } + // So if the `span` comes from a macro expansion, we need to get the original + // macro's `DefId`. + let mut data = span.ctxt().outer_expn_data(); + let mut call_site = data.call_site; + // Macros can expand to code containing macros, which will in turn be expanded, etc. + // So the idea here is to "go up" until we're back to code that was generated from + // macro expansion so that we can get the `DefId` of the original macro that was at the + // origin of this expansion. + while call_site.from_expansion() { + data = call_site.ctxt().outer_expn_data(); + call_site = data.call_site; + } - if let ExpnKind::Macro(MacroKind::Bang, macro_name) = data.kind { - let link_from_src = if let Some(macro_def_id) = data.macro_def_id { - if macro_def_id.is_local() { - LinkFromSrc::Local(clean::Span::new(data.def_site)) - } else { - LinkFromSrc::External(macro_def_id) - } - } else { - return true; - }; - let new_span = data.call_site; - let macro_name = macro_name.as_str(); - // The "call_site" includes the whole macro with its "arguments". We only want - // the macro name. - let new_span = new_span.with_hi(new_span.lo() + BytePos(macro_name.len() as u32)); - self.matches.insert(new_span, link_from_src); + let macro_name = match data.kind { + ExpnKind::Macro(MacroKind::Bang, macro_name) => macro_name, + // Even though we don't handle this kind of macro, this `data` still comes from + // expansion so we return `true` so we don't go any deeper in this code. + _ => return true, + }; + let link_from_src = match data.macro_def_id { + Some(macro_def_id) if macro_def_id.is_local() => { + LinkFromSrc::Local(clean::Span::new(data.def_site)) } - true - } else { - false - } + Some(macro_def_id) => LinkFromSrc::External(macro_def_id), + None => return true, + }; + let new_span = data.call_site; + let macro_name = macro_name.as_str(); + // The "call_site" includes the whole macro with its "arguments". We only want + // the macro name. + let new_span = new_span.with_hi(new_span.lo() + BytePos(macro_name.len() as u32)); + self.matches.insert(new_span, link_from_src); + true } } @@ -175,7 +187,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { } } } else if self.handle_macro(expr.span) { - // We don't want to deeper into the macro. + // We don't want to go deeper into the macro. return; } intravisit::walk_expr(self, expr); From f4db07ed4c1ab8a0f7961efc60ec32193e971f5c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 26 Nov 2021 21:20:45 +0100 Subject: [PATCH 06/15] Add test for macro support in "jump to def" feature --- .../check-source-code-urls-to-def-std.rs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/test/rustdoc/check-source-code-urls-to-def-std.rs b/src/test/rustdoc/check-source-code-urls-to-def-std.rs index b129ceb5b7302..3396b234a77b1 100644 --- a/src/test/rustdoc/check-source-code-urls-to-def-std.rs +++ b/src/test/rustdoc/check-source-code-urls-to-def-std.rs @@ -15,3 +15,28 @@ pub fn foo(a: u32, b: &str, c: String) { let y: bool = true; babar(); } + +macro_rules! yolo { () => {}} + +fn bar(a: i32) {} + +macro_rules! bar { + ($a:ident) => { bar($a) } +} + +macro_rules! data { + ($x:expr) => { $x * 2 } +} + +pub fn another_foo() { + // This is known limitation: if the macro doesn't generate anything, the visitor + // can't find any item or anything that could tell us that it comes from expansion. + // @!has - '//a[@href="../../src/foo/check-source-code-urls-to-def-std.rs.html#19"]' 'yolo!' + yolo!(); + // @has - '//a[@href="{{channel}}/std/macro.eprintln.html"]' 'eprintln!' + eprintln!(); + // @has - '//a[@href="../../src/foo/check-source-code-urls-to-def-std.rs.html#27-29"]' 'data!' + let x = data!(4); + // @has - '//a[@href="../../src/foo/check-source-code-urls-to-def-std.rs.html#23-25"]' 'bar!' + bar!(x); +} From 987c73158e2120ef75b4b7fc46dcd88a621106d8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 20 Jun 2022 22:32:49 +0200 Subject: [PATCH 07/15] Integrate `generate_macro_def_id_path` into `href_with_root_path` --- src/librustdoc/html/format.rs | 71 +++++++++++++++++++++++++++++++- src/librustdoc/html/highlight.rs | 69 +------------------------------ 2 files changed, 72 insertions(+), 68 deletions(-) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 29a58810036c6..67b01245ef7ad 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -8,14 +8,16 @@ use std::borrow::Cow; use std::cell::Cell; use std::fmt; -use std::iter; +use std::iter::{self, once}; +use rustc_ast as ast; use rustc_attr::{ConstStability, StabilityLevel}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; +use rustc_metadata::creader::{CStore, LoadedMacro}; use rustc_middle::ty; use rustc_middle::ty::DefIdTree; use rustc_middle::ty::TyCtxt; @@ -557,6 +559,71 @@ pub(crate) fn join_with_double_colon(syms: &[Symbol]) -> String { s } +/// This function is to get the external macro path because they are not in the cache used in +/// `href_with_root_path`. +fn generate_macro_def_id_path( + def_id: DefId, + cx: &Context<'_>, + root_path: Option<&str>, +) -> (String, ItemType, Vec) { + let tcx = cx.shared.tcx; + let crate_name = tcx.crate_name(def_id.krate).to_string(); + let cache = cx.cache(); + + let fqp: Vec = tcx + .def_path(def_id) + .data + .into_iter() + .filter_map(|elem| { + // extern blocks (and a few others things) have an empty name. + match elem.data.get_opt_name() { + Some(s) if !s.is_empty() => Some(s), + _ => None, + } + }) + .collect(); + let relative = fqp.iter().map(|elem| elem.to_string()); + // Check to see if it is a macro 2.0 or built-in macro. + // More information in . + let is_macro_2 = match CStore::from_tcx(tcx).load_macro_untracked(def_id, tcx.sess) { + LoadedMacro::MacroDef(def, _) => { + // If `ast_def.macro_rules` is `true`, then it's not a macro 2.0. + matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) if !ast_def.macro_rules) + } + _ => false, + }; + + let mut path = if is_macro_2 { + once(crate_name.clone()).chain(relative).collect() + } else { + vec![crate_name.clone(), relative.last().unwrap()] + }; + if path.len() < 2 { + // The minimum we can have is the crate name followed by the macro name. If shorter, then + // it means that that `relative` was empty, which is an error. + panic!("macro path cannot be empty!"); + } + + if let Some(last) = path.last_mut() { + *last = format!("macro.{}.html", last); + } + + let url = match cache.extern_locations[&def_id.krate] { + ExternalLocation::Remote(ref s) => { + // `ExternalLocation::Remote` always end with a `/`. + format!("{}{}", s, path.join("/")) + } + ExternalLocation::Local => { + // `root_path` always end with a `/`. + format!("{}{}/{}", root_path.unwrap_or(""), crate_name, path.join("/")) + } + ExternalLocation::Unknown => { + panic!("crate {} not in cache when linkifying macros", crate_name) + } + }; + (url, ItemType::Macro, fqp) +} + pub(crate) fn href_with_root_path( did: DefId, cx: &Context<'_>, @@ -612,6 +679,8 @@ pub(crate) fn href_with_root_path( ExternalLocation::Unknown => return Err(HrefError::DocumentationNotBuilt), }, ) + } else if matches!(def_kind, DefKind::Macro(_)) { + return Ok(generate_macro_def_id_path(did, cx, root_path)); } else { return Err(HrefError::NotInExternalCache); } diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 11ab3a3f931c3..d2ef89078bf6d 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -5,19 +5,15 @@ //! //! Use the `render_with_highlighting` to highlight some rust code. -use crate::clean::{ExternalLocation, PrimitiveType}; +use crate::clean::PrimitiveType; use crate::html::escape::Escape; use crate::html::render::Context; use std::collections::VecDeque; use std::fmt::{Display, Write}; -use std::iter::once; -use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def_id::DefId; use rustc_lexer::{LiteralKind, TokenKind}; -use rustc_metadata::creader::{CStore, LoadedMacro}; use rustc_span::edition::Edition; use rustc_span::symbol::Symbol; use rustc_span::{BytePos, Span, DUMMY_SP}; @@ -784,17 +780,8 @@ fn string_without_closing_tag( .map(|s| format!("{}{}", href_context.root_path, s)), LinkFromSrc::External(def_id) => { format::href_with_root_path(*def_id, context, Some(href_context.root_path)) - .map(|(url, _, _)| url) - .or_else(|e| { - if e == format::HrefError::NotInExternalCache - && matches!(klass, Class::Macro(_)) - { - Ok(generate_macro_def_id_path(href_context, *def_id)) - } else { - Err(e) - } - }) .ok() + .map(|(url, _, _)| url) } LinkFromSrc::Primitive(prim) => format::href_with_root_path( PrimitiveType::primitive_locations(context.tcx())[prim], @@ -814,57 +801,5 @@ fn string_without_closing_tag( Some("") } -/// This function is to get the external macro path because they are not in the cache used in -/// `href_with_root_path`. -fn generate_macro_def_id_path(href_context: &HrefContext<'_, '_, '_>, def_id: DefId) -> String { - let tcx = href_context.context.shared.tcx; - let crate_name = tcx.crate_name(def_id.krate).to_string(); - let cache = href_context.context.cache(); - - let relative = tcx.def_path(def_id).data.into_iter().filter_map(|elem| { - // extern blocks have an empty name - let s = elem.data.to_string(); - if !s.is_empty() { Some(s) } else { None } - }); - // Check to see if it is a macro 2.0 or built-in macro. - // More information in . - let is_macro_2 = match CStore::from_tcx(tcx).load_macro_untracked(def_id, tcx.sess) { - LoadedMacro::MacroDef(def, _) => { - // If `ast_def.macro_rules` is `true`, then it's not a macro 2.0. - matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) if !ast_def.macro_rules) - } - _ => false, - }; - - let mut path = if is_macro_2 { - once(crate_name.clone()).chain(relative).collect() - } else { - vec![crate_name.clone(), relative.last().unwrap()] - }; - if path.len() < 2 { - // The minimum we can have is the crate name followed by the macro name. If shorter, then - // it means that that `relative` was empty, which is an error. - panic!("macro path cannot be empty!"); - } - - if let Some(last) = path.last_mut() { - *last = format!("macro.{}.html", last); - } - - match cache.extern_locations[&def_id.krate] { - ExternalLocation::Remote(ref s) => { - // `ExternalLocation::Remote` always end with a `/`. - format!("{}{}", s, path.join("/")) - } - ExternalLocation::Local => { - // `href_context.root_path` always end with a `/`. - format!("{}{}/{}", href_context.root_path, crate_name, path.join("/")) - } - ExternalLocation::Unknown => { - panic!("crate {} not in cache when linkifying macros", crate_name) - } - } -} - #[cfg(test)] mod tests; From beb2f364cc85b4408da1d043f875d159003558e4 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 20 Jun 2022 23:31:40 +0200 Subject: [PATCH 08/15] Fix panic by checking if `CStore` has the crate data we want before actually querying it --- compiler/rustc_metadata/src/creader.rs | 4 ++++ src/librustdoc/html/format.rs | 20 ++++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 947d563ae3cd1..555db5846edd0 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -133,6 +133,10 @@ impl CStore { CrateNum::new(self.metas.len() - 1) } + pub fn has_crate_data(&self, cnum: CrateNum) -> bool { + self.metas[cnum].is_some() + } + pub(crate) fn get_crate_data(&self, cnum: CrateNum) -> CrateMetadataRef<'_> { let cdata = self.metas[cnum] .as_ref() diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 67b01245ef7ad..056eda089c1de 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -565,7 +565,7 @@ fn generate_macro_def_id_path( def_id: DefId, cx: &Context<'_>, root_path: Option<&str>, -) -> (String, ItemType, Vec) { +) -> Result<(String, ItemType, Vec), HrefError> { let tcx = cx.shared.tcx; let crate_name = tcx.crate_name(def_id.krate).to_string(); let cache = cx.cache(); @@ -583,9 +583,15 @@ fn generate_macro_def_id_path( }) .collect(); let relative = fqp.iter().map(|elem| elem.to_string()); + let cstore = CStore::from_tcx(tcx); + // We need this to prevent a `panic` when this function is used from intra doc links... + if !cstore.has_crate_data(def_id.krate) { + debug!("No data for crate {}", crate_name); + return Err(HrefError::NotInExternalCache); + } // Check to see if it is a macro 2.0 or built-in macro. // More information in . - let is_macro_2 = match CStore::from_tcx(tcx).load_macro_untracked(def_id, tcx.sess) { + let is_macro_2 = match cstore.load_macro_untracked(def_id, tcx.sess) { LoadedMacro::MacroDef(def, _) => { // If `ast_def.macro_rules` is `true`, then it's not a macro 2.0. matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) if !ast_def.macro_rules) @@ -601,7 +607,8 @@ fn generate_macro_def_id_path( if path.len() < 2 { // The minimum we can have is the crate name followed by the macro name. If shorter, then // it means that that `relative` was empty, which is an error. - panic!("macro path cannot be empty!"); + debug!("macro path cannot be empty!"); + return Err(HrefError::NotInExternalCache); } if let Some(last) = path.last_mut() { @@ -618,10 +625,11 @@ fn generate_macro_def_id_path( format!("{}{}/{}", root_path.unwrap_or(""), crate_name, path.join("/")) } ExternalLocation::Unknown => { - panic!("crate {} not in cache when linkifying macros", crate_name) + debug!("crate {} not in cache when linkifying macros", crate_name); + return Err(HrefError::NotInExternalCache); } }; - (url, ItemType::Macro, fqp) + Ok((url, ItemType::Macro, fqp)) } pub(crate) fn href_with_root_path( @@ -680,7 +688,7 @@ pub(crate) fn href_with_root_path( }, ) } else if matches!(def_kind, DefKind::Macro(_)) { - return Ok(generate_macro_def_id_path(did, cx, root_path)); + return generate_macro_def_id_path(did, cx, root_path); } else { return Err(HrefError::NotInExternalCache); } From d15fed79b84c621b42699442ccf99563a6bc6881 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 19 Jun 2022 23:11:31 -0700 Subject: [PATCH 09/15] Improve suggestion for calling closure on type mismatch --- compiler/rustc_middle/src/ty/print/pretty.rs | 2 +- .../src/check/fn_ctxt/suggestions.rs | 189 +++++++----------- .../suggest-calling-rpit-closure.rs | 12 ++ .../suggest-calling-rpit-closure.stderr | 21 ++ src/test/ui/parser/expr-as-stmt.stderr | 4 + ...truct-literal-restrictions-in-lamda.stderr | 6 + src/test/ui/reify-intrinsic.stderr | 4 - .../disallowed-positions.stderr | 8 + src/test/ui/span/move-closure.stderr | 4 + .../fn-or-tuple-struct-without-args.stderr | 14 +- .../type-alias-impl-trait/issue-63279.stderr | 4 + 11 files changed, 143 insertions(+), 125 deletions(-) create mode 100644 src/test/ui/impl-trait/suggest-calling-rpit-closure.rs create mode 100644 src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 97e5a4983fcd1..3cba6d40e38b1 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -856,7 +856,7 @@ pub trait PrettyPrinter<'tcx>: p!(")"); if let Term::Ty(ty) = return_ty.skip_binder() { if !ty.is_unit() { - p!("-> ", print(return_ty)); + p!(" -> ", print(return_ty)); } } p!(write("{}", if paren_needed { ")" } else { "" })); diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 03d91435e7712..7195da863db48 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -8,15 +8,14 @@ use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ - Expr, ExprKind, GenericBound, ItemKind, Node, Path, QPath, Stmt, StmtKind, TyKind, - WherePredicate, + Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, }; use rustc_infer::infer::{self, TyCtxtInferExt}; use rustc_infer::traits; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Binder, IsSuggestable, ToPredicate, Ty}; -use rustc_span::symbol::{kw, sym}; +use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty}; +use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -78,124 +77,88 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, found: Ty<'tcx>, ) -> bool { - let hir = self.tcx.hir(); - let (def_id, sig) = match *found.kind() { - ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)), - ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig()), + let (def_id, output, inputs) = match *found.kind() { + ty::FnDef(def_id, _) => { + let fn_sig = found.fn_sig(self.tcx); + (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len()) + } + ty::Closure(def_id, substs) => { + let fn_sig = substs.as_closure().sig(); + (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1) + } + ty::Opaque(def_id, substs) => { + let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| { + if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() + && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() + // args tuple will always be substs[1] + && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() + { + Some(( + pred.kind().rebind(proj.term.ty().unwrap()), + args.len(), + )) + } else { + None + } + }); + if let Some((output, inputs)) = sig { + (def_id, output, inputs) + } else { + return false; + } + } _ => return false, }; - let sig = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, sig); - let sig = self.normalize_associated_types_in(expr.span, sig); - if self.can_coerce(sig.output(), expected) { - let (mut sugg_call, applicability) = if sig.inputs().is_empty() { - (String::new(), Applicability::MachineApplicable) - } else { - ("...".to_string(), Applicability::HasPlaceholders) + let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output); + let output = self.normalize_associated_types_in(expr.span, output); + if !output.is_ty_var() && self.can_coerce(output, expected) { + let (sugg_call, mut applicability) = match inputs { + 0 => ("".to_string(), Applicability::MachineApplicable), + 1..=4 => ( + (0..inputs).map(|_| "_").collect::>().join(", "), + Applicability::MachineApplicable, + ), + _ => ("...".to_string(), Applicability::HasPlaceholders), }; - let mut msg = "call this function"; - match hir.get_if_local(def_id) { - Some( - Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. }) - | Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(_, body_id), .. - }) - | Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)), - .. - }), - ) => { - let body = hir.body(*body_id); - sugg_call = body - .params - .iter() - .map(|param| match ¶m.pat.kind { - hir::PatKind::Binding(_, _, ident, None) - if ident.name != kw::SelfLower => - { - ident.to_string() - } - _ => "_".to_string(), - }) - .collect::>() - .join(", "); - } - Some(Node::Expr(hir::Expr { - kind: ExprKind::Closure { body: body_id, .. }, - span: full_closure_span, - .. - })) => { - if *full_closure_span == expr.span { - return false; - } - msg = "call this closure"; - let body = hir.body(*body_id); - sugg_call = body - .params - .iter() - .map(|param| match ¶m.pat.kind { - hir::PatKind::Binding(_, _, ident, None) - if ident.name != kw::SelfLower => - { - ident.to_string() - } - _ => "_".to_string(), - }) - .collect::>() - .join(", "); - } - Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => { - sugg_call = fields.iter().map(|_| "_").collect::>().join(", "); - match def_id.as_local().map(|def_id| self.tcx.def_kind(def_id)) { - Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => { - msg = "instantiate this tuple variant"; - } - Some(DefKind::Ctor(CtorOf::Struct, _)) => { - msg = "instantiate this tuple struct"; - } - _ => {} - } + + let msg = match self.tcx.def_kind(def_id) { + DefKind::Fn => "call this function", + DefKind::Closure | DefKind::OpaqueTy => "call this closure", + DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct", + DefKind::Ctor(CtorOf::Variant, _) => "instantiate this tuple variant", + _ => "call this function", + }; + + let sugg = match expr.kind { + hir::ExprKind::Call(..) + | hir::ExprKind::Path(..) + | hir::ExprKind::Index(..) + | hir::ExprKind::Lit(..) => { + vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))] } - Some(Node::ForeignItem(hir::ForeignItem { - kind: hir::ForeignItemKind::Fn(_, idents, _), - .. - })) => { - sugg_call = idents - .iter() - .map(|ident| { - if ident.name != kw::SelfLower { - ident.to_string() - } else { - "_".to_string() - } - }) - .collect::>() - .join(", ") + hir::ExprKind::Closure { .. } => { + // Might be `{ expr } || { bool }` + applicability = Applicability::MaybeIncorrect; + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(")({sugg_call})")), + ] } - Some(Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)), - .. - })) => { - sugg_call = idents - .iter() - .map(|ident| { - if ident.name != kw::SelfLower { - ident.to_string() - } else { - "_".to_string() - } - }) - .collect::>() - .join(", ") + _ => { + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(")({sugg_call})")), + ] } - _ => {} - } - err.span_suggestion_verbose( - expr.span.shrink_to_hi(), - &format!("use parentheses to {}", msg), - format!("({})", sugg_call), + }; + + err.multipart_suggestion_verbose( + format!("use parentheses to {msg}"), + sugg, applicability, ); + return true; } false diff --git a/src/test/ui/impl-trait/suggest-calling-rpit-closure.rs b/src/test/ui/impl-trait/suggest-calling-rpit-closure.rs new file mode 100644 index 0000000000000..640156291a38c --- /dev/null +++ b/src/test/ui/impl-trait/suggest-calling-rpit-closure.rs @@ -0,0 +1,12 @@ +fn whatever() -> i32 { + opaque() +//~^ ERROR mismatched types +} + +fn opaque() -> impl Fn() -> i32 { + || 0 +} + +fn main() { + let _ = whatever(); +} diff --git a/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr b/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr new file mode 100644 index 0000000000000..2a328a0e6f54d --- /dev/null +++ b/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/suggest-calling-rpit-closure.rs:2:5 + | +LL | fn whatever() -> i32 { + | --- expected `i32` because of return type +LL | opaque() + | ^^^^^^^^ expected `i32`, found opaque type +... +LL | fn opaque() -> impl Fn() -> i32 { + | ---------------- the found opaque type + | + = note: expected type `i32` + found opaque type `impl Fn() -> i32` +help: use parentheses to call this closure + | +LL | opaque()() + | ++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/parser/expr-as-stmt.stderr b/src/test/ui/parser/expr-as-stmt.stderr index 8eb81301bc3e8..d4f64a7de5bcf 100644 --- a/src/test/ui/parser/expr-as-stmt.stderr +++ b/src/test/ui/parser/expr-as-stmt.stderr @@ -201,6 +201,10 @@ LL | { true } || { true } | = note: expected type `bool` found closure `[closure@$DIR/expr-as-stmt.rs:51:14: 51:25]` +help: use parentheses to call this closure + | +LL | { true } (|| { true })() + | + +++ help: parentheses are required to parse this as an expression | LL | ({ true }) || { true } diff --git a/src/test/ui/parser/struct-literal-restrictions-in-lamda.stderr b/src/test/ui/parser/struct-literal-restrictions-in-lamda.stderr index b948ab2abb508..e71f15ebfd2e7 100644 --- a/src/test/ui/parser/struct-literal-restrictions-in-lamda.stderr +++ b/src/test/ui/parser/struct-literal-restrictions-in-lamda.stderr @@ -25,6 +25,12 @@ LL | | }.hi() { | = note: expected type `bool` found closure `[closure@$DIR/struct-literal-restrictions-in-lamda.rs:12:11: 14:11]` +help: use parentheses to call this closure + | +LL ~ while (|| Foo { +LL | x: 3 +LL ~ }.hi())() { + | error: aborting due to 2 previous errors diff --git a/src/test/ui/reify-intrinsic.stderr b/src/test/ui/reify-intrinsic.stderr index 70a64446f6a7d..360557fb5201d 100644 --- a/src/test/ui/reify-intrinsic.stderr +++ b/src/test/ui/reify-intrinsic.stderr @@ -8,10 +8,6 @@ LL | let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::tr | = note: expected fn pointer `unsafe extern "rust-intrinsic" fn(isize) -> usize` found fn item `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}` -help: use parentheses to call this function - | -LL | let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::transmute(...); - | +++++ error[E0606]: casting `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}` as `unsafe extern "rust-intrinsic" fn(isize) -> usize` is invalid --> $DIR/reify-intrinsic.rs:11:13 diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr index dfc7fdc1ed2db..f7f39bd0b9a07 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr @@ -1118,6 +1118,10 @@ LL | if let Range { start: F, end } = F..|| true {} | = note: expected type `bool` found closure `[closure@$DIR/disallowed-positions.rs:136:41: 136:48]` +help: use parentheses to call this closure + | +LL | if let Range { start: F, end } = F..(|| true)() {} + | + +++ error[E0308]: mismatched types --> $DIR/disallowed-positions.rs:136:8 @@ -1314,6 +1318,10 @@ LL | while let Range { start: F, end } = F..|| true {} | = note: expected type `bool` found closure `[closure@$DIR/disallowed-positions.rs:200:44: 200:51]` +help: use parentheses to call this closure + | +LL | while let Range { start: F, end } = F..(|| true)() {} + | + +++ error[E0308]: mismatched types --> $DIR/disallowed-positions.rs:200:11 diff --git a/src/test/ui/span/move-closure.stderr b/src/test/ui/span/move-closure.stderr index ded581dc4968e..3e7041f02b388 100644 --- a/src/test/ui/span/move-closure.stderr +++ b/src/test/ui/span/move-closure.stderr @@ -8,6 +8,10 @@ LL | let x: () = move || (); | = note: expected unit type `()` found closure `[closure@$DIR/move-closure.rs:5:17: 5:27]` +help: use parentheses to call this closure + | +LL | let x: () = (move || ())(); + | + +++ error: aborting due to previous error diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr index 1ed784e8f5bc9..25ce458f6d813 100644 --- a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr +++ b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr @@ -33,7 +33,7 @@ LL | let _: usize = foo; found fn item `fn(usize, usize) -> usize {foo}` help: use parentheses to call this function | -LL | let _: usize = foo(a, b); +LL | let _: usize = foo(_, _); | ++++++ error[E0308]: mismatched types @@ -105,7 +105,7 @@ LL | let _: usize = T::baz; found fn item `fn(usize, usize) -> usize {<_ as T>::baz}` help: use parentheses to call this function | -LL | let _: usize = T::baz(x, y); +LL | let _: usize = T::baz(_, _); | ++++++ error[E0308]: mismatched types @@ -123,7 +123,7 @@ LL | let _: usize = T::bat; found fn item `fn(usize) -> usize {<_ as T>::bat}` help: use parentheses to call this function | -LL | let _: usize = T::bat(x); +LL | let _: usize = T::bat(_); | +++ error[E0308]: mismatched types @@ -159,7 +159,7 @@ LL | let _: usize = X::baz; found fn item `fn(usize, usize) -> usize {::baz}` help: use parentheses to call this function | -LL | let _: usize = X::baz(x, y); +LL | let _: usize = X::baz(_, _); | ++++++ error[E0308]: mismatched types @@ -177,7 +177,7 @@ LL | let _: usize = X::bat; found fn item `fn(usize) -> usize {::bat}` help: use parentheses to call this function | -LL | let _: usize = X::bat(x); +LL | let _: usize = X::bat(_); | +++ error[E0308]: mismatched types @@ -195,7 +195,7 @@ LL | let _: usize = X::bax; found fn item `fn(usize) -> usize {::bax}` help: use parentheses to call this function | -LL | let _: usize = X::bax(x); +LL | let _: usize = X::bax(_); | +++ error[E0308]: mismatched types @@ -213,7 +213,7 @@ LL | let _: usize = X::bach; found fn item `fn(usize) -> usize {::bach}` help: use parentheses to call this function | -LL | let _: usize = X::bach(x); +LL | let _: usize = X::bach(_); | +++ error[E0308]: mismatched types diff --git a/src/test/ui/type-alias-impl-trait/issue-63279.stderr b/src/test/ui/type-alias-impl-trait/issue-63279.stderr index 33f81a77aaf0a..ab39ee74be442 100644 --- a/src/test/ui/type-alias-impl-trait/issue-63279.stderr +++ b/src/test/ui/type-alias-impl-trait/issue-63279.stderr @@ -15,6 +15,10 @@ LL | || -> Closure { || () } | = note: expected unit type `()` found closure `[closure@$DIR/issue-63279.rs:8:21: 8:26]` +help: use parentheses to call this closure + | +LL | || -> Closure { (|| ())() } + | + +++ error[E0308]: mismatched types --> $DIR/issue-63279.rs:8:5 From 94477e3323330f531705d6968761e7e8fd6f749e Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Wed, 22 Jun 2022 18:23:21 +0200 Subject: [PATCH 10/15] Fixup missing renames from `#[main]` to `#[rustc_main]` In fc357039f9 `#[main]` was removed and replaced with `#[rustc_main]`. In some place the rename was forgotten, which makes the current code confusing, because at first glance it seems that `#[main]` is still around. Perform the renames also in these places. --- compiler/rustc_ast/src/entry.rs | 2 +- compiler/rustc_builtin_macros/src/test_harness.rs | 11 ++++++----- compiler/rustc_passes/src/entry.rs | 10 +++++----- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_ast/src/entry.rs b/compiler/rustc_ast/src/entry.rs index c0a837985fd62..3370146193a52 100644 --- a/compiler/rustc_ast/src/entry.rs +++ b/compiler/rustc_ast/src/entry.rs @@ -2,7 +2,7 @@ pub enum EntryPointType { None, MainNamed, - MainAttr, + RustcMainAttr, Start, OtherMain, // Not an entry point, but some other function named main } diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index db8dce804a31b..a08495f7671b0 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -147,7 +147,7 @@ fn entry_point_type(sess: &Session, item: &ast::Item, depth: usize) -> EntryPoin if sess.contains_name(&item.attrs, sym::start) { EntryPointType::Start } else if sess.contains_name(&item.attrs, sym::rustc_main) { - EntryPointType::MainAttr + EntryPointType::RustcMainAttr } else if item.ident.name == sym::main { if depth == 0 { // This is a top-level function so can be 'main' @@ -177,12 +177,12 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> { let item = noop_flat_map_item(i, self).expect_one("noop did something"); self.depth -= 1; - // Remove any #[main] or #[start] from the AST so it doesn't + // Remove any #[rustc_main] or #[start] from the AST so it doesn't // clash with the one we're going to add, but mark it as // #[allow(dead_code)] to avoid printing warnings. let item = match entry_point_type(self.sess, &item, self.depth) { - EntryPointType::MainNamed | EntryPointType::MainAttr | EntryPointType::Start => item - .map(|ast::Item { id, ident, attrs, kind, vis, span, tokens }| { + EntryPointType::MainNamed | EntryPointType::RustcMainAttr | EntryPointType::Start => { + item.map(|ast::Item { id, ident, attrs, kind, vis, span, tokens }| { let allow_ident = Ident::new(sym::allow, self.def_site); let dc_nested = attr::mk_nested_word_item(Ident::new(sym::dead_code, self.def_site)); @@ -197,7 +197,8 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> { .collect(); ast::Item { id, ident, attrs, kind, vis, span, tokens } - }), + }) + } EntryPointType::None | EntryPointType::OtherMain => item, }; diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index b90d44e2af57c..f9e67310452a7 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -56,7 +56,7 @@ fn entry_point_type(ctxt: &EntryContext<'_>, id: ItemId, at_root: bool) -> Entry if ctxt.tcx.sess.contains_name(attrs, sym::start) { EntryPointType::Start } else if ctxt.tcx.sess.contains_name(attrs, sym::rustc_main) { - EntryPointType::MainAttr + EntryPointType::RustcMainAttr } else { if let Some(name) = ctxt.tcx.opt_item_name(id.def_id.to_def_id()) && name == sym::main { @@ -95,7 +95,7 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) { EntryPointType::OtherMain => { ctxt.non_main_fns.push(ctxt.tcx.def_span(id.def_id)); } - EntryPointType::MainAttr => { + EntryPointType::RustcMainAttr => { if ctxt.attr_main_fn.is_none() { ctxt.attr_main_fn = Some((id.def_id, ctxt.tcx.def_span(id.def_id.to_def_id()))); } else { @@ -103,13 +103,13 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) { ctxt.tcx.sess, ctxt.tcx.def_span(id.def_id.to_def_id()), E0137, - "multiple functions with a `#[main]` attribute" + "multiple functions with a `#[rustc_main]` attribute" ) .span_label( ctxt.tcx.def_span(id.def_id.to_def_id()), - "additional `#[main]` function", + "additional `#[rustc_main]` function", ) - .span_label(ctxt.attr_main_fn.unwrap().1, "first `#[main]` function") + .span_label(ctxt.attr_main_fn.unwrap().1, "first `#[rustc_main]` function") .emit(); } } From 36ccdbefbbf48f171ff3d494b9e0dd4e6c0c4530 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Wed, 11 May 2022 16:36:26 -0400 Subject: [PATCH 11/15] Remove (transitive) reliance on sorting by DefId in pretty-printer This moves us a step closer to removing the `PartialOrd/`Ord` impls for `DefId`. See #90317 --- compiler/rustc_middle/src/ty/print/pretty.rs | 21 ++++++++-------- src/test/ui/associated-types/issue-87261.rs | 8 +++---- .../ui/associated-types/issue-87261.stderr | 24 +++++++++---------- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 97e5a4983fcd1..bfb822fab26e0 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -5,7 +5,7 @@ use crate::ty::{ TypeSuperFoldable, }; use rustc_apfloat::ieee::{Double, Single}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::sso::SsoHashSet; use rustc_hir as hir; use rustc_hir::def::{self, CtorKind, DefKind, Namespace}; @@ -779,8 +779,8 @@ pub trait PrettyPrinter<'tcx>: // by looking up the projections associated with the def_id. let bounds = self.tcx().bound_explicit_item_bounds(def_id); - let mut traits = BTreeMap::new(); - let mut fn_traits = BTreeMap::new(); + let mut traits = FxIndexMap::default(); + let mut fn_traits = FxIndexMap::default(); let mut is_sized = false; for predicate in bounds.transpose_iter().map(|e| e.map_bound(|(p, _)| *p)) { @@ -970,11 +970,11 @@ pub trait PrettyPrinter<'tcx>: &mut self, trait_ref: ty::PolyTraitRef<'tcx>, proj_ty: Option<(DefId, ty::Binder<'tcx, Term<'tcx>>)>, - traits: &mut BTreeMap< + traits: &mut FxIndexMap< ty::PolyTraitRef<'tcx>, - BTreeMap>>, + FxIndexMap>>, >, - fn_traits: &mut BTreeMap, OpaqueFnEntry<'tcx>>, + fn_traits: &mut FxIndexMap, OpaqueFnEntry<'tcx>>, ) { let trait_def_id = trait_ref.def_id(); @@ -1110,19 +1110,18 @@ pub trait PrettyPrinter<'tcx>: // Builtin bounds. // FIXME(eddyb) avoid printing twice (needed to ensure // that the auto traits are sorted *and* printed via cx). - let mut auto_traits: Vec<_> = - predicates.auto_traits().map(|did| (self.tcx().def_path_str(did), did)).collect(); + let mut auto_traits: Vec<_> = predicates.auto_traits().collect(); // The auto traits come ordered by `DefPathHash`. While // `DefPathHash` is *stable* in the sense that it depends on // neither the host nor the phase of the moon, it depends // "pseudorandomly" on the compiler version and the target. // - // To avoid that causing instabilities in compiletest + // To avoid causing instabilities in compiletest // output, sort the auto-traits alphabetically. - auto_traits.sort(); + auto_traits.sort_by_cached_key(|did| self.tcx().def_path_str(*did)); - for (_, def_id) in auto_traits { + for def_id in auto_traits { if !first { p!(" + "); } diff --git a/src/test/ui/associated-types/issue-87261.rs b/src/test/ui/associated-types/issue-87261.rs index aae562ae72243..384561f8ccd7e 100644 --- a/src/test/ui/associated-types/issue-87261.rs +++ b/src/test/ui/associated-types/issue-87261.rs @@ -83,17 +83,17 @@ fn main() { //~^ ERROR type mismatch resolving `::Associated == ()` accepts_trait(returns_opaque_foo()); - //~^ ERROR type mismatch resolving `::Associated == ()` + //~^ ERROR type mismatch resolving `::Associated == ()` accepts_trait(returns_opaque_derived_foo()); - //~^ ERROR type mismatch resolving `::Associated == ()` + //~^ ERROR type mismatch resolving `::Associated == ()` accepts_generic_trait(returns_opaque_generic()); //~^ ERROR type mismatch resolving ` as GenericTrait<()>>::Associated == ()` accepts_generic_trait(returns_opaque_generic_foo()); - //~^ ERROR type mismatch resolving ` as GenericTrait<()>>::Associated == ()` + //~^ ERROR type mismatch resolving ` + Foo as GenericTrait<()>>::Associated == ()` accepts_generic_trait(returns_opaque_generic_duplicate()); - //~^ ERROR type mismatch resolving ` + GenericTrait<()> as GenericTrait<()>>::Associated == ()` + //~^ ERROR type mismatch resolving ` + GenericTrait as GenericTrait<()>>::Associated == ()` } diff --git a/src/test/ui/associated-types/issue-87261.stderr b/src/test/ui/associated-types/issue-87261.stderr index c00b48abc1c33..8db4a49da3c96 100644 --- a/src/test/ui/associated-types/issue-87261.stderr +++ b/src/test/ui/associated-types/issue-87261.stderr @@ -160,7 +160,7 @@ help: consider constraining the associated type `::A LL | fn returns_opaque_derived() -> impl DerivedTrait + 'static { | +++++++++++++++++ -error[E0271]: type mismatch resolving `::Associated == ()` +error[E0271]: type mismatch resolving `::Associated == ()` --> $DIR/issue-87261.rs:85:5 | LL | fn returns_opaque_foo() -> impl Trait + Foo { @@ -170,18 +170,18 @@ LL | accepts_trait(returns_opaque_foo()); | ^^^^^^^^^^^^^ expected `()`, found associated type | = note: expected unit type `()` - found associated type `::Associated` + found associated type `::Associated` note: required by a bound in `accepts_trait` --> $DIR/issue-87261.rs:43:27 | LL | fn accepts_trait>(_: T) {} | ^^^^^^^^^^^^^^^ required by this bound in `accepts_trait` -help: consider constraining the associated type `::Associated` to `()` +help: consider constraining the associated type `::Associated` to `()` | LL | fn returns_opaque_foo() -> impl Trait + Foo { | +++++++++++++++++ -error[E0271]: type mismatch resolving `::Associated == ()` +error[E0271]: type mismatch resolving `::Associated == ()` --> $DIR/issue-87261.rs:88:5 | LL | fn returns_opaque_derived_foo() -> impl DerivedTrait + Foo { @@ -191,8 +191,8 @@ LL | accepts_trait(returns_opaque_derived_foo()); | ^^^^^^^^^^^^^ expected `()`, found associated type | = note: expected unit type `()` - found associated type `::Associated` - = help: consider constraining the associated type `::Associated` to `()` + found associated type `::Associated` + = help: consider constraining the associated type `::Associated` to `()` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html note: required by a bound in `accepts_trait` --> $DIR/issue-87261.rs:43:27 @@ -221,7 +221,7 @@ help: consider constraining the associated type ` as Gener LL | fn returns_opaque_generic() -> impl GenericTrait<(), Associated = ()> + 'static { | +++++++++++++++++ -error[E0271]: type mismatch resolving ` as GenericTrait<()>>::Associated == ()` +error[E0271]: type mismatch resolving ` + Foo as GenericTrait<()>>::Associated == ()` --> $DIR/issue-87261.rs:94:5 | LL | fn returns_opaque_generic_foo() -> impl GenericTrait<()> + Foo { @@ -231,18 +231,18 @@ LL | accepts_generic_trait(returns_opaque_generic_foo()); | ^^^^^^^^^^^^^^^^^^^^^ expected `()`, found associated type | = note: expected unit type `()` - found associated type ` as GenericTrait<()>>::Associated` + found associated type ` + Foo as GenericTrait<()>>::Associated` note: required by a bound in `accepts_generic_trait` --> $DIR/issue-87261.rs:44:46 | LL | fn accepts_generic_trait>(_: T) {} | ^^^^^^^^^^^^^^^ required by this bound in `accepts_generic_trait` -help: consider constraining the associated type ` as GenericTrait<()>>::Associated` to `()` +help: consider constraining the associated type ` + Foo as GenericTrait<()>>::Associated` to `()` | LL | fn returns_opaque_generic_foo() -> impl GenericTrait<(), Associated = ()> + Foo { | +++++++++++++++++ -error[E0271]: type mismatch resolving ` + GenericTrait<()> as GenericTrait<()>>::Associated == ()` +error[E0271]: type mismatch resolving ` + GenericTrait as GenericTrait<()>>::Associated == ()` --> $DIR/issue-87261.rs:97:5 | LL | fn returns_opaque_generic_duplicate() -> impl GenericTrait<()> + GenericTrait { @@ -252,8 +252,8 @@ LL | accepts_generic_trait(returns_opaque_generic_duplicate()); | ^^^^^^^^^^^^^^^^^^^^^ expected `()`, found associated type | = note: expected unit type `()` - found associated type ` + GenericTrait<()> as GenericTrait<()>>::Associated` - = help: consider constraining the associated type ` + GenericTrait<()> as GenericTrait<()>>::Associated` to `()` + found associated type ` + GenericTrait as GenericTrait<()>>::Associated` + = help: consider constraining the associated type ` + GenericTrait as GenericTrait<()>>::Associated` to `()` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html note: required by a bound in `accepts_generic_trait` --> $DIR/issue-87261.rs:44:46 From 04b75a72d7b2f375aa05e26c804c6e8d2395011e Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 22 Jun 2022 16:49:24 -0700 Subject: [PATCH 12/15] Update tendril --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6857451116ddd..98d2bbc2ce899 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1428,9 +1428,9 @@ checksum = "d79238883cf0307100b90aba4a755d8051a3182305dfe7f649a1e9dc0517006f" [[package]] name = "futf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" dependencies = [ "mac", "new_debug_unreachable", @@ -5190,9 +5190,9 @@ dependencies = [ [[package]] name = "tendril" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707feda9f2582d5d680d733e38755547a3e8fb471e7ba11452ecfd9ce93a5d3b" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" dependencies = [ "futf", "mac", From 9730221b9d8adafcd7615d5dc43cf83a60baf123 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 23 Jun 2022 12:21:23 +0400 Subject: [PATCH 13/15] Remove excess rib while resolving closures --- compiler/rustc_resolve/src/late.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index e36f55b2e0237..640d13ea43547 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3514,7 +3514,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }) }); } - ExprKind::Async(..) | ExprKind::Closure(..) => { + // For closures, ClosureOrAsyncRibKind is added in visit_fn + ExprKind::Closure(..) => visit::walk_expr(self, expr), + ExprKind::Async(..) => { self.with_label_rib(ClosureOrAsyncRibKind, |this| visit::walk_expr(this, expr)); } ExprKind::Repeat(ref elem, ref ct) => { From 21625e573036b6af90e71046d817fd7c74fa1f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Thu, 2 Jun 2022 00:16:00 +0200 Subject: [PATCH 14/15] Session object: Set OS/ABI This adapts LLVM's behavior of MCELFObjectTargetWriter::getOSABI [1]. [1]: https://github.com/llvm/llvm-project/blob/8c8a2679a20f621994fa904bcfc68775e7345edc/llvm/include/llvm/MC/MCELFObjectWriter.h#L72-L86 --- Cargo.lock | 15 ++++++++++++++- compiler/rustc_codegen_ssa/Cargo.toml | 2 +- compiler/rustc_codegen_ssa/src/back/metadata.rs | 10 +++++++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c066d1b0a5bd1..35819572fb9af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1688,6 +1688,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758" dependencies = [ + "ahash", "compiler_builtins", "rustc-std-workspace-alloc", "rustc-std-workspace-core", @@ -2539,6 +2540,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "crc32fast", + "hashbrown 0.12.0", + "indexmap", + "memchr", +] + [[package]] name = "odht" version = "0.3.1" @@ -3664,7 +3677,7 @@ dependencies = [ "itertools", "jobserver", "libc", - "object 0.28.4", + "object 0.29.0", "pathdiff", "regex", "rustc_apfloat", diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 93b10a07e449d..8a3464286a4d0 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -41,6 +41,6 @@ rustc_target = { path = "../rustc_target" } rustc_session = { path = "../rustc_session" } [dependencies.object] -version = "0.28.4" +version = "0.29.0" default-features = false features = ["read_core", "elf", "macho", "pe", "unaligned", "archive", "write"] diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 4c330c5906ab8..3dd607adee501 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -171,7 +171,15 @@ pub(crate) fn create_object_file(sess: &Session) -> Option 0, }; - file.flags = FileFlags::Elf { e_flags }; + // adapted from LLVM's `MCELFObjectTargetWriter::getOSABI` + let os_abi = match sess.target.options.os.as_ref() { + "hermit" => elf::ELFOSABI_STANDALONE, + "freebsd" => elf::ELFOSABI_FREEBSD, + "solaris" => elf::ELFOSABI_SOLARIS, + _ => elf::ELFOSABI_NONE, + }; + let abi_version = 0; + file.flags = FileFlags::Elf { os_abi, abi_version, e_flags }; Some(file) } From 774e814b9523ee4b7cc96bac0735f185358449e5 Mon Sep 17 00:00:00 2001 From: tnballo Date: Thu, 23 Jun 2022 19:12:24 -0400 Subject: [PATCH 15/15] Fix BTreeSet's range API panic message, document --- library/alloc/src/collections/btree/map.rs | 5 +-- .../alloc/src/collections/btree/map/tests.rs | 33 +++++++++++++++++++ library/alloc/src/collections/btree/mod.rs | 1 + library/alloc/src/collections/btree/search.rs | 15 +++++++-- library/alloc/src/collections/btree/set.rs | 24 +++++++++----- .../alloc/src/collections/btree/set/tests.rs | 23 +++++++++++++ .../alloc/src/collections/btree/set_val.rs | 29 ++++++++++++++++ 7 files changed, 117 insertions(+), 13 deletions(-) create mode 100644 library/alloc/src/collections/btree/set_val.rs diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 28068a8806096..0bddd7a990699 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -16,6 +16,7 @@ use super::dedup_sorted_iter::DedupSortedIter; use super::navigate::{LazyLeafRange, LeafRange}; use super::node::{self, marker, ForceResult::*, Handle, NodeRef, Root}; use super::search::SearchResult::*; +use super::set_val::SetValZST; mod entry; @@ -271,7 +272,7 @@ impl Clone for BTreeMap { } } -impl super::Recover for BTreeMap +impl super::Recover for BTreeMap where K: Borrow + Ord, Q: Ord, @@ -318,7 +319,7 @@ where alloc: (*map.alloc).clone(), _marker: PhantomData, } - .insert(()); + .insert(SetValZST::default()); None } } diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index 5504959c34d87..4c372b1d60ac4 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -897,6 +897,39 @@ fn test_range_mut() { map.check(); } +#[should_panic(expected = "range start is greater than range end in BTreeMap")] +#[test] +fn test_range_panic_1() { + let mut map = BTreeMap::new(); + map.insert(3, "a"); + map.insert(5, "b"); + map.insert(8, "c"); + + let _invalid_range = map.range((Included(&8), Included(&3))); +} + +#[should_panic(expected = "range start and end are equal and excluded in BTreeMap")] +#[test] +fn test_range_panic_2() { + let mut map = BTreeMap::new(); + map.insert(3, "a"); + map.insert(5, "b"); + map.insert(8, "c"); + + let _invalid_range = map.range((Excluded(&5), Excluded(&5))); +} + +#[should_panic(expected = "range start and end are equal and excluded in BTreeMap")] +#[test] +fn test_range_panic_3() { + let mut map: BTreeMap = BTreeMap::new(); + map.insert(3, ()); + map.insert(5, ()); + map.insert(8, ()); + + let _invalid_range = map.range((Excluded(&5), Excluded(&5))); +} + #[test] fn test_retain() { let mut map = BTreeMap::from_iter((0..100).map(|x| (x, x * 10))); diff --git a/library/alloc/src/collections/btree/mod.rs b/library/alloc/src/collections/btree/mod.rs index 9571b3d594df8..9d43ac5c5be59 100644 --- a/library/alloc/src/collections/btree/mod.rs +++ b/library/alloc/src/collections/btree/mod.rs @@ -10,6 +10,7 @@ mod node; mod remove; mod search; pub mod set; +mod set_val; mod split; #[doc(hidden)] diff --git a/library/alloc/src/collections/btree/search.rs b/library/alloc/src/collections/btree/search.rs index 5651a03c47a43..ad3522b4e0418 100644 --- a/library/alloc/src/collections/btree/search.rs +++ b/library/alloc/src/collections/btree/search.rs @@ -97,17 +97,28 @@ impl NodeRef, R: RangeBounds, { + // Determine if map or set is being searched + let is_set = ::is_set_val(); + // Inlining these variables should be avoided. We assume the bounds reported by `range` // remain the same, but an adversarial implementation could change between calls (#81138). let (start, end) = (range.start_bound(), range.end_bound()); match (start, end) { (Bound::Excluded(s), Bound::Excluded(e)) if s == e => { - panic!("range start and end are equal and excluded in BTreeMap") + if is_set { + panic!("range start and end are equal and excluded in BTreeSet") + } else { + panic!("range start and end are equal and excluded in BTreeMap") + } } (Bound::Included(s) | Bound::Excluded(s), Bound::Included(e) | Bound::Excluded(e)) if s > e => { - panic!("range start is greater than range end in BTreeMap") + if is_set { + panic!("range start is greater than range end in BTreeSet") + } else { + panic!("range start is greater than range end in BTreeMap") + } } _ => {} } diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 0d3fdc9019efd..2cfc080740921 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -13,6 +13,7 @@ use core::ops::{BitAnd, BitOr, BitXor, RangeBounds, Sub}; use super::map::{BTreeMap, Keys}; use super::merge_iter::MergeIterInner; +use super::set_val::SetValZST; use super::Recover; use crate::alloc::{Allocator, Global}; @@ -81,7 +82,7 @@ pub struct BTreeSet< T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global, > { - map: BTreeMap, + map: BTreeMap, } #[stable(feature = "rust1", since = "1.0.0")] @@ -135,7 +136,7 @@ impl Clone for BTreeSet { #[must_use = "iterators are lazy and do nothing unless consumed"] #[stable(feature = "rust1", since = "1.0.0")] pub struct Iter<'a, T: 'a> { - iter: Keys<'a, T, ()>, + iter: Keys<'a, T, SetValZST>, } #[stable(feature = "collection_debug", since = "1.17.0")] @@ -158,7 +159,7 @@ pub struct IntoIter< T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global, > { - iter: super::map::IntoIter, + iter: super::map::IntoIter, } /// An iterator over a sub-range of items in a `BTreeSet`. @@ -171,7 +172,7 @@ pub struct IntoIter< #[derive(Debug)] #[stable(feature = "btree_range", since = "1.17.0")] pub struct Range<'a, T: 'a> { - iter: super::map::Range<'a, T, ()>, + iter: super::map::Range<'a, T, SetValZST>, } /// A lazy iterator producing elements in the difference of `BTreeSet`s. @@ -375,6 +376,11 @@ impl BTreeSet { /// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive /// range from 4 to 10. /// + /// # Panics + /// + /// Panics if range `start > end`. + /// Panics if range `start == end` and both bounds are `Excluded`. + /// /// # Examples /// /// ``` @@ -905,7 +911,7 @@ impl BTreeSet { where T: Ord, { - self.map.insert(value, ()).is_none() + self.map.insert(value, SetValZST::default()).is_none() } /// Adds a value to the set, replacing the existing element, if any, that is @@ -1210,7 +1216,7 @@ impl FromIterator for BTreeSet { impl BTreeSet { fn from_sorted_iter>(iter: I, alloc: A) -> BTreeSet { - let iter = iter.map(|k| (k, ())); + let iter = iter.map(|k| (k, SetValZST::default())); let map = BTreeMap::bulk_build_from_sorted_iter(iter, alloc); BTreeSet { map } } @@ -1234,7 +1240,7 @@ impl From<[T; N]> for BTreeSet { // use stable sort to preserve the insertion order. arr.sort(); - let iter = IntoIterator::into_iter(arr).map(|k| (k, ())); + let iter = IntoIterator::into_iter(arr).map(|k| (k, SetValZST::default())); let map = BTreeMap::bulk_build_from_sorted_iter(iter, Global); BTreeSet { map } } @@ -1284,7 +1290,7 @@ pub struct DrainFilter< F: 'a + FnMut(&T) -> bool, { pred: F, - inner: super::map::DrainFilterInner<'a, T, ()>, + inner: super::map::DrainFilterInner<'a, T, SetValZST>, /// The BTreeMap will outlive this IntoIter so we don't care about drop order for `alloc`. alloc: A, } @@ -1319,7 +1325,7 @@ where fn next(&mut self) -> Option { let pred = &mut self.pred; - let mut mapped_pred = |k: &T, _v: &mut ()| pred(k); + let mut mapped_pred = |k: &T, _v: &mut SetValZST| pred(k); self.inner.next(&mut mapped_pred, self.alloc.clone()).map(|(k, _)| k) } diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs index 429b1644976c7..502d3e1d12667 100644 --- a/library/alloc/src/collections/btree/set/tests.rs +++ b/library/alloc/src/collections/btree/set/tests.rs @@ -5,6 +5,7 @@ use crate::vec::Vec; use std::cmp::Ordering; use std::hash::{Hash, Hasher}; use std::iter::FromIterator; +use std::ops::Bound::{Excluded, Included}; use std::panic::{catch_unwind, AssertUnwindSafe}; #[test] @@ -831,3 +832,25 @@ fn from_array() { let unordered_duplicates = BTreeSet::from([4, 1, 4, 3, 2]); assert_eq!(set, unordered_duplicates); } + +#[should_panic(expected = "range start is greater than range end in BTreeSet")] +#[test] +fn test_range_panic_1() { + let mut set = BTreeSet::new(); + set.insert(3); + set.insert(5); + set.insert(8); + + let _invalid_range = set.range((Included(&8), Included(&3))); +} + +#[should_panic(expected = "range start and end are equal and excluded in BTreeSet")] +#[test] +fn test_range_panic_2() { + let mut set = BTreeSet::new(); + set.insert(3); + set.insert(5); + set.insert(8); + + let _invalid_range = set.range((Excluded(&5), Excluded(&5))); +} diff --git a/library/alloc/src/collections/btree/set_val.rs b/library/alloc/src/collections/btree/set_val.rs new file mode 100644 index 0000000000000..80c459bcf81db --- /dev/null +++ b/library/alloc/src/collections/btree/set_val.rs @@ -0,0 +1,29 @@ +/// Zero-Sized Type (ZST) for internal `BTreeSet` values. +/// Used instead of `()` to differentiate between: +/// * `BTreeMap` (possible user-defined map) +/// * `BTreeMap` (internal set representation) +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Default)] +pub struct SetValZST; + +/// A trait to differentiate between `BTreeMap` and `BTreeSet` values. +/// Returns `true` only for type `SetValZST`, `false` for all other types (blanket implementation). +/// `TypeId` requires a `'static` lifetime, use of this trait avoids that restriction. +/// +/// [`TypeId`]: std::any::TypeId +pub trait IsSetVal { + fn is_set_val() -> bool; +} + +// Blanket implementation +impl IsSetVal for V { + default fn is_set_val() -> bool { + false + } +} + +// Specialization +impl IsSetVal for SetValZST { + fn is_set_val() -> bool { + true + } +}