diff --git a/Cargo.lock b/Cargo.lock index f49c8515fca1a..5b8d28752b4da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3556,6 +3556,7 @@ dependencies = [ "rustc_parse", "rustc_session", "rustc_span", + "rustc_target", "tracing", ] diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 8daeef0cbd95f..162b59777ca62 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -279,7 +279,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ); let sig = hir::FnSig { decl, - header: this.lower_fn_header(header, fn_sig_span, id), + header: this.lower_fn_header(header), span: this.lower_span(fn_sig_span), }; hir::ItemKind::Fn(sig, generics, body_id) @@ -291,17 +291,12 @@ impl<'hir> LoweringContext<'_, 'hir> { } ModKind::Unloaded => panic!("`mod` items should have been loaded by now"), }, - ItemKind::ForeignMod(ref fm) => { - if fm.abi.is_none() { - self.maybe_lint_missing_abi(span, id, abi::Abi::C { unwind: false }); - } - hir::ItemKind::ForeignMod { - abi: fm.abi.map_or(abi::Abi::C { unwind: false }, |abi| self.lower_abi(abi)), - items: self - .arena - .alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))), - } - } + ItemKind::ForeignMod(ref fm) => hir::ItemKind::ForeignMod { + abi: fm.abi.map_or(abi::Abi::FALLBACK, |abi| self.lower_abi(abi)), + items: self + .arena + .alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))), + }, ItemKind::GlobalAsm(ref asm) => { hir::ItemKind::GlobalAsm(self.lower_inline_asm(span, asm)) } @@ -807,7 +802,7 @@ impl<'hir> LoweringContext<'_, 'hir> { AssocItemKind::Fn(box FnKind(_, ref sig, ref generics, None)) => { let names = self.lower_fn_params_to_names(&sig.decl); let (generics, sig) = - self.lower_method_sig(generics, sig, trait_item_def_id, false, None, i.id); + self.lower_method_sig(generics, sig, trait_item_def_id, false, None); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names))) } AssocItemKind::Fn(box FnKind(_, ref sig, ref generics, Some(ref body))) => { @@ -820,7 +815,6 @@ impl<'hir> LoweringContext<'_, 'hir> { trait_item_def_id, false, asyncness.opt_return_id(), - i.id, ); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id))) } @@ -897,7 +891,6 @@ impl<'hir> LoweringContext<'_, 'hir> { impl_item_def_id, impl_trait_return_allow, asyncness.opt_return_id(), - i.id, ); (generics, hir::ImplItemKind::Fn(sig, body_id)) @@ -1292,9 +1285,8 @@ impl<'hir> LoweringContext<'_, 'hir> { fn_def_id: LocalDefId, impl_trait_return_allow: bool, is_async: Option, - id: NodeId, ) -> (hir::Generics<'hir>, hir::FnSig<'hir>) { - let header = self.lower_fn_header(sig.header, sig.span, id); + let header = self.lower_fn_header(sig.header); let (generics, decl) = self.add_in_band_defs( generics, fn_def_id, @@ -1311,12 +1303,12 @@ impl<'hir> LoweringContext<'_, 'hir> { (generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) }) } - fn lower_fn_header(&mut self, h: FnHeader, span: Span, id: NodeId) -> hir::FnHeader { + fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader { hir::FnHeader { unsafety: self.lower_unsafety(h.unsafety), asyncness: self.lower_asyncness(h.asyncness), constness: self.lower_constness(h.constness), - abi: self.lower_extern(h.ext, span, id), + abi: self.lower_extern(h.ext), } } @@ -1327,13 +1319,10 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } - pub(super) fn lower_extern(&mut self, ext: Extern, span: Span, id: NodeId) -> abi::Abi { + pub(super) fn lower_extern(&mut self, ext: Extern) -> abi::Abi { match ext { Extern::None => abi::Abi::Rust, - Extern::Implicit => { - self.maybe_lint_missing_abi(span, id, abi::Abi::C { unwind: false }); - abi::Abi::C { unwind: false } - } + Extern::Implicit => abi::Abi::FALLBACK, Extern::Explicit(abi) => self.lower_abi(abi), } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index deb7e742e5cc3..fc953279c4665 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -53,7 +53,7 @@ use rustc_hir::definitions::{DefKey, DefPathData, Definitions}; use rustc_hir::intravisit; use rustc_hir::{ConstArg, GenericArg, InferKind, ParamName}; use rustc_index::vec::{Idx, IndexVec}; -use rustc_session::lint::builtin::{BARE_TRAIT_OBJECTS, MISSING_ABI}; +use rustc_session::lint::builtin::BARE_TRAIT_OBJECTS; use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; use rustc_session::utils::{FlattenNonterminals, NtToTokenstream}; use rustc_session::Session; @@ -62,7 +62,6 @@ use rustc_span::hygiene::ExpnId; use rustc_span::source_map::{respan, CachingSourceMapView, DesugaringKind}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; -use rustc_target::spec::abi::Abi; use smallvec::SmallVec; use std::collections::BTreeMap; @@ -1361,7 +1360,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } TyKind::BareFn(ref f) => self.with_in_scope_lifetime_defs(&f.generic_params, |this| { this.with_anonymous_lifetime_mode(AnonymousLifetimeMode::PassThrough, |this| { - let span = this.sess.source_map().next_point(t.span.shrink_to_lo()); hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy { generic_params: this.lower_generic_params( &f.generic_params, @@ -1369,7 +1367,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ImplTraitContext::disallowed(), ), unsafety: this.lower_unsafety(f.unsafety), - abi: this.lower_extern(f.ext, span, t.id), + abi: this.lower_extern(f.ext), decl: this.lower_fn_decl(&f.decl, None, false, None), param_names: this.lower_fn_params_to_names(&f.decl), })) @@ -2749,26 +2747,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } } - - fn maybe_lint_missing_abi(&mut self, span: Span, id: NodeId, default: Abi) { - // FIXME(davidtwco): This is a hack to detect macros which produce spans of the - // call site which do not have a macro backtrace. See #61963. - let is_macro_callsite = self - .sess - .source_map() - .span_to_snippet(span) - .map(|snippet| snippet.starts_with("#[")) - .unwrap_or(true); - if !is_macro_callsite { - self.resolver.lint_buffer().buffer_lint_with_diagnostic( - MISSING_ABI, - id, - span, - "extern declarations without an explicit ABI are deprecated", - BuiltinLintDiagnostics::MissingAbi(span, default), - ) - } - } } fn body_ids(bodies: &BTreeMap>) -> Vec { diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml index 6b931c598ed84..4a6eb80fb30ce 100644 --- a/compiler/rustc_ast_passes/Cargo.toml +++ b/compiler/rustc_ast_passes/Cargo.toml @@ -14,4 +14,5 @@ rustc_feature = { path = "../rustc_feature" } rustc_parse = { path = "../rustc_parse" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index a243300edd9d2..aca4503903c06 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -15,12 +15,13 @@ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{error_code, pluralize, struct_span_err, Applicability}; use rustc_parse::validate_attr; -use rustc_session::lint::builtin::PATTERNS_IN_FNS_WITHOUT_BODY; +use rustc_session::lint::builtin::{MISSING_ABI, PATTERNS_IN_FNS_WITHOUT_BODY}; use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; use rustc_session::Session; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; +use rustc_target::spec::abi; use std::mem; use std::ops::DerefMut; @@ -844,6 +845,10 @@ impl<'a> AstValidator<'a> { .emit(); }); self.check_late_bound_lifetime_defs(&bfty.generic_params); + if let Extern::Implicit = bfty.ext { + let sig_span = self.session.source_map().next_point(ty.span.shrink_to_lo()); + self.maybe_lint_missing_abi(sig_span, ty.id); + } } TyKind::TraitObject(ref bounds, ..) => { let mut any_lifetime_bounds = false; @@ -894,6 +899,26 @@ impl<'a> AstValidator<'a> { _ => {} } } + + fn maybe_lint_missing_abi(&mut self, span: Span, id: NodeId) { + // FIXME(davidtwco): This is a hack to detect macros which produce spans of the + // call site which do not have a macro backtrace. See #61963. + let is_macro_callsite = self + .session + .source_map() + .span_to_snippet(span) + .map(|snippet| snippet.starts_with("#[")) + .unwrap_or(true); + if !is_macro_callsite { + self.lint_buffer.buffer_lint_with_diagnostic( + MISSING_ABI, + id, + span, + "extern declarations without an explicit ABI are deprecated", + BuiltinLintDiagnostics::MissingAbi(span, abi::Abi::FALLBACK), + ) + } + } } /// Checks that generic parameters are in the correct order, @@ -1178,7 +1203,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { walk_list!(self, visit_attribute, &item.attrs); return; // Avoid visiting again. } - ItemKind::ForeignMod(ForeignMod { unsafety, .. }) => { + ItemKind::ForeignMod(ForeignMod { abi, unsafety, .. }) => { let old_item = mem::replace(&mut self.extern_mod, Some(item)); self.invalid_visibility( &item.vis, @@ -1187,6 +1212,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> { if let Unsafe::Yes(span) = unsafety { self.err_handler().span_err(span, "extern block cannot be declared unsafe"); } + if abi.is_none() { + self.maybe_lint_missing_abi(item.span, item.id); + } visit::walk_item(self, item); self.extern_mod = old_item; return; // Avoid visiting again. @@ -1526,6 +1554,17 @@ impl<'a> Visitor<'a> for AstValidator<'a> { .emit(); } + if let FnKind::Fn( + _, + _, + FnSig { span: sig_span, header: FnHeader { ext: Extern::Implicit, .. }, .. }, + _, + _, + ) = fk + { + self.maybe_lint_missing_abi(*sig_span, id); + } + // Functions without bodies cannot have patterns. if let FnKind::Fn(ctxt, _, sig, _, None) = fk { Self::check_decl_no_pat(&sig.decl, |span, ident, mut_ident| { diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs index 95216f1c3d787..cabe3e43b342c 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs @@ -66,7 +66,7 @@ impl<'tcx> DebugContext<'tcx> { rustc_interface::util::version_str().unwrap_or("unknown version"), cranelift_codegen::VERSION, ); - let comp_dir = tcx.sess.opts.working_dir.to_string_lossy(false).into_owned(); + let comp_dir = tcx.sess.opts.working_dir.to_string_lossy(FileNameDisplayPreference::Remapped).into_owned(); let (name, file_info) = match tcx.sess.local_crate_source_file.clone() { Some(path) => { let name = path.to_string_lossy().into_owned(); diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 4ef53663ca0d9..6c7c8cbc311f9 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -74,7 +74,7 @@ mod vtable; mod prelude { pub(crate) use std::convert::{TryFrom, TryInto}; - pub(crate) use rustc_span::Span; + pub(crate) use rustc_span::{Span, FileNameDisplayPreference}; pub(crate) use rustc_hir::def_id::{DefId, LOCAL_CRATE}; pub(crate) use rustc_middle::bug; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 8ff2f1cc6520f..346c51c5426d8 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -35,6 +35,7 @@ use rustc_middle::ty::{self, AdtKind, GeneratorSubsts, ParamEnv, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::config::{self, DebugInfo}; use rustc_span::symbol::{Interner, Symbol}; +use rustc_span::FileNameDisplayPreference; use rustc_span::{self, SourceFile, SourceFileHash, Span}; use rustc_target::abi::{Abi, Align, HasDataLayout, Integer, LayoutOf, TagEncoding}; use rustc_target::abi::{Int, Pointer, F32, F64}; @@ -771,7 +772,13 @@ pub fn file_metadata(cx: &CodegenCx<'ll, '_>, source_file: &SourceFile) -> &'ll let hash = Some(&source_file.src_hash); let file_name = Some(source_file.name.prefer_remapped().to_string()); let directory = if source_file.is_real_file() && !source_file.is_imported() { - Some(cx.sess().opts.working_dir.to_string_lossy(false).to_string()) + Some( + cx.sess() + .opts + .working_dir + .to_string_lossy(FileNameDisplayPreference::Remapped) + .to_string(), + ) } else { // If the path comes from an upstream crate we assume it has been made // independent of the compiler's working directory one way or another. @@ -999,7 +1006,7 @@ pub fn compile_unit_metadata( let producer = format!("clang LLVM ({})", rustc_producer); let name_in_debuginfo = name_in_debuginfo.to_string_lossy(); - let work_dir = tcx.sess.opts.working_dir.to_string_lossy(false); + let work_dir = tcx.sess.opts.working_dir.to_string_lossy(FileNameDisplayPreference::Remapped); let flags = "\0"; let output_filenames = tcx.output_filenames(()); let out_dir = &output_filenames.out_directory; diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index f5463bca3384c..173b91e7903de 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -36,6 +36,7 @@ use regex::Regex; use tempfile::Builder as TempFileBuilder; use std::ffi::OsString; +use std::lazy::OnceCell; use std::path::{Path, PathBuf}; use std::process::{ExitStatus, Output, Stdio}; use std::{ascii, char, env, fmt, fs, io, mem, str}; @@ -254,6 +255,19 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>( // metadata of the rlib we're generating somehow. for lib in codegen_results.crate_info.used_libraries.iter() { match lib.kind { + NativeLibKind::Static { bundle: None | Some(true), whole_archive: Some(true) } + if flavor == RlibFlavor::Normal => + { + // Don't allow mixing +bundle with +whole_archive since an rlib may contain + // multiple native libs, some of which are +whole-archive and some of which are + // -whole-archive and it isn't clear how we can currently handle such a + // situation correctly. + // See https://github.com/rust-lang/rust/issues/88085#issuecomment-901050897 + sess.err( + "the linking modifiers `+bundle` and `+whole-archive` are not compatible \ + with each other when generating rlibs", + ); + } NativeLibKind::Static { bundle: None | Some(true), .. } => {} NativeLibKind::Static { bundle: Some(false), .. } | NativeLibKind::Dylib { .. } @@ -1222,6 +1236,7 @@ pub fn archive_search_paths(sess: &Session) -> Vec { sess.target_filesearch(PathKind::Native).search_path_dirs() } +#[derive(PartialEq)] enum RlibFlavor { Normal, StaticlibBase, @@ -2001,7 +2016,7 @@ fn add_local_native_libraries( let relevant_libs = codegen_results.crate_info.used_libraries.iter().filter(|l| relevant_lib(sess, l)); - let search_path = archive_search_paths(sess); + let search_path = OnceCell::new(); let mut last = (NativeLibKind::Unspecified, None); for lib in relevant_libs { let name = match lib.name { @@ -2023,7 +2038,11 @@ fn add_local_native_libraries( } NativeLibKind::Static { bundle: None | Some(true), .. } | NativeLibKind::Static { whole_archive: Some(true), .. } => { - cmd.link_whole_staticlib(name, verbatim, &search_path); + cmd.link_whole_staticlib( + name, + verbatim, + &search_path.get_or_init(|| archive_search_paths(sess)), + ); } NativeLibKind::Static { .. } => cmd.link_staticlib(name, verbatim), NativeLibKind::RawDylib => { @@ -2116,6 +2135,7 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( } let mut compiler_builtins = None; + let search_path = OnceCell::new(); for &cnum in deps.iter() { if group_start == Some(cnum) { @@ -2149,16 +2169,35 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( // external build system already has the native dependencies defined, and it // will provide them to the linker itself. if sess.opts.debugging_opts.link_native_libraries { - // Skip if this library is the same as the last. let mut last = None; for lib in &codegen_results.crate_info.native_libraries[&cnum] { - if lib.name.is_some() - && relevant_lib(sess, lib) - && matches!(lib.kind, NativeLibKind::Static { bundle: Some(false), .. }) - && last != lib.name - { - cmd.link_staticlib(lib.name.unwrap(), lib.verbatim.unwrap_or(false)); - last = lib.name; + if !relevant_lib(sess, lib) { + // Skip libraries if they are disabled by `#[link(cfg=...)]` + continue; + } + + // Skip if this library is the same as the last. + if last == lib.name { + continue; + } + + if let Some(static_lib_name) = lib.name { + if let NativeLibKind::Static { bundle: Some(false), whole_archive } = + lib.kind + { + let verbatim = lib.verbatim.unwrap_or(false); + if whole_archive == Some(true) { + cmd.link_whole_staticlib( + static_lib_name, + verbatim, + search_path.get_or_init(|| archive_search_paths(sess)), + ); + } else { + cmd.link_staticlib(static_lib_name, verbatim); + } + + last = lib.name; + } } } } diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index b6ee70c419b16..634286770d1f9 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -3,6 +3,7 @@ #![feature(box_patterns)] #![feature(try_blocks)] #![feature(in_band_lifetimes)] +#![feature(once_cell)] #![feature(nll)] #![feature(associated_type_bounds)] #![recursion_limit = "256"] diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 2253007ce3027..1eb497460e63c 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -126,7 +126,7 @@ impl AnnotateSnippetEmitterWriter { } // owned: line source, line index, annotations type Owned = (String, usize, Vec); - let filename = primary_lo.file.name.prefer_local(); + let filename = source_map.filename_for_diagnostics(&primary_lo.file.name); let origin = filename.to_string_lossy(); let annotated_files: Vec = annotated_files .into_iter() diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 25777f4133b94..29f352ae58559 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -1320,7 +1320,7 @@ impl EmitterWriter { buffer_msg_line_offset, &format!( "{}:{}:{}", - loc.file.name.prefer_local(), + sm.filename_for_diagnostics(&loc.file.name), sm.doctest_offset_line(&loc.file.name, loc.line), loc.col.0 + 1, ), @@ -1334,7 +1334,7 @@ impl EmitterWriter { 0, &format!( "{}:{}:{}: ", - loc.file.name.prefer_local(), + sm.filename_for_diagnostics(&loc.file.name), sm.doctest_offset_line(&loc.file.name, loc.line), loc.col.0 + 1, ), @@ -1362,12 +1362,12 @@ impl EmitterWriter { }; format!( "{}:{}{}", - annotated_file.file.name.prefer_local(), + sm.filename_for_diagnostics(&annotated_file.file.name), sm.doctest_offset_line(&annotated_file.file.name, first_line.line_index), col ) } else { - format!("{}", annotated_file.file.name.prefer_local()) + format!("{}", sm.filename_for_diagnostics(&annotated_file.file.name)) }; buffer.append(buffer_msg_line_offset + 1, &loc, Style::LineAndColumn); for _ in 0..max_line_num_len { diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 1b6cd04cca642..dde978cd8c6ce 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -464,7 +464,7 @@ impl DiagnosticSpan { }); DiagnosticSpan { - file_name: start.file.name.prefer_local().to_string(), + file_name: je.sm.filename_for_diagnostics(&start.file.name).to_string(), byte_start: start.file.original_relative_byte_pos(span.lo()).0, byte_end: start.file.original_relative_byte_pos(span.hi()).0, line_start: start.line, diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index a4b7bdd9155cc..1fd48309af976 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1113,7 +1113,7 @@ impl<'a> ExtCtxt<'a> { span, &format!( "cannot resolve relative path in non-file source `{}`", - other.prefer_local() + self.source_map().filename_for_diagnostics(&other) ), )); } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index d54933841fd4e..32150c7f4c615 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1630,14 +1630,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { (TypeError::Sorts(values), extra) => { let sort_string = |ty: Ty<'tcx>| match (extra, ty.kind()) { (true, ty::Opaque(def_id, _)) => { - let pos = self - .tcx - .sess - .source_map() - .lookup_char_pos(self.tcx.def_span(*def_id).lo()); + let sm = self.tcx.sess.source_map(); + let pos = sm.lookup_char_pos(self.tcx.def_span(*def_id).lo()); format!( " (opaque type at <{}:{}:{}>)", - pos.file.name.prefer_local(), + sm.filename_for_diagnostics(&pos.file.name), pos.line, pos.col.to_usize() + 1, ) diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index f04ac8dd9426f..7a42e8c1037ba 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -461,13 +461,16 @@ trait UnusedDelimLint { let lhs_needs_parens = { let mut innermost = inner; loop { - if let ExprKind::Binary(_, lhs, _rhs) = &innermost.kind { - innermost = lhs; - if !classify::expr_requires_semi_to_be_stmt(innermost) { - break true; - } - } else { - break false; + innermost = match &innermost.kind { + ExprKind::Binary(_, lhs, _rhs) => lhs, + ExprKind::Call(fn_, _params) => fn_, + ExprKind::Cast(expr, _ty) => expr, + ExprKind::Type(expr, _ty) => expr, + ExprKind::Index(base, _subscript) => base, + _ => break false, + }; + if !classify::expr_requires_semi_to_be_stmt(innermost) { + break true; } } }; diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 346a9e8021731..83f6e79d5fcf6 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -411,8 +411,7 @@ impl<'tcx> Body<'tcx> { /// Returns an iterator over all function arguments. #[inline] pub fn args_iter(&self) -> impl Iterator + ExactSizeIterator { - let arg_count = self.arg_count; - (1..arg_count + 1).map(Local::new) + (1..self.arg_count + 1).map(Local::new) } /// Returns an iterator over all user-defined variables and compiler-generated temporaries (all @@ -421,9 +420,7 @@ impl<'tcx> Body<'tcx> { pub fn vars_and_temps_iter( &self, ) -> impl DoubleEndedIterator + ExactSizeIterator { - let arg_count = self.arg_count; - let local_count = self.local_decls.len(); - (arg_count + 1..local_count).map(Local::new) + (self.arg_count + 1..self.local_decls.len()).map(Local::new) } #[inline] diff --git a/compiler/rustc_mir/src/interpret/eval_context.rs b/compiler/rustc_mir/src/interpret/eval_context.rs index bfb3de04c59fb..c6003d87f3ac6 100644 --- a/compiler/rustc_mir/src/interpret/eval_context.rs +++ b/compiler/rustc_mir/src/interpret/eval_context.rs @@ -272,11 +272,12 @@ impl<'tcx> fmt::Display for FrameInfo<'tcx> { write!(f, "inside `{}`", self.instance)?; } if !self.span.is_dummy() { - let lo = tcx.sess.source_map().lookup_char_pos(self.span.lo()); + let sm = tcx.sess.source_map(); + let lo = sm.lookup_char_pos(self.span.lo()); write!( f, " at {}:{}:{}", - lo.file.name.prefer_local(), + sm.filename_for_diagnostics(&lo.file.name), lo.line, lo.col.to_usize() + 1 )?; diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 73e05a35277ec..bf76dedd252ca 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -190,7 +190,7 @@ pub fn maybe_file_to_stream( let src = source_file.src.as_ref().unwrap_or_else(|| { sess.span_diagnostic.bug(&format!( "cannot lex `source_file` without source: {}", - source_file.name.prefer_local() + sess.source_map().filename_for_diagnostics(&source_file.name) )); }); diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 10e9bde0d972b..b4cc8b2b4a3ed 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -260,11 +260,12 @@ impl RealFileName { } } - pub fn to_string_lossy(&self, prefer_local: bool) -> Cow<'_, str> { - if prefer_local { - self.local_path_if_available().to_string_lossy() - } else { - self.remapped_path_if_available().to_string_lossy() + pub fn to_string_lossy(&self, display_pref: FileNameDisplayPreference) -> Cow<'_, str> { + match display_pref { + FileNameDisplayPreference::Local => self.local_path_if_available().to_string_lossy(), + FileNameDisplayPreference::Remapped => { + self.remapped_path_if_available().to_string_lossy() + } } } } @@ -300,9 +301,15 @@ impl From for FileName { } } +#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)] +pub enum FileNameDisplayPreference { + Remapped, + Local, +} + pub struct FileNameDisplay<'a> { inner: &'a FileName, - prefer_local: bool, + display_pref: FileNameDisplayPreference, } impl fmt::Display for FileNameDisplay<'_> { @@ -310,7 +317,7 @@ impl fmt::Display for FileNameDisplay<'_> { use FileName::*; match *self.inner { Real(ref name) => { - write!(fmt, "{}", name.to_string_lossy(self.prefer_local)) + write!(fmt, "{}", name.to_string_lossy(self.display_pref)) } QuoteExpansion(_) => write!(fmt, ""), MacroExpansion(_) => write!(fmt, ""), @@ -328,7 +335,7 @@ impl fmt::Display for FileNameDisplay<'_> { impl FileNameDisplay<'_> { pub fn to_string_lossy(&self) -> Cow<'_, str> { match self.inner { - FileName::Real(ref inner) => inner.to_string_lossy(self.prefer_local), + FileName::Real(ref inner) => inner.to_string_lossy(self.display_pref), _ => Cow::from(format!("{}", self)), } } @@ -352,13 +359,17 @@ impl FileName { } pub fn prefer_remapped(&self) -> FileNameDisplay<'_> { - FileNameDisplay { inner: self, prefer_local: false } + FileNameDisplay { inner: self, display_pref: FileNameDisplayPreference::Remapped } } // This may include transient local filesystem information. // Must not be embedded in build outputs. pub fn prefer_local(&self) -> FileNameDisplay<'_> { - FileNameDisplay { inner: self, prefer_local: true } + FileNameDisplay { inner: self, display_pref: FileNameDisplayPreference::Local } + } + + pub fn display(&self, display_pref: FileNameDisplayPreference) -> FileNameDisplay<'_> { + FileNameDisplay { inner: self, display_pref } } pub fn macro_expansion_source_code(src: &str) -> FileName { diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index c2de5ed2f5838..9b8c8594ee843 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -427,7 +427,7 @@ impl SourceMap { } } - fn span_to_string(&self, sp: Span, prefer_local: bool) -> String { + fn span_to_string(&self, sp: Span, filename_display_pref: FileNameDisplayPreference) -> String { if self.files.borrow().source_files.is_empty() || sp.is_dummy() { return "no-location".to_string(); } @@ -436,7 +436,7 @@ impl SourceMap { let hi = self.lookup_char_pos(sp.hi()); format!( "{}:{}:{}: {}:{}", - if prefer_local { lo.file.name.prefer_local() } else { lo.file.name.prefer_remapped() }, + lo.file.name.display(filename_display_pref), lo.line, lo.col.to_usize() + 1, hi.line, @@ -446,20 +446,24 @@ impl SourceMap { /// Format the span location suitable for embedding in build artifacts pub fn span_to_embeddable_string(&self, sp: Span) -> String { - self.span_to_string(sp, false) + self.span_to_string(sp, FileNameDisplayPreference::Remapped) } /// Format the span location to be printed in diagnostics. Must not be emitted /// to build artifacts as this may leak local file paths. Use span_to_embeddable_string /// for string suitable for embedding. pub fn span_to_diagnostic_string(&self, sp: Span) -> String { - self.span_to_string(sp, true) + self.span_to_string(sp, self.path_mapping.filename_display_for_diagnostics) } pub fn span_to_filename(&self, sp: Span) -> FileName { self.lookup_char_pos(sp.lo()).file.name.clone() } + pub fn filename_for_diagnostics<'a>(&self, filename: &'a FileName) -> FileNameDisplay<'a> { + filename.display(self.path_mapping.filename_display_for_diagnostics) + } + pub fn is_multiline(&self, sp: Span) -> bool { let lo = self.lookup_source_file_idx(sp.lo()); let hi = self.lookup_source_file_idx(sp.hi()); @@ -1013,15 +1017,22 @@ impl SourceMap { #[derive(Clone)] pub struct FilePathMapping { mapping: Vec<(PathBuf, PathBuf)>, + filename_display_for_diagnostics: FileNameDisplayPreference, } impl FilePathMapping { pub fn empty() -> FilePathMapping { - FilePathMapping { mapping: vec![] } + FilePathMapping::new(Vec::new()) } pub fn new(mapping: Vec<(PathBuf, PathBuf)>) -> FilePathMapping { - FilePathMapping { mapping } + let filename_display_for_diagnostics = if mapping.is_empty() { + FileNameDisplayPreference::Local + } else { + FileNameDisplayPreference::Remapped + }; + + FilePathMapping { mapping, filename_display_for_diagnostics } } /// Applies any path prefix substitution as defined by the mapping. diff --git a/compiler/rustc_target/src/spec/abi.rs b/compiler/rustc_target/src/spec/abi.rs index ee36090c54951..e3a2226eb9d15 100644 --- a/compiler/rustc_target/src/spec/abi.rs +++ b/compiler/rustc_target/src/spec/abi.rs @@ -87,6 +87,9 @@ pub fn all_names() -> Vec<&'static str> { } impl Abi { + /// Default ABI chosen for `extern fn` declarations without an explicit ABI. + pub const FALLBACK: Abi = Abi::C { unwind: false }; + #[inline] pub fn index(self) -> usize { // N.B., this ordering MUST match the AbiDatas array above. diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index a25d0f8064404..702f69a9fcf0b 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -47,7 +47,7 @@ use rustc_middle::ty::{ }; use rustc_session::lint; use rustc_span::sym; -use rustc_span::{BytePos, MultiSpan, Pos, Span, Symbol, DUMMY_SP}; +use rustc_span::{BytePos, MultiSpan, Pos, Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_data_structures::stable_map::FxHashMap; @@ -680,15 +680,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { migrated_variables_concat ); - // If the body was entirely expanded from a macro - // invocation, i.e. the body is not contained inside the - // closure span, then we walk up the expansion until we - // find the span before the expansion. - let closure_body_span = self.tcx.hir().span(body_id.hir_id) - .find_ancestor_inside(closure_span) - .unwrap_or(DUMMY_SP); + let mut closure_body_span = { + // If the body was entirely expanded from a macro + // invocation, i.e. the body is not contained inside the + // closure span, then we walk up the expansion until we + // find the span before the expansion. + let s = self.tcx.hir().span(body_id.hir_id); + s.find_ancestor_inside(closure_span).unwrap_or(s) + }; + + if let Ok(mut s) = self.tcx.sess.source_map().span_to_snippet(closure_body_span) { + if s.starts_with('$') { + // Looks like a macro fragment. Try to find the real block. + if let Some(hir::Node::Expr(&hir::Expr { + kind: hir::ExprKind::Block(block, ..), .. + })) = self.tcx.hir().find(body_id.hir_id) { + // If the body is a block (with `{..}`), we use the span of that block. + // E.g. with a `|| $body` expanded from a `m!({ .. })`, we use `{ .. }`, and not `$body`. + // Since we know it's a block, we know we can insert the `let _ = ..` without + // breaking the macro syntax. + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(block.span) { + closure_body_span = block.span; + s = snippet; + } + } + } - if let Ok(s) = self.tcx.sess.source_map().span_to_snippet(closure_body_span) { let mut lines = s.lines(); let line1 = lines.next().unwrap_or_default(); diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 7aa24ff4afaee..9d45c5082db43 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -300,7 +300,10 @@ impl LinkedList { let tail = self.tail.take(); let len = mem::replace(&mut self.len, 0); if let Some(head) = head { - let tail = tail.unwrap_or_else(|| unsafe { core::hint::unreachable_unchecked() }); + // SAFETY: In a LinkedList, either both the head and tail are None because + // the list is empty, or both head and tail are Some because the list is populated. + // Since we have verified the head is Some, we are sure the tail is Some too. + let tail = unsafe { tail.unwrap_unchecked() }; Some((head, tail, len)) } else { None diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 3c638e655dc91..70cccd31b92de 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -459,11 +459,8 @@ where debug_assert!(N <= iter.size_hint().1.unwrap_or(usize::MAX)); debug_assert!(N <= iter.size_hint().0); - match collect_into_array(iter) { - Some(array) => array, - // SAFETY: covered by the function contract. - None => unsafe { crate::hint::unreachable_unchecked() }, - } + // SAFETY: covered by the function contract. + unsafe { collect_into_array(iter).unwrap_unchecked() } } /// Pulls `N` items from `iter` and returns them as an array. If the iterator diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 47865240f6a6f..9d5e03dd0de79 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1198,11 +1198,8 @@ impl Option { pub fn insert(&mut self, value: T) -> &mut T { *self = Some(value); - match self { - Some(v) => v, - // SAFETY: the code above just filled the option - None => unsafe { hint::unreachable_unchecked() }, - } + // SAFETY: the code above just filled the option + unsafe { self.as_mut().unwrap_unchecked() } } /// Inserts `value` into the option if it is [`None`], then diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 947afbdc68dfb..ca4e2e6b7f351 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -69,7 +69,7 @@ pub use iter::SplitAsciiWhitespace; pub use iter::SplitInclusive; #[unstable(feature = "str_internals", issue = "none")] -pub use validations::next_code_point; +pub use validations::{next_code_point, utf8_char_width}; use iter::MatchIndicesInternal; use iter::SplitInternal; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 43c7ec5fad33a..3a1eb625b57c3 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -253,6 +253,7 @@ #![feature(const_ip)] #![feature(const_ipv4)] #![feature(const_ipv6)] +#![feature(const_option)] #![feature(const_raw_ptr_deref)] #![feature(const_socketaddr)] #![feature(const_trait_impl)] diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs index f1264130faf7a..3919025b08006 100644 --- a/library/std/src/sys/windows/args.rs +++ b/library/std/src/sys/windows/args.rs @@ -1,13 +1,18 @@ -#![allow(dead_code)] // runtime init functions not used during testing +//! The Windows command line is just a string +//! +//! +//! This module implements the parsing necessary to turn that string into a list of arguments. #[cfg(test)] mod tests; use crate::ffi::OsString; use crate::fmt; +use crate::marker::PhantomData; +use crate::num::NonZeroU16; use crate::os::windows::prelude::*; use crate::path::PathBuf; -use crate::slice; +use crate::ptr::NonNull; use crate::sys::c; use crate::sys::windows::os::current_exe; use crate::vec; @@ -15,9 +20,11 @@ use crate::vec; use core::iter; pub fn args() -> Args { + // SAFETY: `GetCommandLineW` returns a pointer to a null terminated UTF-16 + // string so it's safe for `WStrUnits` to use. unsafe { let lp_cmd_line = c::GetCommandLineW(); - let parsed_args_list = parse_lp_cmd_line(lp_cmd_line as *const u16, || { + let parsed_args_list = parse_lp_cmd_line(WStrUnits::new(lp_cmd_line), || { current_exe().map(PathBuf::into_os_string).unwrap_or_else(|_| OsString::new()) }); @@ -28,129 +35,120 @@ pub fn args() -> Args { /// Implements the Windows command-line argument parsing algorithm. /// /// Microsoft's documentation for the Windows CLI argument format can be found at -/// . +/// /// -/// Windows includes a function to do this in shell32.dll, -/// but linking with that DLL causes the process to be registered as a GUI application. +/// A more in-depth explanation is here: +/// +/// +/// Windows includes a function to do command line parsing in shell32.dll. +/// However, this is not used for two reasons: +/// +/// 1. Linking with that DLL causes the process to be registered as a GUI application. /// GUI applications add a bunch of overhead, even if no windows are drawn. See /// . /// -/// This function was tested for equivalence to the shell32.dll implementation in -/// Windows 10 Pro v1803, using an exhaustive test suite available at -/// or -/// . -unsafe fn parse_lp_cmd_line OsString>( - lp_cmd_line: *const u16, +/// 2. It does not follow the modern C/C++ argv rules outlined in the first two links above. +/// +/// This function was tested for equivalence to the C/C++ parsing rules using an +/// extensive test suite available at +/// . +fn parse_lp_cmd_line<'a, F: Fn() -> OsString>( + lp_cmd_line: Option>, exe_name: F, ) -> Vec { - const BACKSLASH: u16 = '\\' as u16; - const QUOTE: u16 = '"' as u16; - const TAB: u16 = '\t' as u16; - const SPACE: u16 = ' ' as u16; + const BACKSLASH: NonZeroU16 = NonZeroU16::new(b'\\' as u16).unwrap(); + const QUOTE: NonZeroU16 = NonZeroU16::new(b'"' as u16).unwrap(); + const TAB: NonZeroU16 = NonZeroU16::new(b'\t' as u16).unwrap(); + const SPACE: NonZeroU16 = NonZeroU16::new(b' ' as u16).unwrap(); + let mut ret_val = Vec::new(); - if lp_cmd_line.is_null() || *lp_cmd_line == 0 { + // If the cmd line pointer is null or it points to an empty string then + // return the name of the executable as argv[0]. + if lp_cmd_line.as_ref().and_then(|cmd| cmd.peek()).is_none() { ret_val.push(exe_name()); return ret_val; } - let mut cmd_line = { - let mut end = 0; - while *lp_cmd_line.offset(end) != 0 { - end += 1; - } - slice::from_raw_parts(lp_cmd_line, end as usize) - }; + let mut code_units = lp_cmd_line.unwrap(); + // The executable name at the beginning is special. - cmd_line = match cmd_line[0] { - // The executable name ends at the next quote mark, - // no matter what. - QUOTE => { - let args = { - let mut cut = cmd_line[1..].splitn(2, |&c| c == QUOTE); - if let Some(exe) = cut.next() { - ret_val.push(OsString::from_wide(exe)); - } - cut.next() - }; - if let Some(args) = args { - args - } else { - return ret_val; - } - } - // Implement quirk: when they say whitespace here, - // they include the entire ASCII control plane: - // "However, if lpCmdLine starts with any amount of whitespace, CommandLineToArgvW - // will consider the first argument to be an empty string. Excess whitespace at the - // end of lpCmdLine is ignored." - 0..=SPACE => { - ret_val.push(OsString::new()); - &cmd_line[1..] - } - // The executable name ends at the next whitespace, - // no matter what. - _ => { - let args = { - let mut cut = cmd_line.splitn(2, |&c| c > 0 && c <= SPACE); - if let Some(exe) = cut.next() { - ret_val.push(OsString::from_wide(exe)); - } - cut.next() - }; - if let Some(args) = args { - args - } else { - return ret_val; - } + let mut in_quotes = false; + let mut cur = Vec::new(); + for w in &mut code_units { + match w { + // A quote mark always toggles `in_quotes` no matter what because + // there are no escape characters when parsing the executable name. + QUOTE => in_quotes = !in_quotes, + // If not `in_quotes` then whitespace ends argv[0]. + SPACE | TAB if !in_quotes => break, + // In all other cases the code unit is taken literally. + _ => cur.push(w.get()), } - }; + } + // Skip whitespace. + code_units.advance_while(|w| w == SPACE || w == TAB); + ret_val.push(OsString::from_wide(&cur)); + + // Parse the arguments according to these rules: + // * All code units are taken literally except space, tab, quote and backslash. + // * When not `in_quotes`, space and tab separate arguments. Consecutive spaces and tabs are + // treated as a single separator. + // * A space or tab `in_quotes` is taken literally. + // * A quote toggles `in_quotes` mode unless it's escaped. An escaped quote is taken literally. + // * A quote can be escaped if preceded by an odd number of backslashes. + // * If any number of backslashes is immediately followed by a quote then the number of + // backslashes is halved (rounding down). + // * Backslashes not followed by a quote are all taken literally. + // * If `in_quotes` then a quote can also be escaped using another quote + // (i.e. two consecutive quotes become one literal quote). let mut cur = Vec::new(); let mut in_quotes = false; - let mut was_in_quotes = false; - let mut backslash_count: usize = 0; - for &c in cmd_line { - match c { - // backslash - BACKSLASH => { - backslash_count += 1; - was_in_quotes = false; + while let Some(w) = code_units.next() { + match w { + // If not `in_quotes`, a space or tab ends the argument. + SPACE | TAB if !in_quotes => { + ret_val.push(OsString::from_wide(&cur[..])); + cur.truncate(0); + + // Skip whitespace. + code_units.advance_while(|w| w == SPACE || w == TAB); } - QUOTE if backslash_count % 2 == 0 => { - cur.extend(iter::repeat(b'\\' as u16).take(backslash_count / 2)); - backslash_count = 0; - if was_in_quotes { - cur.push('"' as u16); - was_in_quotes = false; + // Backslashes can escape quotes or backslashes but only if consecutive backslashes are followed by a quote. + BACKSLASH => { + let backslash_count = code_units.advance_while(|w| w == BACKSLASH) + 1; + if code_units.peek() == Some(QUOTE) { + cur.extend(iter::repeat(BACKSLASH.get()).take(backslash_count / 2)); + // The quote is escaped if there are an odd number of backslashes. + if backslash_count % 2 == 1 { + code_units.next(); + cur.push(QUOTE.get()); + } } else { - was_in_quotes = in_quotes; - in_quotes = !in_quotes; + // If there is no quote on the end then there is no escaping. + cur.extend(iter::repeat(BACKSLASH.get()).take(backslash_count)); } } - QUOTE if backslash_count % 2 != 0 => { - cur.extend(iter::repeat(b'\\' as u16).take(backslash_count / 2)); - backslash_count = 0; - was_in_quotes = false; - cur.push(b'"' as u16); - } - SPACE | TAB if !in_quotes => { - cur.extend(iter::repeat(b'\\' as u16).take(backslash_count)); - if !cur.is_empty() || was_in_quotes { - ret_val.push(OsString::from_wide(&cur[..])); - cur.truncate(0); + // If `in_quotes` and not backslash escaped (see above) then a quote either + // unsets `in_quote` or is escaped by another quote. + QUOTE if in_quotes => match code_units.peek() { + // Two consecutive quotes when `in_quotes` produces one literal quote. + Some(QUOTE) => { + cur.push(QUOTE.get()); + code_units.next(); } - backslash_count = 0; - was_in_quotes = false; - } - _ => { - cur.extend(iter::repeat(b'\\' as u16).take(backslash_count)); - backslash_count = 0; - was_in_quotes = false; - cur.push(c); - } + // Otherwise set `in_quotes`. + Some(_) => in_quotes = false, + // The end of the command line. + // Push `cur` even if empty, which we do by breaking while `in_quotes` is still set. + None => break, + }, + // If not `in_quotes` and not BACKSLASH escaped (see above) then a quote sets `in_quote`. + QUOTE => in_quotes = true, + // Everything else is always taken literally. + _ => cur.push(w.get()), } } - cur.extend(iter::repeat(b'\\' as u16).take(backslash_count)); - // include empty quoted strings at the end of the arguments list - if !cur.is_empty() || was_in_quotes || in_quotes { + // Push the final argument, if any. + if !cur.is_empty() || in_quotes { ret_val.push(OsString::from_wide(&cur[..])); } ret_val @@ -187,3 +185,52 @@ impl ExactSizeIterator for Args { self.parsed_args_list.len() } } + +/// A safe iterator over a LPWSTR +/// (aka a pointer to a series of UTF-16 code units terminated by a NULL). +struct WStrUnits<'a> { + // The pointer must never be null... + lpwstr: NonNull, + // ...and the memory it points to must be valid for this lifetime. + lifetime: PhantomData<&'a [u16]>, +} +impl WStrUnits<'_> { + /// Create the iterator. Returns `None` if `lpwstr` is null. + /// + /// SAFETY: `lpwstr` must point to a null-terminated wide string that lives + /// at least as long as the lifetime of this struct. + unsafe fn new(lpwstr: *const u16) -> Option { + Some(Self { lpwstr: NonNull::new(lpwstr as _)?, lifetime: PhantomData }) + } + fn peek(&self) -> Option { + // SAFETY: It's always safe to read the current item because we don't + // ever move out of the array's bounds. + unsafe { NonZeroU16::new(*self.lpwstr.as_ptr()) } + } + /// Advance the iterator while `predicate` returns true. + /// Returns the number of items it advanced by. + fn advance_while bool>(&mut self, mut predicate: P) -> usize { + let mut counter = 0; + while let Some(w) = self.peek() { + if !predicate(w) { + break; + } + counter += 1; + self.next(); + } + counter + } +} +impl Iterator for WStrUnits<'_> { + // This can never return zero as that marks the end of the string. + type Item = NonZeroU16; + fn next(&mut self) -> Option { + // SAFETY: If NULL is reached we immediately return. + // Therefore it's safe to advance the pointer after that. + unsafe { + let next = self.peek()?; + self.lpwstr = NonNull::new_unchecked(self.lpwstr.as_ptr().add(1)); + Some(next) + } + } +} diff --git a/library/std/src/sys/windows/args/tests.rs b/library/std/src/sys/windows/args/tests.rs index 756a4361ea3de..82c32d08c5ea8 100644 --- a/library/std/src/sys/windows/args/tests.rs +++ b/library/std/src/sys/windows/args/tests.rs @@ -5,9 +5,9 @@ fn chk(string: &str, parts: &[&str]) { let mut wide: Vec = OsString::from(string).encode_wide().collect(); wide.push(0); let parsed = - unsafe { parse_lp_cmd_line(wide.as_ptr() as *const u16, || OsString::from("TEST.EXE")) }; + unsafe { parse_lp_cmd_line(WStrUnits::new(wide.as_ptr()), || OsString::from("TEST.EXE")) }; let expected: Vec = parts.iter().map(|k| OsString::from(k)).collect(); - assert_eq!(parsed.as_slice(), expected.as_slice()); + assert_eq!(parsed.as_slice(), expected.as_slice(), "{:?}", string); } #[test] @@ -27,35 +27,65 @@ fn single_words() { #[test] fn official_examples() { chk(r#"EXE "abc" d e"#, &["EXE", "abc", "d", "e"]); - chk(r#"EXE a\\\b d"e f"g h"#, &["EXE", r#"a\\\b"#, "de fg", "h"]); + chk(r#"EXE a\\\b d"e f"g h"#, &["EXE", r"a\\\b", "de fg", "h"]); chk(r#"EXE a\\\"b c d"#, &["EXE", r#"a\"b"#, "c", "d"]); - chk(r#"EXE a\\\\"b c" d e"#, &["EXE", r#"a\\b c"#, "d", "e"]); + chk(r#"EXE a\\\\"b c" d e"#, &["EXE", r"a\\b c", "d", "e"]); } #[test] fn whitespace_behavior() { - chk(r#" test"#, &["", "test"]); - chk(r#" test"#, &["", "test"]); - chk(r#" test test2"#, &["", "test", "test2"]); - chk(r#" test test2"#, &["", "test", "test2"]); - chk(r#"test test2 "#, &["test", "test2"]); - chk(r#"test test2 "#, &["test", "test2"]); - chk(r#"test "#, &["test"]); + chk(" test", &["", "test"]); + chk(" test", &["", "test"]); + chk(" test test2", &["", "test", "test2"]); + chk(" test test2", &["", "test", "test2"]); + chk("test test2 ", &["test", "test2"]); + chk("test test2 ", &["test", "test2"]); + chk("test ", &["test"]); } #[test] fn genius_quotes() { chk(r#"EXE "" """#, &["EXE", "", ""]); - chk(r#"EXE "" """"#, &["EXE", "", "\""]); + chk(r#"EXE "" """"#, &["EXE", "", r#"""#]); chk( r#"EXE "this is """all""" in the same argument""#, - &["EXE", "this is \"all\" in the same argument"], + &["EXE", r#"this is "all" in the same argument"#], ); - chk(r#"EXE "a"""#, &["EXE", "a\""]); - chk(r#"EXE "a"" a"#, &["EXE", "a\"", "a"]); + chk(r#"EXE "a"""#, &["EXE", r#"a""#]); + chk(r#"EXE "a"" a"#, &["EXE", r#"a" a"#]); // quotes cannot be escaped in command names chk(r#""EXE" check"#, &["EXE", "check"]); chk(r#""EXE check""#, &["EXE check"]); - chk(r#""EXE """for""" check"#, &["EXE ", r#"for""#, "check"]); - chk(r#""EXE \"for\" check"#, &[r#"EXE \"#, r#"for""#, "check"]); + chk(r#""EXE """for""" check"#, &["EXE for check"]); + chk(r#""EXE \"for\" check"#, &[r"EXE \for\ check"]); + chk(r#""EXE \" for \" check"#, &[r"EXE \", "for", r#"""#, "check"]); + chk(r#"E"X"E test"#, &["EXE", "test"]); + chk(r#"EX""E test"#, &["EXE", "test"]); +} + +// from https://daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULESEX +#[test] +fn post_2008() { + chk("EXE CallMeIshmael", &["EXE", "CallMeIshmael"]); + chk(r#"EXE "Call Me Ishmael""#, &["EXE", "Call Me Ishmael"]); + chk(r#"EXE Cal"l Me I"shmael"#, &["EXE", "Call Me Ishmael"]); + chk(r#"EXE CallMe\"Ishmael"#, &["EXE", r#"CallMe"Ishmael"#]); + chk(r#"EXE "CallMe\"Ishmael""#, &["EXE", r#"CallMe"Ishmael"#]); + chk(r#"EXE "Call Me Ishmael\\""#, &["EXE", r"Call Me Ishmael\"]); + chk(r#"EXE "CallMe\\\"Ishmael""#, &["EXE", r#"CallMe\"Ishmael"#]); + chk(r#"EXE a\\\b"#, &["EXE", r"a\\\b"]); + chk(r#"EXE "a\\\b""#, &["EXE", r"a\\\b"]); + chk(r#"EXE "\"Call Me Ishmael\"""#, &["EXE", r#""Call Me Ishmael""#]); + chk(r#"EXE "C:\TEST A\\""#, &["EXE", r"C:\TEST A\"]); + chk(r#"EXE "\"C:\TEST A\\\"""#, &["EXE", r#""C:\TEST A\""#]); + chk(r#"EXE "a b c" d e"#, &["EXE", "a b c", "d", "e"]); + chk(r#"EXE "ab\"c" "\\" d"#, &["EXE", r#"ab"c"#, r"\", "d"]); + chk(r#"EXE a\\\b d"e f"g h"#, &["EXE", r"a\\\b", "de fg", "h"]); + chk(r#"EXE a\\\"b c d"#, &["EXE", r#"a\"b"#, "c", "d"]); + chk(r#"EXE a\\\\"b c" d e"#, &["EXE", r"a\\b c", "d", "e"]); + // Double Double Quotes + chk(r#"EXE "a b c"""#, &["EXE", r#"a b c""#]); + chk(r#"EXE """CallMeIshmael""" b c"#, &["EXE", r#""CallMeIshmael""#, "b", "c"]); + chk(r#"EXE """Call Me Ishmael""""#, &["EXE", r#""Call Me Ishmael""#]); + chk(r#"EXE """"Call Me Ishmael"" b c"#, &["EXE", r#""Call"#, "Me", "Ishmael", "b", "c"]); } diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index cedf389fbf503..6fb850d182889 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -789,7 +789,7 @@ extern "system" { pub fn RemoveDirectoryW(lpPathName: LPCWSTR) -> BOOL; pub fn SetFileAttributesW(lpFileName: LPCWSTR, dwFileAttributes: DWORD) -> BOOL; pub fn SetLastError(dwErrCode: DWORD); - pub fn GetCommandLineW() -> *mut LPCWSTR; + pub fn GetCommandLineW() -> LPWSTR; pub fn GetTempPathW(nBufferLength: DWORD, lpBuffer: LPCWSTR) -> DWORD; pub fn GetCurrentProcess() -> HANDLE; pub fn GetCurrentThread() -> HANDLE; diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs index 6f2618c63b5d5..2719a530dfd41 100644 --- a/library/std/src/sys/windows/stdio.rs +++ b/library/std/src/sys/windows/stdio.rs @@ -9,14 +9,25 @@ use crate::str; use crate::sys::c; use crate::sys::cvt; use crate::sys::handle::Handle; +use core::str::utf8_char_width; // Don't cache handles but get them fresh for every read/write. This allows us to track changes to // the value over time (such as if a process calls `SetStdHandle` while it's running). See #40490. pub struct Stdin { surrogate: u16, } -pub struct Stdout; -pub struct Stderr; +pub struct Stdout { + incomplete_utf8: IncompleteUtf8, +} + +pub struct Stderr { + incomplete_utf8: IncompleteUtf8, +} + +struct IncompleteUtf8 { + bytes: [u8; 4], + len: u8, +} // Apparently Windows doesn't handle large reads on stdin or writes to stdout/stderr well (see // #13304 for details). @@ -51,7 +62,15 @@ fn is_console(handle: c::HANDLE) -> bool { unsafe { c::GetConsoleMode(handle, &mut mode) != 0 } } -fn write(handle_id: c::DWORD, data: &[u8]) -> io::Result { +fn write( + handle_id: c::DWORD, + data: &[u8], + incomplete_utf8: &mut IncompleteUtf8, +) -> io::Result { + if data.is_empty() { + return Ok(0); + } + let handle = get_handle(handle_id)?; if !is_console(handle) { unsafe { @@ -62,22 +81,73 @@ fn write(handle_id: c::DWORD, data: &[u8]) -> io::Result { } } - // As the console is meant for presenting text, we assume bytes of `data` come from a string - // and are encoded as UTF-8, which needs to be encoded as UTF-16. + if incomplete_utf8.len > 0 { + assert!( + incomplete_utf8.len < 4, + "Unexpected number of bytes for incomplete UTF-8 codepoint." + ); + if data[0] >> 6 != 0b10 { + // not a continuation byte - reject + incomplete_utf8.len = 0; + return Err(io::Error::new_const( + io::ErrorKind::InvalidData, + &"Windows stdio in console mode does not support writing non-UTF-8 byte sequences", + )); + } + incomplete_utf8.bytes[incomplete_utf8.len as usize] = data[0]; + incomplete_utf8.len += 1; + let char_width = utf8_char_width(incomplete_utf8.bytes[0]); + if (incomplete_utf8.len as usize) < char_width { + // more bytes needed + return Ok(1); + } + let s = str::from_utf8(&incomplete_utf8.bytes[0..incomplete_utf8.len as usize]); + incomplete_utf8.len = 0; + match s { + Ok(s) => { + assert_eq!(char_width, s.len()); + let written = write_valid_utf8_to_console(handle, s)?; + assert_eq!(written, s.len()); // guaranteed by write_valid_utf8_to_console() for single codepoint writes + return Ok(1); + } + Err(_) => { + return Err(io::Error::new_const( + io::ErrorKind::InvalidData, + &"Windows stdio in console mode does not support writing non-UTF-8 byte sequences", + )); + } + } + } + + // As the console is meant for presenting text, we assume bytes of `data` are encoded as UTF-8, + // which needs to be encoded as UTF-16. // // If the data is not valid UTF-8 we write out as many bytes as are valid. - // Only when there are no valid bytes (which will happen on the next call), return an error. + // If the first byte is invalid it is either first byte of a multi-byte sequence but the + // provided byte slice is too short or it is the first byte of an invalide multi-byte sequence. let len = cmp::min(data.len(), MAX_BUFFER_SIZE / 2); let utf8 = match str::from_utf8(&data[..len]) { Ok(s) => s, Err(ref e) if e.valid_up_to() == 0 => { - return Err(io::Error::new_const( - io::ErrorKind::InvalidData, - &"Windows stdio in console mode does not support writing non-UTF-8 byte sequences", - )); + let first_byte_char_width = utf8_char_width(data[0]); + if first_byte_char_width > 1 && data.len() < first_byte_char_width { + incomplete_utf8.bytes[0] = data[0]; + incomplete_utf8.len = 1; + return Ok(1); + } else { + return Err(io::Error::new_const( + io::ErrorKind::InvalidData, + &"Windows stdio in console mode does not support writing non-UTF-8 byte sequences", + )); + } } Err(e) => str::from_utf8(&data[..e.valid_up_to()]).unwrap(), }; + + write_valid_utf8_to_console(handle, utf8) +} + +fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result { let mut utf16 = [0u16; MAX_BUFFER_SIZE / 2]; let mut len_utf16 = 0; for (chr, dest) in utf8.encode_utf16().zip(utf16.iter_mut()) { @@ -259,15 +329,21 @@ fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result { Ok(written) } +impl IncompleteUtf8 { + pub const fn new() -> IncompleteUtf8 { + IncompleteUtf8 { bytes: [0; 4], len: 0 } + } +} + impl Stdout { pub const fn new() -> Stdout { - Stdout + Stdout { incomplete_utf8: IncompleteUtf8::new() } } } impl io::Write for Stdout { fn write(&mut self, buf: &[u8]) -> io::Result { - write(c::STD_OUTPUT_HANDLE, buf) + write(c::STD_OUTPUT_HANDLE, buf, &mut self.incomplete_utf8) } fn flush(&mut self) -> io::Result<()> { @@ -277,13 +353,13 @@ impl io::Write for Stdout { impl Stderr { pub const fn new() -> Stderr { - Stderr + Stderr { incomplete_utf8: IncompleteUtf8::new() } } } impl io::Write for Stderr { fn write(&mut self, buf: &[u8]) -> io::Result { - write(c::STD_ERROR_HANDLE, buf) + write(c::STD_ERROR_HANDLE, buf, &mut self.incomplete_utf8) } fn flush(&mut self) -> io::Result<()> { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 620b4cdf9da49..172fe5d164b7a 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -710,11 +710,15 @@ fn render_impls( containing_item, assoc_link, RenderMode::Normal, - true, None, - false, - true, &[], + ImplRenderingParameters { + show_def_docs: true, + is_on_foreign_type: false, + show_default_items: true, + show_non_assoc_items: true, + toggle_open_by_default: true, + }, ); buffer.into_inner() }) @@ -1049,11 +1053,15 @@ fn render_assoc_items( containing_item, AssocItemLink::Anchor(None), render_mode, - true, None, - false, - true, &[], + ImplRenderingParameters { + show_def_docs: true, + is_on_foreign_type: false, + show_default_items: true, + show_non_assoc_items: true, + toggle_open_by_default: true, + }, ); } } @@ -1243,6 +1251,16 @@ fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String { out.into_inner() } +#[derive(Clone, Copy, Debug)] +struct ImplRenderingParameters { + show_def_docs: bool, + is_on_foreign_type: bool, + show_default_items: bool, + /// Whether or not to show methods. + show_non_assoc_items: bool, + toggle_open_by_default: bool, +} + fn render_impl( w: &mut Buffer, cx: &Context<'_>, @@ -1250,13 +1268,9 @@ fn render_impl( parent: &clean::Item, link: AssocItemLink<'_>, render_mode: RenderMode, - show_def_docs: bool, use_absolute: Option, - is_on_foreign_type: bool, - show_default_items: bool, - // This argument is used to reference same type with different paths to avoid duplication - // in documentation pages for trait with automatic implementations like "Send" and "Sync". aliases: &[String], + rendering_params: ImplRenderingParameters, ) { let cache = cx.cache(); let traits = &cache.traits; @@ -1279,17 +1293,18 @@ fn render_impl( render_mode: RenderMode, is_default_item: bool, trait_: Option<&clean::Trait>, - show_def_docs: bool, + rendering_params: ImplRenderingParameters, ) { let item_type = item.type_(); let name = item.name.as_ref().unwrap(); - let render_method_item = match render_mode { - RenderMode::Normal => true, - RenderMode::ForDeref { mut_: deref_mut_ } => { - should_render_item(&item, deref_mut_, cx.cache()) - } - }; + let render_method_item = rendering_params.show_non_assoc_items + && match render_mode { + RenderMode::Normal => true, + RenderMode::ForDeref { mut_: deref_mut_ } => { + should_render_item(&item, deref_mut_, cx.cache()) + } + }; let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" }; @@ -1312,18 +1327,32 @@ fn render_impl( } else { // In case the item isn't documented, // provide short documentation from the trait. - document_short(&mut doc_buffer, it, cx, link, parent, show_def_docs); + document_short( + &mut doc_buffer, + it, + cx, + link, + parent, + rendering_params.show_def_docs, + ); } } } else { document_item_info(&mut info_buffer, cx, item, Some(parent)); - if show_def_docs { + if rendering_params.show_def_docs { document_full(&mut doc_buffer, item, cx); short_documented = false; } } } else { - document_short(&mut doc_buffer, item, cx, link, parent, show_def_docs); + document_short( + &mut doc_buffer, + item, + cx, + link, + parent, + rendering_params.show_def_docs, + ); } } let w = if short_documented && trait_.is_some() { interesting } else { boring }; @@ -1455,7 +1484,7 @@ fn render_impl( render_mode, false, trait_.map(|t| &t.trait_), - show_def_docs, + rendering_params, ); } @@ -1468,7 +1497,7 @@ fn render_impl( parent: &clean::Item, containing_item: &clean::Item, render_mode: RenderMode, - show_def_docs: bool, + rendering_params: ImplRenderingParameters, ) { for trait_item in &t.items { let n = trait_item.name; @@ -1490,7 +1519,7 @@ fn render_impl( render_mode, true, Some(t), - show_def_docs, + rendering_params, ); } } @@ -1499,7 +1528,7 @@ fn render_impl( // default items which weren't overridden in the implementation block. // We don't emit documentation for default items if they appear in the // Implementations on Foreign Types or Implementors sections. - if show_default_items { + if rendering_params.show_default_items { if let Some(t) = trait_ { render_default_items( &mut default_impl_items, @@ -1510,7 +1539,7 @@ fn render_impl( &i.impl_item, parent, render_mode, - show_def_docs, + rendering_params, ); } } @@ -1518,7 +1547,11 @@ fn render_impl( let toggled = !(impl_items.is_empty() && default_impl_items.is_empty()); if toggled { close_tags.insert_str(0, ""); - write!(w, "
"); + write!( + w, + "
", + if rendering_params.toggle_open_by_default { " open" } else { "" } + ); write!(w, "") } render_impl_summary( @@ -1527,9 +1560,9 @@ fn render_impl( i, parent, parent, - show_def_docs, + rendering_params.show_def_docs, use_absolute, - is_on_foreign_type, + rendering_params.is_on_foreign_type, aliases, ); if toggled { diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 722cfc97a8d89..39ef641a3ace2 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -16,8 +16,8 @@ use rustc_span::symbol::{kw, sym, Symbol}; use super::{ collect_paths_for_type, document, ensure_trailing_slash, item_ty_to_strs, notable_traits_decl, render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre, - render_impl, render_impl_summary, render_stability_since_raw, write_srclink, AssocItemLink, - Context, + render_impl, render_stability_since_raw, write_srclink, AssocItemLink, Context, + ImplRenderingParameters, }; use crate::clean::{self, GetDefId}; use crate::formats::item_type::ItemType; @@ -736,11 +736,15 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra it, assoc_link, RenderMode::Normal, - false, None, - true, - false, &[], + ImplRenderingParameters { + show_def_docs: false, + is_on_foreign_type: true, + show_default_items: false, + show_non_assoc_items: true, + toggle_open_by_default: false, + }, ); } } @@ -1395,16 +1399,22 @@ fn render_implementor( } => implementor_dups[&path.last()].1, _ => false, }; - render_impl_summary( + render_impl( w, cx, implementor, trait_, - trait_, - false, + AssocItemLink::Anchor(None), + RenderMode::Normal, Some(use_absolute), - false, aliases, + ImplRenderingParameters { + show_def_docs: false, + is_on_foreign_type: false, + show_default_items: false, + show_non_assoc_items: false, + toggle_open_by_default: false, + }, ); } diff --git a/src/test/run-make/native-link-modifier-whole-archive/Makefile b/src/test/run-make/native-link-modifier-whole-archive/Makefile new file mode 100644 index 0000000000000..3e11a965f36e9 --- /dev/null +++ b/src/test/run-make/native-link-modifier-whole-archive/Makefile @@ -0,0 +1,37 @@ +# This test case makes sure that native libraries are linked with --whole-archive semantics +# when the `-bundle,+whole-archive` modifiers are applied to them. +# +# The test works by checking that the resulting executables produce the expected output, +# part of which is emitted by otherwise unreferenced C code. If +whole-archive didn't work +# that code would never make it into the final executable and we'd thus be missing some +# of the output. + +-include ../../run-make-fulldeps/tools.mk + +all: $(TMPDIR)/$(call BIN,directly_linked) $(TMPDIR)/$(call BIN,indirectly_linked) $(TMPDIR)/$(call BIN,indirectly_linked_via_attr) + $(call RUN,directly_linked) | $(CGREP) 'static-initializer.directly_linked.' + $(call RUN,indirectly_linked) | $(CGREP) 'static-initializer.indirectly_linked.' + $(call RUN,indirectly_linked_via_attr) | $(CGREP) 'static-initializer.native_lib_in_src.' + +# Native lib linked directly into executable +$(TMPDIR)/$(call BIN,directly_linked): $(call NATIVE_STATICLIB,c_static_lib_with_constructor) + $(RUSTC) directly_linked.rs -Z unstable-options -l static:+whole-archive=c_static_lib_with_constructor + +# Native lib linked into RLIB via `-l static:-bundle,+whole-archive`, RLIB linked into executable +$(TMPDIR)/$(call BIN,indirectly_linked): $(TMPDIR)/librlib_with_cmdline_native_lib.rlib + $(RUSTC) indirectly_linked.rs + +# Native lib linked into RLIB via #[link] attribute, RLIB linked into executable +$(TMPDIR)/$(call BIN,indirectly_linked_via_attr): $(TMPDIR)/libnative_lib_in_src.rlib + $(RUSTC) indirectly_linked_via_attr.rs + +# Native lib linked into rlib with via commandline +$(TMPDIR)/librlib_with_cmdline_native_lib.rlib: $(call NATIVE_STATICLIB,c_static_lib_with_constructor) + $(RUSTC) rlib_with_cmdline_native_lib.rs -Z unstable-options --crate-type=rlib -l static:-bundle,+whole-archive=c_static_lib_with_constructor + +# Native lib linked into rlib via `#[link()]` attribute on extern block. +$(TMPDIR)/libnative_lib_in_src.rlib: $(call NATIVE_STATICLIB,c_static_lib_with_constructor) + $(RUSTC) native_lib_in_src.rs --crate-type=rlib + +$(TMPDIR)/libc_static_lib_with_constructor.o: c_static_lib_with_constructor.cpp + $(call COMPILE_OBJ_CXX,$@,$<) diff --git a/src/test/run-make/native-link-modifier-whole-archive/c_static_lib_with_constructor.cpp b/src/test/run-make/native-link-modifier-whole-archive/c_static_lib_with_constructor.cpp new file mode 100644 index 0000000000000..c687eb0f092f9 --- /dev/null +++ b/src/test/run-make/native-link-modifier-whole-archive/c_static_lib_with_constructor.cpp @@ -0,0 +1,11 @@ +#include + +// Since this is a global variable, its constructor will be called before +// main() is executed. But only if the object file containing it actually +// gets linked into the executable. +struct Foo { + Foo() { + printf("static-initializer."); + fflush(stdout); + } +} FOO; diff --git a/src/test/run-make/native-link-modifier-whole-archive/directly_linked.rs b/src/test/run-make/native-link-modifier-whole-archive/directly_linked.rs new file mode 100644 index 0000000000000..17518e8b2f963 --- /dev/null +++ b/src/test/run-make/native-link-modifier-whole-archive/directly_linked.rs @@ -0,0 +1,6 @@ +use std::io::Write; + +fn main() { + print!("directly_linked."); + std::io::stdout().flush().unwrap(); +} diff --git a/src/test/run-make/native-link-modifier-whole-archive/indirectly_linked.rs b/src/test/run-make/native-link-modifier-whole-archive/indirectly_linked.rs new file mode 100644 index 0000000000000..c8b83fcfe037d --- /dev/null +++ b/src/test/run-make/native-link-modifier-whole-archive/indirectly_linked.rs @@ -0,0 +1,5 @@ +extern crate rlib_with_cmdline_native_lib; + +fn main() { + rlib_with_cmdline_native_lib::hello(); +} diff --git a/src/test/run-make/native-link-modifier-whole-archive/indirectly_linked_via_attr.rs b/src/test/run-make/native-link-modifier-whole-archive/indirectly_linked_via_attr.rs new file mode 100644 index 0000000000000..b9e347609b2c0 --- /dev/null +++ b/src/test/run-make/native-link-modifier-whole-archive/indirectly_linked_via_attr.rs @@ -0,0 +1,5 @@ +extern crate native_lib_in_src; + +fn main() { + native_lib_in_src::hello(); +} diff --git a/src/test/run-make/native-link-modifier-whole-archive/native_lib_in_src.rs b/src/test/run-make/native-link-modifier-whole-archive/native_lib_in_src.rs new file mode 100644 index 0000000000000..373d89b7936e5 --- /dev/null +++ b/src/test/run-make/native-link-modifier-whole-archive/native_lib_in_src.rs @@ -0,0 +1,15 @@ +#![feature(native_link_modifiers_bundle)] +#![feature(native_link_modifiers_whole_archive)] +#![feature(native_link_modifiers)] + +use std::io::Write; + +#[link(name = "c_static_lib_with_constructor", + kind = "static", + modifiers = "-bundle,+whole-archive")] +extern {} + +pub fn hello() { + print!("native_lib_in_src."); + std::io::stdout().flush().unwrap(); +} diff --git a/src/test/run-make/native-link-modifier-whole-archive/rlib_with_cmdline_native_lib.rs b/src/test/run-make/native-link-modifier-whole-archive/rlib_with_cmdline_native_lib.rs new file mode 100644 index 0000000000000..ef2b702dd8209 --- /dev/null +++ b/src/test/run-make/native-link-modifier-whole-archive/rlib_with_cmdline_native_lib.rs @@ -0,0 +1,6 @@ +use std::io::Write; + +pub fn hello() { + print!("indirectly_linked."); + std::io::stdout().flush().unwrap(); +} diff --git a/src/test/rustdoc-gui/implementors.goml b/src/test/rustdoc-gui/implementors.goml index 87e4f2a7bd141..c9042eb4813ab 100644 --- a/src/test/rustdoc-gui/implementors.goml +++ b/src/test/rustdoc-gui/implementors.goml @@ -3,14 +3,14 @@ goto: file://|DOC_PATH|/implementors/trait.Whatever.html assert: "#implementors-list" // There are supposed to be two implementors listed. -assert-count: ("#implementors-list > .impl", 2) +assert-count: ("#implementors-list .impl", 2) // Now we check that both implementors have an anchor, an ID and a similar DOM. -assert: ("#implementors-list > .impl:nth-child(1) > a.anchor") -assert-attribute: ("#implementors-list > .impl:nth-child(1)", {"id": "impl-Whatever"}) -assert-attribute: ("#implementors-list > .impl:nth-child(1) > a.anchor", {"href": "#impl-Whatever"}) -assert: "#implementors-list > .impl:nth-child(1) > .code-header.in-band" +assert: ("#implementors-list .impl:nth-child(1) > a.anchor") +assert-attribute: ("#implementors-list .impl:nth-child(1)", {"id": "impl-Whatever"}) +assert-attribute: ("#implementors-list .impl:nth-child(1) > a.anchor", {"href": "#impl-Whatever"}) +assert: "#implementors-list .impl:nth-child(1) > .code-header.in-band" -assert: ("#implementors-list > .impl:nth-child(2) > a.anchor") -assert-attribute: ("#implementors-list > .impl:nth-child(2)", {"id": "impl-Whatever-1"}) -assert-attribute: ("#implementors-list > .impl:nth-child(2) > a.anchor", {"href": "#impl-Whatever-1"}) -assert: "#implementors-list > .impl:nth-child(2) > .code-header.in-band" +assert: ("#implementors-list .impl:nth-child(2) > a.anchor") +assert-attribute: ("#implementors-list .impl:nth-child(2)", {"id": "impl-Whatever-1"}) +assert-attribute: ("#implementors-list .impl:nth-child(2) > a.anchor", {"href": "#impl-Whatever-1"}) +assert: "#implementors-list .impl:nth-child(2) > .code-header.in-band" diff --git a/src/test/rustdoc-gui/src/lib2/implementors/lib.rs b/src/test/rustdoc-gui/src/lib2/implementors/lib.rs index 4b2f6962e30c5..6417a6ac5af6d 100644 --- a/src/test/rustdoc-gui/src/lib2/implementors/lib.rs +++ b/src/test/rustdoc-gui/src/lib2/implementors/lib.rs @@ -1,7 +1,11 @@ pub trait Whatever { + type Foo; + fn method() {} } pub struct Struct; -impl Whatever for Struct {} +impl Whatever for Struct { + type Foo = u8; +} diff --git a/src/test/rustdoc-gui/src/lib2/lib.rs b/src/test/rustdoc-gui/src/lib2/lib.rs index 36373d24971c9..cb63a9f600227 100644 --- a/src/test/rustdoc-gui/src/lib2/lib.rs +++ b/src/test/rustdoc-gui/src/lib2/lib.rs @@ -38,7 +38,9 @@ impl Trait for Foo { } -impl implementors::Whatever for Foo {} +impl implementors::Whatever for Foo { + type Foo = u32; +} pub mod sub_mod { /// ```txt diff --git a/src/test/rustdoc-gui/toggle-implementors.goml b/src/test/rustdoc-gui/toggle-implementors.goml new file mode 100644 index 0000000000000..15521ff0f49d3 --- /dev/null +++ b/src/test/rustdoc-gui/toggle-implementors.goml @@ -0,0 +1,4 @@ +// This test ensures that the implementors toggle are not open by default. +goto: file://|DOC_PATH|/implementors/trait.Whatever.html + +assert-attribute-false: ("#implementors-list > details", {"open": ""}, ALL) diff --git a/src/test/rustdoc/trait-impl-items-links-and-anchors.rs b/src/test/rustdoc/trait-impl-items-links-and-anchors.rs index ddbe93febdc25..4d25835bf08fd 100644 --- a/src/test/rustdoc/trait-impl-items-links-and-anchors.rs +++ b/src/test/rustdoc/trait-impl-items-links-and-anchors.rs @@ -1,6 +1,6 @@ pub trait MyTrait { type Assoc; - const VALUE: u32; + const VALUE: u32 = 12; fn trait_function(&self); fn defaulted(&self) {} fn defaulted_override(&self) {} @@ -38,9 +38,11 @@ impl MyTrait for Vec { } impl MyTrait for MyStruct { + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedtype.Assoc-3"]//a[@class="anchor"]/@href' #associatedtype.Assoc-3 // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="associatedtype.Assoc"]//a[@class="type"]/@href' trait.MyTrait.html#associatedtype.Assoc // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="associatedtype.Assoc"]//a[@class="anchor"]/@href' #associatedtype.Assoc type Assoc = bool; + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedconstant.VALUE-3"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-3 // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="associatedconstant.VALUE"]//a[@class="constant"]/@href' trait.MyTrait.html#associatedconstant.VALUE // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="associatedconstant.VALUE"]//a[@class="anchor"]/@href' #associatedconstant.VALUE const VALUE: u32 = 20; @@ -55,3 +57,10 @@ impl MyTrait for MyStruct { } pub struct MyStruct; + +// We check that associated items with default values aren't generated in the implementors list. +impl MyTrait for (u8, u8) { + // @!has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedconstant.VALUE-4"]' + type Assoc = bool; + fn trait_function(&self) {} +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.fixed new file mode 100644 index 0000000000000..f91454aa2111e --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.fixed @@ -0,0 +1,25 @@ +// run-rustfix +// edition:2018 +// check-pass +#![warn(rust_2021_compatibility)] + +macro_rules! m { + (@ $body:expr) => {{ + let f = || $body; + //~^ WARNING: drop order + f(); + }}; + ($body:block) => {{ + m!(@ $body); + }}; +} + +fn main() { + let a = (1.to_string(), 2.to_string()); + m!({ + let _ = &a; + //~^ HELP: add a dummy + let x = a.0; + println!("{}", x); + }); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.rs b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.rs new file mode 100644 index 0000000000000..5a1026d043319 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.rs @@ -0,0 +1,24 @@ +// run-rustfix +// edition:2018 +// check-pass +#![warn(rust_2021_compatibility)] + +macro_rules! m { + (@ $body:expr) => {{ + let f = || $body; + //~^ WARNING: drop order + f(); + }}; + ($body:block) => {{ + m!(@ $body); + }}; +} + +fn main() { + let a = (1.to_string(), 2.to_string()); + m!({ + //~^ HELP: add a dummy + let x = a.0; + println!("{}", x); + }); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.stderr new file mode 100644 index 0000000000000..e6e5598f6d2a1 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.stderr @@ -0,0 +1,37 @@ +warning: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/closure-body-macro-fragment.rs:8:17 + | +LL | let f = || $body; + | _________________^ +LL | | +LL | | f(); +LL | | }}; + | | - in Rust 2018, `a` is dropped here, but in Rust 2021, only `a.0` will be dropped here as part of the closure +LL | | ($body:block) => {{ +LL | | m!(@ $body); + | |__________________^ +... +LL | / m!({ +LL | | +LL | | let x = a.0; + | | --- in Rust 2018, this closure captures all of `a`, but in Rust 2021, it will only capture `a.0` +LL | | println!("{}", x); +LL | | }); + | |_______- in this macro invocation + | +note: the lint level is defined here + --> $DIR/closure-body-macro-fragment.rs:4:9 + | +LL | #![warn(rust_2021_compatibility)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(rust_2021_incompatible_closure_captures)]` implied by `#[warn(rust_2021_compatibility)]` + = note: for more information, see + = note: this warning originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) +help: add a dummy let to cause `a` to be fully captured + | +LL ~ m!({ +LL + let _ = &a; + | + +warning: 1 warning emitted + diff --git a/src/test/ui/issues/issue-83190.rs b/src/test/ui/issues/issue-83190.rs new file mode 100644 index 0000000000000..da931c3edaf6f --- /dev/null +++ b/src/test/ui/issues/issue-83190.rs @@ -0,0 +1,49 @@ +// check-pass + +// Regression test for issue #83190, triggering an ICE in borrowck. + +pub trait Any {} +impl Any for T {} + +pub trait StreamOnce { + type Range; +} + +pub trait Parser: Sized { + type Output; + type PartialState; + fn map(self) -> Map { + todo!() + } +} + +pub struct Map

(P); +impl> Parser for Map

{ + type Output = (); + type PartialState = P::PartialState; +} + +struct TakeWhile1(Input); +impl Parser for TakeWhile1 { + type Output = I::Range; + type PartialState = (); +} +impl TakeWhile1 { + fn new() -> Self { + todo!() + } +} + +impl> Parser for (A,) { + type Output = (); + type PartialState = Map; +} + +pub fn metric_stream_parser<'a, I>() -> impl Parser +where + I: StreamOnce, +{ + (TakeWhile1::new(),).map() +} + +fn main() {} diff --git a/src/test/ui/lint/unused/issue-88519-unused-paren.rs b/src/test/ui/lint/unused/issue-88519-unused-paren.rs new file mode 100644 index 0000000000000..be02fcd3f00c7 --- /dev/null +++ b/src/test/ui/lint/unused/issue-88519-unused-paren.rs @@ -0,0 +1,94 @@ +// check-pass +// Make sure unused parens lint doesn't emit a false positive. +// See https://github.com/rust-lang/rust/issues/88519 +#![deny(unused_parens)] +#![feature(type_ascription)] + +// binary ops are tested in issue-71290-unused-paren-binop.rs + +mod call { + fn noop() -> u8 { 0 } + fn outside() -> u8 { + ({ noop })() + } + fn inside() -> u8 { + ({ noop }()) + } + fn outside_match() -> u8 { + (match noop { x => x })() + } + fn inside_match() -> u8 { + (match noop { x => x }()) + } + fn outside_if() -> u8 { + (if false { noop } else { noop })() + } + fn inside_if() -> u8 { + (if false { noop } else { noop }()) + } +} + +mod casts { + fn outside() -> u8 { + ({ 0 }) as u8 + } + fn inside() -> u8 { + ({ 0 } as u8) + } + fn outside_match() -> u8 { + (match 0 { x => x }) as u8 + } + fn inside_match() -> u8 { + (match 0 { x => x } as u8) + } + fn outside_if() -> u8 { + (if false { 0 } else { 0 }) as u8 + } + fn inside_if() -> u8 { + (if false { 0 } else { 0 } as u8) + } +} + +mod typeascription { + fn outside() -> u8 { + ({ 0 }): u8 + } + fn inside() -> u8 { + ({ 0 }: u8) + } + fn outside_match() -> u8 { + (match 0 { x => x }): u8 + } + fn inside_match() -> u8 { + (match 0 { x => x }: u8) + } + fn outside_if() -> u8 { + (if false { 0 } else { 0 }): u8 + } + fn inside_if() -> u8 { + (if false { 0 } else { 0 }: u8) + } +} + +mod index { + fn outside(x: &[u8]) -> u8 { + ({ x })[0] + } + fn inside(x: &[u8]) -> u8 { + ({ x }[0]) + } + fn outside_match(x: &[u8]) -> u8 { + (match x { x => x })[0] + } + fn inside_match(x: &[u8]) -> u8 { + (match x { x => x }[0]) + } + fn outside_if(x: &[u8]) -> u8 { + (if false { x } else { x })[0] + } + fn inside_if(x: &[u8]) -> u8 { + (if false { x } else { x }[0]) + } +} + +fn main() {} diff --git a/src/test/ui/native-library-link-flags/mix-bundle-and-whole-archive-link-attr.rs b/src/test/ui/native-library-link-flags/mix-bundle-and-whole-archive-link-attr.rs new file mode 100644 index 0000000000000..c3714a3845180 --- /dev/null +++ b/src/test/ui/native-library-link-flags/mix-bundle-and-whole-archive-link-attr.rs @@ -0,0 +1,12 @@ +// compile-flags: -Zunstable-options --crate-type rlib +// build-fail +// error-pattern: the linking modifiers `+bundle` and `+whole-archive` are not compatible with each other when generating rlibs + +#![feature(native_link_modifiers)] +#![feature(native_link_modifiers_bundle)] +#![feature(native_link_modifiers_whole_archive)] + +#[link(name = "mylib", kind = "static", modifiers = "+bundle,+whole-archive")] +extern "C" { } + +fn main() { } diff --git a/src/test/ui/native-library-link-flags/mix-bundle-and-whole-archive-link-attr.stderr b/src/test/ui/native-library-link-flags/mix-bundle-and-whole-archive-link-attr.stderr new file mode 100644 index 0000000000000..246efb8d627cb --- /dev/null +++ b/src/test/ui/native-library-link-flags/mix-bundle-and-whole-archive-link-attr.stderr @@ -0,0 +1,6 @@ +error: the linking modifiers `+bundle` and `+whole-archive` are not compatible with each other when generating rlibs + +error: could not find native static library `mylib`, perhaps an -L flag is missing? + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/native-library-link-flags/mix-bundle-and-whole-archive.rs b/src/test/ui/native-library-link-flags/mix-bundle-and-whole-archive.rs new file mode 100644 index 0000000000000..1d0768d99cffd --- /dev/null +++ b/src/test/ui/native-library-link-flags/mix-bundle-and-whole-archive.rs @@ -0,0 +1,7 @@ +// Mixing +bundle and +whole-archive is not allowed + +// compile-flags: -l static:+bundle,+whole-archive=mylib -Zunstable-options --crate-type rlib +// build-fail +// error-pattern: the linking modifiers `+bundle` and `+whole-archive` are not compatible with each other when generating rlibs + +fn main() { } diff --git a/src/test/ui/native-library-link-flags/mix-bundle-and-whole-archive.stderr b/src/test/ui/native-library-link-flags/mix-bundle-and-whole-archive.stderr new file mode 100644 index 0000000000000..246efb8d627cb --- /dev/null +++ b/src/test/ui/native-library-link-flags/mix-bundle-and-whole-archive.stderr @@ -0,0 +1,6 @@ +error: the linking modifiers `+bundle` and `+whole-archive` are not compatible with each other when generating rlibs + +error: could not find native static library `mylib`, perhaps an -L flag is missing? + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/remap-path-prefix.rs b/src/test/ui/remap-path-prefix.rs new file mode 100644 index 0000000000000..2eef970997708 --- /dev/null +++ b/src/test/ui/remap-path-prefix.rs @@ -0,0 +1,9 @@ +// compile-flags: --remap-path-prefix={{src-base}}=remapped + +fn main() { + // We cannot actually put an ERROR marker here because + // the file name in the error message is not what the + // test framework expects (since the filename gets remapped). + // We still test the expected error in the stderr file. + ferris +} diff --git a/src/test/ui/remap-path-prefix.stderr b/src/test/ui/remap-path-prefix.stderr new file mode 100644 index 0000000000000..ad6a35d1256cd --- /dev/null +++ b/src/test/ui/remap-path-prefix.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find value `ferris` in this scope + --> remapped/remap-path-prefix.rs:8:5 + | +LL | ferris + | ^^^^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs index a371f8bbd3cb4..39f7ade3f81f6 100644 --- a/src/tools/clippy/clippy_lints/src/macro_use.rs +++ b/src/tools/clippy/clippy_lints/src/macro_use.rs @@ -47,11 +47,8 @@ pub struct MacroRefData { impl MacroRefData { pub fn new(name: String, callee: Span, cx: &LateContext<'_>) -> Self { - let mut path = cx - .sess() - .source_map() - .span_to_filename(callee) - .prefer_local() + let sm = cx.sess().source_map(); + let mut path = sm.filename_for_diagnostics(&sm.span_to_filename(callee)) .to_string(); // std lib paths are <::std::module::file type>