diff options
62 files changed, 1123 insertions, 265 deletions
diff --git a/Cargo.lock b/Cargo.lock index 39688b7b7ea..76a71d8994d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3649,6 +3649,7 @@ dependencies = [ "libc", "object 0.25.2", "pathdiff", + "regex", "rustc_apfloat", "rustc_ast", "rustc_attr", diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 554fabaaf6e..0e036a432ad 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -16,6 +16,7 @@ jobserver = "0.1.22" tempfile = "3.2" pathdiff = "0.2.0" smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } +regex = "1.4" rustc_serialize = { path = "../rustc_serialize" } rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index ab211e9daff..f3eb1e04d07 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -32,6 +32,7 @@ use cc::windows_registry; use object::elf; use object::write::Object; use object::{Architecture, BinaryFormat, Endianness, FileFlags, SectionFlags, SectionKind}; +use regex::Regex; use tempfile::Builder as TempFileBuilder; use std::ffi::OsString; @@ -672,6 +673,8 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( // Invoke the system linker info!("{:?}", &cmd); let retry_on_segfault = env::var("RUSTC_RETRY_LINKER_ON_SEGFAULT").is_ok(); + let unknown_arg_regex = + Regex::new(r"(unknown|unrecognized) (command line )?(option|argument)").unwrap(); let mut prog; let mut i = 0; loop { @@ -688,16 +691,15 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( out.extend(&output.stdout); let out = String::from_utf8_lossy(&out); - // Check to see if the link failed with "unrecognized command line option: - // '-no-pie'" for gcc or "unknown argument: '-no-pie'" for clang. If so, - // reperform the link step without the -no-pie option. This is safe because - // if the linker doesn't support -no-pie then it should not default to - // linking executables as pie. Different versions of gcc seem to use - // different quotes in the error message so don't check for them. + // Check to see if the link failed with an error message that indicates it + // doesn't recognize the -no-pie option. If so, reperform the link step + // without it. This is safe because if the linker doesn't support -no-pie + // then it should not default to linking executables as pie. Different + // versions of gcc seem to use different quotes in the error message so + // don't check for them. if sess.target.linker_is_gnu && flavor != LinkerFlavor::Ld - && (out.contains("unrecognized command line option") - || out.contains("unknown argument")) + && unknown_arg_regex.is_match(&out) && out.contains("-no-pie") && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie") { @@ -716,8 +718,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( // Fallback from '-static-pie' to '-static' in that case. if sess.target.linker_is_gnu && flavor != LinkerFlavor::Ld - && (out.contains("unrecognized command line option") - || out.contains("unknown argument")) + && unknown_arg_regex.is_match(&out) && (out.contains("-static-pie") || out.contains("--no-dynamic-linker")) && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-static-pie") { diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index 65999ba707c..719c2c6768b 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -359,6 +359,7 @@ E0621: include_str!("./error_codes/E0621.md"), E0622: include_str!("./error_codes/E0622.md"), E0623: include_str!("./error_codes/E0623.md"), E0624: include_str!("./error_codes/E0624.md"), +E0625: include_str!("./error_codes/E0625.md"), E0626: include_str!("./error_codes/E0626.md"), E0627: include_str!("./error_codes/E0627.md"), E0628: include_str!("./error_codes/E0628.md"), @@ -622,7 +623,6 @@ E0783: include_str!("./error_codes/E0783.md"), // E0611, // merged into E0616 // E0612, // merged into E0609 // E0613, // Removed (merged with E0609) - E0625, // thread-local statics cannot be accessed at compile-time // E0629, // missing 'feature' (rustc_const_unstable) // E0630, // rustc_const_unstable attribute must be paired with stable/unstable // attribute diff --git a/compiler/rustc_error_codes/src/error_codes/E0625.md b/compiler/rustc_error_codes/src/error_codes/E0625.md new file mode 100644 index 00000000000..7db857723cc --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0625.md @@ -0,0 +1,28 @@ +A compile-time const variable is referring to a thread-local static variable. + +Erroneous code example: + +```compile_fail,E0625 +#![feature(thread_local)] + +#[thread_local] +static X: usize = 12; + +const Y: usize = 2 * X; +``` + +Static and const variables can refer to other const variables but a const +variable cannot refer to a thread-local static variable. In this example, +`Y` cannot refer to `X`. To fix this, the value can be extracted as a const +and then used: + +``` +#![feature(thread_local)] + +const C: usize = 12; + +#[thread_local] +static X: usize = C; + +const Y: usize = 2 * C; +``` diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 993a7c2c162..fc0924ac5f9 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -342,6 +342,9 @@ struct HandlerInner { deduplicated_warn_count: usize, future_breakage_diagnostics: Vec<Diagnostic>, + + /// If set to `true`, no warning or error will be emitted. + quiet: bool, } /// A key denoting where from a diagnostic was stashed. @@ -456,10 +459,19 @@ impl Handler { emitted_diagnostics: Default::default(), stashed_diagnostics: Default::default(), future_breakage_diagnostics: Vec::new(), + quiet: false, }), } } + pub fn with_disabled_diagnostic<T, F: FnOnce() -> T>(&self, f: F) -> T { + let prev = self.inner.borrow_mut().quiet; + self.inner.borrow_mut().quiet = true; + let ret = f(); + self.inner.borrow_mut().quiet = prev; + ret + } + // This is here to not allow mutation of flags; // as of this writing it's only used in tests in librustc_middle. pub fn can_emit_warnings(&self) -> bool { @@ -818,7 +830,7 @@ impl HandlerInner { } fn emit_diagnostic(&mut self, diagnostic: &Diagnostic) { - if diagnostic.cancelled() { + if diagnostic.cancelled() || self.quiet { return; } @@ -1035,6 +1047,9 @@ impl HandlerInner { } fn delay_as_bug(&mut self, diagnostic: Diagnostic) { + if self.quiet { + return; + } if self.flags.report_delayed_bugs { self.emit_diagnostic(&diagnostic); } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index de0d5fb0097..b8961434006 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -740,6 +740,7 @@ fn test_debugging_options_tracking_hash() { tracked!(new_llvm_pass_manager, Some(true)); tracked!(no_generate_arange_section, true); tracked!(no_link, true); + tracked!(no_profiler_runtime, true); tracked!(osx_rpath_install_name, true); tracked!(panic_abort_tests, true); tracked!(plt, Some(true)); @@ -748,7 +749,7 @@ fn test_debugging_options_tracking_hash() { tracked!(print_fuel, Some("abc".to_string())); tracked!(profile, true); tracked!(profile_emit, Some(PathBuf::from("abc"))); - tracked!(profiler_runtime, None); + tracked!(profiler_runtime, "abc".to_string()); tracked!(relax_elf_relocations, Some(true)); tracked!(relro_level, Some(RelroLevel::Full)); tracked!(simulate_remapped_rust_src_base, Some(PathBuf::from("/rustc/abc"))); diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 5373169bda7..394cb838935 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -777,19 +777,17 @@ impl<'a> CrateLoader<'a> { } fn inject_profiler_runtime(&mut self, krate: &ast::Crate) { - let profiler_runtime = &self.sess.opts.debugging_opts.profiler_runtime; - - if !(profiler_runtime.is_some() - && (self.sess.instrument_coverage() + if self.sess.opts.debugging_opts.no_profiler_runtime + || !(self.sess.instrument_coverage() || self.sess.opts.debugging_opts.profile - || self.sess.opts.cg.profile_generate.enabled())) + || self.sess.opts.cg.profile_generate.enabled()) { return; } info!("loading profiler"); - let name = Symbol::intern(profiler_runtime.as_ref().unwrap()); + let name = Symbol::intern(&self.sess.opts.debugging_opts.profiler_runtime); if name == sym::profiler_builtins && self.sess.contains_name(&krate.attrs, sym::no_core) { self.sess.err( "`profiler_builtins` crate (required by compiler options) \ diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 4936b22c7b9..cf8577a26cf 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -1103,8 +1103,8 @@ impl CrateError { if sess.is_nightly_build() { err.help("consider building the standard library from source with `cargo build -Zbuild-std`"); } - } else if Some(crate_name) - == sess.opts.debugging_opts.profiler_runtime.as_deref().map(Symbol::intern) + } else if crate_name + == Symbol::intern(&sess.opts.debugging_opts.profiler_runtime) { err.note(&"the compiler may have been built without the profiler runtime"); } diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index d27ce6ec81a..42e4fc3839e 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -456,27 +456,25 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { return; // we have already visited everything by now } } - ExprKind::Borrow { borrow_kind, arg } => match borrow_kind { - BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => { - if !self.thir[arg] - .ty - .is_freeze(self.tcx.at(self.thir[arg].span), self.param_env) - { - let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx); - visit::walk_expr(&mut visitor, expr); - if visitor.found { - self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField); + ExprKind::Borrow { borrow_kind, arg } => { + let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx); + visit::walk_expr(&mut visitor, expr); + if visitor.found { + match borrow_kind { + BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique + if !self.thir[arg] + .ty + .is_freeze(self.tcx.at(self.thir[arg].span), self.param_env) => + { + self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField) } + BorrowKind::Mut { .. } => { + self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField) + } + BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => {} } } - BorrowKind::Mut { .. } => { - let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx); - visit::walk_expr(&mut visitor, expr); - if visitor.found { - self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField); - } - } - }, + } _ => {} } visit::walk_expr(self, expr); diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 4ceefa17bcf..f2c2521ab43 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -332,6 +332,11 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { } } + // Don't run unused pass for #[naked] + if self.tcx.has_attr(def_id, sym::naked) { + return; + } + if let Some(captures) = maps.tcx.typeck(local_def_id).closure_min_captures.get(&def_id) { for &var_hir_id in captures.keys() { let var_name = maps.tcx.hir().name(var_hir_id); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index ec23a0769af..95a7b0994b8 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1172,6 +1172,8 @@ options! { "compile without linking"), no_parallel_llvm: bool = (false, parse_no_flag, [UNTRACKED], "run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO)"), + no_profiler_runtime: bool = (false, parse_no_flag, [TRACKED], + "prevent automatic injection of the profiler_builtins crate"), normalize_docs: bool = (false, parse_bool, [TRACKED], "normalize associated items in rustdoc when generating documentation"), osx_rpath_install_name: bool = (false, parse_bool, [TRACKED], @@ -1217,8 +1219,8 @@ options! { profile_emit: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], "file path to emit profiling data at runtime when using 'profile' \ (default based on relative source path)"), - profiler_runtime: Option<String> = (Some(String::from("profiler_builtins")), parse_opt_string, [TRACKED], - "name of the profiler runtime crate to automatically inject, or None to disable"), + profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED], + "name of the profiler runtime crate to automatically inject (default: `profiler_builtins`)"), query_dep_graph: bool = (false, parse_bool, [UNTRACKED], "enable queries of the dependency graph for regression testing (default: no)"), query_stats: bool = (false, parse_bool, [UNTRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 9ab6dbb1ea9..fe87867d299 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -500,6 +500,10 @@ impl Session { &self.parse_sess.span_diagnostic } + pub fn with_disabled_diagnostic<T, F: FnOnce() -> T>(&self, f: F) -> T { + self.parse_sess.span_diagnostic.with_disabled_diagnostic(f) + } + /// Analogous to calling methods on the given `DiagnosticBuilder`, but /// deduplicates on lint ID, span (if any), and message for this `Session` fn diag_once<'a, 'b>( diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index 0cfdde26c2b..fd0544a47bb 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -613,7 +613,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { param_counts.consts + named_type_param_count - default_counts.types - default_counts.consts - - synth_type_param_count }; debug!("expected_min: {:?}", expected_min); debug!("arg_counts.lifetimes: {:?}", gen_args.num_lifetime_params()); diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs index 01276495c18..17f5020300d 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_typeck/src/check/dropck.rs @@ -218,9 +218,9 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( // This closure is a more robust way to check `Predicate` equality // than simple `==` checks (which were the previous implementation). - // It relies on `ty::relate` for `TraitPredicate` and `ProjectionPredicate` - // (which implement the Relate trait), while delegating on simple equality - // for the other `Predicate`. + // It relies on `ty::relate` for `TraitPredicate`, `ProjectionPredicate`, + // `ConstEvaluatable` and `TypeOutlives` (which implement the Relate trait), + // while delegating on simple equality for the other `Predicate`. // This implementation solves (Issue #59497) and (Issue #58311). // It is unclear to me at the moment whether the approach based on `relate` // could be extended easily also to the other `Predicate`. @@ -235,6 +235,13 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( (ty::PredicateKind::Projection(a), ty::PredicateKind::Projection(b)) => { relator.relate(predicate.rebind(a), p.rebind(b)).is_ok() } + ( + ty::PredicateKind::ConstEvaluatable(def_a, substs_a), + ty::PredicateKind::ConstEvaluatable(def_b, substs_b), + ) => tcx.try_unify_abstract_consts(((def_a, substs_a), (def_b, substs_b))), + (ty::PredicateKind::TypeOutlives(a), ty::PredicateKind::TypeOutlives(b)) => { + relator.relate(predicate.rebind(a.0), p.rebind(b.0)).is_ok() + } _ => predicate == p, } }; diff --git a/library/core/src/internal_macros.rs b/library/core/src/internal_macros.rs index 4ea7dfc0735..be12f904640 100644 --- a/library/core/src/internal_macros.rs +++ b/library/core/src/internal_macros.rs @@ -77,7 +77,6 @@ macro_rules! forward_ref_op_assign { } /// Create a zero-size type similar to a closure type, but named. -#[unstable(feature = "std_internals", issue = "none")] macro_rules! impl_fn_for_zst { ($( $( #[$attr: meta] )* diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 1ab7227933b..222ef34b6aa 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -61,61 +61,74 @@ test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) )] #![no_core] +// +// Lints: +#![deny(rust_2021_incompatible_or_patterns)] +#![deny(unsafe_op_in_unsafe_fn)] #![warn(deprecated_in_future)] -#![warn(missing_docs)] #![warn(missing_debug_implementations)] +#![warn(missing_docs)] #![allow(explicit_outlives_requirements)] -#![feature(rustc_allow_const_fn_unstable)] -#![feature(allow_internal_unstable)] -#![feature(arbitrary_self_types)] -#![feature(asm)] -#![feature(bool_to_option)] -#![feature(cfg_target_has_atomic)] -#![feature(const_heap)] +// +// Library features for const fns: +#![feature(const_align_of_val)] #![feature(const_alloc_layout)] #![feature(const_arguments_as_str)] #![feature(const_assert_type)] -#![feature(const_discriminant)] +#![feature(const_caller_location)] #![feature(const_cell_into_inner)] -#![feature(const_intrinsic_copy)] -#![feature(const_intrinsic_forget)] -#![feature(const_float_classify)] +#![feature(const_discriminant)] #![feature(const_float_bits_conv)] -#![feature(const_int_unchecked_arith)] +#![feature(const_float_classify)] +#![feature(const_heap)] #![feature(const_inherent_unchecked_arith)] -#![feature(const_mut_refs)] -#![feature(const_refs_to_cell)] -#![feature(const_panic)] -#![feature(const_pin)] -#![cfg_attr(bootstrap, feature(const_fn_union))] -#![feature(const_impl_trait)] -#![feature(const_fn_floating_point_arithmetic)] -#![feature(const_fn_fn_ptr_basics)] -#![feature(const_fn_trait_bound)] +#![feature(const_int_unchecked_arith)] +#![feature(const_intrinsic_copy)] +#![feature(const_intrinsic_forget)] +#![feature(const_likely)] +#![feature(const_maybe_uninit_as_ptr)] +#![feature(const_maybe_uninit_assume_init)] #![feature(const_option)] -#![feature(const_precise_live_drops)] +#![feature(const_pin)] #![feature(const_ptr_offset)] #![feature(const_ptr_offset_from)] #![feature(const_ptr_read)] #![feature(const_ptr_write)] #![feature(const_raw_ptr_comparison)] -#![feature(const_raw_ptr_deref)] +#![feature(const_size_of_val)] #![feature(const_slice_from_raw_parts)] #![feature(const_slice_ptr_len)] -#![feature(const_size_of_val)] #![feature(const_swap)] -#![feature(const_align_of_val)] #![feature(const_type_id)] #![feature(const_type_name)] -#![feature(const_likely)] #![feature(const_unreachable_unchecked)] -#![feature(const_maybe_uninit_assume_init)] -#![feature(const_maybe_uninit_as_ptr)] -#![feature(custom_inner_attributes)] +#![feature(duration_consts_2)] +#![feature(ptr_metadata)] +#![feature(slice_ptr_get)] +#![feature(variant_count)] +// +// Language features: +#![feature(abi_unadjusted)] +#![feature(allow_internal_unstable)] +#![feature(asm)] +#![feature(associated_type_bounds)] +#![feature(auto_traits)] +#![feature(cfg_target_has_atomic)] +#![feature(const_fn_floating_point_arithmetic)] +#![feature(const_fn_fn_ptr_basics)] +#![feature(const_fn_trait_bound)] +#![cfg_attr(bootstrap, feature(const_fn_transmute))] +#![cfg_attr(bootstrap, feature(const_fn_union))] +#![feature(const_impl_trait)] +#![feature(const_mut_refs)] +#![feature(const_panic)] +#![feature(const_precise_live_drops)] +#![feature(const_raw_ptr_deref)] +#![feature(const_refs_to_cell)] #![feature(decl_macro)] #![feature(doc_cfg)] #![feature(doc_notable_trait)] -#![feature(duration_consts_2)] +#![feature(exhaustive_patterns)] #![feature(extern_types)] #![feature(fundamental)] #![feature(intra_doc_pointers)] @@ -123,53 +136,40 @@ #![feature(lang_items)] #![feature(link_llvm_intrinsics)] #![feature(llvm_asm)] +#![feature(min_specialization)] #![feature(negative_impls)] #![feature(never_type)] -#![feature(nll)] -#![feature(exhaustive_patterns)] #![feature(no_core)] -#![feature(auto_traits)] -#![feature(pin_deref_mut)] +#![feature(no_coverage)] // rust-lang/rust#84605 +#![feature(no_niche)] // rust-lang/rust#68303 +#![feature(platform_intrinsics)] #![feature(prelude_import)] -#![feature(ptr_metadata)] -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd)] +#![feature(rustc_allow_const_fn_unstable)] #![feature(rustc_attrs)] #![feature(simd_ffi)] -#![feature(min_specialization)] #![feature(staged_api)] -#![feature(std_internals)] #![feature(stmt_expr_attributes)] -#![feature(str_split_as_str)] -#![feature(str_split_inclusive_as_str)] -#![feature(char_indices_offset)] #![feature(trait_alias)] #![feature(transparent_unions)] #![feature(try_blocks)] #![feature(unboxed_closures)] #![feature(unsized_fn_params)] -#![feature(variant_count)] -#![feature(tbm_target_feature)] -#![feature(sse4a_target_feature)] -#![feature(arm_target_feature)] -#![feature(powerpc_target_feature)] -#![feature(mips_target_feature)] +// +// Target features: #![feature(aarch64_target_feature)] -#![feature(wasm_target_feature)] +#![feature(adx_target_feature)] +#![feature(arm_target_feature)] #![feature(avx512_target_feature)] #![feature(cmpxchg16b_target_feature)] -#![feature(rtm_target_feature)] #![feature(f16c_target_feature)] #![feature(hexagon_target_feature)] -#![cfg_attr(bootstrap, feature(const_fn_transmute))] -#![feature(abi_unadjusted)] -#![feature(adx_target_feature)] -#![feature(associated_type_bounds)] -#![feature(const_caller_location)] -#![feature(slice_ptr_get)] -#![feature(no_niche)] // rust-lang/rust#68303 -#![feature(no_coverage)] // rust-lang/rust#84605 -#![deny(unsafe_op_in_unsafe_fn)] -#![deny(rust_2021_incompatible_or_patterns)] +#![feature(mips_target_feature)] +#![feature(powerpc_target_feature)] +#![feature(rtm_target_feature)] +#![feature(sse4a_target_feature)] +#![feature(tbm_target_feature)] +#![feature(wasm_target_feature)] // allow using `core::` in intra-doc links #[allow(unused_extern_crates)] diff --git a/library/std/src/os/linux/process.rs b/library/std/src/os/linux/process.rs index 91547b8f916..6daff0f003c 100644 --- a/library/std/src/os/linux/process.rs +++ b/library/std/src/os/linux/process.rs @@ -5,6 +5,7 @@ use crate::io::Result; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::process; +use crate::sealed::Sealed; #[cfg(not(doc))] use crate::sys::fd::FileDesc; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; @@ -84,15 +85,10 @@ impl IntoRawFd for PidFd { } } -mod private_child_ext { - pub trait Sealed {} - impl Sealed for crate::process::Child {} -} - /// Os-specific extensions for [`Child`] /// /// [`Child`]: process::Child -pub trait ChildExt: private_child_ext::Sealed { +pub trait ChildExt: Sealed { /// Obtains a reference to the [`PidFd`] created for this [`Child`], if available. /// /// A pidfd will only be available if its creation was requested with @@ -120,15 +116,10 @@ pub trait ChildExt: private_child_ext::Sealed { fn take_pidfd(&mut self) -> Result<PidFd>; } -mod private_command_ext { - pub trait Sealed {} - impl Sealed for crate::process::Command {} -} - /// Os-specific extensions for [`Command`] /// /// [`Command`]: process::Command -pub trait CommandExt: private_command_ext::Sealed { +pub trait CommandExt: Sealed { /// Sets whether a [`PidFd`](struct@PidFd) should be created for the [`Child`] /// spawned by this [`Command`]. /// By default, no pidfd will be created. diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 99c3369425b..d3e271df8d8 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -205,6 +205,10 @@ pub struct Child { pub stderr: Option<ChildStderr>, } +/// Allows extension traits within `std`. +#[unstable(feature = "sealed", issue = "none")] +impl crate::sealed::Sealed for Child {} + impl AsInner<imp::Process> for Child { fn as_inner(&self) -> &imp::Process { &self.handle diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index 1488bf94841..bc61f472a2b 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -164,16 +164,23 @@ impl Thread { } } + #[cfg(target_os = "haiku")] + pub fn set_name(name: &CStr) { + unsafe { + let thread_self = libc::find_thread(ptr::null_mut()); + libc::rename_thread(thread_self, name.as_ptr()); + } + } + #[cfg(any( target_env = "newlib", - target_os = "haiku", target_os = "l4re", target_os = "emscripten", target_os = "redox", target_os = "vxworks" ))] pub fn set_name(_name: &CStr) { - // Newlib, Haiku, Emscripten, and VxWorks have no way to set a thread name. + // Newlib, Emscripten, and VxWorks have no way to set a thread name. } pub fn sleep(dur: Duration) { diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index f2e38a7eab6..3faf38c66ec 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -464,7 +464,8 @@ class RustBuild(object): # LLVM more often than necessary. # # This git command finds that commit SHA, looking for bors-authored - # merges that modified src/llvm-project. + # merges that modified src/llvm-project or other relevant version + # stamp files. # # This works even in a repository that has not yet initialized # submodules. @@ -472,8 +473,8 @@ class RustBuild(object): "git", "rev-parse", "--show-toplevel", ]).decode(sys.getdefaultencoding()).strip() llvm_sha = subprocess.check_output([ - "git", "log", "--author=bors", "--format=%H", "-n1", - "--no-patch", "--first-parent", + "git", "rev-list", "--author=bors@rust-lang.org", "-n1", + "--merges", "--first-parent", "HEAD", "--", "{}/src/llvm-project".format(top_level), "{}/src/bootstrap/download-ci-llvm-stamp".format(top_level), @@ -665,7 +666,10 @@ class RustBuild(object): # Look for a version to compare to based on the current commit. # Only commits merged by bors will have CI artifacts. - merge_base = ["git", "log", "--author=bors", "--pretty=%H", "-n1"] + merge_base = [ + "git", "rev-list", "--author=bors@rust-lang.org", "-n1", + "--merges", "--first-parent", "HEAD" + ] commit = subprocess.check_output(merge_base, universal_newlines=True).strip() # Warn if there were changes to the compiler or standard library since the ancestor commit. diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 90ef48798dd..b265760dc57 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -165,6 +165,7 @@ target | std | notes `wasm32-unknown-unknown` | ✓ | WebAssembly `wasm32-wasi` | ✓ | WebAssembly with WASI `x86_64-apple-ios` | ✓ | 64-bit x86 iOS +[`aarch64-apple-ios-sim`](platform-support/aarch64-apple-ios-sim.md) | ✓ | | Apple iOS Simulator on ARM64 `x86_64-fortanix-unknown-sgx` | ✓ | [Fortanix ABI] for 64-bit Intel SGX `x86_64-fuchsia` | ✓ | 64-bit Fuchsia `x86_64-linux-android` | ✓ | 64-bit x86 Android @@ -196,7 +197,6 @@ host tools. target | std | host | notes -------|:---:|:----:|------- `aarch64-apple-ios-macabi` | ? | | Apple Catalyst on ARM64 -[`aarch64-apple-ios-sim`](platform-support/aarch64-apple-ios-sim.md) | ✓ | | Apple iOS Simulator on ARM64 `aarch64-apple-tvos` | * | | ARM64 tvOS `aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD `aarch64-unknown-hermit` | ? | | diff --git a/src/doc/rustc/src/platform-support/aarch64-apple-ios-sim.md b/src/doc/rustc/src/platform-support/aarch64-apple-ios-sim.md index 9aa5db26f91..3f29e2c5e1f 100644 --- a/src/doc/rustc/src/platform-support/aarch64-apple-ios-sim.md +++ b/src/doc/rustc/src/platform-support/aarch64-apple-ios-sim.md @@ -1,6 +1,6 @@ # aarch64-apple-ios-sim -**Tier: 3** +**Tier: 2** Apple iOS Simulator on ARM64. @@ -39,17 +39,16 @@ Currently there is no support to run the rustc test suite for this target. *Note: Building for this target requires the corresponding iOS SDK, as provided by Xcode 12+.* -If `rustc` has support for that target and the library artifacts are available, -then Rust programs can be built for that target: +From Rust Nightly 1.56.0 (2021-08-03) on the artifacts are shipped pre-compiled: ```text -rustc --target aarch64-apple-ios-sim your-code.rs +rustup target add aarch64-apple-ios-sim --toolchain nightly ``` -On Rust Nightly it is possible to build without the target artifacts available: +Rust programs can be built for that target: ```text -cargo build -Z build-std --target aarch64-apple-ios-sim +rustc --target aarch64-apple-ios-sim your-code.rs ``` There is no easy way to run simple programs in the iOS simulator. diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 8f74a48547d..207c89cbfe8 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -98,7 +98,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { visibility: Inherited, def_id: ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, kind: box ImplItem(Impl { - span: Span::from_rustc_span(self.cx.tcx.def_span(impl_def_id)), + span: Span::new(self.cx.tcx.def_span(impl_def_id)), unsafety: hir::Unsafety::Normal, generics: ( self.cx.tcx.generics_of(impl_def_id), diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index b3b89e6e673..43979423ae6 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -517,7 +517,7 @@ fn build_module( } } - let span = clean::Span::from_rustc_span(cx.tcx.def_span(did)); + let span = clean::Span::new(cx.tcx.def_span(did)); clean::Module { items, span } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index b3fc1e73f78..3d65fcedaf4 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -95,7 +95,8 @@ impl Clean<Item> for doctree::Module<'_> { // determine if we should display the inner contents or // the outer `mod` item for the source code. - let span = Span::from_rustc_span({ + + let span = Span::new({ let where_outer = self.where_outer(cx.tcx); let sm = cx.sess().source_map(); let outer = sm.lookup_char_pos(where_outer.lo()); diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 5c73d3de5b9..22e4d21c87b 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -343,7 +343,7 @@ crate struct Item { rustc_data_structures::static_assert_size!(Item, 56); crate fn rustc_span(def_id: DefId, tcx: TyCtxt<'_>) -> Span { - Span::from_rustc_span(def_id.as_local().map_or_else( + Span::new(def_id.as_local().map_or_else( || tcx.def_span(def_id), |local| { let hir = tcx.hir(); @@ -1943,10 +1943,11 @@ crate enum Variant { crate struct Span(rustc_span::Span); impl Span { - crate fn from_rustc_span(sp: rustc_span::Span) -> Self { - // Get the macro invocation instead of the definition, - // in case the span is result of a macro expansion. - // (See rust-lang/rust#39726) + /// Wraps a [`rustc_span::Span`]. In case this span is the result of a macro expansion, the + /// span will be updated to point to the macro invocation instead of the macro definition. + /// + /// (See rust-lang/rust#39726) + crate fn new(sp: rustc_span::Span) -> Self { Self(sp.source_callsite()) } diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index abd1fd2bf39..e44158bc042 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -276,6 +276,8 @@ crate struct RenderOptions { crate show_type_layout: bool, crate unstable_features: rustc_feature::UnstableFeatures, crate emit: Vec<EmitType>, + /// If `true`, HTML source pages will generate links for items to their definition. + crate generate_link_to_definition: bool, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -655,6 +657,15 @@ impl Options { let generate_redirect_map = matches.opt_present("generate-redirect-map"); let show_type_layout = matches.opt_present("show-type-layout"); let nocapture = matches.opt_present("nocapture"); + let generate_link_to_definition = matches.opt_present("generate-link-to-definition"); + + if generate_link_to_definition && (show_coverage || output_format != OutputFormat::Html) { + diag.struct_err( + "--generate-link-to-definition option can only be used with HTML output format", + ) + .emit(); + return Err(1); + } let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format, &debugging_opts); @@ -721,6 +732,7 @@ impl Options { crate_name.as_deref(), ), emit, + generate_link_to_definition, }, crate_name, output_format, diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 8ab6aa775d2..eb7c12d13c3 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -484,7 +484,11 @@ crate enum HrefError { NotInExternalCache, } -crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<String>), HrefError> { +crate fn href_with_root_path( + did: DefId, + cx: &Context<'_>, + root_path: Option<&str>, +) -> Result<(String, ItemType, Vec<String>), HrefError> { let cache = &cx.cache(); let relative_to = &cx.current; fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] { @@ -495,6 +499,7 @@ crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<Str return Err(HrefError::Private); } + let mut is_remote = false; let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) { Some(&(ref fqp, shortty)) => (fqp, shortty, { let module_fqp = to_module_fqp(shortty, fqp); @@ -508,6 +513,7 @@ crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<Str shortty, match cache.extern_locations[&did.krate] { ExternalLocation::Remote(ref s) => { + is_remote = true; let s = s.trim_end_matches('/'); let mut s = vec![s]; s.extend(module_fqp[..].iter().map(String::as_str)); @@ -522,6 +528,12 @@ crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<Str } } }; + if !is_remote { + if let Some(root_path) = root_path { + let root = root_path.trim_end_matches('/'); + url_parts.insert(0, root); + } + } let last = &fqp.last().unwrap()[..]; let filename; match shortty { @@ -536,6 +548,10 @@ crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<Str Ok((url_parts.join("/"), shortty, fqp.to_vec())) } +crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<String>), HrefError> { + href_with_root_path(did, cx, None) +} + /// Both paths should only be modules. /// This is because modules get their own directories; that is, `std::vec` and `std::vec::Vec` will /// both need `../iter/trait.Iterator.html` to get at the iterator trait. diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 33b1d98313c..3cdb1352bef 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -6,15 +6,28 @@ //! Use the `render_with_highlighting` to highlight some rust code. use crate::html::escape::Escape; +use crate::html::render::Context; -use std::fmt::Display; +use std::fmt::{Display, Write}; use std::iter::Peekable; use rustc_lexer::{LiteralKind, TokenKind}; use rustc_span::edition::Edition; use rustc_span::symbol::Symbol; - -use super::format::Buffer; +use rustc_span::{BytePos, Span, DUMMY_SP}; + +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. +crate struct ContextInfo<'a, 'b, 'c> { + crate context: &'a Context<'b>, + /// This span contains the current file we're going through. + crate file_span: Span, + /// This field is used to know "how far" from the top of the directory we are to link to either + /// documentation pages or other source pages. + crate root_path: &'c str, +} /// Highlights `src`, returning the HTML output. crate fn render_with_highlighting( @@ -25,6 +38,7 @@ crate fn render_with_highlighting( tooltip: Option<(Option<Edition>, &str)>, edition: Edition, extra_content: Option<Buffer>, + context_info: Option<ContextInfo<'_, '_, '_>>, ) { debug!("highlighting: ================\n{}\n==============", src); if let Some((edition_info, class)) = tooltip { @@ -41,7 +55,7 @@ crate fn render_with_highlighting( } write_header(out, class, extra_content); - write_code(out, &src, edition); + write_code(out, &src, edition, context_info); write_footer(out, playground_button); } @@ -57,16 +71,33 @@ fn write_header(out: &mut Buffer, class: Option<&str>, extra_content: Option<Buf } } -fn write_code(out: &mut Buffer, src: &str, edition: Edition) { +/// Convert the given `src` source code into HTML by adding classes for highlighting. +/// +/// This code is used to render code blocks (in the documentation) as well as the source code pages. +/// +/// 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 +/// item definition. +/// +/// More explanations about spans and how we use them here are provided in the +fn write_code( + out: &mut Buffer, + src: &str, + edition: Edition, + context_info: Option<ContextInfo<'_, '_, '_>>, +) { // This replace allows to fix how the code source with DOS backline characters is displayed. let src = src.replace("\r\n", "\n"); - Classifier::new(&src, edition).highlight(&mut |highlight| { - match highlight { - Highlight::Token { text, class } => string(out, Escape(text), class), - Highlight::EnterSpan { class } => enter_span(out, class), - Highlight::ExitSpan => exit_span(out), - }; - }); + Classifier::new(&src, edition, context_info.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP)) + .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), + }; + }); } fn write_footer(out: &mut Buffer, playground_button: Option<&str>) { @@ -82,14 +113,14 @@ enum Class { KeyWord, // Keywords that do pointer/reference stuff. RefKeyWord, - Self_, + Self_(Span), Op, Macro, MacroNonTerminal, String, Number, Bool, - Ident, + Ident(Span), Lifetime, PreludeTy, PreludeVal, @@ -105,20 +136,29 @@ impl Class { Class::Attribute => "attribute", Class::KeyWord => "kw", Class::RefKeyWord => "kw-2", - Class::Self_ => "self", + Class::Self_(_) => "self", Class::Op => "op", Class::Macro => "macro", Class::MacroNonTerminal => "macro-nonterminal", Class::String => "string", Class::Number => "number", Class::Bool => "bool-val", - Class::Ident => "ident", + Class::Ident(_) => "ident", Class::Lifetime => "lifetime", Class::PreludeTy => "prelude-ty", Class::PreludeVal => "prelude-val", Class::QuestionMark => "question-mark", } } + + /// In case this is an item which can be converted into a link to a definition, it'll contain + /// 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, + } + } } enum Highlight<'a> { @@ -144,14 +184,19 @@ impl Iterator for TokenIter<'a> { } } -fn get_real_ident_class(text: &str, edition: Edition) -> Class { - match text { +/// Classifies into identifier class; returns `None` if this is a non-keyword identifier. +fn get_real_ident_class(text: &str, edition: Edition, allow_path_keywords: bool) -> Option<Class> { + let ignore: &[&str] = + if allow_path_keywords { &["self", "Self", "super", "crate"] } else { &["self", "Self"] }; + if ignore.iter().any(|k| *k == text) { + return None; + } + Some(match text { "ref" | "mut" => Class::RefKeyWord, - "self" | "Self" => Class::Self_, "false" | "true" => Class::Bool, _ if Symbol::intern(text).is_reserved(|| edition) => Class::KeyWord, - _ => Class::Ident, - } + _ => return None, + }) } /// Processes program tokens, classifying strings of text by highlighting @@ -163,11 +208,14 @@ struct Classifier<'a> { in_macro_nonterminal: bool, edition: Edition, byte_pos: u32, + file_span: Span, src: &'a str, } impl<'a> Classifier<'a> { - fn new(src: &str, edition: Edition) -> Classifier<'_> { + /// Takes as argument the source code to HTML-ify, the rust edition to use and the source code + /// file span which will be used later on by the `span_correspondance_map`. + fn new(src: &str, edition: Edition, file_span: Span) -> Classifier<'_> { let tokens = TokenIter { src }.peekable(); Classifier { tokens, @@ -176,10 +224,18 @@ impl<'a> Classifier<'a> { in_macro_nonterminal: false, edition, byte_pos: 0, + file_span, src, } } + /// Convenient wrapper to create a [`Span`] from a position in the file. + fn new_span(&self, lo: u32, text: &str) -> Span { + let hi = lo + text.len() as u32; + let file_lo = self.file_span.lo(); + self.file_span.with_lo(file_lo + BytePos(lo)).with_hi(file_lo + BytePos(hi)) + } + /// Concatenate colons and idents as one when possible. fn get_full_ident_path(&mut self) -> Vec<(TokenKind, usize, usize)> { let start = self.byte_pos as usize; @@ -201,17 +257,17 @@ impl<'a> Classifier<'a> { if has_ident { return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)]; } else { - return vec![(TokenKind::Colon, pos, pos + nb)]; + return vec![(TokenKind::Colon, start, pos + nb)]; } } - if let Some((Class::Ident, text)) = self.tokens.peek().map(|(token, text)| { + if let Some((None, text)) = self.tokens.peek().map(|(token, text)| { if *token == TokenKind::Ident { - let class = get_real_ident_class(text, edition); + let class = get_real_ident_class(text, edition, true); (class, text) } else { // Doesn't matter which Class we put in here... - (Class::Comment, text) + (Some(Class::Comment), text) } }) { // We only "add" the colon if there is an ident behind. @@ -221,7 +277,7 @@ impl<'a> Classifier<'a> { } else if nb > 0 && has_ident { return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)]; } else if nb > 0 { - return vec![(TokenKind::Colon, pos, pos + nb)]; + return vec![(TokenKind::Colon, start, start + nb)]; } else if has_ident { return vec![(TokenKind::Ident, start, pos)]; } else { @@ -230,11 +286,15 @@ impl<'a> Classifier<'a> { } } - /// Wraps the tokens iteration to ensure that the byte_pos is always correct. - fn next(&mut self) -> Option<(TokenKind, &'a str)> { + /// Wraps the tokens iteration to ensure that the `byte_pos` is always correct. + /// + /// It returns the token's kind, the token as a string and its byte position in the source + /// string. + fn next(&mut self) -> Option<(TokenKind, &'a str, u32)> { if let Some((kind, text)) = self.tokens.next() { + let before = self.byte_pos; self.byte_pos += text.len() as u32; - Some((kind, text)) + Some((kind, text, before)) } else { None } @@ -254,23 +314,36 @@ impl<'a> Classifier<'a> { .unwrap_or(false) { let tokens = self.get_full_ident_path(); - for (token, start, end) in tokens { - let text = &self.src[start..end]; - self.advance(token, text, sink); + for (token, start, end) in &tokens { + let text = &self.src[*start..*end]; + self.advance(*token, text, sink, *start as u32); self.byte_pos += text.len() as u32; } + if !tokens.is_empty() { + continue; + } } - if let Some((token, text)) = self.next() { - self.advance(token, text, sink); + if let Some((token, text, before)) = self.next() { + self.advance(token, text, sink, before); } else { break; } } } - /// Single step of highlighting. This will classify `token`, but maybe also - /// a couple of following ones as well. - fn advance(&mut self, token: TokenKind, text: &'a str, sink: &mut dyn FnMut(Highlight<'a>)) { + /// Single step of highlighting. This will classify `token`, but maybe also a couple of + /// following ones as well. + /// + /// `before` is the position of the given token in the `source` string and is used as "lo" byte + /// in case we want to try to generate a link for this token using the + /// `span_correspondance_map`. + fn advance( + &mut self, + token: TokenKind, + text: &'a str, + sink: &mut dyn FnMut(Highlight<'a>), + before: u32, + ) { let lookahead = self.peek(); let no_highlight = |sink: &mut dyn FnMut(_)| sink(Highlight::Token { text, class: None }); let class = match token { @@ -401,19 +474,22 @@ impl<'a> Classifier<'a> { sink(Highlight::Token { text, class: None }); return; } - TokenKind::Ident => match get_real_ident_class(text, self.edition) { - Class::Ident => match text { + TokenKind::Ident => match get_real_ident_class(text, self.edition, false) { + None => match text { "Option" | "Result" => Class::PreludeTy, "Some" | "None" | "Ok" | "Err" => Class::PreludeVal, _ if self.in_macro_nonterminal => { self.in_macro_nonterminal = false; Class::MacroNonTerminal } - _ => Class::Ident, + "self" | "Self" => Class::Self_(self.new_span(before, text)), + _ => Class::Ident(self.new_span(before, text)), }, - c => c, + Some(c) => c, }, - TokenKind::RawIdent | TokenKind::UnknownPrefix => Class::Ident, + TokenKind::RawIdent | TokenKind::UnknownPrefix => { + Class::Ident(self.new_span(before, text)) + } TokenKind::Lifetime { .. } => Class::Lifetime, }; // Anything that didn't return above is the simple case where we the @@ -446,13 +522,75 @@ fn exit_span(out: &mut Buffer) { /// enter_span(Foo), string("text", None), exit_span() /// string("text", Foo) /// ``` +/// /// The latter can be thought of as a shorthand for the former, which is more /// flexible. -fn string<T: Display>(out: &mut Buffer, text: T, klass: Option<Class>) { - match klass { - None => write!(out, "{}", text), - Some(klass) => write!(out, "<span class=\"{}\">{}</span>", klass.as_html(), text), +/// +/// Note that if `context` is not `None` and that the given `klass` contains a `Span`, the function +/// will then try to find this `span` in the `span_correspondance_map`. If found, it'll then +/// generate a link for this element (which corresponds to where its definition is located). +fn string<T: Display>( + out: &mut Buffer, + text: T, + klass: Option<Class>, + context_info: &Option<ContextInfo<'_, '_, '_>>, +) { + let klass = match klass { + None => return write!(out, "{}", text), + Some(klass) => klass, + }; + let def_span = match klass.get_span() { + Some(d) => d, + None => { + write!(out, "<span class=\"{}\">{}</span>", klass.as_html(), text); + return; + } + }; + let mut text_s = text.to_string(); + if text_s.contains("::") { + text_s = text_s.split("::").intersperse("::").fold(String::new(), |mut path, t| { + match t { + "self" | "Self" => write!( + &mut path, + "<span class=\"{}\">{}</span>", + Class::Self_(DUMMY_SP).as_html(), + t + ), + "crate" | "super" => { + write!(&mut path, "<span class=\"{}\">{}</span>", Class::KeyWord.as_html(), t) + } + t => write!(&mut path, "{}", t), + } + .expect("Failed to build source HTML path"); + path + }); + } + if let Some(context_info) = context_info { + if let Some(href) = + context_info.context.shared.span_correspondance_map.get(&def_span).and_then(|href| { + let context = context_info.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, + // a link to their definition can be generated using this: + // https://github.com/rust-lang/rust/blob/60f1a2fc4b535ead9c85ce085fdce49b1b097531/src/librustdoc/html/render/context.rs#L315-L338 + match href { + LinkFromSrc::Local(span) => context + .href_from_span(*span) + .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) + } + } + }) + { + write!(out, "<a class=\"{}\" href=\"{}\">{}</a>", klass.as_html(), href, text_s); + return; + } } + write!(out, "<span class=\"{}\">{}</span>", klass.as_html(), text_s); } #[cfg(test)] diff --git a/src/librustdoc/html/highlight/fixtures/highlight.html b/src/librustdoc/html/highlight/fixtures/highlight.html new file mode 100644 index 00000000000..abc2db1790c --- /dev/null +++ b/src/librustdoc/html/highlight/fixtures/highlight.html @@ -0,0 +1,4 @@ +<span class="kw">use</span> <span class="ident"><span class="kw">crate</span>::a::foo</span>; +<span class="kw">use</span> <span class="ident"><span class="self">self</span>::whatever</span>; +<span class="kw">let</span> <span class="ident">x</span> <span class="op">=</span> <span class="ident"><span class="kw">super</span>::b::foo</span>; +<span class="kw">let</span> <span class="ident">y</span> <span class="op">=</span> <span class="ident"><span class="self">Self</span>::whatever</span>; \ No newline at end of file diff --git a/src/librustdoc/html/highlight/fixtures/sample.html b/src/librustdoc/html/highlight/fixtures/sample.html index 8d23477bbcb..866caea9256 100644 --- a/src/librustdoc/html/highlight/fixtures/sample.html +++ b/src/librustdoc/html/highlight/fixtures/sample.html @@ -23,7 +23,7 @@ <span class="macro">assert!</span>(<span class="self">self</span>.<span class="ident">length</span> <span class="op"><</span> <span class="ident">N</span> <span class="op">&&</span> <span class="ident">index</span> <span class="op"><</span><span class="op">=</span> <span class="self">self</span>.<span class="ident">length</span>); <span class="ident">::std::env::var</span>(<span class="string">"gateau"</span>).<span class="ident">is_ok</span>(); <span class="attribute">#[<span class="ident">rustfmt::skip</span>]</span> - <span class="kw">let</span> <span class="ident">s</span>:<span class="ident">std</span><span class="ident">::path::PathBuf</span> <span class="op">=</span> <span class="ident">std::path::PathBuf::new</span>(); + <span class="kw">let</span> <span class="ident">s</span>:<span class="ident">std::path::PathBuf</span> <span class="op">=</span> <span class="ident">std::path::PathBuf::new</span>(); <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">s</span> <span class="op">=</span> <span class="ident">String::new</span>(); <span class="kw">match</span> <span class="kw-2">&</span><span class="ident">s</span> { diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs index a505865b149..68592ae96c1 100644 --- a/src/librustdoc/html/highlight/tests.rs +++ b/src/librustdoc/html/highlight/tests.rs @@ -22,7 +22,7 @@ fn test_html_highlighting() { let src = include_str!("fixtures/sample.rs"); let html = { let mut out = Buffer::new(); - write_code(&mut out, src, Edition::Edition2018); + write_code(&mut out, src, Edition::Edition2018, None); format!("{}<pre><code>{}</code></pre>\n", STYLE, out.into_inner()) }; expect_file!["fixtures/sample.html"].assert_eq(&html); @@ -36,7 +36,21 @@ fn test_dos_backline() { println!(\"foo\");\r\n\ }\r\n"; let mut html = Buffer::new(); - write_code(&mut html, src, Edition::Edition2018); + write_code(&mut html, src, Edition::Edition2018, None); expect_file!["fixtures/dos_line.html"].assert_eq(&html.into_inner()); }); } + +#[test] +fn test_keyword_highlight() { + create_default_session_globals_then(|| { + let src = "use crate::a::foo; +use self::whatever; +let x = super::b::foo; +let y = Self::whatever;"; + + let mut html = Buffer::new(); + write_code(&mut html, src, Edition::Edition2018, None); + expect_file!["fixtures/highlight.html"].assert_eq(&html.into_inner()); + }); +} diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index b8756d2526e..472323daf30 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -330,6 +330,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> { tooltip, edition, None, + None, ); Some(Event::Html(s.into_inner().into())) } diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index b6c3220901f..6ce0828e159 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -17,7 +17,10 @@ use rustc_span::symbol::sym; use super::cache::{build_index, ExternalLocation}; use super::print_item::{full_path, item_path, print_item}; use super::write_shared::write_shared; -use super::{print_sidebar, settings, AllTypes, NameDoc, StylePath, BASIC_KEYWORDS}; +use super::{ + collect_spans_and_sources, print_sidebar, settings, AllTypes, LinkFromSrc, NameDoc, StylePath, + BASIC_KEYWORDS, +}; use crate::clean; use crate::clean::ExternalCrate; @@ -46,7 +49,7 @@ crate struct Context<'tcx> { pub(crate) current: Vec<String>, /// The current destination folder of where HTML artifacts should be placed. /// This changes as the context descends into the module hierarchy. - pub(super) dst: PathBuf, + crate dst: PathBuf, /// A flag, which when `true`, will render pages which redirect to the /// real location of an item. This is used to allow external links to /// publicly reused items to redirect to the right location. @@ -58,7 +61,7 @@ crate struct Context<'tcx> { /// Issue for improving the situation: [#82381][] /// /// [#82381]: https://github.com/rust-lang/rust/issues/82381 - pub(super) shared: Rc<SharedContext<'tcx>>, + crate shared: Rc<SharedContext<'tcx>>, /// The [`Cache`] used during rendering. /// /// Ideally the cache would be in [`SharedContext`], but it's mutated @@ -68,7 +71,11 @@ crate struct Context<'tcx> { /// It's immutable once in `Context`, so it's not as bad that it's not in /// `SharedContext`. // FIXME: move `cache` to `SharedContext` - pub(super) cache: Rc<Cache>, + crate cache: Rc<Cache>, + /// This flag indicates whether `[src]` links should be generated or not. If + /// the source files are present in the html rendering, then this will be + /// `true`. + crate include_sources: bool, } // `Context` is cloned a lot, so we don't want the size to grow unexpectedly. @@ -84,10 +91,6 @@ crate struct SharedContext<'tcx> { /// This describes the layout of each page, and is not modified after /// creation of the context (contains info like the favicon and added html). crate layout: layout::Layout, - /// This flag indicates whether `[src]` links should be generated or not. If - /// the source files are present in the html rendering, then this will be - /// `true`. - crate include_sources: bool, /// The local file sources we've emitted and their respective url-paths. crate local_sources: FxHashMap<PathBuf, String>, /// Show the memory layout of types in the docs. @@ -125,6 +128,10 @@ crate struct SharedContext<'tcx> { redirections: Option<RefCell<FxHashMap<String, String>>>, pub(crate) templates: tera::Tera, + + /// Correspondance map used to link types used in the source code pages to allow to click on + /// links to jump to the type's definition. + crate span_correspondance_map: FxHashMap<rustc_span::Span, LinkFromSrc>, } impl SharedContext<'_> { @@ -293,15 +300,19 @@ impl<'tcx> Context<'tcx> { /// may happen, for example, with externally inlined items where the source /// of their crate documentation isn't known. pub(super) fn src_href(&self, item: &clean::Item) -> Option<String> { - if item.span(self.tcx()).is_dummy() { + self.href_from_span(item.span(self.tcx())) + } + + crate fn href_from_span(&self, span: clean::Span) -> Option<String> { + if span.is_dummy() { return None; } let mut root = self.root_path(); let mut path = String::new(); - let cnum = item.span(self.tcx()).cnum(self.sess()); + let cnum = span.cnum(self.sess()); // We can safely ignore synthetic `SourceFile`s. - let file = match item.span(self.tcx()).filename(self.sess()) { + let file = match span.filename(self.sess()) { FileName::Real(ref path) => path.local_path_if_available().to_path_buf(), _ => return None, }; @@ -339,8 +350,8 @@ impl<'tcx> Context<'tcx> { (&*symbol, &path) }; - let loline = item.span(self.tcx()).lo(self.sess()).line; - let hiline = item.span(self.tcx()).hi(self.sess()).line; + let loline = span.lo(self.sess()).line; + let hiline = span.hi(self.sess()).line; let lines = if loline == hiline { loline.to_string() } else { format!("{}-{}", loline, hiline) }; Some(format!( @@ -362,9 +373,9 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { const RUN_ON_MODULE: bool = true; fn init( - mut krate: clean::Crate, + krate: clean::Crate, options: RenderOptions, - mut cache: Cache, + cache: Cache, tcx: TyCtxt<'tcx>, ) -> Result<(Self, clean::Crate), Error> { // need to save a copy of the options for rendering the index page @@ -385,6 +396,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { unstable_features, generate_redirect_map, show_type_layout, + generate_link_to_definition, .. } = options; @@ -444,13 +456,21 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { _ => {} } } + + let (mut krate, local_sources, matches) = collect_spans_and_sources( + tcx, + krate, + &src_root, + include_sources, + generate_link_to_definition, + ); + let (sender, receiver) = channel(); let mut scx = SharedContext { tcx, collapsed: krate.collapsed, src_root, - include_sources, - local_sources: Default::default(), + local_sources, issue_tracker_base_url, layout, created_dirs: Default::default(), @@ -466,6 +486,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { redirections: if generate_redirect_map { Some(Default::default()) } else { None }, show_type_layout, templates, + span_correspondance_map: matches, }; // Add the default themes to the `Vec` of stylepaths @@ -483,12 +504,6 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { let dst = output; scx.ensure_dir(&dst)?; - if emit_crate { - krate = sources::render(&dst, &mut scx, krate)?; - } - - // Build our search index - let index = build_index(&krate, &mut cache, tcx); let mut cx = Context { current: Vec::new(), @@ -497,8 +512,16 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { id_map: RefCell::new(id_map), shared: Rc::new(scx), cache: Rc::new(cache), + include_sources, }; + if emit_crate { + krate = sources::render(&mut cx, krate)?; + } + + // Build our search index + let index = build_index(&krate, Rc::get_mut(&mut cx.cache).unwrap(), tcx); + // Write shared runs within a flock; disable thread dispatching of IO temporarily. Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true); write_shared(&cx, &krate, index, &md_opts)?; @@ -514,6 +537,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { id_map: RefCell::new(IdMap::new()), shared: Rc::clone(&self.shared), cache: Rc::clone(&self.cache), + include_sources: self.include_sources, } } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index c05ea81ac1f..fd2e18a8be7 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -30,9 +30,11 @@ mod tests; mod context; mod print_item; +mod span_map; mod write_shared; crate use context::*; +crate use span_map::{collect_spans_and_sources, LinkFromSrc}; use std::collections::VecDeque; use std::default::Default; diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 5c30d8bbd17..f31305c76e6 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -119,7 +119,7 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer, // [src] link in the downstream documentation will actually come back to // this page, and this link will be auto-clicked. The `id` attribute is // used to find the link to auto-click. - if cx.shared.include_sources && !item.is_primitive() { + if cx.include_sources && !item.is_primitive() { write_srclink(cx, item, buf); } @@ -1081,6 +1081,7 @@ fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Mac None, it.span(cx.tcx()).inner().edition(), None, + None, ); }); document(w, cx, it, None) diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs new file mode 100644 index 00000000000..b35cd45dc9a --- /dev/null +++ b/src/librustdoc/html/render/span_map.rs @@ -0,0 +1,164 @@ +use crate::clean; +use crate::html::sources; + +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{ExprKind, GenericParam, GenericParamKind, HirId, Mod, Node}; +use rustc_middle::ty::TyCtxt; +use rustc_span::Span; + +use std::path::{Path, PathBuf}; + +/// This enum allows us to store two different kinds of information: +/// +/// In case the `span` definition comes from the same crate, we can simply get the `span` and use +/// it as is. +/// +/// Otherwise, we store the definition `DefId` and will generate a link to the documentation page +/// instead of the source code directly. +#[derive(Debug)] +crate enum LinkFromSrc { + Local(clean::Span), + External(DefId), +} + +/// This function will do at most two things: +/// +/// 1. Generate a `span` correspondance map which links an item `span` to its definition `span`. +/// 2. Collect the source code files. +/// +/// It returns the `krate`, the source code files and the `span` correspondance map. +/// +/// Note about the `span` correspondance map: the keys are actually `(lo, hi)` of `span`s. We don't +/// need the `span` context later on, only their position, so instead of keep a whole `Span`, we +/// only keep the `lo` and `hi`. +crate fn collect_spans_and_sources( + tcx: TyCtxt<'_>, + krate: clean::Crate, + src_root: &Path, + include_sources: bool, + generate_link_to_definition: bool, +) -> (clean::Crate, FxHashMap<PathBuf, String>, FxHashMap<Span, LinkFromSrc>) { + let mut visitor = SpanMapVisitor { tcx, matches: FxHashMap::default() }; + + if include_sources { + if generate_link_to_definition { + intravisit::walk_crate(&mut visitor, tcx.hir().krate()); + } + let (krate, sources) = sources::collect_local_sources(tcx, src_root, krate); + (krate, sources, visitor.matches) + } else { + (krate, Default::default(), Default::default()) + } +} + +struct SpanMapVisitor<'tcx> { + crate tcx: TyCtxt<'tcx>, + crate matches: FxHashMap<Span, LinkFromSrc>, +} + +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>) { + 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` + // (such as primitive types!). + Res::Def(kind, def_id) if kind != DefKind::TyParam => { + if matches!(kind, DefKind::Macro(_)) { + return; + } + Some(def_id) + } + Res::Local(_) => None, + Res::Err => return, + _ => return, + }; + if let Some(span) = self.tcx.hir().res_span(path.res) { + self.matches.insert( + path_span.unwrap_or_else(|| path.span), + LinkFromSrc::Local(clean::Span::new(span)), + ); + } else if let Some(def_id) = info { + self.matches + .insert(path_span.unwrap_or_else(|| path.span), LinkFromSrc::External(def_id)); + } + } +} + +impl Visitor<'tcx> for SpanMapVisitor<'tcx> { + type Map = rustc_middle::hir::map::Map<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::All(self.tcx.hir()) + } + + fn visit_generic_param(&mut self, p: &'tcx GenericParam<'tcx>) { + if !matches!(p.kind, GenericParamKind::Type { .. }) { + return; + } + for bound in p.bounds { + if let Some(trait_ref) = bound.trait_ref() { + self.handle_path(&trait_ref.path, None); + } + } + } + + fn visit_path(&mut self, path: &'tcx rustc_hir::Path<'tcx>, _id: HirId) { + self.handle_path(path, None); + intravisit::walk_path(self, path); + } + + fn visit_mod(&mut self, m: &'tcx Mod<'tcx>, span: Span, id: HirId) { + // To make the difference between "mod foo {}" and "mod foo;". In case we "import" another + // file, we want to link to it. Otherwise no need to create a link. + if !span.overlaps(m.inner) { + // Now that we confirmed it's a file import, we want to get the span for the module + // name only and not all the "mod foo;". + if let Some(node) = self.tcx.hir().find(id) { + match node { + Node::Item(item) => { + self.matches + .insert(item.ident.span, LinkFromSrc::Local(clean::Span::new(m.inner))); + } + _ => {} + } + } + } + intravisit::walk_mod(self, m, id); + } + + fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) { + match expr.kind { + ExprKind::MethodCall(segment, method_span, _, _) => { + if let Some(hir_id) = segment.hir_id { + let hir = self.tcx.hir(); + let body_id = hir.enclosing_body_owner(hir_id); + let typeck_results = self.tcx.sess.with_disabled_diagnostic(|| { + self.tcx.typeck_body( + hir.maybe_body_owned_by(body_id).expect("a body which isn't a body"), + ) + }); + if let Some(def_id) = typeck_results.type_dependent_def_id(expr.hir_id) { + self.matches.insert( + method_span, + match hir.span_if_local(def_id) { + Some(span) => LinkFromSrc::Local(clean::Span::new(span)), + None => LinkFromSrc::External(def_id), + }, + ); + } + } + } + _ => {} + } + intravisit::walk_expr(self, expr); + } + + fn visit_use(&mut self, path: &'tcx rustc_hir::Path<'tcx>, id: HirId) { + self.handle_path(path, None); + intravisit::walk_use(self, path, id); + } +} diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 4411b7771ed..c16769c474a 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -272,7 +272,7 @@ pub(super) fn write_shared( write_minify("search.js", static_files::SEARCH_JS)?; write_minify("settings.js", static_files::SETTINGS_JS)?; - if cx.shared.include_sources { + if cx.include_sources { write_minify("source-script.js", static_files::sidebar::SOURCE_SCRIPT)?; } @@ -398,7 +398,7 @@ pub(super) fn write_shared( } } - if cx.shared.include_sources { + if cx.include_sources { let mut hierarchy = Hierarchy::new(OsString::new()); for source in cx .shared diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 80dd7a7a952..73916e204d9 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -5,8 +5,10 @@ use crate::fold::DocFolder; use crate::html::format::Buffer; use crate::html::highlight; use crate::html::layout; -use crate::html::render::{SharedContext, BASIC_KEYWORDS}; +use crate::html::render::{Context, BASIC_KEYWORDS}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::LOCAL_CRATE; +use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::edition::Edition; use rustc_span::source_map::FileName; @@ -14,52 +16,117 @@ use std::ffi::OsStr; use std::fs; use std::path::{Component, Path, PathBuf}; -crate fn render( - dst: &Path, - scx: &mut SharedContext<'_>, - krate: clean::Crate, -) -> Result<clean::Crate, Error> { +crate fn render(cx: &mut Context<'_>, krate: clean::Crate) -> Result<clean::Crate, Error> { info!("emitting source files"); - let dst = dst.join("src").join(&*krate.name.as_str()); - scx.ensure_dir(&dst)?; - let mut folder = SourceCollector { dst, scx }; + let dst = cx.dst.join("src").join(&*krate.name.as_str()); + cx.shared.ensure_dir(&dst)?; + let mut folder = SourceCollector { dst, cx, emitted_local_sources: FxHashSet::default() }; Ok(folder.fold_crate(krate)) } +crate fn collect_local_sources<'tcx>( + tcx: TyCtxt<'tcx>, + src_root: &Path, + krate: clean::Crate, +) -> (clean::Crate, FxHashMap<PathBuf, String>) { + let mut lsc = LocalSourcesCollector { tcx, local_sources: FxHashMap::default(), src_root }; + + let krate = lsc.fold_crate(krate); + (krate, lsc.local_sources) +} + +struct LocalSourcesCollector<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + local_sources: FxHashMap<PathBuf, String>, + src_root: &'a Path, +} + +fn is_real_and_local(span: clean::Span, sess: &Session) -> bool { + span.filename(sess).is_real() && span.cnum(sess) == LOCAL_CRATE +} + +impl LocalSourcesCollector<'_, '_> { + fn add_local_source(&mut self, item: &clean::Item) { + let sess = self.tcx.sess; + let span = item.span(self.tcx); + // skip all synthetic "files" + if !is_real_and_local(span, sess) { + return; + } + let filename = span.filename(sess); + let p = match filename { + FileName::Real(ref file) => match file.local_path() { + Some(p) => p.to_path_buf(), + _ => return, + }, + _ => return, + }; + if self.local_sources.contains_key(&*p) { + // We've already emitted this source + return; + } + + let mut href = String::new(); + clean_path(&self.src_root, &p, false, |component| { + href.push_str(&component.to_string_lossy()); + href.push('/'); + }); + + let src_fname = p.file_name().expect("source has no filename").to_os_string(); + let mut fname = src_fname.clone(); + fname.push(".html"); + href.push_str(&fname.to_string_lossy()); + self.local_sources.insert(p, href); + } +} + +impl DocFolder for LocalSourcesCollector<'_, '_> { + fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> { + self.add_local_source(&item); + + // FIXME: if `include_sources` isn't set and DocFolder didn't require consuming the crate by value, + // we could return None here without having to walk the rest of the crate. + Some(self.fold_item_recur(item)) + } +} + /// Helper struct to render all source code to HTML pages struct SourceCollector<'a, 'tcx> { - scx: &'a mut SharedContext<'tcx>, + cx: &'a mut Context<'tcx>, /// Root destination to place all HTML output into dst: PathBuf, + emitted_local_sources: FxHashSet<PathBuf>, } impl DocFolder for SourceCollector<'_, '_> { fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> { + let tcx = self.cx.tcx(); + let span = item.span(tcx); + let sess = tcx.sess; + // If we're not rendering sources, there's nothing to do. // If we're including source files, and we haven't seen this file yet, // then we need to render it out to the filesystem. - if self.scx.include_sources - // skip all synthetic "files" - && item.span(self.scx.tcx).filename(self.sess()).is_real() - // skip non-local files - && item.span(self.scx.tcx).cnum(self.sess()) == LOCAL_CRATE - { - let filename = item.span(self.scx.tcx).filename(self.sess()); + if self.cx.include_sources && is_real_and_local(span, sess) { + let filename = span.filename(sess); + let span = span.inner(); + let pos = sess.source_map().lookup_source_file(span.lo()); + let file_span = span.with_lo(pos.start_pos).with_hi(pos.end_pos); // If it turns out that we couldn't read this file, then we probably // can't read any of the files (generating html output from json or // something like that), so just don't include sources for the // entire crate. The other option is maintaining this mapping on a // per-file basis, but that's probably not worth it... - self.scx.include_sources = match self.emit_source(&filename) { + self.cx.include_sources = match self.emit_source(&filename, file_span) { Ok(()) => true, Err(e) => { - self.scx.tcx.sess.span_err( - item.span(self.scx.tcx).inner(), + self.cx.shared.tcx.sess.span_err( + span, &format!( "failed to render source code for `{}`: {}", filename.prefer_local(), - e + e, ), ); false @@ -73,12 +140,12 @@ impl DocFolder for SourceCollector<'_, '_> { } impl SourceCollector<'_, 'tcx> { - fn sess(&self) -> &'tcx Session { - &self.scx.tcx.sess - } - /// Renders the given filename into its corresponding HTML source file. - fn emit_source(&mut self, filename: &FileName) -> Result<(), Error> { + fn emit_source( + &mut self, + filename: &FileName, + file_span: rustc_span::Span, + ) -> Result<(), Error> { let p = match *filename { FileName::Real(ref file) => { if let Some(local_path) = file.local_path() { @@ -89,7 +156,7 @@ impl SourceCollector<'_, 'tcx> { } _ => return Ok(()), }; - if self.scx.local_sources.contains_key(&*p) { + if self.emitted_local_sources.contains(&*p) { // We've already emitted this source return Ok(()); } @@ -107,20 +174,17 @@ impl SourceCollector<'_, 'tcx> { // Create the intermediate directories let mut cur = self.dst.clone(); let mut root_path = String::from("../../"); - let mut href = String::new(); - clean_path(&self.scx.src_root, &p, false, |component| { + clean_path(&self.cx.shared.src_root, &p, false, |component| { cur.push(component); root_path.push_str("../"); - href.push_str(&component.to_string_lossy()); - href.push('/'); }); - self.scx.ensure_dir(&cur)?; + + self.cx.shared.ensure_dir(&cur)?; let src_fname = p.file_name().expect("source has no filename").to_os_string(); let mut fname = src_fname.clone(); fname.push(".html"); cur.push(&fname); - href.push_str(&fname.to_string_lossy()); let title = format!("{} - source", src_fname.to_string_lossy()); let desc = format!("Source of the Rust file `{}`.", filename.prefer_remapped()); @@ -128,23 +192,25 @@ impl SourceCollector<'_, 'tcx> { title: &title, css_class: "source", root_path: &root_path, - static_root_path: self.scx.static_root_path.as_deref(), + static_root_path: self.cx.shared.static_root_path.as_deref(), description: &desc, keywords: BASIC_KEYWORDS, - resource_suffix: &self.scx.resource_suffix, - extra_scripts: &[&format!("source-files{}", self.scx.resource_suffix)], - static_extra_scripts: &[&format!("source-script{}", self.scx.resource_suffix)], + resource_suffix: &self.cx.shared.resource_suffix, + extra_scripts: &[&format!("source-files{}", self.cx.shared.resource_suffix)], + static_extra_scripts: &[&format!("source-script{}", self.cx.shared.resource_suffix)], }; let v = layout::render( - &self.scx.templates, - &self.scx.layout, + &self.cx.shared.templates, + &self.cx.shared.layout, &page, "", - |buf: &mut _| print_src(buf, contents, self.scx.edition()), - &self.scx.style_files, + |buf: &mut _| { + print_src(buf, contents, self.cx.shared.edition(), file_span, &self.cx, &root_path) + }, + &self.cx.shared.style_files, ); - self.scx.fs.write(&cur, v.as_bytes())?; - self.scx.local_sources.insert(p, href); + self.cx.shared.fs.write(&cur, v.as_bytes())?; + self.emitted_local_sources.insert(p); Ok(()) } } @@ -178,7 +244,14 @@ where /// Wrapper struct to render the source code of a file. This will do things like /// adding line numbers to the left-hand side. -fn print_src(buf: &mut Buffer, s: &str, edition: Edition) { +fn print_src( + buf: &mut Buffer, + s: &str, + edition: Edition, + file_span: rustc_span::Span, + context: &Context<'_>, + root_path: &str, +) { let lines = s.lines().count(); let mut line_numbers = Buffer::empty_from(buf); let mut cols = 0; @@ -192,5 +265,14 @@ fn print_src(buf: &mut Buffer, s: &str, edition: Edition) { writeln!(line_numbers, "<span id=\"{0}\">{0:1$}</span>", i, cols); } line_numbers.write_str("</pre>"); - highlight::render_with_highlighting(s, buf, None, None, None, edition, Some(line_numbers)); + highlight::render_with_highlighting( + s, + buf, + None, + None, + None, + edition, + Some(line_numbers), + Some(highlight::ContextInfo { context, file_span, root_path }), + ); } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 4e33eab5650..bbc48f49e63 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -450,6 +450,10 @@ nav.sub { border-bottom-left-radius: 5px; } +.example-wrap > pre.rust a:hover { + text-decoration: underline; +} + .rustdoc:not(.source) .example-wrap > pre:not(.line-number) { width: 100%; overflow-x: auto; diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index fa755777584..a98725e683c 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -607,6 +607,13 @@ fn opts() -> Vec<RustcOptGroup> { unstable("nocapture", |o| { o.optflag("", "nocapture", "Don't capture stdout and stderr of tests") }), + unstable("generate-link-to-definition", |o| { + o.optflag( + "", + "generate-link-to-definition", + "Make the identifiers in the HTML source code pages navigable", + ) + }), ] } diff --git a/src/test/rustdoc-ui/generate-link-to-definition-opt-unstable.rs b/src/test/rustdoc-ui/generate-link-to-definition-opt-unstable.rs new file mode 100644 index 00000000000..87620d74ee6 --- /dev/null +++ b/src/test/rustdoc-ui/generate-link-to-definition-opt-unstable.rs @@ -0,0 +1,6 @@ +// This test purpose is to check that the "--generate-link-to-definition" +// option can only be used on nightly. + +// compile-flags: --generate-link-to-definition + +pub fn f() {} diff --git a/src/test/rustdoc-ui/generate-link-to-definition-opt-unstable.stderr b/src/test/rustdoc-ui/generate-link-to-definition-opt-unstable.stderr new file mode 100644 index 00000000000..a8ddf91bcbf --- /dev/null +++ b/src/test/rustdoc-ui/generate-link-to-definition-opt-unstable.stderr @@ -0,0 +1,2 @@ +error: the `-Z unstable-options` flag must also be passed to enable the flag `generate-link-to-definition` + diff --git a/src/test/rustdoc-ui/generate-link-to-definition-opt.rs b/src/test/rustdoc-ui/generate-link-to-definition-opt.rs new file mode 100644 index 00000000000..8f4f561b44d --- /dev/null +++ b/src/test/rustdoc-ui/generate-link-to-definition-opt.rs @@ -0,0 +1,6 @@ +// This test purpose is to check that the "--generate-link-to-definition" +// option can only be used with HTML generation. + +// compile-flags: -Zunstable-options --generate-link-to-definition --output-format json + +pub fn f() {} diff --git a/src/test/rustdoc-ui/generate-link-to-definition-opt.stderr b/src/test/rustdoc-ui/generate-link-to-definition-opt.stderr new file mode 100644 index 00000000000..4c8c607e7da --- /dev/null +++ b/src/test/rustdoc-ui/generate-link-to-definition-opt.stderr @@ -0,0 +1,2 @@ +error: --generate-link-to-definition option can only be used with HTML output format + diff --git a/src/test/rustdoc-ui/generate-link-to-definition-opt2.rs b/src/test/rustdoc-ui/generate-link-to-definition-opt2.rs new file mode 100644 index 00000000000..da5142087dd --- /dev/null +++ b/src/test/rustdoc-ui/generate-link-to-definition-opt2.rs @@ -0,0 +1,6 @@ +// This test purpose is to check that the "--generate-link-to-definition" +// option can only be used with HTML generation. + +// compile-flags: -Zunstable-options --generate-link-to-definition --show-coverage + +pub fn f() {} diff --git a/src/test/rustdoc-ui/generate-link-to-definition-opt2.stderr b/src/test/rustdoc-ui/generate-link-to-definition-opt2.stderr new file mode 100644 index 00000000000..4c8c607e7da --- /dev/null +++ b/src/test/rustdoc-ui/generate-link-to-definition-opt2.stderr @@ -0,0 +1,2 @@ +error: --generate-link-to-definition option can only be used with HTML output format + diff --git a/src/test/rustdoc/auxiliary/source-code-bar.rs b/src/test/rustdoc/auxiliary/source-code-bar.rs new file mode 100644 index 00000000000..8700d688ef7 --- /dev/null +++ b/src/test/rustdoc/auxiliary/source-code-bar.rs @@ -0,0 +1,17 @@ +//! just some other file. :) + +use crate::Foo; + +pub struct Bar { + field: Foo, +} + +pub struct Bar2 { + field: crate::Foo, +} + +pub mod sub { + pub trait Trait { + fn tadam() {} + } +} diff --git a/src/test/rustdoc/auxiliary/source_code.rs b/src/test/rustdoc/auxiliary/source_code.rs new file mode 100644 index 00000000000..72a5c1a0ae9 --- /dev/null +++ b/src/test/rustdoc/auxiliary/source_code.rs @@ -0,0 +1 @@ +pub struct SourceCode; diff --git a/src/test/rustdoc/check-source-code-urls-to-def.rs b/src/test/rustdoc/check-source-code-urls-to-def.rs new file mode 100644 index 00000000000..e3ae79ccdb1 --- /dev/null +++ b/src/test/rustdoc/check-source-code-urls-to-def.rs @@ -0,0 +1,44 @@ +// compile-flags: -Zunstable-options --generate-link-to-definition +// aux-build:source_code.rs +// build-aux-docs + +#![crate_name = "foo"] + +extern crate source_code; + +// @has 'src/foo/check-source-code-urls-to-def.rs.html' + +// @has - '//a[@href="../../src/foo/auxiliary/source-code-bar.rs.html#1-17"]' 'bar' +#[path = "auxiliary/source-code-bar.rs"] +pub mod bar; + +// @count - '//a[@href="../../src/foo/auxiliary/source-code-bar.rs.html#5-7"]' 4 +use bar::Bar; +// @has - '//a[@href="../../src/foo/auxiliary/source-code-bar.rs.html#13-17"]' 'self' +// @has - '//a[@href="../../src/foo/auxiliary/source-code-bar.rs.html#14-16"]' 'Trait' +use bar::sub::{self, Trait}; + +pub struct Foo; + +impl Foo { + fn hello(&self) {} +} + +fn babar() {} + +// @has - '//a/@href' '/struct.String.html' +// @count - '//a[@href="../../src/foo/check-source-code-urls-to-def.rs.html#21"]' 5 +// @has - '//a[@href="../../source_code/struct.SourceCode.html"]' 'source_code::SourceCode' +pub fn foo(a: u32, b: &str, c: String, d: Foo, e: bar::Bar, f: source_code::SourceCode) { + let x = 12; + let y: Foo = Foo; + let z: Bar = bar::Bar { field: Foo }; + babar(); + // @has - '//a[@href="../../src/foo/check-source-code-urls-to-def.rs.html#24"]' 'hello' + y.hello(); +} + +// @has - '//a[@href="../../src/foo/auxiliary/source-code-bar.rs.html#14-16"]' 'bar::sub::Trait' +// @has - '//a[@href="../../src/foo/auxiliary/source-code-bar.rs.html#14-16"]' 'Trait' +pub fn foo2<T: bar::sub::Trait, V: Trait>(t: &T, v: &V) { +} diff --git a/src/test/ui/asm/naked-functions-ffi.rs b/src/test/ui/asm/naked-functions-ffi.rs new file mode 100644 index 00000000000..5b2a8ed3034 --- /dev/null +++ b/src/test/ui/asm/naked-functions-ffi.rs @@ -0,0 +1,12 @@ +// check-pass +// only-x86_64 +#![feature(asm)] +#![feature(naked_functions)] +#![crate_type = "lib"] + +#[naked] +pub extern "C" fn naked(p: char) -> u128 { + //~^ WARN uses type `char` + //~| WARN uses type `u128` + unsafe { asm!("", options(noreturn)); } +} diff --git a/src/test/ui/asm/naked-functions-ffi.stderr b/src/test/ui/asm/naked-functions-ffi.stderr new file mode 100644 index 00000000000..a6772badeb6 --- /dev/null +++ b/src/test/ui/asm/naked-functions-ffi.stderr @@ -0,0 +1,20 @@ +warning: `extern` fn uses type `char`, which is not FFI-safe + --> $DIR/naked-functions-ffi.rs:8:28 + | +LL | pub extern "C" fn naked(p: char) -> u128 { + | ^^^^ not FFI-safe + | + = note: `#[warn(improper_ctypes_definitions)]` on by default + = help: consider using `u32` or `libc::wchar_t` instead + = note: the `char` type has no C equivalent + +warning: `extern` fn uses type `u128`, which is not FFI-safe + --> $DIR/naked-functions-ffi.rs:8:37 + | +LL | pub extern "C" fn naked(p: char) -> u128 { + | ^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + +warning: 2 warnings emitted + diff --git a/src/test/ui/asm/naked-functions-unused.rs b/src/test/ui/asm/naked-functions-unused.rs new file mode 100644 index 00000000000..e1f2362bb6f --- /dev/null +++ b/src/test/ui/asm/naked-functions-unused.rs @@ -0,0 +1,81 @@ +// only-x86_64 +#![deny(unused)] +#![feature(asm)] +#![feature(naked_functions)] +#![crate_type = "lib"] + +pub trait Trait { + extern "sysv64" fn trait_associated(a: usize, b: usize) -> usize; + extern "sysv64" fn trait_method(&self, a: usize, b: usize) -> usize; +} + +pub mod normal { + pub extern "sysv64" fn function(a: usize, b: usize) -> usize { + //~^ ERROR unused variable: `a` + //~| ERROR unused variable: `b` + unsafe { asm!("", options(noreturn)); } + } + + pub struct Normal; + + impl Normal { + pub extern "sysv64" fn associated(a: usize, b: usize) -> usize { + //~^ ERROR unused variable: `a` + //~| ERROR unused variable: `b` + unsafe { asm!("", options(noreturn)); } + } + + pub extern "sysv64" fn method(&self, a: usize, b: usize) -> usize { + //~^ ERROR unused variable: `a` + //~| ERROR unused variable: `b` + unsafe { asm!("", options(noreturn)); } + } + } + + impl super::Trait for Normal { + extern "sysv64" fn trait_associated(a: usize, b: usize) -> usize { + //~^ ERROR unused variable: `a` + //~| ERROR unused variable: `b` + unsafe { asm!("", options(noreturn)); } + } + + extern "sysv64" fn trait_method(&self, a: usize, b: usize) -> usize { + //~^ ERROR unused variable: `a` + //~| ERROR unused variable: `b` + unsafe { asm!("", options(noreturn)); } + } + } +} + +pub mod naked { + #[naked] + pub extern "sysv64" fn function(a: usize, b: usize) -> usize { + unsafe { asm!("", options(noreturn)); } + } + + pub struct Naked; + + impl Naked { + #[naked] + pub extern "sysv64" fn associated(a: usize, b: usize) -> usize { + unsafe { asm!("", options(noreturn)); } + } + + #[naked] + pub extern "sysv64" fn method(&self, a: usize, b: usize) -> usize { + unsafe { asm!("", options(noreturn)); } + } + } + + impl super::Trait for Naked { + #[naked] + extern "sysv64" fn trait_associated(a: usize, b: usize) -> usize { + unsafe { asm!("", options(noreturn)); } + } + + #[naked] + extern "sysv64" fn trait_method(&self, a: usize, b: usize) -> usize { + unsafe { asm!("", options(noreturn)); } + } + } +} diff --git a/src/test/ui/asm/naked-functions-unused.stderr b/src/test/ui/asm/naked-functions-unused.stderr new file mode 100644 index 00000000000..840353366b6 --- /dev/null +++ b/src/test/ui/asm/naked-functions-unused.stderr @@ -0,0 +1,69 @@ +error: unused variable: `a` + --> $DIR/naked-functions-unused.rs:13:37 + | +LL | pub extern "sysv64" fn function(a: usize, b: usize) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_a` + | +note: the lint level is defined here + --> $DIR/naked-functions-unused.rs:2:9 + | +LL | #![deny(unused)] + | ^^^^^^ + = note: `#[deny(unused_variables)]` implied by `#[deny(unused)]` + +error: unused variable: `b` + --> $DIR/naked-functions-unused.rs:13:47 + | +LL | pub extern "sysv64" fn function(a: usize, b: usize) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_b` + +error: unused variable: `a` + --> $DIR/naked-functions-unused.rs:22:43 + | +LL | pub extern "sysv64" fn associated(a: usize, b: usize) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_a` + +error: unused variable: `b` + --> $DIR/naked-functions-unused.rs:22:53 + | +LL | pub extern "sysv64" fn associated(a: usize, b: usize) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_b` + +error: unused variable: `a` + --> $DIR/naked-functions-unused.rs:28:46 + | +LL | pub extern "sysv64" fn method(&self, a: usize, b: usize) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_a` + +error: unused variable: `b` + --> $DIR/naked-functions-unused.rs:28:56 + | +LL | pub extern "sysv64" fn method(&self, a: usize, b: usize) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_b` + +error: unused variable: `a` + --> $DIR/naked-functions-unused.rs:36:45 + | +LL | extern "sysv64" fn trait_associated(a: usize, b: usize) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_a` + +error: unused variable: `b` + --> $DIR/naked-functions-unused.rs:36:55 + | +LL | extern "sysv64" fn trait_associated(a: usize, b: usize) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_b` + +error: unused variable: `a` + --> $DIR/naked-functions-unused.rs:42:48 + | +LL | extern "sysv64" fn trait_method(&self, a: usize, b: usize) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_a` + +error: unused variable: `b` + --> $DIR/naked-functions-unused.rs:42:58 + | +LL | extern "sysv64" fn trait_method(&self, a: usize, b: usize) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_b` + +error: aborting due to 10 previous errors + diff --git a/src/test/ui/const-generics/const_evaluatable_checked/drop_impl.rs b/src/test/ui/const-generics/const_evaluatable_checked/drop_impl.rs new file mode 100644 index 00000000000..41fb5d70afd --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/drop_impl.rs @@ -0,0 +1,16 @@ +//check-pass +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] + +struct Foo<const N: usize> +where + [(); N + 1]: ; + +impl<const N: usize> Drop for Foo<N> +where + [(); N + 1]: , +{ + fn drop(&mut self) {} +} + +fn main() {} diff --git a/src/test/ui/impl-trait/explicit-generic-args-with-impl-trait/explicit-generic-args-for-impl.stderr b/src/test/ui/impl-trait/explicit-generic-args-with-impl-trait/explicit-generic-args-for-impl.stderr index 739e55e2943..3add0429d2d 100644 --- a/src/test/ui/impl-trait/explicit-generic-args-with-impl-trait/explicit-generic-args-for-impl.stderr +++ b/src/test/ui/impl-trait/explicit-generic-args-with-impl-trait/explicit-generic-args-for-impl.stderr @@ -1,12 +1,12 @@ -error[E0107]: this function takes at most 1 generic argument but 2 generic arguments were supplied +error[E0107]: this function takes 1 generic argument but 2 generic arguments were supplied --> $DIR/explicit-generic-args-for-impl.rs:6:5 | LL | foo::<str, String>("".to_string()); | ^^^ ------ help: remove this generic argument | | - | expected at most 1 generic argument + | expected 1 generic argument | -note: function defined here, with at most 1 generic parameter: `T` +note: function defined here, with 1 generic parameter: `T` --> $DIR/explicit-generic-args-for-impl.rs:3:4 | LL | fn foo<T: ?Sized>(_f: impl AsRef<T>) {} diff --git a/src/test/ui/impl-trait/explicit-generic-args-with-impl-trait/issue-87718.rs b/src/test/ui/impl-trait/explicit-generic-args-with-impl-trait/issue-87718.rs new file mode 100644 index 00000000000..e2ee63821ae --- /dev/null +++ b/src/test/ui/impl-trait/explicit-generic-args-with-impl-trait/issue-87718.rs @@ -0,0 +1,9 @@ +// check-pass + +#![feature(explicit_generic_args_with_impl_trait)] + +fn f<T: ?Sized>(_: impl AsRef<T>, _: impl AsRef<T>) {} + +fn main() { + f::<[u8]>("a", b"a"); +} diff --git a/src/test/ui/impl-trait/explicit-generic-args-with-impl-trait/not-enough-args.rs b/src/test/ui/impl-trait/explicit-generic-args-with-impl-trait/not-enough-args.rs new file mode 100644 index 00000000000..ffb0582fe8d --- /dev/null +++ b/src/test/ui/impl-trait/explicit-generic-args-with-impl-trait/not-enough-args.rs @@ -0,0 +1,8 @@ +#![feature(explicit_generic_args_with_impl_trait)] + +fn f<T: ?Sized, U: ?Sized>(_: impl AsRef<T>, _: impl AsRef<U>) {} + +fn main() { + f::<[u8]>("a", b"a"); + //~^ ERROR: this function takes 2 generic arguments but 1 generic argument was supplied +} diff --git a/src/test/ui/impl-trait/explicit-generic-args-with-impl-trait/not-enough-args.stderr b/src/test/ui/impl-trait/explicit-generic-args-with-impl-trait/not-enough-args.stderr new file mode 100644 index 00000000000..233b47445db --- /dev/null +++ b/src/test/ui/impl-trait/explicit-generic-args-with-impl-trait/not-enough-args.stderr @@ -0,0 +1,21 @@ +error[E0107]: this function takes 2 generic arguments but 1 generic argument was supplied + --> $DIR/not-enough-args.rs:6:5 + | +LL | f::<[u8]>("a", b"a"); + | ^ ---- supplied 1 generic argument + | | + | expected 2 generic arguments + | +note: function defined here, with 2 generic parameters: `T`, `U` + --> $DIR/not-enough-args.rs:3:4 + | +LL | fn f<T: ?Sized, U: ?Sized>(_: impl AsRef<T>, _: impl AsRef<U>) {} + | ^ - - +help: add missing generic argument + | +LL | f::<[u8], U>("a", b"a"); + | ^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0107`. diff --git a/src/test/ui/thread-local-in-ctfe.stderr b/src/test/ui/thread-local-in-ctfe.stderr index 9890597b7bd..fd967604624 100644 --- a/src/test/ui/thread-local-in-ctfe.stderr +++ b/src/test/ui/thread-local-in-ctfe.stderr @@ -30,3 +30,4 @@ LL | A error: aborting due to 5 previous errors +For more information about this error, try `rustc --explain E0625`. diff --git a/src/test/ui/thread-local-static.stderr b/src/test/ui/thread-local-static.stderr index 08bf593a5a7..712050a25fc 100644 --- a/src/test/ui/thread-local-static.stderr +++ b/src/test/ui/thread-local-static.stderr @@ -40,5 +40,5 @@ LL | std::mem::swap(x, &mut STATIC_VAR_2) error: aborting due to 5 previous errors -Some errors have detailed explanations: E0013, E0133, E0658. +Some errors have detailed explanations: E0013, E0133, E0625, E0658. For more information about an error, try `rustc --explain E0013`. |
