diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index e9135b7163025..e616a09024cec 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -285,7 +285,7 @@ impl ParenthesizedArgs { pub use crate::node_id::{NodeId, CRATE_NODE_ID, DUMMY_NODE_ID}; -/// A modifier on a bound, e.g., `?Sized` or `~const Trait`. +/// A modifier on a bound, e.g., `?Trait` or `~const Trait`. /// /// Negative bounds should also be handled here. #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)] diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 8d6c8c247851e..68b536da9f70f 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -8,6 +8,7 @@ use rustc_errors::{struct_span_err, Applicability}; use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg}; use rustc_macros::HashStable_Generic; use rustc_session::lint::builtin::UNEXPECTED_CFGS; +use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::parse::{feature_err, ParseSess}; use rustc_session::Session; use rustc_span::hygiene::Transparency; @@ -461,29 +462,37 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat true } MetaItemKind::NameValue(..) | MetaItemKind::Word => { - let name = cfg.ident().expect("multi-segment cfg predicate").name; + let ident = cfg.ident().expect("multi-segment cfg predicate"); + let name = ident.name; let value = cfg.value_str(); - if sess.check_config.names_checked && !sess.check_config.names_valid.contains(&name) - { - sess.buffer_lint( - UNEXPECTED_CFGS, - cfg.span, - CRATE_NODE_ID, - "unexpected `cfg` condition name", - ); - } - if let Some(val) = value { - if sess.check_config.values_checked.contains(&name) - && !sess.check_config.values_valid.contains(&(name, val)) - { - sess.buffer_lint( + if let Some(names_valid) = &sess.check_config.names_valid { + if !names_valid.contains(&name) { + sess.buffer_lint_with_diagnostic( UNEXPECTED_CFGS, cfg.span, CRATE_NODE_ID, - "unexpected `cfg` condition value", + "unexpected `cfg` condition name", + BuiltinLintDiagnostics::UnexpectedCfg(ident.span, name, None), ); } } + if let Some(value) = value { + if let Some(values) = &sess.check_config.values_valid.get(&name) { + if !values.contains(&value) { + sess.buffer_lint_with_diagnostic( + UNEXPECTED_CFGS, + cfg.span, + CRATE_NODE_ID, + "unexpected `cfg` condition value", + BuiltinLintDiagnostics::UnexpectedCfg( + cfg.name_value_literal_span().unwrap(), + name, + Some(value), + ), + ); + } + } + } sess.config.contains(&(name, value)) } } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 1fb1a38a927d5..53762eef78592 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -453,7 +453,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Internal attributes: Stability, deprecation, and unsafe: // ========================================================================== - ungated!(feature, CrateLevel, template!(List: "name1, name1, ..."), DuplicatesOk), + ungated!(feature, CrateLevel, template!(List: "name1, name2, ..."), DuplicatesOk), // DuplicatesOk since it has its own validation ungated!( rustc_deprecated, Normal, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 255e661652db9..d48c8e81f540f 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2776,6 +2776,10 @@ impl FnHeader { pub fn is_const(&self) -> bool { matches!(&self.constness, Constness::Const) } + + pub fn is_unsafe(&self) -> bool { + matches!(&self.unsafety, Unsafety::Unsafe) + } } #[derive(Debug, HashStable_Generic)] diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 609fc4b78c0de..91ced2a2d90e2 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -169,11 +169,12 @@ pub fn parse_check_cfg(specs: Vec) -> CheckCfg { Ok(meta_item) if parser.token == token::Eof => { if let Some(args) = meta_item.meta_item_list() { if meta_item.has_name(sym::names) { - cfg.names_checked = true; + let names_valid = + cfg.names_valid.get_or_insert_with(|| FxHashSet::default()); for arg in args { if arg.is_word() && arg.ident().is_some() { let ident = arg.ident().expect("multi-segment cfg key"); - cfg.names_valid.insert(ident.name.to_string()); + names_valid.insert(ident.name.to_string()); } else { error!("`names()` arguments must be simple identifers"); } @@ -183,13 +184,16 @@ pub fn parse_check_cfg(specs: Vec) -> CheckCfg { if let Some((name, values)) = args.split_first() { if name.is_word() && name.ident().is_some() { let ident = name.ident().expect("multi-segment cfg key"); - cfg.values_checked.insert(ident.to_string()); + let ident_values = cfg + .values_valid + .entry(ident.name.to_string()) + .or_insert_with(|| FxHashSet::default()); + for val in values { if let Some(LitKind::Str(s, _)) = val.literal().map(|lit| &lit.kind) { - cfg.values_valid - .insert((ident.to_string(), s.to_string())); + ident_values.insert(s.to_string()); } else { error!( "`values()` arguments must be string literals" @@ -219,7 +223,9 @@ pub fn parse_check_cfg(specs: Vec) -> CheckCfg { ); } - cfg.names_valid.extend(cfg.values_checked.iter().cloned()); + if let Some(names_valid) = &mut cfg.names_valid { + names_valid.extend(cfg.values_valid.keys().cloned()); + } cfg }) } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index d8f55292ccd8d..0c9defc4ceac4 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -765,7 +765,40 @@ pub trait LintContext: Sized { BuiltinLintDiagnostics::NamedAsmLabel(help) => { db.help(&help); db.note("see the asm section of Rust By Example for more information"); - } + }, + BuiltinLintDiagnostics::UnexpectedCfg(span, name, value) => { + let possibilities: Vec = if value.is_some() { + let Some(values) = &sess.parse_sess.check_config.values_valid.get(&name) else { + bug!("it shouldn't be possible to have a diagnostic on a value whose name is not in values"); + }; + values.iter().map(|&s| s).collect() + } else { + let Some(names_valid) = &sess.parse_sess.check_config.names_valid else { + bug!("it shouldn't be possible to have a diagnostic on a name if name checking is not enabled"); + }; + names_valid.iter().map(|s| *s).collect() + }; + + // Show the full list if all possible values for a given name, but don't do it + // for names as the possibilities could be very long + if value.is_some() { + if !possibilities.is_empty() { + let mut possibilities = possibilities.iter().map(Symbol::as_str).collect::>(); + possibilities.sort(); + + let possibilities = possibilities.join(", "); + db.note(&format!("expected values for `{name}` are: {possibilities}")); + } else { + db.note(&format!("no expected value for `{name}`")); + } + } + + // Suggest the most probable if we found one + if let Some(best_match) = find_best_match_for_name(&possibilities, value.unwrap_or(name), None) { + let punctuation = if value.is_some() { "\"" } else { "" }; + db.span_suggestion(span, "did you mean", format!("{punctuation}{best_match}{punctuation}"), Applicability::MaybeIncorrect); + } + }, } // Rewrap `db`, and pass control to the user. decorate(LintDiagnosticBuilder::new(db)); diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 69863b5ff827f..7182022d25298 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -31,6 +31,7 @@ #![feature(box_patterns)] #![feature(crate_visibility_modifier)] #![feature(if_let_guard)] +#![feature(iter_intersperse)] #![feature(iter_order_by)] #![feature(let_else)] #![feature(never_type)] diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 1f834b7212fe5..e9c62fc400651 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -310,6 +310,7 @@ pub enum BuiltinLintDiagnostics { BreakWithLabelAndLoop(Span), NamedAsmLabel(String), UnicodeTextFlow(Span, String), + UnexpectedCfg(Span, Symbol, Option), } /// Lints that are buffered up early on in the `Session` before the diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 7a0d9a212c9d9..f9b75690e375f 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -8,7 +8,7 @@ use crate::search_paths::SearchPath; use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind}; use crate::{early_error, early_warn, Session}; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::impl_stable_hash_via_hash; use rustc_target::abi::{Align, TargetDataLayout}; @@ -1023,34 +1023,30 @@ pub fn to_crate_config(cfg: FxHashSet<(String, Option)>) -> CrateConfig /// The parsed `--check-cfg` options pub struct CheckCfg { - /// Set if `names()` checking is enabled - pub names_checked: bool, - /// The union of all `names()` - pub names_valid: FxHashSet, - /// The set of names for which `values()` was used - pub values_checked: FxHashSet, - /// The set of all (name, value) pairs passed in `values()` - pub values_valid: FxHashSet<(T, T)>, + /// The set of all `names()`, if None no name checking is performed + pub names_valid: Option>, + /// The set of all `values()` + pub values_valid: FxHashMap>, } impl Default for CheckCfg { fn default() -> Self { - CheckCfg { - names_checked: false, - names_valid: FxHashSet::default(), - values_checked: FxHashSet::default(), - values_valid: FxHashSet::default(), - } + CheckCfg { names_valid: Default::default(), values_valid: Default::default() } } } impl CheckCfg { fn map_data(&self, f: impl Fn(&T) -> O) -> CheckCfg { CheckCfg { - names_checked: self.names_checked, - names_valid: self.names_valid.iter().map(|a| f(a)).collect(), - values_checked: self.values_checked.iter().map(|a| f(a)).collect(), - values_valid: self.values_valid.iter().map(|(a, b)| (f(a), f(b))).collect(), + names_valid: self + .names_valid + .as_ref() + .map(|names_valid| names_valid.iter().map(|a| f(a)).collect()), + values_valid: self + .values_valid + .iter() + .map(|(a, b)| (f(a), b.iter().map(|b| f(b)).collect())) + .collect(), } } } @@ -1090,17 +1086,23 @@ impl CrateCheckConfig { sym::doctest, sym::feature, ]; - for &name in WELL_KNOWN_NAMES { - self.names_valid.insert(name); + if let Some(names_valid) = &mut self.names_valid { + for &name in WELL_KNOWN_NAMES { + names_valid.insert(name); + } } } /// Fills a `CrateCheckConfig` with configuration names and values that are actually active. pub fn fill_actual(&mut self, cfg: &CrateConfig) { for &(k, v) in cfg { - self.names_valid.insert(k); + if let Some(names_valid) = &mut self.names_valid { + names_valid.insert(k); + } if let Some(v) = v { - self.values_valid.insert((k, v)); + self.values_valid.entry(k).and_modify(|values| { + values.insert(v); + }); } } } diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index e1ea98813031a..90c5719f486cb 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -728,7 +728,7 @@ pub use macros::Debug; /// ``` #[rustc_on_unimplemented( on( - _Self = "std::path::Path", + any(_Self = "std::path::Path", _Self = "std::path::PathBuf"), label = "`{Self}` cannot be formatted with the default formatter; call `.display()` on it", note = "call `.display()` or `.to_string_lossy()` to safely print paths, \ as they may contain non-Unicode data" diff --git a/library/core/src/ops/generator.rs b/library/core/src/ops/generator.rs index 52a2e464e3a7c..7c3b2a644e8db 100644 --- a/library/core/src/ops/generator.rs +++ b/library/core/src/ops/generator.rs @@ -47,7 +47,7 @@ pub enum GeneratorState { /// fn main() { /// let mut generator = || { /// yield 1; -/// return "foo" +/// "foo" /// }; /// /// match Pin::new(&mut generator).resume(()) { diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index e2b0e90e2bf7d..775d2ded9daad 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1100,8 +1100,8 @@ pub macro pin($value:expr $(,)?) { // that would break `Pin`'s invariants. // - `{ $value }` is braced, making it a _block expression_, thus **moving** // the given `$value`, and making it _become an **anonymous** temporary_. - // By virtue of being anonynomous, it can no longer be accessed, thus - // preventing any attemps to `mem::replace` it or `mem::forget` it, _etc._ + // By virtue of being anonymous, it can no longer be accessed, thus + // preventing any attempts to `mem::replace` it or `mem::forget` it, _etc._ // // This gives us a `pin!` definition that is sound, and which works, but only // in certain scenarios: diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index f8d790c37852c..beb606099341e 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -1482,7 +1482,7 @@ fn _assert_sync_and_send() { /// /// Parallelism is a resource. A given machine provides a certain capacity for /// parallelism, i.e., a bound on the number of computations it can perform -/// simultaneously. This number often corresponds to the amount of CPUs or +/// simultaneously. This number often corresponds to the amount of CPUs a /// computer has, but it may diverge in various cases. /// /// Host environments such as VMs or container orchestrators may want to diff --git a/src/bootstrap/download-ci-llvm-stamp b/src/bootstrap/download-ci-llvm-stamp index ef635ee26df57..20a98111bebaa 100644 --- a/src/bootstrap/download-ci-llvm-stamp +++ b/src/bootstrap/download-ci-llvm-stamp @@ -1,4 +1,4 @@ Change this file to make users of the `download-ci-llvm` configuration download a new version of LLVM from CI, even if the LLVM submodule hasn’t changed. -Last change is for: https://github.com/rust-lang/rust/pull/91229 +Last change is for: https://github.com/rust-lang/rust/pull/94023 diff --git a/src/bootstrap/format.rs b/src/bootstrap/format.rs index 2408344487bb1..30fe6a7a44695 100644 --- a/src/bootstrap/format.rs +++ b/src/bootstrap/format.rs @@ -13,7 +13,7 @@ fn rustfmt(src: &Path, rustfmt: &Path, paths: &[PathBuf], check: bool) -> impl F // avoid the submodule config paths from coming into play, // we only allow a single global config for the workspace for now cmd.arg("--config-path").arg(&src.canonicalize().unwrap()); - cmd.arg("--edition").arg("2018"); + cmd.arg("--edition").arg("2021"); cmd.arg("--unstable-features"); cmd.arg("--skip-children"); if check { diff --git a/src/doc/unstable-book/src/compiler-flags/check-cfg.md b/src/doc/unstable-book/src/compiler-flags/check-cfg.md new file mode 100644 index 0000000000000..d7345ad0c33f2 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/check-cfg.md @@ -0,0 +1,221 @@ +# `check-cfg` + +The tracking issue for this feature is: [#82450](https://github.com/rust-lang/rust/issues/82450). + +------------------------ + +This feature allows you to enable complete or partial checking of configuration. + +`rustc` accepts the `--check-cfg` option, which specifies whether to check conditions and how to +check them. The `--check-cfg` option takes a value, called the _check cfg specification_. The +check cfg specification is parsed using the Rust metadata syntax, just as the `--cfg` option is. + +`--check-cfg` option can take one of two forms: + +1. `--check-cfg names(...)` enables checking condition names. +2. `--check-cfg values(...)` enables checking the values within list-valued conditions. + +These two options are independent. `names` checks only the namespace of condition names +while `values` checks only the namespace of the values of list-valued conditions. + +## The `names(...)` form + +The `names(...)` form enables checking the names. This form uses a named list: + +```bash +rustc --check-cfg 'names(name1, name2, ... nameN)' +``` + +where each `name` is a bare identifier (has no quotes). The order of the names is not significant. + +If `--check-cfg names(...)` is specified at least once, then `rustc` will check all references to +condition names. `rustc` will check every `#[cfg]` attribute, `#[cfg_attr]` attribute, `cfg` clause +inside `#[link]` attribute and `cfg!(...)` call against the provided list of expected condition +names. If a name is not present in this list, then `rustc` will report an `unexpected_cfgs` lint +diagnostic. The default diagnostic level for this lint is `Warn`. + +If `--check-cfg names(...)` is not specified, then `rustc` will not check references to condition +names. + +`--check-cfg names(...)` may be specified more than once. The result is that the list of valid +condition names is merged across all options. It is legal for a condition name to be specified +more than once; redundantly specifying a condition name has no effect. + +To enable checking condition names with an empty set of valid condition names, use the following +form. The parentheses are required. + +```bash +rustc --check-cfg 'names()' +``` + +Note that `--check-cfg 'names()'` is _not_ equivalent to omitting the option entirely. +The first form enables checking condition names, while specifying that there are no valid +condition names (outside of the set of well-known names defined by `rustc`). Omitting the +`--check-cfg 'names(...)'` option does not enable checking condition names. + +Conditions that are enabled are implicitly valid; it is unnecessary (but legal) to specify a +condition name as both enabled and valid. For example, the following invocations are equivalent: + +```bash +# condition names will be checked, and 'has_time_travel' is valid +rustc --cfg 'has_time_travel' --check-cfg 'names()' + +# condition names will be checked, and 'has_time_travel' is valid +rustc --cfg 'has_time_travel' --check-cfg 'names(has_time_travel)' +``` + +In contrast, the following two invocations are _not_ equivalent: + +```bash +# condition names will not be checked (because there is no --check-cfg names(...)) +rustc --cfg 'has_time_travel' + +# condition names will be checked, and 'has_time_travel' is both valid and enabled. +rustc --cfg 'has_time_travel' --check-cfg 'names(has_time_travel)' +``` + +## The `values(...)` form + +The `values(...)` form enables checking the values within list-valued conditions. It has this +form: + +```bash +rustc --check-cfg `values(name, "value1", "value2", ... "valueN")' +``` + +where `name` is a bare identifier (has no quotes) and each `"value"` term is a quoted literal +string. `name` specifies the name of the condition, such as `feature` or `target_os`. + +When the `values(...)` option is specified, `rustc` will check every `#[cfg(name = "value")]` +attribute, `#[cfg_attr(name = "value")]` attribute, `#[link(name = "a", cfg(name = "value"))]` +and `cfg!(name = "value")` call. It will check that the `"value"` specified is present in the +list of expected values. If `"value"` is not in it, then `rustc` will report an `unexpected_cfgs` +lint diagnostic. The default diagnostic level for this lint is `Warn`. + +The form `values()` is an error, because it does not specify a condition name. + +To enable checking of values, but to provide an empty set of valid values, use this form: + +```bash +rustc --check-cfg `values(name)` +``` + +The `--check-cfg values(...)` option can be repeated, both for the same condition name and for +different names. If it is repeated for the same condition name, then the sets of values for that +condition are merged together. + +## Examples + +Consider this command line: + +```bash +rustc --check-cfg 'names(feature)' \ + --check-cfg 'values(feature,"lion","zebra")' \ + --cfg 'feature="lion"' -Z unstable-options \ + example.rs +``` + +This command line indicates that this crate has two features: `lion` and `zebra`. The `lion` +feature is enabled, while the `zebra` feature is disabled. Consider compiling this code: + +```rust +// This is expected, and tame_lion() will be compiled +#[cfg(feature = "lion")] +fn tame_lion(lion: Lion) {} + +// This is expected, and ride_zebra() will NOT be compiled. +#[cfg(feature = "zebra")] +fn ride_zebra(zebra: Zebra) {} + +// This is UNEXPECTED, and will cause a compiler warning (by default). +#[cfg(feature = "platypus")] +fn poke_platypus() {} + +// This is UNEXPECTED, because 'feechure' is not a known condition name, +// and will cause a compiler warning (by default). +#[cfg(feechure = "lion")] +fn tame_lion() {} +``` + +> Note: The `--check-cfg names(feature)` option is necessary only to enable checking the condition +> name, as in the last example. `feature` is a well-known (always-expected) condition name, and so +> it is not necessary to specify it in a `--check-cfg 'names(...)'` option. That option can be +> shortened to > `--check-cfg names()` in order to enable checking well-known condition names. + +### Example: Checking condition names, but not values + +```bash +# This turns on checking for condition names, but not values, such as 'feature' values. +rustc --check-cfg 'names(is_embedded, has_feathers)' \ + --cfg has_feathers --cfg 'feature = "zapping"' -Z unstable-options +``` + +```rust +#[cfg(is_embedded)] // This is expected as "is_embedded" was provided in names() +fn do_embedded() {} + +#[cfg(has_feathers)] // This is expected as "has_feathers" was provided in names() +fn do_features() {} + +#[cfg(has_mumble_frotz)] // This is UNEXPECTED because names checking is enable and + // "has_mumble_frotz" was not provided in names() +fn do_mumble_frotz() {} + +#[cfg(feature = "lasers")] // This doesn't raise a warning, because values checking for "feature" + // was never used +fn shoot_lasers() {} +``` + +### Example: Checking feature values, but not condition names + +```bash +# This turns on checking for feature values, but not for condition names. +rustc --check-cfg 'values(feature, "zapping", "lasers")' \ + --cfg 'feature="zapping"' -Z unstable-options +``` + +```rust +#[cfg(is_embedded)] // This is doesn't raise a warning, because names checking was not + // enable (ie not names()) +fn do_embedded() {} + +#[cfg(has_feathers)] // Same as above, --check-cfg names(...) was never used so no name + // checking is performed +fn do_features() {} + + +#[cfg(feature = "lasers")] // This is expected, "lasers" is in the values(feature) list +fn shoot_lasers() {} + +#[cfg(feature = "monkeys")] // This is UNEXPECTED, because "monkeys" is not in the + // --check-cfg values(feature) list +fn write_shakespeare() {} +``` + +### Example: Checking both condition names and feature values + +```bash +# This turns on checking for feature values and for condition names. +rustc --check-cfg 'names(is_embedded, has_feathers)' \ + --check-cfg 'values(feature, "zapping", "lasers")' \ + --cfg has_feathers --cfg 'feature="zapping"' -Z unstable-options +``` + +```rust +#[cfg(is_embedded)] // This is expected because "is_embedded" was provided in names() +fn do_embedded() {} + +#[cfg(has_feathers)] // This is expected because "has_feathers" was provided in names() +fn do_features() {} + +#[cfg(has_mumble_frotz)] // This is UNEXPECTED, because has_mumble_frotz is not in the + // --check-cfg names(...) list +fn do_mumble_frotz() {} + +#[cfg(feature = "lasers")] // This is expected, "lasers" is in the values(feature) list +fn shoot_lasers() {} + +#[cfg(feature = "monkeys")] // This is UNEXPECTED, because "monkeys" is not in + // the values(feature) list +fn write_shakespear() {} +``` diff --git a/src/doc/unstable-book/src/compiler-flags/debug_info_for_profiling.md b/src/doc/unstable-book/src/compiler-flags/debug_info_for_profiling.md index 44bd3baeeedfc..ee72b6adf8e9f 100644 --- a/src/doc/unstable-book/src/compiler-flags/debug_info_for_profiling.md +++ b/src/doc/unstable-book/src/compiler-flags/debug_info_for_profiling.md @@ -1,4 +1,4 @@ -# `debug-info-for-profiling +# `debug-info-for-profiling` --- diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index a7f852a432c82..3f71a53f963e4 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -201,19 +201,19 @@ impl<'tcx> Context<'tcx> { } else { tyname.as_str() }; - let page = layout::Page { - css_class: tyname_s, - root_path: &self.root_path(), - static_root_path: self.shared.static_root_path.as_deref(), - title: &title, - description: &desc, - keywords: &keywords, - resource_suffix: &self.shared.resource_suffix, - extra_scripts: &[], - static_extra_scripts: &[], - }; if !self.render_redirect_pages { + let page = layout::Page { + css_class: tyname_s, + root_path: &self.root_path(), + static_root_path: self.shared.static_root_path.as_deref(), + title: &title, + description: &desc, + keywords: &keywords, + resource_suffix: &self.shared.resource_suffix, + extra_scripts: &[], + static_extra_scripts: &[], + }; layout::render( &self.shared.layout, &page, @@ -223,23 +223,31 @@ impl<'tcx> Context<'tcx> { ) } else { if let Some(&(ref names, ty)) = self.cache().paths.get(&it.def_id.expect_def_id()) { - let mut path = String::new(); - for name in &names[..names.len() - 1] { - path.push_str(&name.as_str()); - path.push('/'); - } - path.push_str(&item_path(ty, &names.last().unwrap().as_str())); - match self.shared.redirections { - Some(ref redirections) => { - let mut current_path = String::new(); - for name in &self.current { - current_path.push_str(&name.as_str()); - current_path.push('/'); + if self.current.len() + 1 != names.len() + || self.current.iter().zip(names.iter()).any(|(a, b)| a != b) + { + // We checked that the redirection isn't pointing to the current file, + // preventing an infinite redirection loop in the generated + // documentation. + + let mut path = String::new(); + for name in &names[..names.len() - 1] { + path.push_str(&name.as_str()); + path.push('/'); + } + path.push_str(&item_path(ty, &names.last().unwrap().as_str())); + match self.shared.redirections { + Some(ref redirections) => { + let mut current_path = String::new(); + for name in &self.current { + current_path.push_str(&name.as_str()); + current_path.push('/'); + } + current_path.push_str(&item_path(ty, &names.last().unwrap().as_str())); + redirections.borrow_mut().insert(current_path, path); } - current_path.push_str(&item_path(ty, &names.last().unwrap().as_str())); - redirections.borrow_mut().insert(current_path, path); + None => return layout::redirect(&format!("{}{}", self.root_path(), path)), } - None => return layout::redirect(&format!("{}{}", self.root_path(), path)), } } String::new() diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index e77bd5c922313..d4aedb41ddb18 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -12,6 +12,7 @@ use rustc_hir::{def::CtorKind, def_id::DefId}; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::def_id::CRATE_DEF_INDEX; use rustc_span::Pos; +use rustc_target::spec::abi::Abi as RustcAbi; use rustdoc_json_types::*; @@ -19,7 +20,6 @@ use crate::clean::utils::print_const_expr; use crate::clean::{self, ItemId}; use crate::formats::item_type::ItemType; use crate::json::JsonRenderer; -use std::collections::HashSet; impl JsonRenderer<'_> { pub(super) fn convert_item(&self, item: clean::Item) -> Option { @@ -271,22 +271,28 @@ crate fn from_ctor_kind(struct_type: CtorKind) -> StructType { } } -crate fn from_fn_header(header: &rustc_hir::FnHeader) -> HashSet { - let mut v = HashSet::new(); - - if let rustc_hir::Unsafety::Unsafe = header.unsafety { - v.insert(Qualifiers::Unsafe); - } - - if let rustc_hir::IsAsync::Async = header.asyncness { - v.insert(Qualifiers::Async); +crate fn from_fn_header(header: &rustc_hir::FnHeader) -> Header { + Header { + async_: header.is_async(), + const_: header.is_const(), + unsafe_: header.is_unsafe(), + abi: convert_abi(header.abi), } +} - if let rustc_hir::Constness::Const = header.constness { - v.insert(Qualifiers::Const); +fn convert_abi(a: RustcAbi) -> Abi { + match a { + RustcAbi::Rust => Abi::Rust, + RustcAbi::C { unwind } => Abi::C { unwind }, + RustcAbi::Cdecl { unwind } => Abi::Cdecl { unwind }, + RustcAbi::Stdcall { unwind } => Abi::Stdcall { unwind }, + RustcAbi::Fastcall { unwind } => Abi::Fastcall { unwind }, + RustcAbi::Aapcs { unwind } => Abi::Aapcs { unwind }, + RustcAbi::Win64 { unwind } => Abi::Win64 { unwind }, + RustcAbi::SysV64 { unwind } => Abi::SysV64 { unwind }, + RustcAbi::System { unwind } => Abi::System { unwind }, + _ => Abi::Other(a.to_string()), } - - v } impl FromWithTcx for Function { @@ -296,7 +302,6 @@ impl FromWithTcx for Function { decl: decl.into_tcx(tcx), generics: generics.into_tcx(tcx), header: from_fn_header(&header), - abi: header.abi.to_string(), } } } @@ -465,16 +470,14 @@ impl FromWithTcx for FunctionPointer { fn from_tcx(bare_decl: clean::BareFunctionDecl, tcx: TyCtxt<'_>) -> Self { let clean::BareFunctionDecl { unsafety, generic_params, decl, abi } = bare_decl; FunctionPointer { - header: if let rustc_hir::Unsafety::Unsafe = unsafety { - let mut hs = HashSet::new(); - hs.insert(Qualifiers::Unsafe); - hs - } else { - HashSet::new() + header: Header { + unsafe_: matches!(unsafety, rustc_hir::Unsafety::Unsafe), + const_: false, + async_: false, + abi: convert_abi(abi), }, generic_params: generic_params.into_iter().map(|x| x.into_tcx(tcx)).collect(), decl: decl.into_tcx(tcx), - abi: abi.to_string(), } } } @@ -554,7 +557,6 @@ crate fn from_function_method( decl: decl.into_tcx(tcx), generics: generics.into_tcx(tcx), header: from_fn_header(&header), - abi: header.abi.to_string(), has_body, } } diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 600833664be5e..be9bbc7391d75 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -3,13 +3,13 @@ //! These types are the public API exposed through the `--output-format json` flag. The [`Crate`] //! struct is the root of the JSON blob and all other items are contained within. -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::path::PathBuf; use serde::{Deserialize, Serialize}; /// rustdoc format-version. -pub const FORMAT_VERSION: u32 = 10; +pub const FORMAT_VERSION: u32 = 11; /// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information /// about the language items in the local crate, as well as info about external items to allow @@ -287,29 +287,45 @@ pub enum StructType { Unit, } -#[non_exhaustive] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] -#[serde(rename_all = "snake_case")] -pub enum Qualifiers { - Const, - Unsafe, - Async, +pub struct Header { + #[serde(rename = "const")] + pub const_: bool, + #[serde(rename = "unsafe")] + pub unsafe_: bool, + #[serde(rename = "async")] + pub async_: bool, + pub abi: Abi, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub enum Abi { + // We only have a concrete listing here for stable ABI's because their are so many + // See rustc_ast_passes::feature_gate::PostExpansionVisitor::check_abi for the list + Rust, + C { unwind: bool }, + Cdecl { unwind: bool }, + Stdcall { unwind: bool }, + Fastcall { unwind: bool }, + Aapcs { unwind: bool }, + Win64 { unwind: bool }, + SysV64 { unwind: bool }, + System { unwind: bool }, + Other(String), } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct Function { pub decl: FnDecl, pub generics: Generics, - pub header: HashSet, - pub abi: String, + pub header: Header, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct Method { pub decl: FnDecl, pub generics: Generics, - pub header: HashSet, - pub abi: String, + pub header: Header, pub has_body: bool, } @@ -426,8 +442,7 @@ pub enum Type { pub struct FunctionPointer { pub decl: FnDecl, pub generic_params: Vec, - pub header: HashSet, - pub abi: String, + pub header: Header, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] diff --git a/src/test/rustdoc-json/fn_pointer/abi.rs b/src/test/rustdoc-json/fn_pointer/abi.rs new file mode 100644 index 0000000000000..eef20e60a6a46 --- /dev/null +++ b/src/test/rustdoc-json/fn_pointer/abi.rs @@ -0,0 +1,25 @@ +// ignore-tidy-linelength + +#![feature(abi_vectorcall)] +#![feature(c_unwind)] + +// @is abi.json "$.index[*][?(@.name=='AbiRust')].inner.type.inner.header.abi" \"Rust\" +pub type AbiRust = fn(); + +// @is - "$.index[*][?(@.name=='AbiC')].inner.type.inner.header.abi" '{"C": {"unwind": false}}' +pub type AbiC = extern "C" fn(); + +// @is - "$.index[*][?(@.name=='AbiSystem')].inner.type.inner.header.abi" '{"System": {"unwind": false}}' +pub type AbiSystem = extern "system" fn(); + +// @is - "$.index[*][?(@.name=='AbiCUnwind')].inner.type.inner.header.abi" '{"C": {"unwind": true}}' +pub type AbiCUnwind = extern "C-unwind" fn(); + +// @is - "$.index[*][?(@.name=='AbiSystemUnwind')].inner.type.inner.header.abi" '{"System": {"unwind": true}}' +pub type AbiSystemUnwind = extern "system-unwind" fn(); + +// @is - "$.index[*][?(@.name=='AbiVecorcall')].inner.type.inner.header.abi.Other" '"\"vectorcall\""' +pub type AbiVecorcall = extern "vectorcall" fn(); + +// @is - "$.index[*][?(@.name=='AbiVecorcallUnwind')].inner.type.inner.header.abi.Other" '"\"vectorcall-unwind\""' +pub type AbiVecorcallUnwind = extern "vectorcall-unwind" fn(); diff --git a/src/test/rustdoc-json/fn_pointer/header.rs b/src/test/rustdoc-json/fn_pointer/header.rs deleted file mode 100644 index a5038e0cd2aa8..0000000000000 --- a/src/test/rustdoc-json/fn_pointer/header.rs +++ /dev/null @@ -1,5 +0,0 @@ -// @has header.json "$.index[*][?(@.name=='FnPointer')].inner.type.inner.header" "[]" -pub type FnPointer = fn(); - -// @has - "$.index[*][?(@.name=='UnsafePointer')].inner.type.inner.header" '["unsafe"]' -pub type UnsafePointer = unsafe fn(); diff --git a/src/test/rustdoc-json/fn_pointer/qualifiers.rs b/src/test/rustdoc-json/fn_pointer/qualifiers.rs new file mode 100644 index 0000000000000..3819220853639 --- /dev/null +++ b/src/test/rustdoc-json/fn_pointer/qualifiers.rs @@ -0,0 +1,9 @@ +// @is qualifiers.json "$.index[*][?(@.name=='FnPointer')].inner.type.inner.header.unsafe" false +// @is - "$.index[*][?(@.name=='FnPointer')].inner.type.inner.header.const" false +// @is - "$.index[*][?(@.name=='FnPointer')].inner.type.inner.header.async" false +pub type FnPointer = fn(); + +// @is - "$.index[*][?(@.name=='UnsafePointer')].inner.type.inner.header.unsafe" true +// @is - "$.index[*][?(@.name=='UnsafePointer')].inner.type.inner.header.const" false +// @is - "$.index[*][?(@.name=='UnsafePointer')].inner.type.inner.header.async" false +pub type UnsafePointer = unsafe fn(); diff --git a/src/test/rustdoc-json/fns/abi.rs b/src/test/rustdoc-json/fns/abi.rs new file mode 100644 index 0000000000000..16b579130656a --- /dev/null +++ b/src/test/rustdoc-json/fns/abi.rs @@ -0,0 +1,25 @@ +// ignore-tidy-linelength + +#![feature(abi_vectorcall)] +#![feature(c_unwind)] + +// @is abi.json "$.index[*][?(@.name=='abi_rust')].inner.header.abi" \"Rust\" +pub fn abi_rust() {} + +// @is - "$.index[*][?(@.name=='abi_c')].inner.header.abi" '{"C": {"unwind": false}}' +pub extern "C" fn abi_c() {} + +// @is - "$.index[*][?(@.name=='abi_system')].inner.header.abi" '{"System": {"unwind": false}}' +pub extern "system" fn abi_system() {} + +// @is - "$.index[*][?(@.name=='abi_c_unwind')].inner.header.abi" '{"C": {"unwind": true}}' +pub extern "C-unwind" fn abi_c_unwind() {} + +// @is - "$.index[*][?(@.name=='abi_system_unwind')].inner.header.abi" '{"System": {"unwind": true}}' +pub extern "system-unwind" fn abi_system_unwind() {} + +// @is - "$.index[*][?(@.name=='abi_vectorcall')].inner.header.abi.Other" '"\"vectorcall\""' +pub extern "vectorcall" fn abi_vectorcall() {} + +// @is - "$.index[*][?(@.name=='abi_vectorcall_unwind')].inner.header.abi.Other" '"\"vectorcall-unwind\""' +pub extern "vectorcall-unwind" fn abi_vectorcall_unwind() {} diff --git a/src/test/rustdoc-json/fns/header.rs b/src/test/rustdoc-json/fns/header.rs deleted file mode 100644 index 29741dd50dadc..0000000000000 --- a/src/test/rustdoc-json/fns/header.rs +++ /dev/null @@ -1,22 +0,0 @@ -// edition:2018 - -// @has header.json "$.index[*][?(@.name=='nothing_fn')].inner.header" "[]" -pub fn nothing_fn() {} - -// @has - "$.index[*][?(@.name=='const_fn')].inner.header" '["const"]' -pub const fn const_fn() {} - -// @has - "$.index[*][?(@.name=='async_fn')].inner.header" '["async"]' -pub async fn async_fn() {} - -// @count - "$.index[*][?(@.name=='async_unsafe_fn')].inner.header[*]" 2 -// @has - "$.index[*][?(@.name=='async_unsafe_fn')].inner.header[*]" '"async"' -// @has - "$.index[*][?(@.name=='async_unsafe_fn')].inner.header[*]" '"unsafe"' -pub async unsafe fn async_unsafe_fn() {} - -// @count - "$.index[*][?(@.name=='const_unsafe_fn')].inner.header[*]" 2 -// @has - "$.index[*][?(@.name=='const_unsafe_fn')].inner.header[*]" '"const"' -// @has - "$.index[*][?(@.name=='const_unsafe_fn')].inner.header[*]" '"unsafe"' -pub const unsafe fn const_unsafe_fn() {} - -// It's impossible for a function to be both const and async, so no test for that diff --git a/src/test/rustdoc-json/fns/qualifiers.rs b/src/test/rustdoc-json/fns/qualifiers.rs new file mode 100644 index 0000000000000..5cb3b43e66a96 --- /dev/null +++ b/src/test/rustdoc-json/fns/qualifiers.rs @@ -0,0 +1,33 @@ +// edition:2018 + +// @is qualifiers.json "$.index[*][?(@.name=='nothing_fn')].inner.header.async" false +// @is - "$.index[*][?(@.name=='nothing_fn')].inner.header.const" false +// @is - "$.index[*][?(@.name=='nothing_fn')].inner.header.unsafe" false +pub fn nothing_fn() {} + +// @is - "$.index[*][?(@.name=='unsafe_fn')].inner.header.async" false +// @is - "$.index[*][?(@.name=='unsafe_fn')].inner.header.const" false +// @is - "$.index[*][?(@.name=='unsafe_fn')].inner.header.unsafe" true +pub unsafe fn unsafe_fn() {} + +// @is - "$.index[*][?(@.name=='const_fn')].inner.header.async" false +// @is - "$.index[*][?(@.name=='const_fn')].inner.header.const" true +// @is - "$.index[*][?(@.name=='const_fn')].inner.header.unsafe" false +pub const fn const_fn() {} + +// @is - "$.index[*][?(@.name=='async_fn')].inner.header.async" true +// @is - "$.index[*][?(@.name=='async_fn')].inner.header.const" false +// @is - "$.index[*][?(@.name=='async_fn')].inner.header.unsafe" false +pub async fn async_fn() {} + +// @is - "$.index[*][?(@.name=='async_unsafe_fn')].inner.header.async" true +// @is - "$.index[*][?(@.name=='async_unsafe_fn')].inner.header.const" false +// @is - "$.index[*][?(@.name=='async_unsafe_fn')].inner.header.unsafe" true +pub async unsafe fn async_unsafe_fn() {} + +// @is - "$.index[*][?(@.name=='const_unsafe_fn')].inner.header.async" false +// @is - "$.index[*][?(@.name=='const_unsafe_fn')].inner.header.const" true +// @is - "$.index[*][?(@.name=='const_unsafe_fn')].inner.header.unsafe" true +pub const unsafe fn const_unsafe_fn() {} + +// It's impossible for a function to be both const and async, so no test for that diff --git a/src/test/rustdoc-json/method_abi.rs b/src/test/rustdoc-json/method_abi.rs deleted file mode 100644 index 6fabbc836117b..0000000000000 --- a/src/test/rustdoc-json/method_abi.rs +++ /dev/null @@ -1,25 +0,0 @@ -// @has method_abi.json "$.index[*][?(@.name=='Foo')]" -pub struct Foo; - -impl Foo { - // @has - "$.index[*][?(@.name=='abi_rust')].inner.abi" '"\"Rust\""' - pub fn abi_rust() {} - - // @has - "$.index[*][?(@.name=='abi_c')].inner.abi" '"\"C\""' - pub extern "C" fn abi_c() {} - - // @has - "$.index[*][?(@.name=='abi_system')].inner.abi" '"\"system\""' - pub extern "system" fn abi_system() {} -} - -// @has method_abi.json "$.index[*][?(@.name=='Bar')]" -pub trait Bar { - // @has - "$.index[*][?(@.name=='trait_abi_rust')].inner.abi" '"\"Rust\""' - fn trait_abi_rust(); - - // @has - "$.index[*][?(@.name=='trait_abi_c')].inner.abi" '"\"C\""' - extern "C" fn trait_abi_c(); - - // @has - "$.index[*][?(@.name=='trait_abi_system')].inner.abi" '"\"system\""' - extern "system" fn trait_abi_system(); -} diff --git a/src/test/rustdoc-json/methods/abi.rs b/src/test/rustdoc-json/methods/abi.rs new file mode 100644 index 0000000000000..07b01d03bf6e3 --- /dev/null +++ b/src/test/rustdoc-json/methods/abi.rs @@ -0,0 +1,55 @@ +// ignore-tidy-linelength + +#![feature(abi_vectorcall)] +#![feature(c_unwind)] +#![feature(no_core)] +#![no_core] + +// @has abi.json "$.index[*][?(@.name=='Foo')]" +pub struct Foo; + +impl Foo { + // @is - "$.index[*][?(@.name=='abi_rust')].inner.header.abi" \"Rust\" + pub fn abi_rust() {} + + // @is - "$.index[*][?(@.name=='abi_c')].inner.header.abi" '{"C": {"unwind": false}}' + pub extern "C" fn abi_c() {} + + // @is - "$.index[*][?(@.name=='abi_system')].inner.header.abi" '{"System": {"unwind": false}}' + pub extern "system" fn abi_system() {} + + // @is - "$.index[*][?(@.name=='abi_c_unwind')].inner.header.abi" '{"C": {"unwind": true}}' + pub extern "C-unwind" fn abi_c_unwind() {} + + // @is - "$.index[*][?(@.name=='abi_system_unwind')].inner.header.abi" '{"System": {"unwind": true}}' + pub extern "system-unwind" fn abi_system_unwind() {} + + // @is - "$.index[*][?(@.name=='abi_vectorcall')].inner.header.abi.Other" '"\"vectorcall\""' + pub extern "vectorcall" fn abi_vectorcall() {} + + // @is - "$.index[*][?(@.name=='abi_vectorcall_unwind')].inner.header.abi.Other" '"\"vectorcall-unwind\""' + pub extern "vectorcall-unwind" fn abi_vectorcall_unwind() {} +} + +pub trait Bar { + // @is - "$.index[*][?(@.name=='trait_abi_rust')].inner.header.abi" \"Rust\" + fn trait_abi_rust() {} + + // @is - "$.index[*][?(@.name=='trait_abi_c')].inner.header.abi" '{"C": {"unwind": false}}' + extern "C" fn trait_abi_c() {} + + // @is - "$.index[*][?(@.name=='trait_abi_system')].inner.header.abi" '{"System": {"unwind": false}}' + extern "system" fn trait_abi_system() {} + + // @is - "$.index[*][?(@.name=='trait_abi_c_unwind')].inner.header.abi" '{"C": {"unwind": true}}' + extern "C-unwind" fn trait_abi_c_unwind() {} + + // @is - "$.index[*][?(@.name=='trait_abi_system_unwind')].inner.header.abi" '{"System": {"unwind": true}}' + extern "system-unwind" fn trait_abi_system_unwind() {} + + // @is - "$.index[*][?(@.name=='trait_abi_vectorcall')].inner.header.abi.Other" '"\"vectorcall\""' + extern "vectorcall" fn trait_abi_vectorcall() {} + + // @is - "$.index[*][?(@.name=='trait_abi_vectorcall_unwind')].inner.header.abi.Other" '"\"vectorcall-unwind\""' + extern "vectorcall-unwind" fn trait_abi_vectorcall_unwind() {} +} diff --git a/src/test/rustdoc-json/methods/header.rs b/src/test/rustdoc-json/methods/header.rs deleted file mode 100644 index 50a3db75ef395..0000000000000 --- a/src/test/rustdoc-json/methods/header.rs +++ /dev/null @@ -1,26 +0,0 @@ -// edition:2018 - -pub struct Foo; - -impl Foo { - // @has header.json "$.index[*][?(@.name=='nothing_meth')].inner.header" "[]" - pub fn nothing_meth() {} - - // @has - "$.index[*][?(@.name=='const_meth')].inner.header" '["const"]' - pub const fn const_meth() {} - - // @has - "$.index[*][?(@.name=='async_meth')].inner.header" '["async"]' - pub async fn async_meth() {} - - // @count - "$.index[*][?(@.name=='async_unsafe_meth')].inner.header[*]" 2 - // @has - "$.index[*][?(@.name=='async_unsafe_meth')].inner.header[*]" '"async"' - // @has - "$.index[*][?(@.name=='async_unsafe_meth')].inner.header[*]" '"unsafe"' - pub async unsafe fn async_unsafe_meth() {} - - // @count - "$.index[*][?(@.name=='const_unsafe_meth')].inner.header[*]" 2 - // @has - "$.index[*][?(@.name=='const_unsafe_meth')].inner.header[*]" '"const"' - // @has - "$.index[*][?(@.name=='const_unsafe_meth')].inner.header[*]" '"unsafe"' - pub const unsafe fn const_unsafe_meth() {} - - // It's impossible for a method to be both const and async, so no test for that -} diff --git a/src/test/rustdoc-json/methods/qualifiers.rs b/src/test/rustdoc-json/methods/qualifiers.rs new file mode 100644 index 0000000000000..af36d36b6607d --- /dev/null +++ b/src/test/rustdoc-json/methods/qualifiers.rs @@ -0,0 +1,37 @@ +// edition:2018 + +pub struct Foo; + +impl Foo { + // @is qualifiers.json "$.index[*][?(@.name=='const_meth')].inner.header.async" false + // @is - "$.index[*][?(@.name=='const_meth')].inner.header.const" true + // @is - "$.index[*][?(@.name=='const_meth')].inner.header.unsafe" false + pub const fn const_meth() {} + + // @is - "$.index[*][?(@.name=='nothing_meth')].inner.header.async" false + // @is - "$.index[*][?(@.name=='nothing_meth')].inner.header.const" false + // @is - "$.index[*][?(@.name=='nothing_meth')].inner.header.unsafe" false + pub fn nothing_meth() {} + + // @is - "$.index[*][?(@.name=='unsafe_meth')].inner.header.async" false + // @is - "$.index[*][?(@.name=='unsafe_meth')].inner.header.const" false + // @is - "$.index[*][?(@.name=='unsafe_meth')].inner.header.unsafe" true + pub unsafe fn unsafe_meth() {} + + // @is - "$.index[*][?(@.name=='async_meth')].inner.header.async" true + // @is - "$.index[*][?(@.name=='async_meth')].inner.header.const" false + // @is - "$.index[*][?(@.name=='async_meth')].inner.header.unsafe" false + pub async fn async_meth() {} + + // @is - "$.index[*][?(@.name=='async_unsafe_meth')].inner.header.async" true + // @is - "$.index[*][?(@.name=='async_unsafe_meth')].inner.header.const" false + // @is - "$.index[*][?(@.name=='async_unsafe_meth')].inner.header.unsafe" true + pub async unsafe fn async_unsafe_meth() {} + + // @is - "$.index[*][?(@.name=='const_unsafe_meth')].inner.header.async" false + // @is - "$.index[*][?(@.name=='const_unsafe_meth')].inner.header.const" true + // @is - "$.index[*][?(@.name=='const_unsafe_meth')].inner.header.unsafe" true + pub const unsafe fn const_unsafe_meth() {} + + // It's impossible for a method to be both const and async, so no test for that +} diff --git a/src/test/rustdoc/infinite-redirection.rs b/src/test/rustdoc/infinite-redirection.rs new file mode 100644 index 0000000000000..96a43323ce29d --- /dev/null +++ b/src/test/rustdoc/infinite-redirection.rs @@ -0,0 +1,29 @@ +#![crate_name = "foo"] + +// This test ensures that there is no "infinite redirection" file generated (a +// file which redirects to itself). + +// We check it's not a redirection file. +// @has 'foo/builders/struct.ActionRowBuilder.html' +// @has - '//*[@id="synthetic-implementations"]' 'Auto Trait Implementations' + +// And that the link in the module is targetting it. +// @has 'foo/builders/index.html' +// @has - '//a[@href="struct.ActionRowBuilder.html"]' 'ActionRowBuilder' + +mod auto { + mod action_row { + pub struct ActionRowBuilder; + } + + #[doc(hidden)] + pub mod builders { + pub use super::action_row::ActionRowBuilder; + } +} + +pub use auto::*; + +pub mod builders { + pub use crate::auto::builders::*; +} diff --git a/src/test/ui/check-cfg/invalid-cfg-name.stderr b/src/test/ui/check-cfg/invalid-cfg-name.stderr index 2587685afa048..2bd1821c9422b 100644 --- a/src/test/ui/check-cfg/invalid-cfg-name.stderr +++ b/src/test/ui/check-cfg/invalid-cfg-name.stderr @@ -2,7 +2,7 @@ warning: unexpected `cfg` condition name --> $DIR/invalid-cfg-name.rs:7:7 | LL | #[cfg(widnows)] - | ^^^^^^^ + | ^^^^^^^ help: did you mean: `windows` | = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/src/test/ui/check-cfg/invalid-cfg-value.stderr b/src/test/ui/check-cfg/invalid-cfg-value.stderr index c591d8474a261..bc2c053fed65a 100644 --- a/src/test/ui/check-cfg/invalid-cfg-value.stderr +++ b/src/test/ui/check-cfg/invalid-cfg-value.stderr @@ -5,6 +5,7 @@ LL | #[cfg(feature = "sedre")] | ^^^^^^^^^^^^^^^^^ | = note: `#[warn(unexpected_cfgs)]` on by default + = note: expected values for `feature` are: full, rand, serde warning: 1 warning emitted diff --git a/src/test/ui/check-cfg/mix.rs b/src/test/ui/check-cfg/mix.rs new file mode 100644 index 0000000000000..2e8a4bb1b9100 --- /dev/null +++ b/src/test/ui/check-cfg/mix.rs @@ -0,0 +1,52 @@ +// This test checks the combination of well known names, their activation via names(), the usage of +// partial values() with a --cfg and test that we also correctly lint on the `cfg!` macro and +// `cfg_attr` attribute. +// +// check-pass +// compile-flags: --check-cfg=names() --check-cfg=values(feature,"foo") --cfg feature="bar" -Z unstable-options + +#[cfg(windows)] +fn do_windows_stuff() {} + +#[cfg(widnows)] +//~^ WARNING unexpected `cfg` condition name +fn do_windows_stuff() {} + +#[cfg(feature = "foo")] +fn use_foo() {} + +#[cfg(feature = "bar")] +fn use_bar() {} + +#[cfg(feature = "zebra")] +//~^ WARNING unexpected `cfg` condition value +fn use_zebra() {} + +#[cfg_attr(uu, test)] +//~^ WARNING unexpected `cfg` condition name +fn do_test() {} + +#[cfg_attr(feature = "foo", no_mangle)] +fn do_test_foo() {} + +fn test_cfg_macro() { + cfg!(windows); + cfg!(widnows); + //~^ WARNING unexpected `cfg` condition name + cfg!(feature = "foo"); + cfg!(feature = "bar"); + cfg!(feature = "zebra"); + //~^ WARNING unexpected `cfg` condition value + cfg!(xxx = "foo"); + //~^ WARNING unexpected `cfg` condition name + cfg!(xxx); + //~^ WARNING unexpected `cfg` condition name + cfg!(any(windows, xxx)); + //~^ WARNING unexpected `cfg` condition name + cfg!(any(xxx, windows)); + //~^ WARNING unexpected `cfg` condition name + cfg!(any(feature = "bad", windows)); + //~^ WARNING unexpected `cfg` condition value +} + +fn main() {} diff --git a/src/test/ui/check-cfg/mix.stderr b/src/test/ui/check-cfg/mix.stderr new file mode 100644 index 0000000000000..e913aa3301eaf --- /dev/null +++ b/src/test/ui/check-cfg/mix.stderr @@ -0,0 +1,72 @@ +warning: unexpected `cfg` condition name + --> $DIR/mix.rs:11:7 + | +LL | #[cfg(widnows)] + | ^^^^^^^ help: did you mean: `windows` + | + = note: `#[warn(unexpected_cfgs)]` on by default + +warning: unexpected `cfg` condition value + --> $DIR/mix.rs:21:7 + | +LL | #[cfg(feature = "zebra")] + | ^^^^^^^^^^^^^^^^^ + | + = note: expected values for `feature` are: bar, foo + +warning: unexpected `cfg` condition name + --> $DIR/mix.rs:25:12 + | +LL | #[cfg_attr(uu, test)] + | ^^ + +warning: unexpected `cfg` condition name + --> $DIR/mix.rs:34:10 + | +LL | cfg!(widnows); + | ^^^^^^^ help: did you mean: `windows` + +warning: unexpected `cfg` condition value + --> $DIR/mix.rs:38:10 + | +LL | cfg!(feature = "zebra"); + | ^^^^^^^^^^^^^^^^^ + | + = note: expected values for `feature` are: bar, foo + +warning: unexpected `cfg` condition name + --> $DIR/mix.rs:40:10 + | +LL | cfg!(xxx = "foo"); + | ^^^^^^^^^^^ + +warning: unexpected `cfg` condition name + --> $DIR/mix.rs:42:10 + | +LL | cfg!(xxx); + | ^^^ + +warning: unexpected `cfg` condition name + --> $DIR/mix.rs:44:23 + | +LL | cfg!(any(windows, xxx)); + | ^^^ + +warning: unexpected `cfg` condition name + --> $DIR/mix.rs:46:14 + | +LL | cfg!(any(xxx, windows)); + | ^^^ + +warning: unexpected `cfg` condition value + --> $DIR/mix.rs:48:14 + | +LL | cfg!(any(feature = "bad", windows)); + | ^^^^^^^^^^----- + | | + | help: did you mean: `"bar"` + | + = note: expected values for `feature` are: bar, foo + +warning: 10 warnings emitted + diff --git a/src/test/ui/check-cfg/no-values.rs b/src/test/ui/check-cfg/no-values.rs new file mode 100644 index 0000000000000..2440757e52da9 --- /dev/null +++ b/src/test/ui/check-cfg/no-values.rs @@ -0,0 +1,10 @@ +// Check that we detect unexpected value when none are allowed +// +// check-pass +// compile-flags: --check-cfg=values(feature) -Z unstable-options + +#[cfg(feature = "foo")] +//~^ WARNING unexpected `cfg` condition value +fn do_foo() {} + +fn main() {} diff --git a/src/test/ui/check-cfg/no-values.stderr b/src/test/ui/check-cfg/no-values.stderr new file mode 100644 index 0000000000000..ea1c9107d4c2f --- /dev/null +++ b/src/test/ui/check-cfg/no-values.stderr @@ -0,0 +1,11 @@ +warning: unexpected `cfg` condition value + --> $DIR/no-values.rs:6:7 + | +LL | #[cfg(feature = "foo")] + | ^^^^^^^^^^^^^^^ + | + = note: `#[warn(unexpected_cfgs)]` on by default + = note: no expected value for `feature` + +warning: 1 warning emitted + diff --git a/src/test/ui/check-cfg/well-known-names.rs b/src/test/ui/check-cfg/well-known-names.rs new file mode 100644 index 0000000000000..a66568a2ffdc9 --- /dev/null +++ b/src/test/ui/check-cfg/well-known-names.rs @@ -0,0 +1,27 @@ +// This test checks that we lint on non well known names and that we don't lint on well known names +// +// check-pass +// compile-flags: --check-cfg=names() -Z unstable-options + +#[cfg(target_oz = "linux")] +//~^ WARNING unexpected `cfg` condition name +fn target_os_misspell() {} + +#[cfg(target_os = "linux")] +fn target_os() {} + +#[cfg(features = "foo")] +//~^ WARNING unexpected `cfg` condition name +fn feature_misspell() {} + +#[cfg(feature = "foo")] +fn feature() {} + +#[cfg(uniw)] +//~^ WARNING unexpected `cfg` condition name +fn unix_misspell() {} + +#[cfg(unix)] +fn unix() {} + +fn main() {} diff --git a/src/test/ui/check-cfg/well-known-names.stderr b/src/test/ui/check-cfg/well-known-names.stderr new file mode 100644 index 0000000000000..bdbe4d29d30fe --- /dev/null +++ b/src/test/ui/check-cfg/well-known-names.stderr @@ -0,0 +1,26 @@ +warning: unexpected `cfg` condition name + --> $DIR/well-known-names.rs:6:7 + | +LL | #[cfg(target_oz = "linux")] + | ---------^^^^^^^^^^ + | | + | help: did you mean: `target_os` + | + = note: `#[warn(unexpected_cfgs)]` on by default + +warning: unexpected `cfg` condition name + --> $DIR/well-known-names.rs:13:7 + | +LL | #[cfg(features = "foo")] + | --------^^^^^^^^ + | | + | help: did you mean: `feature` + +warning: unexpected `cfg` condition name + --> $DIR/well-known-names.rs:20:7 + | +LL | #[cfg(uniw)] + | ^^^^ help: did you mean: `unix` + +warning: 3 warnings emitted + diff --git a/src/test/ui/feature-gates/gated-bad-feature.stderr b/src/test/ui/feature-gates/gated-bad-feature.stderr index a8ec9391523c7..477bf5e866eef 100644 --- a/src/test/ui/feature-gates/gated-bad-feature.stderr +++ b/src/test/ui/feature-gates/gated-bad-feature.stderr @@ -20,13 +20,13 @@ error: malformed `feature` attribute input --> $DIR/gated-bad-feature.rs:5:1 | LL | #![feature] - | ^^^^^^^^^^^ help: must be of the form: `#![feature(name1, name1, ...)]` + | ^^^^^^^^^^^ help: must be of the form: `#![feature(name1, name2, ...)]` error: malformed `feature` attribute input --> $DIR/gated-bad-feature.rs:6:1 | LL | #![feature = "foo"] - | ^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![feature(name1, name1, ...)]` + | ^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![feature(name1, name2, ...)]` error: aborting due to 5 previous errors diff --git a/src/test/ui/suggestions/path-display.rs b/src/test/ui/suggestions/path-display.rs index 62fc9e79f7aa2..3a022e6b02fa4 100644 --- a/src/test/ui/suggestions/path-display.rs +++ b/src/test/ui/suggestions/path-display.rs @@ -1,7 +1,11 @@ -use std::path::Path; +use std::path::{Path, PathBuf}; fn main() { let path = Path::new("/tmp/foo/bar.txt"); println!("{}", path); //~^ ERROR E0277 + + let path = PathBuf::from("/tmp/foo/bar.txt"); + println!("{}", path); + //~^ ERROR E0277 } diff --git a/src/test/ui/suggestions/path-display.stderr b/src/test/ui/suggestions/path-display.stderr index 25c73c4c8741a..5e718d79307a8 100644 --- a/src/test/ui/suggestions/path-display.stderr +++ b/src/test/ui/suggestions/path-display.stderr @@ -8,6 +8,16 @@ LL | println!("{}", path); = note: call `.display()` or `.to_string_lossy()` to safely print paths, as they may contain non-Unicode data = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to previous error +error[E0277]: `PathBuf` doesn't implement `std::fmt::Display` + --> $DIR/path-display.rs:9:20 + | +LL | println!("{}", path); + | ^^^^ `PathBuf` cannot be formatted with the default formatter; call `.display()` on it + | + = help: the trait `std::fmt::Display` is not implemented for `PathBuf` + = note: call `.display()` or `.to_string_lossy()` to safely print paths, as they may contain non-Unicode data + = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/src/tools/lld-wrapper/src/main.rs b/src/tools/lld-wrapper/src/main.rs index 1601bf1b34e9c..8d19a054a1d25 100644 --- a/src/tools/lld-wrapper/src/main.rs +++ b/src/tools/lld-wrapper/src/main.rs @@ -8,7 +8,7 @@ //! In Rust with `-Z gcc-ld=lld` we have gcc or clang invoke rust-lld. Since there is no way to //! make gcc/clang pass `-flavor ` as the first two arguments in the linker invocation //! and since Windows does not support symbolic links for files this wrapper is used in place of a -//! symblic link. It execs `../rust-lld -flavor ld` if the feature `ld` is enabled and +//! symbolic link. It execs `../rust-lld -flavor ld` if the feature `ld` is enabled and //! `../rust-lld -flavor ld64` if `ld64` is enabled. On Windows it spawns a `..\rust-lld.exe` //! child process.