mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
Auto merge of #98447 - JohnTitor:rollup-pponoo3, r=JohnTitor
Rollup of 9 pull requests Successful merges: - #91264 (Add macro support in jump to definition feature) - #96955 (Remove (transitive) reliance on sorting by DefId in pretty-printer) - #97633 (Session object: Set OS/ABI) - #98039 (Fix `panic` message for `BTreeSet`'s `range` API and document `panic` cases) - #98214 (rustc_target: Remove some redundant target properties) - #98280 (Improve suggestion for calling fn-like expr on type mismatch) - #98394 (Fixup missing renames from `#[main]` to `#[rustc_main]`) - #98411 (Update tendril) - #98419 (Remove excess rib while resolving closures) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
7036449c77
23
Cargo.lock
23
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",
|
||||
@ -1713,6 +1713,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",
|
||||
@ -2571,6 +2572,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"
|
||||
@ -3720,7 +3733,7 @@ dependencies = [
|
||||
"itertools",
|
||||
"jobserver",
|
||||
"libc",
|
||||
"object 0.28.4",
|
||||
"object 0.29.0",
|
||||
"pathdiff",
|
||||
"regex",
|
||||
"rustc_apfloat",
|
||||
@ -5191,9 +5204,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",
|
||||
|
@ -2,7 +2,7 @@
|
||||
pub enum EntryPointType {
|
||||
None,
|
||||
MainNamed,
|
||||
MainAttr,
|
||||
RustcMainAttr,
|
||||
Start,
|
||||
OtherMain, // Not an entry point, but some other function named main
|
||||
}
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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),
|
||||
_ => {
|
||||
|
@ -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);
|
||||
|
@ -42,6 +42,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"]
|
||||
|
@ -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"
|
||||
|
@ -130,7 +130,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
|
||||
};
|
||||
|
||||
let mut file = write::Object::new(binary_format, architecture, endianness);
|
||||
match architecture {
|
||||
let e_flags = match architecture {
|
||||
Architecture::Mips => {
|
||||
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<write::Object<'static
|
||||
if sess.target.options.cpu.contains("r6") {
|
||||
e_flags |= elf::EF_MIPS_NAN2008;
|
||||
}
|
||||
file.flags = FileFlags::Elf { e_flags };
|
||||
e_flags
|
||||
}
|
||||
Architecture::Mips64 => {
|
||||
// copied from `mips64el-linux-gnuabi64-gcc foo.c -c`
|
||||
@ -160,17 +160,26 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
|
||||
} else {
|
||||
elf::EF_MIPS_ARCH_64R2
|
||||
};
|
||||
file.flags = FileFlags::Elf { e_flags };
|
||||
e_flags
|
||||
}
|
||||
Architecture::Riscv64 if sess.target.options.features.contains("+d") => {
|
||||
// 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,
|
||||
};
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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)) {
|
||||
@ -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 { "" }));
|
||||
@ -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<DefId, ty::Binder<'tcx, Term<'tcx>>>,
|
||||
FxIndexMap<DefId, ty::Binder<'tcx, Term<'tcx>>>,
|
||||
>,
|
||||
fn_traits: &mut BTreeMap<ty::PolyTraitRef<'tcx>, OpaqueFnEntry<'tcx>>,
|
||||
fn_traits: &mut FxIndexMap<ty::PolyTraitRef<'tcx>, 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!(" + ");
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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) => {
|
||||
|
@ -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(
|
||||
|
@ -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"]),
|
||||
|
@ -1273,12 +1273,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.
|
||||
@ -1505,9 +1499,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,
|
||||
@ -2112,8 +2104,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<u32>);
|
||||
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);
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
no_default_libraries: false,
|
||||
post_link_args,
|
||||
|
@ -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::<Vec<_>>().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::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
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::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::<Vec<_>>()
|
||||
.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::Ctor(hir::VariantData::Tuple(fields, _))) => {
|
||||
sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().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";
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {
|
||||
vec![
|
||||
(expr.span.shrink_to_lo(), "(".to_string()),
|
||||
(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::<Vec<_>>()
|
||||
.join(", ")
|
||||
}
|
||||
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::<Vec<_>>()
|
||||
.join(", ")
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
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
|
||||
|
@ -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<K: Clone, V: Clone, A: Allocator + Clone> Clone for BTreeMap<K, V, A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, Q: ?Sized, A: Allocator + Clone> super::Recover<Q> for BTreeMap<K, (), A>
|
||||
impl<K, Q: ?Sized, A: Allocator + Clone> super::Recover<Q> for BTreeMap<K, SetValZST, A>
|
||||
where
|
||||
K: Borrow<Q> + Ord,
|
||||
Q: Ord,
|
||||
@ -318,7 +319,7 @@ where
|
||||
alloc: (*map.alloc).clone(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
.insert(());
|
||||
.insert(SetValZST::default());
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -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<i32, ()> = 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)));
|
||||
|
@ -10,6 +10,7 @@ mod node;
|
||||
mod remove;
|
||||
mod search;
|
||||
pub mod set;
|
||||
mod set_val;
|
||||
mod split;
|
||||
|
||||
#[doc(hidden)]
|
||||
|
@ -97,17 +97,28 @@ impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Lea
|
||||
K: Borrow<Q>,
|
||||
R: RangeBounds<Q>,
|
||||
{
|
||||
// Determine if map or set is being searched
|
||||
let is_set = <V as super::set_val::IsSetVal>::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")
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -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<T, (), A>,
|
||||
map: BTreeMap<T, SetValZST, A>,
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
@ -135,7 +136,7 @@ impl<T: Clone, A: Allocator + Clone> Clone for BTreeSet<T, A> {
|
||||
#[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<T, (), A>,
|
||||
iter: super::map::IntoIter<T, SetValZST, A>,
|
||||
}
|
||||
|
||||
/// 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<T, A: Allocator + Clone> BTreeSet<T, A> {
|
||||
/// `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<T, A: Allocator + Clone> BTreeSet<T, A> {
|
||||
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<T: Ord> FromIterator<T> for BTreeSet<T> {
|
||||
|
||||
impl<T: Ord, A: Allocator + Clone> BTreeSet<T, A> {
|
||||
fn from_sorted_iter<I: Iterator<Item = T>>(iter: I, alloc: A) -> BTreeSet<T, A> {
|
||||
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<T: Ord, const N: usize> From<[T; N]> for BTreeSet<T> {
|
||||
|
||||
// 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<T> {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -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)));
|
||||
}
|
||||
|
29
library/alloc/src/collections/btree/set_val.rs
Normal file
29
library/alloc/src/collections/btree/set_val.rs
Normal file
@ -0,0 +1,29 @@
|
||||
/// Zero-Sized Type (ZST) for internal `BTreeSet` values.
|
||||
/// Used instead of `()` to differentiate between:
|
||||
/// * `BTreeMap<T, ()>` (possible user-defined map)
|
||||
/// * `BTreeMap<T, SetValZST>` (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<V> IsSetVal for V {
|
||||
default fn is_set_val() -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// Specialization
|
||||
impl IsSetVal for SetValZST {
|
||||
fn is_set_val() -> bool {
|
||||
true
|
||||
}
|
||||
}
|
@ -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;
|
||||
@ -519,6 +521,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.
|
||||
///
|
||||
@ -556,6 +559,79 @@ 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>,
|
||||
) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
|
||||
let tcx = cx.shared.tcx;
|
||||
let crate_name = tcx.crate_name(def_id.krate).to_string();
|
||||
let cache = cx.cache();
|
||||
|
||||
let fqp: Vec<Symbol> = 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());
|
||||
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 <https://rust-lang.github.io/rfcs/1584-macros.html>.
|
||||
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)
|
||||
}
|
||||
_ => 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.
|
||||
debug!("macro path cannot be empty!");
|
||||
return Err(HrefError::NotInExternalCache);
|
||||
}
|
||||
|
||||
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 => {
|
||||
debug!("crate {} not in cache when linkifying macros", crate_name);
|
||||
return Err(HrefError::NotInExternalCache);
|
||||
}
|
||||
};
|
||||
Ok((url, ItemType::Macro, fqp))
|
||||
}
|
||||
|
||||
pub(crate) fn href_with_root_path(
|
||||
did: DefId,
|
||||
cx: &Context<'_>,
|
||||
@ -611,6 +687,8 @@ pub(crate) fn href_with_root_path(
|
||||
ExternalLocation::Unknown => return Err(HrefError::DocumentationNotBuilt),
|
||||
},
|
||||
)
|
||||
} else if matches!(def_kind, DefKind::Macro(_)) {
|
||||
return generate_macro_def_id_path(did, cx, root_path);
|
||||
} else {
|
||||
return Err(HrefError::NotInExternalCache);
|
||||
}
|
||||
|
@ -22,7 +22,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,
|
||||
@ -44,7 +44,7 @@ pub(crate) fn render_with_highlighting(
|
||||
tooltip: Option<(Option<Edition>, &str)>,
|
||||
edition: Edition,
|
||||
extra_content: Option<Buffer>,
|
||||
context_info: Option<ContextInfo<'_, '_, '_>>,
|
||||
href_context: Option<HrefContext<'_, '_, '_>>,
|
||||
decoration_info: Option<DecorationInfo>,
|
||||
) {
|
||||
debug!("highlighting: ================\n{}\n==============", src);
|
||||
@ -62,7 +62,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);
|
||||
}
|
||||
|
||||
@ -85,8 +85,8 @@ fn write_header(out: &mut Buffer, class: Option<&str>, extra_content: Option<Buf
|
||||
///
|
||||
/// Some explanations on the last arguments:
|
||||
///
|
||||
/// In case we are rendering a code block and not a source code file, `context_info` will be `None`.
|
||||
/// To put it more simply: if `context_info` is `None`, the code won't try to generate links to an
|
||||
/// In case we are rendering a code block and not a source code file, `href_context` will be `None`.
|
||||
/// To put it more simply: if `href_context` is `None`, the code won't try to generate links to an
|
||||
/// item definition.
|
||||
///
|
||||
/// More explanations about spans and how we use them here are provided in the
|
||||
@ -94,22 +94,27 @@ fn write_code(
|
||||
out: &mut Buffer,
|
||||
src: &str,
|
||||
edition: Edition,
|
||||
context_info: Option<ContextInfo<'_, '_, '_>>,
|
||||
href_context: Option<HrefContext<'_, '_, '_>>,
|
||||
decoration_info: Option<DecorationInfo>,
|
||||
) {
|
||||
// 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_tags: Vec<&'static str> = Vec::new();
|
||||
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 } => enter_span(out, class),
|
||||
Highlight::ExitSpan => exit_span(out),
|
||||
Highlight::Token { text, class } => string(out, Escape(text), class, &href_context),
|
||||
Highlight::EnterSpan { class } => {
|
||||
closing_tags.push(enter_span(out, class, &href_context))
|
||||
}
|
||||
Highlight::ExitSpan => {
|
||||
exit_span(out, closing_tags.pop().expect("ExitSpan without EnterSpan"))
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
@ -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<Span> {
|
||||
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,20 @@ 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, "<span class=\"{}\">", klass.as_html());
|
||||
fn enter_span(
|
||||
out: &mut Buffer,
|
||||
klass: Class,
|
||||
href_context: &Option<HrefContext<'_, '_, '_>>,
|
||||
) -> &'static str {
|
||||
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.
|
||||
fn exit_span(out: &mut Buffer) {
|
||||
out.write_str("</span>");
|
||||
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
|
||||
@ -687,15 +713,39 @@ fn string<T: Display>(
|
||||
out: &mut Buffer,
|
||||
text: T,
|
||||
klass: Option<Class>,
|
||||
context_info: &Option<ContextInfo<'_, '_, '_>>,
|
||||
href_context: &Option<HrefContext<'_, '_, '_>>,
|
||||
) {
|
||||
if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context) {
|
||||
out.write_str(closing_tag);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// `<span>` with the provided `klass`.
|
||||
/// * If `klass` is `Some` and has a [`rustc_span::Span`], it then tries to generate a link (`<a>`
|
||||
/// 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<T: Display>(
|
||||
out: &mut Buffer,
|
||||
text: T,
|
||||
klass: Option<Class>,
|
||||
href_context: &Option<HrefContext<'_, '_, '_>>,
|
||||
) -> 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, "<span class=\"{}\">{}</span>", klass.as_html(), text);
|
||||
return;
|
||||
write!(out, "<span class=\"{}\">{}", klass.as_html(), text);
|
||||
return Some("</span>");
|
||||
};
|
||||
|
||||
let mut text_s = text.to_string();
|
||||
if text_s.contains("::") {
|
||||
text_s = text_s.split("::").intersperse("::").fold(String::new(), |mut path, t| {
|
||||
@ -715,10 +765,10 @@ fn string<T: Display>(
|
||||
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,
|
||||
@ -727,27 +777,28 @@ fn string<T: Display>(
|
||||
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))
|
||||
.ok()
|
||||
.map(|(url, _, _)| url)
|
||||
}
|
||||
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),
|
||||
}
|
||||
})
|
||||
{
|
||||
write!(out, "<a class=\"{}\" href=\"{}\">{}</a>", klass.as_html(), href, text_s);
|
||||
return;
|
||||
write!(out, "<a class=\"{}\" href=\"{}\">{}", klass.as_html(), href, text_s);
|
||||
return Some("</a>");
|
||||
}
|
||||
}
|
||||
write!(out, "<span class=\"{}\">{}</span>", klass.as_html(), text_s);
|
||||
write!(out, "<span class=\"{}\">{}", klass.as_html(), text_s);
|
||||
Some("</span>")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -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,34 +64,73 @@ 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<Span>) {
|
||||
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.
|
||||
///
|
||||
/// 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() {
|
||||
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;
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
|
||||
@ -101,7 +141,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 +186,18 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if self.handle_macro(expr.span) {
|
||||
// We don't want to go 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);
|
||||
}
|
||||
}
|
||||
|
@ -297,7 +297,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,
|
||||
);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -83,17 +83,17 @@ fn main() {
|
||||
//~^ ERROR type mismatch resolving `<impl DerivedTrait as Trait>::Associated == ()`
|
||||
|
||||
accepts_trait(returns_opaque_foo());
|
||||
//~^ ERROR type mismatch resolving `<impl Foo + Trait as Trait>::Associated == ()`
|
||||
//~^ ERROR type mismatch resolving `<impl Trait + Foo as Trait>::Associated == ()`
|
||||
|
||||
accepts_trait(returns_opaque_derived_foo());
|
||||
//~^ ERROR type mismatch resolving `<impl Foo + DerivedTrait as Trait>::Associated == ()`
|
||||
//~^ ERROR type mismatch resolving `<impl DerivedTrait + Foo as Trait>::Associated == ()`
|
||||
|
||||
accepts_generic_trait(returns_opaque_generic());
|
||||
//~^ ERROR type mismatch resolving `<impl GenericTrait<()> as GenericTrait<()>>::Associated == ()`
|
||||
|
||||
accepts_generic_trait(returns_opaque_generic_foo());
|
||||
//~^ ERROR type mismatch resolving `<impl Foo + GenericTrait<()> as GenericTrait<()>>::Associated == ()`
|
||||
//~^ ERROR type mismatch resolving `<impl GenericTrait<()> + Foo as GenericTrait<()>>::Associated == ()`
|
||||
|
||||
accepts_generic_trait(returns_opaque_generic_duplicate());
|
||||
//~^ ERROR type mismatch resolving `<impl GenericTrait<u8> + GenericTrait<()> as GenericTrait<()>>::Associated == ()`
|
||||
//~^ ERROR type mismatch resolving `<impl GenericTrait<()> + GenericTrait<u8> as GenericTrait<()>>::Associated == ()`
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ help: consider constraining the associated type `<impl DerivedTrait as Trait>::A
|
||||
LL | fn returns_opaque_derived() -> impl DerivedTrait<Associated = ()> + 'static {
|
||||
| +++++++++++++++++
|
||||
|
||||
error[E0271]: type mismatch resolving `<impl Foo + Trait as Trait>::Associated == ()`
|
||||
error[E0271]: type mismatch resolving `<impl Trait + Foo as Trait>::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 `<impl Foo + Trait as Trait>::Associated`
|
||||
found associated type `<impl Trait + Foo as Trait>::Associated`
|
||||
note: required by a bound in `accepts_trait`
|
||||
--> $DIR/issue-87261.rs:43:27
|
||||
|
|
||||
LL | fn accepts_trait<T: Trait<Associated = ()>>(_: T) {}
|
||||
| ^^^^^^^^^^^^^^^ required by this bound in `accepts_trait`
|
||||
help: consider constraining the associated type `<impl Foo + Trait as Trait>::Associated` to `()`
|
||||
help: consider constraining the associated type `<impl Trait + Foo as Trait>::Associated` to `()`
|
||||
|
|
||||
LL | fn returns_opaque_foo() -> impl Trait<Associated = ()> + Foo {
|
||||
| +++++++++++++++++
|
||||
|
||||
error[E0271]: type mismatch resolving `<impl Foo + DerivedTrait as Trait>::Associated == ()`
|
||||
error[E0271]: type mismatch resolving `<impl DerivedTrait + Foo as Trait>::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 `<impl Foo + DerivedTrait as Trait>::Associated`
|
||||
= help: consider constraining the associated type `<impl Foo + DerivedTrait as Trait>::Associated` to `()`
|
||||
found associated type `<impl DerivedTrait + Foo as Trait>::Associated`
|
||||
= help: consider constraining the associated type `<impl DerivedTrait + Foo as Trait>::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 `<impl GenericTrait<()> as Gener
|
||||
LL | fn returns_opaque_generic() -> impl GenericTrait<(), Associated = ()> + 'static {
|
||||
| +++++++++++++++++
|
||||
|
||||
error[E0271]: type mismatch resolving `<impl Foo + GenericTrait<()> as GenericTrait<()>>::Associated == ()`
|
||||
error[E0271]: type mismatch resolving `<impl GenericTrait<()> + 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 `<impl Foo + GenericTrait<()> as GenericTrait<()>>::Associated`
|
||||
found associated type `<impl GenericTrait<()> + 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: GenericTrait<(), Associated = ()>>(_: T) {}
|
||||
| ^^^^^^^^^^^^^^^ required by this bound in `accepts_generic_trait`
|
||||
help: consider constraining the associated type `<impl Foo + GenericTrait<()> as GenericTrait<()>>::Associated` to `()`
|
||||
help: consider constraining the associated type `<impl GenericTrait<()> + Foo as GenericTrait<()>>::Associated` to `()`
|
||||
|
|
||||
LL | fn returns_opaque_generic_foo() -> impl GenericTrait<(), Associated = ()> + Foo {
|
||||
| +++++++++++++++++
|
||||
|
||||
error[E0271]: type mismatch resolving `<impl GenericTrait<u8> + GenericTrait<()> as GenericTrait<()>>::Associated == ()`
|
||||
error[E0271]: type mismatch resolving `<impl GenericTrait<()> + GenericTrait<u8> as GenericTrait<()>>::Associated == ()`
|
||||
--> $DIR/issue-87261.rs:97:5
|
||||
|
|
||||
LL | fn returns_opaque_generic_duplicate() -> impl GenericTrait<()> + GenericTrait<u8> {
|
||||
@ -252,8 +252,8 @@ LL | accepts_generic_trait(returns_opaque_generic_duplicate());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ expected `()`, found associated type
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found associated type `<impl GenericTrait<u8> + GenericTrait<()> as GenericTrait<()>>::Associated`
|
||||
= help: consider constraining the associated type `<impl GenericTrait<u8> + GenericTrait<()> as GenericTrait<()>>::Associated` to `()`
|
||||
found associated type `<impl GenericTrait<()> + GenericTrait<u8> as GenericTrait<()>>::Associated`
|
||||
= help: consider constraining the associated type `<impl GenericTrait<()> + GenericTrait<u8> 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
|
||||
|
12
src/test/ui/impl-trait/suggest-calling-rpit-closure.rs
Normal file
12
src/test/ui/impl-trait/suggest-calling-rpit-closure.rs
Normal file
@ -0,0 +1,12 @@
|
||||
fn whatever() -> i32 {
|
||||
opaque()
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
||||
fn opaque() -> impl Fn() -> i32 {
|
||||
|| 0
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = whatever();
|
||||
}
|
21
src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr
Normal file
21
src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr
Normal file
@ -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`.
|
@ -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 }
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 {<X as T>::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 {<X as T>::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 {<X as T>::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 {<X as T>::bach}`
|
||||
help: use parentheses to call this function
|
||||
|
|
||||
LL | let _: usize = X::bach(x);
|
||||
LL | let _: usize = X::bach(_);
|
||||
| +++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user