# created during manual debugging and many people like to clean up instead of
# having git ignore such leftovers. You can use `.git/info/exclude` to
# configure your local ignore list.
-# FIXME: This needs cleanup.
-*~
+
+## File system
+.DS_Store
+desktop.ini
+
+## Editor
*.swp
*.swo
-.#*
-.DS_Store
+Session.vim
.cproject
-.hg/
-.hgignore
.idea
*.iml
-__pycache__/
-*.py[cod]
-*$py.class
+.vscode
.project
+.favorites.json
.settings/
+
+## Tool
.valgrindrc
-.vscode
-.favorites.json
-/Makefile
-/build/
+.cargo
+# Included because it is part of the test case
+!/src/test/run-make/thumb-none-qemu/example/.cargo
+
+## Configuration
/config.toml
-/dist/
+/Makefile
+config.mk
+config.stamp
+no_llvm_build
+
+## Build
/dl/
/doc/
/inst/
/llvm/
/mingw-build/
-/src/tools/x/target
-# Created by default with `src/ci/docker/run.sh`:
-/obj/
+/build/
+/dist/
/unicode-downloads
/target
-# Generated by compiletest for incremental:
+/src/tools/x/target
+# Generated by compiletest for incremental
/tmp/
+# Created by default with `src/ci/docker/run.sh`
+/obj/
+
+## Temporary files
+*~
+\#*
+\#*\#
+.#*
+
+## Tags
tags
tags.*
TAGS
TAGS.*
-\#*
-\#*\#
-config.mk
-config.stamp
-Session.vim
-.cargo
-!/src/test/run-make/thumb-none-qemu/example/.cargo
-no_llvm_build
+
+## Python
+__pycache__/
+*.py[cod]
+*$py.class
+
+## Node
**node_modules
**package-lock.json
+
# Before adding new lines, see the comment at the top.
[submodule "src/llvm-project"]
path = src/llvm-project
url = https://github.com/rust-lang/llvm-project.git
- branch = rustc/12.0-2021-02-03
+ branch = rustc/12.0-2021-04-15
[submodule "src/doc/embedded-book"]
path = src/doc/embedded-book
url = https://github.com/rust-embedded/book.git
checksum = "8f8cb5d814eb646a863c4f24978cff2880c4be96ad8cde2c0f0678732902e271"
[[package]]
-name = "arrayref"
-version = "0.3.6"
+name = "arrayvec"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
+checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "arrayvec"
-version = "0.5.1"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
+checksum = "5a2f58b0bb10c380af2b26e57212856b8c9a59e0925b4c20f4a174a49734eaf7"
[[package]]
name = "atty"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
-[[package]]
-name = "base64"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
-
[[package]]
name = "bitflags"
version = "1.2.1"
"typenum",
]
-[[package]]
-name = "blake2b_simd"
-version = "0.5.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
-dependencies = [
- "arrayref",
- "arrayvec",
- "constant_time_eq",
-]
-
[[package]]
name = "block-buffer"
version = "0.7.3"
"cargo-util",
"clap",
"crates-io",
- "crossbeam-utils 0.8.0",
+ "crossbeam-utils 0.8.3",
"curl",
"curl-sys",
"env_logger 0.8.1",
"winapi 0.3.9",
]
-[[package]]
-name = "const_fn"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c478836e029dcef17fb47c89023448c64f781a046e0300e257ad8225ae59afab"
-
-[[package]]
-name = "constant_time_eq"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
-
[[package]]
name = "core"
version = "0.0.0"
checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
dependencies = [
"cfg-if 1.0.0",
- "crossbeam-utils 0.8.0",
+ "crossbeam-utils 0.8.3",
]
[[package]]
[[package]]
name = "crossbeam-utils"
-version = "0.8.0"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec91540d98355f690a86367e566ecad2e9e579f230230eb7c21398372be73ea5"
+checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
dependencies = [
"autocfg",
"cfg-if 1.0.0",
- "const_fn",
"lazy_static",
]
[[package]]
name = "directories"
-version = "3.0.1"
+version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8fed639d60b58d0f53498ab13d26f621fd77569cc6edb031f4cc36a2ad9da0f"
+checksum = "e69600ff1703123957937708eb27f7a564e48885c537782722ed0ba3189ce1d7"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
-version = "0.3.5"
+version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a"
+checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
dependencies = [
"libc",
- "redox_users 0.3.4",
+ "redox_users",
"winapi 0.3.9",
]
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
dependencies = [
"libc",
- "redox_users 0.4.0",
+ "redox_users",
"winapi 0.3.9",
]
[[package]]
name = "fst"
-version = "0.3.5"
+version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "927fb434ff9f0115b215dc0efd2e4fbdd7448522a92a1aa37c77d6a2f8f1ebd6"
-dependencies = [
- "byteorder",
-]
+checksum = "d79238883cf0307100b90aba4a755d8051a3182305dfe7f649a1e9dc0517006f"
[[package]]
name = "fuchsia-zircon"
[[package]]
name = "ignore"
-version = "0.4.16"
+version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22dcbf2a4a289528dbef21686354904e1c694ac642610a9bff9e7df730d9ec72"
+checksum = "b287fb45c60bb826a0dc68ff08742b9d88a2fea13d6e0c286b3172065aaf878c"
dependencies = [
- "crossbeam-utils 0.7.2",
+ "crossbeam-utils 0.8.3",
"globset",
"lazy_static",
"log",
[[package]]
name = "jobserver"
-version = "0.1.21"
+version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2"
+checksum = "972f5ae5d1cb9c6ae417789196c803205313edde988685da5e3aae0827b9e7fd"
dependencies = [
"libc",
]
[[package]]
name = "json"
-version = "0.11.15"
+version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92c245af8786f6ac35f95ca14feca9119e71339aaab41e878e7cdd655c97e9e5"
+checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
[[package]]
name = "jsondocck"
"parking_lot",
"perf-event-open-sys",
"rustc-hash",
- "smallvec 1.6.1",
+ "smallvec",
]
[[package]]
"rustc-workspace-hack",
"rustc_version",
"shell-escape",
- "smallvec 1.6.1",
+ "smallvec",
]
[[package]]
"instant",
"libc",
"redox_syscall 0.2.5",
- "smallvec 1.6.1",
+ "smallvec",
"winapi 0.3.9",
]
"bitflags",
]
-[[package]]
-name = "redox_users"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431"
-dependencies = [
- "getrandom 0.1.14",
- "redox_syscall 0.1.57",
- "rust-argon2",
-]
-
[[package]]
name = "redox_users"
version = "0.4.0"
"futures 0.3.12",
"heck",
"home",
- "itertools 0.8.2",
+ "itertools 0.9.0",
"jsonrpc-core",
"lazy_static",
"log",
[[package]]
name = "rls-analysis"
-version = "0.18.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "534032993e1b60e5db934eab2dde54da7afd1e46c3465fddb2b29eb47cb1ed3a"
+version = "0.18.2"
dependencies = [
"derive-new",
+ "env_logger 0.7.1",
"fst",
- "itertools 0.8.2",
+ "itertools 0.9.0",
"json",
+ "lazy_static",
"log",
"rls-data",
"rls-span",
"rls-span",
]
-[[package]]
-name = "rust-argon2"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017"
-dependencies = [
- "base64",
- "blake2b_simd",
- "constant_time_eq",
- "crossbeam-utils 0.7.2",
-]
-
[[package]]
name = "rust-demangler"
version = "0.0.1"
checksum = "259cca0e975ecb05fd289ace45280c30ff792efc04e856a7f18b7fc86a3cb610"
dependencies = [
"rustc-ap-rustc_data_structures",
- "smallvec 1.6.1",
+ "smallvec",
]
[[package]]
"rustc-ap-rustc_macros",
"rustc-ap-rustc_serialize",
"rustc-ap-rustc_span",
- "smallvec 1.6.1",
+ "smallvec",
"tracing",
]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cbfa7f82517a1b2efe7106c864c3f930b1da8aff07a27fd317af2f36522fd2e"
dependencies = [
- "arrayvec",
+ "arrayvec 0.5.2",
"bitflags",
"cfg-if 0.1.10",
"crossbeam-utils 0.7.2",
"rustc-hash",
"rustc-rayon",
"rustc-rayon-core",
- "smallvec 1.6.1",
+ "smallvec",
"stable_deref_trait",
"stacker",
"tempfile",
"rustc-ap-rustc_serialize",
"rustc-ap-rustc_session",
"rustc-ap-rustc_span",
- "smallvec 1.6.1",
+ "smallvec",
"tracing",
]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6f53afc4f7111c82295cb7ea3878f520bbac6a2c5a12e125b4ca9156498cff"
dependencies = [
- "arrayvec",
+ "arrayvec 0.5.2",
"rustc-ap-rustc_macros",
"rustc-ap-rustc_serialize",
]
"rustc-ap-rustc_lexer",
"rustc-ap-rustc_session",
"rustc-ap-rustc_span",
- "smallvec 1.6.1",
+ "smallvec",
"tracing",
"unicode-normalization",
]
checksum = "d33c710120953c0214f47a6caf42064d7e241003b4af36c98a6d6156e70335f1"
dependencies = [
"indexmap",
- "smallvec 1.6.1",
+ "smallvec",
]
[[package]]
version = "1.0.0"
dependencies = [
"byteorder",
- "crossbeam-utils 0.7.2",
+ "crossbeam-utils 0.8.3",
"libc",
"libz-sys",
"proc-macro2",
"quote",
+ "rand_core 0.5.1",
"serde",
"serde_json",
- "smallvec 0.6.14",
- "smallvec 1.6.1",
+ "smallvec",
"syn",
"url 2.1.1",
"winapi 0.3.9",
version = "0.0.0"
dependencies = [
"bitflags",
- "smallvec 1.6.1",
+ "smallvec",
]
[[package]]
version = "0.0.0"
dependencies = [
"rustc_data_structures",
- "smallvec 1.6.1",
+ "smallvec",
]
[[package]]
"rustc_macros",
"rustc_serialize",
"rustc_span",
- "smallvec 1.6.1",
+ "smallvec",
"tracing",
]
"rustc_session",
"rustc_span",
"rustc_target",
- "smallvec 1.6.1",
+ "smallvec",
"tracing",
]
"rustc_session",
"rustc_span",
"rustc_target",
- "smallvec 1.6.1",
+ "smallvec",
"tracing",
]
"rustc_session",
"rustc_span",
"rustc_target",
- "smallvec 1.6.1",
+ "smallvec",
"snap",
"tracing",
]
name = "rustc_data_structures"
version = "0.0.0"
dependencies = [
- "arrayvec",
+ "arrayvec 0.7.0",
"bitflags",
"cfg-if 0.1.10",
- "crossbeam-utils 0.7.2",
+ "crossbeam-utils 0.8.3",
"ena",
"indexmap",
"jobserver",
"rustc_index",
"rustc_macros",
"rustc_serialize",
- "smallvec 1.6.1",
+ "smallvec",
"stable_deref_trait",
"stacker",
"tempfile",
"rustc_serialize",
"rustc_session",
"rustc_span",
- "smallvec 1.6.1",
+ "smallvec",
"tracing",
]
"rustc_serialize",
"rustc_span",
"rustc_target",
- "smallvec 1.6.1",
+ "smallvec",
"tracing",
]
name = "rustc_index"
version = "0.0.0"
dependencies = [
- "arrayvec",
+ "arrayvec 0.7.0",
"rustc_macros",
"rustc_serialize",
]
"rustc_session",
"rustc_span",
"rustc_target",
- "smallvec 1.6.1",
+ "smallvec",
"tracing",
]
"rustc_traits",
"rustc_ty_utils",
"rustc_typeck",
- "smallvec 1.6.1",
+ "smallvec",
"tempfile",
"tracing",
"winapi 0.3.9",
"rustc_session",
"rustc_span",
"rustc_target",
- "smallvec 1.6.1",
+ "smallvec",
"snap",
"stable_deref_trait",
"tracing",
"rustc_span",
"rustc_target",
"rustc_type_ir",
- "smallvec 1.6.1",
+ "smallvec",
"tracing",
]
"rustc_span",
"rustc_target",
"rustc_trait_selection",
- "smallvec 1.6.1",
+ "smallvec",
"tracing",
]
"rustc_span",
"rustc_target",
"rustc_trait_selection",
- "smallvec 1.6.1",
+ "smallvec",
"tracing",
]
"rustc_lexer",
"rustc_session",
"rustc_span",
- "smallvec 1.6.1",
+ "smallvec",
"tracing",
"unicode-normalization",
]
"rustc_serialize",
"rustc_session",
"rustc_span",
- "smallvec 1.6.1",
+ "smallvec",
"tracing",
]
"rustc_middle",
"rustc_session",
"rustc_span",
- "smallvec 1.6.1",
+ "smallvec",
"tracing",
]
dependencies = [
"indexmap",
"rustc_macros",
- "smallvec 1.6.1",
+ "smallvec",
]
[[package]]
"rustc_session",
"rustc_span",
"rustc_target",
- "smallvec 1.6.1",
+ "smallvec",
"tracing",
]
"rustc_middle",
"rustc_span",
"rustc_trait_selection",
- "smallvec 1.6.1",
+ "smallvec",
"tracing",
]
"rustc_span",
"rustc_target",
"rustc_trait_selection",
- "smallvec 1.6.1",
+ "rustc_ty_utils",
+ "smallvec",
"tracing",
]
name = "rustdoc"
version = "0.0.0"
dependencies = [
- "arrayvec",
+ "arrayvec 0.7.0",
"expect-test",
"itertools 0.9.0",
"minifier",
"rustdoc-json-types",
"serde",
"serde_json",
- "smallvec 1.6.1",
+ "smallvec",
"tempfile",
"tracing",
"tracing-subscriber",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
-[[package]]
-name = "smallvec"
-version = "0.6.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0"
-dependencies = [
- "maybe-uninit",
-]
-
[[package]]
name = "smallvec"
version = "1.6.1"
[[package]]
name = "tempfile"
-version = "3.1.0"
+version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
+checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if 1.0.0",
"libc",
- "rand 0.7.3",
- "redox_syscall 0.1.57",
+ "rand 0.8.3",
+ "redox_syscall 0.2.5",
"remove_dir_all",
"winapi 0.3.9",
]
version = "0.1.0"
dependencies = [
"cargo_metadata 0.11.1",
- "crossbeam-utils 0.8.0",
+ "crossbeam-utils 0.8.3",
"lazy_static",
"regex",
"walkdir",
"serde",
"serde_json",
"sharded-slab",
- "smallvec 1.6.1",
+ "smallvec",
"thread_local",
"tracing",
"tracing-core",
}
impl PartialEq<Symbol> for Path {
+ #[inline]
fn eq(&self, symbol: &Symbol) -> bool {
self.segments.len() == 1 && { self.segments[0].ident.name == *symbol }
}
Field(P<Expr>, Ident),
/// An indexing operation (e.g., `foo[2]`).
Index(P<Expr>, P<Expr>),
- /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assingment).
+ /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assignment).
Range(Option<P<Expr>>, Option<P<Expr>>, RangeLimits),
/// An underscore, used in destructuring assignment to ignore a value.
Underscore,
)]
#![feature(box_syntax)]
#![feature(box_patterns)]
-#![feature(const_fn)] // For the `transmute` in `P::new`
+#![cfg_attr(bootstrap, feature(const_fn))] // For the `transmute` in `P::new`
+#![cfg_attr(not(bootstrap), feature(const_fn_unsize))] // For the `transmute` in `P::new`
#![feature(const_fn_transmute)]
#![feature(const_panic)]
#![feature(crate_visibility_modifier)]
/// tokens.
///
/// For example, `#[cfg(FALSE)] struct Foo {}` would
-/// have an `attrs` field contaiing the `#[cfg(FALSE)]` attr,
+/// have an `attrs` field containing the `#[cfg(FALSE)]` attr,
/// and a `tokens` field storing the (unparesd) tokens `struct Foo {}`
#[derive(Clone, Debug, Encodable, Decodable)]
pub struct AttributesData {
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)))
}
AssocItemKind::Fn(box FnKind(_, ref sig, ref generics, Some(ref body))) => {
- let body_id = self.lower_fn_body_block(i.span, &sig.decl, Some(body));
- let (generics, sig) =
- self.lower_method_sig(generics, sig, trait_item_def_id, false, None, i.id);
+ let asyncness = sig.header.asyncness;
+ let body_id =
+ self.lower_maybe_async_body(i.span, &sig.decl, asyncness, Some(&body));
+ let (generics, sig) = self.lower_method_sig(
+ generics,
+ sig,
+ trait_item_def_id,
+ false,
+ asyncness.opt_return_id(),
+ i.id,
+ );
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)))
}
AssocItemKind::TyAlias(box TyAliasKind(_, ref generics, ref bounds, ref default)) => {
GenericParamKind::Type { default: _ } => (ParamKindOrd::Type, ident),
GenericParamKind::Const { ref ty, kw_span: _, default: _ } => {
let ty = pprust::ty_to_string(ty);
- let unordered = sess.features_untracked().const_generics;
+ let unordered = sess.features_untracked().unordered_const_ty_params();
(ParamKindOrd::Const { unordered }, Some(format!("const {}: {}", param.ident, ty)))
}
};
deny_equality_constraints(self, predicate, generics);
}
}
-
- visit::walk_generics(self, generics)
+ walk_list!(self, visit_generic_param, &generics.params);
+ for predicate in &generics.where_clause.predicates {
+ match predicate {
+ WherePredicate::BoundPredicate(bound_pred) => {
+ // A type binding, eg `for<'c> Foo: Send+Clone+'c`
+ self.check_late_bound_lifetime_defs(&bound_pred.bound_generic_params);
+
+ // This is slightly complicated. Our representation for poly-trait-refs contains a single
+ // binder and thus we only allow a single level of quantification. However,
+ // the syntax of Rust permits quantification in two places in where clauses,
+ // e.g., `T: for <'a> Foo<'a>` and `for <'a, 'b> &'b T: Foo<'a>`. If both are
+ // defined, then error.
+ if !bound_pred.bound_generic_params.is_empty() {
+ for bound in &bound_pred.bounds {
+ match bound {
+ GenericBound::Trait(t, _) => {
+ if !t.bound_generic_params.is_empty() {
+ struct_span_err!(
+ self.err_handler(),
+ t.span,
+ E0316,
+ "nested quantification of lifetimes"
+ )
+ .emit();
+ }
+ }
+ GenericBound::Outlives(_) => {}
+ }
+ }
+ }
+ }
+ _ => {}
+ }
+ self.visit_where_predicate(predicate);
+ }
}
fn visit_generic_param(&mut self, param: &'a GenericParam) {
visit::walk_pat(self, pat)
}
- fn visit_where_predicate(&mut self, p: &'a WherePredicate) {
- if let &WherePredicate::BoundPredicate(ref bound_predicate) = p {
- // A type binding, eg `for<'c> Foo: Send+Clone+'c`
- self.check_late_bound_lifetime_defs(&bound_predicate.bound_generic_params);
- }
- visit::walk_where_predicate(self, p);
- }
-
fn visit_poly_trait_ref(&mut self, t: &'a PolyTraitRef, m: &'a TraitBoundModifier) {
self.check_late_bound_lifetime_defs(&t.bound_generic_params);
visit::walk_poly_trait_ref(self, t, m);
use rustc_session::parse::{feature_err, feature_err_issue};
use rustc_session::Session;
use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::sym;
use rustc_span::Span;
use tracing::debug;
}
}
- fn visit_name(&mut self, sp: Span, name: Symbol) {
- if !name.as_str().is_ascii() {
- gate_feature_post!(
- &self,
- non_ascii_idents,
- self.sess.parse_sess.source_map().guess_head_span(sp),
- "non-ascii idents are not fully supported"
- );
- }
- }
-
fn visit_item(&mut self, i: &'a ast::Item) {
match i.kind {
ast::ItemKind::ForeignMod(ref foreign_module) => {
over time"
);
}
- if self.sess.contains_name(&i.attrs[..], sym::main) {
- gate_feature_post!(
- &self,
- main,
- i.span,
- "declaration of a non-standard `#[main]` \
- function may change over time, for now \
- a top-level `fn main()` is required"
- );
- }
}
ast::ItemKind::Struct(..) => {
fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) {
let is_fn = match i.kind {
- ast::AssocItemKind::Fn(box ast::FnKind(_, ref sig, _, _)) => {
- if let (ast::Const::Yes(_), AssocCtxt::Trait) = (sig.header.constness, ctxt) {
- gate_feature_post!(&self, const_fn, i.span, "const fn is unstable");
- }
- true
- }
+ ast::AssocItemKind::Fn(_) => true,
ast::AssocItemKind::TyAlias(box ast::TyAliasKind(_, ref generics, _, ref ty)) => {
if let (Some(_), AssocCtxt::Trait) = (ty, ctxt) {
gate_feature_post!(
// involved, so we only emit errors where there are no other parsing errors.
gate_all!(destructuring_assignment, "destructuring assignments are unstable");
}
- gate_all!(pub_macro_rules, "`pub` on `macro_rules` items is unstable");
// All uses of `gate_all!` below this point were added in #65742,
// and subsequently disabled (with the non-early gating readded).
}
fn maybe_stage_features(sess: &Session, krate: &ast::Crate) {
+ use rustc_errors::Applicability;
+
if !sess.opts.unstable_features.is_nightly_build() {
+ let lang_features = &sess.features_untracked().declared_lang_features;
for attr in krate.attrs.iter().filter(|attr| sess.check_name(attr, sym::feature)) {
- struct_span_err!(
+ let mut err = struct_span_err!(
sess.parse_sess.span_diagnostic,
attr.span,
E0554,
"`#![feature]` may not be used on the {} release channel",
option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)")
- )
- .emit();
+ );
+ let mut all_stable = true;
+ for ident in
+ attr.meta_item_list().into_iter().flatten().map(|nested| nested.ident()).flatten()
+ {
+ let name = ident.name;
+ let stable_since = lang_features
+ .iter()
+ .flat_map(|&(feature, _, since)| if feature == name { since } else { None })
+ .next();
+ if let Some(since) = stable_since {
+ err.help(&format!(
+ "the feature `{}` has been stable since {} and no longer requires \
+ an attribute to enable",
+ name, since
+ ));
+ } else {
+ all_stable = false;
+ }
+ }
+ if all_stable {
+ err.span_suggestion(
+ attr.span,
+ "remove the attribute",
+ String::new(),
+ Applicability::MachineApplicable,
+ );
+ }
+ err.emit();
}
}
}
push: &mut dyn FnMut(Annotatable),
) {
let inline = cx.meta_word(span, sym::inline);
+ let no_coverage_ident =
+ rustc_ast::attr::mk_nested_word_item(Ident::new(sym::no_coverage, span));
+ let no_coverage_feature =
+ rustc_ast::attr::mk_list_item(Ident::new(sym::feature, span), vec![no_coverage_ident]);
+ let no_coverage = cx.meta_word(span, sym::no_coverage);
let hidden = rustc_ast::attr::mk_nested_word_item(Ident::new(sym::hidden, span));
let doc = rustc_ast::attr::mk_list_item(Ident::new(sym::doc, span), vec![hidden]);
- let attrs = vec![cx.attribute(inline), cx.attribute(doc)];
+ let attrs = vec![
+ cx.attribute(inline),
+ cx.attribute(no_coverage_feature),
+ cx.attribute(no_coverage),
+ cx.attribute(doc),
+ ];
let trait_def = TraitDef {
span,
attributes: Vec::new(),
self.generics.to_generics(cx, self.span, type_ident, generics);
// Create the generic parameters
- params.extend(generics.params.iter().map(|param| match param.kind {
+ params.extend(generics.params.iter().map(|param| match ¶m.kind {
GenericParamKind::Lifetime { .. } => param.clone(),
GenericParamKind::Type { .. } => {
// I don't think this can be moved out of the loop, since
cx.typaram(self.span, param.ident, vec![], bounds, None)
}
- GenericParamKind::Const { .. } => param.clone(),
+ GenericParamKind::Const { ty, kw_span, .. } => {
+ let const_nodefault_kind = GenericParamKind::Const {
+ ty: ty.clone(),
+ kw_span: kw_span.clone(),
+
+ // We can't have default values inside impl block
+ default: None,
+ };
+ let mut param_clone = param.clone();
+ param_clone.kind = const_nodefault_kind;
+ param_clone
+ }
}));
// and similarly for where clauses
ast::ItemKind::Fn(..) => {
if sess.contains_name(&item.attrs, sym::start) {
EntryPointType::Start
- } else if sess.contains_name(&item.attrs, sym::main) {
+ } else if sess.contains_name(&item.attrs, sym::rustc_main) {
EntryPointType::MainAttr
} else if item.ident.name == sym::main {
if depth == 1 {
let attrs = attrs
.into_iter()
.filter(|attr| {
- !self.sess.check_name(attr, sym::main)
+ !self.sess.check_name(attr, sym::rustc_main)
&& !self.sess.check_name(attr, sym::start)
})
.chain(iter::once(allow_dead_code))
let expn_id = ext_cx.resolver.expansion_for_ast_pass(
DUMMY_SP,
AstPass::TestHarness,
- &[sym::main, sym::test, sym::rustc_attrs],
+ &[sym::test, sym::rustc_attrs],
None,
);
let def_site = DUMMY_SP.with_def_site_ctxt(expn_id);
/// By default this expands to
///
/// ```
-/// #[main]
+/// #[rustc_main]
/// pub fn main() {
/// extern crate test;
/// test::test_main_static(&[
let test_extern_stmt =
ecx.stmt_item(sp, ecx.item(sp, test_id, vec![], ast::ItemKind::ExternCrate(None)));
- // #[main]
- let main_meta = ecx.meta_word(sp, sym::main);
+ // #[rustc_main]
+ let main_meta = ecx.meta_word(sp, sym::rustc_main);
let main_attr = ecx.attribute(main_meta);
// pub fn main() { ... }
// simd_select
// simd_rem
// simd_neg
+ // simd_trunc
+ // simd_floor
}
}
use crate::context::CodegenCx;
use crate::llvm::{self, BasicBlock, False};
use crate::llvm::{AtomicOrdering, AtomicRmwBinOp, SynchronizationScope};
+use crate::llvm_util;
use crate::type_::Type;
use crate::type_of::LayoutLlvmExt;
use crate::value::Value;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_span::{sym, Span};
+use rustc_span::Span;
use rustc_target::abi::{self, Align, Size};
use rustc_target::spec::{HasTargetSpec, Target};
use std::borrow::Cow;
fn fadd_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
unsafe {
let instr = llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, UNNAMED);
- llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+ llvm::LLVMRustSetFastMath(instr);
instr
}
}
fn fsub_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
unsafe {
let instr = llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, UNNAMED);
- llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+ llvm::LLVMRustSetFastMath(instr);
instr
}
}
fn fmul_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
unsafe {
let instr = llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, UNNAMED);
- llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+ llvm::LLVMRustSetFastMath(instr);
instr
}
}
fn fdiv_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
unsafe {
let instr = llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, UNNAMED);
- llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+ llvm::LLVMRustSetFastMath(instr);
instr
}
}
fn frem_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
unsafe {
let instr = llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, UNNAMED);
- llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+ llvm::LLVMRustSetFastMath(instr);
instr
}
}
}
fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> {
- // WebAssembly has saturating floating point to integer casts if the
- // `nontrapping-fptoint` target feature is activated. We'll use those if
- // they are available.
- if self.sess().target.arch == "wasm32"
- && self.sess().target_features.contains(&sym::nontrapping_dash_fptoint)
- {
+ if llvm_util::get_version() >= (12, 0, 0) && !self.fptoint_sat_broken_in_llvm() {
let src_ty = self.cx.val_ty(val);
let float_width = self.cx.float_width(src_ty);
let int_width = self.cx.int_width(dest_ty);
- let name = match (int_width, float_width) {
- (32, 32) => Some("llvm.wasm.trunc.saturate.unsigned.i32.f32"),
- (32, 64) => Some("llvm.wasm.trunc.saturate.unsigned.i32.f64"),
- (64, 32) => Some("llvm.wasm.trunc.saturate.unsigned.i64.f32"),
- (64, 64) => Some("llvm.wasm.trunc.saturate.unsigned.i64.f64"),
- _ => None,
- };
- if let Some(name) = name {
- let intrinsic = self.get_intrinsic(name);
- return Some(self.call(intrinsic, &[val], None));
- }
+ let name = format!("llvm.fptoui.sat.i{}.f{}", int_width, float_width);
+ let intrinsic = self.get_intrinsic(&name);
+ return Some(self.call(intrinsic, &[val], None));
}
+
None
}
fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> {
- // WebAssembly has saturating floating point to integer casts if the
- // `nontrapping-fptoint` target feature is activated. We'll use those if
- // they are available.
- if self.sess().target.arch == "wasm32"
- && self.sess().target_features.contains(&sym::nontrapping_dash_fptoint)
- {
+ if llvm_util::get_version() >= (12, 0, 0) && !self.fptoint_sat_broken_in_llvm() {
let src_ty = self.cx.val_ty(val);
let float_width = self.cx.float_width(src_ty);
let int_width = self.cx.int_width(dest_ty);
- let name = match (int_width, float_width) {
- (32, 32) => Some("llvm.wasm.trunc.saturate.signed.i32.f32"),
- (32, 64) => Some("llvm.wasm.trunc.saturate.signed.i32.f64"),
- (64, 32) => Some("llvm.wasm.trunc.saturate.signed.i64.f32"),
- (64, 64) => Some("llvm.wasm.trunc.saturate.signed.i64.f64"),
- _ => None,
- };
- if let Some(name) = name {
- let intrinsic = self.get_intrinsic(name);
- return Some(self.call(intrinsic, &[val], None));
- }
+ let name = format!("llvm.fptosi.sat.i{}.f{}", int_width, float_width);
+ let intrinsic = self.get_intrinsic(&name);
+ return Some(self.call(intrinsic, &[val], None));
}
- None
- }
- fn fptosui_may_trap(&self, val: &'ll Value, dest_ty: &'ll Type) -> bool {
- // Most of the time we'll be generating the `fptosi` or `fptoui`
- // instruction for floating-point-to-integer conversions. These
- // instructions by definition in LLVM do not trap. For the WebAssembly
- // target, however, we'll lower in some cases to intrinsic calls instead
- // which may trap. If we detect that this is a situation where we'll be
- // using the intrinsics then we report that the call map trap, which
- // callers might need to handle.
- if !self.wasm_and_missing_nontrapping_fptoint() {
- return false;
- }
- let src_ty = self.cx.val_ty(val);
- let float_width = self.cx.float_width(src_ty);
- let int_width = self.cx.int_width(dest_ty);
- matches!((int_width, float_width), (32, 32) | (32, 64) | (64, 32) | (64, 64))
+ None
}
fn fptoui(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
- // When we can, use the native wasm intrinsics which have tighter
- // codegen. Note that this has a semantic difference in that the
- // intrinsic can trap whereas `fptoui` never traps. That difference,
- // however, is handled by `fptosui_may_trap` above.
+ // On WebAssembly the `fptoui` and `fptosi` instructions currently have
+ // poor codegen. The reason for this is that the corresponding wasm
+ // instructions, `i32.trunc_f32_s` for example, will trap when the float
+ // is out-of-bounds, infinity, or nan. This means that LLVM
+ // automatically inserts control flow around `fptoui` and `fptosi`
+ // because the LLVM instruction `fptoui` is defined as producing a
+ // poison value, not having UB on out-of-bounds values.
//
- // Note that we skip the wasm intrinsics for vector types where `fptoui`
- // must be used instead.
- if self.wasm_and_missing_nontrapping_fptoint() {
+ // This method, however, is only used with non-saturating casts that
+ // have UB on out-of-bounds values. This means that it's ok if we use
+ // the raw wasm instruction since out-of-bounds values can do whatever
+ // we like. To ensure that LLVM picks the right instruction we choose
+ // the raw wasm intrinsic functions which avoid LLVM inserting all the
+ // other control flow automatically.
+ if self.sess().target.arch == "wasm32" {
let src_ty = self.cx.val_ty(val);
if self.cx.type_kind(src_ty) != TypeKind::Vector {
let float_width = self.cx.float_width(src_ty);
}
fn fptosi(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
- if self.wasm_and_missing_nontrapping_fptoint() {
+ // see `fptoui` above for why wasm is different here
+ if self.sess().target.arch == "wasm32" {
let src_ty = self.cx.val_ty(val);
if self.cx.type_kind(src_ty) != TypeKind::Vector {
let float_width = self.cx.float_width(src_ty);
pub fn vector_reduce_fadd_fast(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value {
unsafe {
let instr = llvm::LLVMRustBuildVectorReduceFAdd(self.llbuilder, acc, src);
- llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+ llvm::LLVMRustSetFastMath(instr);
instr
}
}
pub fn vector_reduce_fmul_fast(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value {
unsafe {
let instr = llvm::LLVMRustBuildVectorReduceFMul(self.llbuilder, acc, src);
- llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+ llvm::LLVMRustSetFastMath(instr);
instr
}
}
unsafe {
let instr =
llvm::LLVMRustBuildVectorReduceFMin(self.llbuilder, src, /*NoNaNs:*/ true);
- llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+ llvm::LLVMRustSetFastMath(instr);
instr
}
}
unsafe {
let instr =
llvm::LLVMRustBuildVectorReduceFMax(self.llbuilder, src, /*NoNaNs:*/ true);
- llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+ llvm::LLVMRustSetFastMath(instr);
instr
}
}
}
}
- fn wasm_and_missing_nontrapping_fptoint(&self) -> bool {
- self.sess().target.arch == "wasm32"
- && !self.sess().target_features.contains(&sym::nontrapping_dash_fptoint)
+ fn fptoint_sat_broken_in_llvm(&self) -> bool {
+ match self.tcx.sess.target.arch.as_str() {
+ // FIXME - https://bugs.llvm.org/show_bug.cgi?id=50083
+ "riscv64" => llvm_util::get_version() < (13, 0, 0),
+ _ => false,
+ }
}
}
let t_f32 = self.type_f32();
let t_f64 = self.type_f64();
- ifn!("llvm.wasm.trunc.saturate.unsigned.i32.f32", fn(t_f32) -> t_i32);
- ifn!("llvm.wasm.trunc.saturate.unsigned.i32.f64", fn(t_f64) -> t_i32);
- ifn!("llvm.wasm.trunc.saturate.unsigned.i64.f32", fn(t_f32) -> t_i64);
- ifn!("llvm.wasm.trunc.saturate.unsigned.i64.f64", fn(t_f64) -> t_i64);
- ifn!("llvm.wasm.trunc.saturate.signed.i32.f32", fn(t_f32) -> t_i32);
- ifn!("llvm.wasm.trunc.saturate.signed.i32.f64", fn(t_f64) -> t_i32);
- ifn!("llvm.wasm.trunc.saturate.signed.i64.f32", fn(t_f32) -> t_i64);
- ifn!("llvm.wasm.trunc.saturate.signed.i64.f64", fn(t_f64) -> t_i64);
ifn!("llvm.wasm.trunc.unsigned.i32.f32", fn(t_f32) -> t_i32);
ifn!("llvm.wasm.trunc.unsigned.i32.f64", fn(t_f64) -> t_i32);
ifn!("llvm.wasm.trunc.unsigned.i64.f32", fn(t_f32) -> t_i64);
ifn!("llvm.wasm.trunc.signed.i64.f32", fn(t_f32) -> t_i64);
ifn!("llvm.wasm.trunc.signed.i64.f64", fn(t_f64) -> t_i64);
+ ifn!("llvm.fptosi.sat.i8.f32", fn(t_f32) -> t_i8);
+ ifn!("llvm.fptosi.sat.i16.f32", fn(t_f32) -> t_i16);
+ ifn!("llvm.fptosi.sat.i32.f32", fn(t_f32) -> t_i32);
+ ifn!("llvm.fptosi.sat.i64.f32", fn(t_f32) -> t_i64);
+ ifn!("llvm.fptosi.sat.i128.f32", fn(t_f32) -> t_i128);
+ ifn!("llvm.fptosi.sat.i8.f64", fn(t_f64) -> t_i8);
+ ifn!("llvm.fptosi.sat.i16.f64", fn(t_f64) -> t_i16);
+ ifn!("llvm.fptosi.sat.i32.f64", fn(t_f64) -> t_i32);
+ ifn!("llvm.fptosi.sat.i64.f64", fn(t_f64) -> t_i64);
+ ifn!("llvm.fptosi.sat.i128.f64", fn(t_f64) -> t_i128);
+
+ ifn!("llvm.fptoui.sat.i8.f32", fn(t_f32) -> t_i8);
+ ifn!("llvm.fptoui.sat.i16.f32", fn(t_f32) -> t_i16);
+ ifn!("llvm.fptoui.sat.i32.f32", fn(t_f32) -> t_i32);
+ ifn!("llvm.fptoui.sat.i64.f32", fn(t_f32) -> t_i64);
+ ifn!("llvm.fptoui.sat.i128.f32", fn(t_f32) -> t_i128);
+ ifn!("llvm.fptoui.sat.i8.f64", fn(t_f64) -> t_i8);
+ ifn!("llvm.fptoui.sat.i16.f64", fn(t_f64) -> t_i16);
+ ifn!("llvm.fptoui.sat.i32.f64", fn(t_f64) -> t_i32);
+ ifn!("llvm.fptoui.sat.i64.f64", fn(t_f64) -> t_i64);
+ ifn!("llvm.fptoui.sat.i128.f64", fn(t_f64) -> t_i128);
+
ifn!("llvm.trap", fn() -> void);
ifn!("llvm.debugtrap", fn() -> void);
ifn!("llvm.frameaddress", fn(t_i32) -> i8p);
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
use rustc_hir::def_id::{DefId, DefIdSet, LOCAL_CRATE};
use rustc_llvm::RustString;
+use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::coverage::CodeRegion;
use rustc_span::Symbol;
let mut unused_def_ids_by_file: FxHashMap<Symbol, Vec<DefId>> = FxHashMap::default();
for &non_codegenned_def_id in all_def_ids.difference(codegenned_def_ids) {
+ let codegen_fn_attrs = tcx.codegen_fn_attrs(non_codegenned_def_id);
+ if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_COVERAGE) {
+ continue;
+ }
// Make sure the non-codegenned (unused) function has a file_name
if let Some(non_codegenned_file_name) = tcx.covered_file_name(non_codegenned_def_id) {
let def_ids =
let vec_ty = bx.type_vector(elem_ty, in_len);
let (intr_name, fn_ty) = match name {
- sym::simd_fsqrt => ("sqrt", bx.type_func(&[vec_ty], vec_ty)),
- sym::simd_fsin => ("sin", bx.type_func(&[vec_ty], vec_ty)),
- sym::simd_fcos => ("cos", bx.type_func(&[vec_ty], vec_ty)),
- sym::simd_fabs => ("fabs", bx.type_func(&[vec_ty], vec_ty)),
- sym::simd_floor => ("floor", bx.type_func(&[vec_ty], vec_ty)),
sym::simd_ceil => ("ceil", bx.type_func(&[vec_ty], vec_ty)),
- sym::simd_fexp => ("exp", bx.type_func(&[vec_ty], vec_ty)),
+ sym::simd_fabs => ("fabs", bx.type_func(&[vec_ty], vec_ty)),
+ sym::simd_fcos => ("cos", bx.type_func(&[vec_ty], vec_ty)),
sym::simd_fexp2 => ("exp2", bx.type_func(&[vec_ty], vec_ty)),
+ sym::simd_fexp => ("exp", bx.type_func(&[vec_ty], vec_ty)),
sym::simd_flog10 => ("log10", bx.type_func(&[vec_ty], vec_ty)),
sym::simd_flog2 => ("log2", bx.type_func(&[vec_ty], vec_ty)),
sym::simd_flog => ("log", bx.type_func(&[vec_ty], vec_ty)),
+ sym::simd_floor => ("floor", bx.type_func(&[vec_ty], vec_ty)),
+ sym::simd_fma => ("fma", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)),
sym::simd_fpowi => ("powi", bx.type_func(&[vec_ty, bx.type_i32()], vec_ty)),
sym::simd_fpow => ("pow", bx.type_func(&[vec_ty, vec_ty], vec_ty)),
- sym::simd_fma => ("fma", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)),
+ sym::simd_fsin => ("sin", bx.type_func(&[vec_ty], vec_ty)),
+ sym::simd_fsqrt => ("sqrt", bx.type_func(&[vec_ty], vec_ty)),
+ sym::simd_round => ("round", bx.type_func(&[vec_ty], vec_ty)),
+ sym::simd_trunc => ("trunc", bx.type_func(&[vec_ty], vec_ty)),
_ => return_error!("unrecognized intrinsic `{}`", name),
};
-
let llvm_name = &format!("llvm.{0}.v{1}{2}", intr_name, in_len, elem_ty_str);
let f = bx.declare_cfn(&llvm_name, llvm::UnnamedAddr::No, fn_ty);
let c = bx.call(f, &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(), None);
- unsafe { llvm::LLVMRustSetHasUnsafeAlgebra(c) };
Ok(c)
}
if std::matches!(
name,
- sym::simd_fsqrt
- | sym::simd_fsin
- | sym::simd_fcos
+ sym::simd_ceil
| sym::simd_fabs
- | sym::simd_floor
- | sym::simd_ceil
- | sym::simd_fexp
+ | sym::simd_fcos
| sym::simd_fexp2
+ | sym::simd_fexp
| sym::simd_flog10
| sym::simd_flog2
| sym::simd_flog
- | sym::simd_fpowi
- | sym::simd_fpow
+ | sym::simd_floor
| sym::simd_fma
+ | sym::simd_fpow
+ | sym::simd_fpowi
+ | sym::simd_fsin
+ | sym::simd_fsqrt
+ | sym::simd_round
+ | sym::simd_trunc
) {
return simd_simple_float_intrinsic(name, in_elem, in_ty, in_len, bx, span, args);
}
pub fn LLVMBuildNeg(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value;
pub fn LLVMBuildFNeg(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value;
pub fn LLVMBuildNot(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value;
- pub fn LLVMRustSetHasUnsafeAlgebra(Instr: &Value);
+ pub fn LLVMRustSetFastMath(Instr: &Value);
// Memory
pub fn LLVMBuildAlloca(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) -> &'a Value;
itertools = "0.9"
tracing = "0.1"
libc = "0.2.50"
-jobserver = "0.1.11"
-tempfile = "3.1"
+jobserver = "0.1.22"
+tempfile = "3.2"
pathdiff = "0.2.0"
rustc_serialize = { path = "../rustc_serialize" }
use rustc_hir::lang_items::LangItem;
use rustc_middle::mir;
use rustc_middle::ty::cast::{CastTy, IntTy};
-use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout};
+use rustc_middle::ty::layout::HasTyCtxt;
use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
use rustc_span::source_map::{Span, DUMMY_SP};
use rustc_span::symbol::sym;
bx.inttoptr(usize_llval, ll_t_out)
}
(CastTy::Float, CastTy::Int(IntTy::I)) => {
- cast_float_to_int(&mut bx, true, llval, ll_t_in, ll_t_out, cast)
+ cast_float_to_int(&mut bx, true, llval, ll_t_in, ll_t_out)
}
(CastTy::Float, CastTy::Int(_)) => {
- cast_float_to_int(&mut bx, false, llval, ll_t_in, ll_t_out, cast)
+ cast_float_to_int(&mut bx, false, llval, ll_t_in, ll_t_out)
}
_ => bug!("unsupported cast: {:?} to {:?}", operand.layout.ty, cast.ty),
};
x: Bx::Value,
float_ty: Bx::Type,
int_ty: Bx::Type,
- int_layout: TyAndLayout<'tcx>,
) -> Bx::Value {
if let Some(false) = bx.cx().sess().opts.debugging_opts.saturating_float_casts {
return if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) };
let int_min = bx.cx().const_uint_big(int_ty, int_min(signed, int_width) as u128);
let zero = bx.cx().const_uint(int_ty, 0);
- // The codegen here differs quite a bit depending on whether our builder's
- // `fptosi` and `fptoui` instructions may trap for out-of-bounds values. If
- // they don't trap then we can start doing everything inline with a
- // `select` instruction because it's ok to execute `fptosi` and `fptoui`
- // even if we don't use the results.
- if !bx.fptosui_may_trap(x, int_ty) {
- // Step 1 ...
- let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) };
- let less_or_nan = bx.fcmp(RealPredicate::RealULT, x, f_min);
- let greater = bx.fcmp(RealPredicate::RealOGT, x, f_max);
-
- // Step 2: We use two comparisons and two selects, with %s1 being the
- // result:
- // %less_or_nan = fcmp ult %x, %f_min
- // %greater = fcmp olt %x, %f_max
- // %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result
- // %s1 = select %greater, int_ty::MAX, %s0
- // Note that %less_or_nan uses an *unordered* comparison. This
- // comparison is true if the operands are not comparable (i.e., if x is
- // NaN). The unordered comparison ensures that s1 becomes int_ty::MIN if
- // x is NaN.
- //
- // Performance note: Unordered comparison can be lowered to a "flipped"
- // comparison and a negation, and the negation can be merged into the
- // select. Therefore, it not necessarily any more expensive than a
- // ordered ("normal") comparison. Whether these optimizations will be
- // performed is ultimately up to the backend, but at least x86 does
- // perform them.
- let s0 = bx.select(less_or_nan, int_min, fptosui_result);
- let s1 = bx.select(greater, int_max, s0);
-
- // Step 3: NaN replacement.
- // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN.
- // Therefore we only need to execute this step for signed integer types.
- if signed {
- // LLVM has no isNaN predicate, so we use (x == x) instead
- let cmp = bx.fcmp(RealPredicate::RealOEQ, x, x);
- bx.select(cmp, s1, zero)
- } else {
- s1
- }
+ // Step 1 ...
+ let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) };
+ let less_or_nan = bx.fcmp(RealPredicate::RealULT, x, f_min);
+ let greater = bx.fcmp(RealPredicate::RealOGT, x, f_max);
+
+ // Step 2: We use two comparisons and two selects, with %s1 being the
+ // result:
+ // %less_or_nan = fcmp ult %x, %f_min
+ // %greater = fcmp olt %x, %f_max
+ // %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result
+ // %s1 = select %greater, int_ty::MAX, %s0
+ // Note that %less_or_nan uses an *unordered* comparison. This
+ // comparison is true if the operands are not comparable (i.e., if x is
+ // NaN). The unordered comparison ensures that s1 becomes int_ty::MIN if
+ // x is NaN.
+ //
+ // Performance note: Unordered comparison can be lowered to a "flipped"
+ // comparison and a negation, and the negation can be merged into the
+ // select. Therefore, it not necessarily any more expensive than a
+ // ordered ("normal") comparison. Whether these optimizations will be
+ // performed is ultimately up to the backend, but at least x86 does
+ // perform them.
+ let s0 = bx.select(less_or_nan, int_min, fptosui_result);
+ let s1 = bx.select(greater, int_max, s0);
+
+ // Step 3: NaN replacement.
+ // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN.
+ // Therefore we only need to execute this step for signed integer types.
+ if signed {
+ // LLVM has no isNaN predicate, so we use (x == x) instead
+ let cmp = bx.fcmp(RealPredicate::RealOEQ, x, x);
+ bx.select(cmp, s1, zero)
} else {
- // In this case we cannot execute `fptosi` or `fptoui` and then later
- // discard the result. The builder is telling us that these instructions
- // will trap on out-of-bounds values, so we need to use basic blocks and
- // control flow to avoid executing the `fptosi` and `fptoui`
- // instructions.
- //
- // The general idea of what we're constructing here is, for f64 -> i32:
- //
- // ;; block so far... %0 is the argument
- // %result = alloca i32, align 4
- // %inbound_lower = fcmp oge double %0, 0xC1E0000000000000
- // %inbound_upper = fcmp ole double %0, 0x41DFFFFFFFC00000
- // ;; match (inbound_lower, inbound_upper) {
- // ;; (true, true) => %0 can be converted without trapping
- // ;; (false, false) => %0 is a NaN
- // ;; (true, false) => %0 is too large
- // ;; (false, true) => %0 is too small
- // ;; }
- // ;;
- // ;; The (true, true) check, go to %convert if so.
- // %inbounds = and i1 %inbound_lower, %inbound_upper
- // br i1 %inbounds, label %convert, label %specialcase
- //
- // convert:
- // %cvt = call i32 @llvm.wasm.trunc.signed.i32.f64(double %0)
- // store i32 %cvt, i32* %result, align 4
- // br label %done
- //
- // specialcase:
- // ;; Handle the cases where the number is NaN, too large or too small
- //
- // ;; Either (true, false) or (false, true)
- // %is_not_nan = or i1 %inbound_lower, %inbound_upper
- // ;; Figure out which saturated value we are interested in if not `NaN`
- // %saturated = select i1 %inbound_lower, i32 2147483647, i32 -2147483648
- // ;; Figure out between saturated and NaN representations
- // %result_nan = select i1 %is_not_nan, i32 %saturated, i32 0
- // store i32 %result_nan, i32* %result, align 4
- // br label %done
- //
- // done:
- // %r = load i32, i32* %result, align 4
- // ;; ...
- let done = bx.build_sibling_block("float_cast_done");
- let mut convert = bx.build_sibling_block("float_cast_convert");
- let mut specialcase = bx.build_sibling_block("float_cast_specialcase");
-
- let result = PlaceRef::alloca(bx, int_layout);
- result.storage_live(bx);
-
- // Use control flow to figure out whether we can execute `fptosi` in a
- // basic block, or whether we go to a different basic block to implement
- // the saturating logic.
- let inbound_lower = bx.fcmp(RealPredicate::RealOGE, x, f_min);
- let inbound_upper = bx.fcmp(RealPredicate::RealOLE, x, f_max);
- let inbounds = bx.and(inbound_lower, inbound_upper);
- bx.cond_br(inbounds, convert.llbb(), specialcase.llbb());
-
- // Translation of the `convert` basic block
- let cvt = if signed { convert.fptosi(x, int_ty) } else { convert.fptoui(x, int_ty) };
- convert.store(cvt, result.llval, result.align);
- convert.br(done.llbb());
-
- // Translation of the `specialcase` basic block. Note that like above
- // we try to be a bit clever here for unsigned conversions. In those
- // cases the `int_min` is zero so we don't need two select instructions,
- // just one to choose whether we need `int_max` or not. If
- // `inbound_lower` is true then we're guaranteed to not be `NaN` and
- // since we're greater than zero we must be saturating to `int_max`. If
- // `inbound_lower` is false then we're either NaN or less than zero, so
- // we saturate to zero.
- let result_nan = if signed {
- let is_not_nan = specialcase.or(inbound_lower, inbound_upper);
- let saturated = specialcase.select(inbound_lower, int_max, int_min);
- specialcase.select(is_not_nan, saturated, zero)
- } else {
- specialcase.select(inbound_lower, int_max, int_min)
- };
- specialcase.store(result_nan, result.llval, result.align);
- specialcase.br(done.llbb());
-
- // Translation of the `done` basic block, positioning ourselves to
- // continue from that point as well.
- *bx = done;
- let ret = bx.load(result.llval, result.align);
- result.storage_dead(bx);
- ret
+ s1
}
}
fn sext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
fn fptoui_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Option<Self::Value>;
fn fptosi_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Option<Self::Value>;
- fn fptosui_may_trap(&self, val: Self::Value, dest_ty: Self::Type) -> bool;
fn fptoui(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
fn fptosi(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
fn uitofp(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
doctest = false
[dependencies]
-arrayvec = { version = "0.5.1", default-features = false }
+arrayvec = { version = "0.7", default-features = false }
ena = "0.14"
indexmap = "1.5.1"
tracing = "0.1"
rustc_macros = { path = "../rustc_macros" }
rustc_graphviz = { path = "../rustc_graphviz" }
cfg-if = "0.1.2"
-crossbeam-utils = { version = "0.7", features = ["nightly"] }
+crossbeam-utils = { version = "0.8", features = ["nightly"] }
stable_deref_trait = "1.0.0"
rayon = { version = "0.3.1", package = "rustc-rayon" }
rayon-core = { version = "0.3.1", package = "rustc-rayon-core" }
measureme = "9.1.0"
libc = "0.2"
stacker = "0.1.12"
-tempfile = "3.0.5"
+tempfile = "3.2"
[dependencies.parking_lot]
version = "0.11"
// for reasonably small arrays that stay
// small in vast majority of cases.
//
-// '8' is choosen as a sane default, to be
+// '8' is chosen as a sane default, to be
// reevaluated later.
-//
-// Note: As of now ArrayVec design prevents
-// us from making it user-customizable.
const SSO_ARRAY_SIZE: usize = 8;
/// Small-storage-optimized implementation of a map.
#[derive(Clone)]
pub enum SsoHashMap<K, V> {
- Array(ArrayVec<[(K, V); SSO_ARRAY_SIZE]>),
+ Array(ArrayVec<(K, V), SSO_ARRAY_SIZE>),
Map(FxHashMap<K, V>),
}
impl<K, V> IntoIterator for SsoHashMap<K, V> {
type IntoIter = EitherIter<
- <ArrayVec<[(K, V); 8]> as IntoIterator>::IntoIter,
+ <ArrayVec<(K, V), 8> as IntoIterator>::IntoIter,
<FxHashMap<K, V> as IntoIterator>::IntoIter,
>;
type Item = <Self::IntoIter as Iterator>::Item;
impl<'a, K, V> IntoIterator for &'a SsoHashMap<K, V> {
type IntoIter = EitherIter<
std::iter::Map<
- <&'a ArrayVec<[(K, V); 8]> as IntoIterator>::IntoIter,
+ <&'a ArrayVec<(K, V), 8> as IntoIterator>::IntoIter,
fn(&'a (K, V)) -> (&'a K, &'a V),
>,
<&'a FxHashMap<K, V> as IntoIterator>::IntoIter,
impl<'a, K, V> IntoIterator for &'a mut SsoHashMap<K, V> {
type IntoIter = EitherIter<
std::iter::Map<
- <&'a mut ArrayVec<[(K, V); 8]> as IntoIterator>::IntoIter,
+ <&'a mut ArrayVec<(K, V), 8> as IntoIterator>::IntoIter,
fn(&'a mut (K, V)) -> (&'a K, &'a mut V),
>,
<&'a mut FxHashMap<K, V> as IntoIterator>::IntoIter,
E0718: include_str!("./error_codes/E0718.md"),
E0719: include_str!("./error_codes/E0719.md"),
E0720: include_str!("./error_codes/E0720.md"),
-E0723: include_str!("./error_codes/E0723.md"),
E0724: include_str!("./error_codes/E0724.md"),
E0725: include_str!("./error_codes/E0725.md"),
E0727: include_str!("./error_codes/E0727.md"),
E0717, // rustc_promotable without stability attribute
// E0721, // `await` keyword
E0722, // Malformed `#[optimize]` attribute
+// E0723, unstable feature in `const` context
E0726, // non-explicit (not `'_`) elided lifetime in unsupported position
// E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`.
E0757, // `#[ffi_const]` functions cannot be `#[ffi_pure]`
+#### Note: this error code is no longer emitted by the compiler.
+
More than one function was declared with the `#[main]` attribute.
Erroneous code example:
-```compile_fail,E0137
+```compile_fail
#![feature(main)]
#[main]
`#[main]` attribute. This is an error because there must be a unique entry
point into a Rust program. Example:
-```
+```compile_fail
#![feature(main)]
#[main]
Erroneous code example:
```compile_fail,E0379
-#![feature(const_fn)]
-
trait Foo {
const fn bar() -> u32; // error!
}
struct Bar;
impl Foo for Bar {} // error: `Foo` is not a trait
+fn baz<T: Foo>(t: T) {} // error: `Foo` is not a trait
```
Another erroneous code example:
```compile_fail,E0404
-struct Foo;
+type Foo = Iterator<Item=String>;
-fn bar<T: Foo>(t: T) {} // error: `Foo` is not a trait
+fn bar<T: Foo>(t: T) {} // error: `Foo` is a type alias
```
Please verify that the trait's name was not misspelled or that the right
impl Foo for Bar { // ok!
// functions implementation
}
+
+fn baz<T: Foo>(t: T) {} // ok!
```
-or:
+Alternatively, you could introduce a new trait with your desired restrictions
+as a super trait:
```
-trait Foo {
- // some functions
-}
+# trait Foo {}
+# struct Bar;
+# impl Foo for Bar {}
+trait Qux: Foo {} // Anything that implements Qux also needs to implement Foo
+fn baz<T: Qux>(t: T) {} // also ok!
+```
+
+Finally, if you are on nightly and want to use a trait alias
+instead of a type alias, you should use `#![feature(trait_alias)]`:
+
+```
+#![feature(trait_alias)]
+trait Foo = Iterator<Item=String>;
fn bar<T: Foo>(t: T) {} // ok!
```
Erroneous code example:
```ignore (depends on release channel)
-#![feature(non_ascii_idents)] // error: `#![feature]` may not be used on the
- // stable release channel
+#![feature(lang_items)] // error: `#![feature]` may not be used on the
+ // stable release channel
```
If you need the feature, make sure to use a nightly release of the compiler
+++ /dev/null
-An unstable feature in `const` contexts was used.
-
-Erroneous code example:
-
-```compile_fail,E0723
-const fn foo<T: Copy>(_: T) { // error!
- // ...
-}
-```
-
-To enable this feature on a nightly version of rustc, add the `const_fn`
-feature flag:
-
-```
-#![feature(const_fn)]
-
-const fn foo<T: Copy>(_: T) { // ok!
- // ...
-}
-```
Erroneous code examples:
```compile_fail,E0754
-# #![feature(non_ascii_idents)]
mod řųśť; // error!
attribute is specified. For example:
```
-# #![feature(non_ascii_idents)]
-
mod řųśť { // ok!
const IS_GREAT: bool = true;
}
Erroneous code example:
```compile_fail,E0764
-#![feature(const_fn)]
#![feature(const_mut_refs)]
fn main() {
you can totally use it in constant functions:
```
-#![feature(const_fn)]
#![feature(const_mut_refs)]
const fn foo(x: usize) -> usize {
/// This set contains the `DiagnosticId` of all emitted diagnostics to avoid
/// emitting the same diagnostic with extended help (`--teach`) twice, which
- /// would be uneccessary repetition.
+ /// would be unnecessary repetition.
taught_diagnostics: FxHashSet<DiagnosticId>,
/// Used to suggest rustc --explain <error code>
// Code for creating styled buffers
use crate::snippet::{Style, StyledString};
-use std::iter;
#[derive(Debug)]
pub struct StyledBuffer {
- text: Vec<Vec<char>>,
- styles: Vec<Vec<Style>>,
+ lines: Vec<Vec<StyledChar>>,
+}
+
+#[derive(Debug, Clone)]
+struct StyledChar {
+ chr: char,
+ style: Style,
+}
+
+impl StyledChar {
+ const SPACE: Self = StyledChar::new(' ', Style::NoStyle);
+
+ const fn new(chr: char, style: Style) -> Self {
+ StyledChar { chr, style }
+ }
}
impl StyledBuffer {
pub fn new() -> StyledBuffer {
- StyledBuffer { text: vec![], styles: vec![] }
+ StyledBuffer { lines: vec![] }
}
+ /// Returns content of `StyledBuffer` splitted by lines and line styles
pub fn render(&self) -> Vec<Vec<StyledString>> {
// Tabs are assumed to have been replaced by spaces in calling code.
- debug_assert!(self.text.iter().all(|r| !r.contains(&'\t')));
+ debug_assert!(self.lines.iter().all(|r| !r.iter().any(|sc| sc.chr == '\t')));
let mut output: Vec<Vec<StyledString>> = vec![];
let mut styled_vec: Vec<StyledString> = vec![];
- for (row, row_style) in iter::zip(&self.text, &self.styles) {
+ for styled_line in &self.lines {
let mut current_style = Style::NoStyle;
let mut current_text = String::new();
- for (&c, &s) in iter::zip(row, row_style) {
- if s != current_style {
+ for sc in styled_line {
+ if sc.style != current_style {
if !current_text.is_empty() {
styled_vec.push(StyledString { text: current_text, style: current_style });
}
- current_style = s;
+ current_style = sc.style;
current_text = String::new();
}
- current_text.push(c);
+ current_text.push(sc.chr);
}
if !current_text.is_empty() {
styled_vec.push(StyledString { text: current_text, style: current_style });
}
fn ensure_lines(&mut self, line: usize) {
- while line >= self.text.len() {
- self.text.push(vec![]);
- self.styles.push(vec![]);
+ if line >= self.lines.len() {
+ self.lines.resize(line + 1, Vec::new());
}
}
+ /// Sets `chr` with `style` for given `line`, `col`.
+ /// If `line` does not exist in our buffer, adds empty lines up to the given
+ /// and fills the last line with unstyled whitespace.
pub fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) {
self.ensure_lines(line);
- if col < self.text[line].len() {
- self.text[line][col] = chr;
- self.styles[line][col] = style;
- } else {
- let mut i = self.text[line].len();
- while i < col {
- self.text[line].push(' ');
- self.styles[line].push(Style::NoStyle);
- i += 1;
- }
- self.text[line].push(chr);
- self.styles[line].push(style);
+ if col >= self.lines[line].len() {
+ self.lines[line].resize(col + 1, StyledChar::SPACE);
}
+ self.lines[line][col] = StyledChar::new(chr, style);
}
+ /// Sets `string` with `style` for given `line`, starting from `col`.
+ /// If `line` does not exist in our buffer, adds empty lines up to the given
+ /// and fills the last line with unstyled whitespace.
pub fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) {
let mut n = col;
for c in string.chars() {
}
}
+ /// For given `line` inserts `string` with `style` before old content of that line,
+ /// adding lines if needed
pub fn prepend(&mut self, line: usize, string: &str, style: Style) {
self.ensure_lines(line);
let string_len = string.chars().count();
- // Push the old content over to make room for new content
- for _ in 0..string_len {
- self.styles[line].insert(0, Style::NoStyle);
- self.text[line].insert(0, ' ');
+ if !self.lines[line].is_empty() {
+ // Push the old content over to make room for new content
+ for _ in 0..string_len {
+ self.lines[line].insert(0, StyledChar::SPACE);
+ }
}
self.puts(line, 0, string, style);
}
+ /// For given `line` inserts `string` with `style` after old content of that line,
+ /// adding lines if needed
pub fn append(&mut self, line: usize, string: &str, style: Style) {
- if line >= self.text.len() {
+ if line >= self.lines.len() {
self.puts(line, 0, string, style);
} else {
- let col = self.text[line].len();
+ let col = self.lines[line].len();
self.puts(line, col, string, style);
}
}
pub fn num_lines(&self) -> usize {
- self.text.len()
+ self.lines.len()
}
+ /// Set `style` for `line`, `col_start..col_end` range if:
+ /// 1. That line and column range exist in `StyledBuffer`
+ /// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
pub fn set_style_range(
&mut self,
line: usize,
}
}
+ /// Set `style` for `line`, `col` if:
+ /// 1. That line and column exist in `StyledBuffer`
+ /// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
pub fn set_style(&mut self, line: usize, col: usize, style: Style, overwrite: bool) {
- if let Some(ref mut line) = self.styles.get_mut(line) {
- if let Some(s) = line.get_mut(col) {
- if *s == Style::NoStyle || *s == Style::Quotation || overwrite {
+ if let Some(ref mut line) = self.lines.get_mut(line) {
+ if let Some(StyledChar { style: s, .. }) = line.get_mut(col) {
+ if overwrite || *s == Style::NoStyle || *s == Style::Quotation {
*s = style;
}
}
ZeroOrMore,
/// Kleene plus (`+`) for one or more repetitions
OneOrMore,
- /// Kleene optional (`?`) for zero or one reptitions
+ /// Kleene optional (`?`) for zero or one repetitions
ZeroOrOne,
}
}
} else {
// 0 is the initial counter (we have done 0 repretitions so far). `len`
- // is the total number of reptitions we should generate.
+ // is the total number of repetitions we should generate.
repeats.push((0, len));
// The first time we encounter the sequence we push it to the stack. It
/// appropriate meta-vars in `interpolations`.
///
/// Note that if `repeats` does not match the exact correct depth of a meta-var,
-/// `lookup_cur_matched` will return `None`, which is why this still works even in the presnece of
+/// `lookup_cur_matched` will return `None`, which is why this still works even in the presence of
/// multiple nested matcher sequences.
fn lockstep_iter_size(
tree: &mbe::TokenTree,
(accepted, unsafe_block_in_unsafe_fn, "1.52.0", Some(71668), None),
/// Allows the use of or-patterns (e.g., `0 | 1`).
(accepted, or_patterns, "1.53.0", Some(54883), None),
+ /// Allows defining identifiers beyond ASCII.
+ (accepted, non_ascii_idents, "1.53.0", Some(55467), None),
// -------------------------------------------------------------------------
// feature-group-end: accepted features
_ => panic!("`{}` was not listed in `declare_features`", feature),
}
}
+
+ pub fn unordered_const_ty_params(&self) -> bool {
+ self.const_generics || self.const_generics_defaults
+ }
}
};
}
/// Allows using the `box $expr` syntax.
(active, box_syntax, "1.0.0", Some(49733), None),
- /// Allows using `#[main]` to replace the entrypoint `#[lang = "start"]` calls.
- (active, main, "1.0.0", Some(29634), None),
-
/// Allows using `#[start]` on a function indicating that it is the program entrypoint.
(active, start, "1.0.0", Some(29633), None),
// feature-group-start: actual feature gates
// -------------------------------------------------------------------------
- /// Allows defining identifiers beyond ASCII.
- (active, non_ascii_idents, "1.0.0", Some(55467), None),
-
/// Allows using `#[plugin_registrar]` on functions.
(active, plugin_registrar, "1.0.0", Some(29597), None),
/// Allows macro attributes to observe output of `#[derive]`.
(active, macro_attributes_in_derive_output, "1.51.0", Some(81119), None),
- /// Allows `pub` on `macro_rules` items.
- (active, pub_macro_rules, "1.52.0", Some(78855), None),
-
/// Allows the use of type alias impl trait in function return positions
(active, min_type_alias_impl_trait, "1.52.0", Some(63063), None),
/// Allows associated types in inherent impls.
(active, inherent_associated_types, "1.52.0", Some(8995), None),
+ // Allows setting the threshold for the `large_assignments` lint.
+ (active, large_assignments, "1.52.0", Some(83518), None),
+
/// Allows `extern "C-unwind" fn` to enable unwinding across ABI boundaries.
(active, c_unwind, "1.52.0", Some(74990), None),
/// Allows `extern "wasm" fn`
(active, wasm_abi, "1.53.0", Some(83788), None),
+ /// Allows function attribute `#[no_coverage]`, to bypass coverage
+ /// instrumentation of that function.
+ (active, no_coverage, "1.53.0", Some(84605), None),
+
+ /// Allows trait bounds in `const fn`.
+ (active, const_fn_trait_bound, "1.53.0", Some(57563), None),
+
+ /// Allows unsizing coercions in `const fn`.
+ (active, const_fn_unsize, "1.53.0", Some(64992), None),
+
// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------
template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#),
),
ungated!(link_name, AssumedUsed, template!(NameValueStr: "name")),
- ungated!(no_link, Normal, template!(Word)),
- ungated!(repr, Normal, template!(List: "C")),
+ ungated!(no_link, AssumedUsed, template!(Word)),
+ ungated!(repr, AssumedUsed, template!(List: "C")),
ungated!(export_name, AssumedUsed, template!(NameValueStr: "name")),
ungated!(link_section, AssumedUsed, template!(NameValueStr: "name")),
ungated!(no_mangle, AssumedUsed, template!(Word)),
const_eval_limit, CrateLevel, template!(NameValueStr: "N"), const_eval_limit,
experimental!(const_eval_limit)
),
+ gated!(
+ move_size_limit, CrateLevel, template!(NameValueStr: "N"), large_assignments,
+ experimental!(move_size_limit)
+ ),
// Entry point:
ungated!(main, Normal, template!(Word)),
template!(List: "address, memory, thread"),
experimental!(no_sanitize)
),
+ ungated!(
+ // Not exclusively gated at the crate level (though crate-level is
+ // supported). The feature can alternatively be enabled on individual
+ // functions.
+ no_coverage, AssumedUsed,
+ template!(Word),
+ ),
// FIXME: #14408 assume docs are used since rustdoc looks at them.
ungated!(doc, AssumedUsed, template!(List: "hidden|inline|...", NameValueStr: "string")),
"custom test frameworks are an unstable feature",
),
// RFC #1268
- gated!(marker, Normal, template!(Word), marker_trait_attr, experimental!(marker)),
+ gated!(marker, AssumedUsed, template!(Word), marker_trait_attr, experimental!(marker)),
gated!(
thread_local, AssumedUsed, template!(Word),
"`#[thread_local]` is an experimental feature, and does not currently handle destructors",
rustc_specialization_trait, Normal, template!(Word),
"the `#[rustc_specialization_trait]` attribute is used to check specializations"
),
+ rustc_attr!(
+ rustc_main, Normal, template!(Word),
+ "the `#[rustc_main]` attribute is used internally to specify test entry point function",
+ ),
+ rustc_attr!(
+ rustc_skip_array_during_method_dispatch, Normal, template!(Word),
+ "the `#[rustc_skip_array_during_method_dispatch]` attribute is used to exclude a trait \
+ from method dispatch when the receiver is an array, for compatibility in editions < 2021."
+ ),
// ==========================================================================
// Internal attributes, Testing:
(removed, link_args, "1.53.0", Some(29596), None,
Some("removed in favor of using `-C link-arg=ARG` on command line, \
which is available from cargo build scripts with `cargo:rustc-link-arg` now")),
+ /// Allows using `#[main]` to replace the entrypoint `#[lang = "start"]` calls.
+ (removed, main, "1.53.0", Some(29634), None, None),
+ (removed, pub_macro_rules, "1.53.0", Some(78855), None,
+ Some("removed due to being incomplete, in particular it does not work across crates")),
// -------------------------------------------------------------------------
// feature-group-end: removed features
match self {
GenericArg::Lifetime(_) => ast::ParamKindOrd::Lifetime,
GenericArg::Type(_) => ast::ParamKindOrd::Type,
- GenericArg::Const(_) => ast::ParamKindOrd::Const { unordered: feats.const_generics },
+ GenericArg::Const(_) => {
+ ast::ParamKindOrd::Const { unordered: feats.unordered_const_ty_params() }
+ }
}
}
}
/// Like [`PrimTy::name`], but returns a &str instead of a symbol.
///
- /// Used by rustdoc.
+ /// Used by clippy.
pub fn name_str(self) -> &'static str {
match self {
PrimTy::Int(i) => i.name_str(),
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html
#![feature(crate_visibility_modifier)]
-#![feature(const_fn)] // For the unsizing cast on `&[]`
#![feature(const_panic)]
#![feature(extended_key_value_attributes)]
#![feature(in_band_lifetimes)]
fn print_else(&mut self, els: Option<&hir::Expr<'_>>) {
match els {
- Some(_else) => {
- match _else.kind {
+ Some(else_) => {
+ match else_.kind {
// "another else-if"
hir::ExprKind::If(ref i, ref then, ref e) => {
self.cbox(INDENT_UNIT - 1);
self.s.word(" else ");
self.print_block(&b)
}
+ hir::ExprKind::Match(ref expr, arms, _) => {
+ // else if let desugared to match
+ assert!(arms.len() == 2, "if let desugars to match with two arms");
+
+ self.s.word(" else ");
+ self.s.word("{");
+
+ self.cbox(INDENT_UNIT);
+ self.ibox(INDENT_UNIT);
+ self.word_nbsp("match");
+ self.print_expr_as_cond(&expr);
+ self.s.space();
+ self.bopen();
+ for arm in arms {
+ self.print_arm(arm);
+ }
+ self.bclose(expr.span);
+
+ self.s.word("}");
+ }
// BLEAH, constraints would be great here
_ => {
panic!("print_if saw if with weird alternative");
doctest = false
[dependencies]
-arrayvec = { version = "0.5.1", default-features = false }
+arrayvec = { version = "0.7", default-features = false }
rustc_serialize = { path = "../rustc_serialize" }
rustc_macros = { path = "../rustc_macros" }
#[derive(Clone, Debug)]
pub struct SparseBitSet<T> {
domain_size: usize,
- elems: ArrayVec<[T; SPARSE_MAX]>,
+ elems: ArrayVec<T, SPARSE_MAX>,
}
impl<T: Idx> SparseBitSet<T> {
#![feature(allow_internal_unstable)]
-#![feature(const_fn)]
+#![feature(bench_black_box)]
#![feature(const_panic)]
#![feature(extend_one)]
#![feature(iter_zip)]
///
/// This also tests if the given const `ct` contains an inference variable which was previously
/// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct`
- /// would result in an infinite type as we continously replace an inference variable
+ /// would result in an infinite type as we continuously replace an inference variable
/// in `ct` with `ct` itself.
///
/// This is especially important as unevaluated consts use their parents generics.
/// Relate a type inference variable with a value type. This works
/// by creating a "generalization" G of the value where all the
/// lifetimes are replaced with fresh inference values. This
- /// genearlization G becomes the value of the inference variable,
+ /// generalization G becomes the value of the inference variable,
/// and is then related in turn to the value. So e.g. if you had
/// `vid = ?0` and `value = &'a u32`, we might first instantiate
/// `?0` to a type like `&'0 u32` where `'0` is a fresh variable,
#![feature(bool_to_option)]
#![feature(box_patterns)]
#![feature(box_syntax)]
-#![feature(const_fn)]
#![feature(const_panic)]
#![feature(extend_one)]
#![feature(iter_zip)]
rustc_resolve = { path = "../rustc_resolve" }
rustc_trait_selection = { path = "../rustc_trait_selection" }
rustc_ty_utils = { path = "../rustc_ty_utils" }
-tempfile = "3.0.5"
+tempfile = "3.2"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["libloaderapi"] }
untracked!(incremental, Some(String::from("abc")));
// `link_arg` is omitted because it just forwards to `link_args`.
untracked!(link_args, vec![String::from("abc"), String::from("def")]);
- untracked!(link_dead_code, Some(true));
untracked!(link_self_contained, Some(true));
untracked!(linker, Some(PathBuf::from("linker")));
untracked!(linker_flavor, Some(LinkerFlavor::Gcc));
tracked!(force_unwind_tables, Some(true));
tracked!(inline_threshold, Some(0xf007ba11));
tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto);
+ tracked!(link_dead_code, Some(true));
tracked!(llvm_args, vec![String::from("1"), String::from("2")]);
tracked!(lto, LtoCli::Fat);
tracked!(metadata, vec![String::from("A"), String::from("B")]);
false
}
- /// test if experssion is the literal `0`
+ /// test if expression is the literal `0`
fn is_zero(expr: &hir::Expr<'_>) -> bool {
match &expr.kind {
rustc_hir::ExprKind::Lit(ref lit) => {
fn visit_anon_const(&mut self, c: &'a ast::AnonConst) {
run_early_pass!(self, check_anon_const, c);
+ self.check_id(c.id);
ast_visit::walk_anon_const(self, c);
}
Some(lvl) => lvl,
};
- let meta = unwrap_or!(attr.meta(), continue);
self.sess.mark_attr_used(attr);
- let mut metas = unwrap_or!(meta.meta_item_list(), continue);
+ let mut metas = unwrap_or!(attr.meta_item_list(), continue);
if metas.is_empty() {
// FIXME (#55112): issue unused-attributes lint for `#[level()]`
ast::MetaItemKind::Word => {} // actual lint names handled later
ast::MetaItemKind::NameValue(ref name_value) => {
if item.path == sym::reason {
- // found reason, reslice meta list to exclude it
- metas = &metas[0..metas.len() - 1];
// FIXME (#55112): issue unused-attributes lint if we thereby
// don't have any lint names (`#[level(reason = "foo")]`)
if let ast::LitKind::Str(rationale, _) = name_value.kind {
.span_label(name_value.span, "reason must be a string literal")
.emit();
}
+ // found reason, reslice meta list to exclude it
+ metas.pop().unwrap();
} else {
bad_attr(item.span)
.span_label(item.span, "bad attribute argument")
}
for li in metas {
- let meta_item = match li.meta_item() {
- Some(meta_item) if meta_item.is_word() => meta_item,
+ let sp = li.span();
+ let mut meta_item = match li {
+ ast::NestedMetaItem::MetaItem(meta_item) if meta_item.is_word() => meta_item,
_ => {
- let sp = li.span();
let mut err = bad_attr(sp);
let mut add_label = true;
if let Some(item) = li.meta_item() {
continue;
}
- Some(tool_ident.name)
+ Some(meta_item.path.segments.remove(0).ident.name)
} else {
None
};
- let name = meta_item.path.segments.last().expect("empty lint name").ident.name;
- let lint_result = store.check_lint_name(&name.as_str(), tool_name);
+ let name = pprust::path_to_string(&meta_item.path);
+ let lint_result = store.check_lint_name(&name, tool_name);
match &lint_result {
CheckLintNameResult::Ok(ids) => {
- let src = LintLevelSource::Node(name, li.span(), reason);
+ let src = LintLevelSource::Node(
+ meta_item.path.segments.last().expect("empty lint name").ident.name,
+ sp,
+ reason,
+ );
for &id in *ids {
self.check_gated_lint(id, attr.span);
self.insert_spec(&mut specs, id, (level, src));
let complete_name = &format!("{}::{}", tool_name.unwrap(), name);
let src = LintLevelSource::Node(
Symbol::intern(complete_name),
- li.span(),
+ sp,
reason,
);
for id in ids {
lint,
lvl,
src,
- Some(li.span().into()),
+ Some(sp.into()),
|lint| {
let msg = format!(
"lint name `{}` is deprecated \
);
lint.build(&msg)
.span_suggestion(
- li.span(),
+ sp,
"change it to",
new_lint_name.to_string(),
Applicability::MachineApplicable,
let src = LintLevelSource::Node(
Symbol::intern(&new_lint_name),
- li.span(),
+ sp,
reason,
);
for id in ids {
lint,
renamed_lint_level,
src,
- Some(li.span().into()),
+ Some(sp.into()),
|lint| {
let mut err = lint.build(&msg);
if let Some(new_name) = &renamed {
err.span_suggestion(
- li.span(),
+ sp,
"use the new name",
new_name.to_string(),
Applicability::MachineApplicable,
let lint = builtin::UNKNOWN_LINTS;
let (level, src) =
self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess);
- struct_lint_level(
- self.sess,
- lint,
- level,
- src,
- Some(li.span().into()),
- |lint| {
- let name = if let Some(tool_name) = tool_name {
- format!("{}::{}", tool_name, name)
- } else {
- name.to_string()
- };
- let mut db = lint.build(&format!("unknown lint: `{}`", name));
- if let Some(suggestion) = suggestion {
- db.span_suggestion(
- li.span(),
- "did you mean",
- suggestion.to_string(),
- Applicability::MachineApplicable,
- );
- }
- db.emit();
- },
- );
+ struct_lint_level(self.sess, lint, level, src, Some(sp.into()), |lint| {
+ let name = if let Some(tool_name) = tool_name {
+ format!("{}::{}", tool_name, name)
+ } else {
+ name.to_string()
+ };
+ let mut db = lint.build(&format!("unknown lint: `{}`", name));
+ if let Some(suggestion) = suggestion {
+ db.span_suggestion(
+ sp,
+ "did you mean",
+ suggestion.to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+ db.emit();
+ });
}
}
// If this lint was renamed, apply the new lint instead of ignoring the attribute.
// Ignore any errors or warnings that happen because the new name is inaccurate
// NOTE: `new_name` already includes the tool name, so we don't have to add it again.
if let CheckLintNameResult::Ok(ids) = store.check_lint_name(&new_name, None) {
- let src =
- LintLevelSource::Node(Symbol::intern(&new_name), li.span(), reason);
+ let src = LintLevelSource::Node(Symbol::intern(&new_name), sp, reason);
for &id in ids {
self.check_gated_lint(id, attr.span);
self.insert_spec(&mut specs, id, (level, src));
///
/// ```rust,compile_fail
/// # #![allow(unused)]
- /// #![feature(non_ascii_idents)]
/// #![deny(non_ascii_idents)]
/// fn main() {
/// let föö = 1;
///
/// ### Explanation
///
- /// Currently on stable Rust, identifiers must contain ASCII characters.
- /// The [`non_ascii_idents`] nightly-only feature allows identifiers to
- /// contain non-ASCII characters. This lint allows projects that wish to
- /// retain the limit of only using ASCII characters to switch this lint to
- /// "forbid" (for example to ease collaboration or for security reasons).
+ /// This lint allows projects that wish to retain the limit of only using
+ /// ASCII characters to switch this lint to "forbid" (for example to ease
+ /// collaboration or for security reasons).
/// See [RFC 2457] for more details.
///
- /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html
/// [RFC 2457]: https://github.com/rust-lang/rfcs/blob/master/text/2457-non-ascii-idents.md
pub NON_ASCII_IDENTS,
Allow,
///
/// ```rust
/// # #![allow(unused)]
- /// #![feature(non_ascii_idents)]
/// const µ: f64 = 0.000001;
/// ```
///
///
/// ### Explanation
///
- /// With the [`non_ascii_idents`] nightly-only feature enabled,
- /// identifiers are allowed to use non-ASCII characters. This lint warns
- /// about using characters which are not commonly used, and may cause
- /// visual confusion.
+ /// This lint warns about using characters which are not commonly used, and may
+ /// cause visual confusion.
///
/// This lint is triggered by identifiers that contain a codepoint that is
/// not part of the set of "Allowed" codepoints as described by [Unicode®
/// that if you "forbid" this lint that existing code may fail in the
/// future.
///
- /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html
/// [TR39Allowed]: https://www.unicode.org/reports/tr39/#General_Security_Profile
pub UNCOMMON_CODEPOINTS,
Warn,
/// ### Example
///
/// ```rust
- /// #![feature(non_ascii_idents)]
- ///
/// // Latin Capital Letter E With Caron
/// pub const Ě: i32 = 1;
/// // Latin Capital Letter E With Breve
///
/// ### Explanation
///
- /// With the [`non_ascii_idents`] nightly-only feature enabled,
- /// identifiers are allowed to use non-ASCII characters. This lint warns
- /// when different identifiers may appear visually similar, which can
- /// cause confusion.
+ /// This lint warns when different identifiers may appear visually similar,
+ /// which can cause confusion.
///
/// The confusable detection algorithm is based on [Unicode® Technical
/// Standard #39 Unicode Security Mechanisms Section 4 Confusable
/// Beware that if you "forbid" this lint that existing code may fail in
/// the future.
///
- /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html
/// [TR39Confusable]: https://www.unicode.org/reports/tr39/#Confusable_Detection
pub CONFUSABLE_IDENTS,
Warn,
/// ### Example
///
/// ```rust
- /// #![feature(non_ascii_idents)]
- ///
/// // The Japanese katakana character エ can be confused with the Han character 工.
/// const エ: &'static str = "アイウ";
/// ```
///
/// ### Explanation
///
- /// With the [`non_ascii_idents`] nightly-only feature enabled,
- /// identifiers are allowed to use non-ASCII characters. This lint warns
- /// when characters between different scripts may appear visually similar,
- /// which can cause confusion.
+ /// This lint warns when characters between different scripts may appear
+ /// visually similar, which can cause confusion.
///
/// If the crate contains other identifiers in the same script that have
/// non-confusable characters, then this lint will *not* be issued. For
/// Note that the set of confusable characters may change over time.
/// Beware that if you "forbid" this lint that existing code may fail in
/// the future.
- ///
- /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html
pub MIXED_SCRIPT_CONFUSABLES,
Warn,
"detects Unicode scripts whose mixed script confusables codepoints are solely used",
///
/// On x86, `asm!` uses the intel assembly syntax by default. While this
/// can be switched using assembler directives like `.att_syntax`, using the
- /// `att_syntax` option is recomended instead because it will also properly
+ /// `att_syntax` option is recommended instead because it will also properly
/// prefix register placeholders with `%` as required by AT&T syntax.
pub BAD_ASM_STYLE,
Warn,
/// Statics with an uninhabited type can never be initialized, so they are impossible to define.
/// However, this can be side-stepped with an `extern static`, leading to problems later in the
/// compiler which assumes that there are no initialized uninhabited places (such as locals or
- /// statics). This was accientally allowed, but is being phased out.
+ /// statics). This was accidentally allowed, but is being phased out.
pub UNINHABITED_STATIC,
Warn,
"uninhabited static",
};
}
+declare_lint! {
+ /// The `large_assignments` lint detects when objects of large
+ /// types are being moved around.
+ ///
+ /// ### Example
+ ///
+ /// ```rust,ignore (can crash on some platforms)
+ /// let x = [0; 50000];
+ /// let y = x;
+ /// ```
+ ///
+ /// produces:
+ ///
+ /// ```text
+ /// warning: moving a large value
+ /// --> $DIR/move-large.rs:1:3
+ /// let y = x;
+ /// - Copied large value here
+ /// ```
+ ///
+ /// ### Explanation
+ ///
+ /// When using a large type in a plain assignment or in a function
+ /// argument, idiomatic code can be inefficient.
+ /// Ideally appropriate optimizations would resolve this, but such
+ /// optimizations are only done in a best-effort manner.
+ /// This lint will trigger on all sites of large moves and thus allow the
+ /// user to resolve them in code.
+ pub LARGE_ASSIGNMENTS,
+ Warn,
+ "detects large moves or copies",
+}
+
declare_lint_pass! {
/// Does nothing as a lint pass, but registers some `Lint`s
/// that are used by other parts of the compiler.
LEGACY_DERIVE_HELPERS,
PROC_MACRO_BACK_COMPAT,
OR_PATTERNS_BACK_COMPAT,
+ LARGE_ASSIGNMENTS,
]
}
F->setAttributes(PALNew);
}
-// enable fpmath flag UnsafeAlgebra
-extern "C" void LLVMRustSetHasUnsafeAlgebra(LLVMValueRef V) {
+// Enable a fast-math flag
+//
+// https://llvm.org/docs/LangRef.html#fast-math-flags
+extern "C" void LLVMRustSetFastMath(LLVMValueRef V) {
if (auto I = dyn_cast<Instruction>(unwrap<Value>(V))) {
I->setFast(true);
}
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef)
+#if LLVM_VERSION_LT(13, 0)
+using LLVMInlineAsmDiagHandlerTy = LLVMContext::InlineAsmDiagHandlerTy;
+#else
+using LLVMInlineAsmDiagHandlerTy = void*;
+#endif
+
extern "C" void LLVMRustSetInlineAsmDiagnosticHandler(
- LLVMContextRef C, LLVMContext::InlineAsmDiagHandlerTy H, void *CX) {
+ LLVMContextRef C, LLVMInlineAsmDiagHandlerTy H, void *CX) {
+ // Diagnostic handlers were unified in LLVM change 5de2d189e6ad, so starting
+ // with LLVM 13 this function is gone.
+#if LLVM_VERSION_LT(13, 0)
unwrap(C)->setInlineAsmDiagnosticHandler(H, CX);
+#endif
}
extern "C" bool LLVMRustUnpackSMDiagnostic(LLVMSMDiagnosticRef DRef,
if dep.is_none() {
self.used_extern_options.insert(name);
}
- self.maybe_resolve_crate(name, dep_kind, dep)
- .unwrap_or_else(|err| err.report(self.sess, span))
+ self.maybe_resolve_crate(name, dep_kind, dep).unwrap_or_else(|err| {
+ let missing_core =
+ self.maybe_resolve_crate(sym::core, CrateDepKind::Explicit, None).is_err();
+ err.report(&self.sess, span, missing_core)
+ })
}
fn maybe_resolve_crate<'b>(
) -> (PathBuf, CrateDisambiguator) {
match find_plugin_registrar_impl(sess, metadata_loader, name) {
Ok(res) => res,
- Err(err) => err.report(sess, span),
+ // `core` is always available if we got as far as loading plugins.
+ Err(err) => err.report(sess, span, false),
}
}
}
impl CrateError {
- crate fn report(self, sess: &Session, span: Span) -> ! {
+ crate fn report(self, sess: &Session, span: Span, missing_core: bool) -> ! {
let mut err = match self {
CrateError::NonAsciiName(crate_name) => sess.struct_span_err(
span,
if (crate_name == sym::std || crate_name == sym::core)
&& locator.triple != TargetTriple::from_triple(config::host_triple())
{
- err.note(&format!("the `{}` target may not be installed", locator.triple));
+ if missing_core {
+ err.note(&format!(
+ "the `{}` target may not be installed",
+ locator.triple
+ ));
+ } else {
+ err.note(&format!(
+ "the `{}` target may not support the standard library",
+ locator.triple
+ ));
+ }
+ if missing_core && std::env::var("RUSTUP_HOME").is_ok() {
+ err.help(&format!(
+ "consider downloading the target with `rustup target add {}`",
+ locator.triple
+ ));
+ }
+ // Suggest using #![no_std]. #[no_core] is unstable and not really supported anyway.
+ // NOTE: this is a dummy span if `extern crate std` was injected by the compiler.
+ // If it's not a dummy, that means someone added `extern crate std` explicitly and `#![no_std]` won't help.
+ if !missing_core && span.is_dummy() {
+ let current_crate =
+ sess.opts.crate_name.as_deref().unwrap_or("<unknown>");
+ err.note(&format!(
+ "`std` is required by `{}` because it does not declare `#![no_std]`",
+ current_crate
+ ));
+ }
+ if sess.is_nightly_build() && std::env::var("CARGO").is_ok() {
+ err.help("consider building the standard library from source with `cargo build -Zbuild-std`");
+ }
} else if crate_name == sym::profiler_builtins {
err.note(&"the compiler may have been built without the profiler runtime");
}
data.paren_sugar,
data.has_auto_impl,
data.is_marker,
+ data.skip_array_during_method_dispatch,
data.specialization_kind,
self.def_path_hash(item_id),
)
false,
false,
false,
+ false,
ty::trait_def::TraitSpecializationKind::None,
self.def_path_hash(item_id),
),
paren_sugar: trait_def.paren_sugar,
has_auto_impl: self.tcx.trait_is_auto(def_id),
is_marker: trait_def.is_marker,
+ skip_array_during_method_dispatch: trait_def.skip_array_during_method_dispatch,
specialization_kind: trait_def.specialization_kind,
};
paren_sugar: bool,
has_auto_impl: bool,
is_marker: bool,
+ skip_array_during_method_dispatch: bool,
specialization_kind: ty::trait_def::TraitSpecializationKind,
}
//! `DepNode` definition happens in the `define_dep_nodes!()` macro. This macro
//! defines the `DepKind` enum. Each `DepKind` has its own parameters that are
//! needed at runtime in order to construct a valid `DepNode` fingerprint.
-//! However, only `CompileCodegenUnit` is constructed explicitly (with
-//! `make_compile_codegen_unit`).
+//! However, only `CompileCodegenUnit` and `CompileMonoItem` are constructed
+//! explicitly (with `make_compile_codegen_unit` cq `make_compile_mono_item`).
//!
//! Because the macro sees what parameters a given `DepKind` requires, it can
//! "infer" some properties for each kind of `DepNode`:
//! `DefId` it was computed from. In other cases, too much information gets
//! lost during fingerprint computation.
//!
-//! `make_compile_codegen_unit`, together with `DepNode::new()`, ensures that only
-//! valid `DepNode` instances can be constructed. For example, the API does not
-//! allow for constructing parameterless `DepNode`s with anything other
-//! than a zeroed out fingerprint. More generally speaking, it relieves the
-//! user of the `DepNode` API of having to know how to compute the expected
-//! fingerprint for a given set of node parameters.
+//! `make_compile_codegen_unit` and `make_compile_mono_items`, together with
+//! `DepNode::new()`, ensures that only valid `DepNode` instances can be
+//! constructed. For example, the API does not allow for constructing
+//! parameterless `DepNode`s with anything other than a zeroed out fingerprint.
+//! More generally speaking, it relieves the user of the `DepNode` API of
+//! having to know how to compute the expected fingerprint for a given set of
+//! node parameters.
//!
//! [dependency graph]: https://rustc-dev-guide.rust-lang.org/query.html
+use crate::mir::mono::MonoItem;
use crate::ty::TyCtxt;
use rustc_data_structures::fingerprint::Fingerprint;
can_reconstruct_query_key: || false,
};
+ pub const CompileMonoItem: DepKindStruct = DepKindStruct {
+ has_params: true,
+ is_anon: false,
+ is_eval_always: false,
+
+ can_reconstruct_query_key: || false,
+ };
+
macro_rules! define_query_dep_kinds {
($(
[$($attrs:tt)*]
// WARNING: if `Symbol` is changed, make sure you update `make_compile_codegen_unit` below.
[] CompileCodegenUnit(Symbol),
+
+ // WARNING: if `MonoItem` is changed, make sure you update `make_compile_mono_item` below.
+ // Only used by rustc_codegen_cranelift
+ [] CompileMonoItem(MonoItem),
]);
// WARNING: `construct` is generic and does not know that `CompileCodegenUnit` takes `Symbol`s as keys.
DepNode::construct(tcx, DepKind::CompileCodegenUnit, &name)
}
+// WARNING: `construct` is generic and does not know that `CompileMonoItem` takes `MonoItem`s as keys.
+// Be very careful changing this type signature!
+crate fn make_compile_mono_item(tcx: TyCtxt<'tcx>, mono_item: &MonoItem<'tcx>) -> DepNode {
+ DepNode::construct(tcx, DepKind::CompileMonoItem, mono_item)
+}
+
pub type DepNode = rustc_query_system::dep_graph::DepNode<DepKind>;
// We keep a lot of `DepNode`s in memory during compilation. It's not
SerializedDepNodeIndex, WorkProduct, WorkProductId,
};
-crate use dep_node::make_compile_codegen_unit;
pub use dep_node::{label_strs, DepKind, DepNode, DepNodeExt};
+crate use dep_node::{make_compile_codegen_unit, make_compile_mono_item};
pub type DepGraph = rustc_query_system::dep_graph::DepGraph<DepKind>;
pub type TaskDeps = rustc_query_system::dep_graph::TaskDeps<DepKind>;
#![feature(bool_to_option)]
#![feature(box_patterns)]
#![feature(box_syntax)]
-#![feature(const_fn)]
#![feature(const_panic)]
#![feature(core_intrinsics)]
#![feature(discriminant_kind)]
/// #[cmse_nonsecure_entry]: with a TrustZone-M extension, declare a
/// function as an entry function from Non-Secure code.
const CMSE_NONSECURE_ENTRY = 1 << 14;
+ /// `#[no_coverage]`: indicates that the function should be ignored by
+ /// the MIR `InstrumentCoverage` pass and not added to the coverage map
+ /// during codegen.
+ const NO_COVERAGE = 1 << 15;
}
}
-//! Registering limits, recursion_limit, type_length_limit and const_eval_limit
+//! Registering limits:
+//! * recursion_limit,
+//! * move_size_limit,
+//! * type_length_limit, and
+//! * const_eval_limit
//!
//! There are various parts of the compiler that must impose arbitrary limits
//! on how deeply they recurse to prevent stack overflow. Users can override
use crate::bug;
use rustc_ast as ast;
use rustc_data_structures::sync::OnceCell;
-use rustc_session::{Limit, Session};
+use rustc_session::Session;
use rustc_span::symbol::{sym, Symbol};
use std::num::IntErrorKind;
pub fn update_limits(sess: &Session, krate: &ast::Crate) {
update_limit(sess, krate, &sess.recursion_limit, sym::recursion_limit, 128);
+ update_limit(sess, krate, &sess.move_size_limit, sym::move_size_limit, 0);
update_limit(sess, krate, &sess.type_length_limit, sym::type_length_limit, 1048576);
update_limit(sess, krate, &sess.const_eval_limit, sym::const_eval_limit, 1_000_000);
}
fn update_limit(
sess: &Session,
krate: &ast::Crate,
- limit: &OnceCell<Limit>,
+ limit: &OnceCell<impl From<usize> + std::fmt::Debug>,
name: Symbol,
default: usize,
) {
if let Some(s) = attr.value_str() {
match s.as_str().parse() {
Ok(n) => {
- limit.set(Limit::new(n)).unwrap();
+ limit.set(From::from(n)).unwrap();
return;
}
Err(e) => {
}
}
}
- limit.set(Limit::new(default)).unwrap();
+ limit.set(From::from(default)).unwrap();
}
/// escape into 'static and should have no local cleanup scope.
rvalue_scopes: FxHashMap<hir::ItemLocalId, Option<Scope>>,
- /// Encodes the hierarchy of fn bodies. Every fn body (including
- /// closures) forms its own distinct region hierarchy, rooted in
- /// the block that is the fn body. This map points from the ID of
- /// that root block to the ID of the root block for the enclosing
- /// fn, if any. Thus the map structures the fn bodies into a
- /// hierarchy based on their lexical mapping. This is used to
- /// handle the relationships between regions in a fn and in a
- /// closure defined by that fn. See the "Modeling closures"
- /// section of the README in infer::region_constraints for
- /// more details.
- closure_tree: FxHashMap<hir::ItemLocalId, hir::ItemLocalId>,
-
/// If there are any `yield` nested within a scope, this map
/// stores the `Span` of the last one and its index in the
/// postorder of the Visitor traversal on the HIR.
self.destruction_scopes.get(&n).cloned()
}
- /// Records that `sub_closure` is defined within `sup_closure`. These IDs
- /// should be the ID of the block that is the fn body, which is
- /// also the root of the region hierarchy for that fn.
- pub fn record_closure_parent(
- &mut self,
- sub_closure: hir::ItemLocalId,
- sup_closure: hir::ItemLocalId,
- ) {
- debug!(
- "record_closure_parent(sub_closure={:?}, sup_closure={:?})",
- sub_closure, sup_closure
- );
- assert!(sub_closure != sup_closure);
- let previous = self.closure_tree.insert(sub_closure, sup_closure);
- assert!(previous.is_none());
- }
-
pub fn record_var_scope(&mut self, var: hir::ItemLocalId, lifetime: Scope) {
debug!("record_var_scope(sub={:?}, sup={:?})", var, lifetime);
assert!(var != lifetime.item_local_id());
ref var_map,
ref destruction_scopes,
ref rvalue_scopes,
- ref closure_tree,
ref yield_in_scope,
} = *self;
var_map.hash_stable(hcx, hasher);
destruction_scopes.hash_stable(hcx, hasher);
rvalue_scopes.hash_stable(hcx, hasher);
- closure_tree.hash_stable(hcx, hasher);
yield_in_scope.hash_stable(hcx, hasher);
}
}
/// Packages the kind of error we got from the const code interpreter
/// up with a Rust-level backtrace of where the error occurred.
-/// Thsese should always be constructed by calling `.into()` on
+/// These should always be constructed by calling `.into()` on
/// a `InterpError`. In `rustc_mir::interpret`, we have `throw_err_*`
/// macros for this.
#[derive(Debug)]
use crate::ty::subst::{Subst, SubstsRef};
use crate::ty::{self, List, Ty, TyCtxt};
use crate::ty::{AdtDef, InstanceDef, Region, ScalarInt, UserTypeAnnotationIndex};
-use rustc_hir as hir;
use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
use rustc_hir::{self, GeneratorKind};
+use rustc_hir::{self as hir, HirId};
use rustc_target::abi::{Size, VariantIdx};
use polonius_engine::Atom;
/// Marks the start of a "coverage region", injected with '-Zinstrument-coverage'. A
/// `Coverage` statement carries metadata about the coverage region, used to inject a coverage
/// map into the binary. If `Coverage::kind` is a `Counter`, the statement also generates
- /// executable code, to increment a counter varible at runtime, each time the code region is
+ /// executable code, to increment a counter variable at runtime, each time the code region is
/// executed.
Coverage(Box<Coverage>),
}
}
+impl SourceScope {
+ /// Finds the original HirId this MIR item came from.
+ /// This is necessary after MIR optimizations, as otherwise we get a HirId
+ /// from the function that was inlined instead of the function call site.
+ pub fn lint_root(
+ self,
+ source_scopes: &IndexVec<SourceScope, SourceScopeData<'tcx>>,
+ ) -> Option<HirId> {
+ let mut data = &source_scopes[self];
+ // FIXME(oli-obk): we should be able to just walk the `inlined_parent_scope`, but it
+ // does not work as I thought it would. Needs more investigation and documentation.
+ while data.inlined.is_some() {
+ trace!(?data);
+ data = &source_scopes[data.parent_scope.unwrap()];
+ }
+ trace!(?data);
+ match &data.local_data {
+ ClearCrossCrate::Set(data) => Some(data.lint_root),
+ ClearCrossCrate::Clear => None,
+ }
+ }
+}
+
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)]
pub struct SourceScopeData<'tcx> {
pub span: Span,
}
.map(|hir_id| tcx.hir().span(hir_id))
}
+
+ // Only used by rustc_codegen_cranelift
+ pub fn codegen_dep_node(&self, tcx: TyCtxt<'tcx>) -> DepNode {
+ crate::dep_graph::make_compile_mono_item(tcx, self)
+ }
}
impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for MonoItem<'tcx> {
#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
pub enum UnsafetyViolationKind {
- /// Only permitted in regular `fn`s, prohibited in `const fn`s.
+ /// Unsafe operation outside `unsafe`.
General,
- /// Permitted both in `const fn`s and regular `fn`s.
- GeneralAndConstFn,
/// Unsafe operation in an `unsafe fn` but outside an `unsafe` block.
/// Has to be handled as a lint for backwards compatibility.
UnsafeFn,
}
/// Collects the associated items defined on a trait or impl.
- query associated_items(key: DefId) -> ty::AssociatedItems<'tcx> {
+ query associated_items(key: DefId) -> ty::AssocItems<'tcx> {
storage(ArenaCacheSelector<'tcx>)
desc { |tcx| "collecting associated items of {}", tcx.def_path_str(key) }
}
desc { "checking if the crate is_compiler_builtins" }
}
query has_global_allocator(_: CrateNum) -> bool {
+ // This query depends on untracked global state in CStore
+ eval_always
fatal_cycle
desc { "checking if the crate has_global_allocator" }
}
desc { "testing if a region is late bound" }
}
/// For a given item (like a struct), gets the default lifetimes to be used
- /// for each paramter if a trait object were to be passed for that parameter.
+ /// for each parameter if a trait object were to be passed for that parameter.
/// For example, for `struct Foo<'a, T, U>`, this would be `['static, 'static]`.
/// For `struct Foo<'a, T: 'a, U>`, this would instead be `['a, 'static]`.
query object_lifetime_defaults_map(_: LocalDefId)
/// Associated const.
AssocConst(Symbol, Span),
+
+ /// GAT
+ GAT(Symbol, Span),
}
impl ObjectSafetyViolation {
format!("it contains associated `const` `{}`", name).into()
}
ObjectSafetyViolation::AssocConst(..) => "it contains this associated `const`".into(),
+ ObjectSafetyViolation::GAT(name, _) => {
+ format!("it contains the generic associated type `{}`", name).into()
+ }
}
}
);
}
ObjectSafetyViolation::AssocConst(name, _)
+ | ObjectSafetyViolation::GAT(name, _)
| ObjectSafetyViolation::Method(name, ..) => {
err.help(&format!("consider moving `{}` to another trait", name));
}
ObjectSafetyViolation::SupertraitSelf(spans)
| ObjectSafetyViolation::SizedSelf(spans) => spans.clone(),
ObjectSafetyViolation::AssocConst(_, span)
+ | ObjectSafetyViolation::GAT(_, span)
| ObjectSafetyViolation::Method(_, _, span)
if *span != DUMMY_SP =>
{
/// it is relatively expensive. Instead, items are indexed by `Symbol` and hygienic comparison is
/// done only on items with the same name.
#[derive(Debug, Clone, PartialEq, HashStable)]
-pub struct AssociatedItems<'tcx> {
+pub struct AssocItems<'tcx> {
pub(super) items: SortedIndexMultiMap<u32, Symbol, &'tcx ty::AssocItem>,
}
-impl<'tcx> AssociatedItems<'tcx> {
+impl<'tcx> AssocItems<'tcx> {
/// Constructs an `AssociatedItems` map from a series of `ty::AssocItem`s in definition order.
pub fn new(items_in_def_order: impl IntoIterator<Item = &'tcx ty::AssocItem>) -> Self {
let items = items_in_def_order.into_iter().map(|item| (item.ident.name, item)).collect();
- AssociatedItems { items }
+ AssocItems { items }
}
/// Returns a slice of associated items in the order they were defined.
}
}
- /// Returns the `LocalDefId` of the closure that captureed this Place
+ /// Returns the `LocalDefId` of the closure that captured this Place
pub fn get_closure_local_def_id(&self) -> LocalDefId {
match self.place.base {
HirPlaceBase::Upvar(upvar_id) => upvar_id.closure_expr_id,
GenericParamDefKind::Lifetime => ast::ParamKindOrd::Lifetime,
GenericParamDefKind::Type { .. } => ast::ParamKindOrd::Type,
GenericParamDefKind::Const { .. } => {
- ast::ParamKindOrd::Const { unordered: tcx.features().const_generics }
+ ast::ParamKindOrd::Const { unordered: tcx.features().unordered_const_ty_params() }
}
}
}
/// and thus `impl`s of it are allowed to overlap.
pub is_marker: bool,
+ /// If `true`, then this trait has the `#[rustc_skip_array_during_method_dispatch]`
+ /// attribute, indicating that editions before 2021 should not consider this trait
+ /// during method dispatch if the receiver is an array.
+ pub skip_array_during_method_dispatch: bool,
+
/// Used to determine whether the standard library is allowed to specialize
/// on this trait.
pub specialization_kind: TraitSpecializationKind,
paren_sugar: bool,
has_auto_impl: bool,
is_marker: bool,
+ skip_array_during_method_dispatch: bool,
specialization_kind: TraitSpecializationKind,
def_path_hash: DefPathHash,
) -> TraitDef {
paren_sugar,
has_auto_impl,
is_marker,
+ skip_array_during_method_dispatch,
specialization_kind,
def_path_hash,
}
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_macros::HashStable;
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::DUMMY_SP;
use rustc_target::abi::{Integer, Size, TargetDataLayout};
use smallvec::SmallVec;
-use std::{cmp, fmt, iter};
+use std::{fmt, iter};
#[derive(Copy, Clone, Debug)]
pub struct Discr<'tcx> {
}
}
-/// Describes whether a type is representable. For types that are not
-/// representable, 'SelfRecursive' and 'ContainsRecursive' are used to
-/// distinguish between types that are recursive with themselves and types that
-/// contain a different recursive type. These cases can therefore be treated
-/// differently when reporting errors.
-///
-/// The ordering of the cases is significant. They are sorted so that cmp::max
-/// will keep the "more erroneous" of two values.
-#[derive(Clone, PartialOrd, Ord, Eq, PartialEq, Debug)]
-pub enum Representability {
- Representable,
- ContainsRecursive,
- SelfRecursive(Vec<Span>),
-}
-
impl<'tcx> TyCtxt<'tcx> {
/// Creates a hash of the type `Ty` which will be the same no matter what crate
/// context it's calculated within. This is used by the `type_id` intrinsic.
}
}
- /// Check whether a type is representable. This means it cannot contain unboxed
- /// structural recursion. This check is needed for structs and enums.
- pub fn is_representable(&'tcx self, tcx: TyCtxt<'tcx>, sp: Span) -> Representability {
- // Iterate until something non-representable is found
- fn fold_repr<It: Iterator<Item = Representability>>(iter: It) -> Representability {
- iter.fold(Representability::Representable, |r1, r2| match (r1, r2) {
- (Representability::SelfRecursive(v1), Representability::SelfRecursive(v2)) => {
- Representability::SelfRecursive(v1.into_iter().chain(v2).collect())
- }
- (r1, r2) => cmp::max(r1, r2),
- })
- }
-
- fn are_inner_types_recursive<'tcx>(
- tcx: TyCtxt<'tcx>,
- sp: Span,
- seen: &mut Vec<Ty<'tcx>>,
- representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
- ty: Ty<'tcx>,
- ) -> Representability {
- match ty.kind() {
- Tuple(..) => {
- // Find non representable
- fold_repr(ty.tuple_fields().map(|ty| {
- is_type_structurally_recursive(tcx, sp, seen, representable_cache, ty)
- }))
- }
- // Fixed-length vectors.
- // FIXME(#11924) Behavior undecided for zero-length vectors.
- Array(ty, _) => {
- is_type_structurally_recursive(tcx, sp, seen, representable_cache, ty)
- }
- Adt(def, substs) => {
- // Find non representable fields with their spans
- fold_repr(def.all_fields().map(|field| {
- let ty = field.ty(tcx, substs);
- let span = match field
- .did
- .as_local()
- .map(|id| tcx.hir().local_def_id_to_hir_id(id))
- .and_then(|id| tcx.hir().find(id))
- {
- Some(hir::Node::Field(field)) => field.ty.span,
- _ => sp,
- };
- match is_type_structurally_recursive(
- tcx,
- span,
- seen,
- representable_cache,
- ty,
- ) {
- Representability::SelfRecursive(_) => {
- Representability::SelfRecursive(vec![span])
- }
- x => x,
- }
- }))
- }
- Closure(..) => {
- // this check is run on type definitions, so we don't expect
- // to see closure types
- bug!("requires check invoked on inapplicable type: {:?}", ty)
- }
- _ => Representability::Representable,
- }
- }
-
- fn same_struct_or_enum<'tcx>(ty: Ty<'tcx>, def: &'tcx ty::AdtDef) -> bool {
- match *ty.kind() {
- Adt(ty_def, _) => ty_def == def,
- _ => false,
- }
- }
-
- // Does the type `ty` directly (without indirection through a pointer)
- // contain any types on stack `seen`?
- fn is_type_structurally_recursive<'tcx>(
- tcx: TyCtxt<'tcx>,
- sp: Span,
- seen: &mut Vec<Ty<'tcx>>,
- representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
- ty: Ty<'tcx>,
- ) -> Representability {
- debug!("is_type_structurally_recursive: {:?} {:?}", ty, sp);
- if let Some(representability) = representable_cache.get(ty) {
- debug!(
- "is_type_structurally_recursive: {:?} {:?} - (cached) {:?}",
- ty, sp, representability
- );
- return representability.clone();
- }
-
- let representability =
- is_type_structurally_recursive_inner(tcx, sp, seen, representable_cache, ty);
-
- representable_cache.insert(ty, representability.clone());
- representability
- }
-
- fn is_type_structurally_recursive_inner<'tcx>(
- tcx: TyCtxt<'tcx>,
- sp: Span,
- seen: &mut Vec<Ty<'tcx>>,
- representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
- ty: Ty<'tcx>,
- ) -> Representability {
- match ty.kind() {
- Adt(def, _) => {
- {
- // Iterate through stack of previously seen types.
- let mut iter = seen.iter();
-
- // The first item in `seen` is the type we are actually curious about.
- // We want to return SelfRecursive if this type contains itself.
- // It is important that we DON'T take generic parameters into account
- // for this check, so that Bar<T> in this example counts as SelfRecursive:
- //
- // struct Foo;
- // struct Bar<T> { x: Bar<Foo> }
-
- if let Some(&seen_type) = iter.next() {
- if same_struct_or_enum(seen_type, *def) {
- debug!("SelfRecursive: {:?} contains {:?}", seen_type, ty);
- return Representability::SelfRecursive(vec![sp]);
- }
- }
-
- // We also need to know whether the first item contains other types
- // that are structurally recursive. If we don't catch this case, we
- // will recurse infinitely for some inputs.
- //
- // It is important that we DO take generic parameters into account
- // here, so that code like this is considered SelfRecursive, not
- // ContainsRecursive:
- //
- // struct Foo { Option<Option<Foo>> }
-
- for &seen_type in iter {
- if ty::TyS::same_type(ty, seen_type) {
- debug!("ContainsRecursive: {:?} contains {:?}", seen_type, ty);
- return Representability::ContainsRecursive;
- }
- }
- }
-
- // For structs and enums, track all previously seen types by pushing them
- // onto the 'seen' stack.
- seen.push(ty);
- let out = are_inner_types_recursive(tcx, sp, seen, representable_cache, ty);
- seen.pop();
- out
- }
- _ => {
- // No need to push in other cases.
- are_inner_types_recursive(tcx, sp, seen, representable_cache, ty)
- }
- }
- }
-
- debug!("is_type_representable: {:?}", self);
-
- // To avoid a stack overflow when checking an enum variant or struct that
- // contains a different, structurally recursive type, maintain a stack
- // of seen types and check recursion for each of them (issues #3008, #3779).
- let mut seen: Vec<Ty<'_>> = Vec::new();
- let mut representable_cache = FxHashMap::default();
- let r = is_type_structurally_recursive(tcx, sp, &mut seen, &mut representable_cache, self);
- debug!("is_type_representable: {:?} is {:?}", self, r);
- r
- }
-
/// Peel off all reference types in this type until there are none left.
///
/// This method is idempotent, i.e. `ty.peel_refs().peel_refs() == ty.peel_refs()`.
);
}
}
- FnSelfUseKind::Normal { self_arg, implicit_into_iter } => {
+ FnSelfUseKind::Normal {
+ self_arg,
+ implicit_into_iter,
+ is_option_or_result,
+ } => {
if implicit_into_iter {
err.span_label(
fn_call_span,
),
);
}
+ if is_option_or_result {
+ err.span_suggestion_verbose(
+ fn_call_span.shrink_to_lo(),
+ "consider calling `.as_ref()` to borrow the type's contents",
+ "as_ref().".to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
// Avoid pointing to the same function in multiple different
// error messages.
if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span)
if decl.can_be_made_mutable() {
err.span_suggestion(
decl.source_info.span,
- "make this binding mutable",
+ "consider making this binding mutable",
format!("mut {}", name),
Applicability::MachineApplicable,
);
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(super) enum FnSelfUseKind<'tcx> {
/// A normal method call of the form `receiver.foo(a, b, c)`
- Normal { self_arg: Ident, implicit_into_iter: bool },
+ Normal {
+ self_arg: Ident,
+ implicit_into_iter: bool,
+ /// Whether the self type of the method call has an `.as_ref()` method.
+ /// Used for better diagnostics.
+ is_option_or_result: bool,
+ },
/// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)`
FnOnceCall,
/// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`)
fn_call_span.desugaring_kind(),
Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter))
);
- FnSelfUseKind::Normal { self_arg, implicit_into_iter }
+ let parent_self_ty = parent
+ .filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl)
+ .and_then(|did| match tcx.type_of(did).kind() {
+ ty::Adt(def, ..) => Some(def.did),
+ _ => None,
+ });
+ let is_option_or_result = parent_self_ty.map_or(false, |def_id| {
+ tcx.is_diagnostic_item(sym::option_type, def_id)
+ || tcx.is_diagnostic_item(sym::result_type, def_id)
+ });
+ FnSelfUseKind::Normal { self_arg, implicit_into_iter, is_option_or_result }
});
return FnSelfUse {
.starts_with(&original_method_ident.name.to_string())
})
.map(|ident| format!("{}()", ident))
+ .peekable()
});
- if let Some(suggestions) = opt_suggestions {
- err.span_suggestions(
- path_segment.ident.span,
- &format!("use mutable method"),
- suggestions,
- Applicability::MaybeIncorrect,
- );
+ if let Some(mut suggestions) = opt_suggestions {
+ if suggestions.peek().is_some() {
+ err.span_suggestions(
+ path_segment.ident.span,
+ &format!("use mutable method"),
+ suggestions,
+ Applicability::MaybeIncorrect,
+ );
+ }
}
}
};
-//! Contains utilities for generating suggestions for borrowck errors related to unsatisified
+//! Contains utilities for generating suggestions for borrowck errors related to unsatisfied
//! outlives constraints.
use std::collections::BTreeMap;
-use rustc_attr as attr;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::hir::map::blocks::FnLikeNode;
}
}
-/// Returns `true` if this function must conform to `min_const_fn`
-pub fn is_min_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
- // Bail out if the signature doesn't contain `const`
- if !tcx.is_const_fn_raw(def_id) {
- return false;
- }
-
- if tcx.features().staged_api {
- // In order for a libstd function to be considered min_const_fn
- // it needs to be stable and have no `rustc_const_unstable` attribute.
- match tcx.lookup_const_stability(def_id) {
- // `rustc_const_unstable` functions don't need to conform.
- Some(&attr::ConstStability { ref level, .. }) if level.is_unstable() => false,
- None => {
- if let Some(stab) = tcx.lookup_stability(def_id) {
- if stab.level.is_stable() {
- tcx.sess.delay_span_bug(
- tcx.def_span(def_id),
- "stable const functions must have either `rustc_const_stable` or \
- `rustc_const_unstable` attribute",
- );
- // While we errored above, because we don't know if we need to conform, we
- // err on the "safe" side and require min_const_fn.
- true
- } else {
- // Unstable functions need not conform to min_const_fn.
- false
- }
- } else {
- // Internal functions are forced to conform to min_const_fn.
- // Annotate the internal function with a const stability attribute if
- // you need to use unstable features.
- // Note: this is an arbitrary choice that does not affect stability or const
- // safety or anything, it just changes whether we need to annotate some
- // internal functions with `rustc_const_stable` or with `rustc_const_unstable`
- true
- }
- }
- // Everything else needs to conform, because it would be callable from
- // other `min_const_fn` functions.
- _ => true,
- }
- } else {
- // users enabling the `const_fn` feature gate can do what they want
- !tcx.features().const_fn
- }
-}
-
pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
let parent_id = tcx.hir().get_parent_did(hir_id);
if !parent_id.is_top_level_module() { is_const_impl_raw(tcx, parent_id) } else { false }
/// Extra machine state for CTFE, and the Machine instance
pub struct CompileTimeInterpreter<'mir, 'tcx> {
/// For now, the number of terminators that can be evaluated before we throw a resource
- /// exhuastion error.
+ /// exhaustion error.
///
/// Setting this to `0` disables the limit and allows the interpreter to run forever.
pub steps_remaining: usize,
}
}
- /// Returns `true` if the effect at `self` should be applied eariler than the effect at `other`
+ /// Returns `true` if the effect at `self` should be applied earlier than the effect at `other`
/// in forward order.
fn precedes_in_forward_order(self, other: Self) -> bool {
let ord = self
}
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
- /// Normalice `place.ptr` to a `Pointer` if this is a place and not a ZST.
+ /// Normalize `place.ptr` to a `Pointer` if this is a place and not a ZST.
/// Can be helpful to avoid lots of `force_ptr` calls later, if this place is used a lot.
#[inline]
pub fn force_op_ptr(
#![feature(bool_to_option)]
#![feature(box_patterns)]
#![feature(box_syntax)]
-#![feature(const_fn)]
#![feature(const_panic)]
#![feature(crate_visibility_modifier)]
#![feature(decl_macro)]
use rustc_middle::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable};
use rustc_middle::{middle::codegen_fn_attrs::CodegenFnAttrFlags, mir::visit::TyContext};
use rustc_session::config::EntryFnType;
+use rustc_session::lint::builtin::LARGE_ASSIGNMENTS;
use rustc_span::source_map::{dummy_spanned, respan, Span, Spanned, DUMMY_SP};
+use rustc_target::abi::Size;
use smallvec::SmallVec;
use std::iter;
use std::ops::Range;
self.super_terminator(terminator, location);
}
+ fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) {
+ self.super_operand(operand, location);
+ let limit = self.tcx.sess.move_size_limit();
+ if limit == 0 {
+ return;
+ }
+ let limit = Size::from_bytes(limit);
+ let ty = operand.ty(self.body, self.tcx);
+ let ty = self.monomorphize(ty);
+ let layout = self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty));
+ if let Ok(layout) = layout {
+ if layout.size > limit {
+ debug!(?layout);
+ let source_info = self.body.source_info(location);
+ debug!(?source_info);
+ let lint_root = source_info.scope.lint_root(&self.body.source_scopes);
+ debug!(?lint_root);
+ let lint_root = match lint_root {
+ Some(lint_root) => lint_root,
+ // This happens when the issue is in a function from a foreign crate that
+ // we monomorphized in the current crate. We can't get a `HirId` for things
+ // in other crates.
+ // FIXME: Find out where to report the lint on. Maybe simply crate-level lint root
+ // but correct span? This would make the lint at least accept crate-level lint attributes.
+ None => return,
+ };
+ self.tcx.struct_span_lint_hir(
+ LARGE_ASSIGNMENTS,
+ lint_root,
+ source_info.span,
+ |lint| {
+ let mut err = lint.build(&format!("moving {} bytes", layout.size.bytes()));
+ err.span_label(source_info.span, "value moved from here");
+ err.emit()
+ },
+ );
+ }
+ }
+ }
+
fn visit_local(
&mut self,
_place_local: &Local,
pub struct UnsizingCast;
impl NonConstOp for UnsizingCast {
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
- mcf_status_in_item(ccx)
+ if ccx.const_kind() != hir::ConstContext::ConstFn {
+ Status::Allowed
+ } else {
+ Status::Unstable(sym::const_fn_unsize)
+ }
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
- mcf_build_error(
- ccx,
+ feature_err(
+ &ccx.tcx.sess.parse_sess,
+ sym::const_fn_unsize,
span,
"unsizing casts to types besides slices are not allowed in const fn",
)
}
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
- mcf_status_in_item(ccx)
+ if ccx.const_kind() != hir::ConstContext::ConstFn {
+ Status::Allowed
+ } else {
+ Status::Unstable(sym::const_fn_trait_bound)
+ }
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
- mcf_build_error(
- ccx,
+ feature_err(
+ &ccx.tcx.sess.parse_sess,
+ sym::const_fn_trait_bound,
span,
"trait bounds other than `Sized` on const fn parameters are unstable",
)
}
}
}
-
-fn mcf_status_in_item(ccx: &ConstCx<'_, '_>) -> Status {
- if ccx.const_kind() != hir::ConstContext::ConstFn {
- Status::Allowed
- } else {
- Status::Unstable(sym::const_fn)
- }
-}
-
-fn mcf_build_error(ccx: &ConstCx<'_, 'tcx>, span: Span, msg: &str) -> DiagnosticBuilder<'tcx> {
- let mut err = struct_span_err!(ccx.tcx.sess, span, E0723, "{}", msg);
- err.note(
- "see issue #57563 <https://github.com/rust-lang/rust/issues/57563> \
- for more information",
- );
- err.help("add `#![feature(const_fn)]` to the crate attributes to enable");
- err
-}
/// Normally, we would determine what qualifications apply to each type and error when an illegal
/// operation is performed on such a type. However, this was found to be too imprecise, especially
/// in the presence of `enum`s. If only a single variant of an enum has a certain qualification, we
-/// needn't reject code unless it actually constructs and operates on the qualifed variant.
+/// needn't reject code unless it actually constructs and operates on the qualified variant.
///
/// To accomplish this, const-checking and promotion use a value-based analysis (as opposed to a
/// type-based one). Qualifications propagate structurally across variables: If a local (or a
-/// projection of a local) is assigned a qualifed value, that local itself becomes qualifed.
+/// projection of a local) is assigned a qualified value, that local itself becomes qualified.
pub trait Qualif {
/// The name of the file used to debug the dataflow analysis that computes this qualif.
const ANALYSIS_NAME: &'static str;
//! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations.
-use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorReported};
+use rustc_errors::{Applicability, Diagnostic, ErrorReported};
use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, HirId, LangItem};
use rustc_index::bit_set::BitSet;
if self.is_const_stable_const_fn() {
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
if crate::const_eval::is_parent_const_impl_raw(tcx, hir_id) {
- struct_span_err!(
- self.ccx.tcx.sess,
- self.span,
- E0723,
- "trait methods cannot be stable const fn"
- )
- .emit();
+ self.ccx
+ .tcx
+ .sess
+ .struct_span_err(self.span, "trait methods cannot be stable const fn")
+ .emit();
}
}
use std::ops::Bound;
-use crate::const_eval::is_min_const_fn;
-
pub struct UnsafetyChecker<'a, 'tcx> {
body: &'a Body<'tcx>,
body_did: LocalDefId,
const_context: bool,
- min_const_fn: bool,
violations: Vec<UnsafetyViolation>,
source_info: SourceInfo,
tcx: TyCtxt<'tcx>,
impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
fn new(
const_context: bool,
- min_const_fn: bool,
body: &'a Body<'tcx>,
body_did: LocalDefId,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Self {
- // sanity check
- if min_const_fn {
- assert!(const_context);
- }
Self {
body,
body_did,
const_context,
- min_const_fn,
violations: vec![],
source_info: SourceInfo::outermost(body.span),
tcx,
let sig = func_ty.fn_sig(self.tcx);
if let hir::Unsafety::Unsafe = sig.unsafety() {
self.require_unsafe(
- UnsafetyViolationKind::GeneralAndConstFn,
+ UnsafetyViolationKind::General,
UnsafetyViolationDetails::CallToUnsafeFunction,
)
}
match self.tcx.layout_scalar_valid_range(def.did) {
(Bound::Unbounded, Bound::Unbounded) => {}
_ => self.require_unsafe(
- UnsafetyViolationKind::GeneralAndConstFn,
+ UnsafetyViolationKind::General,
UnsafetyViolationDetails::InitializingTypeWith,
),
}
let base_ty = base.ty(self.body, self.tcx).ty;
if base_ty.is_unsafe_ptr() {
self.require_unsafe(
- UnsafetyViolationKind::GeneralAndConstFn,
+ UnsafetyViolationKind::General,
UnsafetyViolationDetails::DerefOfRawPointer,
)
}
);
if !nodrop {
self.require_unsafe(
- UnsafetyViolationKind::GeneralAndConstFn,
+ UnsafetyViolationKind::General,
UnsafetyViolationDetails::AssignToDroppingUnionField,
);
} else {
}
} else {
self.require_unsafe(
- UnsafetyViolationKind::GeneralAndConstFn,
+ UnsafetyViolationKind::General,
UnsafetyViolationDetails::AccessToUnionField,
)
}
impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
fn require_unsafe(&mut self, kind: UnsafetyViolationKind, details: UnsafetyViolationDetails) {
+ // Violations can turn out to be `UnsafeFn` during analysis, but they should not start out as such.
+ assert_ne!(kind, UnsafetyViolationKind::UnsafeFn);
+
let source_info = self.source_info;
let lint_root = self.body.source_scopes[self.source_info.scope]
.local_data
Safety::Safe => {
for violation in violations {
match violation.kind {
- UnsafetyViolationKind::GeneralAndConstFn
- | UnsafetyViolationKind::General => {}
+ UnsafetyViolationKind::General => {}
UnsafetyViolationKind::UnsafeFn => {
bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
}
if !violations.is_empty() {
self.used_unsafe.insert(hir_id);
}
- // only some unsafety is allowed in const fn
- if self.min_const_fn {
- for violation in violations {
- match violation.kind {
- // these unsafe things are stable in const fn
- UnsafetyViolationKind::GeneralAndConstFn => {}
- // these things are forbidden in const fns
- UnsafetyViolationKind::General => {
- let mut violation = *violation;
- // const fns don't need to be backwards compatible and can
- // emit these violations as a hard error instead of a backwards
- // compat lint
- violation.kind = UnsafetyViolationKind::General;
- if !self.violations.contains(&violation) {
- self.violations.push(violation)
- }
- }
- UnsafetyViolationKind::UnsafeFn => bug!(
- "`UnsafetyViolationKind::UnsafeFn` in an `ExplicitUnsafe` context"
- ),
- }
- }
- }
true
}
};
} else {
continue;
};
- self.require_unsafe(UnsafetyViolationKind::GeneralAndConstFn, details);
+ self.require_unsafe(UnsafetyViolationKind::General, details);
}
}
}
// Is `callee_features` a subset of `calling_features`?
if !callee_features.iter().all(|feature| self_features.contains(feature)) {
self.require_unsafe(
- UnsafetyViolationKind::GeneralAndConstFn,
+ UnsafetyViolationKind::General,
UnsafetyViolationDetails::CallToFunctionWith,
)
}
let param_env = tcx.param_env(def.did);
let id = tcx.hir().local_def_id_to_hir_id(def.did);
- let (const_context, min_const_fn) = match tcx.hir().body_owner_kind(id) {
- hir::BodyOwnerKind::Closure => (false, false),
- hir::BodyOwnerKind::Fn => {
- (tcx.is_const_fn_raw(def.did.to_def_id()), is_min_const_fn(tcx, def.did.to_def_id()))
- }
- hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => (true, false),
+ let const_context = match tcx.hir().body_owner_kind(id) {
+ hir::BodyOwnerKind::Closure => false,
+ hir::BodyOwnerKind::Fn => tcx.is_const_fn_raw(def.did.to_def_id()),
+ hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => true,
};
- let mut checker =
- UnsafetyChecker::new(const_context, min_const_fn, body, def.did, tcx, param_env);
+ let mut checker = UnsafetyChecker::new(const_context, body, def.did, tcx, param_env);
checker.visit_body(&body);
check_unused_unsafe(tcx, def.did, &checker.used_unsafe, &mut checker.inherited_blocks);
if unsafe_op_in_unsafe_fn_allowed(tcx, lint_root) { " function or" } else { "" };
match kind {
- UnsafetyViolationKind::GeneralAndConstFn | UnsafetyViolationKind::General => {
+ UnsafetyViolationKind::General => {
// once
struct_span_err!(
tcx.sess,
MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
};
use rustc_middle::mir::{
- AssertKind, BasicBlock, BinOp, Body, ClearCrossCrate, Constant, ConstantKind, Local, LocalDecl,
- LocalKind, Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData,
- Statement, StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
+ AssertKind, BasicBlock, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, LocalKind,
+ Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement,
+ StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
};
use rustc_middle::ty::layout::{HasTyCtxt, LayoutError, TyAndLayout};
use rustc_middle::ty::subst::{InternalSubsts, Subst};
}
fn lint_root(&self, source_info: SourceInfo) -> Option<HirId> {
- let mut data = &self.source_scopes[source_info.scope];
- // FIXME(oli-obk): we should be able to just walk the `inlined_parent_scope`, but it
- // does not work as I thought it would. Needs more investigation and documentation.
- while data.inlined.is_some() {
- trace!(?data);
- data = &self.source_scopes[data.parent_scope.unwrap()];
- }
- trace!(?data);
- match &data.local_data {
- ClearCrossCrate::Set(data) => Some(data.lint_root),
- ClearCrossCrate::Clear => None,
- }
+ source_info.scope.lint_root(&self.source_scopes)
}
fn use_ecx<F, T>(&mut self, f: F) -> Option<T>
sections
}
-/// Returns a simple string representation of a `TerminatorKind` variant, indenpendent of any
+/// Returns a simple string representation of a `TerminatorKind` variant, independent of any
/// values it might hold.
pub(super) fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str {
match kind {
use rustc_middle::hir;
use rustc_middle::hir::map::blocks::FnLikeNode;
use rustc_middle::ich::StableHashingContext;
+use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::coverage::*;
use rustc_middle::mir::{
self, BasicBlock, BasicBlockData, Coverage, SourceInfo, Statement, StatementKind, Terminator,
_ => {}
}
+ let codegen_fn_attrs = tcx.codegen_fn_attrs(mir_source.def_id());
+ if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_COVERAGE) {
+ return;
+ }
+
trace!("InstrumentCoverage starting for {:?}", mir_source.def_id());
Instrumentor::new(&self.name(), tcx, mir_body).inject_counters();
trace!("InstrumentCoverage starting for {:?}", mir_source.def_id());
let body_span = hir_body.value.span;
let source_file = source_map.lookup_source_file(body_span.lo());
let fn_sig_span = match some_fn_sig.filter(|fn_sig| {
- Lrc::ptr_eq(&source_file, &source_map.lookup_source_file(fn_sig.span.hi()))
+ fn_sig.span.ctxt() == body_span.ctxt()
+ && Lrc::ptr_eq(&source_file, &source_map.lookup_source_file(fn_sig.span.lo()))
}) {
Some(fn_sig) => fn_sig.span.with_hi(body_span.lo()),
None => body_span.shrink_to_lo(),
use rustc_middle::ty::TyCtxt;
use rustc_span::source_map::original_sp;
-use rustc_span::{BytePos, Span, SyntaxContext};
+use rustc_span::{BytePos, Span};
use std::cmp::Ordering;
/// to be).
pub(super) fn generate_coverage_spans(
mir_body: &'a mir::Body<'tcx>,
- fn_sig_span: Span,
+ fn_sig_span: Span, // Ensured to be same SourceFile and SyntaxContext as `body_span`
body_span: Span,
basic_coverage_blocks: &'a CoverageGraph,
) -> Vec<CoverageSpan> {
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::Goto { .. } => None,
+ // Call `func` operand can have a more specific span when part of a chain of calls
+ | TerminatorKind::Call { ref func, .. } => {
+ let mut span = terminator.source_info.span;
+ if let mir::Operand::Constant(box constant) = func {
+ if constant.span.lo() > span.lo() {
+ span = span.with_lo(constant.span.lo());
+ }
+ }
+ Some(function_source_span(span, body_span))
+ }
+
// Retain spans from all other terminators
TerminatorKind::Resume
| TerminatorKind::Abort
| TerminatorKind::Return
- | TerminatorKind::Call { .. }
| TerminatorKind::Yield { .. }
| TerminatorKind::GeneratorDrop
| TerminatorKind::FalseUnwind { .. }
#[inline]
fn function_source_span(span: Span, body_span: Span) -> Span {
- let span = original_sp(span, body_span).with_ctxt(SyntaxContext::root());
+ let span = original_sp(span, body_span).with_ctxt(body_span.ctxt());
if body_span.contains(span) { span } else { body_span }
}
//! Also note, some basic features of `Span` also rely on the `Span`s own "session globals", which
//! are unrelated to the `TyCtxt` global. Without initializing the `Span` session globals, some
//! basic, coverage-specific features would be impossible to test, but thankfully initializing these
-//! globals is comparitively simpler. The easiest way is to wrap the test in a closure argument
+//! globals is comparatively simpler. The easiest way is to wrap the test in a closure argument
//! to: `rustc_span::with_default_session_globals(|| { test_here(); })`.
use super::counters;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::def_id::DefId;
-/// Checks if the specified `local` is used as the `self` prameter of a method call
+/// Checks if the specified `local` is used as the `self` parameter of a method call
/// in the provided `BasicBlock`. If it is, then the `DefId` of the called method is
/// returned.
pub fn find_self_call<'tcx>(
/// The projections are truncated to represent a path that might be captured by a
/// closure/generator. This implies the vector returned from this function doesn't contain
/// ProjectionElems `Downcast`, `ConstantIndex`, `Index`, or `Subslice` because those will never be
-/// part of a path that is captued by a closure. We stop applying projections once we see the first
+/// part of a path that is captured by a closure. We stop applying projections once we see the first
/// projection that isn't captured by a closure.
fn convert_to_hir_projections_and_truncate_for_capture<'tcx>(
mir_projections: &[PlaceElem<'tcx>],
/// Lower a captured upvar. Note we might not know the actual capture index,
/// so we create a place starting from `PlaceBase::Upvar`, which will be resolved
- /// once all projections that allow us to indentify a capture have been applied.
+ /// once all projections that allow us to identify a capture have been applied.
fn lower_captured_upvar(
&mut self,
block: BasicBlock,
}
} else {
assert!(value.is_none(), "`return` and `break` should have a destination");
+ if self.tcx.sess.instrument_coverage() {
+ // Unlike `break` and `return`, which push an `Assign` statement to MIR, from which
+ // a Coverage code region can be generated, `continue` needs no `Assign`; but
+ // without one, the `InstrumentCoverage` MIR pass cannot generate a code region for
+ // `continue`. Coverage will be missing unless we add a dummy `Assign` to MIR.
+ self.add_dummy_assignment(&span, block, source_info);
+ }
}
let region_scope = self.scopes.breakable_scopes[break_index].region_scope;
self.cfg.start_new_block().unit()
}
+ // Add a dummy `Assign` statement to the CFG, with the span for the source code's `continue`
+ // statement.
+ fn add_dummy_assignment(&mut self, span: &Span, block: BasicBlock, source_info: SourceInfo) {
+ let local_decl = LocalDecl::new(self.tcx.mk_unit(), *span).internal();
+ let temp_place = Place::from(self.local_decls.push(local_decl));
+ self.cfg.push_assign_unit(block, source_info, temp_place, self.tcx);
+ }
+
crate fn exit_top_scope(
&mut self,
mut block: BasicBlock,
#![feature(array_windows)]
#![feature(box_patterns)]
#![feature(box_syntax)]
-#![feature(const_fn)]
#![feature(const_panic)]
#![feature(control_flow_enum)]
#![feature(crate_visibility_modifier)]
Ok((ident, ItemKind::MacroDef(ast::MacroDef { body, macro_rules: false })))
}
- /// Is this unambiguously the start of a `macro_rules! foo` item defnition?
+ /// Is this unambiguously the start of a `macro_rules! foo` item definition?
fn is_macro_rules_item(&mut self) -> bool {
self.check_keyword(kw::MacroRules)
&& self.look_ahead(1, |t| *t == token::Not)
let vstr = pprust::vis_to_string(vis);
let vstr = vstr.trim_end();
if macro_rules {
- self.sess.gated_spans.gate(sym::pub_macro_rules, vis.span);
+ let msg = format!("can't qualify macro_rules invocation with `{}`", vstr);
+ self.struct_span_err(vis.span, &msg)
+ .span_suggestion(
+ vis.span,
+ "try exporting the macro",
+ "#[macro_export]".to_owned(),
+ Applicability::MaybeIncorrect, // speculative
+ )
+ .emit();
} else {
self.struct_span_err(vis.span, "can't qualify macro invocation with `pub`")
.span_suggestion(
let mut is_valid = true;
let attrs = self.tcx.hir().attrs(hir_id);
for attr in attrs {
- is_valid &= if self.tcx.sess.check_name(attr, sym::inline) {
- self.check_inline(hir_id, attr, span, target)
- } else if self.tcx.sess.check_name(attr, sym::non_exhaustive) {
- self.check_non_exhaustive(hir_id, attr, span, target)
- } else if self.tcx.sess.check_name(attr, sym::marker) {
- self.check_marker(hir_id, attr, span, target)
- } else if self.tcx.sess.check_name(attr, sym::target_feature) {
- self.check_target_feature(hir_id, attr, span, target)
- } else if self.tcx.sess.check_name(attr, sym::track_caller) {
- self.check_track_caller(hir_id, &attr.span, attrs, span, target)
- } else if self.tcx.sess.check_name(attr, sym::doc) {
- self.check_doc_attrs(attr, hir_id, target)
- } else if self.tcx.sess.check_name(attr, sym::no_link) {
- self.check_no_link(hir_id, &attr, span, target)
- } else if self.tcx.sess.check_name(attr, sym::export_name) {
- self.check_export_name(hir_id, &attr, span, target)
- } else if self.tcx.sess.check_name(attr, sym::rustc_args_required_const) {
- self.check_rustc_args_required_const(&attr, span, target, item)
- } else if self.tcx.sess.check_name(attr, sym::rustc_layout_scalar_valid_range_start) {
- self.check_rustc_layout_scalar_valid_range(&attr, span, target)
- } else if self.tcx.sess.check_name(attr, sym::rustc_layout_scalar_valid_range_end) {
- self.check_rustc_layout_scalar_valid_range(&attr, span, target)
- } else if self.tcx.sess.check_name(attr, sym::allow_internal_unstable) {
- self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs)
- } else if self.tcx.sess.check_name(attr, sym::rustc_allow_const_fn_unstable) {
- self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
- } else if self.tcx.sess.check_name(attr, sym::naked) {
- self.check_naked(hir_id, attr, span, target)
- } else if self.tcx.sess.check_name(attr, sym::rustc_legacy_const_generics) {
- self.check_rustc_legacy_const_generics(&attr, span, target, item)
- } else if self.tcx.sess.check_name(attr, sym::rustc_clean)
- || self.tcx.sess.check_name(attr, sym::rustc_dirty)
- || self.tcx.sess.check_name(attr, sym::rustc_if_this_changed)
- || self.tcx.sess.check_name(attr, sym::rustc_then_this_would_need)
- {
- self.check_rustc_dirty_clean(&attr)
- } else {
- // lint-only checks
- if self.tcx.sess.check_name(attr, sym::cold) {
- self.check_cold(hir_id, attr, span, target);
- } else if self.tcx.sess.check_name(attr, sym::link_name) {
- self.check_link_name(hir_id, attr, span, target);
- } else if self.tcx.sess.check_name(attr, sym::link_section) {
- self.check_link_section(hir_id, attr, span, target);
- } else if self.tcx.sess.check_name(attr, sym::no_mangle) {
- self.check_no_mangle(hir_id, attr, span, target);
+ is_valid &= match attr.name_or_empty() {
+ sym::inline => self.check_inline(hir_id, attr, span, target),
+ sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target),
+ sym::marker => self.check_marker(hir_id, attr, span, target),
+ sym::target_feature => self.check_target_feature(hir_id, attr, span, target),
+ sym::track_caller => {
+ self.check_track_caller(hir_id, &attr.span, attrs, span, target)
}
- true
+ sym::doc => self.check_doc_attrs(attr, hir_id, target),
+ sym::no_link => self.check_no_link(hir_id, &attr, span, target),
+ sym::export_name => self.check_export_name(hir_id, &attr, span, target),
+ sym::rustc_args_required_const => {
+ self.check_rustc_args_required_const(&attr, span, target, item)
+ }
+ sym::rustc_layout_scalar_valid_range_start
+ | sym::rustc_layout_scalar_valid_range_end => {
+ self.check_rustc_layout_scalar_valid_range(&attr, span, target)
+ }
+ sym::allow_internal_unstable => {
+ self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs)
+ }
+ sym::rustc_allow_const_fn_unstable => {
+ self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
+ }
+ sym::naked => self.check_naked(hir_id, attr, span, target),
+ sym::rustc_legacy_const_generics => {
+ self.check_rustc_legacy_const_generics(&attr, span, target, item)
+ }
+ sym::rustc_clean
+ | sym::rustc_dirty
+ | sym::rustc_if_this_changed
+ | sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(&attr),
+ _ => true,
};
+ // lint-only checks
+ match attr.name_or_empty() {
+ sym::cold => self.check_cold(hir_id, attr, span, target),
+ sym::link_name => self.check_link_name(hir_id, attr, span, target),
+ sym::link_section => self.check_link_section(hir_id, attr, span, target),
+ sym::no_mangle => self.check_no_mangle(hir_id, attr, span, target),
+ _ => {}
+ }
}
if !is_valid {
// ```
let hints: Vec<_> = attrs
.iter()
- .filter(|attr| self.tcx.sess.check_name(attr, sym::repr))
+ .filter(|attr| attr.has_name(sym::repr))
.filter_map(|attr| attr.meta_item_list())
.flatten()
.collect();
fn check_used(&self, attrs: &'hir [Attribute], target: Target) {
for attr in attrs {
- if self.tcx.sess.check_name(attr, sym::used) && target != Target::Static {
+ if attr.has_name(sym::used) && target != Target::Static {
self.tcx
.sess
.span_err(attr.span, "attribute must be applied to a `static` variable");
sym::path,
sym::automatically_derived,
sym::start,
- sym::main,
+ sym::rustc_main,
];
for attr in attrs {
let attrs = ctxt.map.attrs(item.hir_id());
if ctxt.session.contains_name(attrs, sym::start) {
EntryPointType::Start
- } else if ctxt.session.contains_name(attrs, sym::main) {
+ } else if ctxt.session.contains_name(attrs, sym::rustc_main) {
EntryPointType::MainAttr
} else if item.ident.name == sym::main {
if at_root {
if let Some(attr) = ctxt.session.find_by_name(attrs, sym::start) {
throw_attr_err(&ctxt.session, attr.span, "start");
}
- if let Some(attr) = ctxt.session.find_by_name(attrs, sym::main) {
- throw_attr_err(&ctxt.session, attr.span, "main");
+ if let Some(attr) = ctxt.session.find_by_name(attrs, sym::rustc_main) {
+ throw_attr_err(&ctxt.session, attr.span, "rustc_main");
}
}
EntryPointType::MainNamed => {
err.span_note(span, "here is a function named `main`");
}
err.note("you have one or more functions named `main` not defined at the crate level");
- err.help(
- "either move the `main` function definitions or attach the `#[main]` attribute \
- to one of them",
- );
+ err.help("consider moving the `main` function definitions");
// There were some functions named `main` though. Try to give the user a hint.
format!(
"the main function must be defined at the crate level{}",
//! This API is completely unstable and subject to change.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
-#![feature(const_fn)]
#![feature(const_panic)]
#![feature(crate_visibility_modifier)]
#![feature(in_band_lifetimes)]
for p in body.params {
self.check_unused_vars_in_pat(&p.pat, Some(entry_ln), |spans, hir_id, ln, var| {
if !self.live_on_entry(ln, var) {
- self.report_unsed_assign(hir_id, spans, var, |name| {
+ self.report_unused_assign(hir_id, spans, var, |name| {
format!("value passed to `{}` is never read", name)
});
}
fn warn_about_dead_assign(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) {
if !self.live_on_exit(ln, var) {
- self.report_unsed_assign(hir_id, spans, var, |name| {
+ self.report_unused_assign(hir_id, spans, var, |name| {
format!("value assigned to `{}` is never read", name)
});
}
}
- fn report_unsed_assign(
+ fn report_unused_assign(
&self,
hir_id: HirId,
spans: Vec<Span>,
#[derive(Debug, Copy, Clone)]
pub struct Context {
- /// The root of the current region tree. This is typically the id
- /// of the innermost fn body. Each fn forms its own disjoint tree
- /// in the region hierarchy. These fn bodies are themselves
- /// arranged into a tree. See the "Modeling closures" section of
- /// the README in `rustc_trait_selection::infer::region_constraints`
- /// for more details.
- root_id: Option<hir::ItemLocalId>,
-
/// The scope that contains any new variables declared, plus its depth in
/// the scope tree.
var_parent: Option<(Scope, ScopeDepth)>,
let outer_pessimistic_yield = mem::replace(&mut self.pessimistic_yield, false);
self.terminating_scopes.insert(body.value.hir_id.local_id);
- if let Some(root_id) = self.cx.root_id {
- self.scope_tree.record_closure_parent(body.value.hir_id.local_id, root_id);
- }
- self.cx.root_id = Some(body.value.hir_id.local_id);
-
self.enter_scope(Scope { id: body.value.hir_id.local_id, data: ScopeData::CallSite });
self.enter_scope(Scope { id: body.value.hir_id.local_id, data: ScopeData::Arguments });
tcx,
scope_tree: ScopeTree::default(),
expr_and_pat_count: 0,
- cx: Context { root_id: None, parent: None, var_parent: None },
+ cx: Context { parent: None, var_parent: None },
terminating_scopes: Default::default(),
pessimistic_yield: false,
fixup_scopes: vec![],
////////////////////////////////////////////////////////////////////////////////////////////
/// Type privacy visitor, checks types for privacy and reports violations.
-/// Both explicitly written types and inferred types of expressions and patters are checked.
+/// Both explicitly written types and inferred types of expressions and patterns are checked.
/// Checks are performed on "semantic" types regardless of names and their hygiene.
////////////////////////////////////////////////////////////////////////////////////////////
try_load_from_on_disk_cache: |_, _| {},
};
+ pub const CompileMonoItem: QueryStruct = QueryStruct {
+ force_from_dep_node: |_, _| false,
+ try_load_from_on_disk_cache: |_, _| {},
+ };
+
$(pub const $name: QueryStruct = {
const is_anon: bool = is_anon!([$($modifiers)*]);
#![feature(bool_to_option)]
-#![feature(const_fn)]
#![feature(const_panic)]
#![feature(core_intrinsics)]
#![feature(drain_filter)]
};
let res = Res::Def(DefKind::Macro(ext.macro_kind()), def_id.to_def_id());
- let is_macro_export = self.r.session.contains_name(&item.attrs, sym::macro_export);
self.r.macro_map.insert(def_id.to_def_id(), ext);
self.r.local_macro_def_scopes.insert(def_id, parent_scope.module);
- if macro_rules && matches!(item.vis.kind, ast::VisibilityKind::Inherited) {
+ if macro_rules {
let ident = ident.normalize_to_macros_2_0();
self.r.macro_names.insert(ident);
+ let is_macro_export = self.r.session.contains_name(&item.attrs, sym::macro_export);
let vis = if is_macro_export {
ty::Visibility::Public
} else {
}),
))
} else {
- if is_macro_export {
- let what = if macro_rules { "`macro_rules` with `pub`" } else { "`macro` items" };
- let msg = format!("`#[macro_export]` cannot be used on {what}");
- self.r.session.span_err(item.span, &msg);
- }
let module = parent_scope.module;
let vis = match item.kind {
// Visibilities must not be resolved non-speculatively twice
);
err
}
- ResolutionError::ParamInAnonConstInTyDefault(name) => {
- let mut err = self.session.struct_span_err(
- span,
- "constant values inside of type parameter defaults must not depend on generic parameters",
- );
- err.span_label(
- span,
- format!("the anonymous constant must not depend on the parameter `{}`", name),
- );
- err
- }
ResolutionError::ParamInNonTrivialAnonConst { name, is_type } => {
let mut err = self.session.struct_span_err(
span,
{
let mut candidates = Vec::new();
let mut seen_modules = FxHashSet::default();
- let not_local_module = crate_name.name != kw::Crate;
- let mut worklist =
- vec![(start_module, Vec::<ast::PathSegment>::new(), true, not_local_module)];
+ let mut worklist = vec![(start_module, Vec::<ast::PathSegment>::new(), true)];
let mut worklist_via_import = vec![];
- while let Some((in_module, path_segments, accessible, in_module_is_extern)) =
- match worklist.pop() {
- None => worklist_via_import.pop(),
- Some(x) => Some(x),
- }
- {
+ while let Some((in_module, path_segments, accessible)) = match worklist.pop() {
+ None => worklist_via_import.pop(),
+ Some(x) => Some(x),
+ } {
+ let in_module_is_extern = !in_module.def_id().unwrap().is_local();
// We have to visit module children in deterministic order to avoid
// instabilities in reported imports (#43552).
in_module.for_each_child(self, |this, ident, ns, name_binding| {
name_binding.is_extern_crate() && lookup_ident.span.rust_2018();
if !is_extern_crate_that_also_appears_in_prelude {
- let is_extern = in_module_is_extern || name_binding.is_extern_crate();
// add the module to the lookup
if seen_modules.insert(module.def_id().unwrap()) {
if via_import { &mut worklist_via_import } else { &mut worklist }
- .push((module, path_segments, child_accessible, is_extern));
+ .push((module, path_segments, child_accessible));
}
}
}
// provide previous type parameters as they're built. We
// put all the parameters on the ban list and then remove
// them one by one as they are processed and become available.
- let mut default_ban_rib = Rib::new(ForwardGenericParamBanRibKind);
- let mut found_default = false;
- default_ban_rib.bindings.extend(generics.params.iter().filter_map(
- |param| match param.kind {
- GenericParamKind::Type { default: Some(_), .. }
- | GenericParamKind::Const { default: Some(_), .. } => {
- found_default = true;
- Some((Ident::with_dummy_span(param.ident.name), Res::Err))
+ let mut forward_ty_ban_rib = Rib::new(ForwardGenericParamBanRibKind);
+ let mut forward_const_ban_rib = Rib::new(ForwardGenericParamBanRibKind);
+ for param in generics.params.iter() {
+ match param.kind {
+ GenericParamKind::Type { .. } => {
+ forward_ty_ban_rib
+ .bindings
+ .insert(Ident::with_dummy_span(param.ident.name), Res::Err);
}
- _ => None,
- },
- ));
+ GenericParamKind::Const { .. } => {
+ forward_const_ban_rib
+ .bindings
+ .insert(Ident::with_dummy_span(param.ident.name), Res::Err);
+ }
+ GenericParamKind::Lifetime => {}
+ }
+ }
// rust-lang/rust#61631: The type `Self` is essentially
// another type parameter. For ADTs, we consider it
// such as in the case of `trait Add<Rhs = Self>`.)
if self.diagnostic_metadata.current_self_item.is_some() {
// (`Some` if + only if we are in ADT's generics.)
- default_ban_rib.bindings.insert(Ident::with_dummy_span(kw::SelfUpper), Res::Err);
+ forward_ty_ban_rib.bindings.insert(Ident::with_dummy_span(kw::SelfUpper), Res::Err);
}
for param in &generics.params {
}
if let Some(ref ty) = default {
- self.ribs[TypeNS].push(default_ban_rib);
- self.with_rib(ValueNS, ForwardGenericParamBanRibKind, |this| {
- // HACK: We use an empty `ForwardGenericParamBanRibKind` here which
- // is only used to forbid the use of const parameters inside of
- // type defaults.
- //
- // While the rib name doesn't really fit here, it does allow us to use the same
- // code for both const and type parameters.
- this.visit_ty(ty);
- });
- default_ban_rib = self.ribs[TypeNS].pop().unwrap();
+ self.ribs[TypeNS].push(forward_ty_ban_rib);
+ self.ribs[ValueNS].push(forward_const_ban_rib);
+ self.visit_ty(ty);
+ forward_const_ban_rib = self.ribs[ValueNS].pop().unwrap();
+ forward_ty_ban_rib = self.ribs[TypeNS].pop().unwrap();
}
// Allow all following defaults to refer to this type parameter.
- default_ban_rib.bindings.remove(&Ident::with_dummy_span(param.ident.name));
+ forward_ty_ban_rib.bindings.remove(&Ident::with_dummy_span(param.ident.name));
}
- GenericParamKind::Const { ref ty, kw_span: _, default: _ } => {
- // FIXME(const_generics_defaults): handle `default` value here
- for bound in ¶m.bounds {
- self.visit_param_bound(bound);
- }
+ GenericParamKind::Const { ref ty, kw_span: _, ref default } => {
+ // Const parameters can't have param bounds.
+ assert!(param.bounds.is_empty());
+
self.ribs[TypeNS].push(Rib::new(ConstParamTyRibKind));
self.ribs[ValueNS].push(Rib::new(ConstParamTyRibKind));
self.visit_ty(ty);
self.ribs[TypeNS].pop().unwrap();
self.ribs[ValueNS].pop().unwrap();
+
+ if let Some(ref expr) = default {
+ self.ribs[TypeNS].push(forward_ty_ban_rib);
+ self.ribs[ValueNS].push(forward_const_ban_rib);
+ self.visit_anon_const(expr);
+ forward_const_ban_rib = self.ribs[ValueNS].pop().unwrap();
+ forward_ty_ban_rib = self.ribs[TypeNS].pop().unwrap();
+ }
+
+ // Allow all following defaults to refer to this const parameter.
+ forward_const_ban_rib
+ .bindings
+ .remove(&Ident::with_dummy_span(param.ident.name));
}
}
}
let msg = "you might have meant to use `#![feature(trait_alias)]` instead of a \
`type` alias";
if let Some(span) = self.def_span(def_id) {
- err.span_help(span, msg);
+ if let Ok(snip) = self.r.session.source_map().span_to_snippet(span) {
+ // The span contains a type alias so we should be able to
+ // replace `type` with `trait`.
+ let snip = snip.replacen("type", "trait", 1);
+ err.span_suggestion(span, msg, snip, Applicability::MaybeIncorrect);
+ } else {
+ err.span_help(span, msg);
+ }
} else {
err.help(msg);
}
map: &'a mut NamedRegionMap,
scope: ScopeRef<'a>,
- /// This is slightly complicated. Our representation for poly-trait-refs contains a single
- /// binder and thus we only allow a single level of quantification. However,
- /// the syntax of Rust permits quantification in two places, e.g., `T: for <'a> Foo<'a>`
- /// and `for <'a, 'b> &'b T: Foo<'a>`. In order to get the De Bruijn indices
- /// correct when representing these constraints, we should only introduce one
- /// scope. However, we want to support both locations for the quantifier and
- /// during lifetime resolution we want precise information (so we can't
- /// desugar in an earlier phase). Moreso, an error here doesn't cause a bail
- /// from type checking, so we need to be extra careful that we don't lose
- /// any bound var information.
- ///
- /// So, if we encounter a quantifier at the outer scope, we set
- /// `trait_ref_hack` to the hir id of the bounded type (and introduce a scope).
- /// Then, if we encounter a quantifier at the inner scope, then we know to
- /// emit an error. Importantly though, we do have to track the lifetimes
- /// defined on the outer scope (i.e. the bounded ty), since we continue
- /// to type check after emitting an error; we therefore assume that the bound
- /// vars on the inner trait refs come from both quantifiers.
- ///
- /// If we encounter a quantifier in the inner scope `trait_ref_hack` being
- /// `None`, then we just introduce the scope at the inner quantifier as normal.
- trait_ref_hack: Option<hir::HirId>,
-
/// Used to disallow the use of in-band lifetimes in `fn` or `Fn` syntax.
is_in_fn_syntax: bool,
/// of the resulting opaque type.
opaque_type_parent: bool,
- /// True only if this `Binder` scope is from the quantifiers on a
- /// `PolyTraitRef`. This is necessary for `assocated_type_bounds`, which
- /// requires binders of nested trait refs to be merged.
- from_poly_trait_ref: bool,
+ scope_type: BinderScopeType,
/// The late bound vars for a given item are stored by `HirId` to be
/// queried later. However, if we enter an elision scope, we have to
s: ScopeRef<'a>,
},
- /// This is a particularly interesting consequence of how we handle poly
- /// trait refs. See `trait_ref_hack` for additional info. This bit is
- /// important w.r.t. querying late-bound vars.
- ///
- /// To completely understand why this is necessary, first it's important to
- /// realize that `T: for<'a> U + for<'a, 'b> V` is actually two separate
- /// trait refs: `T: for<'a> U` and `T: for<'b> V` and as such, the late
- /// bound vars on each needs to be tracked separately. Also, in this case,
- /// are *three* relevant `HirId`s: one for the entire bound and one
- /// for each separate one.
- ///
- /// Next, imagine three different poly trait refs:
- /// 1) `for<'a, 'b> T: U<'a, 'b>`
- /// 2) `T: for<'a, 'b> U<'a, 'b>`
- /// 3) `for<'a> T: for<'b> U<'a, 'b>`
- ///
- /// First, note that the third example is semantically invalid and an error,
- /// but we *must* handle it as valid, since type checking isn't bailed out
- /// of. Other than that, if ask for bound vars for each, we expect
- /// `['a, 'b]`. If we *didn't* allow binders before `T`, then we would
- /// always introduce a binder scope at the inner trait ref. This is great,
- /// becauase later on during type-checking, we will ask "what are the late
- /// bound vars on this trait ref". However, because we allow bound vars on
- /// the bound itself, we have to have some way of keeping track of the fact
- /// that we actually want to store the late bound vars as being associated
- /// with the trait ref; this is that.
- ///
- /// One alternative way to handle this would be to just introduce a new
- /// `Binder` scope, but that's semantically a bit different, since bound
- /// vars from both `for<...>`s *do* share the same binder level.
- TraitRefHackInner {
- hir_id: hir::HirId,
- s: ScopeRef<'a>,
- },
-
/// When we have nested trait refs, we concanetate late bound vars for inner
/// trait refs from outer ones. But we also need to include any HRTB
/// lifetimes encountered when identifying the trait that an associated type
Root,
}
+#[derive(Copy, Clone, Debug)]
+enum BinderScopeType {
+ /// Any non-concatenating binder scopes.
+ Normal,
+ /// Within a syntactic trait ref, there may be multiple poly trait refs that
+ /// are nested (under the `associcated_type_bounds` feature). The binders of
+ /// the innner poly trait refs are extended from the outer poly trait refs
+ /// and don't increase the late bound depth. If you had
+ /// `T: for<'a> Foo<Bar: for<'b> Baz<'a, 'b>>`, then the `for<'b>` scope
+ /// would be `Concatenating`. This also used in trait refs in where clauses
+ /// where we have two binders `for<> T: for<> Foo` (I've intentionally left
+ /// out any lifetimes because they aren't needed to show the two scopes).
+ /// The inner `for<>` has a scope of `Concatenating`.
+ Concatenating,
+}
+
// A helper struct for debugging scopes without printing parent scopes
struct TruncatedScopeDebug<'a>(&'a Scope<'a>);
next_early_index,
track_lifetime_uses,
opaque_type_parent,
- from_poly_trait_ref,
+ scope_type,
hir_id,
s: _,
} => f
.field("next_early_index", next_early_index)
.field("track_lifetime_uses", track_lifetime_uses)
.field("opaque_type_parent", opaque_type_parent)
- .field("from_poly_trait_ref", from_poly_trait_ref)
+ .field("scope_type", scope_type)
.field("hir_id", hir_id)
.field("s", &"..")
.finish(),
.field("lifetime", lifetime)
.field("s", &"..")
.finish(),
- Scope::TraitRefHackInner { hir_id, s: _ } => f
- .debug_struct("TraitRefHackInner")
- .field("hir_id", hir_id)
- .field("s", &"..")
- .finish(),
Scope::Supertrait { lifetimes, s: _ } => f
.debug_struct("Supertrait")
.field("lifetimes", lifetimes)
tcx,
map: &mut named_region_map,
scope: ROOT_SCOPE,
- trait_ref_hack: None,
is_in_fn_syntax: false,
is_in_const_generic: false,
trait_definition_only,
}
}
+impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
+ /// Returns the binders in scope and the type of `Binder` that should be created for a poly trait ref.
+ fn poly_trait_ref_binder_info(&mut self) -> (Vec<ty::BoundVariableKind>, BinderScopeType) {
+ let mut scope = self.scope;
+ let mut supertrait_lifetimes = vec![];
+ loop {
+ match scope {
+ Scope::Body { .. } | Scope::Root => {
+ break (vec![], BinderScopeType::Normal);
+ }
+
+ Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } => {
+ scope = s;
+ }
+
+ Scope::Supertrait { s, lifetimes } => {
+ supertrait_lifetimes = lifetimes.clone();
+ scope = s;
+ }
+
+ Scope::TraitRefBoundary { .. } => {
+ // We should only see super trait lifetimes if there is a `Binder` above
+ assert!(supertrait_lifetimes.is_empty());
+ break (vec![], BinderScopeType::Normal);
+ }
+
+ Scope::Binder { hir_id, .. } => {
+ // Nested poly trait refs have the binders concatenated
+ let mut full_binders =
+ self.map.late_bound_vars.entry(*hir_id).or_default().clone();
+ full_binders.extend(supertrait_lifetimes.into_iter());
+ break (full_binders, BinderScopeType::Concatenating);
+ }
+ }
+ }
+ }
+}
impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
type Map = Map<'tcx>;
s: self.scope,
track_lifetime_uses: true,
opaque_type_parent: false,
- from_poly_trait_ref: false,
+ scope_type: BinderScopeType::Normal,
};
self.with(scope, move |_old_scope, this| {
intravisit::walk_fn(this, fk, fd, b, s, hir_id)
next_early_index: index + non_lifetime_count,
opaque_type_parent: true,
track_lifetime_uses,
- from_poly_trait_ref: false,
+ scope_type: BinderScopeType::Normal,
s: ROOT_SCOPE,
};
self.with(scope, |old_scope, this| {
this.check_lifetime_params(old_scope, &generics.params);
- intravisit::walk_item(this, item);
+ let scope = Scope::TraitRefBoundary { s: this.scope };
+ this.with(scope, |_, this| {
+ intravisit::walk_item(this, item);
+ });
});
self.missing_named_lifetime_spots.pop();
}
next_early_index,
track_lifetime_uses: true,
opaque_type_parent: false,
- from_poly_trait_ref: false,
+ scope_type: BinderScopeType::Normal,
};
self.with(scope, |old_scope, this| {
// a bare fn has no bounds, so everything
// Elided lifetimes are not allowed in non-return
// position impl Trait
- let scope = Scope::Elision { elide: Elide::Forbid, s: self.scope };
+ let scope = Scope::TraitRefBoundary { s: self.scope };
self.with(scope, |_, this| {
- intravisit::walk_item(this, opaque_ty);
+ let scope = Scope::Elision { elide: Elide::Forbid, s: this.scope };
+ this.with(scope, |_, this| {
+ intravisit::walk_item(this, opaque_ty);
+ })
});
return;
s: this.scope,
track_lifetime_uses: true,
opaque_type_parent: false,
- from_poly_trait_ref: false,
+ scope_type: BinderScopeType::Normal,
};
this.with(scope, |_old_scope, this| {
this.visit_generics(generics);
s: self.scope,
track_lifetime_uses: true,
opaque_type_parent: false,
- from_poly_trait_ref: false,
+ scope_type: BinderScopeType::Normal,
};
self.with(scope, |_old_scope, this| {
let scope = Scope::TraitRefBoundary { s: this.scope };
s: self.scope,
track_lifetime_uses: true,
opaque_type_parent: true,
- from_poly_trait_ref: false,
+ scope_type: BinderScopeType::Normal,
};
self.with(scope, |old_scope, this| {
this.check_lifetime_params(old_scope, &generics.params);
s: self.scope,
track_lifetime_uses: true,
opaque_type_parent: true,
- from_poly_trait_ref: false,
+ scope_type: BinderScopeType::Normal,
};
self.with(scope, |old_scope, this| {
this.check_lifetime_params(old_scope, &generics.params);
if !self.trait_definition_only {
check_mixed_explicit_and_in_band_defs(self.tcx, &generics.params);
}
- for param in generics.params {
- match param.kind {
- GenericParamKind::Lifetime { .. } => {}
- GenericParamKind::Type { ref default, .. } => {
- walk_list!(self, visit_param_bound, param.bounds);
- if let Some(ref ty) = default {
- self.visit_ty(&ty);
+ let scope = Scope::TraitRefBoundary { s: self.scope };
+ self.with(scope, |_, this| {
+ for param in generics.params {
+ match param.kind {
+ GenericParamKind::Lifetime { .. } => {}
+ GenericParamKind::Type { ref default, .. } => {
+ walk_list!(this, visit_param_bound, param.bounds);
+ if let Some(ref ty) = default {
+ this.visit_ty(&ty);
+ }
+ }
+ GenericParamKind::Const { ref ty, .. } => {
+ let was_in_const_generic = this.is_in_const_generic;
+ this.is_in_const_generic = true;
+ walk_list!(this, visit_param_bound, param.bounds);
+ this.visit_ty(&ty);
+ this.is_in_const_generic = was_in_const_generic;
}
}
- GenericParamKind::Const { ref ty, .. } => {
- let was_in_const_generic = self.is_in_const_generic;
- self.is_in_const_generic = true;
- walk_list!(self, visit_param_bound, param.bounds);
- self.visit_ty(&ty);
- self.is_in_const_generic = was_in_const_generic;
- }
- }
- }
- for predicate in generics.where_clause.predicates {
- match predicate {
- &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
- ref bounded_ty,
- bounds,
- ref bound_generic_params,
- ..
- }) => {
- let (lifetimes, binders): (FxHashMap<hir::ParamName, Region>, Vec<_>) =
- bound_generic_params
- .iter()
- .filter_map(|param| match param.kind {
- GenericParamKind::Lifetime { .. } => Some(param),
- _ => None,
- })
- .enumerate()
- .map(|(late_bound_idx, param)| {
- let pair =
- Region::late(late_bound_idx as u32, &self.tcx.hir(), param);
- let r = late_region_as_bound_region(self.tcx, &pair.1);
- (pair, r)
- })
- .unzip();
- self.map.late_bound_vars.insert(bounded_ty.hir_id, binders.clone());
- let scope = Scope::TraitRefBoundary { s: self.scope };
- self.with(scope, |_, this| {
- if !lifetimes.is_empty() {
- let next_early_index = this.next_early_index();
- let scope = Scope::Binder {
- hir_id: bounded_ty.hir_id,
- lifetimes,
- s: this.scope,
- next_early_index,
- track_lifetime_uses: true,
- opaque_type_parent: false,
- from_poly_trait_ref: true,
- };
- this.with(scope, |old_scope, this| {
- this.check_lifetime_params(old_scope, &bound_generic_params);
- this.visit_ty(&bounded_ty);
- this.trait_ref_hack = Some(bounded_ty.hir_id);
- walk_list!(this, visit_param_bound, bounds);
- this.trait_ref_hack = None;
- })
- } else {
+ }
+ for predicate in generics.where_clause.predicates {
+ match predicate {
+ &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
+ ref bounded_ty,
+ bounds,
+ ref bound_generic_params,
+ ..
+ }) => {
+ let (lifetimes, binders): (FxHashMap<hir::ParamName, Region>, Vec<_>) =
+ bound_generic_params
+ .iter()
+ .filter_map(|param| match param.kind {
+ GenericParamKind::Lifetime { .. } => Some(param),
+ _ => None,
+ })
+ .enumerate()
+ .map(|(late_bound_idx, param)| {
+ let pair =
+ Region::late(late_bound_idx as u32, &this.tcx.hir(), param);
+ let r = late_region_as_bound_region(this.tcx, &pair.1);
+ (pair, r)
+ })
+ .unzip();
+ this.map.late_bound_vars.insert(bounded_ty.hir_id, binders.clone());
+ let next_early_index = this.next_early_index();
+ // Even if there are no lifetimes defined here, we still wrap it in a binder
+ // scope. If there happens to be a nested poly trait ref (an error), that
+ // will be `Concatenating` anyways, so we don't have to worry about the depth
+ // being wrong.
+ let scope = Scope::Binder {
+ hir_id: bounded_ty.hir_id,
+ lifetimes,
+ s: this.scope,
+ next_early_index,
+ track_lifetime_uses: true,
+ opaque_type_parent: false,
+ scope_type: BinderScopeType::Normal,
+ };
+ this.with(scope, |old_scope, this| {
+ this.check_lifetime_params(old_scope, &bound_generic_params);
this.visit_ty(&bounded_ty);
walk_list!(this, visit_param_bound, bounds);
- }
- })
- }
- &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
- ref lifetime,
- bounds,
- ..
- }) => {
- self.visit_lifetime(lifetime);
- walk_list!(self, visit_param_bound, bounds);
- }
- &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
- ref lhs_ty,
- ref rhs_ty,
- ..
- }) => {
- self.visit_ty(lhs_ty);
- self.visit_ty(rhs_ty);
+ })
+ }
+ &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
+ ref lifetime,
+ bounds,
+ ..
+ }) => {
+ this.visit_lifetime(lifetime);
+ walk_list!(this, visit_param_bound, bounds);
+ }
+ &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
+ ref lhs_ty,
+ ref rhs_ty,
+ ..
+ }) => {
+ this.visit_ty(lhs_ty);
+ this.visit_ty(rhs_ty);
+ }
}
}
- }
+ })
}
fn visit_param_bound(&mut self, bound: &'tcx hir::GenericBound<'tcx>) {
match bound {
- hir::GenericBound::LangItemTrait(_, _, hir_id, _) if self.trait_ref_hack.is_none() => {
- self.map.late_bound_vars.insert(*hir_id, vec![]);
+ hir::GenericBound::LangItemTrait(_, _, hir_id, _) => {
+ // FIXME(jackh726): This is pretty weird. `LangItemTrait` doesn't go
+ // through the regular poly trait ref code, so we don't get another
+ // chance to introduce a binder. For now, I'm keeping the existing logic
+ // of "if there isn't a Binder scope above us, add one", but I
+ // imagine there's a better way to go about this.
+ let (binders, scope_type) = self.poly_trait_ref_binder_info();
+
+ self.map.late_bound_vars.insert(*hir_id, binders);
let scope = Scope::Binder {
hir_id: *hir_id,
lifetimes: FxHashMap::default(),
next_early_index: self.next_early_index(),
track_lifetime_uses: true,
opaque_type_parent: false,
- from_poly_trait_ref: false,
+ scope_type,
};
self.with(scope, |_, this| {
intravisit::walk_param_bound(this, bound);
let should_pop_missing_lt = self.is_trait_ref_fn_scope(trait_ref);
- let trait_ref_hack = self.trait_ref_hack.take();
let next_early_index = self.next_early_index();
- // See note on `trait_ref_hack`. If `for<..>` has been defined in both
- // the outer and inner part of the trait ref, emit an error.
- let has_lifetimes = trait_ref.bound_generic_params.iter().any(|param| match param.kind {
- GenericParamKind::Lifetime { .. } => true,
- _ => false,
- });
- if trait_ref_hack.is_some() && has_lifetimes {
- struct_span_err!(
- self.tcx.sess,
- trait_ref.span,
- E0316,
- "nested quantification of lifetimes"
- )
- .emit();
- }
-
- let (binders, lifetimes) = if let Some(hir_id) = trait_ref_hack {
- let mut binders = self.map.late_bound_vars.entry(hir_id).or_default().clone();
- let initial_bound_vars = binders.len() as u32;
- let mut lifetimes: FxHashMap<hir::ParamName, Region> = FxHashMap::default();
- let binders_iter = trait_ref
- .bound_generic_params
- .iter()
- .filter_map(|param| match param.kind {
- GenericParamKind::Lifetime { .. } => Some(param),
- _ => None,
- })
- .enumerate()
- .map(|(late_bound_idx, param)| {
- let pair = Region::late(
- initial_bound_vars + late_bound_idx as u32,
- &self.tcx.hir(),
- param,
- );
- let r = late_region_as_bound_region(self.tcx, &pair.1);
- lifetimes.insert(pair.0, pair.1);
- r
- });
- binders.extend(binders_iter);
-
- (binders, lifetimes)
- } else {
- let mut supertrait_lifetimes = vec![];
- let mut scope = self.scope;
- let mut outer_binders = loop {
- match scope {
- Scope::Body { .. } | Scope::Root => {
- break vec![];
- }
+ let (mut binders, scope_type) = self.poly_trait_ref_binder_info();
- Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } => {
- scope = s;
- }
-
- Scope::TraitRefHackInner { hir_id, .. } => {
- // Nested poly trait refs have the binders concatenated
- // If we reach `TraitRefHackInner`, then there is only one more `Binder` above us,
- // over all the bounds. We don't want this, since all the lifetimes we care about
- // are here anyways.
- let mut full_binders =
- self.map.late_bound_vars.entry(*hir_id).or_default().clone();
- full_binders.extend(supertrait_lifetimes.into_iter());
- break full_binders;
- }
-
- Scope::Supertrait { s, lifetimes } => {
- supertrait_lifetimes = lifetimes.clone();
- scope = s;
- }
-
- Scope::TraitRefBoundary { .. } => {
- // We should only see super trait lifetimes if there is a `Binder` above
- assert!(supertrait_lifetimes.is_empty());
- break vec![];
- }
-
- Scope::Binder { hir_id, from_poly_trait_ref, .. } => {
- if !from_poly_trait_ref {
- // We should only see super trait lifetimes if there is a `Binder` above
- assert!(supertrait_lifetimes.is_empty());
- break vec![];
- }
- // Nested poly trait refs have the binders concatenated
- let mut full_binders =
- self.map.late_bound_vars.entry(*hir_id).or_default().clone();
- full_binders.extend(supertrait_lifetimes.into_iter());
- break full_binders;
- }
- }
- };
- let (lifetimes, local_binders): (FxHashMap<hir::ParamName, Region>, Vec<_>) = trait_ref
- .bound_generic_params
- .iter()
- .filter_map(|param| match param.kind {
- GenericParamKind::Lifetime { .. } => Some(param),
- _ => None,
- })
- .enumerate()
- .map(|(late_bound_idx, param)| {
- let pair = Region::late(
- outer_binders.len() as u32 + late_bound_idx as u32,
- &self.tcx.hir(),
- param,
- );
- let r = late_region_as_bound_region(self.tcx, &pair.1);
- (pair, r)
- })
- .unzip();
-
- outer_binders.extend(local_binders.into_iter());
-
- (outer_binders, lifetimes)
- };
+ let initial_bound_vars = binders.len() as u32;
+ let mut lifetimes: FxHashMap<hir::ParamName, Region> = FxHashMap::default();
+ let binders_iter = trait_ref
+ .bound_generic_params
+ .iter()
+ .filter_map(|param| match param.kind {
+ GenericParamKind::Lifetime { .. } => Some(param),
+ _ => None,
+ })
+ .enumerate()
+ .map(|(late_bound_idx, param)| {
+ let pair = Region::late(
+ initial_bound_vars + late_bound_idx as u32,
+ &self.tcx.hir(),
+ param,
+ );
+ let r = late_region_as_bound_region(self.tcx, &pair.1);
+ lifetimes.insert(pair.0, pair.1);
+ r
+ });
+ binders.extend(binders_iter);
debug!(?binders);
self.map.late_bound_vars.insert(trait_ref.trait_ref.hir_ref_id, binders);
- if trait_ref_hack.is_none() || has_lifetimes {
- let scope = Scope::Binder {
- hir_id: trait_ref.trait_ref.hir_ref_id,
- lifetimes,
- s: self.scope,
- next_early_index,
- track_lifetime_uses: true,
- opaque_type_parent: false,
- from_poly_trait_ref: true,
- };
- self.with(scope, |old_scope, this| {
- this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params);
- walk_list!(this, visit_generic_param, trait_ref.bound_generic_params);
- this.visit_trait_ref(&trait_ref.trait_ref);
- });
- } else {
- let scope =
- Scope::TraitRefHackInner { hir_id: trait_ref.trait_ref.hir_ref_id, s: self.scope };
- self.with(scope, |_old_scope, this| {
- this.visit_trait_ref(&trait_ref.trait_ref);
- });
- }
- self.trait_ref_hack = trait_ref_hack;
+ // Always introduce a scope here, even if this is in a where clause and
+ // we introduced the binders around the bounded Ty. In that case, we
+ // just reuse the concatenation functionality also present in nested trait
+ // refs.
+ let scope = Scope::Binder {
+ hir_id: trait_ref.trait_ref.hir_ref_id,
+ lifetimes,
+ s: self.scope,
+ next_early_index,
+ track_lifetime_uses: true,
+ opaque_type_parent: false,
+ scope_type,
+ };
+ self.with(scope, |old_scope, this| {
+ this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params);
+ walk_list!(this, visit_generic_param, trait_ref.bound_generic_params);
+ this.visit_trait_ref(&trait_ref.trait_ref);
+ });
+
if should_pop_missing_lt {
self.missing_named_lifetime_spots.pop();
}
Scope::Body { s, .. }
| Scope::Elision { s, .. }
| Scope::ObjectLifetimeDefault { s, .. }
- | Scope::TraitRefHackInner { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s, .. } => {
scope = s;
let labels_in_fn = take(&mut self.labels_in_fn);
let xcrate_object_lifetime_defaults = take(&mut self.xcrate_object_lifetime_defaults);
let missing_named_lifetime_spots = take(&mut self.missing_named_lifetime_spots);
- let trait_ref_hack = take(&mut self.trait_ref_hack);
let mut this = LifetimeContext {
tcx: *tcx,
map,
scope: &wrap_scope,
- trait_ref_hack,
is_in_fn_syntax: self.is_in_fn_syntax,
is_in_const_generic: self.is_in_const_generic,
trait_definition_only: self.trait_definition_only,
self.labels_in_fn = this.labels_in_fn;
self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults;
self.missing_named_lifetime_spots = this.missing_named_lifetime_spots;
- self.trait_ref_hack = this.trait_ref_hack;
}
/// helper method to determine the span to remove when suggesting the
s: self.scope,
opaque_type_parent: true,
track_lifetime_uses: false,
- from_poly_trait_ref: false,
+ scope_type: BinderScopeType::Normal,
};
self.with(scope, move |old_scope, this| {
this.check_lifetime_params(old_scope, &generics.params);
| Scope::Body { s, .. }
| Scope::Elision { s, .. }
| Scope::ObjectLifetimeDefault { s, .. }
- | Scope::TraitRefHackInner { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s, .. } => scope = s,
}
// given name or we run out of scopes.
// search.
let mut late_depth = 0;
- let mut in_poly_trait_ref = false;
let mut scope = self.scope;
let mut outermost_body = None;
let result = loop {
break None;
}
- Scope::TraitRefBoundary { s, .. } => {
- // We've exited nested poly trait refs; mark that we are no longer in nested trait refs.
- // We don't increase the late depth because this isn't a `Binder` scope.
- //
- // This came up in #83737, which boiled down to a case like this:
- //
- // ```
- // F: for<> Fn(&()) -> Box<dyn for<> Future<Output = ()> + Unpin>,
- // // ^^^^^
-
- // ```
- //
- // Here, as we traverse upwards from the `dyn for<>` binder, we want to reset `in_poly_trait_ref`
- // to false, so that we avoid excess contaenation when we encounter the outer `for<>` binder.
- in_poly_trait_ref = false;
- scope = s;
- }
-
- Scope::Binder { ref lifetimes, from_poly_trait_ref, s, .. } => {
+ Scope::Binder { ref lifetimes, scope_type, s, .. } => {
match lifetime_ref.name {
LifetimeName::Param(param_name) => {
if let Some(&def) = lifetimes.get(¶m_name.normalize_to_macros_2_0())
}
_ => bug!("expected LifetimeName::Param"),
}
-
- match (from_poly_trait_ref, in_poly_trait_ref) {
- // This is the first binder we see that is a poly trait ref; add one to the
- // late depth and mark that we're potentially in nested trait refs.
- (true, false) => {
- in_poly_trait_ref = true;
- late_depth += 1;
- }
- // We've already seen a binder that is a poly trait ref and this one is too,
- // that means that they are nested and we are concatenating the bound vars;
- // don't increase the late depth.
- //
- // This happens specifically with associated trait bounds like the following:
- //
- // ```
- // for<'a> T: Iterator<Item: for<'b> Foo<'a, 'b>>
- // ```
- //
- // In this case, as we traverse `for<'b>`, we would increment `late_depth` but
- // set `in_poly_trait_ref` to true. Then when we traverse `for<'a>`, we would
- // not increment `late_depth` again. (NB: Niko thinks this logic is actually
- // wrong.)
- (true, true) => {}
- // We've exited nested poly trait refs; add one to the late depth and mark
- // that we are no longer in nested trait refs
- (false, true) => {
- in_poly_trait_ref = false;
- late_depth += 1;
- }
- // Any other kind of nested binders: just increase late depth.
- (false, false) => {
- late_depth += 1;
- }
+ match scope_type {
+ BinderScopeType::Normal => late_depth += 1,
+ BinderScopeType::Concatenating => {}
}
scope = s;
}
Scope::Elision { s, .. }
| Scope::ObjectLifetimeDefault { s, .. }
- | Scope::TraitRefHackInner { s, .. }
- | Scope::Supertrait { s, .. } => {
+ | Scope::Supertrait { s, .. }
+ | Scope::TraitRefBoundary { s, .. } => {
scope = s;
}
}
Scope::Binder { s, .. }
| Scope::Elision { s, .. }
| Scope::ObjectLifetimeDefault { s, .. }
- | Scope::TraitRefHackInner { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s, .. } => {
scope = s;
Some(next) => next,
None => break None,
};
+ // See issue #83753. If someone writes an associated type on a non-trait, just treat it as
+ // there being no supertrait HRTBs.
+ match tcx.def_kind(def_id) {
+ DefKind::Trait | DefKind::TraitAlias | DefKind::Impl => {}
+ _ => break None,
+ }
+
if trait_defines_associated_type_named(def_id) {
break Some(bound_vars.into_iter().collect());
}
let mut scope = &*self.scope;
let hir_id = loop {
match scope {
- Scope::Binder { hir_id, .. } | Scope::TraitRefHackInner { hir_id, .. } => {
+ Scope::Binder { hir_id, .. } => {
break *hir_id;
}
Scope::Body { id, .. } => break id.hir_id,
| Scope::TraitRefBoundary { ref s, .. } => {
scope = *s;
}
- Scope::Root => bug!("In fn_like_elision without appropriate scope above"),
+ Scope::Root => {
+ // See issue #83907. Just bail out from looking inside.
+ self.tcx.sess.delay_span_bug(
+ rustc_span::DUMMY_SP,
+ "In fn_like_elision without appropriate scope above",
+ );
+ return;
+ }
}
};
// While not strictly necessary, we gather anon lifetimes *before* actually
let span = lifetime_refs[0].span;
let mut late_depth = 0;
- let mut in_poly_trait_ref = false;
let mut scope = self.scope;
let mut lifetime_names = FxHashSet::default();
let mut lifetime_spans = vec![];
Scope::Root => break None,
- Scope::TraitRefBoundary { s, .. } => {
- // We've exited nested poly trait refs; mark that we are no longer in nested trait refs.
- // We don't increase the late depth because this isn't a `Binder` scope
- in_poly_trait_ref = false;
- scope = s;
- }
-
- Scope::Binder { s, ref lifetimes, from_poly_trait_ref, .. } => {
+ Scope::Binder { s, ref lifetimes, scope_type, .. } => {
// collect named lifetimes for suggestions
for name in lifetimes.keys() {
if let hir::ParamName::Plain(name) = name {
lifetime_spans.push(name.span);
}
}
- // See comments in `resolve_lifetime_ref`
- match (from_poly_trait_ref, in_poly_trait_ref) {
- (true, false) => {
- in_poly_trait_ref = true;
- late_depth += 1;
- }
- (true, true) => {}
- (false, true) => {
- in_poly_trait_ref = false;
- late_depth += 1;
- }
- (false, false) => {
- late_depth += 1;
- }
+ match scope_type {
+ BinderScopeType::Normal => late_depth += 1,
+ BinderScopeType::Concatenating => {}
}
scope = s;
}
}
Scope::ObjectLifetimeDefault { s, .. }
- | Scope::TraitRefHackInner { s, .. }
- | Scope::Supertrait { s, .. } => {
+ | Scope::Supertrait { s, .. }
+ | Scope::TraitRefBoundary { s, .. } => {
scope = s;
}
}
fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
debug!("resolve_object_lifetime_default(lifetime_ref={:?})", lifetime_ref);
let mut late_depth = 0;
- let mut in_poly_trait_ref = false;
let mut scope = self.scope;
let lifetime = loop {
match *scope {
- Scope::TraitRefBoundary { s, .. } => {
- // We've exited nested poly trait refs; mark that we are no longer in nested trait refs.
- // We don't increase the late depth because this isn't a `Binder` scope
- in_poly_trait_ref = false;
- scope = s;
- }
-
- Scope::Binder { s, from_poly_trait_ref, .. } => {
- match (from_poly_trait_ref, in_poly_trait_ref) {
- (true, false) => {
- in_poly_trait_ref = true;
- late_depth += 1;
- }
- (true, true) => {}
- (false, true) => {
- in_poly_trait_ref = false;
- late_depth += 1;
- }
- (false, false) => {
- late_depth += 1;
- }
+ Scope::Binder { s, scope_type, .. } => {
+ match scope_type {
+ BinderScopeType::Normal => late_depth += 1,
+ BinderScopeType::Concatenating => {}
}
scope = s;
}
Scope::ObjectLifetimeDefault { lifetime: Some(l), .. } => break l,
- Scope::TraitRefHackInner { s, .. } | Scope::Supertrait { s, .. } => {
+ Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => {
scope = s;
}
}
Scope::Body { s, .. }
| Scope::Elision { s, .. }
| Scope::ObjectLifetimeDefault { s, .. }
- | Scope::TraitRefHackInner { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s, .. } => {
old_scope = s;
} => break false,
Scope::ObjectLifetimeDefault { s, .. }
- | Scope::TraitRefHackInner { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s, .. } => scope = s,
}
ForwardDeclaredTyParam, // FIXME(const_generics_defaults)
/// ERROR E0770: the type of const parameters must not depend on other generic parameters.
ParamInTyOfConstParam(Symbol),
- /// constant values inside of type parameter defaults must not depend on generic parameters.
- ParamInAnonConstInTyDefault(Symbol),
/// generic parameters must not be used inside const evaluations.
///
/// This error is only emitted when using `min_const_generics`.
}
}
Res::Def(DefKind::TyParam, _) | Res::SelfTy(..) => {
- let mut in_ty_param_default = false;
for rib in ribs {
- let has_generic_params = match rib.kind {
+ let has_generic_params: HasGenericParams = match rib.kind {
NormalRibKind
| ClosureOrAsyncRibKind
| AssocItemRibKind
| ModuleRibKind(..)
- | MacroDefinition(..) => {
+ | MacroDefinition(..)
+ | ForwardGenericParamBanRibKind => {
// Nothing to do. Continue.
continue;
}
- // We only forbid constant items if we are inside of type defaults,
- // for example `struct Foo<T, U = [u8; std::mem::size_of::<T>()]>`
- ForwardGenericParamBanRibKind => {
- // FIXME(const_generic_defaults): we may need to distinguish between
- // being in type parameter defaults and const parameter defaults
- in_ty_param_default = true;
- continue;
- }
ConstantItemRibKind(trivial, _) => {
let features = self.session.features_untracked();
// HACK(min_const_generics): We currently only allow `N` or `{ N }`.
}
}
- if in_ty_param_default {
- if record_used {
- self.report_error(
- span,
- ResolutionError::ParamInAnonConstInTyDefault(
- rib_ident.name,
- ),
- );
- }
- return Res::Err;
- } else {
- continue;
- }
+ continue;
}
// This was an attempt to use a type parameter outside its scope.
ribs.next();
}
- let mut in_ty_param_default = false;
for rib in ribs {
let has_generic_params = match rib.kind {
NormalRibKind
| ClosureOrAsyncRibKind
| AssocItemRibKind
| ModuleRibKind(..)
- | MacroDefinition(..) => continue,
-
- // We only forbid constant items if we are inside of type defaults,
- // for example `struct Foo<T, U = [u8; std::mem::size_of::<T>()]>`
- ForwardGenericParamBanRibKind => {
- // FIXME(const_generic_defaults): we may need to distinguish between
- // being in type parameter defaults and const parameter defaults
- in_ty_param_default = true;
- continue;
- }
+ | MacroDefinition(..)
+ | ForwardGenericParamBanRibKind => continue,
+
ConstantItemRibKind(trivial, _) => {
let features = self.session.features_untracked();
// HACK(min_const_generics): We currently only allow `N` or `{ N }`.
return Res::Err;
}
- if in_ty_param_default {
- if record_used {
- self.report_error(
- span,
- ResolutionError::ParamInAnonConstInTyDefault(
- rib_ident.name,
- ),
- );
- }
- return Res::Err;
- } else {
- continue;
- }
+ continue;
}
ItemRibKind(has_generic_params) => has_generic_params,
/// This is what the `LtoCli` values get mapped to after resolving defaults and
/// and taking other command line options into account.
+///
+/// Note that linker plugin-based LTO is a different mechanism entirely.
#[derive(Clone, PartialEq)]
pub enum Lto {
- /// Don't do any LTO whatsoever
+ /// Don't do any LTO whatsoever.
No,
- /// Do a full crate graph LTO with ThinLTO
+ /// Do a full-crate-graph (inter-crate) LTO with ThinLTO.
Thin,
- /// Do a local graph LTO with ThinLTO (only relevant for multiple codegen
- /// units).
+ /// Do a local ThinLTO (intra-crate, over the CodeGen Units of the local crate only). This is
+ /// only relevant if multiple CGUs are used.
ThinLocal,
- /// Do a full crate graph LTO with "fat" LTO
+ /// Do a full-crate-graph (inter-crate) LTO with "fat" LTO.
Fat,
}
CrateType::Rlib
}
-pub fn default_configuration(sess: &Session) -> CrateConfig {
+fn default_configuration(sess: &Session) -> CrateConfig {
let end = &sess.target.endian;
let arch = &sess.target.arch;
let wordsz = sess.target.pointer_width.to_string();
user_cfg
}
-pub fn build_target_config(opts: &Options, target_override: Option<Target>) -> Target {
+pub(super) fn build_target_config(opts: &Options, target_override: Option<Target>) -> Target {
let target_result = target_override.map_or_else(|| Target::search(&opts.target_triple), Ok);
let target = target_result.unwrap_or_else(|e| {
early_error(
"a single extra argument to append to the linker invocation (can be used several times)"),
link_args: Vec<String> = (Vec::new(), parse_list, [UNTRACKED],
"extra arguments to append to the linker invocation (space separated)"),
- link_dead_code: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
+ link_dead_code: Option<bool> = (None, parse_opt_bool, [TRACKED],
"keep dead code at link time (useful for code coverage) (default: no)"),
link_self_contained: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
"control whether to link Rust provided C objects/libraries or rely
}
}
+impl From<usize> for Limit {
+ fn from(value: usize) -> Self {
+ Self::new(value)
+ }
+}
+
impl fmt::Display for Limit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
/// operations such as auto-dereference and monomorphization.
pub recursion_limit: OnceCell<Limit>,
+ /// The size at which the `large_assignments` lint starts
+ /// being emitted.
+ pub move_size_limit: OnceCell<usize>,
+
/// The maximum length of types during monomorphization.
pub type_length_limit: OnceCell<Limit>,
self.recursion_limit.get().copied().unwrap()
}
+ #[inline]
+ pub fn move_size_limit(&self) -> usize {
+ self.move_size_limit.get().copied().unwrap()
+ }
+
#[inline]
pub fn type_length_limit(&self) -> Limit {
self.type_length_limit.get().copied().unwrap()
features: OnceCell::new(),
lint_store: OnceCell::new(),
recursion_limit: OnceCell::new(),
+ move_size_limit: OnceCell::new(),
type_length_limit: OnceCell::new(),
const_eval_limit: OnceCell::new(),
incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)),
/// Used to force two `ExpnData`s to have different `Fingerprint`s.
/// Due to macro expansion, it's possible to end up with two `ExpnId`s
- /// that have identical `ExpnData`s. This violates the constract of `HashStable`
+ /// that have identical `ExpnData`s. This violates the contract of `HashStable`
/// - the two `ExpnId`s are not equal, but their `Fingerprint`s are equal
/// (since the numerical `ExpnId` value is not considered by the `HashStable`
/// implementation).
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(array_windows)]
#![feature(crate_visibility_modifier)]
-#![feature(const_fn)]
#![feature(const_panic)]
#![feature(negative_impls)]
#![feature(nll)]
const_fn,
const_fn_floating_point_arithmetic,
const_fn_fn_ptr_basics,
+ const_fn_trait_bound,
const_fn_transmute,
const_fn_union,
+ const_fn_unsize,
const_generic_defaults,
const_generics,
const_generics_defaults,
label_break_value,
lang,
lang_items,
+ large_assignments,
lateout,
lazy_normalization_consts,
le,
more_struct_aliases,
movbe_target_feature,
move_ref_pattern,
+ move_size_limit,
mul,
mul_assign,
mul_with_overflow,
no,
no_builtins,
no_core,
+ no_coverage,
no_crate_inject,
no_debug,
no_default_passes,
non_modrs_mods,
none_error,
nontemporal_store,
- nontrapping_dash_fptoint: "nontrapping-fptoint",
noop_method_borrow,
noop_method_clone,
noop_method_deref,
rustc_layout_scalar_valid_range_start,
rustc_legacy_const_generics,
rustc_macro_transparency,
+ rustc_main,
rustc_mir,
rustc_nonnull_optimization_guaranteed,
rustc_object_lifetime_default,
rustc_regions,
rustc_reservation_impl,
rustc_serialize,
+ rustc_skip_array_during_method_dispatch,
rustc_specialization_trait,
rustc_stable,
rustc_std_internal_symbol,
simd_reduce_or,
simd_reduce_xor,
simd_rem,
+ simd_round,
simd_saturating_add,
simd_saturating_sub,
simd_scatter,
simd_shl,
simd_shr,
simd_sub,
+ simd_trunc,
simd_xor,
since,
sinf32,
}
/// Sometimes an ABI requires small integers to be extended to a full or partial register. This enum
-/// defines if this extension should be zero-extension or sign-extension when necssary. When it is
-/// not necesary to extend the argument, this enum is ignored.
+/// defines if this extension should be zero-extension or sign-extension when necessary. When it is
+/// not necessary to extend the argument, this enum is ignored.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum ArgExtension {
None,
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(bool_to_option)]
-#![feature(const_fn)]
#![feature(const_panic)]
#![feature(nll)]
#![feature(never_type)]
-use crate::spec::{LinkerFlavor, StackProbeType, Target};
+use crate::spec::{LinkerFlavor, SanitizerSet, StackProbeType, Target};
pub fn target() -> Target {
let mut base = super::linux_musl_base::opts();
base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string());
base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };
base.static_position_independent_executables = true;
+ base.supported_sanitizers =
+ SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD;
Target {
llvm_target: "x86_64-unknown-linux-musl".to_string(),
let unit_obligation =
obligation.with(predicate.without_const().to_predicate(tcx));
if self.predicate_may_hold(&unit_obligation) {
+ err.note("this trait is implemented for `()`.");
err.note(
- "the trait is implemented for `()`. \
- Possibly this error has been caused by changes to \
- Rust's type-inference algorithm (see issue #48950 \
- <https://github.com/rust-lang/rust/issues/48950> \
- for more information). Consider whether you meant to use \
- the type `()` here instead.",
+ "this error might have been caused by changes to \
+ Rust's type-inference algorithm (see issue #48950 \
+ <https://github.com/rust-lang/rust/issues/48950> \
+ for more information).",
);
+ err.help("did you intend to use the type `()` here instead?");
}
}
Arg(String, String),
/// An argument of tuple type. For a "found" argument, the span is
- /// the locationo in the source of the pattern. For a "expected"
+ /// the location in the source of the pattern. For a "expected"
/// argument, it will be None. The vector is a list of (name, ty)
/// strings for the components of the tuple.
Tuple(Option<Span>, Vec<(String, String)>),
.map(|item| ObjectSafetyViolation::AssocConst(item.ident.name, item.ident.span)),
);
+ violations.extend(
+ tcx.associated_items(trait_def_id)
+ .in_definition_order()
+ .filter(|item| item.kind == ty::AssocKind::Type)
+ .filter(|item| !tcx.generics_of(item.def_id).params.is_empty())
+ .map(|item| ObjectSafetyViolation::GAT(item.ident.name, item.ident.span)),
+ );
+
debug!(
"object_safety_violations_for_trait(trait_def_id={:?}) = {:?}",
trait_def_id, violations
return Some(MethodViolationCode::WhereClauseReferencesSelf);
}
- let receiver_ty =
- tcx.liberate_late_bound_regions(method.def_id, sig.map_bound(|sig| sig.inputs()[0]));
+ let receiver_ty = tcx.liberate_late_bound_regions(method.def_id, sig.input(0));
// Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on.
// However, this is already considered object-safe. We allow it as a special case here.
for assoc_type in assoc_types {
if !tcx.generics_of(assoc_type).params.is_empty() {
- // FIXME(generic_associated_types) generate placeholders to
- // extend the trait substs.
- tcx.sess.span_fatal(
+ tcx.sess.delay_span_bug(
obligation.cause.span,
- "generic associated types in trait objects are not supported yet",
+ "GATs in trait object shouldn't have been considered",
);
+ return Err(SelectionError::Unimplemented);
}
// This maybe belongs in wf, but that can't (doesn't) handle
// higher-ranked things.
/// subobligations without taking in a 'parent' depth, causing the
/// generated subobligations to have a `recursion_depth` of `0`.
///
- /// To ensure that obligation_depth never decreasees, we force all subobligations
+ /// To ensure that obligation_depth never decreases, we force all subobligations
/// to have at least the depth of the original obligation.
fn add_depth<T: 'cx, I: Iterator<Item = &'cx mut Obligation<'tcx, T>>>(
&self,
mod common_traits;
pub mod instance;
mod needs_drop;
+pub mod representability;
mod ty;
pub fn provide(providers: &mut Providers) {
--- /dev/null
+//! Check whether a type is representable.
+use rustc_data_structures::stable_map::FxHashMap;
+use rustc_hir as hir;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::Span;
+use std::cmp;
+
+/// Describes whether a type is representable. For types that are not
+/// representable, 'SelfRecursive' and 'ContainsRecursive' are used to
+/// distinguish between types that are recursive with themselves and types that
+/// contain a different recursive type. These cases can therefore be treated
+/// differently when reporting errors.
+///
+/// The ordering of the cases is significant. They are sorted so that cmp::max
+/// will keep the "more erroneous" of two values.
+#[derive(Clone, PartialOrd, Ord, Eq, PartialEq, Debug)]
+pub enum Representability {
+ Representable,
+ ContainsRecursive,
+ SelfRecursive(Vec<Span>),
+}
+
+/// Check whether a type is representable. This means it cannot contain unboxed
+/// structural recursion. This check is needed for structs and enums.
+pub fn ty_is_representable<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, sp: Span) -> Representability {
+ debug!("is_type_representable: {:?}", ty);
+ // To avoid a stack overflow when checking an enum variant or struct that
+ // contains a different, structurally recursive type, maintain a stack
+ // of seen types and check recursion for each of them (issues #3008, #3779).
+ let mut seen: Vec<Ty<'_>> = Vec::new();
+ let mut representable_cache = FxHashMap::default();
+ let r = is_type_structurally_recursive(tcx, sp, &mut seen, &mut representable_cache, ty);
+ debug!("is_type_representable: {:?} is {:?}", ty, r);
+ r
+}
+
+// Iterate until something non-representable is found
+fn fold_repr<It: Iterator<Item = Representability>>(iter: It) -> Representability {
+ iter.fold(Representability::Representable, |r1, r2| match (r1, r2) {
+ (Representability::SelfRecursive(v1), Representability::SelfRecursive(v2)) => {
+ Representability::SelfRecursive(v1.into_iter().chain(v2).collect())
+ }
+ (r1, r2) => cmp::max(r1, r2),
+ })
+}
+
+fn are_inner_types_recursive<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ sp: Span,
+ seen: &mut Vec<Ty<'tcx>>,
+ representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
+ ty: Ty<'tcx>,
+) -> Representability {
+ match ty.kind() {
+ ty::Tuple(..) => {
+ // Find non representable
+ fold_repr(
+ ty.tuple_fields().map(|ty| {
+ is_type_structurally_recursive(tcx, sp, seen, representable_cache, ty)
+ }),
+ )
+ }
+ // Fixed-length vectors.
+ // FIXME(#11924) Behavior undecided for zero-length vectors.
+ ty::Array(ty, _) => is_type_structurally_recursive(tcx, sp, seen, representable_cache, ty),
+ ty::Adt(def, substs) => {
+ // Find non representable fields with their spans
+ fold_repr(def.all_fields().map(|field| {
+ let ty = field.ty(tcx, substs);
+ let span = match field
+ .did
+ .as_local()
+ .map(|id| tcx.hir().local_def_id_to_hir_id(id))
+ .and_then(|id| tcx.hir().find(id))
+ {
+ Some(hir::Node::Field(field)) => field.ty.span,
+ _ => sp,
+ };
+ match is_type_structurally_recursive(tcx, span, seen, representable_cache, ty) {
+ Representability::SelfRecursive(_) => {
+ Representability::SelfRecursive(vec![span])
+ }
+ x => x,
+ }
+ }))
+ }
+ ty::Closure(..) => {
+ // this check is run on type definitions, so we don't expect
+ // to see closure types
+ bug!("requires check invoked on inapplicable type: {:?}", ty)
+ }
+ _ => Representability::Representable,
+ }
+}
+
+fn same_adt<'tcx>(ty: Ty<'tcx>, def: &'tcx ty::AdtDef) -> bool {
+ match *ty.kind() {
+ ty::Adt(ty_def, _) => ty_def == def,
+ _ => false,
+ }
+}
+
+// Does the type `ty` directly (without indirection through a pointer)
+// contain any types on stack `seen`?
+fn is_type_structurally_recursive<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ sp: Span,
+ seen: &mut Vec<Ty<'tcx>>,
+ representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
+ ty: Ty<'tcx>,
+) -> Representability {
+ debug!("is_type_structurally_recursive: {:?} {:?}", ty, sp);
+ if let Some(representability) = representable_cache.get(ty) {
+ debug!(
+ "is_type_structurally_recursive: {:?} {:?} - (cached) {:?}",
+ ty, sp, representability
+ );
+ return representability.clone();
+ }
+
+ let representability =
+ is_type_structurally_recursive_inner(tcx, sp, seen, representable_cache, ty);
+
+ representable_cache.insert(ty, representability.clone());
+ representability
+}
+
+fn is_type_structurally_recursive_inner<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ sp: Span,
+ seen: &mut Vec<Ty<'tcx>>,
+ representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
+ ty: Ty<'tcx>,
+) -> Representability {
+ match ty.kind() {
+ ty::Adt(def, _) => {
+ {
+ // Iterate through stack of previously seen types.
+ let mut iter = seen.iter();
+
+ // The first item in `seen` is the type we are actually curious about.
+ // We want to return SelfRecursive if this type contains itself.
+ // It is important that we DON'T take generic parameters into account
+ // for this check, so that Bar<T> in this example counts as SelfRecursive:
+ //
+ // struct Foo;
+ // struct Bar<T> { x: Bar<Foo> }
+
+ if let Some(&seen_adt) = iter.next() {
+ if same_adt(seen_adt, *def) {
+ debug!("SelfRecursive: {:?} contains {:?}", seen_adt, ty);
+ return Representability::SelfRecursive(vec![sp]);
+ }
+ }
+
+ // We also need to know whether the first item contains other types
+ // that are structurally recursive. If we don't catch this case, we
+ // will recurse infinitely for some inputs.
+ //
+ // It is important that we DO take generic parameters into account
+ // here, so that code like this is considered SelfRecursive, not
+ // ContainsRecursive:
+ //
+ // struct Foo { Option<Option<Foo>> }
+
+ for &seen_adt in iter {
+ if ty::TyS::same_type(ty, seen_adt) {
+ debug!("ContainsRecursive: {:?} contains {:?}", seen_adt, ty);
+ return Representability::ContainsRecursive;
+ }
+ }
+ }
+
+ // For structs and enums, track all previously seen types by pushing them
+ // onto the 'seen' stack.
+ seen.push(ty);
+ let out = are_inner_types_recursive(tcx, sp, seen, representable_cache, ty);
+ seen.pop();
+ out
+ }
+ _ => {
+ // No need to push in other cases.
+ are_inner_types_recursive(tcx, sp, seen, representable_cache, ty)
+ }
+ }
+}
}
}
-fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssociatedItems<'_> {
+fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItems<'_> {
let items = tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did));
- ty::AssociatedItems::new(items)
+ ty::AssocItems::new(items)
}
fn def_ident_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
rustc_index = { path = "../rustc_index" }
rustc_infer = { path = "../rustc_infer" }
rustc_trait_selection = { path = "../rustc_trait_selection" }
+rustc_ty_utils = { path = "../rustc_ty_utils" }
);
}
}
+ (GenericArg::Const(cnst), GenericParamDefKind::Type { .. }) => {
+ let body = tcx.hir().body(cnst.value.body);
+ if let rustc_hir::ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) =
+ body.value.kind
+ {
+ if let Res::Def(DefKind::Fn { .. }, id) = path.res {
+ err.help(&format!(
+ "`{}` is a function item, not a type",
+ tcx.item_name(id)
+ ));
+ err.help("function item types cannot be named directly");
+ }
+ }
+ }
_ => {}
}
ParamKindOrd::Const {
unordered: tcx
.features()
- .const_generics,
+ .unordered_const_ty_params(),
}
}
},
GenericArg::Lifetime(_) => ParamKindOrd::Lifetime,
GenericArg::Type(_) => ParamKindOrd::Type,
GenericArg::Const(_) => ParamKindOrd::Const {
- unordered: tcx.features().const_generics,
+ unordered: tcx
+ .features()
+ .unordered_const_ty_params(),
},
}),
Some(&format!(
GenericParamDefKind::Const { has_default } => {
let ty = tcx.at(self.span).type_of(param.def_id);
if !infer_args && has_default {
- tcx.const_param_default(param.def_id).into()
+ tcx.const_param_default(param.def_id)
+ .subst_spanned(tcx, substs.unwrap(), Some(self.span))
+ .into()
} else {
if infer_args {
self.astconv.ct_infer(ty, Some(param), self.span).into()
let array_ty = tcx.mk_ty(ty::Array(self.ast_ty_to_ty(&ty), length));
self.normalize_ty(ast_ty.span, array_ty)
}
- hir::TyKind::Typeof(ref _e) => {
+ hir::TyKind::Typeof(ref e) => {
tcx.sess.emit_err(TypeofReservedKeywordUsed { span: ast_ty.span });
- tcx.ty_error()
+ tcx.type_of(tcx.hir().local_def_id(e.hir_id))
}
hir::TyKind::Infer => {
// Infer also appears as the type of arguments or return
{
sugg = Some(format!("&{}", mutbl.prefix_str()));
}
+ } else if let ty::RawPtr(TypeAndMut { mutbl, .. }) = *self.cast_ty.kind() {
+ if fcx
+ .try_coerce(
+ self.expr,
+ fcx.tcx.mk_ref(
+ &ty::RegionKind::ReErased,
+ TypeAndMut { ty: self.expr_ty, mutbl },
+ ),
+ self.cast_ty,
+ AllowTwoPhase::No,
+ )
+ .is_ok()
+ {
+ sugg = Some(format!("&{}", mutbl.prefix_str()));
+ }
}
if let Some(sugg) = sugg {
err.span_label(self.span, "invalid cast");
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::layout::MAX_SIMD_LANES;
use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::util::{Discr, IntTypeExt, Representability};
+use rustc_middle::ty::util::{Discr, IntTypeExt};
use rustc_middle::ty::{self, ParamEnv, RegionKind, ToPredicate, Ty, TyCtxt};
use rustc_session::config::EntryFnType;
use rustc_session::lint::builtin::UNINHABITED_STATIC;
use rustc_trait_selection::opaque_types::InferCtxtExt as _;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
use rustc_trait_selection::traits::{self, ObligationCauseCode};
+use rustc_ty_utils::representability::{self, Representability};
use std::iter;
use std::ops::ControlFlow;
// recursive type. It is only necessary to throw an error on those that
// contain themselves. For case 2, there must be an inner type that will be
// caught by case 1.
- match rty.is_representable(tcx, sp) {
+ match representability::ty_is_representable(tcx, rty, sp) {
Representability::SelfRecursive(spans) => {
recursive_type_with_infinite_size_error(tcx, item_def_id.to_def_id(), spans);
return false;
}
};
if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) {
+ // Intrinsics are not coercible to function pointers.
+ if a_sig.abi() == Abi::RustIntrinsic
+ || a_sig.abi() == Abi::PlatformIntrinsic
+ || b_sig.abi() == Abi::RustIntrinsic
+ || b_sig.abi() == Abi::PlatformIntrinsic
+ {
+ return Err(TypeError::IntrinsicCast);
+ }
// The signature must match.
let a_sig = self.normalize_associated_types_in(new.span, a_sig);
let b_sig = self.normalize_associated_types_in(new.span, b_sig);
if let (Some((expr, _)), Some((fn_decl, _, _))) =
(expression, fcx.get_node_fn_decl(parent_item))
{
- fcx.suggest_missing_return_expr(&mut err, expr, fn_decl, expected, found, parent_id);
+ fcx.suggest_missing_break_or_return_expr(
+ &mut err, expr, fn_decl, expected, found, id, parent_id,
+ );
}
if let (Some(sp), Some(fn_output)) = (fcx.ret_coercion_span.get(), fn_output) {
self.add_impl_trait_explanation(&mut err, cause, fcx, expected, sp, fn_output);
}
-
- if let Some(sp) = fcx.ret_coercion_span.get() {
- // If the closure has an explicit return type annotation,
- // then a type error may occur at the first return expression we
- // see in the closure (if it conflicts with the declared
- // return type). Skip adding a note in this case, since it
- // would be incorrect.
- if !err.span.primary_spans().iter().any(|&span| span == sp) {
- let hir = fcx.tcx.hir();
- let body_owner = hir.body_owned_by(hir.enclosing_body_owner(fcx.body_id));
- if fcx.tcx.is_closure(hir.body_owner_def_id(body_owner).to_def_id()) {
- err.span_note(
- sp,
- &format!(
- "return type inferred to be `{}` here",
- fcx.resolve_vars_if_possible(expected)
- ),
- );
- }
- }
- }
-
err
}
self.suggest_missing_parentheses(err, expr);
self.note_need_for_fn_pointer(err, expected, expr_ty);
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
+ self.report_closure_infered_return_type(err, expected)
}
// Requires that the two types unify, and prints an error message if
_ => false,
}
}
+
+ // Report the type inferred by the return statement.
+ fn report_closure_infered_return_type(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expected: Ty<'tcx>,
+ ) {
+ if let Some(sp) = self.ret_coercion_span.get() {
+ // If the closure has an explicit return type annotation,
+ // then a type error may occur at the first return expression we
+ // see in the closure (if it conflicts with the declared
+ // return type). Skip adding a note in this case, since it
+ // would be incorrect.
+ if !err.span.primary_spans().iter().any(|&span| span == sp) {
+ let hir = self.tcx.hir();
+ let body_owner = hir.body_owned_by(hir.enclosing_body_owner(self.body_id));
+ if self.tcx.is_closure(hir.body_owner_def_id(body_owner).to_def_id()) {
+ err.span_note(
+ sp,
+ &format!(
+ "return type inferred to be `{}` here",
+ self.resolve_vars_if_possible(expected)
+ ),
+ );
+ }
+ }
+ }
+ }
}
}
GenericParamDefKind::Const { has_default, .. } => {
if !infer_args && has_default {
- tcx.const_param_default(param.def_id).into()
+ tcx.const_param_default(param.def_id)
+ .subst_spanned(tcx, substs.unwrap(), Some(self.span))
+ .into()
} else {
self.fcx.var_for_def(self.span, param)
}
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind};
use rustc_hir::lang_items::LangItem;
-use rustc_hir::{ExprKind, ItemKind, Node};
+use rustc_hir::{Expr, ExprKind, ItemKind, Node, Stmt, StmtKind};
use rustc_infer::infer;
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Binder, Ty};
pointing_at_return_type =
self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest);
let fn_id = self.tcx.hir().get_return_block(blk_id).unwrap();
- self.suggest_missing_return_expr(err, expr, &fn_decl, expected, found, fn_id);
+ self.suggest_missing_break_or_return_expr(
+ err, expr, &fn_decl, expected, found, blk_id, fn_id,
+ );
}
pointing_at_return_type
}
}
}
- pub(in super::super) fn suggest_missing_return_expr(
+ pub(in super::super) fn suggest_missing_break_or_return_expr(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &'tcx hir::Expr<'tcx>,
expected: Ty<'tcx>,
found: Ty<'tcx>,
id: hir::HirId,
+ fn_id: hir::HirId,
) {
if !expected.is_unit() {
return;
}
let found = self.resolve_vars_with_obligations(found);
+
+ let in_loop = self.is_loop(id)
+ || self.tcx.hir().parent_iter(id).any(|(parent_id, _)| self.is_loop(parent_id));
+
+ let in_local_statement = self.is_local_statement(id)
+ || self
+ .tcx
+ .hir()
+ .parent_iter(id)
+ .any(|(parent_id, _)| self.is_local_statement(parent_id));
+
+ if in_loop && in_local_statement {
+ err.multipart_suggestion(
+ "you might have meant to break the loop with this value",
+ vec![
+ (expr.span.shrink_to_lo(), "break ".to_string()),
+ (expr.span.shrink_to_hi(), ";".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ return;
+ }
+
if let hir::FnRetTy::Return(ty) = fn_decl.output {
let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
- let bound_vars = self.tcx.late_bound_vars(id);
+ let bound_vars = self.tcx.late_bound_vars(fn_id);
let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars));
let ty = self.normalize_associated_types_in(expr.span, ty);
if self.can_coerce(found, ty) {
self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp, None);
}
}
+
+ fn is_loop(&self, id: hir::HirId) -> bool {
+ let node = self.tcx.hir().get(id);
+ matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
+ }
+
+ fn is_local_statement(&self, id: hir::HirId) -> bool {
+ let node = self.tcx.hir().get(id);
+ matches!(node, Node::Stmt(Stmt { kind: StmtKind::Local(..), .. }))
+ }
}
use rustc_middle::ty::Ty;
use rustc_span::{sym, Span};
use rustc_trait_selection::traits;
-use std::mem;
pub(super) struct GatherLocalsVisitor<'a, 'tcx> {
fcx: &'a FnCtxt<'a, 'tcx>,
// parameters are special cases of patterns, but we want to handle them as
// *distinct* cases. so track when we are hitting a pattern *within* an fn
// parameter.
- outermost_fn_param_pat: bool,
+ outermost_fn_param_pat: Option<Span>,
}
impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
pub(super) fn new(fcx: &'a FnCtxt<'a, 'tcx>, parent_id: hir::HirId) -> Self {
- Self { fcx, parent_id, outermost_fn_param_pat: false }
+ Self { fcx, parent_id, outermost_fn_param_pat: None }
}
fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option<LocalTy<'tcx>>) -> Ty<'tcx> {
}
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
- let old_outermost_fn_param_pat = mem::replace(&mut self.outermost_fn_param_pat, true);
+ let old_outermost_fn_param_pat = self.outermost_fn_param_pat.replace(param.ty_span);
intravisit::walk_param(self, param);
self.outermost_fn_param_pat = old_outermost_fn_param_pat;
}
if let PatKind::Binding(_, _, ident, _) = p.kind {
let var_ty = self.assign(p.span, p.hir_id, None);
- if self.outermost_fn_param_pat {
+ if let Some(ty_span) = self.outermost_fn_param_pat {
if !self.fcx.tcx.features().unsized_fn_params {
self.fcx.require_type_is_sized(
var_ty,
p.span,
- traits::SizedArgumentType(Some(p.span)),
+ traits::SizedArgumentType(Some(ty_span)),
);
}
} else {
var_ty
);
}
- let old_outermost_fn_param_pat = mem::replace(&mut self.outermost_fn_param_pat, false);
+ let old_outermost_fn_param_pat = self.outermost_fn_param_pat.take();
intravisit::walk_pat(self, p);
self.outermost_fn_param_pat = old_outermost_fn_param_pat;
}
| sym::simd_fpow
| sym::simd_saturating_add
| sym::simd_saturating_sub => (1, vec![param(0), param(0)], param(0)),
- sym::simd_neg => (1, vec![param(0)], param(0)),
- sym::simd_fsqrt
+ sym::simd_neg
+ | sym::simd_fsqrt
| sym::simd_fsin
| sym::simd_fcos
| sym::simd_fexp
| sym::simd_flog10
| sym::simd_flog
| sym::simd_fabs
+ | sym::simd_ceil
| sym::simd_floor
- | sym::simd_ceil => (1, vec![param(0)], param(0)),
+ | sym::simd_round
+ | sym::simd_trunc => (1, vec![param(0)], param(0)),
sym::simd_fpowi => (1, vec![param(0), tcx.types.i32], param(0)),
sym::simd_fma => (1, vec![param(0), param(0), param(0)], param(0)),
sym::simd_gather => (3, vec![param(0), param(1), param(2)], param(0)),
/// When adjusting a receiver we often want to do one of
///
-/// - Add a `&` (or `&mut`), converting the recevier from `T` to `&T` (or `&mut T`)
+/// - Add a `&` (or `&mut`), converting the receiver from `T` to `&T` (or `&mut T`)
/// - If the receiver has type `*mut T`, convert it to `*const T`
///
/// This type tells us which one to do.
}
TraitCandidate(trait_ref) => {
+ if let Some(method_name) = self.method_name {
+ // Some trait methods are excluded for arrays before 2021.
+ // (`array.into_iter()` wants a slice iterator for compatibility.)
+ if self_ty.is_array() && !method_name.span.rust_2021() {
+ let trait_def = self.tcx.trait_def(trait_ref.def_id);
+ if trait_def.skip_array_during_method_dispatch {
+ return ProbeResult::NoMatch;
+ }
+ }
+ }
let predicate = trait_ref.without_const().to_predicate(self.tcx);
let obligation = traits::Obligation::new(cause, self.param_env, predicate);
if !self.predicate_may_hold(&obligation) {
let mut alt_rcvr_sugg = false;
if let SelfSource::MethodCall(rcvr) = source {
debug!(?span, ?item_name, ?rcvr_ty, ?rcvr);
+ let skippable = [
+ self.tcx.lang_items().clone_trait(),
+ self.tcx.lang_items().deref_trait(),
+ self.tcx.lang_items().deref_mut_trait(),
+ self.tcx.lang_items().drop_trait(),
+ ];
// Try alternative arbitrary self types that could fulfill this call.
// FIXME: probe for all types that *could* be arbitrary self-types, not
// just this list.
(self.tcx.mk_mut_ref(&ty::ReErased, rcvr_ty), "&mut "),
(self.tcx.mk_imm_ref(&ty::ReErased, rcvr_ty), "&"),
] {
+ if let Ok(pick) = self.lookup_probe(
+ span,
+ item_name,
+ rcvr_ty,
+ rcvr,
+ crate::check::method::probe::ProbeScope::AllTraits,
+ ) {
+ // If the method is defined for the receiver we have, it likely wasn't `use`d.
+ // We point at the method, but we just skip the rest of the check for arbitrary
+ // self types and rely on the suggestion to `use` the trait from
+ // `suggest_valid_traits`.
+ let did = Some(pick.item.container.id());
+ let skip = skippable.contains(&did);
+ if pick.autoderefs == 0 && !skip {
+ err.span_label(
+ pick.item.ident.span,
+ &format!("the method is available for `{}` here", rcvr_ty),
+ );
+ }
+ break;
+ }
for (rcvr_ty, pre) in &[
(self.tcx.mk_lang_item(rcvr_ty, LangItem::OwnedBox), "Box::new"),
(self.tcx.mk_lang_item(rcvr_ty, LangItem::Pin), "Pin::new"),
// We don't want to suggest a container type when the missing
// method is `.clone()` or `.deref()` otherwise we'd suggest
// `Arc::new(foo).clone()`, which is far from what the user wants.
- let skip = [
- self.tcx.lang_items().clone_trait(),
- self.tcx.lang_items().deref_trait(),
- self.tcx.lang_items().deref_mut_trait(),
- self.tcx.lang_items().drop_trait(),
- ]
- .contains(&did);
+ let skip = skippable.contains(&did);
// Make sure the method is defined for the *actual* receiver: we don't
// want to treat `Box<Self>` as a receiver if it only works because of
// an autoderef to `&self`
}
}
}
- if !alt_rcvr_sugg && self.suggest_valid_traits(err, valid_out_of_scope_traits) {
+ if self.suggest_valid_traits(err, valid_out_of_scope_traits) {
return;
}
kind: TypeVariableOriginKind::TypeInference,
span,
}),
+ Node::Ty(&hir::Ty {
+ kind: hir::TyKind::Typeof(ref anon_const), ..
+ }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin {
+ kind: TypeVariableOriginKind::TypeInference,
+ span,
+ }),
Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(ia), .. })
if ia.operands.iter().any(|(op, _op_sp)| match op {
hir::InlineAsmOperand::Const { anon_const } => {
/// If both the CaptureKind and Expression are considered to be equivalent,
/// then `CaptureInfo` A is preferred. This can be useful in cases where we want to priortize
/// expressions reported back to the user as part of diagnostics based on which appears earlier
-/// in the closure. This can be acheived simply by calling
+/// in the closure. This can be achieved simply by calling
/// `determine_capture_info(existing_info, current_info)`. This works out because the
/// expressions that occur earlier in the closure body than the current expression are processed before.
/// Consider the following example
//
// Here, the default `Vec<[u32]>` is not WF because `[u32]: Sized` does not hold.
for param in &generics.params {
- if let GenericParamDefKind::Type { .. } = param.kind {
- if is_our_default(¶m) {
- let ty = fcx.tcx.type_of(param.def_id);
- // Ignore dependent defaults -- that is, where the default of one type
- // parameter includes another (e.g., `<T, U = T>`). In those cases, we can't
- // be sure if it will error or not as user might always specify the other.
- if !ty.needs_subst() {
+ match param.kind {
+ GenericParamDefKind::Type { .. } => {
+ if is_our_default(¶m) {
+ let ty = fcx.tcx.type_of(param.def_id);
+ // Ignore dependent defaults -- that is, where the default of one type
+ // parameter includes another (e.g., `<T, U = T>`). In those cases, we can't
+ // be sure if it will error or not as user might always specify the other.
+ if !ty.needs_subst() {
+ fcx.register_wf_obligation(
+ ty.into(),
+ fcx.tcx.def_span(param.def_id),
+ ObligationCauseCode::MiscObligation,
+ );
+ }
+ }
+ }
+ GenericParamDefKind::Const { .. } => {
+ // FIXME(const_generics_defaults): Figure out if this
+ // is the behavior we want, see the comment further below.
+ if is_our_default(¶m) {
+ let default_ct = tcx.const_param_default(param.def_id);
fcx.register_wf_obligation(
- ty.into(),
+ default_ct.into(),
fcx.tcx.def_span(param.def_id),
ObligationCauseCode::MiscObligation,
);
}
}
+ // Doesn't have defaults.
+ GenericParamDefKind::Lifetime => {}
}
}
fcx.tcx.mk_param_from_def(param)
}
GenericParamDefKind::Const { .. } => {
+ // FIXME(const_generics_defaults): I(@lcnr) feel like always
+ // using the const parameter is the right choice here, even
+ // if it needs substs.
+ //
+ // Before stabilizing this we probably want to get some tests
+ // where this makes a difference and figure out what's the exact
+ // behavior we want here.
+
+ // If the param has a default, ...
if is_our_default(param) {
let default_ct = tcx.const_param_default(param.def_id);
- // Const params currently have to be concrete.
- assert!(!default_ct.needs_subst());
- default_ct.into()
- } else {
- fcx.tcx.mk_param_from_def(param)
+ // ... and it's not a dependent default, ...
+ if !default_ct.needs_subst() {
+ // ... then substitute it with the default.
+ return default_ct.into();
+ }
}
+
+ fcx.tcx.mk_param_from_def(param)
}
}
});
/// namespace.
fn impls_have_common_items(
&self,
- impl_items1: &ty::AssociatedItems<'_>,
- impl_items2: &ty::AssociatedItems<'_>,
+ impl_items1: &ty::AssocItems<'_>,
+ impl_items2: &ty::AssocItems<'_>,
) -> bool {
let mut impl_items1 = &impl_items1;
let mut impl_items2 = &impl_items2;
}
let is_marker = tcx.has_attr(def_id, sym::marker);
+ let skip_array_during_method_dispatch =
+ tcx.has_attr(def_id, sym::rustc_skip_array_during_method_dispatch);
let spec_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) {
ty::trait_def::TraitSpecializationKind::Marker
} else if tcx.has_attr(def_id, sym::rustc_specialization_trait) {
ty::trait_def::TraitSpecializationKind::None
};
let def_path_hash = tcx.def_path_hash(def_id);
- ty::TraitDef::new(def_id, unsafety, paren_sugar, is_auto, is_marker, spec_kind, def_path_hash)
+ ty::TraitDef::new(
+ def_id,
+ unsafety,
+ paren_sugar,
+ is_auto,
+ is_marker,
+ skip_array_during_method_dispatch,
+ spec_kind,
+ def_path_hash,
+ )
}
fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<Span> {
}
}
-struct AnonConstInParamListDetector {
- in_param_list: bool,
- found_anon_const_in_list: bool,
+struct AnonConstInParamTyDetector {
+ in_param_ty: bool,
+ found_anon_const_in_param_ty: bool,
ct: HirId,
}
-impl<'v> Visitor<'v> for AnonConstInParamListDetector {
+impl<'v> Visitor<'v> for AnonConstInParamTyDetector {
type Map = intravisit::ErasedMap<'v>;
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
}
fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) {
- let prev = self.in_param_list;
- self.in_param_list = true;
- intravisit::walk_generic_param(self, p);
- self.in_param_list = prev;
+ if let GenericParamKind::Const { ref ty, default: _ } = p.kind {
+ let prev = self.in_param_ty;
+ self.in_param_ty = true;
+ self.visit_ty(ty);
+ self.in_param_ty = prev;
+ }
}
fn visit_anon_const(&mut self, c: &'v hir::AnonConst) {
- if self.in_param_list && self.ct == c.hir_id {
- self.found_anon_const_in_list = true;
+ if self.in_param_ty && self.ct == c.hir_id {
+ self.found_anon_const_in_param_ty = true;
} else {
intravisit::walk_anon_const(self, c)
}
let parent_id = tcx.hir().get_parent_item(hir_id);
let parent_def_id = tcx.hir().local_def_id(parent_id);
- let mut in_param_list = false;
+ let mut in_param_ty = false;
for (_parent, node) in tcx.hir().parent_iter(hir_id) {
if let Some(generics) = node.generics() {
- let mut visitor = AnonConstInParamListDetector {
- in_param_list: false,
- found_anon_const_in_list: false,
+ let mut visitor = AnonConstInParamTyDetector {
+ in_param_ty: false,
+ found_anon_const_in_param_ty: false,
ct: hir_id,
};
visitor.visit_generics(generics);
- in_param_list = visitor.found_anon_const_in_list;
+ in_param_ty = visitor.found_anon_const_in_param_ty;
break;
}
}
- if in_param_list {
+ if in_param_ty {
// We do not allow generic parameters in anon consts if we are inside
- // of a param list.
- //
- // This affects both default type bindings, e.g. `struct<T, U = [u8; std::mem::size_of::<T>()]>(T, U)`,
- // and the types of const parameters, e.g. `struct V<const N: usize, const M: [u8; N]>();`.
+ // of a const parameter type, e.g. `struct Foo<const N: usize, const M: [u8; N]>` is not allowed.
None
} else if tcx.lazy_normalization() {
// HACK(eddyb) this provides the correct generics when
let mut inline_span = None;
let mut link_ordinal_span = None;
let mut no_sanitize_span = None;
+ let mut no_coverage_feature_enabled = false;
+ let mut no_coverage_attr = None;
for attr in attrs.iter() {
if tcx.sess.check_name(attr, sym::cold) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED;
} else if tcx.sess.check_name(attr, sym::no_mangle) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
+ } else if attr.has_name(sym::feature) {
+ if let Some(list) = attr.meta_item_list() {
+ if list.iter().any(|nested_meta_item| nested_meta_item.has_name(sym::no_coverage)) {
+ tcx.sess.mark_attr_used(attr);
+ no_coverage_feature_enabled = true;
+ }
+ }
+ } else if tcx.sess.check_name(attr, sym::no_coverage) {
+ no_coverage_attr = Some(attr);
} else if tcx.sess.check_name(attr, sym::rustc_std_internal_symbol) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
} else if tcx.sess.check_name(attr, sym::used) {
}
}
+ if let Some(no_coverage_attr) = no_coverage_attr {
+ if tcx.sess.features_untracked().no_coverage || no_coverage_feature_enabled {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE
+ } else {
+ let mut err = feature_err(
+ &tcx.sess.parse_sess,
+ sym::no_coverage,
+ no_coverage_attr.span,
+ "the `#[no_coverage]` attribute is an experimental feature",
+ );
+ if tcx.sess.parse_sess.unstable_features.is_nightly_build() {
+ err.help("or, alternatively, add `#[feature(no_coverage)]` to the function");
+ }
+ err.emit();
+ }
+ }
+
codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
if !attr.has_name(sym::inline) {
return ia;
let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));
match parent_node {
Node::Ty(&Ty { kind: TyKind::Array(_, ref constant), .. })
- | Node::Ty(&Ty { kind: TyKind::Typeof(ref constant), .. })
| Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. })
if constant.hir_id == hir_id =>
{
tcx.types.usize
}
+ Node::Ty(&Ty { kind: TyKind::Typeof(ref e), .. }) if e.hir_id == hir_id => {
+ tcx.typeck(def_id).node_type(e.hir_id)
+ }
Node::Expr(&Expr { kind: ExprKind::ConstBlock(ref anon_const), .. })
if anon_const.hir_id == hir_id =>
/// In the following example the closures `c` only captures `p.x`` even though `incr`
/// is a capture of the nested closure
///
- /// ```rust,ignore(cannot-test-this-because-pseduo-code)
+ /// ```rust,ignore(cannot-test-this-because-pseudo-code)
/// let p = ..;
/// let c = || {
/// let incr = 10;
// The only places we want to fake read before creating the parent closure are the ones that
// are not local to it/ defined by it.
//
- // ```rust,ignore(cannot-test-this-because-pseduo-code)
+ // ```rust,ignore(cannot-test-this-because-pseudo-code)
// let v1 = (0, 1);
// let c = || { // fake reads: v1
// let v2 = (0, 1);
unsafe { self.sift_up(start, pos) };
}
+ /// Rebuild assuming data[0..start] is still a proper heap.
+ fn rebuild_tail(&mut self, start: usize) {
+ if start == self.len() {
+ return;
+ }
+
+ let tail_len = self.len() - start;
+
+ #[inline(always)]
+ fn log2_fast(x: usize) -> usize {
+ (usize::BITS - x.leading_zeros() - 1) as usize
+ }
+
+ // `rebuild` takes O(self.len()) operations
+ // and about 2 * self.len() comparisons in the worst case
+ // while repeating `sift_up` takes O(tail_len * log(start)) operations
+ // and about 1 * tail_len * log_2(start) comparisons in the worst case,
+ // assuming start >= tail_len. For larger heaps, the crossover point
+ // no longer follows this reasoning and was determined empirically.
+ let better_to_rebuild = if start < tail_len {
+ true
+ } else if self.len() <= 2048 {
+ 2 * self.len() < tail_len * log2_fast(start)
+ } else {
+ 2 * self.len() < tail_len * 11
+ };
+
+ if better_to_rebuild {
+ self.rebuild();
+ } else {
+ for i in start..self.len() {
+ // SAFETY: The index `i` is always less than self.len().
+ unsafe { self.sift_up(0, i) };
+ }
+ }
+ }
+
fn rebuild(&mut self) {
let mut n = self.len() / 2;
while n > 0 {
swap(self, other);
}
- if other.is_empty() {
- return;
- }
-
- #[inline(always)]
- fn log2_fast(x: usize) -> usize {
- (usize::BITS - x.leading_zeros() - 1) as usize
- }
+ let start = self.data.len();
- // `rebuild` takes O(len1 + len2) operations
- // and about 2 * (len1 + len2) comparisons in the worst case
- // while `extend` takes O(len2 * log(len1)) operations
- // and about 1 * len2 * log_2(len1) comparisons in the worst case,
- // assuming len1 >= len2. For larger heaps, the crossover point
- // no longer follows this reasoning and was determined empirically.
- #[inline]
- fn better_to_rebuild(len1: usize, len2: usize) -> bool {
- let tot_len = len1 + len2;
- if tot_len <= 2048 {
- 2 * tot_len < len2 * log2_fast(len1)
- } else {
- 2 * tot_len < len2 * 11
- }
- }
+ self.data.append(&mut other.data);
- if better_to_rebuild(self.len(), other.len()) {
- self.data.append(&mut other.data);
- self.rebuild();
- } else {
- self.extend(other.drain());
- }
+ self.rebuild_tail(start);
}
/// Returns an iterator which retrieves elements in heap order.
/// assert_eq!(heap.into_sorted_vec(), [-10, 2, 4])
/// ```
#[unstable(feature = "binary_heap_retain", issue = "71503")]
- pub fn retain<F>(&mut self, f: F)
+ pub fn retain<F>(&mut self, mut f: F)
where
F: FnMut(&T) -> bool,
{
- self.data.retain(f);
- self.rebuild();
+ let mut first_removed = self.len();
+ let mut i = 0;
+ self.data.retain(|e| {
+ let keep = f(e);
+ if !keep && i < first_removed {
+ first_removed = i;
+ }
+ i += 1;
+ keep
+ });
+ // data[0..first_removed] is untouched, so we only need to rebuild the tail:
+ self.rebuild_tail(first_removed);
}
}
/// [`Result::Err`] is returned, containing the index where a matching
/// element could be inserted while maintaining sorted order.
///
+ /// See also [`binary_search_by`], [`binary_search_by_key`], and [`partition_point`].
+ ///
+ /// [`binary_search_by`]: VecDeque::binary_search_by
+ /// [`binary_search_by_key`]: VecDeque::binary_search_by_key
+ /// [`partition_point`]: VecDeque::partition_point
+ ///
/// # Examples
///
/// Looks up a series of four elements. The first is found, with a
/// [`Result::Err`] is returned, containing the index where a matching
/// element could be inserted while maintaining sorted order.
///
+ /// See also [`binary_search`], [`binary_search_by_key`], and [`partition_point`].
+ ///
+ /// [`binary_search`]: VecDeque::binary_search
+ /// [`binary_search_by_key`]: VecDeque::binary_search_by_key
+ /// [`partition_point`]: VecDeque::partition_point
+ ///
/// # Examples
///
/// Looks up a series of four elements. The first is found, with a
F: FnMut(&'a T) -> Ordering,
{
let (front, back) = self.as_slices();
+ let cmp_back = back.first().map(|elem| f(elem));
- if let Some(Ordering::Less | Ordering::Equal) = back.first().map(|elem| f(elem)) {
+ if let Some(Ordering::Equal) = cmp_back {
+ Ok(front.len())
+ } else if let Some(Ordering::Less) = cmp_back {
back.binary_search_by(f).map(|idx| idx + front.len()).map_err(|idx| idx + front.len())
} else {
front.binary_search_by(f)
/// Binary searches this sorted `VecDeque` with a key extraction function.
///
/// Assumes that the `VecDeque` is sorted by the key, for instance with
- /// [`make_contiguous().sort_by_key()`](#method.make_contiguous) using the same
- /// key extraction function.
+ /// [`make_contiguous().sort_by_key()`] using the same key extraction function.
///
/// If the value is found then [`Result::Ok`] is returned, containing the
/// index of the matching element. If there are multiple matches, then any
/// [`Result::Err`] is returned, containing the index where a matching
/// element could be inserted while maintaining sorted order.
///
+ /// See also [`binary_search`], [`binary_search_by`], and [`partition_point`].
+ ///
+ /// [`make_contiguous().sort_by_key()`]: VecDeque::make_contiguous
+ /// [`binary_search`]: VecDeque::binary_search
+ /// [`binary_search_by`]: VecDeque::binary_search_by
+ /// [`partition_point`]: VecDeque::partition_point
+ ///
/// # Examples
///
/// Looks up a series of four elements in a slice of pairs sorted by
{
self.binary_search_by(|k| f(k).cmp(b))
}
+
+ /// Returns the index of the partition point according to the given predicate
+ /// (the index of the first element of the second partition).
+ ///
+ /// The deque is assumed to be partitioned according to the given predicate.
+ /// This means that all elements for which the predicate returns true are at the start of the deque
+ /// and all elements for which the predicate returns false are at the end.
+ /// For example, [7, 15, 3, 5, 4, 12, 6] is a partitioned under the predicate x % 2 != 0
+ /// (all odd numbers are at the start, all even at the end).
+ ///
+ /// If this deque is not partitioned, the returned result is unspecified and meaningless,
+ /// as this method performs a kind of binary search.
+ ///
+ /// See also [`binary_search`], [`binary_search_by`], and [`binary_search_by_key`].
+ ///
+ /// [`binary_search`]: VecDeque::binary_search
+ /// [`binary_search_by`]: VecDeque::binary_search_by
+ /// [`binary_search_by_key`]: VecDeque::binary_search_by_key
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(vecdeque_binary_search)]
+ /// use std::collections::VecDeque;
+ ///
+ /// let deque: VecDeque<_> = vec![1, 2, 3, 3, 5, 6, 7].into();
+ /// let i = deque.partition_point(|&x| x < 5);
+ ///
+ /// assert_eq!(i, 4);
+ /// assert!(deque.iter().take(i).all(|&x| x < 5));
+ /// assert!(deque.iter().skip(i).all(|&x| !(x < 5)));
+ /// ```
+ #[unstable(feature = "vecdeque_binary_search", issue = "78021")]
+ pub fn partition_point<P>(&self, mut pred: P) -> usize
+ where
+ P: FnMut(&T) -> bool,
+ {
+ let (front, back) = self.as_slices();
+
+ if let Some(true) = back.first().map(|v| pred(v)) {
+ back.partition_point(pred) + front.len()
+ } else {
+ front.partition_point(pred)
+ }
+ }
}
impl<T: Clone> VecDeque<T> {
#![cfg_attr(test, feature(test))]
#![cfg_attr(test, feature(new_uninit))]
#![feature(allocator_api)]
-#![feature(vec_extend_from_within)]
#![feature(array_chunks)]
#![feature(array_methods)]
#![feature(array_windows)]
#![feature(cfg_target_has_atomic)]
#![feature(coerce_unsized)]
#![feature(const_btree_new)]
-#![feature(const_fn)]
+#![cfg_attr(bootstrap, feature(const_fn))]
+#![cfg_attr(not(bootstrap), feature(const_fn_trait_bound))]
#![feature(cow_is_borrowed)]
#![feature(const_cow_is_borrowed)]
#![feature(destructuring_assignment)]
}
impl<T> RawVec<T, Global> {
- /// HACK(Centril): This exists because `#[unstable]` `const fn`s needn't conform
- /// to `min_const_fn` and so they cannot be called in `min_const_fn`s either.
+ /// HACK(Centril): This exists because stable `const fn` can only call stable `const fn`, so
+ /// they cannot call `Self::new()`.
///
- /// If you change `RawVec<T>::new` or dependencies, please take care to not
- /// introduce anything that would truly violate `min_const_fn`.
- ///
- /// NOTE: We could avoid this hack and check conformance with some
- /// `#[rustc_force_min_const_fn]` attribute which requires conformance
- /// with `min_const_fn` but does not necessarily allow calling it in
- /// `stable(...) const fn` / user code not enabling `foo` when
- /// `#[rustc_const_unstable(feature = "foo", issue = "01234")]` is present.
+ /// If you change `RawVec<T>::new` or dependencies, please take care to not introduce anything
+ /// that would truly const-call something unstable.
pub const NEW: Self = Self::new();
/// Creates the biggest possible `RawVec` (on the system heap)
#[stable(feature = "downgraded_weak", since = "1.10.0")]
impl<T> Default for Weak<T> {
- /// Constructs a new `Weak<T>`, allocating memory for `T` without initializing
- /// it. Calling [`upgrade`] on the return value always gives [`None`].
+ /// Constructs a new `Weak<T>`, without allocating any memory.
+ /// Calling [`upgrade`] on the return value always gives [`None`].
///
/// [`None`]: Option
/// [`upgrade`]: Weak::upgrade
}
#[lang = "slice_alloc"]
-#[cfg_attr(not(test), rustc_diagnostic_item = "slice")]
#[cfg(not(test))]
impl<T> [T] {
/// Sorts the slice.
#[stable(feature = "wake_trait", since = "1.51.0")]
impl<W: Wake + Send + Sync + 'static> From<Arc<W>> for Waker {
+ /// Use a `Wake`-able type as a `Waker`.
+ ///
+ /// No heap allocations or atomic operations are used for this conversion.
fn from(waker: Arc<W>) -> Waker {
// SAFETY: This is safe because raw_waker safely constructs
// a RawWaker from Arc<W>.
#[stable(feature = "wake_trait", since = "1.51.0")]
impl<W: Wake + Send + Sync + 'static> From<Arc<W>> for RawWaker {
+ /// Use a `Wake`-able type as a `RawWaker`.
+ ///
+ /// No heap allocations or atomic operations are used for this conversion.
fn from(waker: Arc<W>) -> RawWaker {
raw_waker(waker)
}
let b = Box::new(Test) as Box<dyn Any>;
let a_str = format!("{:?}", a);
let b_str = format!("{:?}", b);
- assert_eq!(a_str, "Any");
- assert_eq!(b_str, "Any");
+ assert_eq!(a_str, "Any { .. }");
+ assert_eq!(b_str, "Any { .. }");
static EIGHT: usize = 8;
static TEST: Test = Test;
let a = &EIGHT as &dyn Any;
let b = &TEST as &dyn Any;
let s = format!("{:?}", a);
- assert_eq!(s, "Any");
+ assert_eq!(s, "Any { .. }");
let s = format!("{:?}", b);
- assert_eq!(s, "Any");
+ assert_eq!(s, "Any { .. }");
}
#[test]
/// Safety: changing returned .2 (&mut usize) is considered the same as calling `.set_len(_)`.
///
- /// This method is used to have unique access to all vec parts at once in `extend_from_within`.
+ /// This method provides unique access to all vec parts at once in `extend_from_within`.
unsafe fn split_at_spare_mut_with_len(
&mut self,
) -> (&mut [T], &mut [MaybeUninit<T>], &mut usize) {
/// ## Examples
///
/// ```
- /// #![feature(vec_extend_from_within)]
- ///
/// let mut vec = vec![0, 1, 2, 3, 4];
///
/// vec.extend_from_within(2..);
/// vec.extend_from_within(4..8);
/// assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1, 4, 2, 3, 4]);
/// ```
- #[unstable(feature = "vec_extend_from_within", issue = "81656")]
+ #[stable(feature = "vec_extend_from_within", since = "1.53.0")]
pub fn extend_from_within<R>(&mut self, src: R)
where
R: RangeBounds<usize>,
iter::zip(to_clone, spare)
.map(|(src, dst)| dst.write(src.clone()))
// Note:
- // - Element was just initialized with `MaybeUninit::write`, so it's ok to increace len
+ // - Element was just initialized with `MaybeUninit::write`, so it's ok to increase len
// - len is increased after each element to prevent leaks (see issue #82533)
.for_each(|_| *len += 1);
}
/// assert_eq!(Vec::from(b), vec![1, 2, 3]);
/// ```
fn from(s: Box<[T], A>) -> Self {
- let len = s.len();
- Self { buf: RawVec::from_box(s), len }
+ s.into_vec()
}
}
#[test]
fn test_retain() {
- let mut a = BinaryHeap::from(vec![-10, -5, 1, 2, 4, 13]);
- a.retain(|x| x % 2 == 0);
+ let mut a = BinaryHeap::from(vec![100, 10, 50, 1, 2, 20, 30]);
+ a.retain(|&x| x != 2);
- assert_eq!(a.into_sorted_vec(), [-10, 2, 4])
+ // Check that 20 moved into 10's place.
+ assert_eq!(a.clone().into_vec(), [100, 20, 50, 1, 10, 30]);
+
+ a.retain(|_| true);
+
+ assert_eq!(a.clone().into_vec(), [100, 20, 50, 1, 10, 30]);
+
+ a.retain(|&x| x < 50);
+
+ assert_eq!(a.clone().into_vec(), [30, 20, 10, 1]);
+
+ a.retain(|_| false);
+
+ assert!(a.is_empty());
}
// old binaryheap failed this test
#![feature(vecdeque_binary_search)]
#![feature(slice_group_by)]
#![feature(slice_partition_dedup)]
-#![feature(vec_extend_from_within)]
#![feature(vec_spare_capacity)]
#![feature(string_remove_matches)]
assert_eq!(deque.binary_search_by_key(&4, |&(v,)| v), Err(3));
}
+#[test]
+fn test_partition_point() {
+ // Contiguous (front only) search:
+ let deque: VecDeque<_> = vec![1, 2, 3, 5, 6].into();
+ assert!(deque.as_slices().1.is_empty());
+ assert_eq!(deque.partition_point(|&v| v <= 3), 3);
+
+ // Split search (both front & back non-empty):
+ let mut deque: VecDeque<_> = vec![5, 6].into();
+ deque.push_front(3);
+ deque.push_front(2);
+ deque.push_front(1);
+ deque.push_back(10);
+ assert!(!deque.as_slices().0.is_empty());
+ assert!(!deque.as_slices().1.is_empty());
+ assert_eq!(deque.partition_point(|&v| v <= 5), 4);
+}
+
#[test]
fn test_zero_sized_push() {
const N: usize = 8;
-Subproject commit 710fc18ddcb6c7677b3c96359abb35da37f2a488
+Subproject commit 221483ebaf45df5c956adffee2d4024e7c3b96b8
#[bench]
fn write_u128_max(bh: &mut Bencher) {
bh.iter(|| {
- std::hint::black_box(format!("{}", u128::MAX));
+ test::black_box(format!("{}", u128::MAX));
});
}
fn write_u128_min(bh: &mut Bencher) {
bh.iter(|| {
let s = format!("{}", 0u128);
- std::hint::black_box(s);
+ test::black_box(s);
});
}
#[bench]
fn write_u64_max(bh: &mut Bencher) {
bh.iter(|| {
- std::hint::black_box(format!("{}", u64::MAX));
+ test::black_box(format!("{}", u64::MAX));
});
}
#[bench]
fn write_u64_min(bh: &mut Bencher) {
bh.iter(|| {
- std::hint::black_box(format!("{}", 0u64));
+ test::black_box(format!("{}", 0u64));
});
}
#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Debug for dyn Any {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("Any")
+ f.debug_struct("Any").finish_non_exhaustive()
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Debug for dyn Any + Send {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("Any")
+ f.debug_struct("Any").finish_non_exhaustive()
}
}
#[stable(feature = "any_send_sync_methods", since = "1.28.0")]
impl fmt::Debug for dyn Any + Send + Sync {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("Any")
+ f.debug_struct("Any").finish_non_exhaustive()
}
}
pub use iter::IntoIter;
/// Converts a reference to `T` into a reference to an array of length 1 (without copying).
-#[unstable(feature = "array_from_ref", issue = "77101")]
+#[stable(feature = "array_from_ref", since = "1.53.0")]
pub fn from_ref<T>(s: &T) -> &[T; 1] {
// SAFETY: Converting `&T` to `&[T; 1]` is sound.
unsafe { &*(s as *const T).cast::<[T; 1]>() }
}
/// Converts a mutable reference to `T` into a mutable reference to an array of length 1 (without copying).
-#[unstable(feature = "array_from_ref", issue = "77101")]
+#[stable(feature = "array_from_ref", since = "1.53.0")]
pub fn from_mut<T>(s: &mut T) -> &mut [T; 1] {
// SAFETY: Converting `&mut T` to `&mut [T; 1]` is sound.
unsafe { &mut *(s as *mut T).cast::<[T; 1]>() }
}
}
+// Note: the `#[rustc_skip_array_during_method_dispatch]` on `trait IntoIterator`
+// hides this implementation from explicit `.into_iter()` calls on editions < 2021,
+// so those calls will still resolve to the slice implementation, by reference.
+#[cfg(not(bootstrap))]
+#[stable(feature = "array_into_iter_impl", since = "1.53.0")]
+impl<T, const N: usize> IntoIterator for [T; N] {
+ type Item = T;
+ type IntoIter = IntoIter<T, N>;
+
+ /// Creates a consuming iterator, that is, one that moves each value out of
+ /// the array (from start to end). The array cannot be used after calling
+ /// this unless `T` implements `Copy`, so the whole array is copied.
+ ///
+ /// Arrays have special behavior when calling `.into_iter()` prior to the
+ /// 2021 edition -- see the [array] Editions section for more information.
+ ///
+ /// [array]: prim@array
+ fn into_iter(self) -> Self::IntoIter {
+ IntoIter::new(self)
+ }
+}
+
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T, const N: usize> IntoIterator for &'a [T; N] {
type Item = &'a T;
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for EscapeDefault {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("EscapeDefault { .. }")
+ f.debug_struct("EscapeDefault").finish_non_exhaustive()
}
}
//
// This should never be implemented by hand.
#[doc(hidden)]
+ #[cfg_attr(not(bootstrap), feature(no_coverage))]
+ #[cfg_attr(not(bootstrap), no_coverage)]
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
fn assert_receiver_is_total_eq(&self) {}
/// # Examples
///
/// ```
- /// #![feature(ordering_helpers)]
/// use std::cmp::Ordering;
///
/// assert_eq!(Ordering::Less.is_eq(), false);
/// ```
#[inline]
#[must_use]
- #[unstable(feature = "ordering_helpers", issue = "79885")]
+ #[rustc_const_stable(feature = "ordering_helpers", since = "1.53.0")]
+ #[stable(feature = "ordering_helpers", since = "1.53.0")]
pub const fn is_eq(self) -> bool {
matches!(self, Equal)
}
/// # Examples
///
/// ```
- /// #![feature(ordering_helpers)]
/// use std::cmp::Ordering;
///
/// assert_eq!(Ordering::Less.is_ne(), true);
/// ```
#[inline]
#[must_use]
- #[unstable(feature = "ordering_helpers", issue = "79885")]
+ #[rustc_const_stable(feature = "ordering_helpers", since = "1.53.0")]
+ #[stable(feature = "ordering_helpers", since = "1.53.0")]
pub const fn is_ne(self) -> bool {
!matches!(self, Equal)
}
/// # Examples
///
/// ```
- /// #![feature(ordering_helpers)]
/// use std::cmp::Ordering;
///
/// assert_eq!(Ordering::Less.is_lt(), true);
/// ```
#[inline]
#[must_use]
- #[unstable(feature = "ordering_helpers", issue = "79885")]
+ #[rustc_const_stable(feature = "ordering_helpers", since = "1.53.0")]
+ #[stable(feature = "ordering_helpers", since = "1.53.0")]
pub const fn is_lt(self) -> bool {
matches!(self, Less)
}
/// # Examples
///
/// ```
- /// #![feature(ordering_helpers)]
/// use std::cmp::Ordering;
///
/// assert_eq!(Ordering::Less.is_gt(), false);
/// ```
#[inline]
#[must_use]
- #[unstable(feature = "ordering_helpers", issue = "79885")]
+ #[rustc_const_stable(feature = "ordering_helpers", since = "1.53.0")]
+ #[stable(feature = "ordering_helpers", since = "1.53.0")]
pub const fn is_gt(self) -> bool {
matches!(self, Greater)
}
/// # Examples
///
/// ```
- /// #![feature(ordering_helpers)]
/// use std::cmp::Ordering;
///
/// assert_eq!(Ordering::Less.is_le(), true);
/// ```
#[inline]
#[must_use]
- #[unstable(feature = "ordering_helpers", issue = "79885")]
+ #[rustc_const_stable(feature = "ordering_helpers", since = "1.53.0")]
+ #[stable(feature = "ordering_helpers", since = "1.53.0")]
pub const fn is_le(self) -> bool {
!matches!(self, Greater)
}
/// # Examples
///
/// ```
- /// #![feature(ordering_helpers)]
/// use std::cmp::Ordering;
///
/// assert_eq!(Ordering::Less.is_ge(), false);
/// ```
#[inline]
#[must_use]
- #[unstable(feature = "ordering_helpers", issue = "79885")]
+ #[rustc_const_stable(feature = "ordering_helpers", since = "1.53.0")]
+ #[stable(feature = "ordering_helpers", since = "1.53.0")]
pub const fn is_ge(self) -> bool {
!matches!(self, Less)
}
macro_rules! impl_from {
($Small: ty, $Large: ty, #[$attr:meta], $doc: expr) => {
#[$attr]
- #[doc = $doc]
impl From<$Small> for $Large {
+ // Rustdocs on the impl block show a "[+] show undocumented items" toggle.
+ // Rustdocs on functions do not.
+ #[doc = $doc]
#[inline]
fn from(small: $Small) -> Self {
small as Self
macro_rules! nzint_impl_from {
($Small: ty, $Large: ty, #[$attr:meta], $doc: expr) => {
#[$attr]
- #[doc = $doc]
impl From<$Small> for $Large {
+ // Rustdocs on the impl block show a "[+] show undocumented items" toggle.
+ // Rustdocs on functions do not.
+ #[doc = $doc]
#[inline]
fn from(small: $Small) -> Self {
// SAFETY: input type guarantees the value is non-zero
macro_rules! nzint_impl_try_from_int {
($Int: ty, $NonZeroInt: ty, #[$attr:meta], $doc: expr) => {
#[$attr]
- #[doc = $doc]
impl TryFrom<$Int> for $NonZeroInt {
type Error = TryFromIntError;
+ // Rustdocs on the impl block show a "[+] show undocumented items" toggle.
+ // Rustdocs on functions do not.
+ #[doc = $doc]
#[inline]
fn try_from(value: $Int) -> Result<Self, Self::Error> {
Self::new(value).ok_or(TryFromIntError(()))
macro_rules! nzint_impl_try_from_nzint {
($From:ty => $To:ty, $doc: expr) => {
#[stable(feature = "nzint_try_from_nzint_conv", since = "1.49.0")]
- #[doc = $doc]
impl TryFrom<$From> for $To {
type Error = TryFromIntError;
+ // Rustdocs on the impl block show a "[+] show undocumented items" toggle.
+ // Rustdocs on functions do not.
+ #[doc = $doc]
#[inline]
fn try_from(value: $From) -> Result<Self, Self::Error> {
TryFrom::try_from(value.get()).map(|v| {
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for c_void {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("c_void")
+ f.debug_struct("c_void").finish()
}
}
#[stable(feature = "debug_non_exhaustive", since = "1.53.0")]
pub fn finish_non_exhaustive(&mut self) -> fmt::Result {
self.result = self.result.and_then(|_| {
- // Draw non-exhaustive dots (`..`), and open brace if necessary (no fields).
- if self.is_pretty() {
- if !self.has_fields {
- self.fmt.write_str(" {\n")?;
- }
- let mut slot = None;
- let mut state = Default::default();
- let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot, &mut state);
- writer.write_str("..\n")?;
- } else {
- if self.has_fields {
- self.fmt.write_str(", ..")?;
+ if self.has_fields {
+ if self.is_pretty() {
+ let mut slot = None;
+ let mut state = Default::default();
+ let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot, &mut state);
+ writer.write_str("..\n")?;
+ self.fmt.write_str("}")
} else {
- self.fmt.write_str(" { ..")?;
+ self.fmt.write_str(", .. }")
}
- }
- if self.is_pretty() {
- self.fmt.write_str("}")?
} else {
- self.fmt.write_str(" }")?;
+ self.fmt.write_str(" { .. }")
}
- Ok(())
});
self.result
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Debug for PhantomData<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
- f.pad("PhantomData")
+ f.debug_struct("PhantomData").finish()
}
}
#[stable(feature = "core_impl_debug", since = "1.9.0")]
impl<T: ?Sized> Debug for UnsafeCell<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
- f.pad("UnsafeCell")
+ f.debug_struct("UnsafeCell").finish_non_exhaustive()
}
}
/// Feeds a slice of this type into the given [`Hasher`].
///
+ /// This method is meant as a convenience, but its implementation is
+ /// also explicitly left unspecified. It isn't guaranteed to be
+ /// equivalent to repeated calls of [`hash`] and implementations of
+ /// [`Hash`] should keep that in mind and call [`hash`] themselves
+ /// if the slice isn't treated as a whole unit in the [`PartialEq`]
+ /// implementation.
+ ///
+ /// For example, a [`VecDeque`] implementation might naïvely call
+ /// [`as_slices`] and then [`hash_slice`] on each slice, but this
+ /// is wrong since the two slices can change with a call to
+ /// [`make_contiguous`] without affecting the [`PartialEq`]
+ /// result. Since these slices aren't treated as singular
+ /// units, and instead part of a larger deque, this method cannot
+ /// be used.
+ ///
/// # Examples
///
/// ```
/// Hash::hash_slice(&numbers, &mut hasher);
/// println!("Hash is {:x}!", hasher.finish());
/// ```
+ ///
+ /// [`VecDeque`]: ../../std/collections/struct.VecDeque.html
+ /// [`as_slices`]: ../../std/collections/struct.VecDeque.html#method.as_slices
+ /// [`make_contiguous`]: ../../std/collections/struct.VecDeque.html#method.make_contiguous
+ /// [`hash`]: Hash::hash
+ /// [`hash_slice`]: Hash::hash_slice
#[stable(feature = "hash_slice", since = "1.3.0")]
fn hash_slice<H: Hasher>(data: &[Self], state: &mut H)
where
/// instance (with [`write`] and [`write_u8`] etc.). Most of the time, `Hasher`
/// instances are used in conjunction with the [`Hash`] trait.
///
+/// This trait makes no assumptions about how the various `write_*` methods are
+/// defined and implementations of [`Hash`] should not assume that they work one
+/// way or another. You cannot assume, for example, that a [`write_u32`] call is
+/// equivalent to four calls of [`write_u8`].
+///
/// # Examples
///
/// ```
/// [`finish`]: Hasher::finish
/// [`write`]: Hasher::write
/// [`write_u8`]: Hasher::write_u8
+/// [`write_u32`]: Hasher::write_u32
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Hasher {
/// Returns the hash value for the values written so far.
#[stable(since = "1.9.0", feature = "core_impl_debug")]
impl<H> fmt::Debug for BuildHasherDefault<H> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("BuildHasherDefault")
+ f.debug_struct("BuildHasherDefault").finish()
}
}
/// [`std::convert::identity`]: crate::convert::identity
#[cfg_attr(not(miri), inline)]
#[cfg_attr(miri, inline(never))]
-#[unstable(feature = "test", issue = "50297")]
+#[unstable(feature = "bench_black_box", issue = "64102")]
#[cfg_attr(miri, allow(unused_mut))]
pub fn black_box<T>(mut dummy: T) -> T {
// We need to "use" the argument in some way LLVM can't introspect, and on
use crate::cmp;
-use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedLen};
+use crate::iter::{
+ adapters::zip::try_get_unchecked, adapters::SourceIter, FusedIterator, InPlaceIterable,
+ TrustedLen, TrustedRandomAccess,
+};
use crate::ops::{ControlFlow, Try};
/// An iterator that only iterates over the first `n` iterations of `iter`.
self.try_fold(init, ok(fold)).unwrap()
}
+
+ unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> <I as Iterator>::Item
+ where
+ Self: TrustedRandomAccess,
+ {
+ // SAFETY: the caller must uphold the contract for
+ // `Iterator::__iterator_get_unchecked`.
+ unsafe { try_get_unchecked(&mut self.iter, idx) }
+ }
}
#[unstable(issue = "none", feature = "inplace_iteration")]
#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<I: TrustedLen> TrustedLen for Take<I> {}
+
+#[doc(hidden)]
+#[unstable(feature = "trusted_random_access", issue = "none")]
+unsafe impl<I> TrustedRandomAccess for Take<I>
+where
+ I: TrustedRandomAccess,
+{
+ const MAY_HAVE_SIDE_EFFECT: bool = I::MAY_HAVE_SIDE_EFFECT;
+}
#[stable(feature = "core_impl_debug", since = "1.9.0")]
impl<T> fmt::Debug for Empty<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("Empty")
+ f.debug_struct("Empty").finish()
}
}
/// }
/// ```
#[rustc_diagnostic_item = "IntoIterator"]
+#[cfg_attr(not(bootstrap), rustc_skip_array_during_method_dispatch)]
#[stable(feature = "rust1", since = "1.0.0")]
pub trait IntoIterator {
/// The type of the elements being iterated over.
/// [`Some(T)`] again. `fuse()` adapts an iterator, ensuring that after a
/// [`None`] is given, it will always return [`None`] forever.
///
+ /// Note that the [`Fuse`] wrapper is a no-op on iterators that implement
+ /// the [`FusedIterator`] trait. `fuse()` may therefore behave incorrectly
+ /// if the [`FusedIterator`] trait is improperly implemented.
+ ///
/// [`Some(T)`]: Some
+ /// [`FusedIterator`]: crate::iter::FusedIterator
///
/// # Examples
///
/// Basic usage:
///
/// ```
- /// let a = [1, 2, 3];
- ///
- /// let iter = a.iter();
+ /// let mut words = vec!["hello", "world", "of", "Rust"].into_iter();
///
- /// let sum: i32 = iter.take(5).fold(0, |acc, i| acc + i);
+ /// // Take the first two words.
+ /// let hello_world: Vec<_> = words.by_ref().take(2).collect();
+ /// assert_eq!(hello_world, vec!["hello", "world"]);
///
- /// assert_eq!(sum, 6);
- ///
- /// // if we try to use iter again, it won't work. The following line
- /// // gives "error: use of moved value: `iter`
- /// // assert_eq!(iter.next(), None);
- ///
- /// // let's try that again
- /// let a = [1, 2, 3];
- ///
- /// let mut iter = a.iter();
- ///
- /// // instead, we add in a .by_ref()
- /// let sum: i32 = iter.by_ref().take(2).fold(0, |acc, i| acc + i);
- ///
- /// assert_eq!(sum, 3);
- ///
- /// // now this is just fine:
- /// assert_eq!(iter.next(), Some(&3));
- /// assert_eq!(iter.next(), None);
+ /// // Collect the rest of the words.
+ /// // We can only do this because we used `by_ref` earlier.
+ /// let of_rust: Vec<_> = words.collect();
+ /// assert_eq!(of_rust, vec!["of", "Rust"]);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
fn by_ref(&mut self) -> &mut Self
#![feature(const_refs_to_cell)]
#![feature(const_panic)]
#![feature(const_pin)]
-#![feature(const_fn)]
+#![cfg_attr(bootstrap, feature(const_fn))]
#![feature(const_fn_union)]
#![feature(const_impl_trait)]
#![feature(const_fn_floating_point_arithmetic)]
#![feature(const_fn_fn_ptr_basics)]
+#![cfg_attr(not(bootstrap), feature(const_fn_trait_bound))]
#![feature(const_option)]
#![feature(const_precise_live_drops)]
#![feature(const_ptr_offset)]
#![feature(stmt_expr_attributes)]
#![feature(str_split_as_str)]
#![feature(str_split_inclusive_as_str)]
+#![feature(char_indices_offset)]
#![feature(trait_alias)]
#![feature(transparent_unions)]
#![feature(try_blocks)]
/// ```
///
/// [`RefCell`]: crate::cell::RefCell
-#[doc(alias = "delete")]
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn drop<T>(_x: T) {}
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
#[doc(alias = "popcount")]
#[doc(alias = "popcnt")]
- #[inline]
+ #[inline(always)]
pub const fn count_ones(self) -> u32 { (self as $UnsignedT).count_ones() }
/// Returns the number of zeros in the binary representation of `self`.
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
- #[inline]
+ #[inline(always)]
pub const fn count_zeros(self) -> u32 {
(!self).count_ones()
}
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
- #[inline]
+ #[inline(always)]
pub const fn leading_zeros(self) -> u32 {
(self as $UnsignedT).leading_zeros()
}
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
- #[inline]
+ #[inline(always)]
pub const fn trailing_zeros(self) -> u32 {
(self as $UnsignedT).trailing_zeros()
}
/// ```
#[stable(feature = "leading_trailing_ones", since = "1.46.0")]
#[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")]
- #[inline]
+ #[inline(always)]
pub const fn leading_ones(self) -> u32 {
(self as $UnsignedT).leading_ones()
}
/// ```
#[stable(feature = "leading_trailing_ones", since = "1.46.0")]
#[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")]
- #[inline]
+ #[inline(always)]
pub const fn trailing_ones(self) -> u32 {
(self as $UnsignedT).trailing_ones()
}
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn rotate_left(self, n: u32) -> Self {
(self as $UnsignedT).rotate_left(n) as Self
}
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn rotate_right(self, n: u32) -> Self {
(self as $UnsignedT).rotate_right(n) as Self
}
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
- #[inline]
+ #[inline(always)]
pub const fn swap_bytes(self) -> Self {
(self as $UnsignedT).swap_bytes() as Self
}
#[doc = concat!("assert_eq!(0, 0", stringify!($SelfT), ".reverse_bits());")]
/// ```
#[stable(feature = "reverse_bits", since = "1.37.0")]
- #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
- #[inline]
+ #[rustc_const_stable(feature = "const_int_methods", since = "1.37.0")]
+ #[inline(always)]
#[must_use]
pub const fn reverse_bits(self) -> Self {
(self as $UnsignedT).reverse_bits() as Self
)]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub unsafe fn unchecked_add(self, rhs: Self) -> Self {
// SAFETY: the caller must uphold the safety contract for
// `unchecked_add`.
)]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub unsafe fn unchecked_sub(self, rhs: Self) -> Self {
// SAFETY: the caller must uphold the safety contract for
// `unchecked_sub`.
)]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub unsafe fn unchecked_mul(self, rhs: Self) -> Self {
// SAFETY: the caller must uphold the safety contract for
// `unchecked_mul`.
#[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn saturating_add(self, rhs: Self) -> Self {
intrinsics::saturating_add(self, rhs)
}
#[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn saturating_sub(self, rhs: Self) -> Self {
intrinsics::saturating_sub(self, rhs)
}
#[stable(feature = "saturating_neg", since = "1.45.0")]
#[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")]
- #[inline]
+ #[inline(always)]
pub const fn saturating_neg(self) -> Self {
intrinsics::saturating_sub(0, self)
}
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_add(self, rhs: Self) -> Self {
intrinsics::wrapping_add(self, rhs)
}
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_sub(self, rhs: Self) -> Self {
intrinsics::wrapping_sub(self, rhs)
}
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_mul(self, rhs: Self) -> Self {
intrinsics::wrapping_mul(self, rhs)
}
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_shl(self, rhs: u32) -> Self {
// SAFETY: the masking by the bitsize of the type ensures that we do not shift
// out of bounds
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_shr(self, rhs: u32) -> Self {
// SAFETY: the masking by the bitsize of the type ensures that we do not shift
// out of bounds
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) {
let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT);
(a as Self, b)
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT);
(a as Self, b)
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) {
let (a, b) = intrinsics::mul_with_overflow(self as $ActualT, rhs as $ActualT);
(a as Self, b)
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_int_sign", since = "1.47.0")]
- #[inline]
+ #[inline(always)]
pub const fn signum(self) -> Self {
match self {
n if n > 0 => 1,
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
- #[inline]
+ #[inline(always)]
pub const fn is_positive(self) -> bool { self > 0 }
/// Returns `true` if `self` is negative and `false` if the number is zero or
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
- #[inline]
+ #[inline(always)]
pub const fn is_negative(self) -> bool { self < 0 }
/// Return the memory representation of this integer as a byte array in
}
macro_rules! nonzero_integers {
- ( $( #[$stability: meta] $Ty: ident($Int: ty); )+ ) => {
+ ( $( #[$stability: meta] #[$const_new_unchecked_stability: meta] $Ty: ident($Int: ty); )+ ) => {
$(
/// An integer that is known not to equal zero.
///
///
/// The value must not be zero.
#[$stability]
- #[rustc_const_stable(feature = "nonzero", since = "1.34.0")]
+ #[$const_new_unchecked_stability]
#[inline]
pub const unsafe fn new_unchecked(n: $Int) -> Self {
// SAFETY: this is guaranteed to be safe by the caller.
}
nonzero_integers! {
- #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU8(u8);
- #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU16(u16);
- #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU32(u32);
- #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU64(u64);
- #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU128(u128);
- #[stable(feature = "nonzero", since = "1.28.0")] NonZeroUsize(usize);
- #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI8(i8);
- #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI16(i16);
- #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI32(i32);
- #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI64(i64);
- #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI128(i128);
- #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroIsize(isize);
+ #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU8(u8);
+ #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU16(u16);
+ #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU32(u32);
+ #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU64(u64);
+ #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU128(u128);
+ #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroUsize(usize);
+ #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI8(i8);
+ #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI16(i16);
+ #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI32(i32);
+ #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI64(i64);
+ #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI128(i128);
+ #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroIsize(isize);
}
macro_rules! from_str_radix_nzint_impl {
#[rustc_const_stable(feature = "const_math", since = "1.32.0")]
#[doc(alias = "popcount")]
#[doc(alias = "popcnt")]
- #[inline]
+ #[inline(always)]
pub const fn count_ones(self) -> u32 {
intrinsics::ctpop(self as $ActualT) as u32
}
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_math", since = "1.32.0")]
- #[inline]
+ #[inline(always)]
pub const fn count_zeros(self) -> u32 {
(!self).count_ones()
}
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_math", since = "1.32.0")]
- #[inline]
+ #[inline(always)]
pub const fn leading_zeros(self) -> u32 {
intrinsics::ctlz(self as $ActualT) as u32
}
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_math", since = "1.32.0")]
- #[inline]
+ #[inline(always)]
pub const fn trailing_zeros(self) -> u32 {
intrinsics::cttz(self) as u32
}
/// ```
#[stable(feature = "leading_trailing_ones", since = "1.46.0")]
#[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")]
- #[inline]
+ #[inline(always)]
pub const fn leading_ones(self) -> u32 {
(!self).leading_zeros()
}
/// ```
#[stable(feature = "leading_trailing_ones", since = "1.46.0")]
#[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")]
- #[inline]
+ #[inline(always)]
pub const fn trailing_ones(self) -> u32 {
(!self).trailing_zeros()
}
#[rustc_const_stable(feature = "const_math", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn rotate_left(self, n: u32) -> Self {
intrinsics::rotate_left(self, n as $SelfT)
}
#[rustc_const_stable(feature = "const_math", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn rotate_right(self, n: u32) -> Self {
intrinsics::rotate_right(self, n as $SelfT)
}
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_math", since = "1.32.0")]
- #[inline]
+ #[inline(always)]
pub const fn swap_bytes(self) -> Self {
intrinsics::bswap(self as $ActualT) as Self
}
#[doc = concat!("assert_eq!(0, 0", stringify!($SelfT), ".reverse_bits());")]
/// ```
#[stable(feature = "reverse_bits", since = "1.37.0")]
- #[rustc_const_stable(feature = "const_math", since = "1.32.0")]
- #[inline]
+ #[rustc_const_stable(feature = "const_math", since = "1.37.0")]
+ #[inline(always)]
#[must_use]
pub const fn reverse_bits(self) -> Self {
intrinsics::bitreverse(self as $ActualT) as Self
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_math", since = "1.32.0")]
- #[inline]
+ #[inline(always)]
pub const fn from_be(x: Self) -> Self {
#[cfg(target_endian = "big")]
{
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_math", since = "1.32.0")]
- #[inline]
+ #[inline(always)]
pub const fn from_le(x: Self) -> Self {
#[cfg(target_endian = "little")]
{
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_math", since = "1.32.0")]
- #[inline]
+ #[inline(always)]
pub const fn to_be(self) -> Self { // or not to be?
#[cfg(target_endian = "big")]
{
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_math", since = "1.32.0")]
- #[inline]
+ #[inline(always)]
pub const fn to_le(self) -> Self {
#[cfg(target_endian = "little")]
{
)]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub unsafe fn unchecked_add(self, rhs: Self) -> Self {
// SAFETY: the caller must uphold the safety contract for
// `unchecked_add`.
)]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub unsafe fn unchecked_sub(self, rhs: Self) -> Self {
// SAFETY: the caller must uphold the safety contract for
// `unchecked_sub`.
)]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub unsafe fn unchecked_mul(self, rhs: Self) -> Self {
// SAFETY: the caller must uphold the safety contract for
// `unchecked_mul`.
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")]
- #[inline]
+ #[inline(always)]
pub const fn saturating_add(self, rhs: Self) -> Self {
intrinsics::saturating_add(self, rhs)
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")]
- #[inline]
+ #[inline(always)]
pub const fn saturating_sub(self, rhs: Self) -> Self {
intrinsics::saturating_sub(self, rhs)
}
#[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_add(self, rhs: Self) -> Self {
intrinsics::wrapping_add(self, rhs)
}
#[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_sub(self, rhs: Self) -> Self {
intrinsics::wrapping_sub(self, rhs)
}
#[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_mul(self, rhs: Self) -> Self {
intrinsics::wrapping_mul(self, rhs)
}
#[rustc_const_stable(feature = "const_wrapping_int_methods", since = "1.52.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_div(self, rhs: Self) -> Self {
self / rhs
}
#[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_div_euclid(self, rhs: Self) -> Self {
self / rhs
}
#[rustc_const_stable(feature = "const_wrapping_int_methods", since = "1.52.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_rem(self, rhs: Self) -> Self {
self % rhs
}
#[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_rem_euclid(self, rhs: Self) -> Self {
self % rhs
}
#[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_shl(self, rhs: u32) -> Self {
// SAFETY: the masking by the bitsize of the type ensures that we do not shift
// out of bounds
#[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_shr(self, rhs: u32) -> Self {
// SAFETY: the masking by the bitsize of the type ensures that we do not shift
// out of bounds
#[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) {
let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT);
(a as Self, b)
#[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT);
(a as Self, b)
#[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) {
let (a, b) = intrinsics::mul_with_overflow(self as $ActualT, rhs as $ActualT);
(a as Self, b)
/// ```
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div(2), (2, false));")]
/// ```
- #[inline]
+ #[inline(always)]
#[stable(feature = "wrapping", since = "1.7.0")]
#[rustc_const_stable(feature = "const_overflowing_int_methods", since = "1.52.0")]
#[must_use = "this returns the result of the operation, \
/// ```
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div_euclid(2), (2, false));")]
/// ```
- #[inline]
+ #[inline(always)]
#[stable(feature = "euclidean_division", since = "1.38.0")]
#[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")]
#[must_use = "this returns the result of the operation, \
/// ```
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem(2), (1, false));")]
/// ```
- #[inline]
+ #[inline(always)]
#[stable(feature = "wrapping", since = "1.7.0")]
#[rustc_const_stable(feature = "const_overflowing_int_methods", since = "1.52.0")]
#[must_use = "this returns the result of the operation, \
/// ```
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem_euclid(2), (1, false));")]
/// ```
- #[inline]
+ #[inline(always)]
#[stable(feature = "euclidean_division", since = "1.38.0")]
#[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")]
#[must_use = "this returns the result of the operation, \
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".overflowing_neg(), (0, false));")]
#[doc = concat!("assert_eq!(2", stringify!($SelfT), ".overflowing_neg(), (-2i32 as ", stringify!($SelfT), ", true));")]
/// ```
- #[inline]
+ #[inline(always)]
#[stable(feature = "wrapping", since = "1.7.0")]
#[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
pub const fn overflowing_neg(self) -> (Self, bool) {
#[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn overflowing_shl(self, rhs: u32) -> (Self, bool) {
(self.wrapping_shl(rhs), (rhs > ($BITS - 1)))
}
#[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) {
(self.wrapping_shr(rhs), (rhs > ($BITS - 1)))
}
#[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
#[rustc_inherit_overflow_checks]
pub const fn div_euclid(self, rhs: Self) -> Self {
self / rhs
#[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
- #[inline]
+ #[inline(always)]
#[rustc_inherit_overflow_checks]
pub const fn rem_euclid(self, rhs: Self) -> Self {
self % rhs
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_is_power_of_two", since = "1.32.0")]
- #[inline]
+ #[inline(always)]
pub const fn is_power_of_two(self) -> bool {
self.count_ones() == 1
}
-use crate::ops::Try;
+use crate::convert;
+use crate::ops::{self, Try};
/// Used to tell an operation whether it should exit early or go on as usual.
///
}
}
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<B, C> ops::TryV2 for ControlFlow<B, C> {
+ type Output = C;
+ type Residual = ControlFlow<B, convert::Infallible>;
+
+ #[inline]
+ fn from_output(output: Self::Output) -> Self {
+ ControlFlow::Continue(output)
+ }
+
+ #[inline]
+ fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
+ match self {
+ ControlFlow::Continue(c) => ControlFlow::Continue(c),
+ ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)),
+ }
+ }
+}
+
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<B, C> ops::FromResidual for ControlFlow<B, C> {
+ #[inline]
+ fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
+ match residual {
+ ControlFlow::Break(b) => ControlFlow::Break(b),
+ }
+ }
+}
+
impl<B, C> ControlFlow<B, C> {
/// Returns `true` if this is a `Break` variant.
///
type Output: ?Sized;
/// Performs the indexing (`container[index]`) operation.
+ ///
+ /// # Panics
+ ///
+ /// May panic if the index is out of bounds.
#[stable(feature = "rust1", since = "1.0.0")]
#[track_caller]
fn index(&self, index: Idx) -> &Self::Output;
#[doc(alias = "[]")]
pub trait IndexMut<Idx: ?Sized>: Index<Idx> {
/// Performs the mutable indexing (`container[index]`) operation.
+ ///
+ /// # Panics
+ ///
+ /// May panic if the index is out of bounds.
#[stable(feature = "rust1", since = "1.0.0")]
#[track_caller]
fn index_mut(&mut self, index: Idx) -> &mut Self::Output;
mod index;
mod range;
mod r#try;
+mod try_trait;
mod unsize;
#[stable(feature = "rust1", since = "1.0.0")]
#[unstable(feature = "try_trait", issue = "42327")]
pub use self::r#try::Try;
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+pub use self::try_trait::FromResidual;
+
+#[unstable(feature = "try_trait_transition", reason = "for bootstrap", issue = "none")]
+pub use self::try_trait::Try as TryV2;
+
#[unstable(feature = "generator_trait", issue = "43122")]
pub use self::generator::{Generator, GeneratorState};
--- /dev/null
+use crate::ops::ControlFlow;
+
+/// The `?` operator and `try {}` blocks.
+///
+/// `try_*` methods typically involve a type implementing this trait. For
+/// example, the closures passed to [`Iterator::try_fold`] and
+/// [`Iterator::try_for_each`] must return such a type.
+///
+/// `Try` types are typically those containing two or more categories of values,
+/// some subset of which are so commonly handled via early returns that it's
+/// worth providing a terse (but still visible) syntax to make that easy.
+///
+/// This is most often seen for error handling with [`Result`] and [`Option`].
+/// The quintessential implementation of this trait is on [`ControlFlow`].
+///
+/// # Using `Try` in Generic Code
+///
+/// `Iterator::try_fold` was stabilized to call back in Rust 1.27, but
+/// this trait is much newer. To illustrate the various associated types and
+/// methods, let's implement our own version.
+///
+/// As a reminder, an infallible version of a fold looks something like this:
+/// ```
+/// fn simple_fold<A, T>(
+/// iter: impl Iterator<Item = T>,
+/// mut accum: A,
+/// mut f: impl FnMut(A, T) -> A,
+/// ) -> A {
+/// for x in iter {
+/// accum = f(accum, x);
+/// }
+/// accum
+/// }
+/// ```
+///
+/// So instead of `f` returning just an `A`, we'll need it to return some other
+/// type that produces an `A` in the "don't short circuit" path. Conveniently,
+/// that's also the type we need to return from the function.
+///
+/// Let's add a new generic parameter `R` for that type, and bound it to the
+/// output type that we want:
+/// ```
+/// # #![feature(try_trait_v2)]
+/// # #![feature(try_trait_transition)]
+/// # use std::ops::TryV2 as Try;
+/// fn simple_try_fold_1<A, T, R: Try<Output = A>>(
+/// iter: impl Iterator<Item = T>,
+/// mut accum: A,
+/// mut f: impl FnMut(A, T) -> R,
+/// ) -> R {
+/// todo!()
+/// }
+/// ```
+///
+/// If we get through the entire iterator, we need to wrap up the accumulator
+/// into the return type using [`Try::from_output`]:
+/// ```
+/// # #![feature(try_trait_v2)]
+/// # #![feature(try_trait_transition)]
+/// # #![feature(control_flow_enum)]
+/// # use std::ops::{ControlFlow, TryV2 as Try};
+/// fn simple_try_fold_2<A, T, R: Try<Output = A>>(
+/// iter: impl Iterator<Item = T>,
+/// mut accum: A,
+/// mut f: impl FnMut(A, T) -> R,
+/// ) -> R {
+/// for x in iter {
+/// let cf = f(accum, x).branch();
+/// match cf {
+/// ControlFlow::Continue(a) => accum = a,
+/// ControlFlow::Break(_) => todo!(),
+/// }
+/// }
+/// R::from_output(accum)
+/// }
+/// ```
+///
+/// We'll also need [`FromResidual::from_residual`] to turn the residual back
+/// into the original type. But because it's a supertrait of `Try`, we don't
+/// need to mention it in the bounds. All types which implement `Try` can be
+/// recreated from their corresponding residual, so we'll just call it:
+/// ```
+/// # #![feature(try_trait_v2)]
+/// # #![feature(try_trait_transition)]
+/// # #![feature(control_flow_enum)]
+/// # use std::ops::{ControlFlow, TryV2 as Try};
+/// pub fn simple_try_fold_3<A, T, R: Try<Output = A>>(
+/// iter: impl Iterator<Item = T>,
+/// mut accum: A,
+/// mut f: impl FnMut(A, T) -> R,
+/// ) -> R {
+/// for x in iter {
+/// let cf = f(accum, x).branch();
+/// match cf {
+/// ControlFlow::Continue(a) => accum = a,
+/// ControlFlow::Break(r) => return R::from_residual(r),
+/// }
+/// }
+/// R::from_output(accum)
+/// }
+/// ```
+///
+/// But this "call `branch`, then `match` on it, and `return` if it was a
+/// `Break`" is exactly what happens inside the `?` operator. So rather than
+/// do all this manually, we can just use `?` instead:
+/// ```compile_fail (enable again once ? converts to the new trait)
+/// # #![feature(try_trait_v2)]
+/// # #![feature(try_trait_transition)]
+/// # use std::ops::TryV2 as Try;
+/// fn simple_try_fold<A, T, R: Try<Output = A>>(
+/// iter: impl Iterator<Item = T>,
+/// mut accum: A,
+/// mut f: impl FnMut(A, T) -> R,
+/// ) -> R {
+/// for x in iter {
+/// accum = f(accum, x)?;
+/// }
+/// R::from_output(accum)
+/// }
+/// ```
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+pub trait Try: FromResidual {
+ /// The type of the value produced by `?` when *not* short-circuiting.
+ #[unstable(feature = "try_trait_v2", issue = "84277")]
+ type Output;
+
+ /// The type of the value passed to [`FromResidual::from_residual`]
+ /// as part of `?` when short-circuiting.
+ ///
+ /// This represents the possible values of the `Self` type which are *not*
+ /// represented by the `Output` type.
+ ///
+ /// # Note to Implementors
+ ///
+ /// The choice of this type is critical to interconversion.
+ /// Unlike the `Output` type, which will often be a raw generic type,
+ /// this type is typically a newtype of some sort to "color" the type
+ /// so that it's distinguishable from the residuals of other types.
+ ///
+ /// This is why `Result<T, E>::Residual` is not `E`, but `Result<Infallible, E>`.
+ /// That way it's distinct from `ControlFlow<E>::Residual`, for example,
+ /// and thus `?` on `ControlFlow` cannot be used in a method returning `Result`.
+ ///
+ /// If you're making a generic type `Foo<T>` that implements `Try<Output = T>`,
+ /// then typically you can use `Foo<std::convert::Infallible>` as its `Residual`
+ /// type: that type will have a "hole" in the correct place, and will maintain the
+ /// "foo-ness" of the residual so other types need to opt-in to interconversion.
+ #[unstable(feature = "try_trait_v2", issue = "84277")]
+ type Residual;
+
+ /// Constructs the type from its `Output` type.
+ ///
+ /// This should be implemented consistently with the `branch` method
+ /// such that applying the `?` operator will get back the original value:
+ /// `Try::from_output(x).branch() --> ControlFlow::Continue(x)`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(try_trait_v2)]
+ /// #![feature(control_flow_enum)]
+ /// #![feature(try_trait_transition)]
+ /// use std::ops::TryV2 as Try;
+ ///
+ /// assert_eq!(<Result<_, String> as Try>::from_output(3), Ok(3));
+ /// assert_eq!(<Option<_> as Try>::from_output(4), Some(4));
+ /// assert_eq!(
+ /// <std::ops::ControlFlow<String, _> as Try>::from_output(5),
+ /// std::ops::ControlFlow::Continue(5),
+ /// );
+ ///
+ /// # fn make_question_mark_work() -> Option<()> {
+ /// assert_eq!(Option::from_output(4)?, 4);
+ /// # None }
+ /// # make_question_mark_work();
+ ///
+ /// // This is used, for example, on the accumulator in `try_fold`:
+ /// let r = std::iter::empty().try_fold(4, |_, ()| -> Option<_> { unreachable!() });
+ /// assert_eq!(r, Some(4));
+ /// ```
+ #[unstable(feature = "try_trait_v2", issue = "84277")]
+ fn from_output(output: Self::Output) -> Self;
+
+ /// Used in `?` to decide whether the operator should produce a value
+ /// (because this returned [`ControlFlow::Continue`])
+ /// or propagate a value back to the caller
+ /// (because this returned [`ControlFlow::Break`]).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(try_trait_v2)]
+ /// #![feature(control_flow_enum)]
+ /// #![feature(try_trait_transition)]
+ /// use std::ops::{ControlFlow, TryV2 as Try};
+ ///
+ /// assert_eq!(Ok::<_, String>(3).branch(), ControlFlow::Continue(3));
+ /// assert_eq!(Err::<String, _>(3).branch(), ControlFlow::Break(Err(3)));
+ ///
+ /// assert_eq!(Some(3).branch(), ControlFlow::Continue(3));
+ /// assert_eq!(None::<String>.branch(), ControlFlow::Break(None));
+ ///
+ /// assert_eq!(ControlFlow::<String, _>::Continue(3).branch(), ControlFlow::Continue(3));
+ /// assert_eq!(
+ /// ControlFlow::<_, String>::Break(3).branch(),
+ /// ControlFlow::Break(ControlFlow::Break(3)),
+ /// );
+ /// ```
+ #[unstable(feature = "try_trait_v2", issue = "84277")]
+ fn branch(self) -> ControlFlow<Self::Residual, Self::Output>;
+}
+
+/// Used to specify which residuals can be converted into which [`Try`] types.
+///
+/// Every `Try` type needs to be recreatable from its own associated
+/// `Residual` type, but can also have additional `FromResidual` implementations
+/// to support interconversion with other `Try` types.
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+pub trait FromResidual<R = <Self as Try>::Residual> {
+ /// Constructs the type from a compatible `Residual` type.
+ ///
+ /// This should be implemented consistently with the `branch` method such
+ /// that applying the `?` operator will get back an equivalent residual:
+ /// `FromResidual::from_residual(r).branch() --> ControlFlow::Break(r)`.
+ /// (It may not be an *identical* residual when interconversion is involved.)
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(try_trait_v2)]
+ /// #![feature(control_flow_enum)]
+ /// use std::ops::{ControlFlow, FromResidual};
+ ///
+ /// assert_eq!(Result::<String, i64>::from_residual(Err(3_u8)), Err(3));
+ /// assert_eq!(Option::<String>::from_residual(None), None);
+ /// assert_eq!(
+ /// ControlFlow::<_, String>::from_residual(ControlFlow::Break(5)),
+ /// ControlFlow::Break(5),
+ /// );
+ /// ```
+ #[unstable(feature = "try_trait_v2", issue = "84277")]
+ fn from_residual(residual: R) -> Self;
+}
use crate::iter::{FromIterator, FusedIterator, TrustedLen};
use crate::pin::Pin;
use crate::{
- hint, mem,
- ops::{self, Deref, DerefMut},
+ convert, hint, mem,
+ ops::{self, ControlFlow, Deref, DerefMut},
};
/// The `Option` type. See [the module level documentation](self) for more.
}
}
- /// Applies a function to the contained value (if any),
- /// or returns the provided default (if not).
+ /// Returns the provided default result (if none),
+ /// or applies a function to the contained value (if any).
///
/// Arguments passed to `map_or` are eagerly evaluated; if you are passing
/// the result of a function call, it is recommended to use [`map_or_else`],
}
}
- /// Applies a function to the contained value (if any),
- /// or computes a default (if not).
+ /// Computes a default function result (if none), or
+ /// applies a different function to the contained value (if any).
///
/// # Examples
///
}
}
- /// Inserts `value` into the option then returns a mutable reference to it.
- ///
- /// If the option already contains a value, the old value is dropped.
- ///
- /// # Example
- ///
- /// ```
- /// let mut opt = None;
- /// let val = opt.insert(1);
- /// assert_eq!(*val, 1);
- /// assert_eq!(opt.unwrap(), 1);
- /// let val = opt.insert(2);
- /// assert_eq!(*val, 2);
- /// *val = 3;
- /// assert_eq!(opt.unwrap(), 3);
- /// ```
- #[inline]
- #[stable(feature = "option_insert", since = "1.53.0")]
- 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() },
- }
- }
-
/////////////////////////////////////////////////////////////////////////
// Iterator constructors
/////////////////////////////////////////////////////////////////////////
}
/////////////////////////////////////////////////////////////////////////
- // Entry-like operations to insert if None and return a reference
+ // Entry-like operations to insert a value and return a reference
/////////////////////////////////////////////////////////////////////////
+ /// Inserts `value` into the option then returns a mutable reference to it.
+ ///
+ /// If the option already contains a value, the old value is dropped.
+ ///
+ /// See also [`Option::get_or_insert`], which doesn't update the value if
+ /// the option already contains [`Some`].
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// let mut opt = None;
+ /// let val = opt.insert(1);
+ /// assert_eq!(*val, 1);
+ /// assert_eq!(opt.unwrap(), 1);
+ /// let val = opt.insert(2);
+ /// assert_eq!(*val, 2);
+ /// *val = 3;
+ /// assert_eq!(opt.unwrap(), 3);
+ /// ```
+ #[inline]
+ #[stable(feature = "option_insert", since = "1.53.0")]
+ 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() },
+ }
+ }
+
/// Inserts `value` into the option if it is [`None`], then
/// returns a mutable reference to the contained value.
///
+ /// See also [`Option::insert`], which updates the value even if
+ /// the option already contains [`Some`].
+ ///
/// # Examples
///
/// ```
}
}
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<T> ops::TryV2 for Option<T> {
+ type Output = T;
+ type Residual = Option<convert::Infallible>;
+
+ #[inline]
+ fn from_output(output: Self::Output) -> Self {
+ Some(output)
+ }
+
+ #[inline]
+ fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
+ match self {
+ Some(v) => ControlFlow::Continue(v),
+ None => ControlFlow::Break(None),
+ }
+ }
+}
+
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<T> ops::FromResidual for Option<T> {
+ #[inline]
+ fn from_residual(residual: Option<convert::Infallible>) -> Self {
+ match residual {
+ None => None,
+ }
+ }
+}
+
impl<T> Option<Option<T>> {
/// Converts from `Option<Option<T>>` to `Option<T>`
///
#[stable(feature = "rust1", since = "1.0.0")]
#[must_use = "returns a new pointer rather than modifying its argument"]
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
- #[inline]
+ #[inline(always)]
pub const unsafe fn offset(self, count: isize) -> *const T
where
T: Sized,
#[stable(feature = "ptr_wrapping_offset", since = "1.16.0")]
#[must_use = "returns a new pointer rather than modifying its argument"]
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_offset(self, count: isize) -> *const T
where
T: Sized,
#[stable(feature = "pointer_methods", since = "1.26.0")]
#[must_use = "returns a new pointer rather than modifying its argument"]
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
- #[inline]
+ #[inline(always)]
pub const unsafe fn add(self, count: usize) -> Self
where
T: Sized,
#[stable(feature = "pointer_methods", since = "1.26.0")]
#[must_use = "returns a new pointer rather than modifying its argument"]
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_add(self, count: usize) -> Self
where
T: Sized,
#[stable(feature = "rust1", since = "1.0.0")]
#[must_use = "returns a new pointer rather than modifying its argument"]
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
- #[inline]
+ #[inline(always)]
pub const unsafe fn offset(self, count: isize) -> *mut T
where
T: Sized,
#[stable(feature = "ptr_wrapping_offset", since = "1.16.0")]
#[must_use = "returns a new pointer rather than modifying its argument"]
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_offset(self, count: isize) -> *mut T
where
T: Sized,
#[stable(feature = "pointer_methods", since = "1.26.0")]
#[must_use = "returns a new pointer rather than modifying its argument"]
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
- #[inline]
+ #[inline(always)]
pub const unsafe fn add(self, count: usize) -> Self
where
T: Sized,
#[stable(feature = "pointer_methods", since = "1.26.0")]
#[must_use = "returns a new pointer rather than modifying its argument"]
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_add(self, count: usize) -> Self
where
T: Sized,
#![allow(missing_docs)]
#![unstable(feature = "raw", issue = "27751")]
+#![rustc_deprecated(
+ since = "1.53.0",
+ reason = "use pointer metadata APIs instead https://github.com/rust-lang/rust/issues/81513"
+)]
//! Contains struct definitions for the layout of compiler built-in types.
//!
#![stable(feature = "rust1", since = "1.0.0")]
use crate::iter::{self, FromIterator, FusedIterator, TrustedLen};
-use crate::ops::{self, Deref, DerefMut};
+use crate::ops::{self, ControlFlow, Deref, DerefMut};
use crate::{convert, fmt, hint};
/// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]).
}
}
- /// Applies a function to the contained value (if [`Ok`]),
- /// or returns the provided default (if [`Err`]).
+ /// Returns the provided default (if [`Err`]), or
+ /// applies a function to the contained value (if [`Ok`]),
///
/// Arguments passed to `map_or` are eagerly evaluated; if you are passing
/// the result of a function call, it is recommended to use [`map_or_else`],
}
}
- /// Maps a `Result<T, E>` to `U` by applying a function to a
- /// contained [`Ok`] value, or a fallback function to a
- /// contained [`Err`] value.
+ /// Maps a `Result<T, E>` to `U` by applying a fallback function to a
+ /// contained [`Err`] value, or a default function to a
+ /// contained [`Ok`] value.
///
/// This function can be used to unpack a successful result
/// while handling an error.
Err(v)
}
}
+
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<T, E> ops::TryV2 for Result<T, E> {
+ type Output = T;
+ type Residual = Result<convert::Infallible, E>;
+
+ #[inline]
+ fn from_output(output: Self::Output) -> Self {
+ Ok(output)
+ }
+
+ #[inline]
+ fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
+ match self {
+ Ok(v) => ControlFlow::Continue(v),
+ Err(e) => ControlFlow::Break(Err(e)),
+ }
+ }
+}
+
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<T, E, F: From<E>> ops::FromResidual<Result<convert::Infallible, E>> for Result<T, F> {
+ #[inline]
+ fn from_residual(residual: Result<convert::Infallible, E>) -> Self {
+ match residual {
+ Err(e) => Err(From::from(e)),
+ }
+ }
+}
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
impl<'a> fmt::Debug for EscapeAscii<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("EscapeAscii { .. }")
+ f.debug_struct("EscapeAscii").finish_non_exhaustive()
}
}
impl Sealed for ops::RangeInclusive<usize> {}
#[stable(feature = "slice_get_slice", since = "1.28.0")]
impl Sealed for ops::RangeToInclusive<usize> {}
+ #[stable(feature = "slice_index_with_ops_bound_pair", since = "1.53.0")]
+ impl Sealed for (ops::Bound<usize>, ops::Bound<usize>) {}
}
/// A helper trait used for indexing operations.
ops::Range { start, end }
}
+
+/// Convert pair of `ops::Bound`s into `ops::Range` without performing any bounds checking and (in debug) overflow checking
+fn into_range_unchecked(
+ len: usize,
+ (start, end): (ops::Bound<usize>, ops::Bound<usize>),
+) -> ops::Range<usize> {
+ use ops::Bound;
+ let start = match start {
+ Bound::Included(i) => i,
+ Bound::Excluded(i) => i + 1,
+ Bound::Unbounded => 0,
+ };
+ let end = match end {
+ Bound::Included(i) => i + 1,
+ Bound::Excluded(i) => i,
+ Bound::Unbounded => len,
+ };
+ start..end
+}
+
+/// Convert pair of `ops::Bound`s into `ops::Range`.
+/// Returns `None` on overflowing indices.
+fn into_range(
+ len: usize,
+ (start, end): (ops::Bound<usize>, ops::Bound<usize>),
+) -> Option<ops::Range<usize>> {
+ use ops::Bound;
+ let start = match start {
+ Bound::Included(start) => start,
+ Bound::Excluded(start) => start.checked_add(1)?,
+ Bound::Unbounded => 0,
+ };
+
+ let end = match end {
+ Bound::Included(end) => end.checked_add(1)?,
+ Bound::Excluded(end) => end,
+ Bound::Unbounded => len,
+ };
+
+ // Don't bother with checking `start < end` and `end <= len`
+ // since these checks are handled by `Range` impls
+
+ Some(start..end)
+}
+
+/// Convert pair of `ops::Bound`s into `ops::Range`.
+/// Panics on overflowing indices.
+fn into_slice_range(
+ len: usize,
+ (start, end): (ops::Bound<usize>, ops::Bound<usize>),
+) -> ops::Range<usize> {
+ use ops::Bound;
+ let start = match start {
+ Bound::Included(start) => start,
+ Bound::Excluded(start) => {
+ start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail())
+ }
+ Bound::Unbounded => 0,
+ };
+
+ let end = match end {
+ Bound::Included(end) => {
+ end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail())
+ }
+ Bound::Excluded(end) => end,
+ Bound::Unbounded => len,
+ };
+
+ // Don't bother with checking `start < end` and `end <= len`
+ // since these checks are handled by `Range` impls
+
+ start..end
+}
+
+#[stable(feature = "slice_index_with_ops_bound_pair", since = "1.53.0")]
+unsafe impl<T> SliceIndex<[T]> for (ops::Bound<usize>, ops::Bound<usize>) {
+ type Output = [T];
+
+ #[inline]
+ fn get(self, slice: &[T]) -> Option<&Self::Output> {
+ into_range(slice.len(), self)?.get(slice)
+ }
+
+ #[inline]
+ fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> {
+ into_range(slice.len(), self)?.get_mut(slice)
+ }
+
+ #[inline]
+ unsafe fn get_unchecked(self, slice: *const [T]) -> *const Self::Output {
+ // SAFETY: the caller has to uphold the safety contract for `get_unchecked`.
+ unsafe { into_range_unchecked(slice.len(), self).get_unchecked(slice) }
+ }
+
+ #[inline]
+ unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut Self::Output {
+ // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`.
+ unsafe { into_range_unchecked(slice.len(), self).get_unchecked_mut(slice) }
+ }
+
+ #[inline]
+ fn index(self, slice: &[T]) -> &Self::Output {
+ into_slice_range(slice.len(), self).index(slice)
+ }
+
+ #[inline]
+ fn index_mut(self, slice: &mut [T]) -> &mut Self::Output {
+ into_slice_range(slice.len(), self).index_mut(slice)
+ }
+}
pub fn as_str(&self) -> &'a str {
self.iter.as_str()
}
+
+ /// Returns the byte position of the next character, or the length
+ /// of the underlying string if there are no more characters.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(char_indices_offset)]
+ /// let mut chars = "a楽".char_indices();
+ ///
+ /// assert_eq!(chars.offset(), 0);
+ /// assert_eq!(chars.next(), Some((0, 'a')));
+ ///
+ /// assert_eq!(chars.offset(), 1);
+ /// assert_eq!(chars.next(), Some((1, '楽')));
+ ///
+ /// assert_eq!(chars.offset(), 4);
+ /// assert_eq!(chars.next(), None);
+ /// ```
+ #[inline]
+ #[unstable(feature = "char_indices_offset", issue = "83871")]
+ pub fn offset(&self) -> usize {
+ self.front_offset
+ }
}
/// An iterator over the bytes of a string slice.
#[stable(feature = "collection_debug", since = "1.17.0")]
impl fmt::Debug for EncodeUtf16<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("EncodeUtf16 { .. }")
+ f.debug_struct("EncodeUtf16").finish_non_exhaustive()
}
}
#![stable(feature = "futures_api", since = "1.36.0")]
-use crate::ops::Try;
+use crate::convert;
+use crate::ops::{self, ControlFlow, Try};
use crate::result::Result;
/// Indicates whether a value is available or if the current task has been
}
}
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<T, E> ops::TryV2 for Poll<Result<T, E>> {
+ type Output = Poll<T>;
+ type Residual = Result<convert::Infallible, E>;
+
+ #[inline]
+ fn from_output(c: Self::Output) -> Self {
+ c.map(Ok)
+ }
+
+ #[inline]
+ fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
+ match self {
+ Poll::Ready(Ok(x)) => ControlFlow::Continue(Poll::Ready(x)),
+ Poll::Ready(Err(e)) => ControlFlow::Break(Err(e)),
+ Poll::Pending => ControlFlow::Continue(Poll::Pending),
+ }
+ }
+}
+
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<T, E, F: From<E>> ops::FromResidual<Result<convert::Infallible, E>> for Poll<Result<T, F>> {
+ #[inline]
+ fn from_residual(x: Result<convert::Infallible, E>) -> Self {
+ match x {
+ Err(e) => Poll::Ready(Err(From::from(e))),
+ }
+ }
+}
+
#[stable(feature = "futures_api", since = "1.36.0")]
impl<T, E> Try for Poll<Option<Result<T, E>>> {
type Ok = Poll<Option<T>>;
x.map(|x| x.map(Ok))
}
}
+
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<T, E> ops::TryV2 for Poll<Option<Result<T, E>>> {
+ type Output = Poll<Option<T>>;
+ type Residual = Result<convert::Infallible, E>;
+
+ #[inline]
+ fn from_output(c: Self::Output) -> Self {
+ c.map(|x| x.map(Ok))
+ }
+
+ #[inline]
+ fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
+ match self {
+ Poll::Ready(Some(Ok(x))) => ControlFlow::Continue(Poll::Ready(Some(x))),
+ Poll::Ready(Some(Err(e))) => ControlFlow::Break(Err(e)),
+ Poll::Ready(None) => ControlFlow::Continue(Poll::Ready(None)),
+ Poll::Pending => ControlFlow::Continue(Poll::Pending),
+ }
+ }
+}
+
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<T, E, F: From<E>> ops::FromResidual<Result<convert::Infallible, E>>
+ for Poll<Option<Result<T, F>>>
+{
+ #[inline]
+ fn from_residual(x: Result<convert::Infallible, E>) -> Self {
+ match x {
+ Err(e) => Poll::Ready(Some(Err(From::from(e)))),
+ }
+ }
+}
/// The maximum duration.
///
- /// It is roughly equal to a duration of 584,942,417,355 years.
+ /// May vary by platform as necessary. Must be able to contain the difference between
+ /// two instances of [`Instant`] or two instances of [`SystemTime`].
+ /// This constraint gives it a value of about 584,942,417,355 years in practice,
+ /// which is currently used on all platforms.
///
/// # Examples
///
/// ```
- /// #![feature(duration_constants)]
/// use std::time::Duration;
///
/// assert_eq!(Duration::MAX, Duration::new(u64::MAX, 1_000_000_000 - 1));
/// ```
- #[unstable(feature = "duration_constants", issue = "57391")]
+ /// [`Instant`]: ../../std/time/struct.Instant.html
+ /// [`SystemTime`]: ../../std/time/struct.SystemTime.html
+ #[stable(feature = "duration_saturating_ops", since = "1.53.0")]
pub const MAX: Duration = Duration::new(u64::MAX, NANOS_PER_SEC - 1);
/// Creates a new `Duration` from the specified number of whole seconds and
}
assert_eq!("Foo { .. }", format!("{:?}", Foo));
- assert_eq!(
- "Foo {
- ..
-}",
- format!("{:#?}", Foo)
- );
+ assert_eq!("Foo { .. }", format!("{:#?}", Foo));
}
#[test]
#![feature(alloc_layout_extra)]
#![feature(array_chunks)]
-#![feature(array_from_ref)]
#![feature(array_methods)]
#![feature(array_map)]
#![feature(array_windows)]
assert_eq!(1, unsafe { transmute_copy(&1) });
}
+// Remove this test when `std::raw` is removed.
+// The replacement pointer metadata APIs are tested in library/core/tests/ptr.rs
+#[allow(deprecated)]
#[test]
fn test_transmute() {
trait Foo {
}
)*) => {$(
mod $case_name {
+ #[allow(unused_imports)]
+ use core::ops::Bound;
+
#[test]
fn pass() {
let mut v = $data;
bad: data[7..=6];
message: "out of range";
}
+
+ in mod boundpair_len {
+ data: [0, 1, 2, 3, 4, 5];
+
+ good: data[(Bound::Included(6), Bound::Unbounded)] == [];
+ good: data[(Bound::Unbounded, Bound::Included(5))] == [0, 1, 2, 3, 4, 5];
+ good: data[(Bound::Unbounded, Bound::Excluded(6))] == [0, 1, 2, 3, 4, 5];
+ good: data[(Bound::Included(0), Bound::Included(5))] == [0, 1, 2, 3, 4, 5];
+ good: data[(Bound::Included(0), Bound::Excluded(6))] == [0, 1, 2, 3, 4, 5];
+ good: data[(Bound::Included(2), Bound::Excluded(4))] == [2, 3];
+ good: data[(Bound::Excluded(1), Bound::Included(4))] == [2, 3, 4];
+ good: data[(Bound::Excluded(5), Bound::Excluded(6))] == [];
+ good: data[(Bound::Included(6), Bound::Excluded(6))] == [];
+ good: data[(Bound::Excluded(5), Bound::Included(5))] == [];
+ good: data[(Bound::Included(6), Bound::Included(5))] == [];
+ bad: data[(Bound::Unbounded, Bound::Included(6))];
+ message: "out of range";
+ }
}
panic_cases! {
bad: data[4..=2];
message: "but ends at";
}
+
+ in mod boundpair_neg_width {
+ data: [0, 1, 2, 3, 4, 5];
+
+ good: data[(Bound::Included(4), Bound::Excluded(4))] == [];
+ bad: data[(Bound::Included(4), Bound::Excluded(3))];
+ message: "but ends at";
+ }
}
panic_cases! {
bad: data[..= usize::MAX];
message: "maximum usize";
}
+
+ in mod boundpair_overflow_end {
+ data: [0; 1];
+
+ bad: data[(Bound::Unbounded, Bound::Included(usize::MAX))];
+ message: "maximum usize";
+ }
+
+ in mod boundpair_overflow_start {
+ data: [0; 1];
+
+ bad: data[(Bound::Excluded(usize::MAX), Bound::Unbounded)];
+ message: "maximum usize";
+ }
} // panic_cases!
}
F: FnMut(&K, &mut V) -> bool,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("DrainFilter { .. }")
+ f.debug_struct("DrainFilter").finish_non_exhaustive()
}
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for RandomState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("RandomState { .. }")
+ f.debug_struct("RandomState").finish_non_exhaustive()
}
}
F: FnMut(&K) -> bool,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("DrainFilter { .. }")
+ f.debug_struct("DrainFilter").finish_non_exhaustive()
}
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for Vars {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("Vars { .. }")
+ f.debug_struct("Vars").finish_non_exhaustive()
}
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for VarsOs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("VarsOs { .. }")
+ f.debug_struct("VarOs").finish_non_exhaustive()
}
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for SplitPaths<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("SplitPaths { .. }")
+ f.debug_struct("SplitPaths").finish_non_exhaustive()
}
}
/// passed as-is.
///
/// On glibc Linux systems, arguments are retrieved by placing a function in `.init_array`.
-/// Glibc passes `argc`, `argv`, and `envp` to functions in `.init_array`, as a non-standard
+/// glibc passes `argc`, `argv`, and `envp` to functions in `.init_array`, as a non-standard
/// extension. This allows `std::env::args` to work even in a `cdylib` or `staticlib`, as it
/// does on macOS and Windows.
///
/// # Panics
///
/// The returned iterator will panic during iteration if any argument to the
-/// process is not valid unicode. If this is not desired,
+/// process is not valid Unicode. If this is not desired,
/// use the [`args_os`] function instead.
///
/// # Examples
Args { inner: args_os() }
}
-/// Returns the arguments which this program was started with (normally passed
+/// Returns the arguments that this program was started with (normally passed
/// via the command line).
///
/// The first element is traditionally the path of the executable, but it can be
-/// set to arbitrary text, and it may not even exist, so this property should
+/// set to arbitrary text, and may not even exist. This means this property should
/// not be relied upon for security purposes.
///
-/// On glibc Linux systems, arguments are retrieved by placing a function in ".init_array".
-/// Glibc passes argc, argv, and envp to functions in ".init_array", as a non-standard extension.
-/// This allows `std::env::args` to work even in a `cdylib` or `staticlib`, as it does on macOS
-/// and Windows.
+/// On Unix systems the shell usually expands unquoted arguments with glob patterns
+/// (such as `*` and `?`). On Windows this is not done, and such arguments are
+/// passed as-is.
+///
+/// On glibc Linux systems, arguments are retrieved by placing a function in `.init_array`.
+/// glibc passes `argc`, `argv`, and `envp` to functions in `.init_array`, as a non-standard
+/// extension. This allows `std::env::args_os` to work even in a `cdylib` or `staticlib`, as it
+/// does on macOS and Windows.
+///
+/// Note that the returned iterator will not check if the arguments to the
+/// process are valid Unicode. If you want to panic on invalid UTF-8,
+/// use the [`args`] function instead.
///
/// # Examples
///
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for Args {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Args").field("inner", &self.inner.inner.inner_debug()).finish()
+ f.debug_struct("Args").field("inner", &self.inner.inner).finish()
}
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for ArgsOs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("ArgsOs").field("inner", &self.inner.inner_debug()).finish()
+ f.debug_struct("ArgsOs").field("inner", &self.inner).finish()
}
}
match (a.created(), b.created()) {
(Ok(t1), Ok(t2)) => assert!(t1 <= t2),
(Err(e1), Err(e2))
- if e1.kind() == ErrorKind::Other && e2.kind() == ErrorKind::Other => {}
+ if e1.kind() == ErrorKind::Other && e2.kind() == ErrorKind::Other
+ || e1.kind() == ErrorKind::Unsupported
+ && e2.kind() == ErrorKind::Unsupported => {}
(a, b) => {
panic!("creation time must be always supported or not supported: {:?} {:?}", a, b,)
}
/// read.
#[stable(feature = "read_exact", since = "1.6.0")]
UnexpectedEof,
+
+ /// This operation is unsupported on this platform.
+ ///
+ /// This means that the operation can never succeed.
+ #[stable(feature = "unsupported_error", since = "1.53.0")]
+ Unsupported,
}
impl ErrorKind {
ErrorKind::Interrupted => "operation interrupted",
ErrorKind::Other => "other os error",
ErrorKind::UnexpectedEof => "unexpected end of file",
+ ErrorKind::Unsupported => "unsupported",
}
}
}
const DEFAULT_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
+pub(crate) fn cleanup() {
+ stdio::cleanup()
+}
+
struct Guard<'a> {
buf: &'a mut Vec<u8>,
len: usize,
use crate::sync::atomic::{AtomicBool, Ordering};
use crate::sync::{Arc, Mutex, MutexGuard};
use crate::sys::stdio;
-use crate::sys_common;
use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
type LocalStream = Arc<Mutex<Vec<u8>>>;
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for Stdin {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("Stdin { .. }")
+ f.debug_struct("Stdin").finish_non_exhaustive()
}
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for StdinLock<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("StdinLock { .. }")
+ f.debug_struct("StdinLock").finish_non_exhaustive()
}
}
inner: ReentrantMutexGuard<'a, RefCell<LineWriter<StdoutRaw>>>,
}
+static STDOUT: SyncOnceCell<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> = SyncOnceCell::new();
+
/// Constructs a new handle to the standard output of the current process.
///
/// Each handle returned is a reference to a shared global buffer whose access
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn stdout() -> Stdout {
- static INSTANCE: SyncOnceCell<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> =
- SyncOnceCell::new();
-
- fn cleanup() {
- if let Some(instance) = INSTANCE.get() {
- // Flush the data and disable buffering during shutdown
- // by replacing the line writer by one with zero
- // buffering capacity.
- // We use try_lock() instead of lock(), because someone
- // might have leaked a StdoutLock, which would
- // otherwise cause a deadlock here.
- if let Some(lock) = Pin::static_ref(instance).try_lock() {
- *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw());
- }
- }
- }
-
Stdout {
- inner: Pin::static_ref(&INSTANCE).get_or_init_pin(
- || unsafe {
- let _ = sys_common::at_exit(cleanup);
- ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw())))
- },
+ inner: Pin::static_ref(&STDOUT).get_or_init_pin(
+ || unsafe { ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw()))) },
|mutex| unsafe { mutex.init() },
),
}
}
+pub fn cleanup() {
+ if let Some(instance) = STDOUT.get() {
+ // Flush the data and disable buffering during shutdown
+ // by replacing the line writer by one with zero
+ // buffering capacity.
+ // We use try_lock() instead of lock(), because someone
+ // might have leaked a StdoutLock, which would
+ // otherwise cause a deadlock here.
+ if let Some(lock) = Pin::static_ref(instance).try_lock() {
+ *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw());
+ }
+ }
+}
+
impl Stdout {
/// Locks this handle to the standard output stream, returning a writable
/// guard.
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for Stdout {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("Stdout { .. }")
+ f.debug_struct("Stdout").finish_non_exhaustive()
}
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for StdoutLock<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("StdoutLock { .. }")
+ f.debug_struct("StdoutLock").finish_non_exhaustive()
}
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for Stderr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("Stderr { .. }")
+ f.debug_struct("Stderr").finish_non_exhaustive()
}
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for StderrLock<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("StderrLock { .. }")
+ f.debug_struct("StderrLock").finish_non_exhaustive()
}
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for Empty {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("Empty { .. }")
+ f.debug_struct("Empty").finish_non_exhaustive()
}
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for Repeat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("Repeat { .. }")
+ f.debug_struct("Repeat").finish_non_exhaustive()
}
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for Sink {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("Sink { .. }")
+ f.debug_struct("Sink").finish_non_exhaustive()
}
}
// std may use features in a platform-specific way
#![allow(unused_features)]
#![feature(rustc_allow_const_fn_unstable)]
-#![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count))]
+#![cfg_attr(
+ test,
+ feature(internal_output_capture, print_internals, update_panic_count, thread_local_const_init)
+)]
#![cfg_attr(
all(target_vendor = "fortanix", target_env = "sgx"),
feature(slice_index_methods, coerce_unsized, sgx_platform)
#![feature(assert_matches)]
#![feature(associated_type_bounds)]
#![feature(atomic_mut_ptr)]
+#![feature(bench_black_box)]
#![feature(box_syntax)]
#![feature(c_variadic)]
#![feature(cfg_accessible)]
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::ptr;
#[stable(feature = "rust1", since = "1.0.0")]
+#[allow(deprecated, deprecated_in_future)]
pub use core::raw;
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::result;
/// An IPv4 address representing an unspecified address: 0.0.0.0
///
+ /// This corresponds to the constant `INADDR_ANY` in other languages.
+ ///
/// # Examples
///
/// ```
/// let addr = Ipv4Addr::UNSPECIFIED;
/// assert_eq!(addr, Ipv4Addr::new(0, 0, 0, 0));
/// ```
+ #[doc(alias = "INADDR_ANY")]
#[stable(feature = "ip_constructors", since = "1.30.0")]
pub const UNSPECIFIED: Self = Ipv4Addr::new(0, 0, 0, 0);
/// - [`Copy`]
/// - [`Clone`]
/// - [`Debug`]
-/// - [`IntoIterator`] (implemented for `&[T; N]` and `&mut [T; N]`)
+/// - [`IntoIterator`] (implemented for `[T; N]`, `&[T; N]` and `&mut [T; N]`)
/// - [`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`]
/// - [`Hash`]
/// - [`AsRef`], [`AsMut`]
///
/// # Examples
///
-/// ```
+#[cfg_attr(bootstrap, doc = "```ignore")]
+#[cfg_attr(not(bootstrap), doc = "```")]
/// let mut array: [i32; 3] = [0; 3];
///
/// array[1] = 1;
/// assert_eq!([1, 2], &array[1..]);
///
/// // This loop prints: 0 1 2
-/// for x in &array {
+/// for x in array {
/// print!("{} ", x);
/// }
/// ```
///
-/// An array itself is not iterable:
-///
-/// ```compile_fail,E0277
-/// let array: [i32; 3] = [0; 3];
-///
-/// for x in array { }
-/// // error: the trait bound `[i32; 3]: std::iter::Iterator` is not satisfied
-/// ```
-///
-/// The solution is to coerce the array to a slice by calling a slice method:
+/// You can also iterate over reference to the array's elements:
///
/// ```
-/// # let array: [i32; 3] = [0; 3];
-/// for x in array.iter() { }
-/// ```
-///
-/// You can also use the array reference's [`IntoIterator`] implementation:
+/// let array: [i32; 3] = [0; 3];
///
-/// ```
-/// # let array: [i32; 3] = [0; 3];
/// for x in &array { }
/// ```
///
/// move_away(roa);
/// ```
///
+/// # Editions
+///
+/// Prior to Rust 1.53, arrays did not implement `IntoIterator` by value, so the method call
+/// `array.into_iter()` auto-referenced into a slice iterator. That behavior is preserved in the
+/// 2015 and 2018 editions of Rust for compatability, ignoring `IntoIterator` by value.
+///
+#[cfg_attr(bootstrap, doc = "```rust,edition2018,ignore")]
+#[cfg_attr(not(bootstrap), doc = "```rust,edition2018")]
+/// # #![allow(array_into_iter)] // override our `deny(warnings)`
+/// let array: [i32; 3] = [0; 3];
+///
+/// // This creates a slice iterator, producing references to each value.
+/// for item in array.into_iter().enumerate() {
+/// let (i, x): (usize, &i32) = item;
+/// println!("array[{}] = {}", i, x);
+/// }
+///
+/// // The `array_into_iter` lint suggests this change for future compatibility:
+/// for item in array.iter().enumerate() {
+/// let (i, x): (usize, &i32) = item;
+/// println!("array[{}] = {}", i, x);
+/// }
+///
+/// // You can explicitly iterate an array by value using
+/// // `IntoIterator::into_iter` or `std::array::IntoIter::new`:
+/// for item in IntoIterator::into_iter(array).enumerate() {
+/// let (i, x): (usize, i32) = item;
+/// println!("array[{}] = {}", i, x);
+/// }
+/// ```
+///
+/// Starting in the 2021 edition, `array.into_iter()` will use `IntoIterator` normally to iterate
+/// by value, and `iter()` should be used to iterate by reference like previous editions.
+///
+/// ```rust,edition2021,ignore
+/// # // FIXME: ignored because 2021 testing is still unstable
+/// let array: [i32; 3] = [0; 3];
+///
+/// // This iterates by reference:
+/// for item in array.iter().enumerate() {
+/// let (i, x): (usize, &i32) = item;
+/// println!("array[{}] = {}", i, x);
+/// }
+///
+/// // This iterates by value:
+/// for item in array.into_iter().enumerate() {
+/// let (i, x): (usize, i32) = item;
+/// println!("array[{}] = {}", i, x);
+/// }
+/// ```
+///
/// [slice]: prim@slice
/// [`Debug`]: fmt::Debug
/// [`Hash`]: hash::Hash
///
/// Additionally, `f32` can represent some special values:
///
-/// - -0.0: IEEE 754 floating point numbers have a bit that indicates their sign, so -0.0 is a
-/// possible value. For comparison `-0.0 == +0.0` is true but floating point operations can
-/// carry the sign bit through arithmetic operations. This means `-1.0 * 0.0` produces -0.0 and
-/// a negative number rounded to a value smaller than a float can represent also produces -0.0.
+/// - −0.0: IEEE 754 floating point numbers have a bit that indicates their sign, so −0.0 is a
+/// possible value. For comparison −0.0 = +0.0, but floating point operations can carry
+/// the sign bit through arithmetic operations. This means −0.0 × +0.0 produces −0.0 and
+/// a negative number rounded to a value smaller than a float can represent also produces −0.0.
/// - [∞](#associatedconstant.INFINITY) and
/// [−∞](#associatedconstant.NEG_INFINITY): these result from calculations
/// like `1.0 / 0.0`.
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for ChildStdin {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("ChildStdin { .. }")
+ f.debug_struct("ChildStdin").finish_non_exhaustive()
}
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for ChildStdout {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("ChildStdout { .. }")
+ f.debug_struct("ChildStdout").finish_non_exhaustive()
}
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for ChildStderr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("ChildStderr { .. }")
+ f.debug_struct("ChildStderr").finish_non_exhaustive()
}
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for Stdio {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("Stdio { .. }")
+ f.debug_struct("Stdio").finish_non_exhaustive()
}
}
/// [platform-specific behavior]: #platform-specific-behavior
#[stable(feature = "rust1", since = "1.0.0")]
pub fn exit(code: i32) -> ! {
- crate::sys_common::cleanup();
+ crate::sys_common::rt::cleanup();
crate::sys::os::exit(code)
}
argv: *const *const u8,
) -> isize {
use crate::panic;
- use crate::sys;
use crate::sys_common;
- use crate::sys_common::thread_info;
- use crate::thread::Thread;
- sys::init();
+ // SAFETY: Only called once during runtime initialization.
+ unsafe { sys_common::rt::init(argc, argv) };
- unsafe {
- let main_guard = sys::thread::guard::init();
- sys::stack_overflow::init();
+ let exit_code = panic::catch_unwind(main);
- // Next, set up the current Thread with the guard information we just
- // created. Note that this isn't necessary in general for new threads,
- // but we just do this to name the main thread and to give it correct
- // info about the stack bounds.
- let thread = Thread::new(Some("main".to_owned()));
- thread_info::set(main_guard, thread);
+ sys_common::rt::cleanup();
- // Store our args if necessary in a squirreled away location
- sys::args::init(argc, argv);
-
- // Let's run some code!
- let exit_code = panic::catch_unwind(main);
-
- sys_common::cleanup();
- exit_code.unwrap_or(101) as isize
- }
+ exit_code.unwrap_or(101) as isize
}
#[cfg(not(test))]
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for Barrier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("Barrier { .. }")
+ f.debug_struct("Barrier").finish_non_exhaustive()
}
}
mod tests;
use crate::fmt;
-use crate::sync::{mutex, MutexGuard, PoisonError};
+use crate::sync::{mutex, poison, LockResult, MutexGuard, PoisonError};
use crate::sys_common::condvar as sys;
-use crate::sys_common::poison::{self, LockResult};
use crate::time::{Duration, Instant};
/// A type indicating whether a timed wait on a condition variable returned
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for Condvar {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("Condvar { .. }")
+ f.debug_struct("Condvar").finish_non_exhaustive()
}
}
#[allow(deprecated)]
pub use self::once::{Once, OnceState, ONCE_INIT};
#[stable(feature = "rust1", since = "1.0.0")]
-pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
+pub use self::poison::{LockResult, PoisonError, TryLockError, TryLockResult};
#[stable(feature = "rust1", since = "1.0.0")]
-pub use crate::sys_common::poison::{LockResult, PoisonError, TryLockError, TryLockResult};
+pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
pub mod mpsc;
mod condvar;
mod mutex;
mod once;
+mod poison;
mod rwlock;
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> fmt::Debug for SendError<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- "SendError(..)".fmt(f)
+ f.debug_struct("SendError").finish_non_exhaustive()
}
}
use crate::cell::UnsafeCell;
use crate::fmt;
-use crate::mem;
use crate::ops::{Deref, DerefMut};
-use crate::ptr;
+use crate::sync::{poison, LockResult, TryLockError, TryLockResult};
use crate::sys_common::mutex as sys;
-use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult};
/// A mutual exclusion primitive useful for protecting shared data
///
where
T: Sized,
{
- // We know statically that there are no outstanding references to
- // `self` so there's no need to lock the inner mutex.
- //
- // To get the inner value, we'd like to call `data.into_inner()`,
- // but because `Mutex` impl-s `Drop`, we can't move out of it, so
- // we'll have to destructure it manually instead.
- unsafe {
- // Like `let Mutex { inner, poison, data } = self`.
- let (inner, poison, data) = {
- let Mutex { ref inner, ref poison, ref data } = self;
- (ptr::read(inner), ptr::read(poison), ptr::read(data))
- };
- mem::forget(self);
- drop(inner);
-
- poison::map_result(poison.borrow(), |_| data.into_inner())
- }
+ let data = self.data.into_inner();
+ poison::map_result(self.poison.borrow(), |_| data)
}
/// Returns a mutable reference to the underlying data.
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for Once {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("Once { .. }")
+ f.debug_struct("Once").finish_non_exhaustive()
}
}
--- /dev/null
+use crate::error::Error;
+use crate::fmt;
+use crate::sync::atomic::{AtomicBool, Ordering};
+use crate::thread;
+
+pub struct Flag {
+ failed: AtomicBool,
+}
+
+// Note that the Ordering uses to access the `failed` field of `Flag` below is
+// always `Relaxed`, and that's because this isn't actually protecting any data,
+// it's just a flag whether we've panicked or not.
+//
+// The actual location that this matters is when a mutex is **locked** which is
+// where we have external synchronization ensuring that we see memory
+// reads/writes to this flag.
+//
+// As a result, if it matters, we should see the correct value for `failed` in
+// all cases.
+
+impl Flag {
+ pub const fn new() -> Flag {
+ Flag { failed: AtomicBool::new(false) }
+ }
+
+ #[inline]
+ pub fn borrow(&self) -> LockResult<Guard> {
+ let ret = Guard { panicking: thread::panicking() };
+ if self.get() { Err(PoisonError::new(ret)) } else { Ok(ret) }
+ }
+
+ #[inline]
+ pub fn done(&self, guard: &Guard) {
+ if !guard.panicking && thread::panicking() {
+ self.failed.store(true, Ordering::Relaxed);
+ }
+ }
+
+ #[inline]
+ pub fn get(&self) -> bool {
+ self.failed.load(Ordering::Relaxed)
+ }
+}
+
+pub struct Guard {
+ panicking: bool,
+}
+
+/// A type of error which can be returned whenever a lock is acquired.
+///
+/// Both [`Mutex`]es and [`RwLock`]s are poisoned whenever a thread fails while the lock
+/// is held. The precise semantics for when a lock is poisoned is documented on
+/// each lock, but once a lock is poisoned then all future acquisitions will
+/// return this error.
+///
+/// # Examples
+///
+/// ```
+/// use std::sync::{Arc, Mutex};
+/// use std::thread;
+///
+/// let mutex = Arc::new(Mutex::new(1));
+///
+/// // poison the mutex
+/// let c_mutex = Arc::clone(&mutex);
+/// let _ = thread::spawn(move || {
+/// let mut data = c_mutex.lock().unwrap();
+/// *data = 2;
+/// panic!();
+/// }).join();
+///
+/// match mutex.lock() {
+/// Ok(_) => unreachable!(),
+/// Err(p_err) => {
+/// let data = p_err.get_ref();
+/// println!("recovered: {}", data);
+/// }
+/// };
+/// ```
+/// [`Mutex`]: crate::sync::Mutex
+/// [`RwLock`]: crate::sync::RwLock
+#[stable(feature = "rust1", since = "1.0.0")]
+pub struct PoisonError<T> {
+ guard: T,
+}
+
+/// An enumeration of possible errors associated with a [`TryLockResult`] which
+/// can occur while trying to acquire a lock, from the [`try_lock`] method on a
+/// [`Mutex`] or the [`try_read`] and [`try_write`] methods on an [`RwLock`].
+///
+/// [`try_lock`]: crate::sync::Mutex::try_lock
+/// [`try_read`]: crate::sync::RwLock::try_read
+/// [`try_write`]: crate::sync::RwLock::try_write
+/// [`Mutex`]: crate::sync::Mutex
+/// [`RwLock`]: crate::sync::RwLock
+#[stable(feature = "rust1", since = "1.0.0")]
+pub enum TryLockError<T> {
+ /// The lock could not be acquired because another thread failed while holding
+ /// the lock.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ Poisoned(#[stable(feature = "rust1", since = "1.0.0")] PoisonError<T>),
+ /// The lock could not be acquired at this time because the operation would
+ /// otherwise block.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ WouldBlock,
+}
+
+/// A type alias for the result of a lock method which can be poisoned.
+///
+/// The [`Ok`] variant of this result indicates that the primitive was not
+/// poisoned, and the `Guard` is contained within. The [`Err`] variant indicates
+/// that the primitive was poisoned. Note that the [`Err`] variant *also* carries
+/// the associated guard, and it can be acquired through the [`into_inner`]
+/// method.
+///
+/// [`into_inner`]: PoisonError::into_inner
+#[stable(feature = "rust1", since = "1.0.0")]
+pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>;
+
+/// A type alias for the result of a nonblocking locking method.
+///
+/// For more information, see [`LockResult`]. A `TryLockResult` doesn't
+/// necessarily hold the associated guard in the [`Err`] type as the lock may not
+/// have been acquired for other reasons.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>;
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> fmt::Debug for PoisonError<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("PoisonError").finish_non_exhaustive()
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> fmt::Display for PoisonError<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ "poisoned lock: another task failed inside".fmt(f)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> Error for PoisonError<T> {
+ #[allow(deprecated)]
+ fn description(&self) -> &str {
+ "poisoned lock: another task failed inside"
+ }
+}
+
+impl<T> PoisonError<T> {
+ /// Creates a `PoisonError`.
+ ///
+ /// This is generally created by methods like [`Mutex::lock`](crate::sync::Mutex::lock)
+ /// or [`RwLock::read`](crate::sync::RwLock::read).
+ #[stable(feature = "sync_poison", since = "1.2.0")]
+ pub fn new(guard: T) -> PoisonError<T> {
+ PoisonError { guard }
+ }
+
+ /// Consumes this error indicating that a lock is poisoned, returning the
+ /// underlying guard to allow access regardless.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::HashSet;
+ /// use std::sync::{Arc, Mutex};
+ /// use std::thread;
+ ///
+ /// let mutex = Arc::new(Mutex::new(HashSet::new()));
+ ///
+ /// // poison the mutex
+ /// let c_mutex = Arc::clone(&mutex);
+ /// let _ = thread::spawn(move || {
+ /// let mut data = c_mutex.lock().unwrap();
+ /// data.insert(10);
+ /// panic!();
+ /// }).join();
+ ///
+ /// let p_err = mutex.lock().unwrap_err();
+ /// let data = p_err.into_inner();
+ /// println!("recovered {} items", data.len());
+ /// ```
+ #[stable(feature = "sync_poison", since = "1.2.0")]
+ pub fn into_inner(self) -> T {
+ self.guard
+ }
+
+ /// Reaches into this error indicating that a lock is poisoned, returning a
+ /// reference to the underlying guard to allow access regardless.
+ #[stable(feature = "sync_poison", since = "1.2.0")]
+ pub fn get_ref(&self) -> &T {
+ &self.guard
+ }
+
+ /// Reaches into this error indicating that a lock is poisoned, returning a
+ /// mutable reference to the underlying guard to allow access regardless.
+ #[stable(feature = "sync_poison", since = "1.2.0")]
+ pub fn get_mut(&mut self) -> &mut T {
+ &mut self.guard
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> From<PoisonError<T>> for TryLockError<T> {
+ fn from(err: PoisonError<T>) -> TryLockError<T> {
+ TryLockError::Poisoned(err)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> fmt::Debug for TryLockError<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ TryLockError::Poisoned(..) => "Poisoned(..)".fmt(f),
+ TryLockError::WouldBlock => "WouldBlock".fmt(f),
+ }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> fmt::Display for TryLockError<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ TryLockError::Poisoned(..) => "poisoned lock: another task failed inside",
+ TryLockError::WouldBlock => "try_lock failed because the operation would block",
+ }
+ .fmt(f)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> Error for TryLockError<T> {
+ #[allow(deprecated, deprecated_in_future)]
+ fn description(&self) -> &str {
+ match *self {
+ TryLockError::Poisoned(ref p) => p.description(),
+ TryLockError::WouldBlock => "try_lock failed because the operation would block",
+ }
+ }
+
+ #[allow(deprecated)]
+ fn cause(&self) -> Option<&dyn Error> {
+ match *self {
+ TryLockError::Poisoned(ref p) => Some(p),
+ _ => None,
+ }
+ }
+}
+
+pub fn map_result<T, U, F>(result: LockResult<T>, f: F) -> LockResult<U>
+where
+ F: FnOnce(T) -> U,
+{
+ match result {
+ Ok(t) => Ok(f(t)),
+ Err(PoisonError { guard }) => Err(PoisonError::new(f(guard))),
+ }
+}
use crate::mem;
use crate::ops::{Deref, DerefMut};
use crate::ptr;
-use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult};
+use crate::sync::{poison, LockResult, TryLockError, TryLockResult};
use crate::sys_common::rwlock as sys;
/// A reader-writer lock
use crate::ffi::OsString;
-use crate::marker::PhantomData;
+use crate::fmt;
use crate::vec;
/// One-time global initialization.
pub struct Args {
iter: vec::IntoIter<OsString>,
- _dont_send_or_sync_me: PhantomData<*mut ()>,
}
-impl Args {
- pub fn inner_debug(&self) -> &[OsString] {
- self.iter.as_slice()
+impl fmt::Debug for Args {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.iter.as_slice().fmt(f)
}
}
+impl !Send for Args {}
+impl !Sync for Args {}
+
impl Iterator for Args {
type Item = OsString;
fn next(&mut self) -> Option<OsString> {
mod imp {
use super::Args;
use crate::ffi::{CStr, OsString};
- use crate::marker::PhantomData;
use crate::ptr;
use crate::sys_common::os_str_bytes::*;
}
pub fn args() -> Args {
- Args { iter: clone().into_iter(), _dont_send_or_sync_me: PhantomData }
+ Args { iter: clone().into_iter() }
}
fn clone() -> Vec<OsString> {
#![unstable(reason = "not public", issue = "none", feature = "fd")]
-use crate::io::{self, ErrorKind, Read};
+use crate::io::{self, Read};
use crate::mem;
use crate::sys::cvt;
use crate::sys::hermit::abi;
+use crate::sys::unsupported;
use crate::sys_common::AsInner;
#[derive(Debug)]
self.duplicate_path(&[])
}
pub fn duplicate_path(&self, _path: &[u8]) -> io::Result<FileDesc> {
- Err(io::Error::new_const(ErrorKind::Other, &"duplicate isn't supported"))
+ unsupported()
}
pub fn nonblocking(&self) -> io::Result<bool> {
}
pub fn set_cloexec(&self) -> io::Result<()> {
- Err(io::Error::new_const(ErrorKind::Other, &"cloexec isn't supported"))
+ unsupported()
}
pub fn set_nonblocking(&self, _nonblocking: bool) -> io::Result<()> {
- Err(io::Error::new_const(ErrorKind::Other, &"nonblocking isn't supported"))
+ unsupported()
}
}
use crate::sys::hermit::abi::{O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY};
use crate::sys::hermit::fd::FileDesc;
use crate::sys::time::SystemTime;
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
use crate::sys_common::os_str_bytes::OsStrExt;
pub use crate::sys_common::fs::copy;
#[derive(Debug)]
pub struct File(FileDesc);
-pub struct FileAttr(Void);
+pub struct FileAttr(!);
-pub struct ReadDir(Void);
+pub struct ReadDir(!);
-pub struct DirEntry(Void);
+pub struct DirEntry(!);
#[derive(Clone, Debug)]
pub struct OpenOptions {
mode: i32,
}
-pub struct FilePermissions(Void);
+pub struct FilePermissions(!);
-pub struct FileType(Void);
+pub struct FileType(!);
#[derive(Debug)]
pub struct DirBuilder {}
impl FileAttr {
pub fn size(&self) -> u64 {
- match self.0 {}
+ self.0
}
pub fn perm(&self) -> FilePermissions {
- match self.0 {}
+ self.0
}
pub fn file_type(&self) -> FileType {
- match self.0 {}
+ self.0
}
pub fn modified(&self) -> io::Result<SystemTime> {
- match self.0 {}
+ self.0
}
pub fn accessed(&self) -> io::Result<SystemTime> {
- match self.0 {}
+ self.0
}
pub fn created(&self) -> io::Result<SystemTime> {
- match self.0 {}
+ self.0
}
}
impl Clone for FileAttr {
fn clone(&self) -> FileAttr {
- match self.0 {}
+ self.0
}
}
impl FilePermissions {
pub fn readonly(&self) -> bool {
- match self.0 {}
+ self.0
}
pub fn set_readonly(&mut self, _readonly: bool) {
- match self.0 {}
+ self.0
}
}
impl Clone for FilePermissions {
fn clone(&self) -> FilePermissions {
- match self.0 {}
+ self.0
}
}
impl PartialEq for FilePermissions {
fn eq(&self, _other: &FilePermissions) -> bool {
- match self.0 {}
+ self.0
}
}
impl fmt::Debug for FilePermissions {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.0 {}
+ self.0
}
}
impl FileType {
pub fn is_dir(&self) -> bool {
- match self.0 {}
+ self.0
}
pub fn is_file(&self) -> bool {
- match self.0 {}
+ self.0
}
pub fn is_symlink(&self) -> bool {
- match self.0 {}
+ self.0
}
}
impl Clone for FileType {
fn clone(&self) -> FileType {
- match self.0 {}
+ self.0
}
}
impl PartialEq for FileType {
fn eq(&self, _other: &FileType) -> bool {
- match self.0 {}
+ self.0
}
}
impl Hash for FileType {
fn hash<H: Hasher>(&self, _h: &mut H) {
- match self.0 {}
+ self.0
}
}
impl fmt::Debug for FileType {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.0 {}
+ self.0
}
}
impl fmt::Debug for ReadDir {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.0 {}
+ self.0
}
}
type Item = io::Result<DirEntry>;
fn next(&mut self) -> Option<io::Result<DirEntry>> {
- match self.0 {}
+ self.0
}
}
impl DirEntry {
pub fn path(&self) -> PathBuf {
- match self.0 {}
+ self.0
}
pub fn file_name(&self) -> OsString {
- match self.0 {}
+ self.0
}
pub fn metadata(&self) -> io::Result<FileAttr> {
- match self.0 {}
+ self.0
}
pub fn file_type(&self) -> io::Result<FileType> {
- match self.0 {}
+ self.0
}
}
+++ /dev/null
-use crate::mem;
-
-#[derive(Copy, Clone)]
-pub struct IoSlice<'a>(&'a [u8]);
-
-impl<'a> IoSlice<'a> {
- #[inline]
- pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
- IoSlice(buf)
- }
-
- #[inline]
- pub fn advance(&mut self, n: usize) {
- self.0 = &self.0[n..]
- }
-
- #[inline]
- pub fn as_slice(&self) -> &[u8] {
- self.0
- }
-}
-
-pub struct IoSliceMut<'a>(&'a mut [u8]);
-
-impl<'a> IoSliceMut<'a> {
- #[inline]
- pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
- IoSliceMut(buf)
- }
-
- #[inline]
- pub fn advance(&mut self, n: usize) {
- let slice = mem::replace(&mut self.0, &mut []);
- let (_, remaining) = slice.split_at_mut(n);
- self.0 = remaining;
- }
-
- #[inline]
- pub fn as_slice(&self) -> &[u8] {
- self.0
- }
-
- #[inline]
- pub fn as_mut_slice(&mut self) -> &mut [u8] {
- self.0
- }
-}
pub mod ext;
pub mod fd;
pub mod fs;
+#[path = "../unsupported/io.rs"]
pub mod io;
pub mod memchr;
pub mod mutex;
pub mod net;
pub mod os;
+#[path = "../unix/path.rs"]
pub mod path;
#[path = "../unsupported/pipe.rs"]
pub mod pipe;
#[path = "../unsupported/process.rs"]
pub mod process;
pub mod rwlock;
-pub mod stack_overflow;
pub mod stdio;
pub mod thread;
pub mod thread_local_dtor;
+#[path = "../unsupported/thread_local_key.rs"]
pub mod thread_local_key;
pub mod time;
pub fn unsupported_err() -> crate::io::Error {
crate::io::Error::new_const(
- crate::io::ErrorKind::Other,
+ crate::io::ErrorKind::Unsupported,
&"operation not supported on HermitCore yet",
)
}
-// This enum is used as the storage for a bunch of types which can't actually
-// exist.
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
-pub enum Void {}
-
pub unsafe fn strlen(start: *const c_char) -> usize {
let mut str = start;
abort_internal();
}
-#[cfg(not(test))]
-pub fn init() {
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
let _ = net::init();
+ args::init(argc, argv);
+}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {
+ args::cleanup();
}
#[cfg(not(test))]
use crate::sync::Arc;
use crate::sys::hermit::abi;
use crate::sys::hermit::abi::IpAddress::{Ipv4, Ipv6};
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
use crate::sys_common::AsInner;
use crate::time::Duration;
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
- Err(io::Error::new_const(ErrorKind::Other, &"socket_addr isn't supported"))
+ unsupported()
}
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
- Err(io::Error::new_const(ErrorKind::Other, &"take_error isn't supported"))
+ unsupported()
}
pub fn set_nonblocking(&self, mode: bool) -> io::Result<()> {
}
pub fn set_ttl(&self, _: u32) -> io::Result<()> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn ttl(&self) -> io::Result<u32> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn only_v6(&self) -> io::Result<bool> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
}
impl UdpSocket {
pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn duplicate(&self) -> io::Result<UdpSocket> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn broadcast(&self) -> io::Result<bool> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn multicast_loop_v4(&self) -> io::Result<bool> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn multicast_loop_v6(&self) -> io::Result<bool> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn set_ttl(&self, _: u32) -> io::Result<()> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn ttl(&self) -> io::Result<u32> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn send(&self, _: &[u8]) -> io::Result<usize> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
- Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+ unsupported()
}
}
}
}
-pub struct LookupHost(Void);
+pub struct LookupHost(!);
impl LookupHost {
pub fn port(&self) -> u16 {
- match self.0 {}
+ self.0
}
}
impl Iterator for LookupHost {
type Item = SocketAddr;
fn next(&mut self) -> Option<SocketAddr> {
- match self.0 {}
+ self.0
}
}
use crate::str;
use crate::sync::Mutex;
use crate::sys::hermit::abi;
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
use crate::sys_common::os_str_bytes::*;
use crate::vec;
unsupported()
}
-pub struct SplitPaths<'a>(&'a Void);
+pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);
pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
panic!("unsupported")
impl<'a> Iterator for SplitPaths<'a> {
type Item = PathBuf;
fn next(&mut self) -> Option<PathBuf> {
- match *self.0 {}
+ self.0
}
}
pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
- _dont_send_or_sync_me: PhantomData<*mut ()>,
}
+impl !Send for Env {}
+impl !Sync for Env {}
+
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
result.push((key.clone(), value.clone()));
}
- return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData };
+ return Env { iter: result.into_iter() };
}
}
+++ /dev/null
-use crate::ffi::OsStr;
-use crate::path::Prefix;
-
-#[inline]
-pub fn is_sep_byte(b: u8) -> bool {
- b == b'/'
-}
-
-#[inline]
-pub fn is_verbatim_sep(b: u8) -> bool {
- b == b'/'
-}
-
-pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> {
- None
-}
-
-pub const MAIN_SEP_STR: &str = "/";
-pub const MAIN_SEP: char = '/';
+++ /dev/null
-#[inline]
-pub unsafe fn init() {}
-
-#[inline]
-pub unsafe fn cleanup() {}
+++ /dev/null
-pub type Key = usize;
-
-#[inline]
-pub unsafe fn create(_dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key {
- panic!("should not be used on the hermit target");
-}
-
-#[inline]
-pub unsafe fn set(_key: Key, _value: *mut u8) {
- panic!("should not be used on the hermit target");
-}
-
-#[inline]
-pub unsafe fn get(_key: Key) -> *mut u8 {
- panic!("should not be used on the hermit target");
-}
-
-#[inline]
-pub unsafe fn destroy(_key: Key) {
- panic!("should not be used on the hermit target");
-}
-
-#[inline]
-pub fn requires_synchronized_create() -> bool {
- panic!("should not be used on the hermit target");
-}
mod common;
cfg_if::cfg_if! {
- if #[cfg(target_os = "vxworks")] {
- mod vxworks;
- pub use self::vxworks::*;
- } else if #[cfg(unix)] {
+ if #[cfg(unix)] {
mod unix;
pub use self::unix::*;
} else if #[cfg(windows)] {
use super::abi::usercalls::{alloc, raw::ByteBuffer};
use crate::ffi::OsString;
+use crate::fmt;
use crate::slice;
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sys::os_str::Buf;
}
}
-pub unsafe fn cleanup() {}
-
pub fn args() -> Args {
let args = unsafe { (ARGS.load(Ordering::Relaxed) as *const ArgsStore).as_ref() };
if let Some(args) = args { Args(args.iter()) } else { Args([].iter()) }
pub struct Args(slice::Iter<'static, OsString>);
-impl Args {
- pub fn inner_debug(&self) -> &[OsString] {
- self.0.as_slice()
+impl fmt::Debug for Args {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0.as_slice().fmt(f)
}
}
}
impl AsRawFd for net::TcpStream {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
*self.as_inner().as_inner().as_inner().as_inner()
}
}
impl AsRawFd for net::TcpListener {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
*self.as_inner().as_inner().as_inner().as_inner()
}
impl FromRawFd for net::TcpStream {
type Metadata = TcpStreamMetadata;
+ #[inline]
unsafe fn from_raw_fd(fd: RawFd, metadata: Self::Metadata) -> net::TcpStream {
let fd = sys::fd::FileDesc::from_inner(fd);
let socket = sys::net::Socket::from_inner((fd, metadata.local_addr));
impl FromRawFd for net::TcpListener {
type Metadata = TcpListenerMetadata;
+ #[inline]
unsafe fn from_raw_fd(fd: RawFd, metadata: Self::Metadata) -> net::TcpListener {
let fd = sys::fd::FileDesc::from_inner(fd);
let socket = sys::net::Socket::from_inner((fd, metadata.local_addr));
}
impl TryIntoRawFd for net::TcpStream {
+ #[inline]
fn try_into_raw_fd(self) -> Result<RawFd, Self> {
let (socket, peer_addr) = self.into_inner().into_inner();
match socket.try_into_inner() {
}
impl TryIntoRawFd for net::TcpListener {
+ #[inline]
fn try_into_raw_fd(self) -> Result<RawFd, Self> {
match self.into_inner().into_inner().try_into_inner() {
Ok(fd) => Ok(fd.into_inner()),
#[path = "../unsupported/process.rs"]
pub mod process;
pub mod rwlock;
-pub mod stack_overflow;
pub mod stdio;
pub mod thread;
pub mod thread_local_key;
pub use crate::sys_common::os_str_bytes as os_str;
-#[cfg(not(test))]
-pub fn init() {}
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
+ unsafe {
+ args::init(argc, argv);
+ }
+}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {}
/// This function is used to implement functionality that simply doesn't exist.
/// Programs relying on this functionality will need to deal with the error.
}
pub fn unsupported_err() -> crate::io::Error {
- crate::io::Error::new_const(ErrorKind::Other, &"operation not supported on SGX yet")
+ crate::io::Error::new_const(ErrorKind::Unsupported, &"operation not supported on SGX yet")
}
/// This function is used to implement various functions that doesn't exist,
}
}
-// This enum is used as the storage for a bunch of types which can't actually
-// exist.
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
-pub enum Void {}
-
pub unsafe fn strlen(mut s: *const c_char) -> usize {
let mut n = 0;
while unsafe { *s } != 0 {
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs};
use crate::sync::Arc;
use crate::sys::fd::FileDesc;
-use crate::sys::{sgx_ineffective, unsupported, AsInner, FromInner, IntoInner, TryIntoInner, Void};
+use crate::sys::{sgx_ineffective, unsupported, AsInner, FromInner, IntoInner, TryIntoInner};
use crate::time::Duration;
use super::abi::usercalls;
}
}
-pub struct UdpSocket(Void);
+pub struct UdpSocket(!);
impl UdpSocket {
pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
- match self.0 {}
+ self.0
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
- match self.0 {}
+ self.0
}
pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
- match self.0 {}
+ self.0
}
pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
- match self.0 {}
+ self.0
}
pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn duplicate(&self) -> io::Result<UdpSocket> {
- match self.0 {}
+ self.0
}
pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
- match self.0 {}
+ self.0
}
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
- match self.0 {}
+ self.0
}
pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn broadcast(&self) -> io::Result<bool> {
- match self.0 {}
+ self.0
}
pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn multicast_loop_v4(&self) -> io::Result<bool> {
- match self.0 {}
+ self.0
}
pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
- match self.0 {}
+ self.0
}
pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn multicast_loop_v6(&self) -> io::Result<bool> {
- match self.0 {}
+ self.0
}
pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn set_ttl(&self, _: u32) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn ttl(&self) -> io::Result<u32> {
- match self.0 {}
+ self.0
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
- match self.0 {}
+ self.0
}
pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn send(&self, _: &[u8]) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
- match self.0 {}
+ self.0
}
}
impl fmt::Debug for UdpSocket {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.0 {}
+ self.0
}
}
}
}
-pub struct LookupHost(Void);
+pub struct LookupHost(!);
impl LookupHost {
fn new(host: String) -> io::Result<LookupHost> {
}
pub fn port(&self) -> u16 {
- match self.0 {}
+ self.0
}
}
impl Iterator for LookupHost {
type Item = SocketAddr;
fn next(&mut self) -> Option<SocketAddr> {
- match self.0 {}
+ self.0
}
}
use crate::ffi::{OsStr, OsString};
use crate::fmt;
use crate::io;
+use crate::marker::PhantomData;
use crate::path::{self, PathBuf};
use crate::str;
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sync::Mutex;
use crate::sync::Once;
-use crate::sys::{decode_error_kind, sgx_ineffective, unsupported, Void};
+use crate::sys::{decode_error_kind, sgx_ineffective, unsupported};
use crate::vec;
pub fn errno() -> i32 {
sgx_ineffective(())
}
-pub struct SplitPaths<'a>(&'a Void);
+pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);
pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
panic!("unsupported")
impl<'a> Iterator for SplitPaths<'a> {
type Item = PathBuf;
fn next(&mut self) -> Option<PathBuf> {
- match *self.0 {}
+ self.0
}
}
+++ /dev/null
-#[cfg_attr(test, allow(dead_code))]
-pub unsafe fn init() {}
-
-pub unsafe fn cleanup() {}
#![allow(dead_code)] // runtime init functions not used during testing
use crate::ffi::OsString;
-use crate::marker::PhantomData;
+use crate::fmt;
use crate::vec;
/// One-time global initialization.
pub struct Args {
iter: vec::IntoIter<OsString>,
- _dont_send_or_sync_me: PhantomData<*mut ()>,
}
-impl Args {
- pub fn inner_debug(&self) -> &[OsString] {
- self.iter.as_slice()
+impl !Send for Args {}
+impl !Sync for Args {}
+
+impl fmt::Debug for Args {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.iter.as_slice().fmt(f)
}
}
mod imp {
use super::Args;
use crate::ffi::{CStr, OsString};
- use crate::marker::PhantomData;
use crate::os::unix::prelude::*;
use crate::ptr;
use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering};
}
pub fn args() -> Args {
- Args { iter: clone().into_iter(), _dont_send_or_sync_me: PhantomData }
+ Args { iter: clone().into_iter() }
}
fn clone() -> Vec<OsString> {
mod imp {
use super::Args;
use crate::ffi::CStr;
- use crate::marker::PhantomData;
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
})
.collect::<Vec<_>>()
};
- Args { iter: vec.into_iter(), _dont_send_or_sync_me: PhantomData }
+ Args { iter: vec.into_iter() }
}
// As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs
}
}
- Args { iter: res.into_iter(), _dont_send_or_sync_me: PhantomData }
+ Args { iter: res.into_iter() }
}
}
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
+
+#[cfg(target_os = "vxworks")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "vxworks";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
impl AsRawFd for RawFd {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
*self
}
}
#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
impl IntoRawFd for RawFd {
+ #[inline]
fn into_raw_fd(self) -> RawFd {
self
}
}
#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
impl FromRawFd for RawFd {
+ #[inline]
unsafe fn from_raw_fd(fd: RawFd) -> RawFd {
fd
}
#[stable(feature = "rust1", since = "1.0.0")]
impl AsRawFd for fs::File {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
self.as_inner().fd().raw()
}
}
#[stable(feature = "from_raw_os", since = "1.1.0")]
impl FromRawFd for fs::File {
+ #[inline]
unsafe fn from_raw_fd(fd: RawFd) -> fs::File {
fs::File::from_inner(sys::fs::File::from_inner(fd))
}
}
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for fs::File {
+ #[inline]
fn into_raw_fd(self) -> RawFd {
self.into_inner().into_fd().into_raw()
}
#[stable(feature = "asraw_stdio", since = "1.21.0")]
impl AsRawFd for io::Stdin {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
libc::STDIN_FILENO
}
#[stable(feature = "asraw_stdio", since = "1.21.0")]
impl AsRawFd for io::Stdout {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
libc::STDOUT_FILENO
}
#[stable(feature = "asraw_stdio", since = "1.21.0")]
impl AsRawFd for io::Stderr {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
libc::STDERR_FILENO
}
#[stable(feature = "asraw_stdio_locks", since = "1.35.0")]
impl<'a> AsRawFd for io::StdinLock<'a> {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
libc::STDIN_FILENO
}
#[stable(feature = "asraw_stdio_locks", since = "1.35.0")]
impl<'a> AsRawFd for io::StdoutLock<'a> {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
libc::STDOUT_FILENO
}
#[stable(feature = "asraw_stdio_locks", since = "1.35.0")]
impl<'a> AsRawFd for io::StderrLock<'a> {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
libc::STDERR_FILENO
}
use crate::os::redox as platform;
#[cfg(target_os = "solaris")]
use crate::os::solaris as platform;
+ #[cfg(target_os = "vxworks")]
+ use crate::os::vxworks as platform;
}
}
#[stable(feature = "unix_socket", since = "1.10.0")]
impl AsRawFd for UnixDatagram {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
*self.0.as_inner()
}
#[stable(feature = "unix_socket", since = "1.10.0")]
impl FromRawFd for UnixDatagram {
+ #[inline]
unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram {
UnixDatagram(Socket::from_inner(fd))
}
#[stable(feature = "unix_socket", since = "1.10.0")]
impl IntoRawFd for UnixDatagram {
+ #[inline]
fn into_raw_fd(self) -> RawFd {
self.0.into_inner()
}
#[stable(feature = "unix_socket", since = "1.10.0")]
impl AsRawFd for UnixListener {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
*self.0.as_inner()
}
#[stable(feature = "unix_socket", since = "1.10.0")]
impl FromRawFd for UnixListener {
+ #[inline]
unsafe fn from_raw_fd(fd: RawFd) -> UnixListener {
UnixListener(Socket::from_inner(fd))
}
#[stable(feature = "unix_socket", since = "1.10.0")]
impl IntoRawFd for UnixListener {
+ #[inline]
fn into_raw_fd(self) -> RawFd {
self.0.into_inner()
}
($($t:ident)*) => {$(
#[stable(feature = "rust1", since = "1.0.0")]
impl AsRawFd for net::$t {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
*self.as_inner().socket().as_inner()
}
($($t:ident)*) => {$(
#[stable(feature = "from_raw_os", since = "1.1.0")]
impl FromRawFd for net::$t {
+ #[inline]
unsafe fn from_raw_fd(fd: RawFd) -> net::$t {
let socket = sys::net::Socket::from_inner(fd);
net::$t::from_inner(sys_common::net::$t::from_inner(socket))
($($t:ident)*) => {$(
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for net::$t {
+ #[inline]
fn into_raw_fd(self) -> RawFd {
self.into_inner().into_socket().into_inner()
}
#[stable(feature = "unix_socket", since = "1.10.0")]
impl AsRawFd for UnixStream {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
*self.0.as_inner()
}
#[stable(feature = "unix_socket", since = "1.10.0")]
impl FromRawFd for UnixStream {
+ #[inline]
unsafe fn from_raw_fd(fd: RawFd) -> UnixStream {
UnixStream(Socket::from_inner(fd))
}
#[stable(feature = "unix_socket", since = "1.10.0")]
impl IntoRawFd for UnixStream {
+ #[inline]
fn into_raw_fd(self) -> RawFd {
self.0.into_inner()
}
#[stable(feature = "process_extensions", since = "1.2.0")]
impl FromRawFd for process::Stdio {
+ #[inline]
unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio {
let fd = sys::fd::FileDesc::new(fd);
let io = sys::process::Stdio::Fd(fd);
#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStdin {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
self.as_inner().fd().raw()
}
#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStdout {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
self.as_inner().fd().raw()
}
#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStderr {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
self.as_inner().fd().raw()
}
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStdin {
+ #[inline]
fn into_raw_fd(self) -> RawFd {
self.into_inner().into_fd().into_raw()
}
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStdout {
+ #[inline]
fn into_raw_fd(self) -> RawFd {
self.into_inner().into_fd().into_raw()
}
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStderr {
+ #[inline]
fn into_raw_fd(self) -> RawFd {
self.into_inner().into_fd().into_raw()
}
}
Err(io::Error::new_const(
- io::ErrorKind::Other,
+ io::ErrorKind::Unsupported,
&"creation time is not available on this platform \
currently",
))
macro_rules! unimpl {
() => {
- return Err(io::Error::new_const(io::ErrorKind::Other, &"No networking available on L4Re."));
+ return Err(io::Error::new_const(
+ io::ErrorKind::Unsupported,
+ &"No networking available on L4Re.",
+ ));
};
}
pub use crate::sys_common::os_str_bytes as os_str;
-#[cfg(not(test))]
-pub fn init() {
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
// The standard streams might be closed on application startup. To prevent
// std::io::{stdin, stdout,stderr} objects from using other unrelated file
// resources opened later, we reopen standards streams when they are closed.
- unsafe {
- sanitize_standard_fds();
- }
+ sanitize_standard_fds();
// By default, some platforms will send a *signal* when an EPIPE error
// would otherwise be delivered. This runtime doesn't install a SIGPIPE
//
// Hence, we set SIGPIPE to ignore when the program starts up in order
// to prevent this problem.
- unsafe {
- reset_sigpipe();
- }
+ reset_sigpipe();
+
+ stack_overflow::init();
+ args::init(argc, argv);
- cfg_if::cfg_if! {
- if #[cfg(miri)] {
- // The standard fds are always available in Miri.
- unsafe fn sanitize_standard_fds() {}
- } else if #[cfg(not(any(
- target_os = "emscripten",
- target_os = "fuchsia",
- // The poll on Darwin doesn't set POLLNVAL for closed fds.
- target_os = "macos",
- target_os = "ios",
- target_os = "redox",
- )))] {
- // In the case when all file descriptors are open, the poll has been
- // observed to perform better than fcntl (on GNU/Linux).
- unsafe fn sanitize_standard_fds() {
+ unsafe fn sanitize_standard_fds() {
+ #[cfg(not(miri))]
+ // The standard fds are always available in Miri.
+ cfg_if::cfg_if! {
+ if #[cfg(not(any(
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "vxworks",
+ // The poll on Darwin doesn't set POLLNVAL for closed fds.
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "redox",
+ )))] {
use crate::sys::os::errno;
let pfds: &mut [_] = &mut [
libc::pollfd { fd: 0, events: 0, revents: 0 },
libc::abort();
}
}
- }
- } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "redox"))] {
- unsafe fn sanitize_standard_fds() {
+ } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "redox"))] {
use crate::sys::os::errno;
for fd in 0..3 {
if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF {
}
}
}
- } else {
- unsafe fn sanitize_standard_fds() {}
}
}
- #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
unsafe fn reset_sigpipe() {
+ #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
}
- #[cfg(any(target_os = "emscripten", target_os = "fuchsia"))]
- unsafe fn reset_sigpipe() {}
+}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {
+ args::cleanup();
+ stack_overflow::cleanup();
}
#[cfg(target_os = "android")]
libc::EINVAL => ErrorKind::InvalidInput,
libc::ETIMEDOUT => ErrorKind::TimedOut,
libc::EEXIST => ErrorKind::AlreadyExists,
+ libc::ENOSYS => ErrorKind::Unsupported,
// These two constants can have the same value on some systems,
// but different values on others, so we can't use a match
use crate::fmt;
use crate::io;
use crate::iter;
-use crate::marker::PhantomData;
use crate::mem;
use crate::memchr;
use crate::path::{self, PathBuf};
use crate::str;
use crate::sys::cvt;
use crate::sys::fd;
+use crate::sys::rwlock::{RWLockReadGuard, StaticRWLock};
use crate::sys_common::mutex::{StaticMutex, StaticMutexGuard};
-use crate::sys_common::rwlock::{RWLockReadGuard, StaticRWLock};
use crate::vec;
use libc::{c_char, c_int, c_void};
unsafe { libc::errnoGet() }
}
-#[cfg(target_os = "vxworks")]
-pub fn set_errno(e: i32) {
- unsafe { libc::errnoSet(e as c_int) };
-}
-
#[cfg(target_os = "dragonfly")]
pub fn errno() -> i32 {
extern "C" {
#[cfg(any(target_os = "fuchsia", target_os = "l4re"))]
pub fn current_exe() -> io::Result<PathBuf> {
use crate::io::ErrorKind;
- Err(io::Error::new_const(ErrorKind::Other, &"Not yet implemented!"))
+ Err(io::Error::new_const(ErrorKind::Unsupported, &"Not yet implemented!"))
}
#[cfg(target_os = "vxworks")]
pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
- _dont_send_or_sync_me: PhantomData<*mut ()>,
}
+impl !Send for Env {}
+impl !Sync for Env {}
+
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
environ = environ.add(1);
}
}
- return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData };
+ return Env { iter: result.into_iter() };
}
fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
unsafe { libc::getppid() as u32 }
}
-#[cfg(target_env = "gnu")]
+#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
pub fn glibc_version() -> Option<(usize, usize)> {
if let Some(Ok(version_str)) = glibc_version_cstr().map(CStr::to_str) {
parse_glibc_version(version_str)
}
}
-#[cfg(target_env = "gnu")]
+#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
fn glibc_version_cstr() -> Option<&'static CStr> {
weak! {
fn gnu_get_libc_version() -> *const libc::c_char
// Returns Some((major, minor)) if the string is a valid "x.y" version,
// ignoring any extra dot-separated parts. Otherwise return None.
-#[cfg(target_env = "gnu")]
+#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
let mut parsed_ints = version.split('.').map(str::parse::<usize>).fuse();
match (parsed_ints.next(), parsed_ints.next()) {
pub use crate::sys_common::process::CommandEnvs;
mod process_common;
-#[cfg(not(target_os = "fuchsia"))]
-#[path = "process_unix.rs"]
-mod process_inner;
-#[cfg(target_os = "fuchsia")]
-#[path = "process_fuchsia.rs"]
-mod process_inner;
-#[cfg(target_os = "fuchsia")]
-mod zircon;
+
+cfg_if::cfg_if! {
+ if #[cfg(target_os = "fuchsia")] {
+ #[path = "process_fuchsia.rs"]
+ mod process_inner;
+ mod zircon;
+ } else if #[cfg(target_os = "vxworks")] {
+ #[path = "process_vxworks.rs"]
+ mod process_inner;
+ } else {
+ #[path = "process_unix.rs"]
+ mod process_inner;
+ }
+}
--- /dev/null
+use crate::fmt;
+use crate::io::{self, Error, ErrorKind};
+use crate::sys;
+use crate::sys::cvt;
+use crate::sys::process::process_common::*;
+use crate::sys_common::thread;
+use libc::RTP_ID;
+use libc::{self, c_char, c_int};
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+impl Command {
+ pub fn spawn(
+ &mut self,
+ default: Stdio,
+ needs_stdin: bool,
+ ) -> io::Result<(Process, StdioPipes)> {
+ use crate::sys::cvt_r;
+ let envp = self.capture_env();
+
+ if self.saw_nul() {
+ return Err(io::Error::new_const(
+ ErrorKind::InvalidInput,
+ &"nul byte found in provided data",
+ ));
+ }
+ let (ours, theirs) = self.setup_io(default, needs_stdin)?;
+ let mut p = Process { pid: 0, status: None };
+
+ unsafe {
+ macro_rules! t {
+ ($e:expr) => {
+ match $e {
+ Ok(e) => e,
+ Err(e) => return Err(e.into()),
+ }
+ };
+ }
+
+ let mut orig_stdin = libc::STDIN_FILENO;
+ let mut orig_stdout = libc::STDOUT_FILENO;
+ let mut orig_stderr = libc::STDERR_FILENO;
+
+ if let Some(fd) = theirs.stdin.fd() {
+ orig_stdin = t!(cvt_r(|| libc::dup(libc::STDIN_FILENO)));
+ t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO)));
+ }
+ if let Some(fd) = theirs.stdout.fd() {
+ orig_stdout = t!(cvt_r(|| libc::dup(libc::STDOUT_FILENO)));
+ t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO)));
+ }
+ if let Some(fd) = theirs.stderr.fd() {
+ orig_stderr = t!(cvt_r(|| libc::dup(libc::STDERR_FILENO)));
+ t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO)));
+ }
+
+ if let Some(ref cwd) = *self.get_cwd() {
+ t!(cvt(libc::chdir(cwd.as_ptr())));
+ }
+
+ // pre_exec closures are ignored on VxWorks
+ let _ = self.get_closures();
+
+ let c_envp = envp
+ .as_ref()
+ .map(|c| c.as_ptr())
+ .unwrap_or_else(|| *sys::os::environ() as *const _);
+ let stack_size = thread::min_stack();
+
+ // ensure that access to the environment is synchronized
+ let _lock = sys::os::env_read_lock();
+
+ let ret = libc::rtpSpawn(
+ self.get_program_cstr().as_ptr(),
+ self.get_argv().as_ptr() as *mut *const c_char, // argv
+ c_envp as *mut *const c_char,
+ 100 as c_int, // initial priority
+ stack_size, // initial stack size.
+ 0, // options
+ 0, // task options
+ );
+
+ // Because FileDesc was not used, each duplicated file descriptor
+ // needs to be closed manually
+ if orig_stdin != libc::STDIN_FILENO {
+ t!(cvt_r(|| libc::dup2(orig_stdin, libc::STDIN_FILENO)));
+ libc::close(orig_stdin);
+ }
+ if orig_stdout != libc::STDOUT_FILENO {
+ t!(cvt_r(|| libc::dup2(orig_stdout, libc::STDOUT_FILENO)));
+ libc::close(orig_stdout);
+ }
+ if orig_stderr != libc::STDERR_FILENO {
+ t!(cvt_r(|| libc::dup2(orig_stderr, libc::STDERR_FILENO)));
+ libc::close(orig_stderr);
+ }
+
+ if ret != libc::RTP_ID_ERROR {
+ p.pid = ret;
+ Ok((p, ours))
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ }
+ }
+
+ pub fn exec(&mut self, default: Stdio) -> io::Error {
+ let ret = Command::spawn(self, default, false);
+ match ret {
+ Ok(t) => unsafe {
+ let mut status = 0 as c_int;
+ libc::waitpid(t.0.pid, &mut status, 0);
+ libc::exit(0);
+ },
+ Err(e) => e,
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Processes
+////////////////////////////////////////////////////////////////////////////////
+
+/// The unique id of the process (this should never be negative).
+pub struct Process {
+ pid: RTP_ID,
+ status: Option<ExitStatus>,
+}
+
+impl Process {
+ pub fn id(&self) -> u32 {
+ self.pid as u32
+ }
+
+ pub fn kill(&mut self) -> io::Result<()> {
+ // If we've already waited on this process then the pid can be recycled
+ // and used for another process, and we probably shouldn't be killing
+ // random processes, so just return an error.
+ if self.status.is_some() {
+ Err(Error::new_const(
+ ErrorKind::InvalidInput,
+ &"invalid argument: can't kill an exited process",
+ ))
+ } else {
+ cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
+ }
+ }
+
+ pub fn wait(&mut self) -> io::Result<ExitStatus> {
+ use crate::sys::cvt_r;
+ if let Some(status) = self.status {
+ return Ok(status);
+ }
+ let mut status = 0 as c_int;
+ cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?;
+ self.status = Some(ExitStatus::new(status));
+ Ok(ExitStatus::new(status))
+ }
+
+ pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
+ if let Some(status) = self.status {
+ return Ok(Some(status));
+ }
+ let mut status = 0 as c_int;
+ let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?;
+ if pid == 0 {
+ Ok(None)
+ } else {
+ self.status = Some(ExitStatus::new(status));
+ Ok(Some(ExitStatus::new(status)))
+ }
+ }
+}
+
+/// Unix exit statuses
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitStatus(c_int);
+
+impl ExitStatus {
+ pub fn new(status: c_int) -> ExitStatus {
+ ExitStatus(status)
+ }
+
+ fn exited(&self) -> bool {
+ libc::WIFEXITED(self.0)
+ }
+
+ pub fn success(&self) -> bool {
+ self.code() == Some(0)
+ }
+
+ pub fn code(&self) -> Option<i32> {
+ if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None }
+ }
+
+ pub fn signal(&self) -> Option<i32> {
+ if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None }
+ }
+
+ pub fn core_dumped(&self) -> bool {
+ // This method is not yet properly implemented on VxWorks
+ false
+ }
+
+ pub fn stopped_signal(&self) -> Option<i32> {
+ if libc::WIFSTOPPED(self.0) { Some(libc::WSTOPSIG(self.0)) } else { None }
+ }
+
+ pub fn continued(&self) -> bool {
+ // This method is not yet properly implemented on VxWorks
+ false
+ }
+
+ pub fn into_raw(&self) -> c_int {
+ self.0
+ }
+}
+
+/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
+impl From<c_int> for ExitStatus {
+ fn from(a: c_int) -> ExitStatus {
+ ExitStatus(a)
+ }
+}
+
+impl fmt::Display for ExitStatus {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if let Some(code) = self.code() {
+ write!(f, "exit code: {}", code)
+ } else {
+ let signal = self.signal().unwrap();
+ write!(f, "signal: {}", signal)
+ }
+ }
+}
not(target_os = "freebsd"),
not(target_os = "netbsd"),
not(target_os = "fuchsia"),
- not(target_os = "redox")
+ not(target_os = "redox"),
+ not(target_os = "vxworks")
))]
mod imp {
use crate::fs::File;
file.read_exact(v).expect("failed to read rand:")
}
}
+
+#[cfg(target_os = "vxworks")]
+mod imp {
+ use crate::io;
+ use core::sync::atomic::{AtomicBool, Ordering::Relaxed};
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ static RNG_INIT: AtomicBool = AtomicBool::new(false);
+ while !RNG_INIT.load(Relaxed) {
+ let ret = unsafe { libc::randSecure() };
+ if ret < 0 {
+ panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
+ } else if ret > 0 {
+ RNG_INIT.store(true, Relaxed);
+ break;
+ }
+ unsafe { libc::usleep(10) };
+ }
+ let ret = unsafe {
+ libc::randABytes(v.as_mut_ptr() as *mut libc::c_uchar, v.len() as libc::c_int)
+ };
+ if ret < 0 {
+ panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
+ }
+ }
+}
}
}
}
+
+pub struct StaticRWLock(RWLock);
+
+impl StaticRWLock {
+ pub const fn new() -> StaticRWLock {
+ StaticRWLock(RWLock::new())
+ }
+
+ /// Acquires shared access to the underlying lock, blocking the current
+ /// thread to do so.
+ ///
+ /// The lock is automatically unlocked when the returned guard is dropped.
+ #[inline]
+ pub fn read_with_guard(&'static self) -> RWLockReadGuard {
+ // SAFETY: All methods require static references, therefore self
+ // cannot be moved between invocations.
+ unsafe {
+ self.0.read();
+ }
+ RWLockReadGuard(&self.0)
+ }
+
+ /// Acquires write access to the underlying lock, blocking the current thread
+ /// to do so.
+ ///
+ /// The lock is automatically unlocked when the returned guard is dropped.
+ #[inline]
+ pub fn write_with_guard(&'static self) -> RWLockWriteGuard {
+ // SAFETY: All methods require static references, therefore self
+ // cannot be moved between invocations.
+ unsafe {
+ self.0.write();
+ }
+ RWLockWriteGuard(&self.0)
+ }
+}
+
+pub struct RWLockReadGuard(&'static RWLock);
+
+impl Drop for RWLockReadGuard {
+ fn drop(&mut self) {
+ unsafe { self.0.read_unlock() }
+ }
+}
+
+pub struct RWLockWriteGuard(&'static RWLock);
+
+impl Drop for RWLockWriteGuard {
+ fn drop(&mut self) {
+ unsafe { self.0.write_unlock() }
+ }
+}
}
}
}
+
+#[cfg(target_os = "vxworks")]
+pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
+ use crate::sys_common::thread_local_dtor::register_dtor_fallback;
+ register_dtor_fallback(t, dtor);
+}
use crate::ffi::OsString;
-pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
-pub unsafe fn cleanup() {}
-
pub struct Args {}
pub fn args() -> Args {
Args {}
}
-impl Args {
- pub fn inner_debug(&self) -> &[OsString] {
- &[]
+impl fmt::Debug for Args {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_list().finish()
}
}
// spec definition?
use crate::os::raw::c_char;
-#[cfg(not(test))]
-pub fn init() {}
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {}
pub fn unsupported<T>() -> std_io::Result<T> {
Err(unsupported_err())
}
pub fn unsupported_err() -> std_io::Error {
- std_io::Error::new_const(std_io::ErrorKind::Other, &"operation not supported on this platform")
+ std_io::Error::new_const(
+ std_io::ErrorKind::Unsupported,
+ &"operation not supported on this platform",
+ )
}
pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind {
(1, 2)
}
-// This enum is used as the storage for a bunch of types which can't actually
-// exist.
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
-pub enum Void {}
-
pub unsafe fn strlen(mut s: *const c_char) -> usize {
// SAFETY: The caller must guarantee `s` points to a valid 0-terminated string.
unsafe {
use crate::io::{self, IoSlice, IoSliceMut, SeekFrom};
use crate::path::{Path, PathBuf};
use crate::sys::time::SystemTime;
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
-pub struct File(Void);
+pub struct File(!);
-pub struct FileAttr(Void);
+pub struct FileAttr(!);
-pub struct ReadDir(Void);
+pub struct ReadDir(!);
-pub struct DirEntry(Void);
+pub struct DirEntry(!);
#[derive(Clone, Debug)]
pub struct OpenOptions {}
-pub struct FilePermissions(Void);
+pub struct FilePermissions(!);
-pub struct FileType(Void);
+pub struct FileType(!);
#[derive(Debug)]
pub struct DirBuilder {}
impl FileAttr {
pub fn size(&self) -> u64 {
- match self.0 {}
+ self.0
}
pub fn perm(&self) -> FilePermissions {
- match self.0 {}
+ self.0
}
pub fn file_type(&self) -> FileType {
- match self.0 {}
+ self.0
}
pub fn modified(&self) -> io::Result<SystemTime> {
- match self.0 {}
+ self.0
}
pub fn accessed(&self) -> io::Result<SystemTime> {
- match self.0 {}
+ self.0
}
pub fn created(&self) -> io::Result<SystemTime> {
- match self.0 {}
+ self.0
}
}
impl Clone for FileAttr {
fn clone(&self) -> FileAttr {
- match self.0 {}
+ self.0
}
}
impl FilePermissions {
pub fn readonly(&self) -> bool {
- match self.0 {}
+ self.0
}
pub fn set_readonly(&mut self, _readonly: bool) {
- match self.0 {}
+ self.0
}
}
impl Clone for FilePermissions {
fn clone(&self) -> FilePermissions {
- match self.0 {}
+ self.0
}
}
impl PartialEq for FilePermissions {
fn eq(&self, _other: &FilePermissions) -> bool {
- match self.0 {}
+ self.0
}
}
impl fmt::Debug for FilePermissions {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.0 {}
+ self.0
}
}
impl FileType {
pub fn is_dir(&self) -> bool {
- match self.0 {}
+ self.0
}
pub fn is_file(&self) -> bool {
- match self.0 {}
+ self.0
}
pub fn is_symlink(&self) -> bool {
- match self.0 {}
+ self.0
}
}
impl Clone for FileType {
fn clone(&self) -> FileType {
- match self.0 {}
+ self.0
}
}
impl PartialEq for FileType {
fn eq(&self, _other: &FileType) -> bool {
- match self.0 {}
+ self.0
}
}
impl Hash for FileType {
fn hash<H: Hasher>(&self, _h: &mut H) {
- match self.0 {}
+ self.0
}
}
impl fmt::Debug for FileType {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.0 {}
+ self.0
}
}
impl fmt::Debug for ReadDir {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.0 {}
+ self.0
}
}
type Item = io::Result<DirEntry>;
fn next(&mut self) -> Option<io::Result<DirEntry>> {
- match self.0 {}
+ self.0
}
}
impl DirEntry {
pub fn path(&self) -> PathBuf {
- match self.0 {}
+ self.0
}
pub fn file_name(&self) -> OsString {
- match self.0 {}
+ self.0
}
pub fn metadata(&self) -> io::Result<FileAttr> {
- match self.0 {}
+ self.0
}
pub fn file_type(&self) -> io::Result<FileType> {
- match self.0 {}
+ self.0
}
}
}
pub fn file_attr(&self) -> io::Result<FileAttr> {
- match self.0 {}
+ self.0
}
pub fn fsync(&self) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn datasync(&self) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn truncate(&self, _size: u64) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn is_read_vectored(&self) -> bool {
- match self.0 {}
+ self.0
}
pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn is_write_vectored(&self) -> bool {
- match self.0 {}
+ self.0
}
pub fn flush(&self) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> {
- match self.0 {}
+ self.0
}
pub fn duplicate(&self) -> io::Result<File> {
- match self.0 {}
+ self.0
}
pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
- match self.0 {}
+ self.0
}
}
impl fmt::Debug for File {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.0 {}
+ self.0
}
}
pub mod pipe;
pub mod process;
pub mod rwlock;
-pub mod stack_overflow;
pub mod stdio;
pub mod thread;
#[cfg(target_thread_local)]
use crate::fmt;
use crate::io::{self, IoSlice, IoSliceMut};
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
use crate::time::Duration;
-pub struct TcpStream(Void);
+pub struct TcpStream(!);
impl TcpStream {
pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
}
pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
- match self.0 {}
+ self.0
}
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
- match self.0 {}
+ self.0
}
pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn is_read_vectored(&self) -> bool {
- match self.0 {}
+ self.0
}
pub fn write(&self, _: &[u8]) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn is_write_vectored(&self) -> bool {
- match self.0 {}
+ self.0
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
- match self.0 {}
+ self.0
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
- match self.0 {}
+ self.0
}
pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn duplicate(&self) -> io::Result<TcpStream> {
- match self.0 {}
+ self.0
}
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn nodelay(&self) -> io::Result<bool> {
- match self.0 {}
+ self.0
}
pub fn set_ttl(&self, _: u32) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn ttl(&self) -> io::Result<u32> {
- match self.0 {}
+ self.0
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
- match self.0 {}
+ self.0
}
pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
- match self.0 {}
+ self.0
}
}
impl fmt::Debug for TcpStream {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.0 {}
+ self.0
}
}
-pub struct TcpListener(Void);
+pub struct TcpListener(!);
impl TcpListener {
pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
- match self.0 {}
+ self.0
}
pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
- match self.0 {}
+ self.0
}
pub fn duplicate(&self) -> io::Result<TcpListener> {
- match self.0 {}
+ self.0
}
pub fn set_ttl(&self, _: u32) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn ttl(&self) -> io::Result<u32> {
- match self.0 {}
+ self.0
}
pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn only_v6(&self) -> io::Result<bool> {
- match self.0 {}
+ self.0
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
- match self.0 {}
+ self.0
}
pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
- match self.0 {}
+ self.0
}
}
impl fmt::Debug for TcpListener {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.0 {}
+ self.0
}
}
-pub struct UdpSocket(Void);
+pub struct UdpSocket(!);
impl UdpSocket {
pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
- match self.0 {}
+ self.0
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
- match self.0 {}
+ self.0
}
pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
- match self.0 {}
+ self.0
}
pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
- match self.0 {}
+ self.0
}
pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn duplicate(&self) -> io::Result<UdpSocket> {
- match self.0 {}
+ self.0
}
pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
- match self.0 {}
+ self.0
}
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
- match self.0 {}
+ self.0
}
pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn broadcast(&self) -> io::Result<bool> {
- match self.0 {}
+ self.0
}
pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn multicast_loop_v4(&self) -> io::Result<bool> {
- match self.0 {}
+ self.0
}
pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
- match self.0 {}
+ self.0
}
pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn multicast_loop_v6(&self) -> io::Result<bool> {
- match self.0 {}
+ self.0
}
pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn set_ttl(&self, _: u32) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn ttl(&self) -> io::Result<u32> {
- match self.0 {}
+ self.0
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
- match self.0 {}
+ self.0
}
pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn send(&self, _: &[u8]) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
- match self.0 {}
+ self.0
}
}
impl fmt::Debug for UdpSocket {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.0 {}
+ self.0
}
}
-pub struct LookupHost(Void);
+pub struct LookupHost(!);
impl LookupHost {
pub fn port(&self) -> u16 {
- match self.0 {}
+ self.0
}
}
impl Iterator for LookupHost {
type Item = SocketAddr;
fn next(&mut self) -> Option<SocketAddr> {
- match self.0 {}
+ self.0
}
}
-use super::{unsupported, Void};
+use super::unsupported;
use crate::error::Error as StdError;
use crate::ffi::{OsStr, OsString};
use crate::fmt;
use crate::io;
+use crate::marker::PhantomData;
use crate::path::{self, PathBuf};
pub fn errno() -> i32 {
unsupported()
}
-pub struct SplitPaths<'a>(&'a Void);
+pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);
pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
panic!("unsupported")
impl<'a> Iterator for SplitPaths<'a> {
type Item = PathBuf;
fn next(&mut self) -> Option<PathBuf> {
- match *self.0 {}
+ self.0
}
}
unsupported()
}
-pub struct Env(Void);
+pub struct Env(!);
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
- match self.0 {}
+ self.0
}
}
}
pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
- Err(io::Error::new_const(io::ErrorKind::Other, &"cannot set env vars on this platform"))
+ Err(io::Error::new_const(io::ErrorKind::Unsupported, &"cannot set env vars on this platform"))
}
pub fn unsetenv(_: &OsStr) -> io::Result<()> {
- Err(io::Error::new_const(io::ErrorKind::Other, &"cannot unset env vars on this platform"))
+ Err(io::Error::new_const(io::ErrorKind::Unsupported, &"cannot unset env vars on this platform"))
}
pub fn temp_dir() -> PathBuf {
use crate::io::{self, IoSlice, IoSliceMut};
-use crate::sys::Void;
-pub struct AnonPipe(Void);
+pub struct AnonPipe(!);
impl AnonPipe {
pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn is_read_vectored(&self) -> bool {
- match self.0 {}
+ self.0
}
pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> {
- match self.0 {}
+ self.0
}
pub fn is_write_vectored(&self) -> bool {
- match self.0 {}
+ self.0
}
pub fn diverge(&self) -> ! {
- match self.0 {}
+ self.0
}
}
use crate::path::Path;
use crate::sys::fs::File;
use crate::sys::pipe::AnonPipe;
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
use crate::sys_common::process::{CommandEnv, CommandEnvs};
pub use crate::ffi::OsString as EnvKey;
}
}
-pub struct ExitStatus(Void);
+pub struct ExitStatus(!);
impl ExitStatus {
pub fn success(&self) -> bool {
- match self.0 {}
+ self.0
}
pub fn code(&self) -> Option<i32> {
- match self.0 {}
+ self.0
}
}
impl Clone for ExitStatus {
fn clone(&self) -> ExitStatus {
- match self.0 {}
+ self.0
}
}
impl PartialEq for ExitStatus {
fn eq(&self, _other: &ExitStatus) -> bool {
- match self.0 {}
+ self.0
}
}
impl fmt::Debug for ExitStatus {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.0 {}
+ self.0
}
}
impl fmt::Display for ExitStatus {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.0 {}
+ self.0
}
}
}
}
-pub struct Process(Void);
+pub struct Process(!);
impl Process {
pub fn id(&self) -> u32 {
- match self.0 {}
+ self.0
}
pub fn kill(&mut self) -> io::Result<()> {
- match self.0 {}
+ self.0
}
pub fn wait(&mut self) -> io::Result<ExitStatus> {
- match self.0 {}
+ self.0
}
pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
- match self.0 {}
+ self.0
}
}
+++ /dev/null
-pub unsafe fn init() {}
-
-pub unsafe fn cleanup() {}
-use super::{unsupported, Void};
+use super::unsupported;
use crate::ffi::CStr;
use crate::io;
use crate::time::Duration;
-pub struct Thread(Void);
+pub struct Thread(!);
pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
}
pub fn join(self) {
- match self.0 {}
+ self.0
}
}
+++ /dev/null
-pub mod os {
- pub const FAMILY: &str = "vxworks";
- pub const OS: &str = "vxworks";
- pub const DLL_PREFIX: &str = "lib";
- pub const DLL_SUFFIX: &str = ".so";
- pub const DLL_EXTENSION: &str = "so";
- pub const EXE_SUFFIX: &str = "";
- pub const EXE_EXTENSION: &str = "";
-}
+++ /dev/null
-#![allow(dead_code)]
-#![allow(missing_docs, nonstandard_style)]
-
-use crate::io::ErrorKind;
-
-pub use self::rand::hashmap_random_keys;
-pub use crate::os::vxworks as platform;
-pub use libc::strlen;
-
-#[macro_use]
-#[path = "../unix/weak.rs"]
-pub mod weak;
-
-#[path = "../unix/alloc.rs"]
-pub mod alloc;
-#[path = "../unix/args.rs"]
-pub mod args;
-#[path = "../unix/cmath.rs"]
-pub mod cmath;
-#[path = "../unix/condvar.rs"]
-pub mod condvar;
-pub mod env;
-#[path = "../unix/ext/mod.rs"]
-pub mod ext;
-#[path = "../unix/fd.rs"]
-pub mod fd;
-#[path = "../unix/fs.rs"]
-pub mod fs;
-#[path = "../unix/io.rs"]
-pub mod io;
-#[path = "../unix/memchr.rs"]
-pub mod memchr;
-#[path = "../unix/mutex.rs"]
-pub mod mutex;
-#[path = "../unix/net.rs"]
-pub mod net;
-#[path = "../unix/os.rs"]
-pub mod os;
-#[path = "../unix/path.rs"]
-pub mod path;
-#[path = "../unix/pipe.rs"]
-pub mod pipe;
-pub mod process;
-pub mod rand;
-#[path = "../unix/rwlock.rs"]
-pub mod rwlock;
-#[path = "../unix/stack_overflow.rs"]
-pub mod stack_overflow;
-#[path = "../unix/stdio.rs"]
-pub mod stdio;
-#[path = "../unix/thread.rs"]
-pub mod thread;
-pub mod thread_local_dtor;
-#[path = "../unix/thread_local_key.rs"]
-pub mod thread_local_key;
-#[path = "../unix/time.rs"]
-pub mod time;
-
-pub use crate::sys_common::os_str_bytes as os_str;
-
-#[cfg(not(test))]
-pub fn init() {
- // ignore SIGPIPE
- unsafe {
- assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
- }
-}
-
-pub use libc::signal;
-
-pub fn decode_error_kind(errno: i32) -> ErrorKind {
- match errno as libc::c_int {
- libc::ECONNREFUSED => ErrorKind::ConnectionRefused,
- libc::ECONNRESET => ErrorKind::ConnectionReset,
- libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied,
- libc::EPIPE => ErrorKind::BrokenPipe,
- libc::ENOTCONN => ErrorKind::NotConnected,
- libc::ECONNABORTED => ErrorKind::ConnectionAborted,
- libc::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable,
- libc::EADDRINUSE => ErrorKind::AddrInUse,
- libc::ENOENT => ErrorKind::NotFound,
- libc::EINTR => ErrorKind::Interrupted,
- libc::EINVAL => ErrorKind::InvalidInput,
- libc::ETIMEDOUT => ErrorKind::TimedOut,
- libc::EEXIST => ErrorKind::AlreadyExists,
-
- // These two constants can have the same value on some systems,
- // but different values on others, so we can't use a match
- // clause
- x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => ErrorKind::WouldBlock,
-
- _ => ErrorKind::Other,
- }
-}
-
-#[doc(hidden)]
-pub trait IsMinusOne {
- fn is_minus_one(&self) -> bool;
-}
-
-macro_rules! impl_is_minus_one {
- ($($t:ident)*) => ($(impl IsMinusOne for $t {
- fn is_minus_one(&self) -> bool {
- *self == -1
- }
- })*)
-}
-
-impl_is_minus_one! { i8 i16 i32 i64 isize }
-
-pub fn cvt<T: IsMinusOne>(t: T) -> crate::io::Result<T> {
- if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) }
-}
-
-pub fn cvt_r<T, F>(mut f: F) -> crate::io::Result<T>
-where
- T: IsMinusOne,
- F: FnMut() -> T,
-{
- loop {
- match cvt(f()) {
- Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
- other => return other,
- }
- }
-}
-
-// On Unix-like platforms, libc::abort will unregister signal handlers
-// including the SIGABRT handler, preventing the abort from being blocked, and
-// fclose streams, with the side effect of flushing them so libc buffered
-// output will be printed. Additionally the shell will generally print a more
-// understandable error message like "Abort trap" rather than "Illegal
-// instruction" that intrinsics::abort would cause, as intrinsics::abort is
-// implemented as an illegal instruction.
-pub fn abort_internal() -> ! {
- unsafe { libc::abort() }
-}
+++ /dev/null
-pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes};
-pub use self::process_inner::{ExitStatus, Process};
-pub use crate::ffi::OsString as EnvKey;
-pub use crate::sys_common::process::CommandEnvs;
-
-#[path = "../../unix/process/process_common.rs"]
-mod process_common;
-#[path = "process_vxworks.rs"]
-mod process_inner;
+++ /dev/null
-use crate::fmt;
-use crate::io::{self, Error, ErrorKind};
-use crate::sys;
-use crate::sys::cvt;
-use crate::sys::process::process_common::*;
-use crate::sys_common::thread;
-use libc::RTP_ID;
-use libc::{self, c_char, c_int};
-
-////////////////////////////////////////////////////////////////////////////////
-// Command
-////////////////////////////////////////////////////////////////////////////////
-
-impl Command {
- pub fn spawn(
- &mut self,
- default: Stdio,
- needs_stdin: bool,
- ) -> io::Result<(Process, StdioPipes)> {
- use crate::sys::cvt_r;
- const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";
- let envp = self.capture_env();
-
- if self.saw_nul() {
- return Err(io::Error::new_const(
- ErrorKind::InvalidInput,
- &"nul byte found in provided data",
- ));
- }
- let (ours, theirs) = self.setup_io(default, needs_stdin)?;
- let mut p = Process { pid: 0, status: None };
-
- unsafe {
- macro_rules! t {
- ($e:expr) => {
- match $e {
- Ok(e) => e,
- Err(e) => return Err(e.into()),
- }
- };
- }
-
- let mut orig_stdin = libc::STDIN_FILENO;
- let mut orig_stdout = libc::STDOUT_FILENO;
- let mut orig_stderr = libc::STDERR_FILENO;
-
- if let Some(fd) = theirs.stdin.fd() {
- orig_stdin = t!(cvt_r(|| libc::dup(libc::STDIN_FILENO)));
- t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO)));
- }
- if let Some(fd) = theirs.stdout.fd() {
- orig_stdout = t!(cvt_r(|| libc::dup(libc::STDOUT_FILENO)));
- t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO)));
- }
- if let Some(fd) = theirs.stderr.fd() {
- orig_stderr = t!(cvt_r(|| libc::dup(libc::STDERR_FILENO)));
- t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO)));
- }
-
- if let Some(ref cwd) = *self.get_cwd() {
- t!(cvt(libc::chdir(cwd.as_ptr())));
- }
-
- let c_envp = envp
- .as_ref()
- .map(|c| c.as_ptr())
- .unwrap_or_else(|| *sys::os::environ() as *const _);
- let stack_size = thread::min_stack();
-
- // ensure that access to the environment is synchronized
- let _lock = sys::os::env_lock();
-
- let ret = libc::rtpSpawn(
- self.get_program_cstr().as_ptr(),
- self.get_argv().as_ptr() as *mut *const c_char, // argv
- c_envp as *mut *const c_char,
- 100 as c_int, // initial priority
- stack_size, // initial stack size.
- 0, // options
- 0, // task options
- );
-
- // Because FileDesc was not used, each duplicated file descriptor
- // needs to be closed manually
- if orig_stdin != libc::STDIN_FILENO {
- t!(cvt_r(|| libc::dup2(orig_stdin, libc::STDIN_FILENO)));
- libc::close(orig_stdin);
- }
- if orig_stdout != libc::STDOUT_FILENO {
- t!(cvt_r(|| libc::dup2(orig_stdout, libc::STDOUT_FILENO)));
- libc::close(orig_stdout);
- }
- if orig_stderr != libc::STDERR_FILENO {
- t!(cvt_r(|| libc::dup2(orig_stderr, libc::STDERR_FILENO)));
- libc::close(orig_stderr);
- }
-
- if ret != libc::RTP_ID_ERROR {
- p.pid = ret;
- Ok((p, ours))
- } else {
- Err(io::Error::last_os_error())
- }
- }
- }
-
- pub fn exec(&mut self, default: Stdio) -> io::Error {
- let ret = Command::spawn(self, default, false);
- match ret {
- Ok(t) => unsafe {
- let mut status = 0 as c_int;
- libc::waitpid(t.0.pid, &mut status, 0);
- libc::exit(0);
- },
- Err(e) => e,
- }
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Processes
-////////////////////////////////////////////////////////////////////////////////
-
-/// The unique id of the process (this should never be negative).
-pub struct Process {
- pid: RTP_ID,
- status: Option<ExitStatus>,
-}
-
-impl Process {
- pub fn id(&self) -> u32 {
- self.pid as u32
- }
-
- pub fn kill(&mut self) -> io::Result<()> {
- // If we've already waited on this process then the pid can be recycled
- // and used for another process, and we probably shouldn't be killing
- // random processes, so just return an error.
- if self.status.is_some() {
- Err(Error::new_const(
- ErrorKind::InvalidInput,
- &"invalid argument: can't kill an exited process",
- ))
- } else {
- cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
- }
- }
-
- pub fn wait(&mut self) -> io::Result<ExitStatus> {
- use crate::sys::cvt_r;
- if let Some(status) = self.status {
- return Ok(status);
- }
- let mut status = 0 as c_int;
- cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?;
- self.status = Some(ExitStatus::new(status));
- Ok(ExitStatus::new(status))
- }
-
- pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
- if let Some(status) = self.status {
- return Ok(Some(status));
- }
- let mut status = 0 as c_int;
- let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?;
- if pid == 0 {
- Ok(None)
- } else {
- self.status = Some(ExitStatus::new(status));
- Ok(Some(ExitStatus::new(status)))
- }
- }
-}
-
-/// Unix exit statuses
-#[derive(PartialEq, Eq, Clone, Copy, Debug)]
-pub struct ExitStatus(c_int);
-
-impl ExitStatus {
- pub fn new(status: c_int) -> ExitStatus {
- ExitStatus(status)
- }
-
- fn exited(&self) -> bool {
- libc::WIFEXITED(self.0)
- }
-
- pub fn success(&self) -> bool {
- self.code() == Some(0)
- }
-
- pub fn code(&self) -> Option<i32> {
- if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None }
- }
-
- pub fn signal(&self) -> Option<i32> {
- if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None }
- }
-}
-
-/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
-impl From<c_int> for ExitStatus {
- fn from(a: c_int) -> ExitStatus {
- ExitStatus(a)
- }
-}
-
-impl fmt::Display for ExitStatus {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- if let Some(code) = self.code() {
- write!(f, "exit code: {}", code)
- } else {
- let signal = self.signal().unwrap();
- write!(f, "signal: {}", signal)
- }
- }
-}
+++ /dev/null
-use crate::mem;
-use crate::slice;
-
-pub fn hashmap_random_keys() -> (u64, u64) {
- let mut v = (0, 0);
- unsafe {
- let view = slice::from_raw_parts_mut(&mut v as *mut _ as *mut u8, mem::size_of_val(&v));
- imp::fill_bytes(view);
- }
- return v;
-}
-
-mod imp {
- use crate::io;
- use core::sync::atomic::{AtomicBool, Ordering::Relaxed};
-
- pub fn fill_bytes(v: &mut [u8]) {
- static RNG_INIT: AtomicBool = AtomicBool::new(false);
- while !RNG_INIT.load(Relaxed) {
- let ret = unsafe { libc::randSecure() };
- if ret < 0 {
- panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
- } else if ret > 0 {
- RNG_INIT.store(true, Relaxed);
- break;
- }
- unsafe { libc::usleep(10) };
- }
- let ret = unsafe {
- libc::randABytes(v.as_mut_ptr() as *mut libc::c_uchar, v.len() as libc::c_int)
- };
- if ret < 0 {
- panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
- }
- }
-}
+++ /dev/null
-#![cfg(target_thread_local)]
-#![unstable(feature = "thread_local_internals", issue = "none")]
-
-pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
- use crate::sys_common::thread_local_dtor::register_dtor_fallback;
- register_dtor_fallback(t, dtor);
-}
#![deny(unsafe_op_in_unsafe_fn)]
use crate::ffi::{CStr, OsStr, OsString};
-use crate::marker::PhantomData;
+use crate::fmt;
use crate::os::wasi::ffi::OsStrExt;
use crate::vec;
-pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
-
-pub unsafe fn cleanup() {}
-
pub struct Args {
iter: vec::IntoIter<OsString>,
- _dont_send_or_sync_me: PhantomData<*mut ()>,
}
+impl !Send for Args {}
+impl !Sync for Args {}
+
/// Returns the command line arguments
pub fn args() -> Args {
- Args {
- iter: maybe_args().unwrap_or(Vec::new()).into_iter(),
- _dont_send_or_sync_me: PhantomData,
- }
+ Args { iter: maybe_args().unwrap_or(Vec::new()).into_iter() }
}
fn maybe_args() -> Option<Vec<OsString>> {
}
}
-impl Args {
- pub fn inner_debug(&self) -> &[OsString] {
- self.iter.as_slice()
+impl fmt::Debug for Args {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.iter.as_slice().fmt(f)
}
}
#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
impl AsRawFd for RawFd {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
*self
}
}
#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
impl IntoRawFd for RawFd {
+ #[inline]
fn into_raw_fd(self) -> RawFd {
self
}
}
#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
impl FromRawFd for RawFd {
+ #[inline]
unsafe fn from_raw_fd(fd: RawFd) -> RawFd {
fd
}
}
impl AsRawFd for net::TcpStream {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
self.as_inner().fd().as_raw()
}
}
impl FromRawFd for net::TcpStream {
+ #[inline]
unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream {
net::TcpStream::from_inner(sys::net::TcpStream::from_inner(fd))
}
}
impl IntoRawFd for net::TcpStream {
+ #[inline]
fn into_raw_fd(self) -> RawFd {
self.into_inner().into_fd().into_raw()
}
}
impl AsRawFd for net::TcpListener {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
self.as_inner().fd().as_raw()
}
}
impl FromRawFd for net::TcpListener {
+ #[inline]
unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener {
net::TcpListener::from_inner(sys::net::TcpListener::from_inner(fd))
}
}
impl IntoRawFd for net::TcpListener {
+ #[inline]
fn into_raw_fd(self) -> RawFd {
self.into_inner().into_fd().into_raw()
}
}
impl AsRawFd for net::UdpSocket {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
self.as_inner().fd().as_raw()
}
}
impl FromRawFd for net::UdpSocket {
+ #[inline]
unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket {
net::UdpSocket::from_inner(sys::net::UdpSocket::from_inner(fd))
}
}
impl IntoRawFd for net::UdpSocket {
+ #[inline]
fn into_raw_fd(self) -> RawFd {
self.into_inner().into_fd().into_raw()
}
}
impl AsRawFd for fs::File {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
self.as_inner().fd().as_raw()
}
}
impl FromRawFd for fs::File {
+ #[inline]
unsafe fn from_raw_fd(fd: RawFd) -> fs::File {
fs::File::from_inner(sys::fs::File::from_inner(fd))
}
}
impl IntoRawFd for fs::File {
+ #[inline]
fn into_raw_fd(self) -> RawFd {
self.into_inner().into_fd().into_raw()
}
}
impl AsRawFd for io::Stdin {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
libc::STDIN_FILENO as RawFd
}
}
impl AsRawFd for io::Stdout {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
libc::STDOUT_FILENO as RawFd
}
}
impl AsRawFd for io::Stderr {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
libc::STDERR_FILENO as RawFd
}
}
impl<'a> AsRawFd for io::StdinLock<'a> {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
libc::STDIN_FILENO as RawFd
}
}
impl<'a> AsRawFd for io::StdoutLock<'a> {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
libc::STDOUT_FILENO as RawFd
}
}
impl<'a> AsRawFd for io::StderrLock<'a> {
+ #[inline]
fn as_raw_fd(&self) -> RawFd {
libc::STDERR_FILENO as RawFd
}
pub mod process;
#[path = "../unsupported/rwlock.rs"]
pub mod rwlock;
-#[path = "../unsupported/stack_overflow.rs"]
-pub mod stack_overflow;
pub mod stdio;
pub mod thread;
#[path = "../unsupported/thread_local_dtor.rs"]
wasi::ERRNO_TIMEDOUT => TimedOut,
wasi::ERRNO_EXIST => AlreadyExists,
wasi::ERRNO_AGAIN => WouldBlock,
+ wasi::ERRNO_NOSYS => Unsupported,
_ => Other,
}
}
use crate::fmt;
use crate::io::{self, IoSlice, IoSliceMut};
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
use crate::sys_common::FromInner;
use crate::time::Duration;
}
}
-pub struct LookupHost(Void);
+pub struct LookupHost(!);
impl LookupHost {
pub fn port(&self) -> u16 {
- match self.0 {}
+ self.0
}
}
impl Iterator for LookupHost {
type Item = SocketAddr;
fn next(&mut self) -> Option<SocketAddr> {
- match self.0 {}
+ self.0
}
}
use crate::path::{self, PathBuf};
use crate::str;
use crate::sys::memchr;
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
use crate::vec;
// Add a few symbols not in upstream `libc` just yet.
}
}
-pub struct SplitPaths<'a>(&'a Void);
+pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);
pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
panic!("unsupported")
impl<'a> Iterator for SplitPaths<'a> {
type Item = PathBuf;
fn next(&mut self) -> Option<PathBuf> {
- match *self.0 {}
+ self.0
}
}
}
pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
- _dont_send_or_sync_me: PhantomData<*mut ()>,
}
+impl !Send for Env {}
+impl !Sync for Env {}
+
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
environ = environ.add(1);
}
}
- return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData };
+ return Env { iter: result.into_iter() };
}
// See src/libstd/sys/unix/os.rs, same as that
use crate::ffi::CStr;
use crate::io;
use crate::mem;
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
use crate::time::Duration;
-pub struct Thread(Void);
+pub struct Thread(!);
pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
}
pub fn join(self) {
- match self.0 {}
+ self.0
}
}
use crate::ffi::OsString;
-use crate::marker::PhantomData;
+use crate::fmt;
use crate::vec;
-pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
- // On wasm these should always be null, so there's nothing for us to do here
-}
-
-pub unsafe fn cleanup() {}
-
pub fn args() -> Args {
- Args { iter: Vec::new().into_iter(), _dont_send_or_sync_me: PhantomData }
+ Args { iter: Vec::new().into_iter() }
}
pub struct Args {
iter: vec::IntoIter<OsString>,
- _dont_send_or_sync_me: PhantomData<*mut ()>,
}
-impl Args {
- pub fn inner_debug(&self) -> &[OsString] {
- self.iter.as_slice()
+impl !Send for Args {}
+impl !Sync for Args {}
+
+impl fmt::Debug for Args {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.iter.as_slice().fmt(f)
}
}
pub mod pipe;
#[path = "../unsupported/process.rs"]
pub mod process;
-#[path = "../unsupported/stack_overflow.rs"]
-pub mod stack_overflow;
#[path = "../unsupported/stdio.rs"]
pub mod stdio;
pub mod thread;
use crate::ffi::CStr;
use crate::io;
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
use crate::time::Duration;
-pub struct Thread(Void);
+pub struct Thread(!);
pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
}
pub fn join(self) {
- match self.0 {}
+ self.0
}
}
use core::iter;
-pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
-
-pub unsafe fn cleanup() {}
-
pub fn args() -> Args {
unsafe {
let lp_cmd_line = c::GetCommandLineW();
parsed_args_list: vec::IntoIter<OsString>,
}
-pub struct ArgsInnerDebug<'a> {
- args: &'a Args,
-}
-
-impl<'a> fmt::Debug for ArgsInnerDebug<'a> {
+impl fmt::Debug for Args {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.args.parsed_args_list.as_slice().fmt(f)
- }
-}
-
-impl Args {
- pub fn inner_debug(&self) -> ArgsInnerDebug<'_> {
- ArgsInnerDebug { args: self }
+ self.parsed_args_list.as_slice().fmt(f)
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl AsRawHandle for fs::File {
+ #[inline]
fn as_raw_handle(&self) -> RawHandle {
self.as_inner().handle().raw() as RawHandle
}
#[stable(feature = "from_raw_os", since = "1.1.0")]
impl FromRawHandle for fs::File {
+ #[inline]
unsafe fn from_raw_handle(handle: RawHandle) -> fs::File {
let handle = handle as c::HANDLE;
fs::File::from_inner(sys::fs::File::from_inner(handle))
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawHandle for fs::File {
+ #[inline]
fn into_raw_handle(self) -> RawHandle {
self.into_inner().into_handle().into_raw() as *mut _
}
#[stable(feature = "rust1", since = "1.0.0")]
impl AsRawSocket for net::TcpStream {
+ #[inline]
fn as_raw_socket(&self) -> RawSocket {
*self.as_inner().socket().as_inner()
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl AsRawSocket for net::TcpListener {
+ #[inline]
fn as_raw_socket(&self) -> RawSocket {
*self.as_inner().socket().as_inner()
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl AsRawSocket for net::UdpSocket {
+ #[inline]
fn as_raw_socket(&self) -> RawSocket {
*self.as_inner().socket().as_inner()
}
#[stable(feature = "from_raw_os", since = "1.1.0")]
impl FromRawSocket for net::TcpStream {
+ #[inline]
unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream {
let sock = sys::net::Socket::from_inner(sock);
net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(sock))
}
#[stable(feature = "from_raw_os", since = "1.1.0")]
impl FromRawSocket for net::TcpListener {
+ #[inline]
unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener {
let sock = sys::net::Socket::from_inner(sock);
net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(sock))
}
#[stable(feature = "from_raw_os", since = "1.1.0")]
impl FromRawSocket for net::UdpSocket {
+ #[inline]
unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket {
let sock = sys::net::Socket::from_inner(sock);
net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(sock))
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawSocket for net::TcpStream {
+ #[inline]
fn into_raw_socket(self) -> RawSocket {
self.into_inner().into_socket().into_inner()
}
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawSocket for net::TcpListener {
+ #[inline]
fn into_raw_socket(self) -> RawSocket {
self.into_inner().into_socket().into_inner()
}
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawSocket for net::UdpSocket {
+ #[inline]
fn into_raw_socket(self) -> RawSocket {
self.into_inner().into_socket().into_inner()
}
#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawHandle for process::Child {
+ #[inline]
fn as_raw_handle(&self) -> RawHandle {
self.as_inner().handle().raw() as *mut _
}
#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawHandle for process::ChildStdin {
+ #[inline]
fn as_raw_handle(&self) -> RawHandle {
self.as_inner().handle().raw() as *mut _
}
#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawHandle for process::ChildStdout {
+ #[inline]
fn as_raw_handle(&self) -> RawHandle {
self.as_inner().handle().raw() as *mut _
}
#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawHandle for process::ChildStderr {
+ #[inline]
fn as_raw_handle(&self) -> RawHandle {
self.as_inner().handle().raw() as *mut _
}
#[stable(feature = "thread_extensions", since = "1.9.0")]
impl<T> AsRawHandle for thread::JoinHandle<T> {
+ #[inline]
fn as_raw_handle(&self) -> RawHandle {
self.as_inner().handle().raw() as *mut _
}
#[stable(feature = "thread_extensions", since = "1.9.0")]
impl<T> IntoRawHandle for thread::JoinHandle<T> {
+ #[inline]
fn into_raw_handle(self) -> RawHandle {
self.into_inner().into_handle().into_raw() as *mut _
}
#[cfg(target_vendor = "uwp")]
pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
- return Err(io::Error::new_const(io::ErrorKind::Other, &"hard link are not supported on UWP"));
+ return Err(io::Error::new_const(
+ io::ErrorKind::Unsupported,
+ &"hard link are not supported on UWP",
+ ));
}
pub fn stat(path: &Path) -> io::Result<FileAttr> {
}
}
-#[cfg(not(test))]
-pub fn init() {}
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
+ stack_overflow::init();
+}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {
+ net::cleanup();
+}
pub fn decode_error_kind(errno: i32) -> ErrorKind {
match errno as c::DWORD {
| c::ERROR_IPSEC_IKE_TIMED_OUT
| c::ERROR_RUNLEVEL_SWITCH_TIMEOUT
| c::ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT => return ErrorKind::TimedOut,
+ c::ERROR_CALL_NOT_IMPLEMENTED => return ErrorKind::Unsupported,
_ => {}
}
use crate::sys;
use crate::sys::c;
use crate::sys_common::net;
-use crate::sys_common::{self, AsInner, FromInner, IntoInner};
+use crate::sys_common::{AsInner, FromInner, IntoInner};
use crate::time::Duration;
use libc::{c_int, c_long, c_ulong, c_void};
pub struct Socket(c::SOCKET);
+static INIT: Once = Once::new();
+
/// Checks whether the Windows socket interface has been started already, and
/// if not, starts it.
pub fn init() {
- static START: Once = Once::new();
-
- START.call_once(|| unsafe {
+ INIT.call_once(|| unsafe {
let mut data: c::WSADATA = mem::zeroed();
let ret = c::WSAStartup(
0x202, // version 2.2
&mut data,
);
assert_eq!(ret, 0);
+ });
+}
- let _ = sys_common::at_exit(|| {
+pub fn cleanup() {
+ if INIT.is_completed() {
+ // only close the socket interface if it has actually been started
+ unsafe {
c::WSACleanup();
- });
- });
+ }
+ }
}
/// Returns the last error from the Windows socket interface.
#[cfg(target_vendor = "uwp")]
fn set_no_inherit(&self) -> io::Result<()> {
- Err(io::Error::new_const(io::ErrorKind::Other, &"Unavailable on UWP"))
+ Err(io::Error::new_const(io::ErrorKind::Unsupported, &"Unavailable on UWP"))
}
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
// Set the thread stack guarantee for the main thread.
let _h = Handler::new();
}
-
-pub unsafe fn cleanup() {}
}
pub unsafe fn init() {}
-
-pub unsafe fn cleanup() {}
+++ /dev/null
-//! Implementation of running at_exit routines
-//!
-//! Documentation can be found on the `rt::at_exit` function.
-
-use crate::mem;
-use crate::ptr;
-use crate::sys_common::mutex::StaticMutex;
-
-type Queue = Vec<Box<dyn FnOnce()>>;
-
-// NB these are specifically not types from `std::sync` as they currently rely
-// on poisoning and this module needs to operate at a lower level than requiring
-// the thread infrastructure to be in place (useful on the borders of
-// initialization/destruction).
-// It is UB to attempt to acquire this mutex reentrantly!
-static LOCK: StaticMutex = StaticMutex::new();
-static mut QUEUE: *mut Queue = ptr::null_mut();
-
-const DONE: *mut Queue = 1_usize as *mut _;
-
-// The maximum number of times the cleanup routines will be run. While running
-// the at_exit closures new ones may be registered, and this count is the number
-// of times the new closures will be allowed to register successfully. After
-// this number of iterations all new registrations will return `false`.
-const ITERS: usize = 10;
-
-unsafe fn init() -> bool {
- if QUEUE.is_null() {
- let state: Box<Queue> = box Vec::new();
- QUEUE = Box::into_raw(state);
- } else if QUEUE == DONE {
- // can't re-init after a cleanup
- return false;
- }
-
- true
-}
-
-pub fn cleanup() {
- for i in 1..=ITERS {
- unsafe {
- let queue = {
- let _guard = LOCK.lock();
- mem::replace(&mut QUEUE, if i == ITERS { DONE } else { ptr::null_mut() })
- };
-
- // make sure we're not recursively cleaning up
- assert!(queue != DONE);
-
- // If we never called init, not need to cleanup!
- if !queue.is_null() {
- let queue: Box<Queue> = Box::from_raw(queue);
- for to_run in *queue {
- // We are not holding any lock, so reentrancy is fine.
- to_run();
- }
- }
- }
- }
-}
-
-pub fn push(f: Box<dyn FnOnce()>) -> bool {
- unsafe {
- let _guard = LOCK.lock();
- if init() {
- // We are just moving `f` around, not calling it.
- // There is no possibility of reentrancy here.
- (*QUEUE).push(f);
- true
- } else {
- false
- }
- }
-}
#[cfg(test)]
mod tests;
-use crate::sync::Once;
-use crate::sys;
-
-macro_rules! rtabort {
- ($($t:tt)*) => (crate::sys_common::util::abort(format_args!($($t)*)))
-}
-
-macro_rules! rtassert {
- ($e:expr) => {
- if !$e {
- rtabort!(concat!("assertion failed: ", stringify!($e)));
- }
- };
-}
-
-#[allow(unused_macros)] // not used on all platforms
-macro_rules! rtunwrap {
- ($ok:ident, $e:expr) => {
- match $e {
- $ok(v) => v,
- ref err => {
- let err = err.as_ref().map(drop); // map Ok/Some which might not be Debug
- rtabort!(concat!("unwrap failed: ", stringify!($e), " = {:?}"), err)
- }
- }
- };
-}
-
-pub mod at_exit_imp;
pub mod backtrace;
pub mod bytestring;
pub mod condvar;
// when generating documentation.
#[cfg(any(doc, not(windows)))]
pub mod os_str_bytes;
-pub mod poison;
pub mod process;
pub mod remutex;
+#[macro_use]
+pub mod rt;
pub mod rwlock;
pub mod thread;
pub mod thread_info;
fn from_inner(inner: Inner) -> Self;
}
-/// Enqueues a procedure to run when the main thread exits.
-///
-/// Currently these closures are only run once the main *Rust* thread exits.
-/// Once the `at_exit` handlers begin running, more may be enqueued, but not
-/// infinitely so. Eventually a handler registration will be forced to fail.
-///
-/// Returns `Ok` if the handler was successfully registered, meaning that the
-/// closure will be run once the main thread exits. Returns `Err` to indicate
-/// that the closure could not be registered, meaning that it is not scheduled
-/// to be run.
-pub fn at_exit<F: FnOnce() + Send + 'static>(f: F) -> Result<(), ()> {
- if at_exit_imp::push(Box::new(f)) { Ok(()) } else { Err(()) }
-}
-
-/// One-time runtime cleanup.
-pub fn cleanup() {
- static CLEANUP: Once = Once::new();
- CLEANUP.call_once(|| unsafe {
- sys::args::cleanup();
- sys::stack_overflow::cleanup();
- at_exit_imp::cleanup();
- });
-}
-
// Computes (value*numer)/denom without overflow, as long as both
// (numer*denom) and the overall result fit into i64 (which is the case
// for our time conversions).
+++ /dev/null
-use crate::error::Error;
-use crate::fmt;
-use crate::sync::atomic::{AtomicBool, Ordering};
-use crate::thread;
-
-#[allow(unused_imports)] // for intra-doc links
-use crate::sync::{Mutex, RwLock};
-
-pub struct Flag {
- failed: AtomicBool,
-}
-
-// Note that the Ordering uses to access the `failed` field of `Flag` below is
-// always `Relaxed`, and that's because this isn't actually protecting any data,
-// it's just a flag whether we've panicked or not.
-//
-// The actual location that this matters is when a mutex is **locked** which is
-// where we have external synchronization ensuring that we see memory
-// reads/writes to this flag.
-//
-// As a result, if it matters, we should see the correct value for `failed` in
-// all cases.
-
-impl Flag {
- pub const fn new() -> Flag {
- Flag { failed: AtomicBool::new(false) }
- }
-
- #[inline]
- pub fn borrow(&self) -> LockResult<Guard> {
- let ret = Guard { panicking: thread::panicking() };
- if self.get() { Err(PoisonError::new(ret)) } else { Ok(ret) }
- }
-
- #[inline]
- pub fn done(&self, guard: &Guard) {
- if !guard.panicking && thread::panicking() {
- self.failed.store(true, Ordering::Relaxed);
- }
- }
-
- #[inline]
- pub fn get(&self) -> bool {
- self.failed.load(Ordering::Relaxed)
- }
-}
-
-pub struct Guard {
- panicking: bool,
-}
-
-/// A type of error which can be returned whenever a lock is acquired.
-///
-/// Both [`Mutex`]es and [`RwLock`]s are poisoned whenever a thread fails while the lock
-/// is held. The precise semantics for when a lock is poisoned is documented on
-/// each lock, but once a lock is poisoned then all future acquisitions will
-/// return this error.
-///
-/// # Examples
-///
-/// ```
-/// use std::sync::{Arc, Mutex};
-/// use std::thread;
-///
-/// let mutex = Arc::new(Mutex::new(1));
-///
-/// // poison the mutex
-/// let c_mutex = Arc::clone(&mutex);
-/// let _ = thread::spawn(move || {
-/// let mut data = c_mutex.lock().unwrap();
-/// *data = 2;
-/// panic!();
-/// }).join();
-///
-/// match mutex.lock() {
-/// Ok(_) => unreachable!(),
-/// Err(p_err) => {
-/// let data = p_err.get_ref();
-/// println!("recovered: {}", data);
-/// }
-/// };
-/// ```
-#[stable(feature = "rust1", since = "1.0.0")]
-pub struct PoisonError<T> {
- guard: T,
-}
-
-/// An enumeration of possible errors associated with a [`TryLockResult`] which
-/// can occur while trying to acquire a lock, from the [`try_lock`] method on a
-/// [`Mutex`] or the [`try_read`] and [`try_write`] methods on an [`RwLock`].
-///
-/// [`try_lock`]: Mutex::try_lock
-/// [`try_read`]: RwLock::try_read
-/// [`try_write`]: RwLock::try_write
-#[stable(feature = "rust1", since = "1.0.0")]
-pub enum TryLockError<T> {
- /// The lock could not be acquired because another thread failed while holding
- /// the lock.
- #[stable(feature = "rust1", since = "1.0.0")]
- Poisoned(#[stable(feature = "rust1", since = "1.0.0")] PoisonError<T>),
- /// The lock could not be acquired at this time because the operation would
- /// otherwise block.
- #[stable(feature = "rust1", since = "1.0.0")]
- WouldBlock,
-}
-
-/// A type alias for the result of a lock method which can be poisoned.
-///
-/// The [`Ok`] variant of this result indicates that the primitive was not
-/// poisoned, and the `Guard` is contained within. The [`Err`] variant indicates
-/// that the primitive was poisoned. Note that the [`Err`] variant *also* carries
-/// the associated guard, and it can be acquired through the [`into_inner`]
-/// method.
-///
-/// [`into_inner`]: PoisonError::into_inner
-#[stable(feature = "rust1", since = "1.0.0")]
-pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>;
-
-/// A type alias for the result of a nonblocking locking method.
-///
-/// For more information, see [`LockResult`]. A `TryLockResult` doesn't
-/// necessarily hold the associated guard in the [`Err`] type as the lock may not
-/// have been acquired for other reasons.
-#[stable(feature = "rust1", since = "1.0.0")]
-pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>;
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> fmt::Debug for PoisonError<T> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- "PoisonError { inner: .. }".fmt(f)
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> fmt::Display for PoisonError<T> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- "poisoned lock: another task failed inside".fmt(f)
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> Error for PoisonError<T> {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- "poisoned lock: another task failed inside"
- }
-}
-
-impl<T> PoisonError<T> {
- /// Creates a `PoisonError`.
- ///
- /// This is generally created by methods like [`Mutex::lock`] or [`RwLock::read`].
- #[stable(feature = "sync_poison", since = "1.2.0")]
- pub fn new(guard: T) -> PoisonError<T> {
- PoisonError { guard }
- }
-
- /// Consumes this error indicating that a lock is poisoned, returning the
- /// underlying guard to allow access regardless.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::collections::HashSet;
- /// use std::sync::{Arc, Mutex};
- /// use std::thread;
- ///
- /// let mutex = Arc::new(Mutex::new(HashSet::new()));
- ///
- /// // poison the mutex
- /// let c_mutex = Arc::clone(&mutex);
- /// let _ = thread::spawn(move || {
- /// let mut data = c_mutex.lock().unwrap();
- /// data.insert(10);
- /// panic!();
- /// }).join();
- ///
- /// let p_err = mutex.lock().unwrap_err();
- /// let data = p_err.into_inner();
- /// println!("recovered {} items", data.len());
- /// ```
- #[stable(feature = "sync_poison", since = "1.2.0")]
- pub fn into_inner(self) -> T {
- self.guard
- }
-
- /// Reaches into this error indicating that a lock is poisoned, returning a
- /// reference to the underlying guard to allow access regardless.
- #[stable(feature = "sync_poison", since = "1.2.0")]
- pub fn get_ref(&self) -> &T {
- &self.guard
- }
-
- /// Reaches into this error indicating that a lock is poisoned, returning a
- /// mutable reference to the underlying guard to allow access regardless.
- #[stable(feature = "sync_poison", since = "1.2.0")]
- pub fn get_mut(&mut self) -> &mut T {
- &mut self.guard
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> From<PoisonError<T>> for TryLockError<T> {
- fn from(err: PoisonError<T>) -> TryLockError<T> {
- TryLockError::Poisoned(err)
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> fmt::Debug for TryLockError<T> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match *self {
- TryLockError::Poisoned(..) => "Poisoned(..)".fmt(f),
- TryLockError::WouldBlock => "WouldBlock".fmt(f),
- }
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> fmt::Display for TryLockError<T> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match *self {
- TryLockError::Poisoned(..) => "poisoned lock: another task failed inside",
- TryLockError::WouldBlock => "try_lock failed because the operation would block",
- }
- .fmt(f)
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> Error for TryLockError<T> {
- #[allow(deprecated, deprecated_in_future)]
- fn description(&self) -> &str {
- match *self {
- TryLockError::Poisoned(ref p) => p.description(),
- TryLockError::WouldBlock => "try_lock failed because the operation would block",
- }
- }
-
- #[allow(deprecated)]
- fn cause(&self) -> Option<&dyn Error> {
- match *self {
- TryLockError::Poisoned(ref p) => Some(p),
- _ => None,
- }
- }
-}
-
-pub fn map_result<T, U, F>(result: LockResult<T>, f: F) -> LockResult<U>
-where
- F: FnOnce(T) -> U,
-{
- match result {
- Ok(t) => Ok(f(t)),
- Err(PoisonError { guard }) => Err(PoisonError::new(f(guard))),
- }
-}
--- /dev/null
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use crate::sync::Once;
+use crate::sys;
+use crate::sys_common::thread_info;
+use crate::thread::Thread;
+
+// One-time runtime initialization.
+// Runs before `main`.
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+#[cfg_attr(test, allow(dead_code))]
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
+ unsafe {
+ sys::init(argc, argv);
+
+ let main_guard = sys::thread::guard::init();
+ // Next, set up the current Thread with the guard information we just
+ // created. Note that this isn't necessary in general for new threads,
+ // but we just do this to name the main thread and to give it correct
+ // info about the stack bounds.
+ let thread = Thread::new(Some("main".to_owned()));
+ thread_info::set(main_guard, thread);
+ }
+}
+
+// One-time runtime cleanup.
+// Runs after `main` or at program exit.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+#[cfg_attr(test, allow(dead_code))]
+pub fn cleanup() {
+ static CLEANUP: Once = Once::new();
+ CLEANUP.call_once(|| unsafe {
+ // Flush stdout and disable buffering.
+ crate::io::cleanup();
+ // SAFETY: Only called once during runtime cleanup.
+ sys::cleanup();
+ });
+}
+
+macro_rules! rtabort {
+ ($($t:tt)*) => (crate::sys_common::util::abort(format_args!($($t)*)))
+}
+
+macro_rules! rtassert {
+ ($e:expr) => {
+ if !$e {
+ rtabort!(concat!("assertion failed: ", stringify!($e)));
+ }
+ };
+}
+
+#[allow(unused_macros)] // not used on all platforms
+macro_rules! rtunwrap {
+ ($ok:ident, $e:expr) => {
+ match $e {
+ $ok(v) => v,
+ ref err => {
+ let err = err.as_ref().map(drop); // map Ok/Some which might not be Debug
+ rtabort!(concat!("unwrap failed: ", stringify!($e), " = {:?}"), err)
+ }
+ }
+ };
+}
self.0.destroy()
}
}
-
-// the cfg annotations only exist due to dead code warnings. the code itself is portable
-#[cfg(unix)]
-pub struct StaticRWLock(RWLock);
-
-#[cfg(unix)]
-impl StaticRWLock {
- pub const fn new() -> StaticRWLock {
- StaticRWLock(RWLock::new())
- }
-
- /// Acquires shared access to the underlying lock, blocking the current
- /// thread to do so.
- ///
- /// The lock is automatically unlocked when the returned guard is dropped.
- #[inline]
- pub fn read_with_guard(&'static self) -> RWLockReadGuard {
- // SAFETY: All methods require static references, therefore self
- // cannot be moved between invocations.
- unsafe {
- self.0.read();
- }
- RWLockReadGuard(&self.0)
- }
-
- /// Acquires write access to the underlying lock, blocking the current thread
- /// to do so.
- ///
- /// The lock is automatically unlocked when the returned guard is dropped.
- #[inline]
- pub fn write_with_guard(&'static self) -> RWLockWriteGuard {
- // SAFETY: All methods require static references, therefore self
- // cannot be moved between invocations.
- unsafe {
- self.0.write();
- }
- RWLockWriteGuard(&self.0)
- }
-}
-
-#[cfg(unix)]
-pub struct RWLockReadGuard(&'static RWLock);
-
-#[cfg(unix)]
-impl Drop for RWLockReadGuard {
- fn drop(&mut self) {
- unsafe { self.0.read_unlock() }
- }
-}
-
-#[cfg(unix)]
-pub struct RWLockWriteGuard(&'static RWLock);
-
-#[cfg(unix)]
-impl Drop for RWLockWriteGuard {
- fn drop(&mut self) {
- unsafe { self.0.write_unlock() }
- }
-}
#[stable(feature = "std_debug", since = "1.16.0")]
impl<T: 'static> fmt::Debug for LocalKey<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("LocalKey { .. }")
+ f.debug_struct("LocalKey").finish_non_exhaustive()
}
}
// empty (base case for the recursion)
() => {};
+ ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const { $init:expr }; $($rest:tt)*) => (
+ $crate::__thread_local_inner!($(#[$attr])* $vis $name, $t, const $init);
+ $crate::thread_local!($($rest)*);
+ );
+
+ ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const { $init:expr }) => (
+ $crate::__thread_local_inner!($(#[$attr])* $vis $name, $t, const $init);
+ );
+
// process multiple declarations
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => (
$crate::__thread_local_inner!($(#[$attr])* $vis $name, $t, $init);
#[allow_internal_unstable(thread_local_internals, cfg_target_thread_local, thread_local)]
#[allow_internal_unsafe]
macro_rules! __thread_local_inner {
+ // used to generate the `LocalKey` value for const-initialized thread locals
+ (@key $t:ty, const $init:expr) => {{
+ unsafe fn __getit() -> $crate::option::Option<&'static $t> {
+ const _REQUIRE_UNSTABLE: () = $crate::thread::require_unstable_const_init_thread_local();
+
+ // wasm without atomics maps directly to `static mut`, and dtors
+ // aren't implemented because thread dtors aren't really a thing
+ // on wasm right now
+ //
+ // FIXME(#84224) this should come after the `target_thread_local`
+ // block.
+ #[cfg(all(target_arch = "wasm32", not(target_feature = "atomics")))]
+ {
+ static mut VAL: $t = $init;
+ Some(&VAL)
+ }
+
+ // If the platform has support for `#[thread_local]`, use it.
+ #[cfg(all(
+ target_thread_local,
+ not(all(target_arch = "wasm32", not(target_feature = "atomics"))),
+ ))]
+ {
+ // If a dtor isn't needed we can do something "very raw" and
+ // just get going.
+ if !$crate::mem::needs_drop::<$t>() {
+ #[thread_local]
+ static mut VAL: $t = $init;
+ unsafe {
+ return Some(&VAL)
+ }
+ }
+
+ #[thread_local]
+ static mut VAL: $t = $init;
+ // 0 == dtor not registered
+ // 1 == dtor registered, dtor not run
+ // 2 == dtor registered and is running or has run
+ #[thread_local]
+ static mut STATE: u8 = 0;
+
+ unsafe extern "C" fn destroy(ptr: *mut u8) {
+ let ptr = ptr as *mut $t;
+
+ unsafe {
+ debug_assert_eq!(STATE, 1);
+ STATE = 2;
+ $crate::ptr::drop_in_place(ptr);
+ }
+ }
+
+ unsafe {
+ match STATE {
+ // 0 == we haven't registered a destructor, so do
+ // so now.
+ 0 => {
+ $crate::thread::__FastLocalKeyInner::<$t>::register_dtor(
+ $crate::ptr::addr_of_mut!(VAL) as *mut u8,
+ destroy,
+ );
+ STATE = 1;
+ Some(&VAL)
+ }
+ // 1 == the destructor is registered and the value
+ // is valid, so return the pointer.
+ 1 => Some(&VAL),
+ // otherwise the destructor has already run, so we
+ // can't give access.
+ _ => None,
+ }
+ }
+ }
+
+ // On platforms without `#[thread_local]` we fall back to the
+ // same implementation as below for os thread locals.
+ #[cfg(all(
+ not(target_thread_local),
+ not(all(target_arch = "wasm32", not(target_feature = "atomics"))),
+ ))]
+ {
+ #[inline]
+ const fn __init() -> $t { $init }
+ static __KEY: $crate::thread::__OsLocalKeyInner<$t> =
+ $crate::thread::__OsLocalKeyInner::new();
+ #[allow(unused_unsafe)]
+ unsafe { __KEY.get(__init) }
+ }
+ }
+
+ unsafe {
+ $crate::thread::LocalKey::new(__getit)
+ }
+ }};
+
+ // used to generate the `LocalKey` value for `thread_local!`
(@key $t:ty, $init:expr) => {
{
#[inline]
}
}
};
- ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => {
+ ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
$(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
- $crate::__thread_local_inner!(@key $t, $init);
+ $crate::__thread_local_inner!(@key $t, $($init)*);
}
}
impl<T> fmt::Debug for Key<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("Key { .. }")
+ f.debug_struct("Key").finish_non_exhaustive()
}
}
impl<T> fmt::Debug for Key<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("Key { .. }")
+ f.debug_struct("Key").finish_non_exhaustive()
}
}
Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) }
}
+ // note that this is just a publically-callable function only for the
+ // const-initialized form of thread locals, basically a way to call the
+ // free `register_dtor` function defined elsewhere in libstd.
+ pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
+ unsafe {
+ register_dtor(a, dtor);
+ }
+ }
+
pub unsafe fn get<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> {
// SAFETY: See the definitions of `LazyKeyInner::get` and
// `try_initialize` for more informations.
impl<T> fmt::Debug for Key<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("Key { .. }")
+ f.debug_struct("Key").finish_non_exhaustive()
}
}
use crate::cell::{Cell, UnsafeCell};
use crate::sync::mpsc::{channel, Sender};
-use crate::thread;
+use crate::thread::{self, LocalKey};
use crate::thread_local;
struct Foo(Sender<()>);
#[test]
fn smoke_no_dtor() {
thread_local!(static FOO: Cell<i32> = Cell::new(1));
+ run(&FOO);
+ thread_local!(static FOO2: Cell<i32> = const { Cell::new(1) });
+ run(&FOO2);
- FOO.with(|f| {
- assert_eq!(f.get(), 1);
- f.set(2);
- });
- let (tx, rx) = channel();
- let _t = thread::spawn(move || {
- FOO.with(|f| {
+ fn run(key: &'static LocalKey<Cell<i32>>) {
+ key.with(|f| {
assert_eq!(f.get(), 1);
+ f.set(2);
});
- tx.send(()).unwrap();
- });
- rx.recv().unwrap();
+ let t = thread::spawn(move || {
+ key.with(|f| {
+ assert_eq!(f.get(), 1);
+ });
+ });
+ t.join().unwrap();
- FOO.with(|f| {
- assert_eq!(f.get(), 2);
- });
+ key.with(|f| {
+ assert_eq!(f.get(), 2);
+ });
+ }
}
#[test]
fn states() {
- struct Foo;
+ struct Foo(&'static LocalKey<Foo>);
impl Drop for Foo {
fn drop(&mut self) {
- assert!(FOO.try_with(|_| ()).is_err());
+ assert!(self.0.try_with(|_| ()).is_err());
}
}
- thread_local!(static FOO: Foo = Foo);
- thread::spawn(|| {
- assert!(FOO.try_with(|_| ()).is_ok());
- })
- .join()
- .ok()
- .expect("thread panicked");
+ thread_local!(static FOO: Foo = Foo(&FOO));
+ run(&FOO);
+ thread_local!(static FOO2: Foo = const { Foo(&FOO2) });
+ run(&FOO2);
+
+ fn run(foo: &'static LocalKey<Foo>) {
+ thread::spawn(move || {
+ assert!(foo.try_with(|_| ()).is_ok());
+ })
+ .join()
+ .unwrap();
+ }
}
#[test]
fn smoke_dtor() {
thread_local!(static FOO: UnsafeCell<Option<Foo>> = UnsafeCell::new(None));
-
- let (tx, rx) = channel();
- let _t = thread::spawn(move || unsafe {
- let mut tx = Some(tx);
- FOO.with(|f| {
- *f.get() = Some(Foo(tx.take().unwrap()));
+ run(&FOO);
+ thread_local!(static FOO2: UnsafeCell<Option<Foo>> = const { UnsafeCell::new(None) });
+ run(&FOO2);
+
+ fn run(key: &'static LocalKey<UnsafeCell<Option<Foo>>>) {
+ let (tx, rx) = channel();
+ let t = thread::spawn(move || unsafe {
+ let mut tx = Some(tx);
+ key.with(|f| {
+ *f.get() = Some(Foo(tx.take().unwrap()));
+ });
});
- });
- rx.recv().unwrap();
+ rx.recv().unwrap();
+ t.join().unwrap();
+ }
}
#[test]
fn circular() {
- struct S1;
- struct S2;
+ struct S1(&'static LocalKey<UnsafeCell<Option<S1>>>, &'static LocalKey<UnsafeCell<Option<S2>>>);
+ struct S2(&'static LocalKey<UnsafeCell<Option<S1>>>, &'static LocalKey<UnsafeCell<Option<S2>>>);
thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None));
thread_local!(static K2: UnsafeCell<Option<S2>> = UnsafeCell::new(None));
- static mut HITS: u32 = 0;
+ thread_local!(static K3: UnsafeCell<Option<S1>> = const { UnsafeCell::new(None) });
+ thread_local!(static K4: UnsafeCell<Option<S2>> = const { UnsafeCell::new(None) });
+ static mut HITS: usize = 0;
impl Drop for S1 {
fn drop(&mut self) {
unsafe {
HITS += 1;
- if K2.try_with(|_| ()).is_err() {
+ if self.1.try_with(|_| ()).is_err() {
assert_eq!(HITS, 3);
} else {
if HITS == 1 {
- K2.with(|s| *s.get() = Some(S2));
+ self.1.with(|s| *s.get() = Some(S2(self.0, self.1)));
} else {
assert_eq!(HITS, 3);
}
fn drop(&mut self) {
unsafe {
HITS += 1;
- assert!(K1.try_with(|_| ()).is_ok());
+ assert!(self.0.try_with(|_| ()).is_ok());
assert_eq!(HITS, 2);
- K1.with(|s| *s.get() = Some(S1));
+ self.0.with(|s| *s.get() = Some(S1(self.0, self.1)));
}
}
}
thread::spawn(move || {
- drop(S1);
+ drop(S1(&K1, &K2));
+ })
+ .join()
+ .unwrap();
+
+ unsafe {
+ HITS = 0;
+ }
+
+ thread::spawn(move || {
+ drop(S1(&K3, &K4));
})
.join()
- .ok()
- .expect("thread panicked");
+ .unwrap();
}
#[test]
fn self_referential() {
- struct S1;
+ struct S1(&'static LocalKey<UnsafeCell<Option<S1>>>);
+
thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None));
+ thread_local!(static K2: UnsafeCell<Option<S1>> = const { UnsafeCell::new(None) });
impl Drop for S1 {
fn drop(&mut self) {
- assert!(K1.try_with(|_| ()).is_err());
+ assert!(self.0.try_with(|_| ()).is_err());
}
}
thread::spawn(move || unsafe {
- K1.with(|s| *s.get() = Some(S1));
+ K1.with(|s| *s.get() = Some(S1(&K1)));
})
.join()
- .ok()
- .expect("thread panicked");
+ .unwrap();
+
+ thread::spawn(move || unsafe {
+ K2.with(|s| *s.get() = Some(S1(&K2)));
+ })
+ .join()
+ .unwrap();
}
// Note that this test will deadlock if TLS destructors aren't run (this
});
rx.recv().unwrap();
}
+
+#[test]
+fn dtors_in_dtors_in_dtors_const_init() {
+ struct S1(Sender<()>);
+ thread_local!(static K1: UnsafeCell<Option<S1>> = const { UnsafeCell::new(None) });
+ thread_local!(static K2: UnsafeCell<Option<Foo>> = const { UnsafeCell::new(None) });
+
+ impl Drop for S1 {
+ fn drop(&mut self) {
+ let S1(ref tx) = *self;
+ unsafe {
+ let _ = K2.try_with(|s| *s.get() = Some(Foo(tx.clone())));
+ }
+ }
+ }
+
+ let (tx, rx) = channel();
+ let _t = thread::spawn(move || unsafe {
+ let mut tx = Some(tx);
+ K1.with(|s| *s.get() = Some(S1(tx.take().unwrap())));
+ });
+ rx.recv().unwrap();
+}
#[doc(hidden)]
pub use self::local::statik::Key as __StaticLocalKeyInner;
+// This is only used to make thread locals with `const { .. }` initialization
+// expressions unstable. If and/or when that syntax is stabilized with thread
+// locals this will simply be removed.
+#[doc(hidden)]
+#[unstable(feature = "thread_local_const_init", issue = "84223")]
+pub const fn require_unstable_const_init_thread_local() {}
+
////////////////////////////////////////////////////////////////////////////////
// Builder
////////////////////////////////////////////////////////////////////////////////
#[stable(feature = "std_debug", since = "1.16.0")]
impl<T> fmt::Debug for JoinHandle<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.pad("JoinHandle { .. }")
+ f.debug_struct("JoinHandle").finish_non_exhaustive()
}
}
//! Benchmarking module.
-pub use std::hint::black_box;
-
use super::{
event::CompletedTest,
options::BenchMode,
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
+/// An identity function that *__hints__* to the compiler to be maximally pessimistic about what
+/// `black_box` could do.
+///
+/// See [`std::hint::black_box`] for details.
+#[inline(always)]
+pub fn black_box<T>(dummy: T) -> T {
+ std::hint::black_box(dummy)
+}
+
/// Manager of the benchmarking runs.
///
/// This is fed into functions marked with `#[bench]` to allow for
#![feature(rustc_private)]
#![feature(nll)]
#![feature(available_concurrency)]
+#![feature(bench_black_box)]
#![feature(internal_output_capture)]
#![feature(panic_unwind)]
#![feature(staged_api)]
let mut tarball = Tarball::new(builder, "rust-docs", &host.triple);
tarball.set_product_name("Rust Documentation");
- tarball.add_dir(&builder.doc_out(host), dest);
+ tarball.add_bulk_dir(&builder.doc_out(host), dest);
tarball.add_file(&builder.src.join("src/doc/robots.txt"), dest, 0o644);
Some(tarball.generate())
}
let mut tarball = Tarball::new(builder, "rustc-docs", &host.triple);
tarball.set_product_name("Rustc Documentation");
- tarball.add_dir(&builder.compiler_doc_out(host), "share/doc/rust/html/rustc");
+ tarball.add_bulk_dir(&builder.compiler_doc_out(host), "share/doc/rust/html/rustc");
Some(tarball.generate())
}
}
// create correct links between crates because rustdoc depends on the
// existence of the output directories to know if it should be a local
// or remote link.
- //
- // There's also a mild hack here where we build the first crate in this
- // list, core, twice. This is currently necessary to make sure that
- // cargo's cached rustc/rustdoc versions are up to date which means
- // cargo won't delete the out_dir we create for the stampfile.
- // Essentially any crate could go into the first slot here as it's
- // output directory will be deleted by us (as cargo will purge the stamp
- // file during the first slot's run), and core is relatively fast to
- // build so works OK to fill this 'dummy' slot.
let krates = ["core", "alloc", "std", "proc_macro", "test"];
for krate in &krates {
run_cargo_rustdoc_for(krate);
// Look for library/std, library/core etc in the `x.py doc` arguments and
// open the corresponding rendered docs.
for path in builder.paths.iter().map(components_simplified) {
- if path.get(0) == Some(&"library") {
- let requested_crate = &path[1];
- if krates.contains(&requested_crate) {
- let index = out.join(requested_crate).join("index.html");
- open(builder, &index);
- }
+ let requested_crate = if path.get(0) == Some(&"library") {
+ &path[1]
+ } else if !path.is_empty() {
+ &path[0]
+ } else {
+ continue;
+ };
+ if krates.contains(&requested_crate) {
+ let index = out.join(requested_crate).join("index.html");
+ open(builder, &index);
}
}
}
use crate::builder::{Builder, RunConfig, ShouldRun, Step};
use crate::config::{Config, TargetSelection};
+#[cfg(target_os = "illumos")]
+const SHELL: &str = "bash";
+#[cfg(not(target_os = "illumos"))]
+const SHELL: &str = "sh";
+
fn install_sh(
builder: &Builder<'_>,
package: &str,
let empty_dir = builder.out.join("tmp/empty_dir");
t!(fs::create_dir_all(&empty_dir));
- let mut cmd = Command::new("sh");
+ let mut cmd = Command::new(SHELL);
cmd.current_dir(&empty_dir)
.arg(sanitize_sh(&tarball.decompressed_output().join("install.sh")))
.arg(format!("--prefix={}", prepare_dir(prefix)))
"x86_64-unknown-linux-gnu" => {
common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])
}
+ "x86_64-unknown-linux-musl" => {
+ common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])
+ }
_ => Vec::new(),
}
}
temp_dir: PathBuf,
image_dir: PathBuf,
overlay_dir: PathBuf,
+ bulk_dirs: Vec<PathBuf>,
include_target_in_component_name: bool,
is_preview: bool,
temp_dir,
image_dir,
overlay_dir,
+ bulk_dirs: Vec::new(),
include_target_in_component_name: false,
is_preview: false,
self.builder.cp_r(src.as_ref(), &dest);
}
+ pub(crate) fn add_bulk_dir(&mut self, src: impl AsRef<Path>, dest: impl AsRef<Path>) {
+ self.bulk_dirs.push(dest.as_ref().to_path_buf());
+ self.add_dir(src, dest);
+ }
+
pub(crate) fn generate(self) -> GeneratedTarball {
let mut component_name = self.component.clone();
if self.is_preview {
.arg("--image-dir")
.arg(&this.image_dir)
.arg(format!("--component-name={}", &component_name));
+
+ if let Some((dir, dirs)) = this.bulk_dirs.split_first() {
+ let mut arg = dir.as_os_str().to_os_string();
+ for dir in dirs {
+ arg.push(",");
+ arg.push(dir);
+ }
+ cmd.arg("--bulk-dirs").arg(&arg);
+ }
+
this.non_bare_args(cmd);
})
}
CT_EXPAT_PATCH_ORDER="global"
CT_EXPAT_V_2_2=y
# CT_EXPAT_NO_VERSIONS is not set
-CT_EXPAT_VERSION="2.2.6"
+CT_EXPAT_VERSION="2.3.0"
CT_EXPAT_MIRRORS="http://downloads.sourceforge.net/project/expat/expat/${CT_EXPAT_VERSION}"
CT_EXPAT_ARCHIVE_FILENAME="@{pkg_name}-@{version}"
CT_EXPAT_ARCHIVE_DIRNAME="@{pkg_name}-@{version}"
# Download and build a single-file stress test benchmark on perf.rust-lang.org.
function pgo_perf_benchmark {
- local PERF=e095f5021bf01cf3800f50b3a9f14a9683eb3e4e
+ local PERF=9442def56a39d742bf27ebcc3e0614cf117e1bc2
local github_prefix=https://raw.githubusercontent.com/rust-lang/rustc-perf/$PERF
local name=$1
curl -o /tmp/$name.rs $github_prefix/collector/benchmarks/$name/src/lib.rs
- [JSON Output](json.md)
- [Tests](tests/index.md)
- [Platform Support](platform-support.md)
+- [Target Tier Policy](target-tier-policy.md)
- [Targets](targets/index.md)
- [Built-in Targets](targets/built-in.md)
- [Custom Targets](targets/custom.md)
</style>
Support for different platforms are organized into three tiers, each with a
-different set of guarantees.
+different set of guarantees. For more information on the policies for targets
+at each tier, see the [Target Tier Policy](target-tier-policy.md).
Platforms are identified by their "target triple" which is the string to
inform the compiler what kind of output should be produced. The columns in the
`i386-apple-ios` | ✓ | | 32-bit x86 iOS
`i686-apple-darwin` | ✓ | ✓ | 32-bit macOS (10.7+, Lion+)
`i686-pc-windows-msvc` | ✓ | | 32-bit Windows XP support
-`i686-unknown-uefi` | ? | | 32-bit UEFI
+`i686-unknown-uefi` | * | | 32-bit UEFI
`i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku
`i686-unknown-netbsd` | ✓ | ✓ | NetBSD/i386 with SSE2
`i686-unknown-openbsd` | ✓ | ✓ | 32-bit OpenBSD
`x86_64-unknown-none-hermitkernel` | ? | | HermitCore kernel
`x86_64-unknown-l4re-uclibc` | ? | |
`x86_64-unknown-openbsd` | ✓ | ✓ | 64-bit OpenBSD
-`x86_64-unknown-uefi` | ? | |
+`x86_64-unknown-uefi` | * | | 64-bit UEFI
`x86_64-uwp-windows-gnu` | ✓ | |
`x86_64-uwp-windows-msvc` | ✓ | |
`x86_64-wrs-vxworks` | ? | |
--- /dev/null
+# Target Tier Policy
+
+Rust provides three tiers of target support:
+
+- Rust provides no guarantees about tier 3 targets; they exist in the codebase,
+ but may or may not build.
+- Rust's continuous integration checks that tier 2 targets will always build,
+ but they may or may not pass tests.
+- Rust's continuous integration checks that tier 1 targets will always build
+ and pass tests.
+
+Adding a new tier 3 target imposes minimal requirements; we focus primarily on
+avoiding disruption to other ongoing Rust development.
+
+Tier 2 and tier 1 targets place work on Rust project developers as a whole, to
+avoid breaking the target. The broader Rust community may also feel more
+inclined to support higher-tier targets in their crates (though they are not
+obligated to do so). Thus, these tiers require commensurate and ongoing efforts
+from the maintainers of the target, to demonstrate value and to minimize any
+disruptions to ongoing Rust development.
+
+This policy defines the requirements for accepting a proposed target at a given
+level of support.
+
+Each tier builds on all the requirements from the previous tier, unless
+overridden by a stronger requirement. Targets at tier 2 and tier 1 may also
+provide *host tools* (such as `rustc` and `cargo`); each of those tiers
+includes a set of supplementary requirements that must be met if supplying host
+tools for the target. A target at tier 2 or tier 1 is not required to supply
+host tools, but if it does, it must meet the corresponding additional
+requirements for host tools.
+
+The policy for each tier also documents the Rust governance teams that must
+approve the addition of any target at that tier. Those teams are responsible
+for reviewing and evaluating the target, based on these requirements and their
+own judgment. Those teams may apply additional requirements, including
+subjective requirements, such as to deal with issues not foreseen by this
+policy. (Such requirements may subsequently motivate additions to this policy.)
+
+While these criteria attempt to document the policy, that policy still involves
+human judgment. Targets must fulfill the spirit of the requirements as well, as
+determined by the judgment of the approving teams. Reviewers and team members
+evaluating targets and target-specific patches should always use their own best
+judgment regarding the quality of work, and the suitability of a target for the
+Rust project. Neither this policy nor any decisions made regarding targets
+shall create any binding agreement or estoppel by any party.
+
+Before filing an issue or pull request (PR) to introduce or promote a target,
+the target should already meet the corresponding tier requirements. This does
+not preclude an existing target's maintainers using issues (on the Rust
+repository or otherwise) to track requirements that have not yet been met, as
+appropriate; however, before officially proposing the introduction or promotion
+of a target, it should meet all of the necessary requirements. A target
+proposal is encouraged to quote the corresponding requirements verbatim as part
+of explaining how the target meets those requirements.
+
+For a list of all supported targets and their corresponding tiers ("tier 3",
+"tier 2", "tier 2 with host tools", "tier 1", or "tier 1 with host tools"), see
+[platform support](platform-support.md).
+
+Note that a target must have already received approval for the next lower tier,
+and spent a reasonable amount of time at that tier, before making a proposal
+for promotion to the next higher tier; this is true even if a target meets the
+requirements for several tiers at once. This policy leaves the precise
+interpretation of "reasonable amount of time" up to the approving teams; those
+teams may scale the amount of time required based on their confidence in the
+target and its demonstrated track record at its current tier. At a minimum,
+multiple stable releases of Rust should typically occur between promotions of a
+target.
+
+The availability or tier of a target in stable Rust is not a hard stability
+guarantee about the future availability or tier of that target. Higher-level
+target tiers are an increasing commitment to the support of a target, and we
+will take that commitment and potential disruptions into account when
+evaluating the potential demotion or removal of a target that has been part of
+a stable release. The promotion or demotion of a target will not generally
+affect existing stable releases, only current development and future releases.
+
+In this policy, the words "must" and "must not" specify absolute requirements
+that a target must meet to qualify for a tier. The words "should" and "should
+not" specify requirements that apply in almost all cases, but for which the
+approving teams may grant an exception for good reason. The word "may"
+indicates something entirely optional, and does not indicate guidance or
+recommendations. This language is based on [IETF RFC
+2119](https://tools.ietf.org/html/rfc2119).
+
+## Tier 3 target policy
+
+At this tier, the Rust project provides no official support for a target, so we
+place minimal requirements on the introduction of targets.
+
+A proposed new tier 3 target must be reviewed and approved by a member of the
+compiler team based on these requirements. The reviewer may choose to gauge
+broader compiler team consensus via a Major Change Proposal (MCP).
+
+A proposed target or target-specific patch that substantially changes code
+shared with other targets (not just target-specific code) must be reviewed and
+approved by the appropriate team for that shared code before acceptance.
+
+- A tier 3 target must have a designated developer or developers (the "target
+ maintainers") on record to be CCed when issues arise regarding the target.
+ (The mechanism to track and CC such developers may evolve over time.)
+- Targets must use naming consistent with any existing targets; for instance, a
+ target for the same CPU or OS as an existing Rust target should use the same
+ name for that CPU or OS. Targets should normally use the same names and
+ naming conventions as used elsewhere in the broader ecosystem beyond Rust
+ (such as in other toolchains), unless they have a very good reason to
+ diverge. Changing the name of a target can be highly disruptive, especially
+ once the target reaches a higher tier, so getting the name right is important
+ even for a tier 3 target.
+ - Target names should not introduce undue confusion or ambiguity unless
+ absolutely necessary to maintain ecosystem compatibility. For example, if
+ the name of the target makes people extremely likely to form incorrect
+ beliefs about what it targets, the name should be changed or augmented to
+ disambiguate it.
+- Tier 3 targets may have unusual requirements to build or use, but must not
+ create legal issues or impose onerous legal terms for the Rust project or for
+ Rust developers or users.
+ - The target must not introduce license incompatibilities.
+ - Anything added to the Rust repository must be under the standard Rust
+ license (`MIT OR Apache-2.0`).
+ - The target must not cause the Rust tools or libraries built for any other
+ host (even when supporting cross-compilation to the target) to depend
+ on any new dependency less permissive than the Rust licensing policy. This
+ applies whether the dependency is a Rust crate that would require adding
+ new license exceptions (as specified by the `tidy` tool in the
+ rust-lang/rust repository), or whether the dependency is a native library
+ or binary. In other words, the introduction of the target must not cause a
+ user installing or running a version of Rust or the Rust tools to be
+ subject to any new license requirements.
+ - If the target supports building host tools (such as `rustc` or `cargo`),
+ those host tools must not depend on proprietary (non-FOSS) libraries, other
+ than ordinary runtime libraries supplied by the platform and commonly used
+ by other binaries built for the target. For instance, `rustc` built for the
+ target may depend on a common proprietary C runtime library or console
+ output library, but must not depend on a proprietary code generation
+ library or code optimization library. Rust's license permits such
+ combinations, but the Rust project has no interest in maintaining such
+ combinations within the scope of Rust itself, even at tier 3.
+ - Targets should not require proprietary (non-FOSS) components to link a
+ functional binary or library.
+ - "onerous" here is an intentionally subjective term. At a minimum, "onerous"
+ legal/licensing terms include but are *not* limited to: non-disclosure
+ requirements, non-compete requirements, contributor license agreements
+ (CLAs) or equivalent, "non-commercial"/"research-only"/etc terms,
+ requirements conditional on the employer or employment of any particular
+ Rust developers, revocable terms, any requirements that create liability
+ for the Rust project or its developers or users, or any requirements that
+ adversely affect the livelihood or prospects of the Rust project or its
+ developers or users.
+- Neither this policy nor any decisions made regarding targets shall create any
+ binding agreement or estoppel by any party. If any member of an approving
+ Rust team serves as one of the maintainers of a target, or has any legal or
+ employment requirement (explicit or implicit) that might affect their
+ decisions regarding a target, they must recuse themselves from any approval
+ decisions regarding the target's tier status, though they may otherwise
+ participate in discussions.
+ - This requirement does not prevent part or all of this policy from being
+ cited in an explicit contract or work agreement (e.g. to implement or
+ maintain support for a target). This requirement exists to ensure that a
+ developer or team responsible for reviewing and approving a target does not
+ face any legal threats or obligations that would prevent them from freely
+ exercising their judgment in such approval, even if such judgment involves
+ subjective matters or goes beyond the letter of these requirements.
+- Tier 3 targets should attempt to implement as much of the standard libraries
+ as possible and appropriate (`core` for most targets, `alloc` for targets
+ that can support dynamic memory allocation, `std` for targets with an
+ operating system or equivalent layer of system-provided functionality), but
+ may leave some code unimplemented (either unavailable or stubbed out as
+ appropriate), whether because the target makes it impossible to implement or
+ challenging to implement. The authors of pull requests are not obligated to
+ avoid calling any portions of the standard library on the basis of a tier 3
+ target not implementing those portions.
+- The target must provide documentation for the Rust community explaining how
+ to build for the target, using cross-compilation if possible. If the target
+ supports running tests (even if they do not pass), the documentation must
+ explain how to run tests for the target, using emulation if possible or
+ dedicated hardware if necessary.
+- Tier 3 targets must not impose burden on the authors of pull requests, or
+ other developers in the community, to maintain the target. In particular,
+ do not post comments (automated or manual) on a PR that derail or suggest a
+ block on the PR based on a tier 3 target. Do not send automated messages or
+ notifications (via any medium, including via `@`) to a PR author or others
+ involved with a PR regarding a tier 3 target, unless they have opted into
+ such messages.
+ - Backlinks such as those generated by the issue/PR tracker when linking to
+ an issue or PR are not considered a violation of this policy, within
+ reason. However, such messages (even on a separate repository) must not
+ generate notifications to anyone involved with a PR who has not requested
+ such notifications.
+- Patches adding or updating tier 3 targets must not break any existing tier 2
+ or tier 1 target, and must not knowingly break another tier 3 target without
+ approval of either the compiler team or the maintainers of the other tier 3
+ target.
+ - In particular, this may come up when working on closely related targets,
+ such as variations of the same architecture with different features. Avoid
+ introducing unconditional uses of features that another variation of the
+ target may not have; use conditional compilation or runtime detection, as
+ appropriate, to let each target run code supported by that target.
+
+If a tier 3 target stops meeting these requirements, or the target maintainers
+no longer have interest or time, or the target shows no signs of activity and
+has not built for some time, or removing the target would improve the quality
+of the Rust codebase, we may post a PR to remove it; any such PR will be CCed
+to the target maintainers (and potentially other people who have previously
+worked on the target), to check potential interest in improving the situation.
+
+## Tier 2 target policy
+
+At this tier, the Rust project guarantees that a target builds, and will reject
+patches that fail to build on a target. Thus, we place requirements that ensure
+the target will not block forward progress of the Rust project.
+
+A proposed new tier 2 target must be reviewed and approved by the compiler team
+based on these requirements. Such review and approval may occur via a Major
+Change Proposal (MCP).
+
+In addition, the infrastructure team must approve the integration of the target
+into Continuous Integration (CI), and the tier 2 CI-related requirements. This
+review and approval may take place in a PR adding the target to CI, or simply
+by an infrastructure team member reporting the outcome of a team discussion.
+
+- A tier 2 target must have value to people other than its maintainers. (It may
+ still be a niche target, but it must not be exclusively useful for an
+ inherently closed group.)
+- A tier 2 target must have a designated team of developers (the "target
+ maintainers") available to consult on target-specific build-breaking issues,
+ or if necessary to develop target-specific language or library implementation
+ details. This team must have at least 2 developers.
+ - The target maintainers should not only fix target-specific issues, but
+ should use any such issue as an opportunity to educate the Rust community
+ about portability to their target, and enhance documentation of the target.
+- The target must not place undue burden on Rust developers not specifically
+ concerned with that target. Rust developers are expected to not gratuitously
+ break a tier 2 target, but are not expected to become experts in every tier 2
+ target, and are not expected to provide target-specific implementations for
+ every tier 2 target.
+- The target must provide documentation for the Rust community explaining how
+ to build for the target using cross-compilation, and explaining how to run
+ tests for the target. If at all possible, this documentation should show how
+ to run Rust programs and tests for the target using emulation, to allow
+ anyone to do so. If the target cannot be feasibly emulated, the documentation
+ should explain how to obtain and work with physical hardware, cloud systems,
+ or equivalent.
+- The target must document its baseline expectations for the features or
+ versions of CPUs, operating systems, libraries, runtime environments, and
+ similar.
+- If introducing a new tier 2 or higher target that is identical to an existing
+ Rust target except for the baseline expectations for the features or versions
+ of CPUs, operating systems, libraries, runtime environments, and similar,
+ then the proposed target must document to the satisfaction of the approving
+ teams why the specific difference in baseline expectations provides
+ sufficient value to justify a separate target.
+ - Note that in some cases, based on the usage of existing targets within the
+ Rust community, Rust developers or a target's maintainers may wish to
+ modify the baseline expectations of a target, or split an existing target
+ into multiple targets with different baseline expectations. A proposal to
+ do so will be treated similarly to the analogous promotion, demotion, or
+ removal of a target, according to this policy, with the same team approvals
+ required.
+ - For instance, if an OS version has become obsolete and unsupported, a
+ target for that OS may raise its baseline expectations for OS version
+ (treated as though removing a target corresponding to the older
+ versions), or a target for that OS may split out support for older OS
+ versions into a lower-tier target (treated as though demoting a target
+ corresponding to the older versions, and requiring justification for a
+ new target at a lower tier for the older OS versions).
+- Tier 2 targets must not leave any significant portions of `core` or the
+ standard library unimplemented or stubbed out, unless they cannot possibly be
+ supported on the target.
+ - The right approach to handling a missing feature from a target may depend
+ on whether the target seems likely to develop the feature in the future. In
+ some cases, a target may be co-developed along with Rust support, and Rust
+ may gain new features on the target as that target gains the capabilities
+ to support those features.
+ - As an exception, a target identical to an existing tier 1 target except for
+ lower baseline expectations for the OS, CPU, or similar, may propose to
+ qualify as tier 2 (but not higher) without support for `std` if the target
+ will primarily be used in `no_std` applications, to reduce the support
+ burden for the standard library. In this case, evaluation of the proposed
+ target's value will take this limitation into account.
+- The code generation backend for the target should not have deficiencies that
+ invalidate Rust safety properties, as evaluated by the Rust compiler team.
+ (This requirement does not apply to arbitrary security enhancements or
+ mitigations provided by code generation backends, only to those properties
+ needed to ensure safe Rust code cannot cause undefined behavior or other
+ unsoundness.) If this requirement does not hold, the target must clearly and
+ prominently document any such limitations as part of the target's entry in
+ the target tier list, and ideally also via a failing test in the testsuite.
+ The Rust compiler team must be satisfied with the balance between these
+ limitations and the difficulty of implementing the necessary features.
+ - For example, if Rust relies on a specific code generation feature to ensure
+ that safe code cannot overflow the stack, the code generation for the
+ target should support that feature.
+ - If the Rust compiler introduces new safety properties (such as via new
+ capabilities of a compiler backend), the Rust compiler team will determine
+ if they consider those new safety properties a best-effort improvement for
+ specific targets, or a required property for all Rust targets. In the
+ latter case, the compiler team may require the maintainers of existing
+ targets to either implement and confirm support for the property or update
+ the target tier list with documentation of the missing property.
+- If the target supports C code, and the target has an interoperable calling
+ convention for C code, the Rust target must support that C calling convention
+ for the platform via `extern "C"`. The C calling convention does not need to
+ be the default Rust calling convention for the target, however.
+- The target must build reliably in CI, for all components that Rust's CI
+ considers mandatory.
+- The approving teams may additionally require that a subset of tests pass in
+ CI, such as enough to build a functional "hello world" program, `./x.py test
+ --no-run`, or equivalent "smoke tests". In particular, this requirement may
+ apply if the target builds host tools, or if the tests in question provide
+ substantial value via early detection of critical problems.
+- Building the target in CI must not take substantially longer than the current
+ slowest target in CI, and should not substantially raise the maintenance
+ burden of the CI infrastructure. This requirement is subjective, to be
+ evaluated by the infrastructure team, and will take the community importance
+ of the target into account.
+- Tier 2 targets should, if at all possible, support cross-compiling. Tier 2
+ targets should not require using the target as the host for builds, even if
+ the target supports host tools.
+- In addition to the legal requirements for all targets (specified in the tier
+ 3 requirements), because a tier 2 target typically involves the Rust project
+ building and supplying various compiled binaries, incorporating the target
+ and redistributing any resulting compiled binaries (e.g. built libraries,
+ host tools if any) must not impose any onerous license requirements on any
+ members of the Rust project, including infrastructure team members and those
+ operating CI systems. This is a subjective requirement, to be evaluated by
+ the approving teams.
+ - As an exception to this, if the target's primary purpose is to build
+ components for a Free and Open Source Software (FOSS) project licensed
+ under "copyleft" terms (terms which require licensing other code under
+ compatible FOSS terms), such as kernel modules or plugins, then the
+ standard libraries for the target may potentially be subject to copyleft
+ terms, as long as such terms are satisfied by Rust's existing practices of
+ providing full corresponding source code. Note that anything added to the
+ Rust repository itself must still use Rust's standard license terms.
+- Tier 2 targets must not impose burden on the authors of pull requests, or
+ other developers in the community, to ensure that tests pass for the target.
+ In particular, do not post comments (automated or manual) on a PR that derail
+ or suggest a block on the PR based on tests failing for the target. Do not
+ send automated messages or notifications (via any medium, including via `@`)
+ to a PR author or others involved with a PR regarding the PR breaking tests
+ on a tier 2 target, unless they have opted into such messages.
+ - Backlinks such as those generated by the issue/PR tracker when linking to
+ an issue or PR are not considered a violation of this policy, within
+ reason. However, such messages (even on a separate repository) must not
+ generate notifications to anyone involved with a PR who has not requested
+ such notifications.
+- The target maintainers should regularly run the testsuite for the target, and
+ should fix any test failures in a reasonably timely fashion.
+- All requirements for tier 3 apply.
+
+A tier 2 target may be demoted or removed if it no longer meets these
+requirements. Any proposal for demotion or removal will be CCed to the target
+maintainers, and will be communicated widely to the Rust community before being
+dropped from a stable release. (The amount of time between such communication
+and the next stable release may depend on the nature and severity of the failed
+requirement, the timing of its discovery, whether the target has been part of a
+stable release yet, and whether the demotion or removal can be a planned and
+scheduled action.)
+
+In some circumstances, especially if the target maintainers do not respond in a
+timely fashion, Rust teams may land pull requests that temporarily disable some
+targets in the nightly compiler, in order to implement a feature not yet
+supported by those targets. (As an example, this happened when introducing the
+128-bit types `u128` and `i128`.) Such a pull request will include notification
+and coordination with the maintainers of such targets, and will ideally happen
+towards the beginning of a new development cycle to give maintainers time to
+update their targets. The maintainers of such targets will then be expected to
+implement the corresponding target-specific support in order to re-enable the
+target. If the maintainers of such targets cannot provide such support in time
+for the next stable release, this may result in demoting or removing the
+targets.
+
+### Tier 2 with host tools
+
+Some tier 2 targets may additionally have binaries built to run on them as a
+host (such as `rustc` and `cargo`). This allows the target to be used as a
+development platform, not just a compilation target.
+
+A proposed new tier 2 target with host tools must be reviewed and approved by
+the compiler team based on these requirements. Such review and approval may
+occur via a Major Change Proposal (MCP).
+
+In addition, the infrastructure team must approve the integration of the
+target's host tools into Continuous Integration (CI), and the CI-related
+requirements for host tools. This review and approval may take place in a PR
+adding the target's host tools to CI, or simply by an infrastructure team
+member reporting the outcome of a team discussion.
+
+- Depending on the target, its capabilities, its performance, and the
+ likelihood of use for any given tool, the host tools provided for a tier 2
+ target may include only `rustc` and `cargo`, or may include additional tools
+ such as `clippy` and `rustfmt`.
+- Approval of host tools will take into account the additional time required to
+ build the host tools, and the substantial additional storage required for the
+ host tools.
+- The host tools must have direct value to people other than the target's
+ maintainers. (It may still be a niche target, but the host tools must not be
+ exclusively useful for an inherently closed group.) This requirement will be
+ evaluated independently from the corresponding tier 2 requirement.
+ - The requirement to provide "direct value" means that it does not suffice to
+ argue that having host tools will help the target's maintainers more easily
+ provide the target to others. The tools themselves must provide value to
+ others.
+- There must be a reasonable expectation that the host tools will be used, for
+ purposes other than to prove that they can be used.
+- The host tools must build and run reliably in CI (for all components that
+ Rust's CI considers mandatory), though they may or may not pass tests.
+- Building host tools for the target must not take substantially longer than
+ building host tools for other targets, and should not substantially raise the
+ maintenance burden of the CI infrastructure.
+- The host tools must provide a substantively similar experience as on other
+ targets, subject to reasonable target limitations.
+ - Adding a substantively different interface to an existing tool, or a
+ target-specific interface to the functionality of an existing tool,
+ requires design and implementation approval (e.g. RFC/MCP) from the
+ appropriate approving teams for that tool.
+ - Such an interface should have a design that could potentially work for
+ other targets with similar properties.
+ - This should happen separately from the review and approval of the target,
+ to simplify the target review and approval processes, and to simplify the
+ review and approval processes for the proposed new interface.
+ - By way of example, a target that runs within a sandbox may need to modify
+ the handling of files, tool invocation, and similar to meet the
+ expectations and conventions of the sandbox, but must not introduce a
+ separate "sandboxed compilation" interface separate from the CLI interface
+ without going through the normal approval process for such an interface.
+ Such an interface should take into account potential other targets with
+ similar sandboxes.
+- If the host tools for the platform would normally be expected to be signed or
+ equivalent (e.g. if running unsigned binaries or similar involves a
+ "developer mode" or an additional prompt), it must be possible for the Rust
+ project's automated builds to apply the appropriate signature process,
+ without any manual intervention by either Rust developers, target
+ maintainers, or a third party. This process must meet the approval of the
+ infrastructure team.
+ - This process may require one-time or semi-regular manual steps by the
+ infrastructure team, such as registration or renewal of a signing key. Any
+ such manual process must meet the approval of the infrastructure team.
+ - This process may require the execution of a legal agreement with the
+ signature provider. Such a legal agreement may be revocable, and may
+ potentially require a nominal fee, but must not be otherwise onerous. Any
+ such legal agreement must meet the approval of the infrastructure team.
+ (The infrastructure team is not expected or required to sign binding legal
+ agreements on behalf of the Rust project; this review and approval exists
+ to ensure no terms are onerous or cause problems for infrastructure,
+ especially if such terms may impose requirements or obligations on people
+ who have access to target-specific infrastructure.)
+ - Changes to this process, or to any legal agreements involved, may
+ cause a target to stop meeting this requirement.
+ - This process involved must be available under substantially similar
+ non-onerous terms to the general public. Making it available exclusively to
+ the Rust project does not suffice.
+ - This requirement exists to ensure that Rust builds, including nightly
+ builds, can meet the necessary requirements to allow users to smoothly run
+ the host tools.
+- Providing host tools does not exempt a target from requirements to support
+ cross-compilation if at all possible.
+- All requirements for tier 2 apply.
+
+A target may be promoted directly from tier 3 to tier 2 with host tools if it
+meets all the necessary requirements, but doing so may introduce substantial
+additional complexity. If in doubt, the target should qualify for tier 2
+without host tools first.
+
+## Tier 1 target policy
+
+At this tier, the Rust project guarantees that a target builds and passes all
+tests, and will reject patches that fail to build or pass the testsuite on a
+target. We hold tier 1 targets to our highest standard of requirements.
+
+A proposed new tier 1 target must be reviewed and approved by the compiler team
+based on these requirements. In addition, the release team must approve the
+viability and value of supporting the target. For a tier 1 target, this will
+typically take place via a full RFC proposing the target, to be jointly
+reviewed and approved by the compiler team and release team.
+
+In addition, the infrastructure team must approve the integration of the target
+into Continuous Integration (CI), and the tier 1 CI-related requirements. This
+review and approval may take place in a PR adding the target to CI, by an
+infrastructure team member reporting the outcome of a team discussion, or by
+including the infrastructure team in the RFC proposing the target.
+
+- Tier 1 targets must have substantial, widespread interest within the
+ developer community, and must serve the ongoing needs of multiple production
+ users of Rust across multiple organizations or projects. These requirements
+ are subjective, and determined by consensus of the approving teams. A tier 1
+ target may be demoted or removed if it becomes obsolete or no longer meets
+ this requirement.
+- The target maintainer team must include at least 3 developers.
+- The target must build and pass tests reliably in CI, for all components that
+ Rust's CI considers mandatory.
+ - The target must not disable an excessive number of tests or pieces of tests
+ in the testsuite in order to do so. This is a subjective requirement.
+ - If the target does not have host tools support, or if the target has low
+ performance, the infrastructure team may choose to have CI cross-compile
+ the testsuite from another platform, and then run the compiled tests
+ either natively or via accurate emulation. However, the approving teams may
+ take such performance considerations into account when determining the
+ viability of the target or of its host tools.
+- The target must provide as much of the Rust standard library as is feasible
+ and appropriate to provide. For instance, if the target can support dynamic
+ memory allocation, it must provide an implementation of `alloc` and the
+ associated data structures.
+- Building the target and running the testsuite for the target must not take
+ substantially longer than other targets, and should not substantially raise
+ the maintenance burden of the CI infrastructure.
+ - In particular, if building the target takes a reasonable amount of time,
+ but the target cannot run the testsuite in a timely fashion due to low
+ performance of either native code or accurate emulation, that alone may
+ prevent the target from qualifying as tier 1.
+- If running the testsuite requires additional infrastructure (such as physical
+ systems running the target), the target maintainers must arrange to provide
+ such resources to the Rust project, to the satisfaction and approval of the
+ Rust infrastructure team.
+ - Such resources may be provided via cloud systems, via emulation, or via
+ physical hardware.
+ - If the target requires the use of emulation to meet any of the tier
+ requirements, the approving teams for those requirements must have high
+ confidence in the accuracy of the emulation, such that discrepancies
+ between emulation and native operation that affect test results will
+ constitute a high-priority bug in either the emulation or the
+ implementation of the target.
+ - If it is not possible to run the target via emulation, these resources must
+ additionally be sufficient for the Rust infrastructure team to make them
+ available for access by Rust team members, for the purposes of development
+ and testing. (Note that the responsibility for doing target-specific
+ development to keep the target well maintained remains with the target
+ maintainers. This requirement ensures that it is possible for other
+ Rust developers to test the target, but does not obligate other Rust
+ developers to make target-specific fixes.)
+ - Resources provided for CI and similar infrastructure must be available for
+ continuous exclusive use by the Rust project. Resources provided
+ for access by Rust team members for development and testing must be
+ available on an exclusive basis when in use, but need not be available on a
+ continuous basis when not in use.
+- Tier 1 targets must not have a hard requirement for signed, verified, or
+ otherwise "approved" binaries. Developers must be able to build, run, and
+ test binaries for the target on systems they control, or provide such
+ binaries for others to run. (Doing so may require enabling some appropriate
+ "developer mode" on such systems, but must not require the payment of any
+ additional fee or other consideration, or agreement to any onerous legal
+ agreements.)
+ - The Rust project may decide to supply appropriately signed binaries if
+ doing so provides a smoother experience for developers using the target,
+ and a tier 2 target with host tools already requires providing appropriate
+ mechanisms that enable our infrastructure to provide such signed binaries.
+ However, this additional tier 1 requirement ensures that Rust developers
+ can develop and test Rust software for the target (including Rust itself),
+ and that development or testing for the target is not limited.
+- All requirements for tier 2 apply.
+
+A tier 1 target may be demoted if it no longer meets these requirements but
+still meets the requirements for a lower tier. Any proposal for demotion of a
+tier 1 target requires a full RFC process, with approval by the compiler and
+release teams. Any such proposal will be communicated widely to the Rust
+community, both when initially proposed and before being dropped from a stable
+release. A tier 1 target is highly unlikely to be directly removed without
+first being demoted to tier 2 or tier 3. (The amount of time between such
+communication and the next stable release may depend on the nature and severity
+of the failed requirement, the timing of its discovery, whether the target has
+been part of a stable release yet, and whether the demotion or removal can be a
+planned and scheduled action.)
+
+Raising the baseline expectations of a tier 1 target (such as the minimum CPU
+features or OS version required) requires the approval of the compiler and
+release teams, and should be widely communicated as well, but does not
+necessarily require a full RFC.
+
+### Tier 1 with host tools
+
+Some tier 1 targets may additionally have binaries built to run on them as a
+host (such as `rustc` and `cargo`). This allows the target to be used as a
+development platform, not just a compilation target.
+
+A proposed new tier 1 target with host tools must be reviewed and approved by
+the compiler team based on these requirements. In addition, the release team
+must approve the viability and value of supporting host tools for the target.
+For a tier 1 target, this will typically take place via a full RFC proposing
+the target, to be jointly reviewed and approved by the compiler team and
+release team.
+
+In addition, the infrastructure team must approve the integration of the
+target's host tools into Continuous Integration (CI), and the CI-related
+requirements for host tools. This review and approval may take place in a PR
+adding the target's host tools to CI, by an infrastructure team member
+reporting the outcome of a team discussion, or by including the infrastructure
+team in the RFC proposing the target.
+
+- Tier 1 targets with host tools should typically include all of the additional
+ tools such as `clippy` and `rustfmt`, unless there is a target-specific
+ reason why a tool cannot possibly make sense for the target.
+ - Unlike with tier 2, for tier 1 we will not exclude specific tools on the
+ sole basis of them being less likely to be used; rather, we'll take that
+ into account when considering whether the target should be at tier 1 with
+ host tools. In general, on any tier 1 target with host tools, people
+ should be able to expect to find and install all the same components that
+ they would for any other tier 1 target with host tools.
+- Approval of host tools will take into account the additional time required to
+ build the host tools, and the substantial additional storage required for the
+ host tools.
+- Host tools for the target must have substantial, widespread interest within
+ the developer community, and must serve the ongoing needs of multiple
+ production users of Rust across multiple organizations or projects. These
+ requirements are subjective, and determined by consensus of the approving
+ teams. This requirement will be evaluated independently from the
+ corresponding tier 1 requirement; it is possible for a target to have
+ sufficient interest for cross-compilation, but not have sufficient interest
+ for native compilation. The host tools may be dropped if they no longer meet
+ this requirement, even if the target otherwise qualifies as tier 1.
+- The host tools must build, run, and pass tests reliably in CI, for all
+ components that Rust's CI considers mandatory.
+ - The target must not disable an excessive number of tests or pieces of tests
+ in the testsuite in order to do so. This is a subjective requirement.
+- Building the host tools and running the testsuite for the host tools must not
+ take substantially longer than other targets, and should not substantially raise
+ the maintenance burden of the CI infrastructure.
+ - In particular, if building the target's host tools takes a reasonable
+ amount of time, but the target cannot run the testsuite in a timely fashion
+ due to low performance of either native code or accurate emulation, that
+ alone may prevent the target from qualifying as tier 1 with host tools.
+- Providing host tools does not exempt a target from requirements to support
+ cross-compilation if at all possible.
+- All requirements for tier 2 targets with host tools apply.
+- All requirements for tier 1 apply.
+
+A target seeking promotion to tier 1 with host tools should typically either be
+tier 2 with host tools or tier 1 without host tools, to reduce the number of
+requirements to simultaneously review and approve.
+
+In addition to the general process for demoting a tier 1 target, a tier 1
+target with host tools may be demoted (including having its host tools dropped,
+or being demoted to tier 2 with host tools) if it no longer meets these
+requirements but still meets the requirements for a lower tier. Any proposal
+for demotion of a tier 1 target (with or without host tools) requires a full
+RFC process, with approval by the compiler and release teams. Any such proposal
+will be communicated widely to the Rust community, both when initially proposed
+and before being dropped from a stable release.
which do not trigger a panic can be assured that this function is never
called. The language item's name is `eh_personality`.
-[unwind]: https://github.com/rust-lang/rust/blob/master/src/libpanic_unwind/gcc.rs
+[unwind]: https://github.com/rust-lang/rust/blob/master/library/panic_unwind/src/gcc.rs
The second function, `rust_begin_panic`, is also used by the failure mechanisms of the
compiler. When a panic happens, this controls the message that's displayed on
+++ /dev/null
-# `non_ascii_idents`
-
-The tracking issue for this feature is: [#55467]
-
-[#55467]: https://github.com/rust-lang/rust/issues/55467
-
-------------------------
-
-The `non_ascii_idents` feature adds support for non-ASCII identifiers.
-
-## Examples
-
-```rust
-#![feature(non_ascii_idents)]
-
-const ε: f64 = 0.00001f64;
-const Π: f64 = 3.14f64;
-```
-
-## Changes to the language reference
-
-> **<sup>Lexer:<sup>**\
-> IDENTIFIER :\
-> XID_start XID_continue<sup>\*</sup>\
-> | `_` XID_continue<sup>+</sup>
-
-An identifier is any nonempty Unicode string of the following form:
-
-Either
-
- * The first character has property [`XID_start`]
- * The remaining characters have property [`XID_continue`]
-
-Or
-
- * The first character is `_`
- * The identifier is more than one character, `_` alone is not an identifier
- * The remaining characters have property [`XID_continue`]
-
-that does _not_ occur in the set of [strict keywords].
-
-> **Note**: [`XID_start`] and [`XID_continue`] as character properties cover the
-> character ranges used to form the more familiar C and Java language-family
-> identifiers.
-
-[`XID_start`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Start%3A%5D&abb=on&g=&i=
-[`XID_continue`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Continue%3A%5D&abb=on&g=&i=
-[strict keywords]: ../../reference/keywords.md#strict-keywords
path = "lib.rs"
[dependencies]
-arrayvec = { version = "0.5.1", default-features = false }
+arrayvec = { version = "0.7", default-features = false }
pulldown-cmark = { version = "0.8", default-features = false }
minifier = "0.0.39"
rayon = { version = "0.3.0", package = "rustc-rayon" }
};
Some(Item {
- span: Span::dummy(),
name: None,
attrs: Default::default(),
visibility: Inherited,
def_id: self.cx.next_def_id(item_def_id.krate),
kind: box ImplItem(Impl {
+ span: Span::dummy(),
unsafety: hir::Unsafety::Normal,
generics: new_generics,
provided_trait_methods: Default::default(),
synthetic: true,
blanket_impl: None,
}),
+ cfg: None,
})
}
.collect();
impls.push(Item {
- span: self.cx.tcx.def_span(impl_def_id).clean(self.cx),
name: None,
attrs: Default::default(),
visibility: Inherited,
def_id: self.cx.next_def_id(impl_def_id.krate),
kind: box ImplItem(Impl {
+ span: self.cx.tcx.def_span(impl_def_id).clean(self.cx),
unsafety: hir::Unsafety::Normal,
generics: (
self.cx.tcx.generics_of(impl_def_id),
synthetic: false,
blanket_impl: Some(trait_ref.self_ty().clean(self.cx)),
}),
+ cfg: None,
});
}
}
//! Support for inlining external documentation into the current AST.
use std::iter::once;
+use std::sync::Arc;
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
+use rustc_hir::def_id::DefId;
use rustc_hir::Mutability;
use rustc_metadata::creader::LoadedMacro;
use rustc_middle::ty::{self, TyCtxt};
-use rustc_mir::const_eval::is_min_const_fn;
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::Span;
-use crate::clean::{self, Attributes, GetDefId, ToSource, TypeKind};
+use crate::clean::{self, Attributes, AttributesExt, GetDefId, ToSource};
use crate::core::DocContext;
use crate::formats::item_type::ItemType;
let kind = match res {
Res::Def(DefKind::Trait, did) => {
- record_extern_fqn(cx, did, clean::TypeKind::Trait);
+ record_extern_fqn(cx, did, ItemType::Trait);
build_impls(cx, Some(parent_module), did, attrs, &mut ret);
clean::TraitItem(build_external_trait(cx, did))
}
Res::Def(DefKind::Fn, did) => {
- record_extern_fqn(cx, did, clean::TypeKind::Function);
+ record_extern_fqn(cx, did, ItemType::Function);
clean::FunctionItem(build_external_function(cx, did))
}
Res::Def(DefKind::Struct, did) => {
- record_extern_fqn(cx, did, clean::TypeKind::Struct);
+ record_extern_fqn(cx, did, ItemType::Struct);
build_impls(cx, Some(parent_module), did, attrs, &mut ret);
clean::StructItem(build_struct(cx, did))
}
Res::Def(DefKind::Union, did) => {
- record_extern_fqn(cx, did, clean::TypeKind::Union);
+ record_extern_fqn(cx, did, ItemType::Union);
build_impls(cx, Some(parent_module), did, attrs, &mut ret);
clean::UnionItem(build_union(cx, did))
}
Res::Def(DefKind::TyAlias, did) => {
- record_extern_fqn(cx, did, clean::TypeKind::Typedef);
+ record_extern_fqn(cx, did, ItemType::Typedef);
build_impls(cx, Some(parent_module), did, attrs, &mut ret);
clean::TypedefItem(build_type_alias(cx, did), false)
}
Res::Def(DefKind::Enum, did) => {
- record_extern_fqn(cx, did, clean::TypeKind::Enum);
+ record_extern_fqn(cx, did, ItemType::Enum);
build_impls(cx, Some(parent_module), did, attrs, &mut ret);
clean::EnumItem(build_enum(cx, did))
}
Res::Def(DefKind::ForeignTy, did) => {
- record_extern_fqn(cx, did, clean::TypeKind::Foreign);
+ record_extern_fqn(cx, did, ItemType::ForeignType);
build_impls(cx, Some(parent_module), did, attrs, &mut ret);
clean::ForeignTypeItem
}
// their constructors.
Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => return Some(Vec::new()),
Res::Def(DefKind::Mod, did) => {
- record_extern_fqn(cx, did, clean::TypeKind::Module);
+ record_extern_fqn(cx, did, ItemType::Module);
clean::ModuleItem(build_module(cx, did, visited))
}
Res::Def(DefKind::Static, did) => {
- record_extern_fqn(cx, did, clean::TypeKind::Static);
+ record_extern_fqn(cx, did, ItemType::Static);
clean::StaticItem(build_static(cx, did, cx.tcx.is_mutable_static(did)))
}
Res::Def(DefKind::Const, did) => {
- record_extern_fqn(cx, did, clean::TypeKind::Const);
+ record_extern_fqn(cx, did, ItemType::Constant);
clean::ConstantItem(build_const(cx, did))
}
Res::Def(DefKind::Macro(kind), did) => {
let mac = build_macro(cx, did, name);
let type_kind = match kind {
- MacroKind::Bang => TypeKind::Macro,
- MacroKind::Attr => TypeKind::Attr,
- MacroKind::Derive => TypeKind::Derive,
+ MacroKind::Bang => ItemType::Macro,
+ MacroKind::Attr => ItemType::ProcAttribute,
+ MacroKind::Derive => ItemType::ProcDerive,
};
record_extern_fqn(cx, did, type_kind);
mac
_ => return None,
};
- let target_attrs = load_attrs(cx, did);
- let attrs = box merge_attrs(cx, Some(parent_module), target_attrs, attrs_clone);
-
+ let (attrs, cfg) = merge_attrs(cx, Some(parent_module), load_attrs(cx, did), attrs_clone);
cx.inlined.insert(did);
- let what_rustc_thinks = clean::Item::from_def_id_and_parts(did, Some(name), kind, cx);
- ret.push(clean::Item { attrs, ..what_rustc_thinks });
+ ret.push(clean::Item::from_def_id_and_attrs_and_parts(
+ did,
+ Some(name),
+ kind,
+ box attrs,
+ cx,
+ cfg,
+ ));
Some(ret)
}
///
/// These names are used later on by HTML rendering to generate things like
/// source links back to the original item.
-crate fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: clean::TypeKind) {
+crate fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: ItemType) {
let crate_name = cx.tcx.crate_name(did.krate).to_string();
let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| {
let s = elem.data.to_string();
if !s.is_empty() { Some(s) } else { None }
});
- let fqn = if let clean::TypeKind::Macro = kind {
+ let fqn = if let ItemType::Macro = kind {
// Check to see if it is a macro 2.0 or built-in macro
if matches!(
cx.enter_resolver(|r| r.cstore().load_macro_untracked(did, cx.sess())),
let sig = cx.tcx.fn_sig(did);
let constness =
- if is_min_const_fn(cx.tcx, did) { hir::Constness::Const } else { hir::Constness::NotConst };
+ if cx.tcx.is_const_fn_raw(did) { hir::Constness::Const } else { hir::Constness::NotConst };
let asyncness = cx.tcx.asyncness(did);
let predicates = cx.tcx.predicates_of(did);
let (generics, decl) = clean::enter_impl_trait(cx, |cx| {
parent_module: Option<DefId>,
old_attrs: Attrs<'_>,
new_attrs: Option<Attrs<'_>>,
-) -> clean::Attributes {
+) -> (clean::Attributes, Option<Arc<clean::cfg::Cfg>>) {
// NOTE: If we have additional attributes (from a re-export),
// always insert them first. This ensure that re-export
// doc comments show up before the original doc comments
// when we render them.
if let Some(inner) = new_attrs {
- if let Some(new_id) = parent_module {
- let diag = cx.sess().diagnostic();
- Attributes::from_ast(diag, old_attrs, Some((inner, new_id)))
- } else {
- let mut both = inner.to_vec();
- both.extend_from_slice(old_attrs);
- both.clean(cx)
- }
+ let mut both = inner.to_vec();
+ both.extend_from_slice(old_attrs);
+ (
+ if let Some(new_id) = parent_module {
+ Attributes::from_ast(old_attrs, Some((inner, new_id)))
+ } else {
+ Attributes::from_ast(&both, None)
+ },
+ both.cfg(cx.sess().diagnostic()),
+ )
} else {
- old_attrs.clean(cx)
+ (old_attrs.clean(cx), old_attrs.cfg(cx.sess().diagnostic()))
}
}
debug!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id());
- let attrs = box merge_attrs(cx, parent_module.into(), load_attrs(cx, did), attrs);
- debug!("merged_attrs={:?}", attrs);
+ let (merged_attrs, cfg) = merge_attrs(cx, parent_module.into(), load_attrs(cx, did), attrs);
+ debug!("merged_attrs={:?}", merged_attrs);
ret.push(clean::Item::from_def_id_and_attrs_and_parts(
did,
None,
clean::ImplItem(clean::Impl {
+ span: clean::types::rustc_span(did, cx.tcx),
unsafety: hir::Unsafety::Normal,
generics,
provided_trait_methods: provided,
synthetic: false,
blanket_impl: None,
}),
- attrs,
+ box merged_attrs,
cx,
+ cfg,
));
}
items.push(clean::Item {
name: None,
attrs: box clean::Attributes::default(),
- span: clean::Span::dummy(),
- def_id: DefId::local(CRATE_DEF_INDEX),
+ def_id: cx.next_def_id(did.krate),
visibility: clean::Public,
kind: box clean::ImportItem(clean::Import::new_simple(
item.ident.name,
},
true,
)),
+ cfg: None,
});
} else if let Some(i) = try_inline(cx, did, item.res, item.ident.name, None, visited) {
items.extend(i)
}
}
- clean::Module { items, is_crate: false }
+ let span = clean::Span::from_rustc_span(cx.tcx.def_span(did));
+ clean::Module { items, span }
}
crate fn print_inlined_const(tcx: TyCtxt<'_>, did: DefId) -> String {
use rustc_middle::ty::fold::TypeFolder;
use rustc_middle::ty::subst::{InternalSubsts, Subst};
use rustc_middle::ty::{self, AdtKind, Lift, Ty, TyCtxt};
-use rustc_mir::const_eval::{is_const_fn, is_min_const_fn, is_unstable_const_fn};
+use rustc_mir::const_eval::{is_const_fn, is_unstable_const_fn};
use rustc_span::hygiene::{AstPass, MacroKind};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{self, ExpnKind};
use crate::core::{self, DocContext, ImplTraitParam};
use crate::doctree;
+use crate::formats::item_type::ItemType;
use utils::*;
}
impl Clean<ExternalCrate> for CrateNum {
- fn clean(&self, cx: &mut DocContext<'_>) -> ExternalCrate {
- let tcx = cx.tcx;
- let root = DefId { krate: *self, index: CRATE_DEF_INDEX };
- let krate_span = tcx.def_span(root);
- let krate_src = cx.sess().source_map().span_to_filename(krate_span);
-
- // Collect all inner modules which are tagged as implementations of
- // primitives.
- //
- // Note that this loop only searches the top-level items of the crate,
- // and this is intentional. If we were to search the entire crate for an
- // item tagged with `#[doc(primitive)]` then we would also have to
- // search the entirety of external modules for items tagged
- // `#[doc(primitive)]`, which is a pretty inefficient process (decoding
- // all that metadata unconditionally).
- //
- // In order to keep the metadata load under control, the
- // `#[doc(primitive)]` feature is explicitly designed to only allow the
- // primitive tags to show up as the top level items in a crate.
- //
- // Also note that this does not attempt to deal with modules tagged
- // duplicately for the same primitive. This is handled later on when
- // rendering by delegating everything to a hash map.
- let mut as_primitive = |res: Res| {
- if let Res::Def(DefKind::Mod, def_id) = res {
- let attrs = cx.tcx.get_attrs(def_id).clean(cx);
- let mut prim = None;
- for attr in attrs.lists(sym::doc) {
- if let Some(v) = attr.value_str() {
- if attr.has_name(sym::primitive) {
- prim = PrimitiveType::from_symbol(v);
- if prim.is_some() {
- break;
- }
- // FIXME: should warn on unknown primitives?
- }
- }
- }
- return prim.map(|p| (def_id, p));
- }
- None
- };
- let primitives = if root.is_local() {
- tcx.hir()
- .krate()
- .item
- .item_ids
- .iter()
- .filter_map(|&id| {
- let item = tcx.hir().item(id);
- match item.kind {
- hir::ItemKind::Mod(_) => {
- as_primitive(Res::Def(DefKind::Mod, id.def_id.to_def_id()))
- }
- hir::ItemKind::Use(ref path, hir::UseKind::Single)
- if item.vis.node.is_pub() =>
- {
- as_primitive(path.res).map(|(_, prim)| {
- // Pretend the primitive is local.
- (id.def_id.to_def_id(), prim)
- })
- }
- _ => None,
- }
- })
- .collect()
- } else {
- tcx.item_children(root).iter().map(|item| item.res).filter_map(as_primitive).collect()
- };
-
- let mut as_keyword = |res: Res| {
- if let Res::Def(DefKind::Mod, def_id) = res {
- let attrs = tcx.get_attrs(def_id).clean(cx);
- let mut keyword = None;
- for attr in attrs.lists(sym::doc) {
- if attr.has_name(sym::keyword) {
- if let Some(v) = attr.value_str() {
- keyword = Some(v);
- break;
- }
- }
- }
- return keyword.map(|p| (def_id, p));
- }
- None
- };
- let keywords = if root.is_local() {
- tcx.hir()
- .krate()
- .item
- .item_ids
- .iter()
- .filter_map(|&id| {
- let item = tcx.hir().item(id);
- match item.kind {
- hir::ItemKind::Mod(_) => {
- as_keyword(Res::Def(DefKind::Mod, id.def_id.to_def_id()))
- }
- hir::ItemKind::Use(ref path, hir::UseKind::Single)
- if item.vis.node.is_pub() =>
- {
- as_keyword(path.res).map(|(_, prim)| (id.def_id.to_def_id(), prim))
- }
- _ => None,
- }
- })
- .collect()
- } else {
- tcx.item_children(root).iter().map(|item| item.res).filter_map(as_keyword).collect()
- };
-
- ExternalCrate {
- name: tcx.crate_name(*self),
- src: krate_src,
- attrs: tcx.get_attrs(root).clean(cx),
- primitives,
- keywords,
- }
+ fn clean(&self, _cx: &mut DocContext<'_>) -> ExternalCrate {
+ ExternalCrate { crate_num: *self }
}
}
// determine if we should display the inner contents or
// the outer `mod` item for the source code.
- let span = {
+ let span = Span::from_rustc_span({
let sm = cx.sess().source_map();
let outer = sm.lookup_char_pos(self.where_outer.lo());
let inner = sm.lookup_char_pos(self.where_inner.lo());
// mod foo; (and a separate SourceFile for the contents)
self.where_inner
}
- };
+ });
- let what_rustc_thinks = Item::from_hir_id_and_parts(
+ Item::from_hir_id_and_parts(
self.id,
Some(self.name),
- ModuleItem(Module { is_crate: self.is_crate, items }),
+ ModuleItem(Module { items, span }),
cx,
- );
- Item { span: span.clean(cx), ..what_rustc_thinks }
+ )
}
}
impl Clean<Attributes> for [ast::Attribute] {
- fn clean(&self, cx: &mut DocContext<'_>) -> Attributes {
- Attributes::from_ast(cx.sess().diagnostic(), self, None)
+ fn clean(&self, _cx: &mut DocContext<'_>) -> Attributes {
+ Attributes::from_ast(self, None)
}
}
impl Clean<Type> for (ty::TraitRef<'_>, &[TypeBinding]) {
fn clean(&self, cx: &mut DocContext<'_>) -> Type {
let (trait_ref, bounds) = *self;
- inline::record_extern_fqn(cx, trait_ref.def_id, TypeKind::Trait);
+ inline::record_extern_fqn(cx, trait_ref.def_id, ItemType::Trait);
let path = external_path(
cx,
cx.tcx.item_name(trait_ref.def_id),
inputs: (self.0.inputs, self.1).clean(cx),
output: self.0.output.clean(cx),
c_variadic: self.0.c_variadic,
- attrs: Attributes::default(),
}
}
}
FnDecl {
output: Return(sig.skip_binder().output().clean(cx)),
- attrs: Attributes::default(),
c_variadic: sig.skip_binder().c_variadic,
inputs: Arguments {
values: sig
}
}
-impl Clean<TypeKind> for hir::def::DefKind {
- fn clean(&self, _: &mut DocContext<'_>) -> TypeKind {
- (*self).into()
- }
-}
-
impl Clean<Item> for hir::TraitItem<'_> {
fn clean(&self, cx: &mut DocContext<'_>) -> Item {
let local_did = self.def_id.to_def_id();
ty::TraitContainer(_) => self.defaultness.has_value(),
};
if provided {
- let constness = if is_min_const_fn(tcx, self.def_id) {
+ let constness = if tcx.is_const_fn_raw(self.def_id) {
hir::Constness::Const
} else {
hir::Constness::NotConst
ty::Adt(def, substs) => {
let did = def.did;
let kind = match def.adt_kind() {
- AdtKind::Struct => TypeKind::Struct,
- AdtKind::Union => TypeKind::Union,
- AdtKind::Enum => TypeKind::Enum,
+ AdtKind::Struct => ItemType::Struct,
+ AdtKind::Union => ItemType::Union,
+ AdtKind::Enum => ItemType::Enum,
};
inline::record_extern_fqn(cx, did, kind);
let path = external_path(cx, cx.tcx.item_name(did), None, false, vec![], substs);
ResolvedPath { path, param_names: None, did, is_generic: false }
}
ty::Foreign(did) => {
- inline::record_extern_fqn(cx, did, TypeKind::Foreign);
+ inline::record_extern_fqn(cx, did, ItemType::ForeignType);
let path = external_path(
cx,
cx.tcx.item_name(did),
_ => cx.tcx.intern_substs(&[]),
};
- inline::record_extern_fqn(cx, did, TypeKind::Trait);
+ inline::record_extern_fqn(cx, did, ItemType::Trait);
let mut param_names = vec![];
if let Some(b) = reg.clean(cx) {
let empty = cx.tcx.intern_substs(&[]);
let path =
external_path(cx, cx.tcx.item_name(did), Some(did), false, vec![], empty);
- inline::record_extern_fqn(cx, did, TypeKind::Trait);
+ inline::record_extern_fqn(cx, did, ItemType::Trait);
let bound = GenericBound::TraitBound(
PolyTrait {
trait_: ResolvedPath {
});
let mut make_item = |trait_: Option<Type>, for_: Type, items: Vec<Item>| {
let kind = ImplItem(Impl {
+ span: types::rustc_span(tcx.hir().local_def_id(hir_id).to_def_id(), tcx),
unsafety: impl_.unsafety,
generics: impl_.generics.clean(cx),
provided_trait_methods: provided.clone(),
return items;
}
}
+
// FIXME: using `from_def_id_and_kind` breaks `rustdoc/masked` for some reason
vec![Item {
name: Some(name),
attrs: box attrs.clean(cx),
- span: krate.span.clean(cx),
def_id: crate_def_id,
visibility: krate.vis.clean(cx),
kind: box ExternCrateItem { src: orig_name },
+ cfg: attrs.cfg(cx.sess().diagnostic()),
}]
}
if matchers.len() <= 1 {
format!(
"{}macro {}{} {{\n ...\n}}",
- vis.print_with_space(cx.tcx, def_id, &cx.cache),
+ vis.to_src_with_space(cx.tcx, def_id),
name,
matchers.iter().map(|span| span.to_src(cx)).collect::<String>(),
)
} else {
format!(
"{}macro {} {{\n{}}}",
- vis.print_with_space(cx.tcx, def_id, &cx.cache),
+ vis.to_src_with_space(cx.tcx, def_id),
name,
matchers
.iter()
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::thin_vec::ThinVec;
use rustc_hir as hir;
-use rustc_hir::def::{CtorKind, Res};
-use rustc_hir::def_id::{CrateNum, DefId, DefIndex};
+use rustc_hir::def::{CtorKind, DefKind, Res};
+use rustc_hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX};
use rustc_hir::lang_items::LangItem;
use rustc_hir::{BodyId, Mutability};
use rustc_index::vec::IndexVec;
use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
use crate::html::render::cache::ExternalLocation;
+use crate::html::render::Context;
use self::FnRetTy::*;
use self::ItemKind::*;
#[derive(Clone, Debug)]
crate struct ExternalCrate {
- crate name: Symbol,
- crate src: FileName,
- crate attrs: Attributes,
- crate primitives: ThinVec<(DefId, PrimitiveType)>,
- crate keywords: ThinVec<(DefId, Symbol)>,
+ crate crate_num: CrateNum,
+}
+
+impl ExternalCrate {
+ #[inline]
+ fn def_id(&self) -> DefId {
+ DefId { krate: self.crate_num, index: CRATE_DEF_INDEX }
+ }
+
+ crate fn src(&self, tcx: TyCtxt<'_>) -> FileName {
+ let krate_span = tcx.def_span(self.def_id());
+ tcx.sess.source_map().span_to_filename(krate_span)
+ }
+
+ crate fn name(&self, tcx: TyCtxt<'_>) -> Symbol {
+ tcx.crate_name(self.crate_num)
+ }
+
+ crate fn keywords(&self, tcx: TyCtxt<'_>) -> ThinVec<(DefId, Symbol)> {
+ let root = self.def_id();
+
+ let as_keyword = |res: Res| {
+ if let Res::Def(DefKind::Mod, def_id) = res {
+ let attrs = tcx.get_attrs(def_id);
+ let mut keyword = None;
+ for attr in attrs.lists(sym::doc) {
+ if attr.has_name(sym::keyword) {
+ if let Some(v) = attr.value_str() {
+ keyword = Some(v);
+ break;
+ }
+ }
+ }
+ return keyword.map(|p| (def_id, p));
+ }
+ None
+ };
+ if root.is_local() {
+ tcx.hir()
+ .krate()
+ .item
+ .item_ids
+ .iter()
+ .filter_map(|&id| {
+ let item = tcx.hir().item(id);
+ match item.kind {
+ hir::ItemKind::Mod(_) => {
+ as_keyword(Res::Def(DefKind::Mod, id.def_id.to_def_id()))
+ }
+ hir::ItemKind::Use(ref path, hir::UseKind::Single)
+ if item.vis.node.is_pub() =>
+ {
+ as_keyword(path.res).map(|(_, prim)| (id.def_id.to_def_id(), prim))
+ }
+ _ => None,
+ }
+ })
+ .collect()
+ } else {
+ tcx.item_children(root).iter().map(|item| item.res).filter_map(as_keyword).collect()
+ }
+ }
+
+ crate fn primitives(&self, tcx: TyCtxt<'_>) -> ThinVec<(DefId, PrimitiveType)> {
+ let root = self.def_id();
+
+ // Collect all inner modules which are tagged as implementations of
+ // primitives.
+ //
+ // Note that this loop only searches the top-level items of the crate,
+ // and this is intentional. If we were to search the entire crate for an
+ // item tagged with `#[doc(primitive)]` then we would also have to
+ // search the entirety of external modules for items tagged
+ // `#[doc(primitive)]`, which is a pretty inefficient process (decoding
+ // all that metadata unconditionally).
+ //
+ // In order to keep the metadata load under control, the
+ // `#[doc(primitive)]` feature is explicitly designed to only allow the
+ // primitive tags to show up as the top level items in a crate.
+ //
+ // Also note that this does not attempt to deal with modules tagged
+ // duplicately for the same primitive. This is handled later on when
+ // rendering by delegating everything to a hash map.
+ let as_primitive = |res: Res| {
+ if let Res::Def(DefKind::Mod, def_id) = res {
+ let attrs = tcx.get_attrs(def_id);
+ let mut prim = None;
+ for attr in attrs.lists(sym::doc) {
+ if let Some(v) = attr.value_str() {
+ if attr.has_name(sym::primitive) {
+ prim = PrimitiveType::from_symbol(v);
+ if prim.is_some() {
+ break;
+ }
+ // FIXME: should warn on unknown primitives?
+ }
+ }
+ }
+ return prim.map(|p| (def_id, p));
+ }
+ None
+ };
+
+ if root.is_local() {
+ tcx.hir()
+ .krate()
+ .item
+ .item_ids
+ .iter()
+ .filter_map(|&id| {
+ let item = tcx.hir().item(id);
+ match item.kind {
+ hir::ItemKind::Mod(_) => {
+ as_primitive(Res::Def(DefKind::Mod, id.def_id.to_def_id()))
+ }
+ hir::ItemKind::Use(ref path, hir::UseKind::Single)
+ if item.vis.node.is_pub() =>
+ {
+ as_primitive(path.res).map(|(_, prim)| {
+ // Pretend the primitive is local.
+ (id.def_id.to_def_id(), prim)
+ })
+ }
+ _ => None,
+ }
+ })
+ .collect()
+ } else {
+ tcx.item_children(root).iter().map(|item| item.res).filter_map(as_primitive).collect()
+ }
+ }
}
/// Anything with a source location and set of attributes and, optionally, a
/// directly to the AST's concept of an item; it's a strict superset.
#[derive(Clone)]
crate struct Item {
- crate span: Span,
/// The name of this item.
/// Optional because not every item has a name, e.g. impls.
crate name: Option<Symbol>,
/// E.g., struct vs enum vs function.
crate kind: Box<ItemKind>,
crate def_id: DefId,
+
+ crate cfg: Option<Arc<Cfg>>,
}
// `Item` is used a lot. Make sure it doesn't unintentionally get bigger.
let def_id: &dyn fmt::Debug = if self.is_fake() { &"**FAKE**" } else { &self.def_id };
fmt.debug_struct("Item")
- .field("source", &self.span)
.field("name", &self.name)
.field("attrs", &self.attrs)
.field("kind", &self.kind)
.field("visibility", &self.visibility)
.field("def_id", def_id)
+ .field("cfg", &self.cfg)
.finish()
}
}
+crate fn rustc_span(def_id: DefId, tcx: TyCtxt<'_>) -> Span {
+ Span::from_rustc_span(def_id.as_local().map_or_else(
+ || tcx.def_span(def_id),
+ |local| {
+ let hir = tcx.hir();
+ hir.span_with_body(hir.local_def_id_to_hir_id(local))
+ },
+ ))
+}
+
impl Item {
crate fn stability<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx Stability> {
if self.is_fake() { None } else { tcx.lookup_stability(self.def_id) }
if self.is_fake() { None } else { tcx.lookup_deprecation(self.def_id) }
}
+ crate fn inner_docs(&self, tcx: TyCtxt<'_>) -> bool {
+ if self.is_fake() { false } else { tcx.get_attrs(self.def_id).inner_docs() }
+ }
+
+ crate fn span(&self, tcx: TyCtxt<'_>) -> Span {
+ let kind = match &*self.kind {
+ ItemKind::StrippedItem(k) => k,
+ _ => &*self.kind,
+ };
+ if let ItemKind::ModuleItem(Module { span, .. }) | ItemKind::ImplItem(Impl { span, .. }) =
+ kind
+ {
+ *span
+ } else if self.is_fake() {
+ Span::dummy()
+ } else {
+ rustc_span(self.def_id, tcx)
+ }
+ }
+
+ crate fn attr_span(&self, tcx: TyCtxt<'_>) -> rustc_span::Span {
+ crate::passes::span_of_attrs(&self.attrs).unwrap_or_else(|| self.span(tcx).inner())
+ }
+
/// Finds the `doc` attribute as a NameValue and returns the corresponding
/// value found.
crate fn doc_value(&self) -> Option<String> {
kind: ItemKind,
cx: &mut DocContext<'_>,
) -> Item {
+ let ast_attrs = cx.tcx.get_attrs(def_id);
+
Self::from_def_id_and_attrs_and_parts(
def_id,
name,
kind,
- box cx.tcx.get_attrs(def_id).clean(cx),
+ box ast_attrs.clean(cx),
cx,
+ ast_attrs.cfg(cx.sess().diagnostic()),
)
}
kind: ItemKind,
attrs: Box<Attributes>,
cx: &mut DocContext<'_>,
+ cfg: Option<Arc<Cfg>>,
) -> Item {
debug!("name={:?}, def_id={:?}", name, def_id);
- // `span_if_local()` lies about functions and only gives the span of the function signature
- let span = def_id.as_local().map_or_else(
- || cx.tcx.def_span(def_id),
- |local| {
- let hir = cx.tcx.hir();
- hir.span_with_body(hir.local_def_id_to_hir_id(local))
- },
- );
-
Item {
def_id,
kind: box kind,
name,
- span: span.clean(cx),
attrs,
visibility: cx.tcx.visibility(def_id).clean(cx),
+ cfg,
}
}
self.attrs.collapsed_doc_value()
}
- crate fn links(&self, cache: &Cache) -> Vec<RenderedLink> {
+ crate fn links(&self, cx: &Context<'_>) -> Vec<RenderedLink> {
use crate::html::format::href;
- use crate::html::render::CURRENT_DEPTH;
- cache
+ cx.cache()
.intra_doc_links
.get(&self.def_id)
.map_or(&[][..], |v| v.as_slice())
.iter()
- .filter_map(|ItemLink { link: s, link_text, did, fragment }| {
+ .filter_map(|ItemLink { link: s, link_text, did, ref fragment }| {
match *did {
Some(did) => {
- if let Some((mut href, ..)) = href(did, cache) {
+ if let Some((mut href, ..)) = href(did, cx) {
if let Some(ref fragment) = *fragment {
href.push('#');
href.push_str(fragment);
None
}
}
+ // FIXME(83083): using fragments as a side-channel for
+ // primitive names is very unfortunate
None => {
+ let relative_to = &cx.current;
if let Some(ref fragment) = *fragment {
- let url = match cache.extern_locations.get(&self.def_id.krate) {
+ let url = match cx.cache().extern_locations.get(&self.def_id.krate) {
Some(&(_, _, ExternalLocation::Local)) => {
- let depth = CURRENT_DEPTH.with(|l| l.get());
- "../".repeat(depth)
+ if relative_to[0] == "std" {
+ let depth = relative_to.len() - 1;
+ "../".repeat(depth)
+ } else {
+ let depth = relative_to.len();
+ format!("{}std/", "../".repeat(depth))
+ }
+ }
+ Some(&(_, _, ExternalLocation::Remote(ref s))) => {
+ format!("{}/std/", s.trim_end_matches('/'))
}
- Some(&(_, _, ExternalLocation::Remote(ref s))) => s.to_string(),
Some(&(_, _, ExternalLocation::Unknown)) | None => format!(
- "https://doc.rust-lang.org/{}",
+ "https://doc.rust-lang.org/{}/std/",
crate::doc_rust_lang_org_channel(),
),
};
original_text: s.clone(),
new_text: link_text.clone(),
href: format!(
- "{}{}std/primitive.{}.html{}",
+ "{}primitive.{}.html{}",
url,
- if !url.ends_with('/') { "/" } else { "" },
&fragment[..tail],
&fragment[tail..]
),
}
crate fn is_crate(&self) -> bool {
- matches!(
- *self.kind,
- StrippedItem(box ModuleItem(Module { is_crate: true, .. }))
- | ModuleItem(Module { is_crate: true, .. })
- )
+ self.is_mod() && self.def_id.index == CRATE_DEF_INDEX
}
+
crate fn is_mod(&self) -> bool {
self.type_() == ItemType::Module
}
#[derive(Clone, Debug)]
crate struct Module {
crate items: Vec<Item>,
- crate is_crate: bool,
+ crate span: Span,
}
crate struct ListAttributesIter<'a> {
crate trait AttributesExt {
/// Finds an attribute as List and returns the list of attributes nested inside.
fn lists(&self, name: Symbol) -> ListAttributesIter<'_>;
+
+ fn span(&self) -> Option<rustc_span::Span>;
+
+ fn inner_docs(&self) -> bool;
+
+ fn other_attrs(&self) -> Vec<ast::Attribute>;
+
+ fn cfg(&self, diagnostic: &::rustc_errors::Handler) -> Option<Arc<Cfg>>;
}
impl AttributesExt for [ast::Attribute] {
fn lists(&self, name: Symbol) -> ListAttributesIter<'_> {
ListAttributesIter { attrs: self.iter(), current_list: Vec::new().into_iter(), name }
}
+
+ /// Return the span of the first doc-comment, if it exists.
+ fn span(&self) -> Option<rustc_span::Span> {
+ self.iter().find(|attr| attr.doc_str().is_some()).map(|attr| attr.span)
+ }
+
+ /// Returns whether the first doc-comment is an inner attribute.
+ ///
+ //// If there are no doc-comments, return true.
+ /// FIXME(#78591): Support both inner and outer attributes on the same item.
+ fn inner_docs(&self) -> bool {
+ self.iter().find(|a| a.doc_str().is_some()).map_or(true, |a| a.style == AttrStyle::Inner)
+ }
+
+ fn other_attrs(&self) -> Vec<ast::Attribute> {
+ self.iter().filter(|attr| attr.doc_str().is_none()).cloned().collect()
+ }
+
+ fn cfg(&self, diagnostic: &::rustc_errors::Handler) -> Option<Arc<Cfg>> {
+ let mut cfg = Cfg::True;
+
+ for attr in self.iter() {
+ if attr.doc_str().is_none() && attr.has_name(sym::doc) {
+ if let Some(mi) = attr.meta() {
+ if let Some(cfg_mi) = Attributes::extract_cfg(&mi) {
+ // Extracted #[doc(cfg(...))]
+ match Cfg::parse(cfg_mi) {
+ Ok(new_cfg) => cfg &= new_cfg,
+ Err(e) => diagnostic.span_err(e.span, e.msg),
+ }
+ }
+ }
+ }
+ }
+
+ for attr in self.lists(sym::target_feature) {
+ if attr.has_name(sym::enable) {
+ if let Some(feat) = attr.value_str() {
+ let meta = attr::mk_name_value_item_str(
+ Ident::with_dummy_span(sym::target_feature),
+ feat,
+ DUMMY_SP,
+ );
+ if let Ok(feat_cfg) = Cfg::parse(&meta) {
+ cfg &= feat_cfg;
+ }
+ }
+ }
+ }
+
+ if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) }
+ }
}
crate trait NestedAttributesExt {
crate struct Attributes {
crate doc_strings: Vec<DocFragment>,
crate other_attrs: Vec<ast::Attribute>,
- crate cfg: Option<Arc<Cfg>>,
- crate span: Option<rustc_span::Span>,
- crate inner_docs: bool,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
}
impl Attributes {
+ crate fn lists(&self, name: Symbol) -> ListAttributesIter<'_> {
+ self.other_attrs.lists(name)
+ }
+
/// Extracts the content from an attribute `#[doc(cfg(content))]`.
crate fn extract_cfg(mi: &ast::MetaItem) -> Option<&ast::MetaItem> {
use rustc_ast::NestedMetaItem::MetaItem;
}
crate fn from_ast(
- diagnostic: &::rustc_errors::Handler,
attrs: &[ast::Attribute],
additional_attrs: Option<(&[ast::Attribute], DefId)>,
) -> Attributes {
let mut doc_strings: Vec<DocFragment> = vec![];
- let mut sp = None;
- let mut cfg = Cfg::True;
let mut doc_line = 0;
fn update_need_backline(doc_strings: &mut Vec<DocFragment>, frag: &DocFragment) {
doc_strings.push(frag);
- if sp.is_none() {
- sp = Some(attr.span);
- }
None
} else {
if attr.has_name(sym::doc) {
if let Some(mi) = attr.meta() {
- if let Some(cfg_mi) = Attributes::extract_cfg(&mi) {
- // Extracted #[doc(cfg(...))]
- match Cfg::parse(cfg_mi) {
- Ok(new_cfg) => cfg &= new_cfg,
- Err(e) => diagnostic.span_err(e.span, e.msg),
- }
- } else if let Some((filename, contents)) = Attributes::extract_include(&mi)
- {
+ if let Some((filename, contents)) = Attributes::extract_include(&mi) {
let line = doc_line;
doc_line += contents.as_str().lines().count();
let frag = DocFragment {
.filter_map(clean_attr)
.collect();
- // treat #[target_feature(enable = "feat")] attributes as if they were
- // #[doc(cfg(target_feature = "feat"))] attributes as well
- for attr in attrs.lists(sym::target_feature) {
- if attr.has_name(sym::enable) {
- if let Some(feat) = attr.value_str() {
- let meta = attr::mk_name_value_item_str(
- Ident::with_dummy_span(sym::target_feature),
- feat,
- DUMMY_SP,
- );
- if let Ok(feat_cfg) = Cfg::parse(&meta) {
- cfg &= feat_cfg;
- }
- }
- }
- }
-
- let inner_docs = attrs
- .iter()
- .find(|a| a.doc_str().is_some())
- .map_or(true, |a| a.style == AttrStyle::Inner);
-
- Attributes {
- doc_strings,
- other_attrs,
- cfg: if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) },
- span: sp,
- inner_docs,
- }
+ Attributes { doc_strings, other_attrs }
}
/// Finds the `doc` attribute as a NameValue and returns the corresponding
impl PartialEq for Attributes {
fn eq(&self, rhs: &Self) -> bool {
self.doc_strings == rhs.doc_strings
- && self.cfg == rhs.cfg
- && self.span == rhs.span
&& self
.other_attrs
.iter()
impl Hash for Attributes {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.doc_strings.hash(hasher);
- self.cfg.hash(hasher);
- self.span.hash(hasher);
for attr in &self.other_attrs {
attr.id.hash(hasher);
}
}
}
-impl AttributesExt for Attributes {
- fn lists(&self, name: Symbol) -> ListAttributesIter<'_> {
- self.other_attrs.lists(name)
- }
-}
-
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
crate enum GenericBound {
TraitBound(PolyTrait, hir::TraitBoundModifier),
let did = cx.tcx.require_lang_item(LangItem::Sized, None);
let empty = cx.tcx.intern_substs(&[]);
let path = external_path(cx, cx.tcx.item_name(did), Some(did), false, vec![], empty);
- inline::record_extern_fqn(cx, did, TypeKind::Trait);
+ inline::record_extern_fqn(cx, did, ItemType::Trait);
GenericBound::TraitBound(
PolyTrait {
trait_: ResolvedPath { path, param_names: None, did, is_generic: false },
crate inputs: Arguments,
crate output: FnRetTy,
crate c_variadic: bool,
- crate attrs: Attributes,
}
impl FnDecl {
Never,
}
-#[derive(Clone, PartialEq, Eq, Hash, Copy, Debug)]
-crate enum TypeKind {
- Enum,
- Function,
- Module,
- Const,
- Static,
- Struct,
- Union,
- Trait,
- Typedef,
- Foreign,
- Macro,
- Attr,
- Derive,
- TraitAlias,
- Primitive,
-}
-
-impl From<hir::def::DefKind> for TypeKind {
- fn from(other: hir::def::DefKind) -> Self {
- match other {
- hir::def::DefKind::Enum => Self::Enum,
- hir::def::DefKind::Fn => Self::Function,
- hir::def::DefKind::Mod => Self::Module,
- hir::def::DefKind::Const => Self::Const,
- hir::def::DefKind::Static => Self::Static,
- hir::def::DefKind::Struct => Self::Struct,
- hir::def::DefKind::Union => Self::Union,
- hir::def::DefKind::Trait => Self::Trait,
- hir::def::DefKind::TyAlias => Self::Typedef,
- hir::def::DefKind::TraitAlias => Self::TraitAlias,
- hir::def::DefKind::Macro(_) => Self::Macro,
- hir::def::DefKind::ForeignTy
- | hir::def::DefKind::Variant
- | hir::def::DefKind::AssocTy
- | hir::def::DefKind::TyParam
- | hir::def::DefKind::ConstParam
- | hir::def::DefKind::Ctor(..)
- | hir::def::DefKind::AssocFn
- | hir::def::DefKind::AssocConst
- | hir::def::DefKind::ExternCrate
- | hir::def::DefKind::Use
- | hir::def::DefKind::ForeignMod
- | hir::def::DefKind::AnonConst
- | hir::def::DefKind::OpaqueTy
- | hir::def::DefKind::Field
- | hir::def::DefKind::LifetimeParam
- | hir::def::DefKind::GlobalAsm
- | hir::def::DefKind::Impl
- | hir::def::DefKind::Closure
- | hir::def::DefKind::Generator => Self::Foreign,
- }
- }
-}
-
crate trait GetDefId {
/// Use this method to get the [`DefId`] of a [`clean`] AST node.
/// This will return [`None`] when called on a primitive [`clean::Type`].
}
}
- crate fn impls(&self, tcx: TyCtxt<'_>) -> &'static ArrayVec<[DefId; 4]> {
+ crate fn impls(&self, tcx: TyCtxt<'_>) -> &'static ArrayVec<DefId, 4> {
Self::all_impls(tcx).get(self).expect("missing impl for primitive type")
}
- crate fn all_impls(tcx: TyCtxt<'_>) -> &'static FxHashMap<PrimitiveType, ArrayVec<[DefId; 4]>> {
- static CELL: OnceCell<FxHashMap<PrimitiveType, ArrayVec<[DefId; 4]>>> = OnceCell::new();
+ crate fn all_impls(tcx: TyCtxt<'_>) -> &'static FxHashMap<PrimitiveType, ArrayVec<DefId, 4>> {
+ static CELL: OnceCell<FxHashMap<PrimitiveType, ArrayVec<DefId, 4>>> = OnceCell::new();
CELL.get_or_init(move || {
use self::PrimitiveType::*;
let single = |a: Option<DefId>| a.into_iter().collect();
- let both = |a: Option<DefId>, b: Option<DefId>| -> ArrayVec<_> {
+ let both = |a: Option<DefId>, b: Option<DefId>| -> ArrayVec<_, 4> {
a.into_iter().chain(b).collect()
};
/// Small wrapper around [`rustc_span::Span]` that adds helper methods
/// and enforces calling [`rustc_span::Span::source_callsite()`].
-#[derive(Clone, Debug)]
+#[derive(Copy, Clone, Debug)]
crate struct Span(rustc_span::Span);
impl Span {
#[derive(Clone, Debug)]
crate struct Impl {
+ crate span: Span,
crate unsafety: hir::Unsafety,
crate generics: Generics,
crate provided_trait_methods: FxHashSet<Symbol>,
use crate::clean::auto_trait::AutoTraitFinder;
use crate::clean::blanket_impl::BlanketImplFinder;
use crate::clean::{
- inline, Clean, Crate, ExternalCrate, Generic, GenericArg, GenericArgs, ImportSource, Item,
- ItemKind, Lifetime, MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Type,
- TypeBinding, TypeKind,
+ inline, Clean, Crate, Generic, GenericArg, GenericArgs, ImportSource, Item, ItemKind, Lifetime,
+ MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Type, TypeBinding,
};
use crate::core::DocContext;
+use crate::formats::item_type::ItemType;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
_ => unreachable!(),
}
- let ExternalCrate { name, src, primitives, keywords, .. } = LOCAL_CRATE.clean(cx);
+ let local_crate = LOCAL_CRATE.clean(cx);
+ let src = local_crate.src(cx.tcx);
+ let name = local_crate.name(cx.tcx);
+ let primitives = local_crate.primitives(cx.tcx);
+ let keywords = local_crate.keywords(cx.tcx);
{
let m = match *module.kind {
ItemKind::ModuleItem(ref mut m) => m,
return Generic(kw::SelfUpper);
}
Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => {
- return Generic(Symbol::intern(&format!("{:#}", path.print(&cx.cache, cx.tcx))));
+ return Generic(Symbol::intern(&path.whole_name()));
}
Res::SelfTy(..) | Res::Def(DefKind::TyParam | DefKind::AssocTy, _) => true,
_ => false,
debug!("register_res({:?})", res);
let (did, kind) = match res {
- Res::Def(DefKind::Fn, i) => (i, TypeKind::Function),
- Res::Def(DefKind::TyAlias, i) => (i, TypeKind::Typedef),
- Res::Def(DefKind::Enum, i) => (i, TypeKind::Enum),
- Res::Def(DefKind::Trait, i) => (i, TypeKind::Trait),
+ Res::Def(DefKind::Fn, i) => (i, ItemType::Function),
+ Res::Def(DefKind::TyAlias, i) => (i, ItemType::Typedef),
+ Res::Def(DefKind::Enum, i) => (i, ItemType::Enum),
+ Res::Def(DefKind::Trait, i) => (i, ItemType::Trait),
Res::Def(DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst, i) => {
- (cx.tcx.parent(i).unwrap(), TypeKind::Trait)
+ (cx.tcx.parent(i).unwrap(), ItemType::Trait)
}
- Res::Def(DefKind::Struct, i) => (i, TypeKind::Struct),
- Res::Def(DefKind::Union, i) => (i, TypeKind::Union),
- Res::Def(DefKind::Mod, i) => (i, TypeKind::Module),
- Res::Def(DefKind::ForeignTy, i) => (i, TypeKind::Foreign),
- Res::Def(DefKind::Const, i) => (i, TypeKind::Const),
- Res::Def(DefKind::Static, i) => (i, TypeKind::Static),
+ Res::Def(DefKind::Struct, i) => (i, ItemType::Struct),
+ Res::Def(DefKind::Union, i) => (i, ItemType::Union),
+ Res::Def(DefKind::Mod, i) => (i, ItemType::Module),
+ Res::Def(DefKind::ForeignTy, i) => (i, ItemType::ForeignType),
+ Res::Def(DefKind::Const, i) => (i, ItemType::Constant),
+ Res::Def(DefKind::Static, i) => (i, ItemType::Static),
Res::Def(DefKind::Variant, i) => {
- (cx.tcx.parent(i).expect("cannot get parent def id"), TypeKind::Enum)
+ (cx.tcx.parent(i).expect("cannot get parent def id"), ItemType::Enum)
}
Res::Def(DefKind::Macro(mac_kind), i) => match mac_kind {
- MacroKind::Bang => (i, TypeKind::Macro),
- MacroKind::Attr => (i, TypeKind::Attr),
- MacroKind::Derive => (i, TypeKind::Derive),
+ MacroKind::Bang => (i, ItemType::Macro),
+ MacroKind::Attr => (i, ItemType::ProcAttribute),
+ MacroKind::Derive => (i, ItemType::ProcDerive),
},
- Res::Def(DefKind::TraitAlias, i) => (i, TypeKind::TraitAlias),
- Res::SelfTy(Some(def_id), _) => (def_id, TypeKind::Trait),
+ Res::Def(DefKind::TraitAlias, i) => (i, ItemType::TraitAlias),
+ Res::SelfTy(Some(def_id), _) => (def_id, ItemType::Trait),
Res::SelfTy(_, Some((impl_def_id, _))) => return impl_def_id,
_ => return res.def_id(),
};
return did;
}
inline::record_extern_fqn(cx, did, kind);
- if let TypeKind::Trait = kind {
+ if let ItemType::Trait = kind {
inline::record_extern_trait(cx, did);
}
did
use crate::clean;
use crate::clean::inline::build_external_trait;
-use crate::clean::{AttributesExt, TraitWithExtraInfo, MAX_DEF_IDX};
+use crate::clean::{TraitWithExtraInfo, MAX_DEF_IDX};
use crate::config::{Options as RustdocOptions, OutputFormat, RenderOptions};
use crate::formats::cache::Cache;
use crate::passes::{self, Condition::*, ConditionalPass};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
-use crate::clean::Attributes;
+use crate::clean::{types::AttributesExt, Attributes};
use crate::config::Options;
use crate::html::markdown::{self, ErrorCodes, Ignore, LangString};
use crate::lint::init_lints;
sp: Span,
nested: F,
) {
- let attrs = self.tcx.hir().attrs(hir_id);
- let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs, None);
- if let Some(ref cfg) = attrs.cfg {
+ let ast_attrs = self.tcx.hir().attrs(hir_id);
+ let mut attrs = Attributes::from_ast(ast_attrs, None);
+
+ if let Some(ref cfg) = ast_attrs.cfg(self.sess.diagnostic()) {
if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features_untracked())) {
return;
}
// anything else, this will combine them for us.
if let Some(doc) = attrs.collapsed_doc_value() {
// Use the outermost invocation, so that doctest names come from where the docs were written.
- let span = attrs
- .span
+ let span = ast_attrs
+ .span()
.map(|span| span.ctxt().outer_expn().expansion_cause().unwrap_or(span))
.unwrap_or(DUMMY_SP);
self.collector.set_position(span);
crate items: Vec<(&'hir hir::Item<'hir>, Option<Symbol>)>,
crate foreigns: Vec<(&'hir hir::ForeignItem<'hir>, Option<Symbol>)>,
crate macros: Vec<(&'hir hir::MacroDef<'hir>, Option<Symbol>)>,
- crate is_crate: bool,
}
impl Module<'hir> {
items: Vec::new(),
foreigns: Vec::new(),
macros: Vec::new(),
- is_crate: false,
}
}
}
fn fold_mod(&mut self, m: Module) -> Module {
Module {
- is_crate: m.is_crate,
+ span: m.span,
items: m.items.into_iter().filter_map(|i| self.fold_item(i)).collect(),
}
}
// Cache where all our extern crates are located
// FIXME: this part is specific to HTML so it'd be nice to remove it from the common code
for &(n, ref e) in &krate.externs {
- let src_root = match e.src {
+ let src_root = match e.src(tcx) {
FileName::Real(ref p) => match p.local_path().parent() {
Some(p) => p.to_path_buf(),
None => PathBuf::new(),
},
_ => PathBuf::new(),
};
- let extern_url = extern_html_root_urls.get(&*e.name.as_str()).map(|u| &**u);
- self.extern_locations
- .insert(n, (e.name, src_root, extern_location(e, extern_url, &dst)));
-
+ let name = e.name(tcx);
+ let extern_url = extern_html_root_urls.get(&*name.as_str()).map(|u| &**u);
let did = DefId { krate: n, index: CRATE_DEF_INDEX };
- self.external_paths.insert(did, (vec![e.name.to_string()], ItemType::Module));
+ self.extern_locations.insert(
+ n,
+ (name, src_root, extern_location(e, extern_url, tcx.get_attrs(did), &dst, tcx)),
+ );
+
+ self.external_paths.insert(did, (vec![name.to_string()], ItemType::Module));
}
// Cache where all known primitives have their documentation located.
// Favor linking to as local extern as possible, so iterate all crates in
// reverse topological order.
for &(_, ref e) in krate.externs.iter().rev() {
- for &(def_id, prim) in &e.primitives {
+ for &(def_id, prim) in &e.primitives(tcx) {
self.primitive_locations.insert(prim, def_id);
}
}
use serde::{Serialize, Serializer};
+use rustc_hir::def::DefKind;
use rustc_span::hygiene::MacroKind;
use crate::clean;
/// module headings. If you are adding to this enum and want to ensure that the sidebar also prints
/// a heading, edit the listing in `html/render.rs`, function `sidebar_module`. This uses an
/// ordering based on a helper function inside `item_module`, in the same file.
-#[derive(Copy, PartialEq, Eq, Clone, Debug, PartialOrd, Ord)]
+#[derive(Copy, PartialEq, Eq, Hash, Clone, Debug, PartialOrd, Ord)]
crate enum ItemType {
Module = 0,
ExternCrate = 1,
}
}
-impl From<clean::TypeKind> for ItemType {
- fn from(kind: clean::TypeKind) -> ItemType {
- match kind {
- clean::TypeKind::Struct => ItemType::Struct,
- clean::TypeKind::Union => ItemType::Union,
- clean::TypeKind::Enum => ItemType::Enum,
- clean::TypeKind::Function => ItemType::Function,
- clean::TypeKind::Trait => ItemType::Trait,
- clean::TypeKind::Module => ItemType::Module,
- clean::TypeKind::Static => ItemType::Static,
- clean::TypeKind::Const => ItemType::Constant,
- clean::TypeKind::Typedef => ItemType::Typedef,
- clean::TypeKind::Foreign => ItemType::ForeignType,
- clean::TypeKind::Macro => ItemType::Macro,
- clean::TypeKind::Attr => ItemType::ProcAttribute,
- clean::TypeKind::Derive => ItemType::ProcDerive,
- clean::TypeKind::TraitAlias => ItemType::TraitAlias,
- clean::TypeKind::Primitive => ItemType::Primitive,
+impl From<DefKind> for ItemType {
+ fn from(other: DefKind) -> Self {
+ match other {
+ DefKind::Enum => Self::Enum,
+ DefKind::Fn => Self::Function,
+ DefKind::Mod => Self::Module,
+ DefKind::Const => Self::Constant,
+ DefKind::Static => Self::Static,
+ DefKind::Struct => Self::Struct,
+ DefKind::Union => Self::Union,
+ DefKind::Trait => Self::Trait,
+ DefKind::TyAlias => Self::Typedef,
+ DefKind::TraitAlias => Self::TraitAlias,
+ DefKind::Macro(kind) => match kind {
+ MacroKind::Bang => ItemType::Macro,
+ MacroKind::Attr => ItemType::ProcAttribute,
+ MacroKind::Derive => ItemType::ProcDerive,
+ },
+ DefKind::ForeignTy
+ | DefKind::Variant
+ | DefKind::AssocTy
+ | DefKind::TyParam
+ | DefKind::ConstParam
+ | DefKind::Ctor(..)
+ | DefKind::AssocFn
+ | DefKind::AssocConst
+ | DefKind::ExternCrate
+ | DefKind::Use
+ | DefKind::ForeignMod
+ | DefKind::AnonConst
+ | DefKind::OpaqueTy
+ | DefKind::Field
+ | DefKind::LifetimeParam
+ | DefKind::GlobalAsm
+ | DefKind::Impl
+ | DefKind::Closure
+ | DefKind::Generator => Self::ForeignType,
}
}
}
use rustc_middle::ty::TyCtxt;
-use rustc_span::{edition::Edition, Symbol};
+use rustc_span::Symbol;
use crate::clean;
use crate::config::RenderOptions;
fn init(
krate: clean::Crate,
options: RenderOptions,
- edition: Edition,
cache: Cache,
tcx: TyCtxt<'tcx>,
) -> Result<(Self, clean::Crate), Error>;
fn item(&mut self, item: clean::Item) -> Result<(), Error>;
/// Renders a module (should not handle recursing into children).
- fn mod_item_in(&mut self, item: &clean::Item, item_name: &str) -> Result<(), Error>;
+ fn mod_item_in(&mut self, item: &clean::Item) -> Result<(), Error>;
/// Runs after recursively rendering all sub-items of a module.
- fn mod_item_out(&mut self, item_name: &str) -> Result<(), Error>;
+ fn mod_item_out(&mut self) -> Result<(), Error> {
+ Ok(())
+ }
/// Post processing hook for cleanup and dumping output to files.
- ///
- /// A handler is available if the renderer wants to report errors.
- fn after_krate(
- &mut self,
- crate_name: Symbol,
- diag: &rustc_errors::Handler,
- ) -> Result<(), Error>;
+ fn after_krate(&mut self) -> Result<(), Error>;
fn cache(&self) -> &Cache;
}
krate: clean::Crate,
options: RenderOptions,
cache: Cache,
- diag: &rustc_errors::Handler,
- edition: Edition,
tcx: TyCtxt<'tcx>,
) -> Result<(), Error> {
let prof = &tcx.sess.prof;
let emit_crate = options.should_emit_crate();
let (mut format_renderer, krate) = prof
.extra_verbose_generic_activity("create_renderer", T::descr())
- .run(|| T::init(krate, options, edition, cache, tcx))?;
+ .run(|| T::init(krate, options, cache, tcx))?;
if !emit_crate {
return Ok(());
}
// Render the crate documentation
- let crate_name = krate.name;
let mut work = vec![(format_renderer.make_child_renderer(), krate.module)];
let unknown = Symbol::intern("<unknown item>");
if item.is_mod() && T::RUN_ON_MODULE {
// modules are special because they add a namespace. We also need to
// recurse into the items of the module as well.
- let name = item.name.as_ref().unwrap().to_string();
- if name.is_empty() {
- panic!("Unexpected module with empty name");
- }
- let _timer = prof.generic_activity_with_arg("render_mod_item", name.as_str());
+ let _timer =
+ prof.generic_activity_with_arg("render_mod_item", item.name.unwrap().to_string());
- cx.mod_item_in(&item, &name)?;
+ cx.mod_item_in(&item)?;
let module = match *item.kind {
clean::StrippedItem(box clean::ModuleItem(m)) | clean::ModuleItem(m) => m,
_ => unreachable!(),
work.push((cx.make_child_renderer(), it));
}
- cx.mod_item_out(&name)?;
+ cx.mod_item_out()?;
// FIXME: checking `item.name.is_some()` is very implicit and leads to lots of special
// cases. Use an explicit match instead.
} else if item.name.is_some() && !item.is_extern_crate() {
}
}
prof.extra_verbose_generic_activity("renderer_after_krate", T::descr())
- .run(|| format_renderer.after_krate(crate_name, diag))
+ .run(|| format_renderer.after_krate())
}
use std::cell::Cell;
use std::fmt;
+use std::iter;
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashSet;
use rustc_target::spec::abi::Abi;
use crate::clean::{self, utils::find_nearest_parent_module, PrimitiveType};
-use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
use crate::html::escape::Escape;
use crate::html::render::cache::ExternalLocation;
-use crate::html::render::CURRENT_DEPTH;
+use crate::html::render::Context;
crate trait Print {
fn print(self, buffer: &mut Buffer);
self.buffer.push_str(s);
}
+ crate fn push_buffer(&mut self, other: Buffer) {
+ self.buffer.push_str(&other.buffer);
+ }
+
// Intended for consumption by write! and writeln! (std::fmt) but without
// the fmt::Result return type imposed by fmt::Write (and avoiding the trait
// import).
crate fn print_generic_bounds<'a, 'tcx: 'a>(
bounds: &'a [clean::GenericBound],
- cache: &'a Cache,
- tcx: TyCtxt<'tcx>,
+ cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
let mut bounds_dup = FxHashSet::default();
for (i, bound) in
- bounds.iter().filter(|b| bounds_dup.insert(b.print(cache, tcx).to_string())).enumerate()
+ bounds.iter().filter(|b| bounds_dup.insert(b.print(cx).to_string())).enumerate()
{
if i > 0 {
f.write_str(" + ")?;
}
- fmt::Display::fmt(&bound.print(cache, tcx), f)?;
+ fmt::Display::fmt(&bound.print(cx), f)?;
}
Ok(())
})
impl clean::GenericParamDef {
crate fn print<'a, 'tcx: 'a>(
&'a self,
- cache: &'a Cache,
- tcx: TyCtxt<'tcx>,
+ cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| match self.kind {
clean::GenericParamDefKind::Lifetime => write!(f, "{}", self.name),
if !bounds.is_empty() {
if f.alternate() {
- write!(f, ": {:#}", print_generic_bounds(bounds, cache, tcx))?;
+ write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
} else {
- write!(f, ": {}", print_generic_bounds(bounds, cache, tcx))?;
+ write!(f, ": {}", print_generic_bounds(bounds, cx))?;
}
}
if let Some(ref ty) = default {
if f.alternate() {
- write!(f, " = {:#}", ty.print(cache, tcx))?;
+ write!(f, " = {:#}", ty.print(cx))?;
} else {
- write!(f, " = {}", ty.print(cache, tcx))?;
+ write!(f, " = {}", ty.print(cx))?;
}
}
}
clean::GenericParamDefKind::Const { ref ty, .. } => {
if f.alternate() {
- write!(f, "const {}: {:#}", self.name, ty.print(cache, tcx))
+ write!(f, "const {}: {:#}", self.name, ty.print(cx))
} else {
- write!(f, "const {}: {}", self.name, ty.print(cache, tcx))
+ write!(f, "const {}: {}", self.name, ty.print(cx))
}
}
})
impl clean::Generics {
crate fn print<'a, 'tcx: 'a>(
&'a self,
- cache: &'a Cache,
- tcx: TyCtxt<'tcx>,
+ cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
let real_params =
return Ok(());
}
if f.alternate() {
- write!(f, "<{:#}>", comma_sep(real_params.iter().map(|g| g.print(cache, tcx))))
+ write!(f, "<{:#}>", comma_sep(real_params.iter().map(|g| g.print(cx))))
} else {
- write!(f, "<{}>", comma_sep(real_params.iter().map(|g| g.print(cache, tcx))))
+ write!(f, "<{}>", comma_sep(real_params.iter().map(|g| g.print(cx))))
}
})
}
/// * Whether the where-clause needs to add a comma and newline after the last bound.
crate fn print_where_clause<'a, 'tcx: 'a>(
gens: &'a clean::Generics,
- cache: &'a Cache,
- tcx: TyCtxt<'tcx>,
+ cx: &'a Context<'tcx>,
indent: usize,
end_newline: bool,
) -> impl fmt::Display + 'a + Captures<'tcx> {
if f.alternate() {
clause.push_str(&format!(
"{:#}: {:#}",
- ty.print(cache, tcx),
- print_generic_bounds(bounds, cache, tcx)
+ ty.print(cx),
+ print_generic_bounds(bounds, cx)
));
} else {
clause.push_str(&format!(
"{}: {}",
- ty.print(cache, tcx),
- print_generic_bounds(bounds, cache, tcx)
+ ty.print(cx),
+ print_generic_bounds(bounds, cx)
));
}
}
lifetime.print(),
bounds
.iter()
- .map(|b| b.print(cache, tcx).to_string())
+ .map(|b| b.print(cx).to_string())
.collect::<Vec<_>>()
.join(" + ")
));
}
clean::WherePredicate::EqPredicate { lhs, rhs } => {
if f.alternate() {
- clause.push_str(&format!(
- "{:#} == {:#}",
- lhs.print(cache, tcx),
- rhs.print(cache, tcx),
- ));
+ clause.push_str(&format!("{:#} == {:#}", lhs.print(cx), rhs.print(cx),));
} else {
- clause.push_str(&format!(
- "{} == {}",
- lhs.print(cache, tcx),
- rhs.print(cache, tcx),
- ));
+ clause.push_str(&format!("{} == {}", lhs.print(cx), rhs.print(cx),));
}
}
}
impl clean::PolyTrait {
fn print<'a, 'tcx: 'a>(
&'a self,
- cache: &'a Cache,
- tcx: TyCtxt<'tcx>,
+ cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
if !self.generic_params.is_empty() {
write!(
f,
"for<{:#}> ",
- comma_sep(self.generic_params.iter().map(|g| g.print(cache, tcx)))
+ comma_sep(self.generic_params.iter().map(|g| g.print(cx)))
)?;
} else {
write!(
f,
"for<{}> ",
- comma_sep(self.generic_params.iter().map(|g| g.print(cache, tcx)))
+ comma_sep(self.generic_params.iter().map(|g| g.print(cx)))
)?;
}
}
if f.alternate() {
- write!(f, "{:#}", self.trait_.print(cache, tcx))
+ write!(f, "{:#}", self.trait_.print(cx))
} else {
- write!(f, "{}", self.trait_.print(cache, tcx))
+ write!(f, "{}", self.trait_.print(cx))
}
})
}
impl clean::GenericBound {
crate fn print<'a, 'tcx: 'a>(
&'a self,
- cache: &'a Cache,
- tcx: TyCtxt<'tcx>,
+ cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| match self {
clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()),
hir::TraitBoundModifier::MaybeConst => "?const",
};
if f.alternate() {
- write!(f, "{}{:#}", modifier_str, ty.print(cache, tcx))
+ write!(f, "{}{:#}", modifier_str, ty.print(cx))
} else {
- write!(f, "{}{}", modifier_str, ty.print(cache, tcx))
+ write!(f, "{}{}", modifier_str, ty.print(cx))
}
}
})
impl clean::GenericArgs {
fn print<'a, 'tcx: 'a>(
&'a self,
- cache: &'a Cache,
- tcx: TyCtxt<'tcx>,
+ cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
match self {
}
comma = true;
if f.alternate() {
- write!(f, "{:#}", arg.print(cache, tcx))?;
+ write!(f, "{:#}", arg.print(cx))?;
} else {
- write!(f, "{}", arg.print(cache, tcx))?;
+ write!(f, "{}", arg.print(cx))?;
}
}
for binding in bindings {
}
comma = true;
if f.alternate() {
- write!(f, "{:#}", binding.print(cache, tcx))?;
+ write!(f, "{:#}", binding.print(cx))?;
} else {
- write!(f, "{}", binding.print(cache, tcx))?;
+ write!(f, "{}", binding.print(cx))?;
}
}
if f.alternate() {
}
comma = true;
if f.alternate() {
- write!(f, "{:#}", ty.print(cache, tcx))?;
+ write!(f, "{:#}", ty.print(cx))?;
} else {
- write!(f, "{}", ty.print(cache, tcx))?;
+ write!(f, "{}", ty.print(cx))?;
}
}
f.write_str(")")?;
if let Some(ref ty) = *output {
if f.alternate() {
- write!(f, " -> {:#}", ty.print(cache, tcx))?;
+ write!(f, " -> {:#}", ty.print(cx))?;
} else {
- write!(f, " -> {}", ty.print(cache, tcx))?;
+ write!(f, " -> {}", ty.print(cx))?;
}
}
}
}
}
-impl clean::PathSegment {
- crate fn print<'a, 'tcx: 'a>(
- &'a self,
- cache: &'a Cache,
- tcx: TyCtxt<'tcx>,
- ) -> impl fmt::Display + 'a + Captures<'tcx> {
- display_fn(move |f| {
- if f.alternate() {
- write!(f, "{}{:#}", self.name, self.args.print(cache, tcx))
- } else {
- write!(f, "{}{}", self.name, self.args.print(cache, tcx))
- }
- })
+crate fn href(did: DefId, cx: &Context<'_>) -> Option<(String, ItemType, Vec<String>)> {
+ let cache = &cx.cache();
+ let relative_to = &cx.current;
+ fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] {
+ if shortty == ItemType::Module { &fqp[..] } else { &fqp[..fqp.len() - 1] }
}
-}
-impl clean::Path {
- crate fn print<'a, 'tcx: 'a>(
- &'a self,
- cache: &'a Cache,
- tcx: TyCtxt<'tcx>,
- ) -> impl fmt::Display + 'a + Captures<'tcx> {
- display_fn(move |f| {
- if self.global {
- f.write_str("::")?
- }
-
- for (i, seg) in self.segments.iter().enumerate() {
- if i > 0 {
- f.write_str("::")?
- }
- if f.alternate() {
- write!(f, "{:#}", seg.print(cache, tcx))?;
- } else {
- write!(f, "{}", seg.print(cache, tcx))?;
- }
- }
- Ok(())
- })
- }
-}
-
-crate fn href(did: DefId, cache: &Cache) -> Option<(String, ItemType, Vec<String>)> {
if !did.is_local() && !cache.access_levels.is_public(did) && !cache.document_private {
return None;
}
- let depth = CURRENT_DEPTH.with(|l| l.get());
- let (fqp, shortty, mut url) = match cache.paths.get(&did) {
- Some(&(ref fqp, shortty)) => (fqp, shortty, "../".repeat(depth)),
+ let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) {
+ Some(&(ref fqp, shortty)) => (fqp, shortty, {
+ let module_fqp = to_module_fqp(shortty, fqp);
+ href_relative_parts(module_fqp, relative_to)
+ }),
None => {
let &(ref fqp, shortty) = cache.external_paths.get(&did)?;
+ let module_fqp = to_module_fqp(shortty, fqp);
(
fqp,
shortty,
match cache.extern_locations[&did.krate] {
- (.., ExternalLocation::Remote(ref s)) => s.to_string(),
- (.., ExternalLocation::Local) => "../".repeat(depth),
+ (.., ExternalLocation::Remote(ref s)) => {
+ let s = s.trim_end_matches('/');
+ let mut s = vec![&s[..]];
+ s.extend(module_fqp[..].iter().map(String::as_str));
+ s
+ }
+ (.., ExternalLocation::Local) => href_relative_parts(module_fqp, relative_to),
(.., ExternalLocation::Unknown) => return None,
},
)
}
};
- for component in &fqp[..fqp.len() - 1] {
- url.push_str(component);
- url.push('/');
- }
+ let last = &fqp.last().unwrap()[..];
+ let filename;
match shortty {
ItemType::Module => {
- url.push_str(fqp.last().unwrap());
- url.push_str("/index.html");
+ url_parts.push("index.html");
}
_ => {
- url.push_str(shortty.as_str());
- url.push('.');
- url.push_str(fqp.last().unwrap());
- url.push_str(".html");
+ filename = format!("{}.{}.html", shortty.as_str(), last);
+ url_parts.push(&filename);
}
}
- Some((url, shortty, fqp.to_vec()))
+ Some((url_parts.join("/"), shortty, fqp.to_vec()))
+}
+
+/// Both paths should only be modules.
+/// This is because modules get their own directories; that is, `std::vec` and `std::vec::Vec` will
+/// both need `../iter/trait.Iterator.html` to get at the iterator trait.
+crate fn href_relative_parts<'a>(fqp: &'a [String], relative_to_fqp: &'a [String]) -> Vec<&'a str> {
+ for (i, (f, r)) in fqp.iter().zip(relative_to_fqp.iter()).enumerate() {
+ // e.g. linking to std::iter from std::vec (`dissimilar_part_count` will be 1)
+ if f != r {
+ let dissimilar_part_count = relative_to_fqp.len() - i;
+ let fqp_module = fqp[i..fqp.len()].iter().map(String::as_str);
+ return iter::repeat("..").take(dissimilar_part_count).chain(fqp_module).collect();
+ }
+ }
+ // e.g. linking to std::sync::atomic from std::sync
+ if relative_to_fqp.len() < fqp.len() {
+ fqp[relative_to_fqp.len()..fqp.len()].iter().map(String::as_str).collect()
+ // e.g. linking to std::sync from std::sync::atomic
+ } else if fqp.len() < relative_to_fqp.len() {
+ let dissimilar_part_count = relative_to_fqp.len() - fqp.len();
+ iter::repeat("..").take(dissimilar_part_count).collect()
+ // linking to the same module
+ } else {
+ Vec::new()
+ }
}
/// Used when rendering a `ResolvedPath` structure. This invokes the `path`
/// rendering function with the necessary arguments for linking to a local path.
-fn resolved_path<'a, 'tcx: 'a>(
+fn resolved_path<'a, 'cx: 'a>(
w: &mut fmt::Formatter<'_>,
did: DefId,
path: &clean::Path,
print_all: bool,
use_absolute: bool,
- cache: &Cache,
- tcx: TyCtxt<'tcx>,
+ cx: &'cx Context<'_>,
) -> fmt::Result {
let last = path.segments.last().unwrap();
}
}
if w.alternate() {
- write!(w, "{}{:#}", &last.name, last.args.print(cache, tcx))?;
+ write!(w, "{}{:#}", &last.name, last.args.print(cx))?;
} else {
let path = if use_absolute {
- if let Some((_, _, fqp)) = href(did, cache) {
+ if let Some((_, _, fqp)) = href(did, cx) {
format!(
"{}::{}",
fqp[..fqp.len() - 1].join("::"),
- anchor(did, fqp.last().unwrap(), cache)
+ anchor(did, fqp.last().unwrap(), cx)
)
} else {
last.name.to_string()
}
} else {
- anchor(did, &*last.name.as_str(), cache).to_string()
+ anchor(did, &*last.name.as_str(), cx).to_string()
};
- write!(w, "{}{}", path, last.args.print(cache, tcx))?;
+ write!(w, "{}{}", path, last.args.print(cx))?;
}
Ok(())
}
f: &mut fmt::Formatter<'_>,
prim: clean::PrimitiveType,
name: &str,
- m: &Cache,
+ cx: &Context<'_>,
) -> fmt::Result {
+ let m = &cx.cache();
let mut needs_termination = false;
if !f.alternate() {
match m.primitive_locations.get(&prim) {
Some(&def_id) if def_id.is_local() => {
- let len = CURRENT_DEPTH.with(|s| s.get());
+ let len = cx.current.len();
let len = if len == 0 { 0 } else { len - 1 };
write!(
f,
needs_termination = true;
}
Some(&def_id) => {
+ let cname_str;
let loc = match m.extern_locations[&def_id.krate] {
- (ref cname, _, ExternalLocation::Remote(ref s)) => Some((cname, s.to_string())),
+ (ref cname, _, ExternalLocation::Remote(ref s)) => {
+ cname_str = cname.as_str();
+ Some(vec![s.trim_end_matches('/'), &cname_str[..]])
+ }
(ref cname, _, ExternalLocation::Local) => {
- let len = CURRENT_DEPTH.with(|s| s.get());
- Some((cname, "../".repeat(len)))
+ cname_str = cname.as_str();
+ Some(if cx.current.first().map(|x| &x[..]) == Some(&cname_str[..]) {
+ iter::repeat("..").take(cx.current.len() - 1).collect()
+ } else {
+ let cname = iter::once(&cname_str[..]);
+ iter::repeat("..").take(cx.current.len()).chain(cname).collect()
+ })
}
(.., ExternalLocation::Unknown) => None,
};
- if let Some((cname, root)) = loc {
+ if let Some(loc) = loc {
write!(
f,
- "<a class=\"primitive\" href=\"{}{}/primitive.{}.html\">",
- root,
- cname,
+ "<a class=\"primitive\" href=\"{}/primitive.{}.html\">",
+ loc.join("/"),
prim.to_url_str()
)?;
needs_termination = true;
/// Helper to render type parameters
fn tybounds<'a, 'tcx: 'a>(
param_names: &'a Option<Vec<clean::GenericBound>>,
- cache: &'a Cache,
- tcx: TyCtxt<'tcx>,
+ cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| match *param_names {
Some(ref params) => {
for param in params {
write!(f, " + ")?;
- fmt::Display::fmt(¶m.print(cache, tcx), f)?;
+ fmt::Display::fmt(¶m.print(cx), f)?;
}
Ok(())
}
})
}
-crate fn anchor<'a>(did: DefId, text: &'a str, cache: &'a Cache) -> impl fmt::Display + 'a {
+crate fn anchor<'a, 'cx: 'a>(
+ did: DefId,
+ text: &'a str,
+ cx: &'cx Context<'_>,
+) -> impl fmt::Display + 'a {
+ let parts = href(did, cx);
display_fn(move |f| {
- if let Some((url, short_ty, fqp)) = href(did, cache) {
+ if let Some((url, short_ty, fqp)) = parts {
write!(
f,
r#"<a class="{}" href="{}" title="{} {}">{}</a>"#,
})
}
-fn fmt_type(
+fn fmt_type<'cx>(
t: &clean::Type,
f: &mut fmt::Formatter<'_>,
use_absolute: bool,
- cache: &Cache,
- tcx: TyCtxt<'_>,
+ cx: &'cx Context<'_>,
) -> fmt::Result {
debug!("fmt_type(t = {:?})", t);
f.write_str("dyn ")?;
}
// Paths like `T::Output` and `Self::Output` should be rendered with all segments.
- resolved_path(f, did, path, is_generic, use_absolute, cache, tcx)?;
- fmt::Display::fmt(&tybounds(param_names, cache, tcx), f)
+ resolved_path(f, did, path, is_generic, use_absolute, cx)?;
+ fmt::Display::fmt(&tybounds(param_names, cx), f)
}
clean::Infer => write!(f, "_"),
- clean::Primitive(prim) => primitive_link(f, prim, prim.as_str(), cache),
+ clean::Primitive(prim) => primitive_link(f, prim, prim.as_str(), cx),
clean::BareFunction(ref decl) => {
if f.alternate() {
write!(
f,
"{:#}{}{:#}fn{:#}",
- decl.print_hrtb_with_space(cache, tcx),
+ decl.print_hrtb_with_space(cx),
decl.unsafety.print_with_space(),
print_abi_with_space(decl.abi),
- decl.decl.print(cache, tcx),
+ decl.decl.print(cx),
)
} else {
write!(
f,
"{}{}{}",
- decl.print_hrtb_with_space(cache, tcx),
+ decl.print_hrtb_with_space(cx),
decl.unsafety.print_with_space(),
print_abi_with_space(decl.abi)
)?;
- primitive_link(f, PrimitiveType::Fn, "fn", cache)?;
- write!(f, "{}", decl.decl.print(cache, tcx))
+ primitive_link(f, PrimitiveType::Fn, "fn", cx)?;
+ write!(f, "{}", decl.decl.print(cx))
}
}
clean::Tuple(ref typs) => {
match &typs[..] {
- &[] => primitive_link(f, PrimitiveType::Unit, "()", cache),
+ &[] => primitive_link(f, PrimitiveType::Unit, "()", cx),
&[ref one] => {
- primitive_link(f, PrimitiveType::Tuple, "(", cache)?;
+ primitive_link(f, PrimitiveType::Tuple, "(", cx)?;
// Carry `f.alternate()` into this display w/o branching manually.
- fmt::Display::fmt(&one.print(cache, tcx), f)?;
- primitive_link(f, PrimitiveType::Tuple, ",)", cache)
+ fmt::Display::fmt(&one.print(cx), f)?;
+ primitive_link(f, PrimitiveType::Tuple, ",)", cx)
}
many => {
- primitive_link(f, PrimitiveType::Tuple, "(", cache)?;
+ primitive_link(f, PrimitiveType::Tuple, "(", cx)?;
for (i, item) in many.iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
- fmt::Display::fmt(&item.print(cache, tcx), f)?;
+ fmt::Display::fmt(&item.print(cx), f)?;
}
- primitive_link(f, PrimitiveType::Tuple, ")", cache)
+ primitive_link(f, PrimitiveType::Tuple, ")", cx)
}
}
}
clean::Slice(ref t) => {
- primitive_link(f, PrimitiveType::Slice, "[", cache)?;
- fmt::Display::fmt(&t.print(cache, tcx), f)?;
- primitive_link(f, PrimitiveType::Slice, "]", cache)
+ primitive_link(f, PrimitiveType::Slice, "[", cx)?;
+ fmt::Display::fmt(&t.print(cx), f)?;
+ primitive_link(f, PrimitiveType::Slice, "]", cx)
}
clean::Array(ref t, ref n) => {
- primitive_link(f, PrimitiveType::Array, "[", cache)?;
- fmt::Display::fmt(&t.print(cache, tcx), f)?;
+ primitive_link(f, PrimitiveType::Array, "[", cx)?;
+ fmt::Display::fmt(&t.print(cx), f)?;
if f.alternate() {
- primitive_link(f, PrimitiveType::Array, &format!("; {}]", n), cache)
+ primitive_link(f, PrimitiveType::Array, &format!("; {}]", n), cx)
} else {
- primitive_link(f, PrimitiveType::Array, &format!("; {}]", Escape(n)), cache)
+ primitive_link(f, PrimitiveType::Array, &format!("; {}]", Escape(n)), cx)
}
}
- clean::Never => primitive_link(f, PrimitiveType::Never, "!", cache),
+ clean::Never => primitive_link(f, PrimitiveType::Never, "!", cx),
clean::RawPointer(m, ref t) => {
let m = match m {
hir::Mutability::Mut => "mut",
primitive_link(
f,
clean::PrimitiveType::RawPointer,
- &format!("*{} {:#}", m, t.print(cache, tcx)),
- cache,
+ &format!("*{} {:#}", m, t.print(cx)),
+ cx,
)
} else {
primitive_link(
f,
clean::PrimitiveType::RawPointer,
- &format!("*{} {}", m, t.print(cache, tcx)),
- cache,
+ &format!("*{} {}", m, t.print(cx)),
+ cx,
)
}
}
_ => {
- primitive_link(
- f,
- clean::PrimitiveType::RawPointer,
- &format!("*{} ", m),
- cache,
- )?;
- fmt::Display::fmt(&t.print(cache, tcx), f)
+ primitive_link(f, clean::PrimitiveType::RawPointer, &format!("*{} ", m), cx)?;
+ fmt::Display::fmt(&t.print(cx), f)
}
}
}
primitive_link(
f,
PrimitiveType::Slice,
- &format!("{}{}{}[{:#}]", amp, lt, m, bt.print(cache, tcx)),
- cache,
+ &format!("{}{}{}[{:#}]", amp, lt, m, bt.print(cx)),
+ cx,
)
} else {
primitive_link(
f,
PrimitiveType::Slice,
- &format!("{}{}{}[{}]", amp, lt, m, bt.print(cache, tcx)),
- cache,
+ &format!("{}{}{}[{}]", amp, lt, m, bt.print(cx)),
+ cx,
)
}
}
f,
PrimitiveType::Slice,
&format!("{}{}{}[", amp, lt, m),
- cache,
+ cx,
)?;
if f.alternate() {
- write!(f, "{:#}", bt.print(cache, tcx))?;
+ write!(f, "{:#}", bt.print(cx))?;
} else {
- write!(f, "{}", bt.print(cache, tcx))?;
+ write!(f, "{}", bt.print(cx))?;
}
- primitive_link(f, PrimitiveType::Slice, "]", cache)
+ primitive_link(f, PrimitiveType::Slice, "]", cx)
}
}
}
clean::ResolvedPath { param_names: Some(ref v), .. } if !v.is_empty() => {
write!(f, "{}{}{}(", amp, lt, m)?;
- fmt_type(&ty, f, use_absolute, cache, tcx)?;
+ fmt_type(&ty, f, use_absolute, cx)?;
write!(f, ")")
}
clean::Generic(..) => {
f,
PrimitiveType::Reference,
&format!("{}{}{}", amp, lt, m),
- cache,
+ cx,
)?;
- fmt_type(&ty, f, use_absolute, cache, tcx)
+ fmt_type(&ty, f, use_absolute, cx)
}
_ => {
write!(f, "{}{}{}", amp, lt, m)?;
- fmt_type(&ty, f, use_absolute, cache, tcx)
+ fmt_type(&ty, f, use_absolute, cx)
}
}
}
clean::ImplTrait(ref bounds) => {
if f.alternate() {
- write!(f, "impl {:#}", print_generic_bounds(bounds, cache, tcx))
+ write!(f, "impl {:#}", print_generic_bounds(bounds, cx))
} else {
- write!(f, "impl {}", print_generic_bounds(bounds, cache, tcx))
+ write!(f, "impl {}", print_generic_bounds(bounds, cx))
}
}
clean::QPath { ref name, ref self_type, ref trait_ } => {
};
if f.alternate() {
if should_show_cast {
- write!(
- f,
- "<{:#} as {:#}>::",
- self_type.print(cache, tcx),
- trait_.print(cache, tcx)
- )?
+ write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))?
} else {
- write!(f, "{:#}::", self_type.print(cache, tcx))?
+ write!(f, "{:#}::", self_type.print(cx))?
}
} else {
if should_show_cast {
- write!(
- f,
- "<{} as {}>::",
- self_type.print(cache, tcx),
- trait_.print(cache, tcx)
- )?
+ write!(f, "<{} as {}>::", self_type.print(cx), trait_.print(cx))?
} else {
- write!(f, "{}::", self_type.print(cache, tcx))?
+ write!(f, "{}::", self_type.print(cx))?
}
};
match *trait_ {
// everything comes in as a fully resolved QPath (hard to
// look at).
box clean::ResolvedPath { did, ref param_names, .. } => {
- match href(did, cache) {
+ match href(did, cx) {
Some((ref url, _, ref path)) if !f.alternate() => {
write!(
f,
impl clean::Type {
crate fn print<'b, 'a: 'b, 'tcx: 'a>(
&'a self,
- cache: &'b Cache,
- tcx: TyCtxt<'tcx>,
+ cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'b + Captures<'tcx> {
- display_fn(move |f| fmt_type(self, f, false, cache, tcx))
+ display_fn(move |f| fmt_type(self, f, false, cx))
}
}
impl clean::Impl {
crate fn print<'a, 'tcx: 'a>(
&'a self,
- cache: &'a Cache,
use_absolute: bool,
- tcx: TyCtxt<'tcx>,
+ cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
if f.alternate() {
- write!(f, "impl{:#} ", self.generics.print(cache, tcx))?;
+ write!(f, "impl{:#} ", self.generics.print(cx))?;
} else {
- write!(f, "impl{} ", self.generics.print(cache, tcx))?;
+ write!(f, "impl{} ", self.generics.print(cx))?;
}
if let Some(ref ty) = self.trait_ {
if self.negative_polarity {
write!(f, "!")?;
}
- fmt::Display::fmt(&ty.print(cache, tcx), f)?;
+ fmt::Display::fmt(&ty.print(cx), f)?;
write!(f, " for ")?;
}
if let Some(ref ty) = self.blanket_impl {
- fmt_type(ty, f, use_absolute, cache, tcx)?;
+ fmt_type(ty, f, use_absolute, cx)?;
} else {
- fmt_type(&self.for_, f, use_absolute, cache, tcx)?;
+ fmt_type(&self.for_, f, use_absolute, cx)?;
}
- fmt::Display::fmt(&print_where_clause(&self.generics, cache, tcx, 0, true), f)?;
+ fmt::Display::fmt(&print_where_clause(&self.generics, cx, 0, true), f)?;
Ok(())
})
}
impl clean::Arguments {
crate fn print<'a, 'tcx: 'a>(
&'a self,
- cache: &'a Cache,
- tcx: TyCtxt<'tcx>,
+ cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
for (i, input) in self.values.iter().enumerate() {
write!(f, "{}: ", input.name)?;
}
if f.alternate() {
- write!(f, "{:#}", input.type_.print(cache, tcx))?;
+ write!(f, "{:#}", input.type_.print(cx))?;
} else {
- write!(f, "{}", input.type_.print(cache, tcx))?;
+ write!(f, "{}", input.type_.print(cx))?;
}
if i + 1 < self.values.len() {
write!(f, ", ")?;
impl clean::FnRetTy {
crate fn print<'a, 'tcx: 'a>(
&'a self,
- cache: &'a Cache,
- tcx: TyCtxt<'tcx>,
+ cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| match self {
clean::Return(clean::Tuple(tys)) if tys.is_empty() => Ok(()),
- clean::Return(ty) if f.alternate() => write!(f, " -> {:#}", ty.print(cache, tcx)),
- clean::Return(ty) => write!(f, " -> {}", ty.print(cache, tcx)),
+ clean::Return(ty) if f.alternate() => {
+ write!(f, " -> {:#}", ty.print(cx))
+ }
+ clean::Return(ty) => write!(f, " -> {}", ty.print(cx)),
clean::DefaultReturn => Ok(()),
})
}
impl clean::BareFunctionDecl {
fn print_hrtb_with_space<'a, 'tcx: 'a>(
&'a self,
- cache: &'a Cache,
- tcx: TyCtxt<'tcx>,
+ cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
if !self.generic_params.is_empty() {
- write!(
- f,
- "for<{}> ",
- comma_sep(self.generic_params.iter().map(|g| g.print(cache, tcx)))
- )
+ write!(f, "for<{}> ", comma_sep(self.generic_params.iter().map(|g| g.print(cx))))
} else {
Ok(())
}
impl clean::FnDecl {
crate fn print<'b, 'a: 'b, 'tcx: 'a>(
&'a self,
- cache: &'b Cache,
- tcx: TyCtxt<'tcx>,
+ cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'b + Captures<'tcx> {
display_fn(move |f| {
let ellipsis = if self.c_variadic { ", ..." } else { "" };
write!(
f,
"({args:#}{ellipsis}){arrow:#}",
- args = self.inputs.print(cache, tcx),
+ args = self.inputs.print(cx),
ellipsis = ellipsis,
- arrow = self.output.print(cache, tcx)
+ arrow = self.output.print(cx)
)
} else {
write!(
f,
"({args}{ellipsis}){arrow}",
- args = self.inputs.print(cache, tcx),
+ args = self.inputs.print(cx),
ellipsis = ellipsis,
- arrow = self.output.print(cache, tcx)
+ arrow = self.output.print(cx)
)
}
})
/// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is
/// necessary.
/// * `asyncness`: Whether the function is async or not.
- crate fn full_print<'b, 'a: 'b, 'tcx: 'a>(
+ crate fn full_print<'a, 'tcx: 'a>(
&'a self,
- cache: &'b Cache,
- tcx: TyCtxt<'tcx>,
header_len: usize,
indent: usize,
asyncness: hir::IsAsync,
- ) -> impl fmt::Display + 'b + Captures<'tcx> {
- display_fn(move |f| self.inner_full_print(cache, tcx, header_len, indent, asyncness, f))
+ cx: &'a Context<'tcx>,
+ ) -> impl fmt::Display + 'a + Captures<'tcx> {
+ display_fn(move |f| self.inner_full_print(header_len, indent, asyncness, f, cx))
}
fn inner_full_print(
&self,
- cache: &Cache,
- tcx: TyCtxt<'_>,
header_len: usize,
indent: usize,
asyncness: hir::IsAsync,
f: &mut fmt::Formatter<'_>,
+ cx: &Context<'_>,
) -> fmt::Result {
let amp = if f.alternate() { "&" } else { "&" };
let mut args = String::new();
}
clean::SelfExplicit(ref typ) => {
if f.alternate() {
- args.push_str(&format!("self: {:#}", typ.print(cache, tcx)));
+ args.push_str(&format!("self: {:#}", typ.print(cx)));
} else {
- args.push_str(&format!("self: {}", typ.print(cache, tcx)));
+ args.push_str(&format!("self: {}", typ.print(cx)));
}
- args_plain.push_str(&format!("self: {:#}", typ.print(cache, tcx)));
+ args_plain.push_str(&format!("self: {:#}", typ.print(cx)));
}
}
} else {
}
if f.alternate() {
- args.push_str(&format!("{:#}", input.type_.print(cache, tcx)));
+ args.push_str(&format!("{:#}", input.type_.print(cx)));
} else {
- args.push_str(&input.type_.print(cache, tcx).to_string());
+ args.push_str(&input.type_.print(cx).to_string());
}
- args_plain.push_str(&format!("{:#}", input.type_.print(cache, tcx)));
+ args_plain.push_str(&format!("{:#}", input.type_.print(cx)));
}
if i + 1 < self.inputs.values.len() {
args.push(',');
let arrow_plain;
let arrow = if let hir::IsAsync::Async = asyncness {
let output = self.sugared_async_return_type();
- arrow_plain = format!("{:#}", output.print(cache, tcx));
- if f.alternate() {
- arrow_plain.clone()
- } else {
- format!("{}", output.print(cache, tcx))
- }
+ arrow_plain = format!("{:#}", output.print(cx));
+ if f.alternate() { arrow_plain.clone() } else { format!("{}", output.print(cx)) }
} else {
- arrow_plain = format!("{:#}", self.output.print(cache, tcx));
- if f.alternate() {
- arrow_plain.clone()
- } else {
- format!("{}", self.output.print(cache, tcx))
- }
+ arrow_plain = format!("{:#}", self.output.print(cx));
+ if f.alternate() { arrow_plain.clone() } else { format!("{}", self.output.print(cx)) }
};
let declaration_len = header_len + args_plain.len() + arrow_plain.len();
impl clean::Visibility {
crate fn print_with_space<'a, 'tcx: 'a>(
self,
- tcx: TyCtxt<'tcx>,
item_did: DefId,
- cache: &'a Cache,
+ cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
- use rustc_span::symbol::kw;
-
let to_print = match self {
clean::Public => "pub ".to_owned(),
clean::Inherited => String::new(),
// FIXME(camelid): This may not work correctly if `item_did` is a module.
// However, rustdoc currently never displays a module's
// visibility, so it shouldn't matter.
- let parent_module = find_nearest_parent_module(tcx, item_did);
+ let parent_module = find_nearest_parent_module(cx.tcx(), item_did);
if vis_did.index == CRATE_DEF_INDEX {
"pub(crate) ".to_owned()
// is the same as no visibility modifier
String::new()
} else if parent_module
- .map(|parent| find_nearest_parent_module(tcx, parent))
+ .map(|parent| find_nearest_parent_module(cx.tcx(), parent))
.flatten()
== Some(vis_did)
{
"pub(super) ".to_owned()
} else {
- let path = tcx.def_path(vis_did);
+ let path = cx.tcx().def_path(vis_did);
debug!("path={:?}", path);
- let first_name =
- path.data[0].data.get_opt_name().expect("modules are always named");
// modified from `resolved_path()` to work with `DefPathData`
let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
- let anchor = anchor(vis_did, &last_name.as_str(), cache).to_string();
+ let anchor = anchor(vis_did, &last_name.as_str(), cx).to_string();
- let mut s = "pub(".to_owned();
- if path.data.len() != 1
- || (first_name != kw::SelfLower && first_name != kw::Super)
- {
- s.push_str("in ");
- }
+ let mut s = "pub(in ".to_owned();
for seg in &path.data[..path.data.len() - 1] {
s.push_str(&format!("{}::", seg.data.get_opt_name().unwrap()));
}
};
display_fn(move |f| f.write_str(&to_print))
}
+
+ /// This function is the same as print_with_space, except that it renders no links.
+ /// It's used for macros' rendered source view, which is syntax highlighted and cannot have
+ /// any HTML in it.
+ crate fn to_src_with_space<'a, 'tcx: 'a>(
+ self,
+ tcx: TyCtxt<'tcx>,
+ item_did: DefId,
+ ) -> impl fmt::Display + 'a + Captures<'tcx> {
+ let to_print = match self {
+ clean::Public => "pub ".to_owned(),
+ clean::Inherited => String::new(),
+ clean::Visibility::Restricted(vis_did) => {
+ // FIXME(camelid): This may not work correctly if `item_did` is a module.
+ // However, rustdoc currently never displays a module's
+ // visibility, so it shouldn't matter.
+ let parent_module = find_nearest_parent_module(tcx, item_did);
+
+ if vis_did.index == CRATE_DEF_INDEX {
+ "pub(crate) ".to_owned()
+ } else if parent_module == Some(vis_did) {
+ // `pub(in foo)` where `foo` is the parent module
+ // is the same as no visibility modifier
+ String::new()
+ } else if parent_module
+ .map(|parent| find_nearest_parent_module(tcx, parent))
+ .flatten()
+ == Some(vis_did)
+ {
+ "pub(super) ".to_owned()
+ } else {
+ format!("pub(in {}) ", tcx.def_path_str(vis_did))
+ }
+ }
+ };
+ display_fn(move |f| f.write_str(&to_print))
+ }
}
crate trait PrintWithSpace {
}
impl clean::Import {
- crate fn print<'b, 'a: 'b, 'tcx: 'a>(
+ crate fn print<'a, 'tcx: 'a>(
&'a self,
- cache: &'b Cache,
- tcx: TyCtxt<'tcx>,
- ) -> impl fmt::Display + 'b + Captures<'tcx> {
+ cx: &'a Context<'tcx>,
+ ) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| match self.kind {
clean::ImportKind::Simple(name) => {
if name == self.source.path.last() {
- write!(f, "use {};", self.source.print(cache, tcx))
+ write!(f, "use {};", self.source.print(cx))
} else {
- write!(f, "use {} as {};", self.source.print(cache, tcx), name)
+ write!(f, "use {} as {};", self.source.print(cx), name)
}
}
clean::ImportKind::Glob => {
if self.source.path.segments.is_empty() {
write!(f, "use *;")
} else {
- write!(f, "use {}::*;", self.source.print(cache, tcx))
+ write!(f, "use {}::*;", self.source.print(cx))
}
}
})
}
impl clean::ImportSource {
- crate fn print<'b, 'a: 'b, 'tcx: 'a>(
+ crate fn print<'a, 'tcx: 'a>(
&'a self,
- cache: &'b Cache,
- tcx: TyCtxt<'tcx>,
- ) -> impl fmt::Display + 'b + Captures<'tcx> {
+ cx: &'a Context<'tcx>,
+ ) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| match self.did {
- Some(did) => resolved_path(f, did, &self.path, true, false, cache, tcx),
+ Some(did) => resolved_path(f, did, &self.path, true, false, cx),
_ => {
for seg in &self.path.segments[..self.path.segments.len() - 1] {
write!(f, "{}::", seg.name)?;
}
let name = self.path.last_name();
if let hir::def::Res::PrimTy(p) = self.path.res {
- primitive_link(f, PrimitiveType::from(p), &*name, cache)?;
+ primitive_link(f, PrimitiveType::from(p), &*name, cx)?;
} else {
write!(f, "{}", name)?;
}
}
impl clean::TypeBinding {
- crate fn print<'b, 'a: 'b, 'tcx: 'a>(
+ crate fn print<'a, 'tcx: 'a>(
&'a self,
- cache: &'b Cache,
- tcx: TyCtxt<'tcx>,
- ) -> impl fmt::Display + 'b + Captures<'tcx> {
+ cx: &'a Context<'tcx>,
+ ) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
f.write_str(&*self.name.as_str())?;
match self.kind {
clean::TypeBindingKind::Equality { ref ty } => {
if f.alternate() {
- write!(f, " = {:#}", ty.print(cache, tcx))?;
+ write!(f, " = {:#}", ty.print(cx))?;
} else {
- write!(f, " = {}", ty.print(cache, tcx))?;
+ write!(f, " = {}", ty.print(cx))?;
}
}
clean::TypeBindingKind::Constraint { ref bounds } => {
if !bounds.is_empty() {
if f.alternate() {
- write!(f, ": {:#}", print_generic_bounds(bounds, cache, tcx))?;
+ write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
} else {
- write!(f, ": {}", print_generic_bounds(bounds, cache, tcx))?;
+ write!(f, ": {}", print_generic_bounds(bounds, cx))?;
}
}
}
}
impl clean::GenericArg {
- crate fn print<'b, 'a: 'b, 'tcx: 'a>(
+ crate fn print<'a, 'tcx: 'a>(
&'a self,
- cache: &'b Cache,
- tcx: TyCtxt<'tcx>,
- ) -> impl fmt::Display + 'b + Captures<'tcx> {
+ cx: &'a Context<'tcx>,
+ ) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| match self {
clean::GenericArg::Lifetime(lt) => fmt::Display::fmt(<.print(), f),
- clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cache, tcx), f),
- clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(tcx), f),
+ clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cx), f),
+ clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f),
})
}
}
</style>\
</head>\
<body class=\"rustdoc {css_class}\">\
- <!--[if lte IE 8]>\
+ <!--[if lte IE 11]>\
<div class=\"warning\">\
This old browser is unsupported and will most likely display funky \
things.\
</nav>\
<section id=\"main\" class=\"content\">{content}</section>\
<section id=\"search\" class=\"content hidden\"></section>\
- <section class=\"footer\"></section>\
{after_content}\
<div id=\"rustdoc-vars\" data-root-path=\"{root_path}\" data-current-crate=\"{krate}\" \
- data-search-js=\"{root_path}search-index{suffix}.js\"></div>
+ data-search-index-js=\"{root_path}search-index{suffix}.js\" \
+ data-search-js=\"{static_root_path}search{suffix}.js\"></div>
<script src=\"{static_root_path}main{suffix}.js\"></script>\
{extra_scripts}\
</body>\
crate mod sources;
crate mod static_files;
crate mod toc;
+
+#[cfg(test)]
+mod tests;
use std::collections::BTreeMap;
use std::path::Path;
+use rustc_ast::ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::{sym, Symbol};
use serde::ser::{Serialize, SerializeStruct, Serializer};
+use crate::clean;
use crate::clean::types::{
- FnDecl, FnRetTy, GenericBound, Generics, GetDefId, Type, TypeKind, WherePredicate,
+ AttributesExt, FnDecl, FnRetTy, GenericBound, Generics, GetDefId, Type, WherePredicate,
};
-use crate::clean::{self, AttributesExt};
use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
use crate::html::markdown::short_markdown_summary;
crate fn extern_location(
e: &clean::ExternalCrate,
extern_url: Option<&str>,
+ ast_attrs: &[ast::Attribute],
dst: &Path,
+ tcx: TyCtxt<'_>,
) -> ExternalLocation {
use ExternalLocation::*;
// See if there's documentation generated into the local directory
- let local_location = dst.join(&*e.name.as_str());
+ let local_location = dst.join(&*e.name(tcx).as_str());
if local_location.is_dir() {
return Local;
}
// Failing that, see if there's an attribute specifying where to find this
// external crate
- e.attrs
+ ast_attrs
.lists(sym::doc)
.filter(|a| a.has_name(sym::html_root_url))
.filter_map(|a| a.value_str())
arg: &Type,
tcx: TyCtxt<'tcx>,
recurse: i32,
- res: &mut FxHashSet<(Type, TypeKind)>,
+ res: &mut FxHashSet<(Type, ItemType)>,
) -> usize {
- fn insert(res: &mut FxHashSet<(Type, TypeKind)>, tcx: TyCtxt<'_>, ty: Type) -> usize {
+ fn insert(res: &mut FxHashSet<(Type, ItemType)>, tcx: TyCtxt<'_>, ty: Type) -> usize {
if let Some(kind) = ty.def_id().map(|did| tcx.def_kind(did).into()) {
res.insert((ty, kind));
1
} else if ty.is_primitive() {
// This is a primitive, let's store it as such.
- res.insert((ty, TypeKind::Primitive));
+ res.insert((ty, ItemType::Primitive));
1
} else {
0
generics: &Generics,
decl: &FnDecl,
tcx: TyCtxt<'tcx>,
-) -> (Vec<(Type, TypeKind)>, Vec<(Type, TypeKind)>) {
+) -> (Vec<(Type, ItemType)>, Vec<(Type, ItemType)>) {
let mut all_types = FxHashSet::default();
for arg in decl.inputs.values.iter() {
if arg.type_.is_self_type() {
use rustc_session::Session;
use rustc_span::edition::Edition;
use rustc_span::source_map::FileName;
-use rustc_span::{symbol::sym, Symbol};
+use rustc_span::symbol::sym;
use super::cache::{build_index, ExternalLocation};
use super::print_item::{full_path, item_path, print_item};
use super::write_shared::write_shared;
-use super::{print_sidebar, settings, AllTypes, NameDoc, StylePath, BASIC_KEYWORDS, CURRENT_DEPTH};
+use super::{print_sidebar, settings, AllTypes, NameDoc, StylePath, BASIC_KEYWORDS};
-use crate::clean::{self, AttributesExt};
+use crate::clean;
use crate::config::RenderOptions;
use crate::docfs::{DocFS, PathError};
use crate::error::Error;
crate struct Context<'tcx> {
/// Current hierarchy of components leading down to what's currently being
/// rendered
- pub(super) current: Vec<String>,
+ pub(crate) current: Vec<String>,
/// The current destination folder of where HTML artifacts should be placed.
/// This changes as the context descends into the module hierarchy.
pub(super) dst: PathBuf,
crate static_root_path: Option<String>,
/// The fs handle we are working with.
crate fs: DocFS,
- /// The default edition used to parse doctests.
- crate edition: Edition,
pub(super) codes: ErrorCodes,
pub(super) playground: Option<markdown::Playground>,
all: RefCell<AllTypes>,
crate fn maybe_collapsed_doc_value<'a>(&self, item: &'a clean::Item) -> Option<String> {
if self.collapsed { item.collapsed_doc_value() } else { item.doc_value() }
}
+
+ crate fn edition(&self) -> Edition {
+ self.tcx.sess.edition()
+ }
}
impl<'tcx> Context<'tcx> {
- pub(super) fn tcx(&self) -> TyCtxt<'tcx> {
+ pub(crate) fn tcx(&self) -> TyCtxt<'tcx> {
self.shared.tcx
}
+ pub(crate) fn cache(&self) -> &Cache {
+ &self.cache
+ }
+
fn sess(&self) -> &'tcx Session {
&self.shared.tcx.sess
}
"../".repeat(self.current.len())
}
- fn render_item(&self, it: &clean::Item, pushname: bool) -> String {
- // A little unfortunate that this is done like this, but it sure
- // does make formatting *a lot* nicer.
- CURRENT_DEPTH.with(|slot| {
- slot.set(self.current.len());
- });
-
- let mut title = if it.is_primitive() || it.is_keyword() {
- // No need to include the namespace for primitive types and keywords
- String::new()
- } else {
- self.current.join("::")
- };
- if pushname {
- if !title.is_empty() {
- title.push_str("::");
- }
+ fn render_item(&self, it: &clean::Item, is_module: bool) -> String {
+ let mut title = String::new();
+ if !is_module {
title.push_str(&it.name.unwrap().as_str());
}
+ if !it.is_primitive() && !it.is_keyword() {
+ if !is_module {
+ title.push_str(" in ");
+ }
+ // No need to include the namespace for primitive types and keywords
+ title.push_str(&self.current.join("::"));
+ };
title.push_str(" - Rust");
let tyname = it.type_();
let desc = it.doc_value().as_ref().map(|doc| plain_text_summary(&doc));
/// may happen, for example, with externally inlined items where the source
/// of their crate documentation isn't known.
pub(super) fn src_href(&self, item: &clean::Item) -> Option<String> {
- if item.span.is_dummy() {
+ if item.span(self.tcx()).is_dummy() {
return None;
}
let mut root = self.root_path();
let mut path = String::new();
- let cnum = item.span.cnum(self.sess());
+ let cnum = item.span(self.tcx()).cnum(self.sess());
// We can safely ignore synthetic `SourceFile`s.
- let file = match item.span.filename(self.sess()) {
+ let file = match item.span(self.tcx()).filename(self.sess()) {
FileName::Real(ref path) => path.local_path().to_path_buf(),
_ => return None,
};
(&*symbol, &path)
};
- let loline = item.span.lo(self.sess()).line;
- let hiline = item.span.hi(self.sess()).line;
+ let loline = item.span(self.tcx()).lo(self.sess()).line;
+ let hiline = item.span(self.tcx()).hi(self.sess()).line;
let lines =
if loline == hiline { loline.to_string() } else { format!("{}-{}", loline, hiline) };
Some(format!(
fn init(
mut krate: clean::Crate,
options: RenderOptions,
- edition: Edition,
mut cache: Cache,
tcx: TyCtxt<'tcx>,
) -> Result<(Self, clean::Crate), Error> {
resource_suffix,
static_root_path,
fs: DocFS::new(sender),
- edition,
codes: ErrorCodes::from(unstable_features.is_nightly_build()),
playground,
all: RefCell::new(AllTypes::new()),
cache: Rc::new(cache),
};
- CURRENT_DEPTH.with(|s| s.set(0));
-
// Write shared runs within a flock; disable thread dispatching of IO temporarily.
Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true);
write_shared(&cx, &krate, index, &md_opts)?;
}
}
- fn after_krate(
- &mut self,
- crate_name: Symbol,
- diag: &rustc_errors::Handler,
- ) -> Result<(), Error> {
+ fn after_krate(&mut self) -> Result<(), Error> {
+ let crate_name = self.tcx().crate_name(LOCAL_CRATE);
let final_file = self.dst.join(&*crate_name.as_str()).join("all.html");
let settings_file = self.dst.join("settings.html");
// Flush pending errors.
Rc::get_mut(&mut self.shared).unwrap().fs.close();
- let nb_errors = self.shared.errors.iter().map(|err| diag.struct_err(&err).emit()).count();
+ let nb_errors =
+ self.shared.errors.iter().map(|err| self.tcx().sess.struct_err(&err).emit()).count();
if nb_errors > 0 {
Err(Error::new(io::Error::new(io::ErrorKind::Other, "I/O error"), ""))
} else {
}
}
- fn mod_item_in(&mut self, item: &clean::Item, item_name: &str) -> Result<(), Error> {
+ fn mod_item_in(&mut self, item: &clean::Item) -> Result<(), Error> {
// Stripped modules survive the rustdoc passes (i.e., `strip-private`)
// if they contain impls for public types. These modules can also
// contain items such as publicly re-exported structures.
self.render_redirect_pages = item.is_stripped();
}
let scx = &self.shared;
- self.dst.push(item_name);
- self.current.push(item_name.to_owned());
+ let item_name = item.name.as_ref().unwrap().to_string();
+ self.dst.push(&item_name);
+ self.current.push(item_name);
info!("Recursing into {}", self.dst.display());
- let buf = self.render_item(item, false);
+ let buf = self.render_item(item, true);
// buf will be empty if the module is stripped and there is no redirect for it
if !buf.is_empty() {
self.shared.ensure_dir(&self.dst)?;
Ok(())
}
- fn mod_item_out(&mut self, _item_name: &str) -> Result<(), Error> {
+ fn mod_item_out(&mut self) -> Result<(), Error> {
info!("Recursed; leaving {}", self.dst.display());
// Go back to where we were at
self.render_redirect_pages = item.is_stripped();
}
- let buf = self.render_item(&item, true);
+ let buf = self.render_item(&item, false);
// buf will be empty if the item is stripped and there is no redirect for it
if !buf.is_empty() {
let name = item.name.as_ref().unwrap();
crate use context::*;
crate use write_shared::FILES_UNVERSIONED;
-use std::cell::Cell;
use std::collections::VecDeque;
use std::default::Default;
use std::fmt;
use std::str;
use std::string::ToString;
-use itertools::Itertools;
use rustc_ast_pretty::pprust;
use rustc_attr::{Deprecation, StabilityLevel};
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def_id::DefId;
use rustc_hir::Mutability;
use rustc_middle::middle::stability;
-use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::{kw, sym, Symbol};
use serde::ser::SerializeSeq;
use serde::{Serialize, Serializer};
-use crate::clean::{self, GetDefId, RenderedLink, SelfTy, TypeKind};
+use crate::clean::{self, GetDefId, RenderedLink, SelfTy};
use crate::docfs::PathError;
use crate::error::Error;
use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
-use crate::formats::{AssocItemRender, FormatRenderer, Impl, RenderMode};
+use crate::formats::{AssocItemRender, Impl, RenderMode};
use crate::html::escape::Escape;
use crate::html::format::{
href, print_abi_with_space, print_default_space, print_generic_bounds, print_where_clause,
#[derive(Debug)]
crate struct TypeWithKind {
ty: RenderType,
- kind: TypeKind,
+ kind: ItemType,
}
-impl From<(RenderType, TypeKind)> for TypeWithKind {
- fn from(x: (RenderType, TypeKind)) -> TypeWithKind {
+impl From<(RenderType, ItemType)> for TypeWithKind {
+ fn from(x: (RenderType, ItemType)) -> TypeWithKind {
TypeWithKind { ty: x.0, kind: x.1 }
}
}
where
S: Serializer,
{
- (&self.ty.name, ItemType::from(self.kind)).serialize(serializer)
+ (&self.ty.name, self.kind).serialize(serializer)
}
}
crate disabled: bool,
}
-thread_local!(crate static CURRENT_DEPTH: Cell<usize> = Cell::new(0));
-
fn write_srclink(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) {
if let Some(l) = cx.src_href(item) {
write!(buf, "<a class=\"srclink\" href=\"{}\" title=\"goto source code\">[src]</a>", l)
],
)
.into(),
- (
- "Auto-hide item declarations",
- vec![
- ("auto-hide-struct", "Auto-hide structs declaration", true),
- ("auto-hide-enum", "Auto-hide enums declaration", false),
- ("auto-hide-union", "Auto-hide unions declaration", true),
- ("auto-hide-trait", "Auto-hide traits declaration", true),
- ("auto-hide-macro", "Auto-hide macros declaration", false),
- ],
- )
- .into(),
- ("auto-hide-attributes", "Auto-hide item attributes.", true).into(),
+ ("auto-hide-large-items", "Auto-hide item contents for large items.", true).into(),
("auto-hide-method-docs", "Auto-hide item methods' documentation", false).into(),
("auto-hide-trait-implementations", "Auto-hide trait implementation documentation", true)
.into(),
info!("Documenting {}", name);
}
document_item_info(w, cx, item, false, parent);
- document_full(w, item, cx, "", false);
+ document_full(w, item, cx, false);
}
/// Render md_text as markdown.
cx: &Context<'_>,
md_text: &str,
links: Vec<RenderedLink>,
- prefix: &str,
is_hidden: bool,
) {
let mut ids = cx.id_map.borrow_mut();
write!(
w,
- "<div class=\"docblock{}\">{}{}</div>",
+ "<div class=\"docblock{}\">{}</div>",
if is_hidden { " hidden" } else { "" },
- prefix,
Markdown(
md_text,
&links,
&mut ids,
cx.shared.codes,
- cx.shared.edition,
+ cx.shared.edition(),
&cx.shared.playground
)
.into_string()
item: &clean::Item,
cx: &Context<'_>,
link: AssocItemLink<'_>,
- prefix: &str,
is_hidden: bool,
- parent: Option<&clean::Item>,
+ parent: &clean::Item,
show_def_docs: bool,
) {
- document_item_info(w, cx, item, is_hidden, parent);
+ document_item_info(w, cx, item, is_hidden, Some(parent));
if !show_def_docs {
return;
}
if let Some(s) = item.doc_value() {
- let mut summary_html = MarkdownSummaryLine(&s, &item.links(&cx.cache)).into_string();
+ let mut summary_html = MarkdownSummaryLine(&s, &item.links(cx)).into_string();
if s.contains('\n') {
- let link =
- format!(r#" <a href="{}">Read more</a>"#, naive_assoc_href(item, link, cx.cache()));
+ let link = format!(r#" <a href="{}">Read more</a>"#, naive_assoc_href(item, link, cx));
if let Some(idx) = summary_html.rfind("</p>") {
summary_html.insert_str(idx, &link);
write!(
w,
- "<div class='docblock{}'>{}{}</div>",
+ "<div class='docblock{}'>{}</div>",
if is_hidden { " hidden" } else { "" },
- prefix,
summary_html,
);
- } else if !prefix.is_empty() {
- write!(
- w,
- "<div class=\"docblock{}\">{}</div>",
- if is_hidden { " hidden" } else { "" },
- prefix
- );
}
}
-fn document_full(
- w: &mut Buffer,
- item: &clean::Item,
- cx: &Context<'_>,
- prefix: &str,
- is_hidden: bool,
-) {
+fn document_full(w: &mut Buffer, item: &clean::Item, cx: &Context<'_>, is_hidden: bool) {
if let Some(s) = cx.shared.maybe_collapsed_doc_value(item) {
debug!("Doc block: =====\n{}\n=====", s);
- render_markdown(w, cx, &*s, item.links(&cx.cache), prefix, is_hidden);
- } else if !prefix.is_empty() {
- if is_hidden {
- w.write_str("<div class=\"docblock hidden\">");
- } else {
- w.write_str("<div class=\"docblock\">");
- }
- w.write_str(prefix);
- w.write_str("</div>");
+ render_markdown(w, cx, &s, item.links(cx), is_hidden);
}
}
}
fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<String> {
- let cfg = match (&item.attrs.cfg, parent.and_then(|p| p.attrs.cfg.as_ref())) {
+ let cfg = match (&item.cfg, parent.and_then(|p| p.cfg.as_ref())) {
(Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
(cfg, _) => cfg.as_deref().cloned(),
};
- debug!(
- "Portability {:?} - {:?} = {:?}",
- item.attrs.cfg,
- parent.and_then(|p| p.attrs.cfg.as_ref()),
- cfg
- );
+ debug!("Portability {:?} - {:?} = {:?}", item.cfg, parent.and_then(|p| p.cfg.as_ref()), cfg);
Some(format!("<div class=\"stab portability\">{}</div>", cfg?.render_long_html()))
}
¬e,
&mut ids,
error_codes,
- cx.shared.edition,
+ cx.shared.edition(),
&cx.shared.playground,
);
message.push_str(&format!(": {}", html.into_string()));
&unstable_reason.as_str(),
&mut ids,
error_codes,
- cx.shared.edition,
+ cx.shared.edition(),
&cx.shared.playground,
)
.into_string()
w.write_str(&impls.join(""));
}
-fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>, cache: &Cache) -> String {
+fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>) -> String {
use crate::formats::item_type::ItemType::*;
let name = it.name.as_ref().unwrap();
AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id),
AssocItemLink::Anchor(None) => anchor,
AssocItemLink::GotoSource(did, _) => {
- href(did, cache).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor)
+ href(did, cx).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor)
}
}
}
extra: &str,
cx: &Context<'_>,
) {
- let cache = cx.cache();
- let tcx = cx.tcx();
write!(
w,
"{}{}const <a href=\"{}\" class=\"constant\"><b>{}</b></a>: {}",
extra,
- it.visibility.print_with_space(tcx, it.def_id, cache),
- naive_assoc_href(it, link, cache),
+ it.visibility.print_with_space(it.def_id, cx),
+ naive_assoc_href(it, link, cx),
it.name.as_ref().unwrap(),
- ty.print(cache, tcx)
+ ty.print(cx)
);
}
default: Option<&clean::Type>,
link: AssocItemLink<'_>,
extra: &str,
- cache: &Cache,
- tcx: TyCtxt<'_>,
+ cx: &Context<'_>,
) {
write!(
w,
"{}type <a href=\"{}\" class=\"type\">{}</a>",
extra,
- naive_assoc_href(it, link, cache),
+ naive_assoc_href(it, link, cx),
it.name.as_ref().unwrap()
);
if !bounds.is_empty() {
- write!(w, ": {}", print_generic_bounds(bounds, cache, tcx))
+ write!(w, ": {}", print_generic_bounds(bounds, cx))
}
if let Some(default) = default {
- write!(w, " = {}", default.print(cache, tcx))
+ write!(w, " = {}", default.print(cx))
}
}
parent: ItemType,
cx: &Context<'_>,
) {
- let cache = cx.cache();
- let tcx = cx.tcx();
let name = meth.name.as_ref().unwrap();
let href = match link {
AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id),
ItemType::TyMethod
};
- href(did, cache)
+ href(did, cx)
.map(|p| format!("{}#{}.{}", p.0, ty, name))
.unwrap_or_else(|| format!("#{}.{}", ty, name))
}
};
- let vis = meth.visibility.print_with_space(tcx, meth.def_id, cache).to_string();
+ let vis = meth.visibility.print_with_space(meth.def_id, cx).to_string();
let constness = header.constness.print_with_space();
let asyncness = header.asyncness.print_with_space();
let unsafety = header.unsafety.print_with_space();
let defaultness = print_default_space(meth.is_default());
let abi = print_abi_with_space(header.abi).to_string();
// NOTE: `{:#}` does not print HTML formatting, `{}` does. So `g.print` can't be reused between the length calculation and `write!`.
- let generics_len = format!("{:#}", g.print(cache, tcx)).len();
+ let generics_len = format!("{:#}", g.print(cx)).len();
let mut header_len = "fn ".len()
+ vis.len()
+ constness.len()
+ name.as_str().len()
+ generics_len;
- let (indent, end_newline) = if parent == ItemType::Trait {
+ let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
header_len += 4;
- (4, false)
+ let indent_str = " ";
+ render_attributes_in_pre(w, meth, indent_str);
+ (4, indent_str, false)
} else {
- (0, true)
+ render_attributes_in_code(w, meth);
+ (0, "", true)
};
- render_attributes(w, meth, false);
w.reserve(header_len + "<a href=\"\" class=\"fnname\">{".len() + "</a>".len());
write!(
w,
"{}{}{}{}{}{}{}fn <a href=\"{href}\" class=\"fnname\">{name}</a>\
{generics}{decl}{notable_traits}{where_clause}",
- if parent == ItemType::Trait { " " } else { "" },
+ indent_str,
vis,
constness,
asyncness,
abi,
href = href,
name = name,
- generics = g.print(cache, tcx),
- decl = d.full_print(cache, tcx, header_len, indent, header.asyncness),
- notable_traits = notable_traits_decl(&d, cache, tcx),
- where_clause = print_where_clause(g, cache, tcx, indent, end_newline),
+ generics = g.print(cx),
+ decl = d.full_print(header_len, indent, header.asyncness, cx),
+ notable_traits = notable_traits_decl(&d, cx),
+ where_clause = print_where_clause(g, cx, indent, end_newline),
)
}
match *item.kind {
default.as_ref(),
link,
if parent == ItemType::Trait { " " } else { "" },
- cx.cache(),
- cx.tcx(),
+ cx,
),
_ => panic!("render_assoc_item called on non-associated-item"),
}
const ALLOWED_ATTRIBUTES: &[Symbol] = &[
sym::export_name,
- sym::lang,
sym::link_section,
sym::must_use,
sym::no_mangle,
sym::non_exhaustive,
];
-// The `top` parameter is used when generating the item declaration to ensure it doesn't have a
-// left padding. For example:
-//
-// #[foo] <----- "top" attribute
-// struct Foo {
-// #[bar] <---- not "top" attribute
-// bar: usize,
-// }
-fn render_attributes(w: &mut Buffer, it: &clean::Item, top: bool) {
- let attrs = it
- .attrs
+fn attributes(it: &clean::Item) -> Vec<String> {
+ it.attrs
.other_attrs
.iter()
.filter_map(|attr| {
if ALLOWED_ATTRIBUTES.contains(&attr.name_or_empty()) {
- Some(pprust::attribute_to_string(&attr))
+ Some(pprust::attribute_to_string(&attr).replace("\n", "").replace(" ", " "))
} else {
None
}
})
- .join("\n");
+ .collect()
+}
- if !attrs.is_empty() {
- write!(
- w,
- "<span class=\"docblock attributes{}\">{}</span>",
- if top { " top-attr" } else { "" },
- &attrs
- );
+// When an attribute is rendered inside a `<pre>` tag, it is formatted using
+// a whitespace prefix and newline.
+fn render_attributes_in_pre(w: &mut Buffer, it: &clean::Item, prefix: &str) {
+ for a in attributes(it) {
+ write!(w, "{}{}\n", prefix, a);
+ }
+}
+
+// When an attribute is rendered inside a <code> tag, it is formatted using
+// a div to produce a newline after it.
+fn render_attributes_in_code(w: &mut Buffer, it: &clean::Item) {
+ for a in attributes(it) {
+ write!(w, "<div class=\"code-attribute\">{}</div>", a);
}
}
RenderMode::Normal
}
AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
- let id = cx.derive_id(small_url_encode(format!(
- "deref-methods-{:#}",
- type_.print(cache, tcx)
- )));
- debug!("Adding {} to deref id map", type_.print(cache, tcx));
+ let id =
+ cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx))));
+ debug!("Adding {} to deref id map", type_.print(cx));
cx.deref_id_map.borrow_mut().insert(type_.def_id_full(cache).unwrap(), id.clone());
write!(
w,
<a href=\"#{id}\" class=\"anchor\"></a>\
</h2>",
id = id,
- trait_ = trait_.print(cache, tcx),
- type_ = type_.print(cache, tcx),
+ trait_ = trait_.print(cx),
+ type_ = type_.print(cx),
);
RenderMode::ForDeref { mut_: deref_mut_ }
}
}
}
-fn notable_traits_decl(decl: &clean::FnDecl, cache: &Cache, tcx: TyCtxt<'_>) -> String {
+fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String {
let mut out = Buffer::html();
let mut trait_ = String::new();
- if let Some(did) = decl.output.def_id_full(cache) {
- if let Some(impls) = cache.impls.get(&did) {
+ if let Some(did) = decl.output.def_id_full(cx.cache()) {
+ if let Some(impls) = cx.cache().impls.get(&did) {
for i in impls {
let impl_ = i.inner_impl();
- if impl_
- .trait_
- .def_id()
- .map_or(false, |d| cache.traits.get(&d).map(|t| t.is_notable).unwrap_or(false))
- {
+ if impl_.trait_.def_id().map_or(false, |d| {
+ cx.cache().traits.get(&d).map(|t| t.is_notable).unwrap_or(false)
+ }) {
if out.is_empty() {
write!(
&mut out,
"<h3 class=\"notable\">Notable traits for {}</h3>\
<code class=\"content\">",
- impl_.for_.print(cache, tcx)
+ impl_.for_.print(cx)
);
- trait_.push_str(&impl_.for_.print(cache, tcx).to_string());
+ trait_.push_str(&impl_.for_.print(cx).to_string());
}
//use the "where" class here to make it small
write!(
&mut out,
"<span class=\"where fmt-newline\">{}</span>",
- impl_.print(cache, false, tcx)
+ impl_.print(false, cx)
);
- let t_did = impl_.trait_.def_id_full(cache).unwrap();
+ let t_did = impl_.trait_.def_id_full(cx.cache()).unwrap();
for it in &impl_.items {
if let clean::TypedefItem(ref tydef, _) = *it.kind {
out.push_str("<span class=\"where fmt-newline\"> ");
Some(&tydef.type_),
AssocItemLink::GotoSource(t_did, &FxHashSet::default()),
"",
- cache,
- tcx,
+ cx,
);
out.push_str(";</span>");
}
// in documentation pages for trait with automatic implementations like "Send" and "Sync".
aliases: &[String],
) {
- let traits = &cx.cache.traits;
let tcx = cx.tcx();
let cache = cx.cache();
+ let traits = &cache.traits;
let trait_ = i.trait_did_full(cache).map(|did| &traits[&did]);
-
- if render_mode == RenderMode::Normal {
- let id = cx.derive_id(match i.inner_impl().trait_ {
- Some(ref t) => {
- if is_on_foreign_type {
- get_id_for_impl_on_foreign_type(&i.inner_impl().for_, t, cache, tcx)
- } else {
- format!("impl-{}", small_url_encode(format!("{:#}", t.print(cache, tcx))))
- }
- }
- None => "impl".to_string(),
- });
- let aliases = if aliases.is_empty() {
- String::new()
- } else {
- format!(" aliases=\"{}\"", aliases.join(","))
- };
- if let Some(use_absolute) = use_absolute {
- write!(w, "<h3 id=\"{}\" class=\"impl\"{}><code class=\"in-band\">", id, aliases);
- write!(w, "{}", i.inner_impl().print(cache, use_absolute, tcx));
- if show_def_docs {
- for it in &i.inner_impl().items {
- if let clean::TypedefItem(ref tydef, _) = *it.kind {
- w.write_str("<span class=\"where fmt-newline\"> ");
- assoc_type(
- w,
- it,
- &[],
- Some(&tydef.type_),
- AssocItemLink::Anchor(None),
- "",
- cache,
- tcx,
- );
- w.write_str(";</span>");
- }
- }
- }
- w.write_str("</code>");
- } else {
- write!(
- w,
- "<h3 id=\"{}\" class=\"impl\"{}><code class=\"in-band\">{}</code>",
- id,
- aliases,
- i.inner_impl().print(cache, false, tcx)
- );
- }
- write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
- render_stability_since_raw(
- w,
- i.impl_item.stable_since(tcx).as_deref(),
- i.impl_item.const_stable_since(tcx).as_deref(),
- outer_version,
- outer_const_version,
- );
- write_srclink(cx, &i.impl_item, w);
- w.write_str("</h3>");
-
- if trait_.is_some() {
- if let Some(portability) = portability(&i.impl_item, Some(parent)) {
- write!(w, "<div class=\"item-info\">{}</div>", portability);
- }
- }
-
- if let Some(ref dox) = cx.shared.maybe_collapsed_doc_value(&i.impl_item) {
- let mut ids = cx.id_map.borrow_mut();
- write!(
- w,
- "<div class=\"docblock\">{}</div>",
- Markdown(
- &*dox,
- &i.impl_item.links(&cx.cache),
- &mut ids,
- cx.shared.codes,
- cx.shared.edition,
- &cx.shared.playground
- )
- .into_string()
- );
- }
- }
+ let mut close_tags = String::new();
fn doc_impl_item(
w: &mut Buffer,
Some(&tydef.type_),
link.anchor(if trait_.is_some() { &source_id } else { &id }),
"",
- cx.cache(),
- tcx,
+ cx,
);
w.write_str("</code>");
write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
default.as_ref(),
link.anchor(if trait_.is_some() { &source_id } else { &id }),
"",
- cx.cache(),
- tcx,
+ cx,
);
w.write_str("</code>");
write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
// because impls can't have a stability.
if item.doc_value().is_some() {
document_item_info(w, cx, it, is_hidden, Some(parent));
- document_full(w, item, cx, "", is_hidden);
+ document_full(w, item, cx, is_hidden);
} else {
// In case the item isn't documented,
// provide short documentation from the trait.
- document_short(
- w,
- it,
- cx,
- link,
- "",
- is_hidden,
- Some(parent),
- show_def_docs,
- );
+ document_short(w, it, cx, link, is_hidden, parent, show_def_docs);
}
}
} else {
document_item_info(w, cx, item, is_hidden, Some(parent));
if show_def_docs {
- document_full(w, item, cx, "", is_hidden);
+ document_full(w, item, cx, is_hidden);
}
}
} else {
- document_short(w, item, cx, link, "", is_hidden, Some(parent), show_def_docs);
+ document_short(w, item, cx, link, is_hidden, parent, show_def_docs);
}
}
}
- w.write_str("<div class=\"impl-items\">");
+ let mut impl_items = Buffer::empty_from(w);
for trait_item in &i.inner_impl().items {
doc_impl_item(
- w,
+ &mut impl_items,
cx,
trait_item,
if trait_.is_some() { &i.impl_item } else { parent },
if show_default_items {
if let Some(t) = trait_ {
render_default_items(
- w,
+ &mut impl_items,
cx,
&t.trait_,
&i.inner_impl(),
);
}
}
- w.write_str("</div>");
+ let details_str = if impl_items.is_empty() {
+ ""
+ } else {
+ "<details class=\"rustdoc-toggle implementors-toggle\" open><summary>"
+ };
+ if render_mode == RenderMode::Normal {
+ let id = cx.derive_id(match i.inner_impl().trait_ {
+ Some(ref t) => {
+ if is_on_foreign_type {
+ get_id_for_impl_on_foreign_type(&i.inner_impl().for_, t, cx)
+ } else {
+ format!("impl-{}", small_url_encode(format!("{:#}", t.print(cx))))
+ }
+ }
+ None => "impl".to_string(),
+ });
+ let aliases = if aliases.is_empty() {
+ String::new()
+ } else {
+ format!(" aliases=\"{}\"", aliases.join(","))
+ };
+ if let Some(use_absolute) = use_absolute {
+ write!(
+ w,
+ "{}<h3 id=\"{}\" class=\"impl\"{}><code class=\"in-band\">",
+ details_str, id, aliases
+ );
+ if !impl_items.is_empty() {
+ close_tags.insert_str(0, "</details>");
+ }
+ write!(w, "{}", i.inner_impl().print(use_absolute, cx));
+ if show_def_docs {
+ for it in &i.inner_impl().items {
+ if let clean::TypedefItem(ref tydef, _) = *it.kind {
+ w.write_str("<span class=\"where fmt-newline\"> ");
+ assoc_type(
+ w,
+ it,
+ &[],
+ Some(&tydef.type_),
+ AssocItemLink::Anchor(None),
+ "",
+ cx,
+ );
+ w.write_str(";</span>");
+ }
+ }
+ }
+ w.write_str("</code>");
+ } else {
+ write!(
+ w,
+ "{}<h3 id=\"{}\" class=\"impl\"{}><code class=\"in-band\">{}</code>",
+ details_str,
+ id,
+ aliases,
+ i.inner_impl().print(false, cx)
+ );
+ if !impl_items.is_empty() {
+ close_tags.insert_str(0, "</details>");
+ }
+ }
+ write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+ render_stability_since_raw(
+ w,
+ i.impl_item.stable_since(tcx).as_deref(),
+ i.impl_item.const_stable_since(tcx).as_deref(),
+ outer_version,
+ outer_const_version,
+ );
+ write_srclink(cx, &i.impl_item, w);
+ if impl_items.is_empty() {
+ w.write_str("</h3>");
+ } else {
+ w.write_str("</h3></summary>");
+ }
+
+ if trait_.is_some() {
+ if let Some(portability) = portability(&i.impl_item, Some(parent)) {
+ write!(w, "<div class=\"item-info\">{}</div>", portability);
+ }
+ }
+
+ if let Some(ref dox) = cx.shared.maybe_collapsed_doc_value(&i.impl_item) {
+ let mut ids = cx.id_map.borrow_mut();
+ write!(
+ w,
+ "<div class=\"docblock\">{}</div>",
+ Markdown(
+ &*dox,
+ &i.impl_item.links(cx),
+ &mut ids,
+ cx.shared.codes,
+ cx.shared.edition(),
+ &cx.shared.playground
+ )
+ .into_string()
+ );
+ }
+ }
+ if !impl_items.is_empty() {
+ w.write_str("<div class=\"impl-items\">");
+ w.push_buffer(impl_items);
+ close_tags.insert_str(0, "</div>");
+ }
+ w.write_str(&close_tags);
}
fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
if let Some(v) = cx.cache.impls.get(&it.def_id) {
let mut used_links = FxHashSet::default();
- let tcx = cx.tcx();
let cache = cx.cache();
{
.iter()
.filter_map(|it| {
if let Some(ref i) = it.inner_impl().trait_ {
- let i_display = format!("{:#}", i.print(cache, tcx));
+ let i_display = format!("{:#}", i.print(cx));
let out = Escape(&i_display);
- let encoded = small_url_encode(format!("{:#}", i.print(cache, tcx)));
+ let encoded = small_url_encode(format!("{:#}", i.print(cx)));
let generated = format!(
"<a href=\"#impl-{}\">{}{}</a>",
encoded,
fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &Vec<Impl>) {
let c = cx.cache();
- let tcx = cx.tcx();
debug!("found Deref: {:?}", impl_);
if let Some((target, real_target)) =
out,
"<a class=\"sidebar-title\" href=\"#{}\">Methods from {}<Target={}></a>",
id,
- Escape(&format!(
- "{:#}",
- impl_.inner_impl().trait_.as_ref().unwrap().print(c, tcx)
- )),
- Escape(&format!("{:#}", real_target.print(c, tcx))),
+ Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print(cx))),
+ Escape(&format!("{:#}", real_target.print(cx))),
);
// We want links' order to be reproducible so we don't use unstable sort.
ret.sort();
fn get_id_for_impl_on_foreign_type(
for_: &clean::Type,
trait_: &clean::Type,
- cache: &Cache,
- tcx: TyCtxt<'_>,
+ cx: &Context<'_>,
) -> String {
- small_url_encode(format!(
- "impl-{:#}-for-{:#}",
- trait_.print(cache, tcx),
- for_.print(cache, tcx)
- ))
+ small_url_encode(format!("impl-{:#}-for-{:#}", trait_.print(cx), for_.print(cx),))
}
-fn extract_for_impl_name(
- item: &clean::Item,
- cache: &Cache,
- tcx: TyCtxt<'_>,
-) -> Option<(String, String)> {
+fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
match *item.kind {
clean::ItemKind::ImplItem(ref i) => {
if let Some(ref trait_) = i.trait_ {
+ // Alternative format produces no URLs,
+ // so this parameter does nothing.
Some((
- format!("{:#}", i.for_.print(cache, tcx)),
- get_id_for_impl_on_foreign_type(&i.for_, trait_, cache, tcx),
+ format!("{:#}", i.for_.print(cx)),
+ get_id_for_impl_on_foreign_type(&i.for_, trait_, cx),
))
} else {
None
if let Some(implementors) = cx.cache.implementors.get(&it.def_id) {
let cache = cx.cache();
- let tcx = cx.tcx();
let mut res = implementors
.iter()
.filter(|i| {
.def_id_full(cache)
.map_or(false, |d| !cx.cache.paths.contains_key(&d))
})
- .filter_map(|i| extract_for_impl_name(&i.impl_item, cache, tcx))
+ .filter_map(|i| extract_for_impl_name(&i.impl_item, cx))
.collect::<Vec<_>>();
if !res.is_empty() {
+use clean::AttributesExt;
+
use std::cmp::Ordering;
use rustc_data_structures::fx::FxHashMap;
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, render_impl,
- render_stability_since_raw, write_srclink, AssocItemLink, Context,
+ render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre,
+ render_impl, render_stability_since_raw, write_srclink, AssocItemLink, Context,
};
use crate::clean::{self, GetDefId};
-use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
-use crate::formats::{AssocItemRender, FormatRenderer, Impl, RenderMode};
+use crate::formats::{AssocItemRender, Impl, RenderMode};
use crate::html::escape::Escape;
use crate::html::format::{print_abi_with_space, print_where_clause, Buffer, PrintWithSpace};
use crate::html::highlight;
// Write the breadcrumb trail header for the top
buf.write_str("<h1 class=\"fqn\"><span class=\"in-band\">");
let name = match *item.kind {
- clean::ModuleItem(ref m) => {
- if m.is_crate {
+ clean::ModuleItem(_) => {
+ if item.is_crate() {
"Crate "
} else {
"Module "
}
}
+/// For large structs, enums, unions, etc, determine whether to hide their fields
+fn should_hide_fields(n_fields: usize) -> bool {
+ n_fields > 12
+}
+
+fn toggle_open(w: &mut Buffer, text: &str) {
+ write!(
+ w,
+ "<details class=\"rustdoc-toggle type-contents-toggle\">\
+ <summary class=\"hideme\">\
+ <span>Show {}</span>\
+ </summary>",
+ text
+ );
+}
+
+fn toggle_close(w: &mut Buffer) {
+ w.write_str("</details>");
+}
+
fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) {
document(w, cx, item, None);
Some(ref src) => write!(
w,
"<tr><td><code>{}extern crate {} as {};",
- myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()),
- anchor(myitem.def_id, &*src.as_str(), cx.cache()),
+ myitem.visibility.print_with_space(myitem.def_id, cx),
+ anchor(myitem.def_id, &*src.as_str(), cx),
myitem.name.as_ref().unwrap(),
),
None => write!(
w,
"<tr><td><code>{}extern crate {};",
- myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()),
- anchor(myitem.def_id, &*myitem.name.as_ref().unwrap().as_str(), cx.cache()),
+ myitem.visibility.print_with_space(myitem.def_id, cx),
+ anchor(myitem.def_id, &*myitem.name.as_ref().unwrap().as_str(), cx),
),
}
w.write_str("</code></td></tr>");
}
clean::ImportItem(ref import) => {
+ let (stab, stab_tags) = if let Some(import_def_id) = import.source.did {
+ let ast_attrs = cx.tcx().get_attrs(import_def_id);
+ let import_attrs = Box::new(clean::Attributes::from_ast(ast_attrs, None));
+
+ // Just need an item with the correct def_id and attrs
+ let import_item = clean::Item {
+ def_id: import_def_id,
+ attrs: import_attrs,
+ cfg: ast_attrs.cfg(cx.tcx().sess.diagnostic()),
+ ..myitem.clone()
+ };
+
+ let stab = import_item.stability_class(cx.tcx());
+ let stab_tags = Some(extra_info_tags(&import_item, item, cx.tcx()));
+ (stab, stab_tags)
+ } else {
+ (None, None)
+ };
+
+ let add = if stab.is_some() { " " } else { "" };
+
write!(
w,
- "<tr><td><code>{}{}</code></td></tr>",
- myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()),
- import.print(cx.cache(), cx.tcx()),
+ "<tr class=\"{stab}{add}import-item\">\
+ <td><code>{vis}{imp}</code></td>\
+ <td class=\"docblock-short\">{stab_tags}</td>\
+ </tr>",
+ stab = stab.unwrap_or_default(),
+ add = add,
+ vis = myitem.visibility.print_with_space(myitem.def_id, cx),
+ imp = import.print(cx),
+ stab_tags = stab_tags.unwrap_or_default(),
);
}
</tr>",
name = *myitem.name.as_ref().unwrap(),
stab_tags = extra_info_tags(myitem, item, cx.tcx()),
- docs = MarkdownSummaryLine(&doc_value, &myitem.links(&cx.cache)).into_string(),
+ docs = MarkdownSummaryLine(&doc_value, &myitem.links(cx)).into_string(),
class = myitem.type_(),
add = add,
- stab = stab.unwrap_or_else(String::new),
+ stab = stab.unwrap_or_default(),
unsafety_flag = unsafety_flag,
href = item_path(myitem.type_(), &myitem.name.unwrap().as_str()),
title = [full_path(cx, myitem), myitem.type_().to_string()]
tags += &tag_html("unstable", "", "Experimental");
}
- let cfg = match (&item.attrs.cfg, parent.attrs.cfg.as_ref()) {
+ let cfg = match (&item.cfg, parent.cfg.as_ref()) {
(Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
(cfg, _) => cfg.as_deref().cloned(),
};
- debug!("Portability {:?} - {:?} = {:?}", item.attrs.cfg, parent.attrs.cfg, cfg);
+ debug!("Portability {:?} - {:?} = {:?}", item.cfg, parent.cfg, cfg);
if let Some(ref cfg) = cfg {
tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html());
}
fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean::Function) {
let header_len = format!(
"{}{}{}{}{:#}fn {}{:#}",
- it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+ it.visibility.print_with_space(it.def_id, cx),
f.header.constness.print_with_space(),
f.header.asyncness.print_with_space(),
f.header.unsafety.print_with_space(),
print_abi_with_space(f.header.abi),
it.name.as_ref().unwrap(),
- f.generics.print(cx.cache(), cx.tcx())
+ f.generics.print(cx),
)
.len();
w.write_str("<pre class=\"rust fn\">");
- render_attributes(w, it, false);
+ render_attributes_in_pre(w, it, "");
write!(
w,
"{vis}{constness}{asyncness}{unsafety}{abi}fn \
{name}{generics}{decl}{notable_traits}{where_clause}</pre>",
- vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+ vis = it.visibility.print_with_space(it.def_id, cx),
constness = f.header.constness.print_with_space(),
asyncness = f.header.asyncness.print_with_space(),
unsafety = f.header.unsafety.print_with_space(),
abi = print_abi_with_space(f.header.abi),
name = it.name.as_ref().unwrap(),
- generics = f.generics.print(cx.cache(), cx.tcx()),
- where_clause = print_where_clause(&f.generics, cx.cache(), cx.tcx(), 0, true),
- decl = f.decl.full_print(cx.cache(), cx.tcx(), header_len, 0, f.header.asyncness),
- notable_traits = notable_traits_decl(&f.decl, cx.cache(), cx.tcx()),
+ generics = f.generics.print(cx),
+ where_clause = print_where_clause(&f.generics, cx, 0, true),
+ decl = f.decl.full_print(header_len, 0, f.header.asyncness, cx),
+ notable_traits = notable_traits_decl(&f.decl, cx),
);
document(w, cx, it, None)
}
fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) {
- let bounds = bounds(&t.bounds, false, cx.cache(), cx.tcx());
+ let bounds = bounds(&t.bounds, false, cx);
let types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>();
let consts = t.items.iter().filter(|m| m.is_associated_const()).collect::<Vec<_>>();
let required = t.items.iter().filter(|m| m.is_ty_method()).collect::<Vec<_>>();
// Output the trait definition
wrap_into_docblock(w, |w| {
w.write_str("<pre class=\"rust trait\">");
- render_attributes(w, it, true);
+ render_attributes_in_pre(w, it, "");
write!(
w,
"{}{}{}trait {}{}{}",
- it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+ it.visibility.print_with_space(it.def_id, cx),
t.unsafety.print_with_space(),
if t.is_auto { "auto " } else { "" },
it.name.as_ref().unwrap(),
- t.generics.print(cx.cache(), cx.tcx()),
+ t.generics.print(cx),
bounds
);
if !t.generics.where_predicates.is_empty() {
- write!(w, "{}", print_where_clause(&t.generics, cx.cache(), cx.tcx(), 0, true));
+ write!(w, "{}", print_where_clause(&t.generics, cx, 0, true));
} else {
w.write_str(" ");
}
} else {
// FIXME: we should be using a derived_id for the Anchors here
w.write_str("{\n");
+ let mut toggle = false;
+
+ // If there are too many associated types, hide _everything_
+ if should_hide_fields(types.len()) {
+ toggle = true;
+ toggle_open(w, "associated items");
+ }
for t in &types {
render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
w.write_str(";\n");
}
+ // If there are too many associated constants, hide everything after them
+ // We also do this if the types + consts is large because otherwise we could
+ // render a bunch of types and _then_ a bunch of consts just because both were
+ // _just_ under the limit
+ if !toggle && should_hide_fields(types.len() + consts.len()) {
+ toggle = true;
+ toggle_open(w, "associated constants and methods");
+ }
if !types.is_empty() && !consts.is_empty() {
w.write_str("\n");
}
render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
w.write_str(";\n");
}
+ if !toggle && should_hide_fields(required.len() + provided.len()) {
+ toggle = true;
+ toggle_open(w, "methods");
+ }
if !consts.is_empty() && !required.is_empty() {
w.write_str("\n");
}
w.write_str("<div class=\"item-spacer\"></div>");
}
}
+ if toggle {
+ toggle_close(w);
+ }
w.write_str("}");
}
w.write_str("</pre>")
let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) =
local.iter().partition(|i| i.inner_impl().synthetic);
- synthetic.sort_by(|a, b| compare_impl(a, b, cx.cache(), cx.tcx()));
- concrete.sort_by(|a, b| compare_impl(a, b, cx.cache(), cx.tcx()));
+ synthetic.sort_by(|a, b| compare_impl(a, b, cx));
+ concrete.sort_by(|a, b| compare_impl(a, b, cx));
if !foreign.is_empty() {
write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", "");
fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TraitAlias) {
w.write_str("<pre class=\"rust trait-alias\">");
- render_attributes(w, it, false);
+ render_attributes_in_pre(w, it, "");
write!(
w,
"trait {}{}{} = {};</pre>",
it.name.as_ref().unwrap(),
- t.generics.print(cx.cache(), cx.tcx()),
- print_where_clause(&t.generics, cx.cache(), cx.tcx(), 0, true),
- bounds(&t.bounds, true, cx.cache(), cx.tcx())
+ t.generics.print(cx),
+ print_where_clause(&t.generics, cx, 0, true),
+ bounds(&t.bounds, true, cx)
);
document(w, cx, it, None);
fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) {
w.write_str("<pre class=\"rust opaque\">");
- render_attributes(w, it, false);
+ render_attributes_in_pre(w, it, "");
write!(
w,
"type {}{}{where_clause} = impl {bounds};</pre>",
it.name.as_ref().unwrap(),
- t.generics.print(cx.cache(), cx.tcx()),
- where_clause = print_where_clause(&t.generics, cx.cache(), cx.tcx(), 0, true),
- bounds = bounds(&t.bounds, false, cx.cache(), cx.tcx()),
+ t.generics.print(cx),
+ where_clause = print_where_clause(&t.generics, cx, 0, true),
+ bounds = bounds(&t.bounds, false, cx),
);
document(w, cx, it, None);
fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
w.write_str("<pre class=\"rust typedef\">");
- render_attributes(w, it, false);
+ render_attributes_in_pre(w, it, "");
write!(
w,
"type {}{}{where_clause} = {type_};</pre>",
it.name.as_ref().unwrap(),
- t.generics.print(cx.cache(), cx.tcx()),
- where_clause = print_where_clause(&t.generics, cx.cache(), cx.tcx(), 0, true),
- type_ = t.type_.print(cx.cache(), cx.tcx()),
+ t.generics.print(cx),
+ where_clause = print_where_clause(&t.generics, cx, 0, true),
+ type_ = t.type_.print(cx),
);
document(w, cx, it, None);
fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) {
wrap_into_docblock(w, |w| {
w.write_str("<pre class=\"rust union\">");
- render_attributes(w, it, true);
+ render_attributes_in_pre(w, it, "");
render_union(w, it, Some(&s.generics), &s.fields, "", true, cx);
w.write_str("</pre>")
});
id = id,
name = name,
shortty = ItemType::StructField,
- ty = ty.print(cx.cache(), cx.tcx()),
+ ty = ty.print(cx),
);
if let Some(stability_class) = field.stability_class(cx.tcx()) {
write!(w, "<span class=\"stab {stab}\"></span>", stab = stability_class);
fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) {
wrap_into_docblock(w, |w| {
w.write_str("<pre class=\"rust enum\">");
- render_attributes(w, it, true);
+ render_attributes_in_pre(w, it, "");
write!(
w,
"{}enum {}{}{}",
- it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+ it.visibility.print_with_space(it.def_id, cx),
it.name.as_ref().unwrap(),
- e.generics.print(cx.cache(), cx.tcx()),
- print_where_clause(&e.generics, cx.cache(), cx.tcx(), 0, true),
+ e.generics.print(cx),
+ print_where_clause(&e.generics, cx, 0, true),
);
if e.variants.is_empty() && !e.variants_stripped {
w.write_str(" {}");
} else {
w.write_str(" {\n");
+ let toggle = should_hide_fields(e.variants.len());
+ if toggle {
+ toggle_open(w, "variants");
+ }
for v in &e.variants {
w.write_str(" ");
let name = v.name.as_ref().unwrap();
if i > 0 {
w.write_str(", ")
}
- write!(w, "{}", ty.print(cx.cache(), cx.tcx()));
+ write!(w, "{}", ty.print(cx));
}
w.write_str(")");
}
if e.variants_stripped {
w.write_str(" // some variants omitted\n");
}
+ if toggle {
+ toggle_close(w);
+ }
w.write_str("}");
}
w.write_str("</pre>")
if i > 0 {
w.write_str(", ");
}
- write!(w, "{}", ty.print(cx.cache(), cx.tcx()));
+ write!(w, "{}", ty.print(cx));
}
w.write_str(")");
}
use crate::clean::Variant;
if let clean::VariantItem(Variant::Struct(ref s)) = *variant.kind {
+ toggle_open(w, "fields");
let variant_id = cx.derive_id(format!(
"{}.{}.fields",
ItemType::Variant,
</span>",
id = id,
f = field.name.as_ref().unwrap(),
- t = ty.print(cx.cache(), cx.tcx())
+ t = ty.print(cx)
);
document(w, cx, field, Some(variant));
}
}
w.write_str("</div></div>");
+ toggle_close(w);
}
render_stability_since(w, variant, it, cx.tcx());
}
Some("macro"),
None,
None,
- it.span.inner().edition(),
+ it.span(cx.tcx()).inner().edition(),
);
});
document(w, cx, it, None)
fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::Constant) {
w.write_str("<pre class=\"rust const\">");
- render_attributes(w, it, false);
+ render_attributes_in_code(w, it);
write!(
w,
"{vis}const {name}: {typ}",
- vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+ vis = it.visibility.print_with_space(it.def_id, cx),
name = it.name.as_ref().unwrap(),
- typ = c.type_.print(cx.cache(), cx.tcx()),
+ typ = c.type_.print(cx),
);
let value = c.value(cx.tcx());
fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) {
wrap_into_docblock(w, |w| {
w.write_str("<pre class=\"rust struct\">");
- render_attributes(w, it, true);
+ render_attributes_in_code(w, it);
render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true, cx);
w.write_str("</pre>")
});
item_type = ItemType::StructField,
id = id,
name = field.name.as_ref().unwrap(),
- ty = ty.print(cx.cache(), cx.tcx())
+ ty = ty.print(cx)
);
document(w, cx, field, Some(it));
}
fn item_static(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Static) {
w.write_str("<pre class=\"rust static\">");
- render_attributes(w, it, false);
+ render_attributes_in_code(w, it);
write!(
w,
"{vis}static {mutability}{name}: {typ}</pre>",
- vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+ vis = it.visibility.print_with_space(it.def_id, cx),
mutability = s.mutability.print_with_space(),
name = it.name.as_ref().unwrap(),
- typ = s.type_.print(cx.cache(), cx.tcx())
+ typ = s.type_.print(cx)
);
document(w, cx, it, None)
}
fn item_foreign_type(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
w.write_str("<pre class=\"rust foreigntype\">extern {\n");
- render_attributes(w, it, false);
+ render_attributes_in_code(w, it);
write!(
w,
" {}type {};\n}}</pre>",
- it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+ it.visibility.print_with_space(it.def_id, cx),
it.name.as_ref().unwrap(),
);
}
}
-fn bounds(
- t_bounds: &[clean::GenericBound],
- trait_alias: bool,
- cache: &Cache,
- tcx: TyCtxt<'_>,
-) -> String {
+fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) -> String {
let mut bounds = String::new();
if !t_bounds.is_empty() {
if !trait_alias {
if i > 0 {
bounds.push_str(" + ");
}
- bounds.push_str(&p.print(cache, tcx).to_string());
+ bounds.push_str(&p.print(cx).to_string());
}
}
bounds
where
F: FnOnce(&mut Buffer),
{
- w.write_str("<div class=\"docblock type-decl hidden-by-usual-hider\">");
+ w.write_str("<div class=\"docblock type-decl\">");
f(w);
w.write_str("</div>")
}
)
}
-fn compare_impl<'a, 'b>(
- lhs: &'a &&Impl,
- rhs: &'b &&Impl,
- cache: &Cache,
- tcx: TyCtxt<'_>,
-) -> Ordering {
- let lhs = format!("{}", lhs.inner_impl().print(cache, false, tcx));
- let rhs = format!("{}", rhs.inner_impl().print(cache, false, tcx));
+fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl, cx: &Context<'_>) -> Ordering {
+ let lhss = format!("{}", lhs.inner_impl().print(false, cx));
+ let rhss = format!("{}", rhs.inner_impl().print(false, cx));
// lhs and rhs are formatted as HTML, which may be unnecessary
- compare_names(&lhs, &rhs)
+ compare_names(&lhss, &rhss)
}
fn render_implementor(
write!(
w,
"{}{}{}",
- it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+ it.visibility.print_with_space(it.def_id, cx),
if structhead { "union " } else { "" },
it.name.as_ref().unwrap()
);
if let Some(g) = g {
- write!(w, "{}", g.print(cx.cache(), cx.tcx()));
- write!(w, "{}", print_where_clause(&g, cx.cache(), cx.tcx(), 0, true));
+ write!(w, "{}", g.print(cx));
+ write!(w, "{}", print_where_clause(&g, cx, 0, true));
}
write!(w, " {{\n{}", tab);
+ let count_fields =
+ fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count();
+ let toggle = should_hide_fields(count_fields);
+ if toggle {
+ toggle_open(w, "fields");
+ }
+
for field in fields {
if let clean::StructFieldItem(ref ty) = *field.kind {
write!(
w,
" {}{}: {},\n{}",
- field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()),
+ field.visibility.print_with_space(field.def_id, cx),
field.name.as_ref().unwrap(),
- ty.print(cx.cache(), cx.tcx()),
+ ty.print(cx),
tab
);
}
if it.has_stripped_fields().unwrap() {
write!(w, " // some fields omitted\n{}", tab);
}
+ if toggle {
+ toggle_close(w);
+ }
w.write_str("}");
}
write!(
w,
"{}{}{}",
- it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+ it.visibility.print_with_space(it.def_id, cx),
if structhead { "struct " } else { "" },
it.name.as_ref().unwrap()
);
if let Some(g) = g {
- write!(w, "{}", g.print(cx.cache(), cx.tcx()))
+ write!(w, "{}", g.print(cx))
}
match ty {
CtorKind::Fictive => {
if let Some(g) = g {
- write!(w, "{}", print_where_clause(g, cx.cache(), cx.tcx(), 0, true),)
+ write!(w, "{}", print_where_clause(g, cx, 0, true),)
}
- let mut has_visible_fields = false;
w.write_str(" {");
+ let count_fields =
+ fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count();
+ let has_visible_fields = count_fields > 0;
+ let toggle = should_hide_fields(count_fields);
+ if toggle {
+ toggle_open(w, "fields");
+ }
for field in fields {
if let clean::StructFieldItem(ref ty) = *field.kind {
write!(
w,
"\n{} {}{}: {},",
tab,
- field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()),
+ field.visibility.print_with_space(field.def_id, cx),
field.name.as_ref().unwrap(),
- ty.print(cx.cache(), cx.tcx()),
+ ty.print(cx),
);
- has_visible_fields = true;
}
}
// `{ /* fields omitted */ }` to save space.
write!(w, " /* fields omitted */ ");
}
+ if toggle {
+ toggle_close(w);
+ }
w.write_str("}");
}
CtorKind::Fn => {
write!(
w,
"{}{}",
- field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()),
- ty.print(cx.cache(), cx.tcx()),
+ field.visibility.print_with_space(field.def_id, cx),
+ ty.print(cx),
)
}
_ => unreachable!(),
}
w.write_str(")");
if let Some(g) = g {
- write!(w, "{}", print_where_clause(g, cx.cache(), cx.tcx(), 0, false),)
+ write!(w, "{}", print_where_clause(g, cx, 0, false),)
}
w.write_str(";");
}
CtorKind::Const => {
// Needed for PhantomData.
if let Some(g) = g {
- write!(w, "{}", print_where_clause(g, cx.cache(), cx.tcx(), 0, false),)
+ write!(w, "{}", print_where_clause(g, cx, 0, false),)
}
w.write_str(";");
}
use crate::config::{EmitType, RenderOptions};
use crate::docfs::PathError;
use crate::error::Error;
-use crate::formats::FormatRenderer;
use crate::html::{layout, static_files};
crate static FILES_UNVERSIONED: Lazy<FxHashMap<&str, &[u8]>> = Lazy::new(|| {
&format!(" = {}", serde_json::to_string(&themes).unwrap()),
),
)?;
+ write_minify("search.js", static_files::SEARCH_JS)?;
write_minify("settings.js", static_files::SETTINGS_JS)?;
if cx.shared.include_sources {
write_minify("source-script.js", static_files::sidebar::SOURCE_SCRIPT)?;
write_crate("search-index.js", &|| {
let mut v = String::from("var searchIndex = JSON.parse('{\\\n");
v.push_str(&all_indexes.join(",\\\n"));
- v.push_str("\\\n}');\ninitSearch(searchIndex);");
+ v.push_str("\\\n}');\nif (window.initSearch) {window.initSearch(searchIndex)};");
Ok(v.into_bytes())
})?;
md_opts.output = cx.dst.clone();
md_opts.external_html = (*cx.shared).layout.external_html.clone();
- crate::markdown::render(&index_page, md_opts, cx.shared.edition)
+ crate::markdown::render(&index_page, md_opts, cx.shared.edition())
.map_err(|e| Error::new(e, &index_page))?;
} else {
let dst = cx.dst.join("index.html");
None
} else {
Some(Implementor {
- text: imp.inner_impl().print(cx.cache(), false, cx.tcx()).to_string(),
+ text: imp.inner_impl().print(false, cx).to_string(),
synthetic: imp.inner_impl().synthetic,
types: collect_paths_for_type(imp.inner_impl().for_.clone(), cx.cache()),
})
// then we need to render it out to the filesystem.
if self.scx.include_sources
// skip all synthetic "files"
- && item.span.filename(self.sess()).is_real()
+ && item.span(self.scx.tcx).filename(self.sess()).is_real()
// skip non-local files
- && item.span.cnum(self.sess()) == LOCAL_CRATE
+ && item.span(self.scx.tcx).cnum(self.sess()) == LOCAL_CRATE
{
- let filename = item.span.filename(self.sess());
+ let filename = item.span(self.scx.tcx).filename(self.sess());
// If it turns out that we couldn't read this file, then we probably
// can't read any of the files (generating html output from json or
// something like that), so just don't include sources for the
Ok(()) => true,
Err(e) => {
self.scx.tcx.sess.span_err(
- item.span.inner(),
+ item.span(self.scx.tcx).inner(),
&format!("failed to render source code for `{}`: {}", filename, e),
);
false
&self.scx.layout,
&page,
"",
- |buf: &mut _| print_src(buf, contents, self.scx.edition),
+ |buf: &mut _| print_src(buf, contents, self.scx.edition()),
&self.scx.style_files,
);
self.scx.fs.write(&cur, v.as_bytes())?;
-// ignore-tidy-filelength
// Local js definitions:
/* global addClass, getSettingValue, hasClass */
/* global onEach, onEachLazy, hasOwnProperty, removeClass, updateLocalStorage */
window.rootPath = rustdocVars.attributes["data-root-path"].value;
window.currentCrate = rustdocVars.attributes["data-current-crate"].value;
window.searchJS = rustdocVars.attributes["data-search-js"].value;
+ window.searchIndexJS = rustdocVars.attributes["data-search-index-js"].value;
}
var sidebarVars = document.getElementById("sidebar-vars");
if (sidebarVars) {
return String.fromCharCode(c);
}
-function getSearchInput() {
- return document.getElementsByClassName("search-input")[0];
-}
-
-function getSearchElement() {
- return document.getElementById("search");
-}
-
var THEME_PICKER_ELEMENT_ID = "theme-picker";
var THEMES_ELEMENT_ID = "theme-choices";
return window.location.href.split("?")[0].split("#")[0];
}
-// Sets the focus on the search bar at the top of the page
-function focusSearchBar() {
- getSearchInput().focus();
-}
-
-// Removes the focus from the search bar.
-function defocusSearchBar() {
- getSearchInput().blur();
-}
-
function showThemeButtonState() {
var themePicker = getThemePickerElement();
var themeChoices = getThemesElement();
(function() {
"use strict";
- // This mapping table should match the discriminants of
- // `rustdoc::html::item_type::ItemType` type in Rust.
- var itemTypes = ["mod",
- "externcrate",
- "import",
- "struct",
- "enum",
- "fn",
- "type",
- "static",
- "trait",
- "impl",
- "tymethod",
- "method",
- "structfield",
- "variant",
- "macro",
- "primitive",
- "associatedtype",
- "constant",
- "associatedconstant",
- "union",
- "foreigntype",
- "keyword",
- "existential",
- "attr",
- "derive",
- "traitalias"];
+ window.searchState = {
+ loadingText: "Loading search results...",
+ input: document.getElementsByClassName("search-input")[0],
+ outputElement: function() {
+ return document.getElementById("search");
+ },
+ title: null,
+ titleBeforeSearch: document.title,
+ timeout: null,
+ // On the search screen, so you remain on the last tab you opened.
+ //
+ // 0 for "In Names"
+ // 1 for "In Parameters"
+ // 2 for "In Return Types"
+ currentTab: 0,
+ mouseMovedAfterSearch: true,
+ clearInputTimeout: function() {
+ if (searchState.timeout !== null) {
+ clearTimeout(searchState.timeout);
+ searchState.timeout = null;
+ }
+ },
+ // Sets the focus on the search bar at the top of the page
+ focus: function() {
+ searchState.input.focus();
+ },
+ // Removes the focus from the search bar.
+ defocus: function() {
+ searchState.input.blur();
+ },
+ showResults: function(search) {
+ if (search === null || typeof search === 'undefined') {
+ search = searchState.outputElement();
+ }
+ addClass(main, "hidden");
+ removeClass(search, "hidden");
+ searchState.mouseMovedAfterSearch = false;
+ document.title = searchState.title;
+ },
+ hideResults: function(search) {
+ if (search === null || typeof search === 'undefined') {
+ search = searchState.outputElement();
+ }
+ addClass(search, "hidden");
+ removeClass(main, "hidden");
+ document.title = searchState.titleBeforeSearch;
+ // We also remove the query parameter from the URL.
+ if (searchState.browserSupportsHistoryApi()) {
+ history.replaceState("", window.currentCrate + " - Rust",
+ getNakedUrl() + window.location.hash);
+ }
+ },
+ getQueryStringParams: function() {
+ var params = {};
+ window.location.search.substring(1).split("&").
+ map(function(s) {
+ var pair = s.split("=");
+ params[decodeURIComponent(pair[0])] =
+ typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]);
+ });
+ return params;
+ },
+ putBackSearch: function(search_input) {
+ var search = searchState.outputElement();
+ if (search_input.value !== "" && hasClass(search, "hidden")) {
+ searchState.showResults(search);
+ if (searchState.browserSupportsHistoryApi()) {
+ var extra = "?search=" + encodeURIComponent(search_input.value);
+ history.replaceState(search_input.value, "",
+ getNakedUrl() + extra + window.location.hash);
+ }
+ document.title = searchState.title;
+ }
+ },
+ browserSupportsHistoryApi: function() {
+ return window.history && typeof window.history.pushState === "function";
+ },
+ setup: function() {
+ var search_input = searchState.input;
+ if (!searchState.input) {
+ return;
+ }
+ function loadScript(url) {
+ var script = document.createElement('script');
+ script.src = url;
+ document.head.append(script);
+ }
- var disableShortcuts = getSettingValue("disable-shortcuts") === "true";
- var search_input = getSearchInput();
- var searchTimeout = null;
- var toggleAllDocsId = "toggle-all-docs";
+ var searchLoaded = false;
+ function loadSearch() {
+ if (!searchLoaded) {
+ searchLoaded = true;
+ loadScript(window.searchJS);
+ loadScript(window.searchIndexJS);
+ }
+ }
- // On the search screen, so you remain on the last tab you opened.
- //
- // 0 for "In Names"
- // 1 for "In Parameters"
- // 2 for "In Return Types"
- var currentTab = 0;
+ search_input.addEventListener("focus", function() {
+ searchState.putBackSearch(this);
+ search_input.origPlaceholder = searchState.input.placeholder;
+ search_input.placeholder = "Type your search here.";
+ loadSearch();
+ });
+ search_input.addEventListener("blur", function() {
+ search_input.placeholder = searchState.input.origPlaceholder;
+ });
- var mouseMovedAfterSearch = true;
+ document.addEventListener("mousemove", function() {
+ searchState.mouseMovedAfterSearch = true;
+ });
- var titleBeforeSearch = document.title;
- var searchTitle = null;
+ search_input.removeAttribute('disabled');
- function removeEmptyStringsFromArray(x) {
- for (var i = 0, len = x.length; i < len; ++i) {
- if (x[i] === "") {
- x.splice(i, 1);
- i -= 1;
- }
+ // `crates{version}.js` should always be loaded before this script, so we can use it safely.
+ searchState.addCrateDropdown(window.ALL_CRATES);
+ var params = searchState.getQueryStringParams();
+ if (params.search !== undefined) {
+ var search = searchState.outputElement();
+ search.innerHTML = "<h3 style=\"text-align: center;\">" +
+ searchState.loadingText + "</h3>";
+ searchState.showResults(search);
+ loadSearch();
}
- }
+ },
+ addCrateDropdown: function(crates) {
+ var elem = document.getElementById("crate-search");
- function clearInputTimeout() {
- if (searchTimeout !== null) {
- clearTimeout(searchTimeout);
- searchTimeout = null;
+ if (!elem) {
+ return;
}
- }
+ var savedCrate = getSettingValue("saved-filter-crate");
+ for (var i = 0, len = crates.length; i < len; ++i) {
+ var option = document.createElement("option");
+ option.value = crates[i];
+ option.innerText = crates[i];
+ elem.appendChild(option);
+ // Set the crate filter from saved storage, if the current page has the saved crate
+ // filter.
+ //
+ // If not, ignore the crate filter -- we want to support filtering for crates on sites
+ // like doc.rust-lang.org where the crates may differ from page to page while on the
+ // same domain.
+ if (crates[i] === savedCrate) {
+ elem.value = savedCrate;
+ }
+ }
+ },
+ };
function getPageId() {
if (window.location.hash) {
document.getElementsByTagName("body")[0].style.marginTop = "";
}
- function showSearchResults(search) {
- if (search === null || typeof search === 'undefined') {
- search = getSearchElement();
- }
- addClass(main, "hidden");
- removeClass(search, "hidden");
- mouseMovedAfterSearch = false;
- document.title = searchTitle;
- }
-
- function hideSearchResults(search) {
- if (search === null || typeof search === 'undefined') {
- search = getSearchElement();
- }
- addClass(search, "hidden");
- removeClass(main, "hidden");
- document.title = titleBeforeSearch;
- // We also remove the query parameter from the URL.
- if (browserSupportsHistoryApi()) {
- history.replaceState("", window.currentCrate + " - Rust",
- getNakedUrl() + window.location.hash);
- }
- }
-
- // used for special search precedence
- var TY_PRIMITIVE = itemTypes.indexOf("primitive");
- var TY_KEYWORD = itemTypes.indexOf("keyword");
-
- function getQueryStringParams() {
- var params = {};
- window.location.search.substring(1).split("&").
- map(function(s) {
- var pair = s.split("=");
- params[decodeURIComponent(pair[0])] =
- typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]);
- });
- return params;
- }
-
- function browserSupportsHistoryApi() {
- return window.history && typeof window.history.pushState === "function";
- }
-
function isHidden(elem) {
return elem.offsetHeight === 0;
}
+ var toggleAllDocsId = "toggle-all-docs";
var main = document.getElementById("main");
var savedHash = "";
function handleHashes(ev) {
var elem;
- var search = getSearchElement();
+ var search = searchState.outputElement();
if (ev !== null && search && !hasClass(search, "hidden") && ev.newURL) {
// This block occurs when clicking on an element in the navbar while
// in a search.
- hideSearchResults(search);
+ searchState.hideResults(search);
var hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1);
- if (browserSupportsHistoryApi()) {
+ if (searchState.browserSupportsHistoryApi()) {
// `window.location.search`` contains all the query parameters, not just `search`.
history.replaceState(hash, "",
getNakedUrl() + window.location.search + "#" + hash);
handleHashes(ev);
}
+ function openParentDetails(elem) {
+ while (elem) {
+ if (elem.tagName === "DETAILS") {
+ elem.open = true;
+ }
+ elem = elem.parentNode;
+ }
+ }
+
function expandSection(id) {
var elem = document.getElementById(id);
if (elem && isHidden(elem)) {
// The element is not visible, we need to make it appear!
collapseDocs(collapses[0], "show");
}
+ // Open all ancestor <details> to make this element visible.
+ openParentDetails(h3.parentNode);
+ } else {
+ openParentDetails(elem.parentNode);
}
}
}
function handleEscape(ev) {
var help = getHelpElement(false);
- var search = getSearchElement();
+ var search = searchState.outputElement();
if (hasClass(help, "hidden") === false) {
displayHelp(false, ev, help);
} else if (hasClass(search, "hidden") === false) {
- clearInputTimeout();
+ searchState.clearInputTimeout();
ev.preventDefault();
- hideSearchResults(search);
+ searchState.hideResults(search);
}
- defocusSearchBar();
+ searchState.defocus();
hideThemeButtonState();
}
+ var disableShortcuts = getSettingValue("disable-shortcuts") === "true";
function handleShortcut(ev) {
// Don't interfere with browser shortcuts
if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts === true) {
case "S":
displayHelp(false, ev);
ev.preventDefault();
- focusSearchBar();
+ searchState.focus();
break;
case "+":
// The escape key is handled in handleEscape, not here,
// so that pressing escape will close the menu even if it isn't focused
}
- }
-
- function findParentElement(elem, tagName) {
- do {
- if (elem && elem.tagName === tagName) {
- return elem;
- }
- elem = elem.parentNode;
- } while (elem);
- return null;
- }
-
- document.addEventListener("keypress", handleShortcut);
- document.addEventListener("keydown", handleShortcut);
-
- document.addEventListener("mousemove", function() { mouseMovedAfterSearch = true; });
-
- var handleSourceHighlight = (function() {
- var prev_line_id = 0;
-
- var set_fragment = function(name) {
- var x = window.scrollX,
- y = window.scrollY;
- if (browserSupportsHistoryApi()) {
- history.replaceState(null, null, "#" + name);
- highlightSourceLines();
- } else {
- location.replace("#" + name);
- }
- // Prevent jumps when selecting one or many lines
- window.scrollTo(x, y);
- };
-
- return function(ev) {
- var cur_line_id = parseInt(ev.target.id, 10);
- ev.preventDefault();
-
- if (ev.shiftKey && prev_line_id) {
- // Swap selection if needed
- if (prev_line_id > cur_line_id) {
- var tmp = prev_line_id;
- prev_line_id = cur_line_id;
- cur_line_id = tmp;
- }
-
- set_fragment(prev_line_id + "-" + cur_line_id);
- } else {
- prev_line_id = cur_line_id;
-
- set_fragment(cur_line_id);
- }
- };
- }());
-
- document.addEventListener("click", function(ev) {
- var helpElem = getHelpElement(false);
- if (hasClass(ev.target, "help-button")) {
- displayHelp(true, ev);
- } else if (hasClass(ev.target, "collapse-toggle")) {
- collapseDocs(ev.target, "toggle");
- } else if (hasClass(ev.target.parentNode, "collapse-toggle")) {
- collapseDocs(ev.target.parentNode, "toggle");
- } else if (ev.target.tagName === "SPAN" && hasClass(ev.target.parentNode, "line-numbers")) {
- handleSourceHighlight(ev);
- } else if (helpElem && hasClass(helpElem, "hidden") === false) {
- var is_inside_help_popup = ev.target !== helpElem && helpElem.contains(ev.target);
- if (is_inside_help_popup === false) {
- addClass(helpElem, "hidden");
- removeClass(document.body, "blur");
- }
- } else {
- // Making a collapsed element visible on onhashchange seems
- // too late
- var a = findParentElement(ev.target, "A");
- if (a && a.hash) {
- expandSection(a.hash.replace(/^#/, ""));
- }
- }
- });
-
- (function() {
- var x = document.getElementsByClassName("version-selector");
- if (x.length > 0) {
- x[0].onchange = function() {
- var i, match,
- url = document.location.href,
- stripped = "",
- len = window.rootPath.match(/\.\.\//g).length + 1;
-
- for (i = 0; i < len; ++i) {
- match = url.match(/\/[^\/]*$/);
- if (i < len - 1) {
- stripped = match[0] + stripped;
- }
- url = url.substring(0, url.length - match[0].length);
- }
-
- var selectedVersion = document.getElementsByClassName("version-selector")[0].value;
- url += "/" + selectedVersion + stripped;
-
- document.location.href = url;
- };
- }
- }());
-
- /**
- * A function to compute the Levenshtein distance between two strings
- * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported
- * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode
- * This code is an unmodified version of the code written by Marco de Wit
- * and was found at http://stackoverflow.com/a/18514751/745719
- */
- var levenshtein_row2 = [];
- function levenshtein(s1, s2) {
- if (s1 === s2) {
- return 0;
- }
- var s1_len = s1.length, s2_len = s2.length;
- if (s1_len && s2_len) {
- var i1 = 0, i2 = 0, a, b, c, c2, row = levenshtein_row2;
- while (i1 < s1_len) {
- row[i1] = ++i1;
- }
- while (i2 < s2_len) {
- c2 = s2.charCodeAt(i2);
- a = i2;
- ++i2;
- b = i2;
- for (i1 = 0; i1 < s1_len; ++i1) {
- c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0);
- a = row[i1];
- b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
- row[i1] = b;
- }
- }
- return b;
- }
- return s1_len + s2_len;
- }
-
- window.initSearch = function(rawSearchIndex) {
- var MAX_LEV_DISTANCE = 3;
- var MAX_RESULTS = 200;
- var GENERICS_DATA = 1;
- var NAME = 0;
- var INPUTS_DATA = 0;
- var OUTPUT_DATA = 1;
- var NO_TYPE_FILTER = -1;
- var currentResults, index, searchIndex;
- var ALIASES = {};
- var params = getQueryStringParams();
-
- // Populate search bar with query string search term when provided,
- // but only if the input bar is empty. This avoid the obnoxious issue
- // where you start trying to do a search, and the index loads, and
- // suddenly your search is gone!
- if (search_input.value === "") {
- search_input.value = params.search || "";
- }
-
- /**
- * Executes the query and builds an index of results
- * @param {[Object]} query [The user query]
- * @param {[type]} searchWords [The list of search words to query
- * against]
- * @param {[type]} filterCrates [Crate to search in if defined]
- * @return {[type]} [A search index of results]
- */
- function execQuery(query, searchWords, filterCrates) {
- function itemTypeFromName(typename) {
- for (var i = 0, len = itemTypes.length; i < len; ++i) {
- if (itemTypes[i] === typename) {
- return i;
- }
- }
- return NO_TYPE_FILTER;
- }
-
- var valLower = query.query.toLowerCase(),
- val = valLower,
- typeFilter = itemTypeFromName(query.type),
- results = {}, results_in_args = {}, results_returned = {},
- split = valLower.split("::");
-
- removeEmptyStringsFromArray(split);
-
- function transformResults(results, isType) {
- var out = [];
- for (var i = 0, len = results.length; i < len; ++i) {
- if (results[i].id > -1) {
- var obj = searchIndex[results[i].id];
- obj.lev = results[i].lev;
- if (isType !== true || obj.type) {
- var res = buildHrefAndPath(obj);
- obj.displayPath = pathSplitter(res[0]);
- obj.fullPath = obj.displayPath + obj.name;
- // To be sure than it some items aren't considered as duplicate.
- obj.fullPath += "|" + obj.ty;
- obj.href = res[1];
- out.push(obj);
- if (out.length >= MAX_RESULTS) {
- break;
- }
- }
- }
- }
- return out;
- }
-
- function sortResults(results, isType) {
- var ar = [];
- for (var entry in results) {
- if (hasOwnProperty(results, entry)) {
- ar.push(results[entry]);
- }
- }
- results = ar;
- var i, len, result;
- for (i = 0, len = results.length; i < len; ++i) {
- result = results[i];
- result.word = searchWords[result.id];
- result.item = searchIndex[result.id] || {};
- }
- // if there are no results then return to default and fail
- if (results.length === 0) {
- return [];
- }
-
- results.sort(function(aaa, bbb) {
- var a, b;
-
- // sort by exact match with regard to the last word (mismatch goes later)
- a = (aaa.word !== val);
- b = (bbb.word !== val);
- if (a !== b) { return a - b; }
-
- // Sort by non levenshtein results and then levenshtein results by the distance
- // (less changes required to match means higher rankings)
- a = (aaa.lev);
- b = (bbb.lev);
- if (a !== b) { return a - b; }
-
- // sort by crate (non-current crate goes later)
- a = (aaa.item.crate !== window.currentCrate);
- b = (bbb.item.crate !== window.currentCrate);
- if (a !== b) { return a - b; }
-
- // sort by item name length (longer goes later)
- a = aaa.word.length;
- b = bbb.word.length;
- if (a !== b) { return a - b; }
-
- // sort by item name (lexicographically larger goes later)
- a = aaa.word;
- b = bbb.word;
- if (a !== b) { return (a > b ? +1 : -1); }
-
- // sort by index of keyword in item name (no literal occurrence goes later)
- a = (aaa.index < 0);
- b = (bbb.index < 0);
- if (a !== b) { return a - b; }
- // (later literal occurrence, if any, goes later)
- a = aaa.index;
- b = bbb.index;
- if (a !== b) { return a - b; }
-
- // special precedence for primitive and keyword pages
- if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) ||
- (aaa.item.ty === TY_KEYWORD && bbb.item.ty !== TY_PRIMITIVE)) {
- return -1;
- }
- if ((bbb.item.ty === TY_PRIMITIVE && aaa.item.ty !== TY_PRIMITIVE) ||
- (bbb.item.ty === TY_KEYWORD && aaa.item.ty !== TY_KEYWORD)) {
- return 1;
- }
-
- // sort by description (no description goes later)
- a = (aaa.item.desc === "");
- b = (bbb.item.desc === "");
- if (a !== b) { return a - b; }
-
- // sort by type (later occurrence in `itemTypes` goes later)
- a = aaa.item.ty;
- b = bbb.item.ty;
- if (a !== b) { return a - b; }
-
- // sort by path (lexicographically larger goes later)
- a = aaa.item.path;
- b = bbb.item.path;
- if (a !== b) { return (a > b ? +1 : -1); }
-
- // que sera, sera
- return 0;
- });
-
- for (i = 0, len = results.length; i < len; ++i) {
- var result = results[i];
-
- // this validation does not make sense when searching by types
- if (result.dontValidate) {
- continue;
- }
- var name = result.item.name.toLowerCase(),
- path = result.item.path.toLowerCase(),
- parent = result.item.parent;
-
- if (isType !== true &&
- validateResult(name, path, split, parent) === false)
- {
- result.id = -1;
- }
- }
- return transformResults(results);
- }
-
- function extractGenerics(val) {
- val = val.toLowerCase();
- if (val.indexOf("<") !== -1) {
- var values = val.substring(val.indexOf("<") + 1, val.lastIndexOf(">"));
- return {
- name: val.substring(0, val.indexOf("<")),
- generics: values.split(/\s*,\s*/),
- };
- }
- return {
- name: val,
- generics: [],
- };
- }
-
- function getObjectNameFromId(id) {
- if (typeof id === "number") {
- return searchIndex[id].name;
- }
- return id;
- }
-
- function checkGenerics(obj, val) {
- // The names match, but we need to be sure that all generics kinda
- // match as well.
- var tmp_lev, elem_name;
- if (val.generics.length > 0) {
- if (obj.length > GENERICS_DATA &&
- obj[GENERICS_DATA].length >= val.generics.length) {
- var elems = Object.create(null);
- var elength = object[GENERICS_DATA].length;
- for (var x = 0; x < elength; ++x) {
- elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1;
- }
- var total = 0;
- var done = 0;
- // We need to find the type that matches the most to remove it in order
- // to move forward.
- var vlength = val.generics.length;
- for (x = 0; x < vlength; ++x) {
- var lev = MAX_LEV_DISTANCE + 1;
- var firstGeneric = getObjectNameFromId(val.generics[x]);
- var match = null;
- if (elems[firstGeneric]) {
- match = firstGeneric;
- lev = 0;
- } else {
- for (elem_name in elems) {
- tmp_lev = levenshtein(elem_name, firstGeneric);
- if (tmp_lev < lev) {
- lev = tmp_lev;
- match = elem_name;
- }
- }
- }
- if (match !== null) {
- elems[match] -= 1;
- if (elems[match] == 0) {
- delete elems[match];
- }
- total += lev;
- done += 1;
- } else {
- return MAX_LEV_DISTANCE + 1;
- }
- }
- return Math.ceil(total / done);
- }
- }
- return MAX_LEV_DISTANCE + 1;
- }
-
- // Check for type name and type generics (if any).
- function checkType(obj, val, literalSearch) {
- var lev_distance = MAX_LEV_DISTANCE + 1;
- var len, x, firstGeneric;
- if (obj[NAME] === val.name) {
- if (literalSearch === true) {
- if (val.generics && val.generics.length !== 0) {
- if (obj.length > GENERICS_DATA &&
- obj[GENERICS_DATA].length >= val.generics.length) {
- var elems = Object.create(null);
- len = obj[GENERICS_DATA].length;
- for (x = 0; x < len; ++x) {
- elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1;
- }
-
- var allFound = true;
- len = val.generics.length;
- for (x = 0; x < len; ++x) {
- firstGeneric = getObjectNameFromId(val.generics[x]);
- if (elems[firstGeneric]) {
- elems[firstGeneric] -= 1;
- } else {
- allFound = false;
- break;
- }
- }
- if (allFound === true) {
- return true;
- }
- } else {
- return false;
- }
- }
- return true;
- }
- // If the type has generics but don't match, then it won't return at this point.
- // Otherwise, `checkGenerics` will return 0 and it'll return.
- if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) {
- var tmp_lev = checkGenerics(obj, val);
- if (tmp_lev <= MAX_LEV_DISTANCE) {
- return tmp_lev;
- }
- } else {
- return 0;
- }
- }
- // Names didn't match so let's check if one of the generic types could.
- if (literalSearch === true) {
- if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
- return obj[GENERICS_DATA].some(
- function(name) {
- return name === val.name;
- });
- }
- return false;
- }
- lev_distance = Math.min(levenshtein(obj[NAME], val.name), lev_distance);
- if (lev_distance <= MAX_LEV_DISTANCE) {
- // The generics didn't match but the name kinda did so we give it
- // a levenshtein distance value that isn't *this* good so it goes
- // into the search results but not too high.
- lev_distance = Math.ceil((checkGenerics(obj, val) + lev_distance) / 2);
- } else if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
- // We can check if the type we're looking for is inside the generics!
- var olength = obj[GENERICS_DATA].length;
- for (x = 0; x < olength; ++x) {
- lev_distance = Math.min(levenshtein(obj[GENERICS_DATA][x], val.name),
- lev_distance);
- }
- }
- // Now whatever happens, the returned distance is "less good" so we should mark it
- // as such, and so we add 1 to the distance to make it "less good".
- return lev_distance + 1;
- }
-
- function findArg(obj, val, literalSearch, typeFilter) {
- var lev_distance = MAX_LEV_DISTANCE + 1;
-
- if (obj && obj.type && obj.type[INPUTS_DATA] && obj.type[INPUTS_DATA].length > 0) {
- var length = obj.type[INPUTS_DATA].length;
- for (var i = 0; i < length; i++) {
- var tmp = obj.type[INPUTS_DATA][i];
- if (typePassesFilter(typeFilter, tmp[1]) === false) {
- continue;
- }
- tmp = checkType(tmp, val, literalSearch);
- if (literalSearch === true) {
- if (tmp === true) {
- return true;
- }
- continue;
- }
- lev_distance = Math.min(tmp, lev_distance);
- if (lev_distance === 0) {
- return 0;
- }
- }
- }
- return literalSearch === true ? false : lev_distance;
- }
-
- function checkReturned(obj, val, literalSearch, typeFilter) {
- var lev_distance = MAX_LEV_DISTANCE + 1;
-
- if (obj && obj.type && obj.type.length > OUTPUT_DATA) {
- var ret = obj.type[OUTPUT_DATA];
- if (typeof ret[0] === "string") {
- ret = [ret];
- }
- for (var x = 0, len = ret.length; x < len; ++x) {
- var tmp = ret[x];
- if (typePassesFilter(typeFilter, tmp[1]) === false) {
- continue;
- }
- tmp = checkType(tmp, val, literalSearch);
- if (literalSearch === true) {
- if (tmp === true) {
- return true;
- }
- continue;
- }
- lev_distance = Math.min(tmp, lev_distance);
- if (lev_distance === 0) {
- return 0;
- }
- }
- }
- return literalSearch === true ? false : lev_distance;
- }
-
- function checkPath(contains, lastElem, ty) {
- if (contains.length === 0) {
- return 0;
- }
- var ret_lev = MAX_LEV_DISTANCE + 1;
- var path = ty.path.split("::");
-
- if (ty.parent && ty.parent.name) {
- path.push(ty.parent.name.toLowerCase());
- }
-
- var length = path.length;
- var clength = contains.length;
- if (clength > length) {
- return MAX_LEV_DISTANCE + 1;
- }
- for (var i = 0; i < length; ++i) {
- if (i + clength > length) {
- break;
- }
- var lev_total = 0;
- var aborted = false;
- for (var x = 0; x < clength; ++x) {
- var lev = levenshtein(path[i + x], contains[x]);
- if (lev > MAX_LEV_DISTANCE) {
- aborted = true;
- break;
- }
- lev_total += lev;
- }
- if (aborted === false) {
- ret_lev = Math.min(ret_lev, Math.round(lev_total / clength));
- }
- }
- return ret_lev;
- }
-
- function typePassesFilter(filter, type) {
- // No filter
- if (filter <= NO_TYPE_FILTER) return true;
-
- // Exact match
- if (filter === type) return true;
-
- // Match related items
- var name = itemTypes[type];
- switch (itemTypes[filter]) {
- case "constant":
- return name === "associatedconstant";
- case "fn":
- return name === "method" || name === "tymethod";
- case "type":
- return name === "primitive" || name === "associatedtype";
- case "trait":
- return name === "traitalias";
- }
-
- // No match
- return false;
- }
-
- function createAliasFromItem(item) {
- return {
- crate: item.crate,
- name: item.name,
- path: item.path,
- desc: item.desc,
- ty: item.ty,
- parent: item.parent,
- type: item.type,
- is_alias: true,
- };
- }
-
- function handleAliases(ret, query, filterCrates) {
- // We separate aliases and crate aliases because we want to have current crate
- // aliases to be before the others in the displayed results.
- var aliases = [];
- var crateAliases = [];
- if (filterCrates !== undefined) {
- if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) {
- var query_aliases = ALIASES[filterCrates][query.search];
- var len = query_aliases.length;
- for (var i = 0; i < len; ++i) {
- aliases.push(createAliasFromItem(searchIndex[query_aliases[i]]));
- }
- }
- } else {
- Object.keys(ALIASES).forEach(function(crate) {
- if (ALIASES[crate][query.search]) {
- var pushTo = crate === window.currentCrate ? crateAliases : aliases;
- var query_aliases = ALIASES[crate][query.search];
- var len = query_aliases.length;
- for (var i = 0; i < len; ++i) {
- pushTo.push(createAliasFromItem(searchIndex[query_aliases[i]]));
- }
- }
- });
- }
-
- var sortFunc = function(aaa, bbb) {
- if (aaa.path < bbb.path) {
- return 1;
- } else if (aaa.path === bbb.path) {
- return 0;
- }
- return -1;
- };
- crateAliases.sort(sortFunc);
- aliases.sort(sortFunc);
-
- var pushFunc = function(alias) {
- alias.alias = query.raw;
- var res = buildHrefAndPath(alias);
- alias.displayPath = pathSplitter(res[0]);
- alias.fullPath = alias.displayPath + alias.name;
- alias.href = res[1];
-
- ret.others.unshift(alias);
- if (ret.others.length > MAX_RESULTS) {
- ret.others.pop();
- }
- };
- onEach(aliases, pushFunc);
- onEach(crateAliases, pushFunc);
- }
-
- // quoted values mean literal search
- var nSearchWords = searchWords.length;
- var i, it;
- var ty;
- var fullId;
- var returned;
- var in_args;
- var len;
- if ((val.charAt(0) === "\"" || val.charAt(0) === "'") &&
- val.charAt(val.length - 1) === val.charAt(0))
- {
- val = extractGenerics(val.substr(1, val.length - 2));
- for (i = 0; i < nSearchWords; ++i) {
- if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) {
- continue;
- }
- in_args = findArg(searchIndex[i], val, true, typeFilter);
- returned = checkReturned(searchIndex[i], val, true, typeFilter);
- ty = searchIndex[i];
- fullId = ty.id;
-
- if (searchWords[i] === val.name
- && typePassesFilter(typeFilter, searchIndex[i].ty)
- && results[fullId] === undefined) {
- results[fullId] = {
- id: i,
- index: -1,
- dontValidate: true,
- };
- }
- if (in_args === true && results_in_args[fullId] === undefined) {
- results_in_args[fullId] = {
- id: i,
- index: -1,
- dontValidate: true,
- };
- }
- if (returned === true && results_returned[fullId] === undefined) {
- results_returned[fullId] = {
- id: i,
- index: -1,
- dontValidate: true,
- };
- }
- }
- query.inputs = [val];
- query.output = val;
- query.search = val;
- // searching by type
- } else if (val.search("->") > -1) {
- var trimmer = function(s) { return s.trim(); };
- var parts = val.split("->").map(trimmer);
- var input = parts[0];
- // sort inputs so that order does not matter
- var inputs = input.split(",").map(trimmer).sort();
- for (i = 0, len = inputs.length; i < len; ++i) {
- inputs[i] = extractGenerics(inputs[i]);
- }
- var output = extractGenerics(parts[1]);
-
- for (i = 0; i < nSearchWords; ++i) {
- if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) {
- continue;
- }
- var type = searchIndex[i].type;
- ty = searchIndex[i];
- if (!type) {
- continue;
- }
- fullId = ty.id;
-
- returned = checkReturned(ty, output, true, NO_TYPE_FILTER);
- if (output.name === "*" || returned === true) {
- in_args = false;
- var is_module = false;
-
- if (input === "*") {
- is_module = true;
- } else {
- var allFound = true;
- for (it = 0, len = inputs.length; allFound === true && it < len; it++) {
- allFound = checkType(type, inputs[it], true);
- }
- in_args = allFound;
- }
- if (in_args === true) {
- results_in_args[fullId] = {
- id: i,
- index: -1,
- dontValidate: true,
- };
- }
- if (returned === true) {
- results_returned[fullId] = {
- id: i,
- index: -1,
- dontValidate: true,
- };
- }
- if (is_module === true) {
- results[fullId] = {
- id: i,
- index: -1,
- dontValidate: true,
- };
- }
- }
- }
- query.inputs = inputs.map(function(input) {
- return input.name;
- });
- query.output = output.name;
- } else {
- query.inputs = [val];
- query.output = val;
- query.search = val;
- // gather matching search results up to a certain maximum
- val = val.replace(/\_/g, "");
-
- var valGenerics = extractGenerics(val);
-
- var paths = valLower.split("::");
- removeEmptyStringsFromArray(paths);
- val = paths[paths.length - 1];
- var contains = paths.slice(0, paths.length > 1 ? paths.length - 1 : 1);
-
- var lev, j;
- for (j = 0; j < nSearchWords; ++j) {
- ty = searchIndex[j];
- if (!ty || (filterCrates !== undefined && ty.crate !== filterCrates)) {
- continue;
- }
- var lev_add = 0;
- if (paths.length > 1) {
- lev = checkPath(contains, paths[paths.length - 1], ty);
- if (lev > MAX_LEV_DISTANCE) {
- continue;
- } else if (lev > 0) {
- lev_add = lev / 10;
- }
- }
-
- returned = MAX_LEV_DISTANCE + 1;
- in_args = MAX_LEV_DISTANCE + 1;
- var index = -1;
- // we want lev results to go lower than others
- lev = MAX_LEV_DISTANCE + 1;
- fullId = ty.id;
-
- if (searchWords[j].indexOf(split[i]) > -1 ||
- searchWords[j].indexOf(val) > -1 ||
- ty.normalizedName.indexOf(val) > -1)
- {
- // filter type: ... queries
- if (typePassesFilter(typeFilter, ty.ty) && results[fullId] === undefined) {
- index = ty.normalizedName.indexOf(val);
- }
- }
- if ((lev = levenshtein(searchWords[j], val)) <= MAX_LEV_DISTANCE) {
- if (typePassesFilter(typeFilter, ty.ty) === false) {
- lev = MAX_LEV_DISTANCE + 1;
- } else {
- lev += 1;
- }
- }
- in_args = findArg(ty, valGenerics, false, typeFilter);
- returned = checkReturned(ty, valGenerics, false, typeFilter);
-
- lev += lev_add;
- if (lev > 0 && val.length > 3 && searchWords[j].indexOf(val) > -1) {
- if (val.length < 6) {
- lev -= 1;
- } else {
- lev = 0;
- }
- }
- if (in_args <= MAX_LEV_DISTANCE) {
- if (results_in_args[fullId] === undefined) {
- results_in_args[fullId] = {
- id: j,
- index: index,
- lev: in_args,
- };
- }
- results_in_args[fullId].lev =
- Math.min(results_in_args[fullId].lev, in_args);
- }
- if (returned <= MAX_LEV_DISTANCE) {
- if (results_returned[fullId] === undefined) {
- results_returned[fullId] = {
- id: j,
- index: index,
- lev: returned,
- };
- }
- results_returned[fullId].lev =
- Math.min(results_returned[fullId].lev, returned);
- }
- if (index !== -1 || lev <= MAX_LEV_DISTANCE) {
- if (index !== -1 && paths.length < 2) {
- lev = 0;
- }
- if (results[fullId] === undefined) {
- results[fullId] = {
- id: j,
- index: index,
- lev: lev,
- };
- }
- results[fullId].lev = Math.min(results[fullId].lev, lev);
- }
- }
- }
-
- var ret = {
- "in_args": sortResults(results_in_args, true),
- "returned": sortResults(results_returned, true),
- "others": sortResults(results),
- };
- handleAliases(ret, query, filterCrates);
- return ret;
- }
-
- /**
- * Validate performs the following boolean logic. For example:
- * "File::open" will give IF A PARENT EXISTS => ("file" && "open")
- * exists in (name || path || parent) OR => ("file" && "open") exists in
- * (name || path )
- *
- * This could be written functionally, but I wanted to minimise
- * functions on stack.
- *
- * @param {[string]} name [The name of the result]
- * @param {[string]} path [The path of the result]
- * @param {[string]} keys [The keys to be used (["file", "open"])]
- * @param {[object]} parent [The parent of the result]
- * @return {[boolean]} [Whether the result is valid or not]
- */
- function validateResult(name, path, keys, parent) {
- for (var i = 0, len = keys.length; i < len; ++i) {
- // each check is for validation so we negate the conditions and invalidate
- if (!(
- // check for an exact name match
- name.indexOf(keys[i]) > -1 ||
- // then an exact path match
- path.indexOf(keys[i]) > -1 ||
- // next if there is a parent, check for exact parent match
- (parent !== undefined && parent.name !== undefined &&
- parent.name.toLowerCase().indexOf(keys[i]) > -1) ||
- // lastly check to see if the name was a levenshtein match
- levenshtein(name, keys[i]) <= MAX_LEV_DISTANCE)) {
- return false;
- }
- }
- return true;
- }
-
- function getQuery(raw) {
- var matches, type, query;
- query = raw;
-
- matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i);
- if (matches) {
- type = matches[1].replace(/^const$/, "constant");
- query = query.substring(matches[0].length);
- }
-
- return {
- raw: raw,
- query: query,
- type: type,
- id: query + type
- };
- }
-
- function initSearchNav() {
- var hoverTimeout;
-
- var click_func = function(e) {
- var el = e.target;
- // to retrieve the real "owner" of the event.
- while (el.tagName !== "TR") {
- el = el.parentNode;
- }
- var dst = e.target.getElementsByTagName("a");
- if (dst.length < 1) {
- return;
- }
- dst = dst[0];
- if (window.location.pathname === dst.pathname) {
- hideSearchResults();
- document.location.href = dst.href;
- }
- };
- var mouseover_func = function(e) {
- if (mouseMovedAfterSearch) {
- var el = e.target;
- // to retrieve the real "owner" of the event.
- while (el.tagName !== "TR") {
- el = el.parentNode;
- }
- clearTimeout(hoverTimeout);
- hoverTimeout = setTimeout(function() {
- onEachLazy(document.getElementsByClassName("search-results"), function(e) {
- onEachLazy(e.getElementsByClassName("result"), function(i_e) {
- removeClass(i_e, "highlighted");
- });
- });
- addClass(el, "highlighted");
- }, 20);
- }
- };
- onEachLazy(document.getElementsByClassName("search-results"), function(e) {
- onEachLazy(e.getElementsByClassName("result"), function(i_e) {
- i_e.onclick = click_func;
- i_e.onmouseover = mouseover_func;
- });
- });
-
- search_input.onkeydown = function(e) {
- // "actives" references the currently highlighted item in each search tab.
- // Each array in "actives" represents a tab.
- var actives = [[], [], []];
- // "current" is used to know which tab we're looking into.
- var current = 0;
- onEachLazy(document.getElementById("results").childNodes, function(e) {
- onEachLazy(e.getElementsByClassName("highlighted"), function(h_e) {
- actives[current].push(h_e);
- });
- current += 1;
- });
-
- if (e.which === 38) { // up
- if (e.ctrlKey) { // Going through result tabs.
- printTab(currentTab > 0 ? currentTab - 1 : 2);
- } else {
- if (!actives[currentTab].length ||
- !actives[currentTab][0].previousElementSibling) {
- return;
- }
- addClass(actives[currentTab][0].previousElementSibling, "highlighted");
- removeClass(actives[currentTab][0], "highlighted");
- }
- e.preventDefault();
- } else if (e.which === 40) { // down
- if (e.ctrlKey) { // Going through result tabs.
- printTab(currentTab > 1 ? 0 : currentTab + 1);
- } else if (!actives[currentTab].length) {
- var results = document.getElementById("results").childNodes;
- if (results.length > 0) {
- var res = results[currentTab].getElementsByClassName("result");
- if (res.length > 0) {
- addClass(res[0], "highlighted");
- }
- }
- } else if (actives[currentTab][0].nextElementSibling) {
- addClass(actives[currentTab][0].nextElementSibling, "highlighted");
- removeClass(actives[currentTab][0], "highlighted");
- }
- e.preventDefault();
- } else if (e.which === 13) { // return
- if (actives[currentTab].length) {
- document.location.href =
- actives[currentTab][0].getElementsByTagName("a")[0].href;
- }
- } else if (e.which === 16) { // shift
- // Does nothing, it's just to avoid losing "focus" on the highlighted element.
- } else if (actives[currentTab].length > 0) {
- removeClass(actives[currentTab][0], "highlighted");
- }
- };
- }
-
- function buildHrefAndPath(item) {
- var displayPath;
- var href;
- var type = itemTypes[item.ty];
- var name = item.name;
- var path = item.path;
-
- if (type === "mod") {
- displayPath = path + "::";
- href = window.rootPath + path.replace(/::/g, "/") + "/" +
- name + "/index.html";
- } else if (type === "primitive" || type === "keyword") {
- displayPath = "";
- href = window.rootPath + path.replace(/::/g, "/") +
- "/" + type + "." + name + ".html";
- } else if (type === "externcrate") {
- displayPath = "";
- href = window.rootPath + name + "/index.html";
- } else if (item.parent !== undefined) {
- var myparent = item.parent;
- var anchor = "#" + type + "." + name;
- var parentType = itemTypes[myparent.ty];
- var pageType = parentType;
- var pageName = myparent.name;
-
- if (parentType === "primitive") {
- displayPath = myparent.name + "::";
- } else if (type === "structfield" && parentType === "variant") {
- // Structfields belonging to variants are special: the
- // final path element is the enum name.
- var enumNameIdx = item.path.lastIndexOf("::");
- var enumName = item.path.substr(enumNameIdx + 2);
- path = item.path.substr(0, enumNameIdx);
- displayPath = path + "::" + enumName + "::" + myparent.name + "::";
- anchor = "#variant." + myparent.name + ".field." + name;
- pageType = "enum";
- pageName = enumName;
- } else {
- displayPath = path + "::" + myparent.name + "::";
- }
- href = window.rootPath + path.replace(/::/g, "/") +
- "/" + pageType +
- "." + pageName +
- ".html" + anchor;
- } else {
- displayPath = item.path + "::";
- href = window.rootPath + item.path.replace(/::/g, "/") +
- "/" + type + "." + name + ".html";
- }
- return [displayPath, href];
- }
-
- function escape(content) {
- var h1 = document.createElement("h1");
- h1.textContent = content;
- return h1.innerHTML;
- }
-
- function pathSplitter(path) {
- var tmp = "<span>" + path.replace(/::/g, "::</span><span>");
- if (tmp.endsWith("<span>")) {
- return tmp.slice(0, tmp.length - 6);
- }
- return tmp;
- }
-
- function addTab(array, query, display) {
- var extraStyle = "";
- if (display === false) {
- extraStyle = " style=\"display: none;\"";
- }
-
- var output = "";
- var duplicates = {};
- var length = 0;
- if (array.length > 0) {
- output = "<table class=\"search-results\"" + extraStyle + ">";
-
- array.forEach(function(item) {
- var name, type;
-
- name = item.name;
- type = itemTypes[item.ty];
-
- if (item.is_alias !== true) {
- if (duplicates[item.fullPath]) {
- return;
- }
- duplicates[item.fullPath] = true;
- }
- length += 1;
-
- output += "<tr class=\"" + type + " result\"><td>" +
- "<a href=\"" + item.href + "\">" +
- (item.is_alias === true ?
- ("<span class=\"alias\"><b>" + item.alias + " </b></span><span " +
- "class=\"grey\"><i> - see </i></span>") : "") +
- item.displayPath + "<span class=\"" + type + "\">" +
- name + "</span></a></td><td>" +
- "<a href=\"" + item.href + "\">" +
- "<span class=\"desc\">" + item.desc +
- " </span></a></td></tr>";
- });
- output += "</table>";
- } else {
- output = "<div class=\"search-failed\"" + extraStyle + ">No results :(<br/>" +
- "Try on <a href=\"https://duckduckgo.com/?q=" +
- encodeURIComponent("rust " + query.query) +
- "\">DuckDuckGo</a>?<br/><br/>" +
- "Or try looking in one of these:<ul><li>The <a " +
- "href=\"https://doc.rust-lang.org/reference/index.html\">Rust Reference</a> " +
- " for technical details about the language.</li><li><a " +
- "href=\"https://doc.rust-lang.org/rust-by-example/index.html\">Rust By " +
- "Example</a> for expository code examples.</a></li><li>The <a " +
- "href=\"https://doc.rust-lang.org/book/index.html\">Rust Book</a> for " +
- "introductions to language features and the language itself.</li><li><a " +
- "href=\"https://docs.rs\">Docs.rs</a> for documentation of crates released on" +
- " <a href=\"https://crates.io/\">crates.io</a>.</li></ul></div>";
- }
- return [output, length];
- }
-
- function makeTabHeader(tabNb, text, nbElems) {
- if (currentTab === tabNb) {
- return "<button class=\"selected\">" + text +
- " <div class=\"count\">(" + nbElems + ")</div></button>";
- }
- return "<button>" + text + " <div class=\"count\">(" + nbElems + ")</div></button>";
- }
-
- function showResults(results) {
- var search = getSearchElement();
- if (results.others.length === 1
- && getSettingValue("go-to-only-result") === "true"
- // By default, the search DOM element is "empty" (meaning it has no children not
- // text content). Once a search has been run, it won't be empty, even if you press
- // ESC or empty the search input (which also "cancels" the search).
- && (!search.firstChild || search.firstChild.innerText !== getSearchLoadingText()))
- {
- var elem = document.createElement("a");
- elem.href = results.others[0].href;
- elem.style.display = "none";
- // For firefox, we need the element to be in the DOM so it can be clicked.
- document.body.appendChild(elem);
- elem.click();
- return;
- }
- var query = getQuery(search_input.value);
-
- currentResults = query.id;
-
- var ret_others = addTab(results.others, query);
- var ret_in_args = addTab(results.in_args, query, false);
- var ret_returned = addTab(results.returned, query, false);
-
- // Navigate to the relevant tab if the current tab is empty, like in case users search
- // for "-> String". If they had selected another tab previously, they have to click on
- // it again.
- if ((currentTab === 0 && ret_others[1] === 0) ||
- (currentTab === 1 && ret_in_args[1] === 0) ||
- (currentTab === 2 && ret_returned[1] === 0)) {
- if (ret_others[1] !== 0) {
- currentTab = 0;
- } else if (ret_in_args[1] !== 0) {
- currentTab = 1;
- } else if (ret_returned[1] !== 0) {
- currentTab = 2;
- }
- }
+ }
- var output = "<h1>Results for " + escape(query.query) +
- (query.type ? " (type: " + escape(query.type) + ")" : "") + "</h1>" +
- "<div id=\"titles\">" +
- makeTabHeader(0, "In Names", ret_others[1]) +
- makeTabHeader(1, "In Parameters", ret_in_args[1]) +
- makeTabHeader(2, "In Return Types", ret_returned[1]) +
- "</div><div id=\"results\">" +
- ret_others[0] + ret_in_args[0] + ret_returned[0] + "</div>";
-
- search.innerHTML = output;
- showSearchResults(search);
- initSearchNav();
- var elems = document.getElementById("titles").childNodes;
- elems[0].onclick = function() { printTab(0); };
- elems[1].onclick = function() { printTab(1); };
- elems[2].onclick = function() { printTab(2); };
- printTab(currentTab);
- }
-
- function execSearch(query, searchWords, filterCrates) {
- function getSmallest(arrays, positions, notDuplicates) {
- var start = null;
-
- for (var it = 0, len = positions.length; it < len; ++it) {
- if (arrays[it].length > positions[it] &&
- (start === null || start > arrays[it][positions[it]].lev) &&
- !notDuplicates[arrays[it][positions[it]].fullPath]) {
- start = arrays[it][positions[it]].lev;
- }
- }
- return start;
+ function findParentElement(elem, tagName) {
+ do {
+ if (elem && elem.tagName === tagName) {
+ return elem;
}
+ elem = elem.parentNode;
+ } while (elem);
+ return null;
+ }
- function mergeArrays(arrays) {
- var ret = [];
- var positions = [];
- var notDuplicates = {};
+ document.addEventListener("keypress", handleShortcut);
+ document.addEventListener("keydown", handleShortcut);
- for (var x = 0, arrays_len = arrays.length; x < arrays_len; ++x) {
- positions.push(0);
- }
- while (ret.length < MAX_RESULTS) {
- var smallest = getSmallest(arrays, positions, notDuplicates);
+ var handleSourceHighlight = (function() {
+ var prev_line_id = 0;
- if (smallest === null) {
- break;
- }
- for (x = 0; x < arrays_len && ret.length < MAX_RESULTS; ++x) {
- if (arrays[x].length > positions[x] &&
- arrays[x][positions[x]].lev === smallest &&
- !notDuplicates[arrays[x][positions[x]].fullPath]) {
- ret.push(arrays[x][positions[x]]);
- notDuplicates[arrays[x][positions[x]].fullPath] = true;
- positions[x] += 1;
- }
- }
- }
- return ret;
+ var set_fragment = function(name) {
+ var x = window.scrollX,
+ y = window.scrollY;
+ if (searchState.browserSupportsHistoryApi()) {
+ history.replaceState(null, null, "#" + name);
+ highlightSourceLines();
+ } else {
+ location.replace("#" + name);
}
+ // Prevent jumps when selecting one or many lines
+ window.scrollTo(x, y);
+ };
- var queries = query.raw.split(",");
- var results = {
- "in_args": [],
- "returned": [],
- "others": [],
- };
-
- for (var i = 0, len = queries.length; i < len; ++i) {
- query = queries[i].trim();
- if (query.length !== 0) {
- var tmp = execQuery(getQuery(query), searchWords, filterCrates);
+ return function(ev) {
+ var cur_line_id = parseInt(ev.target.id, 10);
+ ev.preventDefault();
- results.in_args.push(tmp.in_args);
- results.returned.push(tmp.returned);
- results.others.push(tmp.others);
+ if (ev.shiftKey && prev_line_id) {
+ // Swap selection if needed
+ if (prev_line_id > cur_line_id) {
+ var tmp = prev_line_id;
+ prev_line_id = cur_line_id;
+ cur_line_id = tmp;
}
- }
- if (queries.length > 1) {
- return {
- "in_args": mergeArrays(results.in_args),
- "returned": mergeArrays(results.returned),
- "others": mergeArrays(results.others),
- };
- }
- return {
- "in_args": results.in_args[0],
- "returned": results.returned[0],
- "others": results.others[0],
- };
- }
-
- function getFilterCrates() {
- var elem = document.getElementById("crate-search");
-
- if (elem && elem.value !== "All crates" && hasOwnProperty(rawSearchIndex, elem.value)) {
- return elem.value;
- }
- return undefined;
- }
- function search(e, forced) {
- var params = getQueryStringParams();
- var query = getQuery(search_input.value.trim());
+ set_fragment(prev_line_id + "-" + cur_line_id);
+ } else {
+ prev_line_id = cur_line_id;
- if (e) {
- e.preventDefault();
+ set_fragment(cur_line_id);
}
+ };
+ }());
- if (query.query.length === 0) {
- return;
- }
- if (forced !== true && query.id === currentResults) {
- if (query.query.length > 0) {
- putBackSearch(search_input);
- }
- return;
+ document.addEventListener("click", function(ev) {
+ var helpElem = getHelpElement(false);
+ if (hasClass(ev.target, "help-button")) {
+ displayHelp(true, ev);
+ } else if (hasClass(ev.target, "collapse-toggle")) {
+ collapseDocs(ev.target, "toggle");
+ } else if (hasClass(ev.target.parentNode, "collapse-toggle")) {
+ collapseDocs(ev.target.parentNode, "toggle");
+ } else if (ev.target.tagName === "SPAN" && hasClass(ev.target.parentNode, "line-numbers")) {
+ handleSourceHighlight(ev);
+ } else if (helpElem && hasClass(helpElem, "hidden") === false) {
+ var is_inside_help_popup = ev.target !== helpElem && helpElem.contains(ev.target);
+ if (is_inside_help_popup === false) {
+ addClass(helpElem, "hidden");
+ removeClass(document.body, "blur");
}
-
- // Update document title to maintain a meaningful browser history
- searchTitle = "Results for " + query.query + " - Rust";
-
- // Because searching is incremental by character, only the most
- // recent search query is added to the browser history.
- if (browserSupportsHistoryApi()) {
- var newURL = getNakedUrl() + "?search=" + encodeURIComponent(query.raw) +
- window.location.hash;
- if (!history.state && !params.search) {
- history.pushState(query, "", newURL);
- } else {
- history.replaceState(query, "", newURL);
- }
+ } else {
+ // Making a collapsed element visible on onhashchange seems
+ // too late
+ var a = findParentElement(ev.target, "A");
+ if (a && a.hash) {
+ expandSection(a.hash.replace(/^#/, ""));
}
+ }
+ });
- var filterCrates = getFilterCrates();
- showResults(execSearch(query, index, filterCrates));
- }
-
- function buildIndex(rawSearchIndex) {
- searchIndex = [];
- var searchWords = [];
- var i, word;
- var currentIndex = 0;
- var id = 0;
-
- for (var crate in rawSearchIndex) {
- if (!hasOwnProperty(rawSearchIndex, crate)) { continue; }
-
- var crateSize = 0;
-
- searchWords.push(crate);
- var normalizedName = crate.indexOf("_") === -1
- ? crate
- : crate.replace(/_/g, "");
- // This object should have exactly the same set of fields as the "row"
- // object defined below. Your JavaScript runtime will thank you.
- // https://mathiasbynens.be/notes/shapes-ics
- var crateRow = {
- crate: crate,
- ty: 1, // == ExternCrate
- name: crate,
- path: "",
- desc: rawSearchIndex[crate].doc,
- parent: undefined,
- type: null,
- id: id,
- normalizedName: normalizedName,
- };
- id += 1;
- searchIndex.push(crateRow);
- currentIndex += 1;
-
- // an array of (Number) item types
- var itemTypes = rawSearchIndex[crate].t;
- // an array of (String) item names
- var itemNames = rawSearchIndex[crate].n;
- // an array of (String) full paths (or empty string for previous path)
- var itemPaths = rawSearchIndex[crate].q;
- // an array of (String) descriptions
- var itemDescs = rawSearchIndex[crate].d;
- // an array of (Number) the parent path index + 1 to `paths`, or 0 if none
- var itemParentIdxs = rawSearchIndex[crate].i;
- // an array of (Object | null) the type of the function, if any
- var itemFunctionSearchTypes = rawSearchIndex[crate].f;
- // an array of [(Number) item type,
- // (String) name]
- var paths = rawSearchIndex[crate].p;
- // a array of [(String) alias name
- // [Number] index to items]
- var aliases = rawSearchIndex[crate].a;
-
- // convert `rawPaths` entries into object form
- var len = paths.length;
- for (i = 0; i < len; ++i) {
- paths[i] = {ty: paths[i][0], name: paths[i][1]};
- }
+ (function() {
+ var x = document.getElementsByClassName("version-selector");
+ if (x.length > 0) {
+ x[0].onchange = function() {
+ var i, match,
+ url = document.location.href,
+ stripped = "",
+ len = window.rootPath.match(/\.\.\//g).length + 1;
- // convert `item*` into an object form, and construct word indices.
- //
- // before any analysis is performed lets gather the search terms to
- // search against apart from the rest of the data. This is a quick
- // operation that is cached for the life of the page state so that
- // all other search operations have access to this cached data for
- // faster analysis operations
- len = itemTypes.length;
- var lastPath = "";
for (i = 0; i < len; ++i) {
- // This object should have exactly the same set of fields as the "crateRow"
- // object defined above.
- if (typeof itemNames[i] === "string") {
- word = itemNames[i].toLowerCase();
- searchWords.push(word);
- } else {
- word = "";
- searchWords.push("");
- }
- var normalizedName = word.indexOf("_") === -1
- ? word
- : word.replace(/_/g, "");
- var row = {
- crate: crate,
- ty: itemTypes[i],
- name: itemNames[i],
- path: itemPaths[i] ? itemPaths[i] : lastPath,
- desc: itemDescs[i],
- parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
- type: itemFunctionSearchTypes[i],
- id: id,
- normalizedName: normalizedName,
- };
- id += 1;
- searchIndex.push(row);
- lastPath = row.path;
- crateSize += 1;
- }
-
- if (aliases) {
- ALIASES[crate] = {};
- var j, local_aliases;
- for (var alias_name in aliases) {
- if (!aliases.hasOwnProperty(alias_name)) { continue; }
-
- if (!ALIASES[crate].hasOwnProperty(alias_name)) {
- ALIASES[crate][alias_name] = [];
- }
- local_aliases = aliases[alias_name];
- for (j = 0, len = local_aliases.length; j < len; ++j) {
- ALIASES[crate][alias_name].push(local_aliases[j] + currentIndex);
- }
- }
- }
- currentIndex += crateSize;
- }
- return searchWords;
- }
-
- function registerSearchEvents() {
- var searchAfter500ms = function() {
- clearInputTimeout();
- if (search_input.value.length === 0) {
- if (browserSupportsHistoryApi()) {
- history.replaceState("", window.currentCrate + " - Rust",
- getNakedUrl() + window.location.hash);
+ match = url.match(/\/[^\/]*$/);
+ if (i < len - 1) {
+ stripped = match[0] + stripped;
}
- hideSearchResults();
- } else {
- searchTimeout = setTimeout(search, 500);
- }
- };
- search_input.onkeyup = searchAfter500ms;
- search_input.oninput = searchAfter500ms;
- document.getElementsByClassName("search-form")[0].onsubmit = function(e) {
- e.preventDefault();
- clearInputTimeout();
- search();
- };
- search_input.onchange = function(e) {
- if (e.target !== document.activeElement) {
- // To prevent doing anything when it's from a blur event.
- return;
+ url = url.substring(0, url.length - match[0].length);
}
- // Do NOT e.preventDefault() here. It will prevent pasting.
- clearInputTimeout();
- // zero-timeout necessary here because at the time of event handler execution the
- // pasted content is not in the input field yet. Shouldn’t make any difference for
- // change, though.
- setTimeout(search, 0);
- };
- search_input.onpaste = search_input.onchange;
-
- var selectCrate = document.getElementById("crate-search");
- if (selectCrate) {
- selectCrate.onchange = function() {
- updateLocalStorage("rustdoc-saved-filter-crate", selectCrate.value);
- search(undefined, true);
- };
- }
- // Push and pop states are used to add search results to the browser
- // history.
- if (browserSupportsHistoryApi()) {
- // Store the previous <title> so we can revert back to it later.
- var previousTitle = document.title;
-
- window.addEventListener("popstate", function(e) {
- var params = getQueryStringParams();
- // Revert to the previous title manually since the History
- // API ignores the title parameter.
- document.title = previousTitle;
- // When browsing forward to search results the previous
- // search will be repeated, so the currentResults are
- // cleared to ensure the search is successful.
- currentResults = null;
- // Synchronize search bar with query string state and
- // perform the search. This will empty the bar if there's
- // nothing there, which lets you really go back to a
- // previous state with nothing in the bar.
- if (params.search && params.search.length > 0) {
- search_input.value = params.search;
- // Some browsers fire "onpopstate" for every page load
- // (Chrome), while others fire the event only when actually
- // popping a state (Firefox), which is why search() is
- // called both here and at the end of the startSearch()
- // function.
- search(e);
- } else {
- search_input.value = "";
- // When browsing back from search results the main page
- // visibility must be reset.
- hideSearchResults();
- }
- });
- }
+ var selectedVersion = document.getElementsByClassName("version-selector")[0].value;
+ url += "/" + selectedVersion + stripped;
- // This is required in firefox to avoid this problem: Navigating to a search result
- // with the keyboard, hitting enter, and then hitting back would take you back to
- // the doc page, rather than the search that should overlay it.
- // This was an interaction between the back-forward cache and our handlers
- // that try to sync state between the URL and the search input. To work around it,
- // do a small amount of re-init on page show.
- window.onpageshow = function(){
- var qSearch = getQueryStringParams().search;
- if (search_input.value === "" && qSearch) {
- search_input.value = qSearch;
- }
- search();
+ document.location.href = url;
};
}
-
- index = buildIndex(rawSearchIndex);
- registerSearchEvents();
- // If there's a search term in the URL, execute the search now.
- if (getQueryStringParams().search) {
- search();
- }
- };
+ }());
function addSidebarCrates(crates) {
// Draw a convenient sidebar of known crates if we have a listing
block("foreigntype", "Foreign Types");
block("keyword", "Keywords");
block("traitalias", "Trait Aliases");
+
+ // `crates{version}.js` should always be loaded before this script, so we can use it safely.
+ addSidebarCrates(window.ALL_CRATES);
};
window.register_implementors = function(imp) {
return;
}
if (hasClass(innerToggle, "will-expand")) {
- updateLocalStorage("rustdoc-collapse", "false");
removeClass(innerToggle, "will-expand");
+ onEachLazy(document.getElementsByTagName("details"), function(e) {
+ e.open = true;
+ });
onEveryMatchingChild(innerToggle, "inner", function(e) {
e.innerHTML = labelForToggleButton(false);
});
});
}
} else {
- updateLocalStorage("rustdoc-collapse", "true");
addClass(innerToggle, "will-expand");
+ onEachLazy(document.getElementsByTagName("details"), function(e) {
+ e.open = false;
+ });
onEveryMatchingChild(innerToggle, "inner", function(e) {
var parent = e.parentNode;
var superParent = null;
if (hasClass(relatedDoc, "item-info")) {
relatedDoc = relatedDoc.nextElementSibling;
}
- if (hasClass(relatedDoc, "docblock") || hasClass(relatedDoc, "sub-variant")) {
+ if (hasClass(relatedDoc, "docblock")) {
if (mode === "toggle") {
if (hasClass(relatedDoc, "hidden-by-usual-hider")) {
action = "show";
}
}
- function collapser(e, collapse) {
+ function collapseNonInherent(e) {
// inherent impl ids are like "impl" or impl-<number>'.
// they will never be hidden by default.
var n = e.parentElement;
if (n.id.match(/^impl(?:-\d+)?$/) === null) {
// Automatically minimize all non-inherent impls
- if (collapse || hasClass(n, "impl")) {
+ if (hasClass(n, "impl")) {
collapseDocs(e, "hide");
}
}
}
- function autoCollapse(collapse) {
- if (collapse) {
- toggleAllDocs(true);
- } else if (getSettingValue("auto-hide-trait-implementations") !== "false") {
- var impl_list = document.getElementById("trait-implementations-list");
-
- if (impl_list !== null) {
- onEachLazy(impl_list.getElementsByClassName("collapse-toggle"), function(e) {
- collapser(e, collapse);
- });
- }
-
- var blanket_list = document.getElementById("blanket-implementations-list");
-
- if (blanket_list !== null) {
- onEachLazy(blanket_list.getElementsByClassName("collapse-toggle"), function(e) {
- collapser(e, collapse);
- });
- }
- }
- }
-
function insertAfter(newNode, referenceNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
var toggle = createSimpleToggle(false);
var hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true";
var hideImplementors = getSettingValue("auto-collapse-implementors") !== "false";
+ var hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false";
+ var hideTraitImplementations =
+ getSettingValue("auto-hide-trait-implementations") !== "false";
+
+ var impl_list = document.getElementById("trait-implementations-list");
+ if (impl_list !== null) {
+ onEachLazy(impl_list.getElementsByClassName("collapse-toggle"), function(e) {
+ collapseNonInherent(e);
+ });
+ }
+
+ var blanket_list = document.getElementById("blanket-implementations-list");
+ if (blanket_list !== null) {
+ onEachLazy(blanket_list.getElementsByClassName("collapse-toggle"), function(e) {
+ collapseNonInherent(e);
+ });
+ }
var func = function(e) {
var next = e.nextElementSibling;
if (!next) {
return;
}
- if (hasClass(e, "impl") &&
- (next.getElementsByClassName("method").length > 0 ||
- next.getElementsByClassName("associatedconstant").length > 0)) {
- var newToggle = toggle.cloneNode(true);
- insertAfter(newToggle, e.childNodes[e.childNodes.length - 1]);
- // In case the option "auto-collapse implementors" is not set to false, we collapse
- // all implementors.
- if (hideImplementors === true && e.parentNode.id === "implementors-list") {
- collapseDocs(newToggle, "hide");
- }
- }
};
onEachLazy(document.getElementsByClassName("method"), func);
onEachLazy(document.getElementsByClassName("associatedconstant"), func);
- onEachLazy(document.getElementsByClassName("impl"), funcImpl);
var impl_call = function() {};
+ onEachLazy(document.getElementsByTagName("details"), function (e) {
+ var showLargeItem = !hideLargeItemContents && hasClass(e, "type-contents-toggle");
+ var showImplementor = !hideImplementors && hasClass(e, "implementors-toggle");
+ if (showLargeItem || showImplementor) {
+ e.open = true;
+ }
+ });
if (hideMethodDocs === true) {
impl_call = function(e, newToggle) {
if (e.id.match(/^impl(?:-\d+)?$/) === null) {
if (currentType) {
currentType = currentType.getElementsByClassName("rust")[0];
if (currentType) {
- currentType.classList.forEach(function(item) {
+ onEachLazy(currentType.classList, function(item) {
if (item !== "main") {
className = item;
return true;
});
}
}
- var showItemDeclarations = getSettingValue("auto-hide-" + className);
- if (showItemDeclarations === null) {
- if (className === "enum" || className === "macro") {
- showItemDeclarations = "false";
- } else if (className === "struct" || className === "union" || className === "trait") {
- showItemDeclarations = "true";
- } else {
- // In case we found an unknown type, we just use the "parent" value.
- showItemDeclarations = getSettingValue("auto-hide-declarations");
- }
- }
- showItemDeclarations = showItemDeclarations === "false";
+
function buildToggleWrapper(e) {
if (hasClass(e, "autohide")) {
var wrap = e.previousElementSibling;
var extraClass;
if (hasClass(e, "type-decl")) {
- fontSize = "20px";
- otherMessage = " Show declaration";
- if (showItemDeclarations === false) {
- extraClass = "collapsed";
- }
- } else if (hasClass(e, "sub-variant")) {
- otherMessage = " Show fields";
+ // We do something special for these
+ return;
} else if (hasClass(e, "non-exhaustive")) {
otherMessage = " This ";
if (hasClass(e, "non-exhaustive-struct")) {
otherMessage,
fontSize,
extraClass,
- hasClass(e, "type-decl") === false || showItemDeclarations === true),
+ true),
e);
- if (hasClass(e, "type-decl") === true && showItemDeclarations === true) {
- collapseDocs(e.previousSibling.childNodes[0], "toggle");
- }
if (hasClass(e, "non-exhaustive") === true) {
collapseDocs(e.previousSibling.childNodes[0], "toggle");
}
}
onEachLazy(document.getElementsByClassName("docblock"), buildToggleWrapper);
- onEachLazy(document.getElementsByClassName("sub-variant"), buildToggleWrapper);
-
- autoCollapse(getSettingValue("collapse") === "true");
var pageId = getPageId();
if (pageId !== null) {
}
}());
- function createToggleWrapper(tog) {
- var span = document.createElement("span");
- span.className = "toggle-label";
- span.style.display = "none";
- span.innerHTML = " Expand attributes";
- tog.appendChild(span);
-
- var wrapper = document.createElement("div");
- wrapper.className = "toggle-wrapper toggle-attributes";
- wrapper.appendChild(tog);
- return wrapper;
- }
-
- (function() {
- // To avoid checking on "rustdoc-item-attributes" value on every loop...
- var itemAttributesFunc = function() {};
- if (getSettingValue("auto-hide-attributes") !== "false") {
- itemAttributesFunc = function(x) {
- collapseDocs(x.previousSibling.childNodes[0], "toggle");
- };
- }
- var attributesToggle = createToggleWrapper(createSimpleToggle(false));
- onEachLazy(main.getElementsByClassName("attributes"), function(i_e) {
- var attr_tog = attributesToggle.cloneNode(true);
- if (hasClass(i_e, "top-attr") === true) {
- addClass(attr_tog, "top-attr");
- }
- i_e.parentNode.insertBefore(attr_tog, i_e);
- itemAttributesFunc(i_e);
- });
- }());
-
(function() {
// To avoid checking on "rustdoc-line-numbers" value on every loop...
var lineNumbersFunc = function() {};
};
});
- // In the search display, allows to switch between tabs.
- function printTab(nb) {
- if (nb === 0 || nb === 1 || nb === 2) {
- currentTab = nb;
- }
- var nb_copy = nb;
- onEachLazy(document.getElementById("titles").childNodes, function(elem) {
- if (nb_copy === 0) {
- addClass(elem, "selected");
- } else {
- removeClass(elem, "selected");
- }
- nb_copy -= 1;
- });
- onEachLazy(document.getElementById("results").childNodes, function(elem) {
- if (nb === 0) {
- elem.style.display = "";
- } else {
- elem.style.display = "none";
- }
- nb -= 1;
- });
- }
-
- function putBackSearch(search_input) {
- var search = getSearchElement();
- if (search_input.value !== "" && hasClass(search, "hidden")) {
- showSearchResults(search);
- if (browserSupportsHistoryApi()) {
- var extra = "?search=" + encodeURIComponent(search_input.value);
- history.replaceState(search_input.value, "",
- getNakedUrl() + extra + window.location.hash);
- }
- document.title = searchTitle;
- }
- }
-
- function getSearchLoadingText() {
- return "Loading search results...";
- }
-
- if (search_input) {
- search_input.onfocus = function() {
- putBackSearch(this);
- };
- }
-
- var params = getQueryStringParams();
- if (params && params.search) {
- var search = getSearchElement();
- search.innerHTML = "<h3 style=\"text-align: center;\">" + getSearchLoadingText() + "</h3>";
- showSearchResults(search);
- }
-
var sidebar_menu = document.getElementsByClassName("sidebar-menu")[0];
if (sidebar_menu) {
sidebar_menu.onclick = function() {
// errors in mobile browsers).
if (e.tagName === "H2" || e.tagName === "H3") {
var nextTagName = e.nextElementSibling.tagName;
- if (nextTagName == "H2" || nextTagName == "H3") {
+ if (nextTagName === "H2" || nextTagName === "H3") {
e.nextElementSibling.style.display = "flex";
- } else {
+ } else if (nextTagName !== "DETAILS") {
e.nextElementSibling.style.display = "block";
}
}
});
}
- function addSearchOptions(crates) {
- var elem = document.getElementById("crate-search");
-
- if (!elem) {
- return;
- }
- var savedCrate = getSettingValue("saved-filter-crate");
- for (var i = 0, len = crates.length; i < len; ++i) {
- var option = document.createElement("option");
- option.value = crates[i];
- option.innerText = crates[i];
- elem.appendChild(option);
- // Set the crate filter from saved storage, if the current page has the saved crate
- // filter.
- //
- // If not, ignore the crate filter -- we want to support filtering for crates on sites
- // like doc.rust-lang.org where the crates may differ from page to page while on the
- // same domain.
- if (crates[i] === savedCrate) {
- elem.value = savedCrate;
- }
- }
- };
-
function buildHelperPopup() {
var popup = document.createElement("aside");
addClass(popup, "hidden");
container.appendChild(div_infos);
popup.appendChild(container);
- insertAfter(popup, getSearchElement());
+ insertAfter(popup, searchState.outputElement());
// So that it's only built once and then it'll do nothing when called!
buildHelperPopup = function() {};
}
- function loadScript(url) {
- var script = document.createElement('script');
- script.src = url;
- document.head.append(script);
- }
-
- function setupSearchLoader() {
- var searchLoaded = false;
- function loadSearch() {
- if (!searchLoaded) {
- searchLoaded = true;
- loadScript(window.searchJS);
- }
- }
-
- // `crates{version}.js` should always be loaded before this script, so we can use it safely.
- addSearchOptions(window.ALL_CRATES);
- addSidebarCrates(window.ALL_CRATES);
-
- search_input.addEventListener("focus", function() {
- search_input.origPlaceholder = search_input.placeholder;
- search_input.placeholder = "Type your search here.";
- loadSearch();
- });
- search_input.addEventListener("blur", function() {
- search_input.placeholder = search_input.origPlaceholder;
- });
- search_input.removeAttribute('disabled');
-
- var crateSearchDropDown = document.getElementById("crate-search");
- // `crateSearchDropDown` can be null in case there is only crate because in that case, the
- // crate filter dropdown is removed.
- if (crateSearchDropDown) {
- crateSearchDropDown.addEventListener("focus", loadSearch);
- }
- var params = getQueryStringParams();
- if (params.search !== undefined) {
- loadSearch();
- }
- }
-
onHashChange(null);
window.onhashchange = onHashChange;
- setupSearchLoader();
+ searchState.setup();
}());
function copy_path(but) {
padding-bottom: 6px;
}
h1.fqn {
+ display: flex;
+ width: 100%;
border-bottom: 1px dashed;
margin-top: 0;
}
font-weight: normal;
}
+h1.fqn > .out-of-band {
+ float: unset;
+ flex: 1;
+ text-align: right;
+ margin-left: 8px;
+}
+
h3.impl > .out-of-band {
font-size: 21px;
}
0 -1px 0 black;
}
-.module-item .stab {
+.module-item .stab,
+.import-item .stab {
border-radius: 3px;
display: inline-block;
font-size: 80%;
vertical-align: text-bottom;
}
-.module-item.unstable {
+.module-item.unstable,
+.import-item.unstable {
opacity: 0.65;
}
color: inherit;
}
+.code-attribute {
+ font-weight: 300;
+}
+
.collapse-toggle {
font-weight: 300;
position: absolute;
}
.sub-variant, .sub-variant > h3 {
- margin-top: 1px !important;
+ margin-top: 0px !important;
+ padding-top: 1px;
}
-#main > .sub-variant > h3 {
+#main > details > .sub-variant > h3 {
font-size: 15px;
margin-left: 25px;
margin-bottom: 5px;
margin-top: 3px;
}
-.enum > .toggle-wrapper + .docblock, .struct > .toggle-wrapper + .docblock {
- margin-left: 30px;
- margin-bottom: 20px;
- margin-top: 5px;
-}
-
.docblock > .section-header:first-child {
margin-left: 15px;
margin-top: 0;
left: -10px;
}
-.enum > .collapsed, .struct > .collapsed {
- margin-bottom: 25px;
-}
-
#main > .variant, #main > .structfield {
display: block;
}
-.attributes {
- display: block;
- margin-top: 0px !important;
- margin-right: 0px;
- margin-bottom: 0px !important;
- margin-left: 30px;
-}
-.toggle-attributes.collapsed {
- margin-bottom: 0;
-}
-.impl-items > .toggle-attributes {
- margin-left: 20px;
-}
-.impl-items .attributes {
- font-weight: 500;
-}
:target > code {
opacity: 1;
padding: 0;
}
- .content .in-band {
- width: 100%;
- }
-
.content h4 > .out-of-band {
position: inherit;
}
left: -10px;
}
+ .item-list > details.rustdoc-toggle > summary:not(.hideme)::before {
+ left: -10px;
+ }
+
#all-types {
margin: 10px;
}
top: 2px;
}
-/* This part is to fix the "Expand attributes" part in the type declaration. */
-.type-decl > pre > .toggle-wrapper.toggle-attributes.top-attr {
- margin-left: 0 !important;
+/* The hideme class is used on summary tags that contain a span with
+ placeholder text shown only when the toggle is closed. For instance,
+ "Expand description" or "Show methods". */
+details.rustdoc-toggle > summary.hideme {
+ cursor: pointer;
+}
+
+details.rustdoc-toggle > summary::-webkit-details-marker {
+ display: none;
+}
+
+details.rustdoc-toggle > summary.hideme > span {
+ margin-left: 9px;
+}
+
+details.rustdoc-toggle > summary::before {
+ content: "[+]";
+ font-weight: 300;
+ font-size: 0.8em;
+ letter-spacing: 1px;
+ cursor: pointer;
+}
+
+details.rustdoc-toggle > summary.hideme::before {
+ position: relative;
+}
+
+details.rustdoc-toggle > summary:not(.hideme)::before {
+ position: absolute;
+ left: -23px;
+}
+
+/* When a "hideme" summary is open and the "Expand description" or "Show
+ methods" text is hidden, we want the [-] toggle that remains to not
+ affect the layout of the items to its right. To do that, we use
+ absolute positioning. Note that we also set position: relative
+ on the parent <details> to make this work properly. */
+details.rustdoc-toggle[open] > summary.hideme {
+ position: absolute;
}
-.type-decl > pre > .docblock.attributes.top-attr {
- margin-left: 1.8em !important;
+
+details.rustdoc-toggle[open] {
+ position: relative;
}
-.type-decl > pre > .toggle-attributes {
- margin-left: 2.2em;
+
+details.rustdoc-toggle[open] > summary.hideme > span {
+ display: none;
}
-.type-decl > pre > .docblock.attributes {
- margin-left: 4em;
+
+details.rustdoc-toggle[open] > summary::before {
+ content: "[−]";
+ display: inline;
}
--- /dev/null
+(function() {
+// This mapping table should match the discriminants of
+// `rustdoc::html::item_type::ItemType` type in Rust.
+var itemTypes = ["mod",
+ "externcrate",
+ "import",
+ "struct",
+ "enum",
+ "fn",
+ "type",
+ "static",
+ "trait",
+ "impl",
+ "tymethod",
+ "method",
+ "structfield",
+ "variant",
+ "macro",
+ "primitive",
+ "associatedtype",
+ "constant",
+ "associatedconstant",
+ "union",
+ "foreigntype",
+ "keyword",
+ "existential",
+ "attr",
+ "derive",
+ "traitalias"];
+
+// used for special search precedence
+var TY_PRIMITIVE = itemTypes.indexOf("primitive");
+var TY_KEYWORD = itemTypes.indexOf("keyword");
+
+// In the search display, allows to switch between tabs.
+function printTab(nb) {
+ if (nb === 0 || nb === 1 || nb === 2) {
+ searchState.currentTab = nb;
+ }
+ var nb_copy = nb;
+ onEachLazy(document.getElementById("titles").childNodes, function(elem) {
+ if (nb_copy === 0) {
+ addClass(elem, "selected");
+ } else {
+ removeClass(elem, "selected");
+ }
+ nb_copy -= 1;
+ });
+ onEachLazy(document.getElementById("results").childNodes, function(elem) {
+ if (nb === 0) {
+ elem.style.display = "";
+ } else {
+ elem.style.display = "none";
+ }
+ nb -= 1;
+ });
+}
+
+function removeEmptyStringsFromArray(x) {
+ for (var i = 0, len = x.length; i < len; ++i) {
+ if (x[i] === "") {
+ x.splice(i, 1);
+ i -= 1;
+ }
+ }
+}
+
+/**
+ * A function to compute the Levenshtein distance between two strings
+ * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported
+ * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode
+ * This code is an unmodified version of the code written by Marco de Wit
+ * and was found at http://stackoverflow.com/a/18514751/745719
+ */
+var levenshtein_row2 = [];
+function levenshtein(s1, s2) {
+ if (s1 === s2) {
+ return 0;
+ }
+ var s1_len = s1.length, s2_len = s2.length;
+ if (s1_len && s2_len) {
+ var i1 = 0, i2 = 0, a, b, c, c2, row = levenshtein_row2;
+ while (i1 < s1_len) {
+ row[i1] = ++i1;
+ }
+ while (i2 < s2_len) {
+ c2 = s2.charCodeAt(i2);
+ a = i2;
+ ++i2;
+ b = i2;
+ for (i1 = 0; i1 < s1_len; ++i1) {
+ c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0);
+ a = row[i1];
+ b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
+ row[i1] = b;
+ }
+ }
+ return b;
+ }
+ return s1_len + s2_len;
+}
+
+window.initSearch = function(rawSearchIndex) {
+ var MAX_LEV_DISTANCE = 3;
+ var MAX_RESULTS = 200;
+ var GENERICS_DATA = 1;
+ var NAME = 0;
+ var INPUTS_DATA = 0;
+ var OUTPUT_DATA = 1;
+ var NO_TYPE_FILTER = -1;
+ var currentResults, index, searchIndex;
+ var ALIASES = {};
+ var params = searchState.getQueryStringParams();
+
+ // Populate search bar with query string search term when provided,
+ // but only if the input bar is empty. This avoid the obnoxious issue
+ // where you start trying to do a search, and the index loads, and
+ // suddenly your search is gone!
+ if (searchState.input.value === "") {
+ searchState.input.value = params.search || "";
+ }
+
+ /**
+ * Executes the query and builds an index of results
+ * @param {[Object]} query [The user query]
+ * @param {[type]} searchWords [The list of search words to query
+ * against]
+ * @param {[type]} filterCrates [Crate to search in if defined]
+ * @return {[type]} [A search index of results]
+ */
+ function execQuery(query, searchWords, filterCrates) {
+ function itemTypeFromName(typename) {
+ for (var i = 0, len = itemTypes.length; i < len; ++i) {
+ if (itemTypes[i] === typename) {
+ return i;
+ }
+ }
+ return NO_TYPE_FILTER;
+ }
+
+ var valLower = query.query.toLowerCase(),
+ val = valLower,
+ typeFilter = itemTypeFromName(query.type),
+ results = {}, results_in_args = {}, results_returned = {},
+ split = valLower.split("::");
+
+ removeEmptyStringsFromArray(split);
+
+ function transformResults(results, isType) {
+ var out = [];
+ for (var i = 0, len = results.length; i < len; ++i) {
+ if (results[i].id > -1) {
+ var obj = searchIndex[results[i].id];
+ obj.lev = results[i].lev;
+ if (isType !== true || obj.type) {
+ var res = buildHrefAndPath(obj);
+ obj.displayPath = pathSplitter(res[0]);
+ obj.fullPath = obj.displayPath + obj.name;
+ // To be sure than it some items aren't considered as duplicate.
+ obj.fullPath += "|" + obj.ty;
+ obj.href = res[1];
+ out.push(obj);
+ if (out.length >= MAX_RESULTS) {
+ break;
+ }
+ }
+ }
+ }
+ return out;
+ }
+
+ function sortResults(results, isType) {
+ var ar = [];
+ for (var entry in results) {
+ if (hasOwnProperty(results, entry)) {
+ ar.push(results[entry]);
+ }
+ }
+ results = ar;
+ var i, len, result;
+ for (i = 0, len = results.length; i < len; ++i) {
+ result = results[i];
+ result.word = searchWords[result.id];
+ result.item = searchIndex[result.id] || {};
+ }
+ // if there are no results then return to default and fail
+ if (results.length === 0) {
+ return [];
+ }
+
+ results.sort(function(aaa, bbb) {
+ var a, b;
+
+ // sort by exact match with regard to the last word (mismatch goes later)
+ a = (aaa.word !== val);
+ b = (bbb.word !== val);
+ if (a !== b) { return a - b; }
+
+ // Sort by non levenshtein results and then levenshtein results by the distance
+ // (less changes required to match means higher rankings)
+ a = (aaa.lev);
+ b = (bbb.lev);
+ if (a !== b) { return a - b; }
+
+ // sort by crate (non-current crate goes later)
+ a = (aaa.item.crate !== window.currentCrate);
+ b = (bbb.item.crate !== window.currentCrate);
+ if (a !== b) { return a - b; }
+
+ // sort by item name length (longer goes later)
+ a = aaa.word.length;
+ b = bbb.word.length;
+ if (a !== b) { return a - b; }
+
+ // sort by item name (lexicographically larger goes later)
+ a = aaa.word;
+ b = bbb.word;
+ if (a !== b) { return (a > b ? +1 : -1); }
+
+ // sort by index of keyword in item name (no literal occurrence goes later)
+ a = (aaa.index < 0);
+ b = (bbb.index < 0);
+ if (a !== b) { return a - b; }
+ // (later literal occurrence, if any, goes later)
+ a = aaa.index;
+ b = bbb.index;
+ if (a !== b) { return a - b; }
+
+ // special precedence for primitive and keyword pages
+ if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) ||
+ (aaa.item.ty === TY_KEYWORD && bbb.item.ty !== TY_PRIMITIVE)) {
+ return -1;
+ }
+ if ((bbb.item.ty === TY_PRIMITIVE && aaa.item.ty !== TY_PRIMITIVE) ||
+ (bbb.item.ty === TY_KEYWORD && aaa.item.ty !== TY_KEYWORD)) {
+ return 1;
+ }
+
+ // sort by description (no description goes later)
+ a = (aaa.item.desc === "");
+ b = (bbb.item.desc === "");
+ if (a !== b) { return a - b; }
+
+ // sort by type (later occurrence in `itemTypes` goes later)
+ a = aaa.item.ty;
+ b = bbb.item.ty;
+ if (a !== b) { return a - b; }
+
+ // sort by path (lexicographically larger goes later)
+ a = aaa.item.path;
+ b = bbb.item.path;
+ if (a !== b) { return (a > b ? +1 : -1); }
+
+ // que sera, sera
+ return 0;
+ });
+
+ for (i = 0, len = results.length; i < len; ++i) {
+ var result = results[i];
+
+ // this validation does not make sense when searching by types
+ if (result.dontValidate) {
+ continue;
+ }
+ var name = result.item.name.toLowerCase(),
+ path = result.item.path.toLowerCase(),
+ parent = result.item.parent;
+
+ if (isType !== true &&
+ validateResult(name, path, split, parent) === false)
+ {
+ result.id = -1;
+ }
+ }
+ return transformResults(results);
+ }
+
+ function extractGenerics(val) {
+ val = val.toLowerCase();
+ if (val.indexOf("<") !== -1) {
+ var values = val.substring(val.indexOf("<") + 1, val.lastIndexOf(">"));
+ return {
+ name: val.substring(0, val.indexOf("<")),
+ generics: values.split(/\s*,\s*/),
+ };
+ }
+ return {
+ name: val,
+ generics: [],
+ };
+ }
+
+ function getObjectNameFromId(id) {
+ if (typeof id === "number") {
+ return searchIndex[id].name;
+ }
+ return id;
+ }
+
+ function checkGenerics(obj, val) {
+ // The names match, but we need to be sure that all generics kinda
+ // match as well.
+ var tmp_lev, elem_name;
+ if (val.generics.length > 0) {
+ if (obj.length > GENERICS_DATA &&
+ obj[GENERICS_DATA].length >= val.generics.length) {
+ var elems = Object.create(null);
+ var elength = object[GENERICS_DATA].length;
+ for (var x = 0; x < elength; ++x) {
+ elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1;
+ }
+ var total = 0;
+ var done = 0;
+ // We need to find the type that matches the most to remove it in order
+ // to move forward.
+ var vlength = val.generics.length;
+ for (x = 0; x < vlength; ++x) {
+ var lev = MAX_LEV_DISTANCE + 1;
+ var firstGeneric = getObjectNameFromId(val.generics[x]);
+ var match = null;
+ if (elems[firstGeneric]) {
+ match = firstGeneric;
+ lev = 0;
+ } else {
+ for (elem_name in elems) {
+ tmp_lev = levenshtein(elem_name, firstGeneric);
+ if (tmp_lev < lev) {
+ lev = tmp_lev;
+ match = elem_name;
+ }
+ }
+ }
+ if (match !== null) {
+ elems[match] -= 1;
+ if (elems[match] == 0) {
+ delete elems[match];
+ }
+ total += lev;
+ done += 1;
+ } else {
+ return MAX_LEV_DISTANCE + 1;
+ }
+ }
+ return Math.ceil(total / done);
+ }
+ }
+ return MAX_LEV_DISTANCE + 1;
+ }
+
+ // Check for type name and type generics (if any).
+ function checkType(obj, val, literalSearch) {
+ var lev_distance = MAX_LEV_DISTANCE + 1;
+ var len, x, firstGeneric;
+ if (obj[NAME] === val.name) {
+ if (literalSearch === true) {
+ if (val.generics && val.generics.length !== 0) {
+ if (obj.length > GENERICS_DATA &&
+ obj[GENERICS_DATA].length >= val.generics.length) {
+ var elems = Object.create(null);
+ len = obj[GENERICS_DATA].length;
+ for (x = 0; x < len; ++x) {
+ elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1;
+ }
+
+ var allFound = true;
+ len = val.generics.length;
+ for (x = 0; x < len; ++x) {
+ firstGeneric = getObjectNameFromId(val.generics[x]);
+ if (elems[firstGeneric]) {
+ elems[firstGeneric] -= 1;
+ } else {
+ allFound = false;
+ break;
+ }
+ }
+ if (allFound === true) {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+ // If the type has generics but don't match, then it won't return at this point.
+ // Otherwise, `checkGenerics` will return 0 and it'll return.
+ if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) {
+ var tmp_lev = checkGenerics(obj, val);
+ if (tmp_lev <= MAX_LEV_DISTANCE) {
+ return tmp_lev;
+ }
+ } else {
+ return 0;
+ }
+ }
+ // Names didn't match so let's check if one of the generic types could.
+ if (literalSearch === true) {
+ if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
+ return obj[GENERICS_DATA].some(
+ function(name) {
+ return name === val.name;
+ });
+ }
+ return false;
+ }
+ lev_distance = Math.min(levenshtein(obj[NAME], val.name), lev_distance);
+ if (lev_distance <= MAX_LEV_DISTANCE) {
+ // The generics didn't match but the name kinda did so we give it
+ // a levenshtein distance value that isn't *this* good so it goes
+ // into the search results but not too high.
+ lev_distance = Math.ceil((checkGenerics(obj, val) + lev_distance) / 2);
+ } else if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
+ // We can check if the type we're looking for is inside the generics!
+ var olength = obj[GENERICS_DATA].length;
+ for (x = 0; x < olength; ++x) {
+ lev_distance = Math.min(levenshtein(obj[GENERICS_DATA][x], val.name),
+ lev_distance);
+ }
+ }
+ // Now whatever happens, the returned distance is "less good" so we should mark it
+ // as such, and so we add 1 to the distance to make it "less good".
+ return lev_distance + 1;
+ }
+
+ function findArg(obj, val, literalSearch, typeFilter) {
+ var lev_distance = MAX_LEV_DISTANCE + 1;
+
+ if (obj && obj.type && obj.type[INPUTS_DATA] && obj.type[INPUTS_DATA].length > 0) {
+ var length = obj.type[INPUTS_DATA].length;
+ for (var i = 0; i < length; i++) {
+ var tmp = obj.type[INPUTS_DATA][i];
+ if (typePassesFilter(typeFilter, tmp[1]) === false) {
+ continue;
+ }
+ tmp = checkType(tmp, val, literalSearch);
+ if (literalSearch === true) {
+ if (tmp === true) {
+ return true;
+ }
+ continue;
+ }
+ lev_distance = Math.min(tmp, lev_distance);
+ if (lev_distance === 0) {
+ return 0;
+ }
+ }
+ }
+ return literalSearch === true ? false : lev_distance;
+ }
+
+ function checkReturned(obj, val, literalSearch, typeFilter) {
+ var lev_distance = MAX_LEV_DISTANCE + 1;
+
+ if (obj && obj.type && obj.type.length > OUTPUT_DATA) {
+ var ret = obj.type[OUTPUT_DATA];
+ if (typeof ret[0] === "string") {
+ ret = [ret];
+ }
+ for (var x = 0, len = ret.length; x < len; ++x) {
+ var tmp = ret[x];
+ if (typePassesFilter(typeFilter, tmp[1]) === false) {
+ continue;
+ }
+ tmp = checkType(tmp, val, literalSearch);
+ if (literalSearch === true) {
+ if (tmp === true) {
+ return true;
+ }
+ continue;
+ }
+ lev_distance = Math.min(tmp, lev_distance);
+ if (lev_distance === 0) {
+ return 0;
+ }
+ }
+ }
+ return literalSearch === true ? false : lev_distance;
+ }
+
+ function checkPath(contains, lastElem, ty) {
+ if (contains.length === 0) {
+ return 0;
+ }
+ var ret_lev = MAX_LEV_DISTANCE + 1;
+ var path = ty.path.split("::");
+
+ if (ty.parent && ty.parent.name) {
+ path.push(ty.parent.name.toLowerCase());
+ }
+
+ var length = path.length;
+ var clength = contains.length;
+ if (clength > length) {
+ return MAX_LEV_DISTANCE + 1;
+ }
+ for (var i = 0; i < length; ++i) {
+ if (i + clength > length) {
+ break;
+ }
+ var lev_total = 0;
+ var aborted = false;
+ for (var x = 0; x < clength; ++x) {
+ var lev = levenshtein(path[i + x], contains[x]);
+ if (lev > MAX_LEV_DISTANCE) {
+ aborted = true;
+ break;
+ }
+ lev_total += lev;
+ }
+ if (aborted === false) {
+ ret_lev = Math.min(ret_lev, Math.round(lev_total / clength));
+ }
+ }
+ return ret_lev;
+ }
+
+ function typePassesFilter(filter, type) {
+ // No filter
+ if (filter <= NO_TYPE_FILTER) return true;
+
+ // Exact match
+ if (filter === type) return true;
+
+ // Match related items
+ var name = itemTypes[type];
+ switch (itemTypes[filter]) {
+ case "constant":
+ return name === "associatedconstant";
+ case "fn":
+ return name === "method" || name === "tymethod";
+ case "type":
+ return name === "primitive" || name === "associatedtype";
+ case "trait":
+ return name === "traitalias";
+ }
+
+ // No match
+ return false;
+ }
+
+ function createAliasFromItem(item) {
+ return {
+ crate: item.crate,
+ name: item.name,
+ path: item.path,
+ desc: item.desc,
+ ty: item.ty,
+ parent: item.parent,
+ type: item.type,
+ is_alias: true,
+ };
+ }
+
+ function handleAliases(ret, query, filterCrates) {
+ // We separate aliases and crate aliases because we want to have current crate
+ // aliases to be before the others in the displayed results.
+ var aliases = [];
+ var crateAliases = [];
+ if (filterCrates !== undefined) {
+ if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) {
+ var query_aliases = ALIASES[filterCrates][query.search];
+ var len = query_aliases.length;
+ for (var i = 0; i < len; ++i) {
+ aliases.push(createAliasFromItem(searchIndex[query_aliases[i]]));
+ }
+ }
+ } else {
+ Object.keys(ALIASES).forEach(function(crate) {
+ if (ALIASES[crate][query.search]) {
+ var pushTo = crate === window.currentCrate ? crateAliases : aliases;
+ var query_aliases = ALIASES[crate][query.search];
+ var len = query_aliases.length;
+ for (var i = 0; i < len; ++i) {
+ pushTo.push(createAliasFromItem(searchIndex[query_aliases[i]]));
+ }
+ }
+ });
+ }
+
+ var sortFunc = function(aaa, bbb) {
+ if (aaa.path < bbb.path) {
+ return 1;
+ } else if (aaa.path === bbb.path) {
+ return 0;
+ }
+ return -1;
+ };
+ crateAliases.sort(sortFunc);
+ aliases.sort(sortFunc);
+
+ var pushFunc = function(alias) {
+ alias.alias = query.raw;
+ var res = buildHrefAndPath(alias);
+ alias.displayPath = pathSplitter(res[0]);
+ alias.fullPath = alias.displayPath + alias.name;
+ alias.href = res[1];
+
+ ret.others.unshift(alias);
+ if (ret.others.length > MAX_RESULTS) {
+ ret.others.pop();
+ }
+ };
+ onEach(aliases, pushFunc);
+ onEach(crateAliases, pushFunc);
+ }
+
+ // quoted values mean literal search
+ var nSearchWords = searchWords.length;
+ var i, it;
+ var ty;
+ var fullId;
+ var returned;
+ var in_args;
+ var len;
+ if ((val.charAt(0) === "\"" || val.charAt(0) === "'") &&
+ val.charAt(val.length - 1) === val.charAt(0))
+ {
+ val = extractGenerics(val.substr(1, val.length - 2));
+ for (i = 0; i < nSearchWords; ++i) {
+ if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) {
+ continue;
+ }
+ in_args = findArg(searchIndex[i], val, true, typeFilter);
+ returned = checkReturned(searchIndex[i], val, true, typeFilter);
+ ty = searchIndex[i];
+ fullId = ty.id;
+
+ if (searchWords[i] === val.name
+ && typePassesFilter(typeFilter, searchIndex[i].ty)
+ && results[fullId] === undefined) {
+ results[fullId] = {
+ id: i,
+ index: -1,
+ dontValidate: true,
+ };
+ }
+ if (in_args === true && results_in_args[fullId] === undefined) {
+ results_in_args[fullId] = {
+ id: i,
+ index: -1,
+ dontValidate: true,
+ };
+ }
+ if (returned === true && results_returned[fullId] === undefined) {
+ results_returned[fullId] = {
+ id: i,
+ index: -1,
+ dontValidate: true,
+ };
+ }
+ }
+ query.inputs = [val];
+ query.output = val;
+ query.search = val;
+ // searching by type
+ } else if (val.search("->") > -1) {
+ var trimmer = function(s) { return s.trim(); };
+ var parts = val.split("->").map(trimmer);
+ var input = parts[0];
+ // sort inputs so that order does not matter
+ var inputs = input.split(",").map(trimmer).sort();
+ for (i = 0, len = inputs.length; i < len; ++i) {
+ inputs[i] = extractGenerics(inputs[i]);
+ }
+ var output = extractGenerics(parts[1]);
+
+ for (i = 0; i < nSearchWords; ++i) {
+ if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) {
+ continue;
+ }
+ var type = searchIndex[i].type;
+ ty = searchIndex[i];
+ if (!type) {
+ continue;
+ }
+ fullId = ty.id;
+
+ returned = checkReturned(ty, output, true, NO_TYPE_FILTER);
+ if (output.name === "*" || returned === true) {
+ in_args = false;
+ var is_module = false;
+
+ if (input === "*") {
+ is_module = true;
+ } else {
+ var allFound = true;
+ for (it = 0, len = inputs.length; allFound === true && it < len; it++) {
+ allFound = checkType(type, inputs[it], true);
+ }
+ in_args = allFound;
+ }
+ if (in_args === true) {
+ results_in_args[fullId] = {
+ id: i,
+ index: -1,
+ dontValidate: true,
+ };
+ }
+ if (returned === true) {
+ results_returned[fullId] = {
+ id: i,
+ index: -1,
+ dontValidate: true,
+ };
+ }
+ if (is_module === true) {
+ results[fullId] = {
+ id: i,
+ index: -1,
+ dontValidate: true,
+ };
+ }
+ }
+ }
+ query.inputs = inputs.map(function(input) {
+ return input.name;
+ });
+ query.output = output.name;
+ } else {
+ query.inputs = [val];
+ query.output = val;
+ query.search = val;
+ // gather matching search results up to a certain maximum
+ val = val.replace(/\_/g, "");
+
+ var valGenerics = extractGenerics(val);
+
+ var paths = valLower.split("::");
+ removeEmptyStringsFromArray(paths);
+ val = paths[paths.length - 1];
+ var contains = paths.slice(0, paths.length > 1 ? paths.length - 1 : 1);
+
+ var lev, j;
+ for (j = 0; j < nSearchWords; ++j) {
+ ty = searchIndex[j];
+ if (!ty || (filterCrates !== undefined && ty.crate !== filterCrates)) {
+ continue;
+ }
+ var lev_add = 0;
+ if (paths.length > 1) {
+ lev = checkPath(contains, paths[paths.length - 1], ty);
+ if (lev > MAX_LEV_DISTANCE) {
+ continue;
+ } else if (lev > 0) {
+ lev_add = lev / 10;
+ }
+ }
+
+ returned = MAX_LEV_DISTANCE + 1;
+ in_args = MAX_LEV_DISTANCE + 1;
+ var index = -1;
+ // we want lev results to go lower than others
+ lev = MAX_LEV_DISTANCE + 1;
+ fullId = ty.id;
+
+ if (searchWords[j].indexOf(split[i]) > -1 ||
+ searchWords[j].indexOf(val) > -1 ||
+ ty.normalizedName.indexOf(val) > -1)
+ {
+ // filter type: ... queries
+ if (typePassesFilter(typeFilter, ty.ty) && results[fullId] === undefined) {
+ index = ty.normalizedName.indexOf(val);
+ }
+ }
+ if ((lev = levenshtein(searchWords[j], val)) <= MAX_LEV_DISTANCE) {
+ if (typePassesFilter(typeFilter, ty.ty) === false) {
+ lev = MAX_LEV_DISTANCE + 1;
+ } else {
+ lev += 1;
+ }
+ }
+ in_args = findArg(ty, valGenerics, false, typeFilter);
+ returned = checkReturned(ty, valGenerics, false, typeFilter);
+
+ lev += lev_add;
+ if (lev > 0 && val.length > 3 && searchWords[j].indexOf(val) > -1) {
+ if (val.length < 6) {
+ lev -= 1;
+ } else {
+ lev = 0;
+ }
+ }
+ if (in_args <= MAX_LEV_DISTANCE) {
+ if (results_in_args[fullId] === undefined) {
+ results_in_args[fullId] = {
+ id: j,
+ index: index,
+ lev: in_args,
+ };
+ }
+ results_in_args[fullId].lev =
+ Math.min(results_in_args[fullId].lev, in_args);
+ }
+ if (returned <= MAX_LEV_DISTANCE) {
+ if (results_returned[fullId] === undefined) {
+ results_returned[fullId] = {
+ id: j,
+ index: index,
+ lev: returned,
+ };
+ }
+ results_returned[fullId].lev =
+ Math.min(results_returned[fullId].lev, returned);
+ }
+ if (index !== -1 || lev <= MAX_LEV_DISTANCE) {
+ if (index !== -1 && paths.length < 2) {
+ lev = 0;
+ }
+ if (results[fullId] === undefined) {
+ results[fullId] = {
+ id: j,
+ index: index,
+ lev: lev,
+ };
+ }
+ results[fullId].lev = Math.min(results[fullId].lev, lev);
+ }
+ }
+ }
+
+ var ret = {
+ "in_args": sortResults(results_in_args, true),
+ "returned": sortResults(results_returned, true),
+ "others": sortResults(results),
+ };
+ handleAliases(ret, query, filterCrates);
+ return ret;
+ }
+
+ /**
+ * Validate performs the following boolean logic. For example:
+ * "File::open" will give IF A PARENT EXISTS => ("file" && "open")
+ * exists in (name || path || parent) OR => ("file" && "open") exists in
+ * (name || path )
+ *
+ * This could be written functionally, but I wanted to minimise
+ * functions on stack.
+ *
+ * @param {[string]} name [The name of the result]
+ * @param {[string]} path [The path of the result]
+ * @param {[string]} keys [The keys to be used (["file", "open"])]
+ * @param {[object]} parent [The parent of the result]
+ * @return {boolean} [Whether the result is valid or not]
+ */
+ function validateResult(name, path, keys, parent) {
+ for (var i = 0, len = keys.length; i < len; ++i) {
+ // each check is for validation so we negate the conditions and invalidate
+ if (!(
+ // check for an exact name match
+ name.indexOf(keys[i]) > -1 ||
+ // then an exact path match
+ path.indexOf(keys[i]) > -1 ||
+ // next if there is a parent, check for exact parent match
+ (parent !== undefined && parent.name !== undefined &&
+ parent.name.toLowerCase().indexOf(keys[i]) > -1) ||
+ // lastly check to see if the name was a levenshtein match
+ levenshtein(name, keys[i]) <= MAX_LEV_DISTANCE)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ function getQuery(raw) {
+ var matches, type, query;
+ query = raw;
+
+ matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i);
+ if (matches) {
+ type = matches[1].replace(/^const$/, "constant");
+ query = query.substring(matches[0].length);
+ }
+
+ return {
+ raw: raw,
+ query: query,
+ type: type,
+ id: query + type
+ };
+ }
+
+ function initSearchNav() {
+ var hoverTimeout;
+
+ var click_func = function(e) {
+ var el = e.target;
+ // to retrieve the real "owner" of the event.
+ while (el.tagName !== "TR") {
+ el = el.parentNode;
+ }
+ var dst = e.target.getElementsByTagName("a");
+ if (dst.length < 1) {
+ return;
+ }
+ dst = dst[0];
+ if (window.location.pathname === dst.pathname) {
+ searchState.hideResults();
+ document.location.href = dst.href;
+ }
+ };
+ var mouseover_func = function(e) {
+ if (searchState.mouseMovedAfterSearch) {
+ var el = e.target;
+ // to retrieve the real "owner" of the event.
+ while (el.tagName !== "TR") {
+ el = el.parentNode;
+ }
+ clearTimeout(hoverTimeout);
+ hoverTimeout = setTimeout(function() {
+ onEachLazy(document.getElementsByClassName("search-results"), function(e) {
+ onEachLazy(e.getElementsByClassName("result"), function(i_e) {
+ removeClass(i_e, "highlighted");
+ });
+ });
+ addClass(el, "highlighted");
+ }, 20);
+ }
+ };
+ onEachLazy(document.getElementsByClassName("search-results"), function(e) {
+ onEachLazy(e.getElementsByClassName("result"), function(i_e) {
+ i_e.onclick = click_func;
+ i_e.onmouseover = mouseover_func;
+ });
+ });
+
+ searchState.input.onkeydown = function(e) {
+ // "actives" references the currently highlighted item in each search tab.
+ // Each array in "actives" represents a tab.
+ var actives = [[], [], []];
+ // "current" is used to know which tab we're looking into.
+ var current = 0;
+ onEachLazy(document.getElementById("results").childNodes, function(e) {
+ onEachLazy(e.getElementsByClassName("highlighted"), function(h_e) {
+ actives[current].push(h_e);
+ });
+ current += 1;
+ });
+ var SHIFT = 16;
+ var CTRL = 17;
+ var ALT = 18;
+
+ var currentTab = searchState.currentTab;
+ if (e.which === 38) { // up
+ if (e.ctrlKey) { // Going through result tabs.
+ printTab(currentTab > 0 ? currentTab - 1 : 2);
+ } else {
+ if (!actives[currentTab].length ||
+ !actives[currentTab][0].previousElementSibling) {
+ return;
+ }
+ addClass(actives[currentTab][0].previousElementSibling, "highlighted");
+ removeClass(actives[currentTab][0], "highlighted");
+ }
+ e.preventDefault();
+ } else if (e.which === 40) { // down
+ if (e.ctrlKey) { // Going through result tabs.
+ printTab(currentTab > 1 ? 0 : currentTab + 1);
+ } else if (!actives[currentTab].length) {
+ var results = document.getElementById("results").childNodes;
+ if (results.length > 0) {
+ var res = results[currentTab].getElementsByClassName("result");
+ if (res.length > 0) {
+ addClass(res[0], "highlighted");
+ }
+ }
+ } else if (actives[currentTab][0].nextElementSibling) {
+ addClass(actives[currentTab][0].nextElementSibling, "highlighted");
+ removeClass(actives[currentTab][0], "highlighted");
+ }
+ e.preventDefault();
+ } else if (e.which === 13) { // return
+ if (actives[currentTab].length) {
+ var elem = actives[currentTab][0].getElementsByTagName("a")[0];
+ document.location.href = elem.href;
+ }
+ } else if ([SHIFT, CTRL, ALT].indexOf(e.which) !== -1) {
+ // Does nothing, it's just to avoid losing "focus" on the highlighted element.
+ } else if (actives[currentTab].length > 0) {
+ removeClass(actives[currentTab][0], "highlighted");
+ }
+ };
+ }
+
+ function buildHrefAndPath(item) {
+ var displayPath;
+ var href;
+ var type = itemTypes[item.ty];
+ var name = item.name;
+ var path = item.path;
+
+ if (type === "mod") {
+ displayPath = path + "::";
+ href = window.rootPath + path.replace(/::/g, "/") + "/" +
+ name + "/index.html";
+ } else if (type === "primitive" || type === "keyword") {
+ displayPath = "";
+ href = window.rootPath + path.replace(/::/g, "/") +
+ "/" + type + "." + name + ".html";
+ } else if (type === "externcrate") {
+ displayPath = "";
+ href = window.rootPath + name + "/index.html";
+ } else if (item.parent !== undefined) {
+ var myparent = item.parent;
+ var anchor = "#" + type + "." + name;
+ var parentType = itemTypes[myparent.ty];
+ var pageType = parentType;
+ var pageName = myparent.name;
+
+ if (parentType === "primitive") {
+ displayPath = myparent.name + "::";
+ } else if (type === "structfield" && parentType === "variant") {
+ // Structfields belonging to variants are special: the
+ // final path element is the enum name.
+ var enumNameIdx = item.path.lastIndexOf("::");
+ var enumName = item.path.substr(enumNameIdx + 2);
+ path = item.path.substr(0, enumNameIdx);
+ displayPath = path + "::" + enumName + "::" + myparent.name + "::";
+ anchor = "#variant." + myparent.name + ".field." + name;
+ pageType = "enum";
+ pageName = enumName;
+ } else {
+ displayPath = path + "::" + myparent.name + "::";
+ }
+ href = window.rootPath + path.replace(/::/g, "/") +
+ "/" + pageType +
+ "." + pageName +
+ ".html" + anchor;
+ } else {
+ displayPath = item.path + "::";
+ href = window.rootPath + item.path.replace(/::/g, "/") +
+ "/" + type + "." + name + ".html";
+ }
+ return [displayPath, href];
+ }
+
+ function escape(content) {
+ var h1 = document.createElement("h1");
+ h1.textContent = content;
+ return h1.innerHTML;
+ }
+
+ function pathSplitter(path) {
+ var tmp = "<span>" + path.replace(/::/g, "::</span><span>");
+ if (tmp.endsWith("<span>")) {
+ return tmp.slice(0, tmp.length - 6);
+ }
+ return tmp;
+ }
+
+ function addTab(array, query, display) {
+ var extraStyle = "";
+ if (display === false) {
+ extraStyle = " style=\"display: none;\"";
+ }
+
+ var output = "";
+ var duplicates = {};
+ var length = 0;
+ if (array.length > 0) {
+ output = "<table class=\"search-results\"" + extraStyle + ">";
+
+ array.forEach(function(item) {
+ var name, type;
+
+ name = item.name;
+ type = itemTypes[item.ty];
+
+ if (item.is_alias !== true) {
+ if (duplicates[item.fullPath]) {
+ return;
+ }
+ duplicates[item.fullPath] = true;
+ }
+ length += 1;
+
+ output += "<tr class=\"" + type + " result\"><td>" +
+ "<a href=\"" + item.href + "\">" +
+ (item.is_alias === true ?
+ ("<span class=\"alias\"><b>" + item.alias + " </b></span><span " +
+ "class=\"grey\"><i> - see </i></span>") : "") +
+ item.displayPath + "<span class=\"" + type + "\">" +
+ name + "</span></a></td><td>" +
+ "<a href=\"" + item.href + "\">" +
+ "<span class=\"desc\">" + item.desc +
+ " </span></a></td></tr>";
+ });
+ output += "</table>";
+ } else {
+ output = "<div class=\"search-failed\"" + extraStyle + ">No results :(<br/>" +
+ "Try on <a href=\"https://duckduckgo.com/?q=" +
+ encodeURIComponent("rust " + query.query) +
+ "\">DuckDuckGo</a>?<br/><br/>" +
+ "Or try looking in one of these:<ul><li>The <a " +
+ "href=\"https://doc.rust-lang.org/reference/index.html\">Rust Reference</a> " +
+ " for technical details about the language.</li><li><a " +
+ "href=\"https://doc.rust-lang.org/rust-by-example/index.html\">Rust By " +
+ "Example</a> for expository code examples.</a></li><li>The <a " +
+ "href=\"https://doc.rust-lang.org/book/index.html\">Rust Book</a> for " +
+ "introductions to language features and the language itself.</li><li><a " +
+ "href=\"https://docs.rs\">Docs.rs</a> for documentation of crates released on" +
+ " <a href=\"https://crates.io/\">crates.io</a>.</li></ul></div>";
+ }
+ return [output, length];
+ }
+
+ function makeTabHeader(tabNb, text, nbElems) {
+ if (searchState.currentTab === tabNb) {
+ return "<button class=\"selected\">" + text +
+ " <div class=\"count\">(" + nbElems + ")</div></button>";
+ }
+ return "<button>" + text + " <div class=\"count\">(" + nbElems + ")</div></button>";
+ }
+
+ function showResults(results) {
+ var search = searchState.outputElement();
+ if (results.others.length === 1
+ && getSettingValue("go-to-only-result") === "true"
+ // By default, the search DOM element is "empty" (meaning it has no children not
+ // text content). Once a search has been run, it won't be empty, even if you press
+ // ESC or empty the search input (which also "cancels" the search).
+ && (!search.firstChild || search.firstChild.innerText !== searchState.loadingText))
+ {
+ var elem = document.createElement("a");
+ elem.href = results.others[0].href;
+ elem.style.display = "none";
+ // For firefox, we need the element to be in the DOM so it can be clicked.
+ document.body.appendChild(elem);
+ elem.click();
+ return;
+ }
+ var query = getQuery(searchState.input.value);
+
+ currentResults = query.id;
+
+ var ret_others = addTab(results.others, query);
+ var ret_in_args = addTab(results.in_args, query, false);
+ var ret_returned = addTab(results.returned, query, false);
+
+ // Navigate to the relevant tab if the current tab is empty, like in case users search
+ // for "-> String". If they had selected another tab previously, they have to click on
+ // it again.
+ var currentTab = searchState.currentTab;
+ if ((currentTab === 0 && ret_others[1] === 0) ||
+ (currentTab === 1 && ret_in_args[1] === 0) ||
+ (currentTab === 2 && ret_returned[1] === 0)) {
+ if (ret_others[1] !== 0) {
+ currentTab = 0;
+ } else if (ret_in_args[1] !== 0) {
+ currentTab = 1;
+ } else if (ret_returned[1] !== 0) {
+ currentTab = 2;
+ }
+ }
+
+ var output = "<h1>Results for " + escape(query.query) +
+ (query.type ? " (type: " + escape(query.type) + ")" : "") + "</h1>" +
+ "<div id=\"titles\">" +
+ makeTabHeader(0, "In Names", ret_others[1]) +
+ makeTabHeader(1, "In Parameters", ret_in_args[1]) +
+ makeTabHeader(2, "In Return Types", ret_returned[1]) +
+ "</div><div id=\"results\">" +
+ ret_others[0] + ret_in_args[0] + ret_returned[0] + "</div>";
+
+ search.innerHTML = output;
+ searchState.showResults(search);
+ initSearchNav();
+ var elems = document.getElementById("titles").childNodes;
+ elems[0].onclick = function() { printTab(0); };
+ elems[1].onclick = function() { printTab(1); };
+ elems[2].onclick = function() { printTab(2); };
+ printTab(currentTab);
+ }
+
+ function execSearch(query, searchWords, filterCrates) {
+ function getSmallest(arrays, positions, notDuplicates) {
+ var start = null;
+
+ for (var it = 0, len = positions.length; it < len; ++it) {
+ if (arrays[it].length > positions[it] &&
+ (start === null || start > arrays[it][positions[it]].lev) &&
+ !notDuplicates[arrays[it][positions[it]].fullPath]) {
+ start = arrays[it][positions[it]].lev;
+ }
+ }
+ return start;
+ }
+
+ function mergeArrays(arrays) {
+ var ret = [];
+ var positions = [];
+ var notDuplicates = {};
+
+ for (var x = 0, arrays_len = arrays.length; x < arrays_len; ++x) {
+ positions.push(0);
+ }
+ while (ret.length < MAX_RESULTS) {
+ var smallest = getSmallest(arrays, positions, notDuplicates);
+
+ if (smallest === null) {
+ break;
+ }
+ for (x = 0; x < arrays_len && ret.length < MAX_RESULTS; ++x) {
+ if (arrays[x].length > positions[x] &&
+ arrays[x][positions[x]].lev === smallest &&
+ !notDuplicates[arrays[x][positions[x]].fullPath]) {
+ ret.push(arrays[x][positions[x]]);
+ notDuplicates[arrays[x][positions[x]].fullPath] = true;
+ positions[x] += 1;
+ }
+ }
+ }
+ return ret;
+ }
+
+ var queries = query.raw.split(",");
+ var results = {
+ "in_args": [],
+ "returned": [],
+ "others": [],
+ };
+
+ for (var i = 0, len = queries.length; i < len; ++i) {
+ query = queries[i].trim();
+ if (query.length !== 0) {
+ var tmp = execQuery(getQuery(query), searchWords, filterCrates);
+
+ results.in_args.push(tmp.in_args);
+ results.returned.push(tmp.returned);
+ results.others.push(tmp.others);
+ }
+ }
+ if (queries.length > 1) {
+ return {
+ "in_args": mergeArrays(results.in_args),
+ "returned": mergeArrays(results.returned),
+ "others": mergeArrays(results.others),
+ };
+ }
+ return {
+ "in_args": results.in_args[0],
+ "returned": results.returned[0],
+ "others": results.others[0],
+ };
+ }
+
+ function getFilterCrates() {
+ var elem = document.getElementById("crate-search");
+
+ if (elem && elem.value !== "All crates" && hasOwnProperty(rawSearchIndex, elem.value)) {
+ return elem.value;
+ }
+ return undefined;
+ }
+
+ function search(e, forced) {
+ var params = searchState.getQueryStringParams();
+ var query = getQuery(searchState.input.value.trim());
+
+ if (e) {
+ e.preventDefault();
+ }
+
+ if (query.query.length === 0) {
+ return;
+ }
+ if (forced !== true && query.id === currentResults) {
+ if (query.query.length > 0) {
+ searchState.putBackSearch(searchState.input);
+ }
+ return;
+ }
+
+ // Update document title to maintain a meaningful browser history
+ searchState.title = "Results for " + query.query + " - Rust";
+
+ // Because searching is incremental by character, only the most
+ // recent search query is added to the browser history.
+ if (searchState.browserSupportsHistoryApi()) {
+ var newURL = getNakedUrl() + "?search=" + encodeURIComponent(query.raw) +
+ window.location.hash;
+ if (!history.state && !params.search) {
+ history.pushState(query, "", newURL);
+ } else {
+ history.replaceState(query, "", newURL);
+ }
+ }
+
+ var filterCrates = getFilterCrates();
+ showResults(execSearch(query, index, filterCrates));
+ }
+
+ function buildIndex(rawSearchIndex) {
+ searchIndex = [];
+ var searchWords = [];
+ var i, word;
+ var currentIndex = 0;
+ var id = 0;
+
+ for (var crate in rawSearchIndex) {
+ if (!hasOwnProperty(rawSearchIndex, crate)) { continue; }
+
+ var crateSize = 0;
+
+ searchWords.push(crate);
+ var normalizedName = crate.indexOf("_") === -1
+ ? crate
+ : crate.replace(/_/g, "");
+ // This object should have exactly the same set of fields as the "row"
+ // object defined below. Your JavaScript runtime will thank you.
+ // https://mathiasbynens.be/notes/shapes-ics
+ var crateRow = {
+ crate: crate,
+ ty: 1, // == ExternCrate
+ name: crate,
+ path: "",
+ desc: rawSearchIndex[crate].doc,
+ parent: undefined,
+ type: null,
+ id: id,
+ normalizedName: normalizedName,
+ };
+ id += 1;
+ searchIndex.push(crateRow);
+ currentIndex += 1;
+
+ // an array of (Number) item types
+ var itemTypes = rawSearchIndex[crate].t;
+ // an array of (String) item names
+ var itemNames = rawSearchIndex[crate].n;
+ // an array of (String) full paths (or empty string for previous path)
+ var itemPaths = rawSearchIndex[crate].q;
+ // an array of (String) descriptions
+ var itemDescs = rawSearchIndex[crate].d;
+ // an array of (Number) the parent path index + 1 to `paths`, or 0 if none
+ var itemParentIdxs = rawSearchIndex[crate].i;
+ // an array of (Object | null) the type of the function, if any
+ var itemFunctionSearchTypes = rawSearchIndex[crate].f;
+ // an array of [(Number) item type,
+ // (String) name]
+ var paths = rawSearchIndex[crate].p;
+ // a array of [(String) alias name
+ // [Number] index to items]
+ var aliases = rawSearchIndex[crate].a;
+
+ // convert `rawPaths` entries into object form
+ var len = paths.length;
+ for (i = 0; i < len; ++i) {
+ paths[i] = {ty: paths[i][0], name: paths[i][1]};
+ }
+
+ // convert `item*` into an object form, and construct word indices.
+ //
+ // before any analysis is performed lets gather the search terms to
+ // search against apart from the rest of the data. This is a quick
+ // operation that is cached for the life of the page state so that
+ // all other search operations have access to this cached data for
+ // faster analysis operations
+ len = itemTypes.length;
+ var lastPath = "";
+ for (i = 0; i < len; ++i) {
+ // This object should have exactly the same set of fields as the "crateRow"
+ // object defined above.
+ if (typeof itemNames[i] === "string") {
+ word = itemNames[i].toLowerCase();
+ searchWords.push(word);
+ } else {
+ word = "";
+ searchWords.push("");
+ }
+ var normalizedName = word.indexOf("_") === -1
+ ? word
+ : word.replace(/_/g, "");
+ var row = {
+ crate: crate,
+ ty: itemTypes[i],
+ name: itemNames[i],
+ path: itemPaths[i] ? itemPaths[i] : lastPath,
+ desc: itemDescs[i],
+ parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
+ type: itemFunctionSearchTypes[i],
+ id: id,
+ normalizedName: normalizedName,
+ };
+ id += 1;
+ searchIndex.push(row);
+ lastPath = row.path;
+ crateSize += 1;
+ }
+
+ if (aliases) {
+ ALIASES[crate] = {};
+ var j, local_aliases;
+ for (var alias_name in aliases) {
+ if (!aliases.hasOwnProperty(alias_name)) { continue; }
+
+ if (!ALIASES[crate].hasOwnProperty(alias_name)) {
+ ALIASES[crate][alias_name] = [];
+ }
+ local_aliases = aliases[alias_name];
+ for (j = 0, len = local_aliases.length; j < len; ++j) {
+ ALIASES[crate][alias_name].push(local_aliases[j] + currentIndex);
+ }
+ }
+ }
+ currentIndex += crateSize;
+ }
+ return searchWords;
+ }
+
+ function registerSearchEvents() {
+ var searchAfter500ms = function() {
+ searchState.clearInputTimeout();
+ if (searchState.input.value.length === 0) {
+ if (searchState.browserSupportsHistoryApi()) {
+ history.replaceState("", window.currentCrate + " - Rust",
+ getNakedUrl() + window.location.hash);
+ }
+ searchState.hideResults();
+ } else {
+ searchState.timeout = setTimeout(search, 500);
+ }
+ };
+ searchState.input.onkeyup = searchAfter500ms;
+ searchState.input.oninput = searchAfter500ms;
+ document.getElementsByClassName("search-form")[0].onsubmit = function(e) {
+ e.preventDefault();
+ searchState.clearInputTimeout();
+ search();
+ };
+ searchState.input.onchange = function(e) {
+ if (e.target !== document.activeElement) {
+ // To prevent doing anything when it's from a blur event.
+ return;
+ }
+ // Do NOT e.preventDefault() here. It will prevent pasting.
+ searchState.clearInputTimeout();
+ // zero-timeout necessary here because at the time of event handler execution the
+ // pasted content is not in the input field yet. Shouldn’t make any difference for
+ // change, though.
+ setTimeout(search, 0);
+ };
+ searchState.input.onpaste = searchState.input.onchange;
+
+ var selectCrate = document.getElementById("crate-search");
+ if (selectCrate) {
+ selectCrate.onchange = function() {
+ updateLocalStorage("rustdoc-saved-filter-crate", selectCrate.value);
+ search(undefined, true);
+ };
+ }
+
+ // Push and pop states are used to add search results to the browser
+ // history.
+ if (searchState.browserSupportsHistoryApi()) {
+ // Store the previous <title> so we can revert back to it later.
+ var previousTitle = document.title;
+
+ window.addEventListener("popstate", function(e) {
+ var params = searchState.getQueryStringParams();
+ // Revert to the previous title manually since the History
+ // API ignores the title parameter.
+ document.title = previousTitle;
+ // When browsing forward to search results the previous
+ // search will be repeated, so the currentResults are
+ // cleared to ensure the search is successful.
+ currentResults = null;
+ // Synchronize search bar with query string state and
+ // perform the search. This will empty the bar if there's
+ // nothing there, which lets you really go back to a
+ // previous state with nothing in the bar.
+ if (params.search && params.search.length > 0) {
+ searchState.input.value = params.search;
+ // Some browsers fire "onpopstate" for every page load
+ // (Chrome), while others fire the event only when actually
+ // popping a state (Firefox), which is why search() is
+ // called both here and at the end of the startSearch()
+ // function.
+ search(e);
+ } else {
+ searchState.input.value = "";
+ // When browsing back from search results the main page
+ // visibility must be reset.
+ searchState.hideResults();
+ }
+ });
+ }
+
+ // This is required in firefox to avoid this problem: Navigating to a search result
+ // with the keyboard, hitting enter, and then hitting back would take you back to
+ // the doc page, rather than the search that should overlay it.
+ // This was an interaction between the back-forward cache and our handlers
+ // that try to sync state between the URL and the search input. To work around it,
+ // do a small amount of re-init on page show.
+ window.onpageshow = function(){
+ var qSearch = searchState.getQueryStringParams().search;
+ if (searchState.input.value === "" && qSearch) {
+ searchState.input.value = qSearch;
+ }
+ search();
+ };
+ }
+
+ index = buildIndex(rawSearchIndex);
+ registerSearchEvents();
+ // If there's a search term in the URL, execute the search now.
+ if (searchState.getQueryStringParams().search) {
+ search();
+ }
+};
+
+if (window.searchIndex !== undefined) {
+ initSearch(window.searchIndex);
+}
+
+})();
// From rust:
/* global resourcesSuffix */
-
var darkThemes = ["dark", "ayu"];
window.currentTheme = document.getElementById("themeStyle");
window.mainTheme = document.getElementById("mainThemeStyle");
color: #39AFD7;
}
-.collapse-toggle {
+.collapse-toggle,
+details.rustdoc-toggle > summary.hideme > span,
+details.rustdoc-toggle > summary::before {
color: #999;
}
color: #929292;
}
-.module-item .stab {
+.module-item .stab,
+.import-item .stab {
color: #000;
}
color: #c5c5c5;
}
-.toggle-label {
+.toggle-label,
+.code-attribute {
color: #999;
}
color: #dedede;
}
-.collapse-toggle {
+.collapse-toggle,
+details.rustdoc-toggle > summary.hideme > span,
+details.rustdoc-toggle > summary::before {
color: #999;
}
box-shadow: 0 0 8px 4px #078dd8;
}
-.module-item .stab {
+.module-item .stab,
+.import-item .stab {
color: #ddd;
}
background-color: #4e8bca;
}
-.toggle-label {
+.toggle-label,
+.code-attribute {
color: #999;
}
color: #f5f5f5;
}
-.collapse-toggle {
+.collapse-toggle,
+details.rustdoc-toggle > summary.hideme > span,
+details.rustdoc-toggle > summary::before {
color: #999;
}
box-shadow: 0 0 8px #078dd8;
}
-.module-item .stab {
+.module-item .stab,
+.import-item .stab {
color: #000;
}
background-color: #4e8bca;
}
-.toggle-label {
+.toggle-label,
+.code-attribute {
color: #999;
}
/// including search behavior and docblock folding, among others.
crate static MAIN_JS: &str = include_str!("static/main.js");
+/// The file contents of `search.js`, which contains the search behavior.
+crate static SEARCH_JS: &str = include_str!("static/search.js");
+
/// The file contents of `settings.js`, which contains the JavaScript used to handle the settings
/// page.
crate static SETTINGS_JS: &str = include_str!("static/settings.js");
--- /dev/null
+use crate::html::format::href_relative_parts;
+
+fn assert_relative_path(expected: &[&str], relative_to_fqp: &[&str], fqp: &[&str]) {
+ let relative_to_fqp: Vec<String> = relative_to_fqp.iter().copied().map(String::from).collect();
+ let fqp: Vec<String> = fqp.iter().copied().map(String::from).collect();
+ assert_eq!(expected, href_relative_parts(&fqp, &relative_to_fqp));
+}
+
+#[test]
+fn href_relative_parts_basic() {
+ let relative_to_fqp = &["std", "vec"];
+ let fqp = &["std", "iter"];
+ assert_relative_path(&["..", "iter"], relative_to_fqp, fqp);
+}
+#[test]
+fn href_relative_parts_parent_module() {
+ let relative_to_fqp = &["std", "vec"];
+ let fqp = &["std"];
+ assert_relative_path(&[".."], relative_to_fqp, fqp);
+}
+#[test]
+fn href_relative_parts_different_crate() {
+ let relative_to_fqp = &["std", "vec"];
+ let fqp = &["core", "iter"];
+ assert_relative_path(&["..", "..", "core", "iter"], relative_to_fqp, fqp);
+}
+#[test]
+fn href_relative_parts_same_module() {
+ let relative_to_fqp = &["std", "vec"];
+ let fqp = &["std", "vec"];
+ assert_relative_path(&[], relative_to_fqp, fqp);
+}
+#[test]
+fn href_relative_parts_child_module() {
+ let relative_to_fqp = &["std"];
+ let fqp = &["std", "vec"];
+ assert_relative_path(&["vec"], relative_to_fqp, fqp);
+}
+#[test]
+fn href_relative_parts_root() {
+ let relative_to_fqp = &[];
+ let fqp = &["std"];
+ assert_relative_path(&["std"], relative_to_fqp, fqp);
+}
use rustc_hir::def::CtorKind;
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::{DefId, CRATE_DEF_INDEX};
-use rustc_span::symbol::Symbol;
use rustc_span::Pos;
use rustdoc_json_types::*;
did.map(|did| (link.clone(), from_def_id(did)))
})
.collect();
- let clean::Item { span, name, attrs, kind, visibility, def_id } = item;
- let inner = match *kind {
+ let docs = item.attrs.collapsed_doc_value();
+ let attrs = item
+ .attrs
+ .other_attrs
+ .iter()
+ .map(rustc_ast_pretty::pprust::attribute_to_string)
+ .collect();
+ let span = item.span(self.tcx);
+ let clean::Item { name, attrs: _, kind: _, visibility, def_id, cfg: _ } = item;
+ let inner = match *item.kind {
clean::StrippedItem(_) => return None,
- kind => from_clean_item_kind(kind, self.tcx, &name),
+ _ => from_clean_item(item, self.tcx),
};
Some(Item {
id: from_def_id(def_id),
name: name.map(|sym| sym.to_string()),
span: self.convert_span(span),
visibility: self.convert_visibility(visibility),
- docs: attrs.collapsed_doc_value(),
- attrs: attrs
- .other_attrs
- .iter()
- .map(rustc_ast_pretty::pprust::attribute_to_string)
- .collect(),
+ docs,
+ attrs,
deprecation: deprecation.map(from_deprecation),
inner,
links,
Id(format!("{}:{}", did.krate.as_u32(), u32::from(did.index)))
}
-fn from_clean_item_kind(item: clean::ItemKind, tcx: TyCtxt<'_>, name: &Option<Symbol>) -> ItemEnum {
+fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
use clean::ItemKind::*;
- match item {
- ModuleItem(m) => ItemEnum::Module(m.into_tcx(tcx)),
+ let name = item.name;
+ let is_crate = item.is_crate();
+ match *item.kind {
+ ModuleItem(m) => ItemEnum::Module(Module { is_crate, items: ids(m.items) }),
ImportItem(i) => ItemEnum::Import(i.into_tcx(tcx)),
StructItem(s) => ItemEnum::Struct(s.into_tcx(tcx)),
UnionItem(u) => ItemEnum::Union(u.into_tcx(tcx)),
}
}
-impl FromWithTcx<clean::Module> for Module {
- fn from_tcx(module: clean::Module, _tcx: TyCtxt<'_>) -> Self {
- Module { is_crate: module.is_crate, items: ids(module.items) }
- }
-}
-
impl FromWithTcx<clean::Struct> for Struct {
fn from_tcx(struct_: clean::Struct, tcx: TyCtxt<'_>) -> Self {
let clean::Struct { struct_type, generics, fields, fields_stripped } = struct_;
impl FromWithTcx<clean::FnDecl> for FnDecl {
fn from_tcx(decl: clean::FnDecl, tcx: TyCtxt<'_>) -> Self {
- let clean::FnDecl { inputs, output, c_variadic, attrs: _ } = decl;
+ let clean::FnDecl { inputs, output, c_variadic } = decl;
FnDecl {
inputs: inputs
.values
negative_polarity,
synthetic,
blanket_impl,
+ span: _span,
} = impl_;
Impl {
is_unsafe: unsafety == rustc_hir::Unsafety::Unsafe,
use rustc_data_structures::fx::FxHashMap;
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
-use rustc_span::{edition::Edition, Symbol};
use rustdoc_json_types as types;
fn init(
krate: clean::Crate,
options: RenderOptions,
- _edition: Edition,
cache: Cache,
tcx: TyCtxt<'tcx>,
) -> Result<(Self, clean::Crate), Error> {
Ok(())
}
- fn mod_item_in(&mut self, item: &clean::Item, _module_name: &str) -> Result<(), Error> {
- use clean::types::ItemKind::*;
- if let ModuleItem(m) = &*item.kind {
- for item in &m.items {
- match &*item.kind {
- // These don't have names so they don't get added to the output by default
- ImportItem(_) => self.item(item.clone()).unwrap(),
- ExternCrateItem { .. } => self.item(item.clone()).unwrap(),
- ImplItem(i) => i.items.iter().for_each(|i| self.item(i.clone()).unwrap()),
- _ => {}
- }
- }
- }
- self.item(item.clone()).unwrap();
- Ok(())
- }
-
- fn mod_item_out(&mut self, _item_name: &str) -> Result<(), Error> {
- Ok(())
+ fn mod_item_in(&mut self, _item: &clean::Item) -> Result<(), Error> {
+ unreachable!("RUN_ON_MODULE = false should never call mod_item_in")
}
- fn after_krate(
- &mut self,
- _crate_name: Symbol,
- _diag: &rustc_errors::Handler,
- ) -> Result<(), Error> {
+ fn after_krate(&mut self) -> Result<(), Error> {
debug!("Done with crate");
let mut index = (*self.index).clone().into_inner();
index.extend(self.get_trait_items());
stable("cfg", |o| o.optmulti("", "cfg", "pass a --cfg to rustc", "")),
stable("extern", |o| o.optmulti("", "extern", "pass an --extern to rustc", "NAME[=PATH]")),
unstable("extern-html-root-url", |o| {
- o.optmulti("", "extern-html-root-url", "base URL to use for dependencies", "NAME=URL")
+ o.optmulti(
+ "",
+ "extern-html-root-url",
+ "base URL to use for dependencies; for example, \
+ \"std=/doc\" links std::vec::Vec to /doc/std/vec/struct.Vec.html",
+ "NAME=URL",
+ )
}),
stable("plugin-path", |o| o.optmulti("", "plugin-path", "removed", "DIR")),
stable("C", |o| {
krate: clean::Crate,
renderopts: config::RenderOptions,
cache: formats::cache::Cache,
- diag: &rustc_errors::Handler,
- edition: rustc_span::edition::Edition,
tcx: TyCtxt<'tcx>,
) -> MainResult {
- match formats::run_format::<T>(krate, renderopts, cache, &diag, edition, tcx) {
+ match formats::run_format::<T>(krate, renderopts, cache, tcx) {
Ok(_) => Ok(()),
Err(e) => {
- let mut msg = diag.struct_err(&format!("couldn't generate documentation: {}", e.error));
+ let mut msg =
+ tcx.sess.struct_err(&format!("couldn't generate documentation: {}", e.error));
let file = e.file.display().to_string();
if file.is_empty() {
msg.emit()
// need to move these items separately because we lose them by the time the closure is called,
// but we can't create the Handler ahead of time because it's not Send
- let diag_opts = (options.error_format, options.edition, options.debugging_opts.clone());
let show_coverage = options.show_coverage;
let run_check = options.run_check;
}
info!("going to format");
- let (error_format, edition, debugging_options) = diag_opts;
- let diag = core::new_handler(error_format, None, &debugging_options);
match output_format {
config::OutputFormat::Html => sess.time("render_html", || {
- run_renderer::<html::render::Context<'_>>(
- krate,
- render_opts,
- cache,
- &diag,
- edition,
- tcx,
- )
+ run_renderer::<html::render::Context<'_>>(krate, render_opts, cache, tcx)
}),
config::OutputFormat::Json => sess.time("render_json", || {
- run_renderer::<json::JsonRenderer<'_>>(
- krate,
- render_opts,
- cache,
- &diag,
- edition,
- tcx,
- )
+ run_renderer::<json::JsonRenderer<'_>>(krate, render_opts, cache, tcx)
}),
}
})
-use super::{span_of_attrs, Pass};
+use super::Pass;
use crate::clean::*;
use crate::core::DocContext;
use crate::fold::DocFolder;
if !dox.is_empty() {
let report_diag = |cx: &DocContext<'_>, msg: &str, url: &str, range: Range<usize>| {
let sp = super::source_span_for_markdown_range(cx.tcx, &dox, &range, &item.attrs)
- .or_else(|| span_of_attrs(&item.attrs))
- .unwrap_or(item.span.inner());
+ .unwrap_or_else(|| item.attr_span(cx.tcx));
cx.tcx.struct_span_lint_hir(crate::lint::BARE_URLS, hir_id, sp, |lint| {
lint.build(msg)
.note("bare URLs are not automatically turned into clickable links")
None,
);
- let filename = i.span.filename(self.ctx.sess());
+ let filename = i.span(self.ctx.tcx).filename(self.ctx.sess());
let has_doc_example = tests.found_tests != 0;
let hir_id = self.ctx.tcx.hir().local_def_id_to_hir_id(i.def_id.expect_local());
let (level, source) = self.ctx.tcx.lint_level_at_node(MISSING_DOCS, hir_id);
use crate::core::DocContext;
use crate::fold::DocFolder;
use crate::html::markdown::{self, RustCodeBlock};
-use crate::passes::{span_of_attrs, Pass};
+use crate::passes::Pass;
crate const CHECK_CODE_BLOCK_SYNTAX: Pass = Pass {
name: "check-code-block-syntax",
// We couldn't calculate the span of the markdown block that had the error, so our
// diagnostics are going to be a bit lacking.
let mut diag = self.cx.sess().struct_span_warn(
- super::span_of_attrs(&item.attrs).unwrap_or(item.span.inner()),
+ item.attr_span(self.cx.tcx),
"doc comment contains an invalid Rust code block",
);
impl<'a, 'tcx> DocFolder for SyntaxChecker<'a, 'tcx> {
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
if let Some(dox) = &item.attrs.collapsed_doc_value() {
- let sp = span_of_attrs(&item.attrs).unwrap_or(item.span.inner());
+ let sp = item.attr_span(self.cx.tcx);
let extra = crate::html::markdown::ExtraInfo::new_did(self.cx.tcx, item.def_id, sp);
for code_block in markdown::rust_code_blocks(&dox, &extra) {
self.check_rust_syntax(&item, &dox, code_block);
use crate::lint::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS};
use crate::passes::Pass;
-use super::span_of_attrs;
-
mod early;
crate use early::IntraLinkCrateLoader;
}
});
- if item.is_mod() && item.attrs.inner_docs {
+ let inner_docs = item.inner_docs(self.cx.tcx);
+
+ if item.is_mod() && inner_docs {
self.mod_ids.push(item.def_id);
}
}
Some(if item.is_mod() {
- if !item.attrs.inner_docs {
+ if !inner_docs {
self.mod_ids.push(item.def_id);
}
};
let mut path_str = &*path_str;
+ let inner_docs = item.inner_docs(self.cx.tcx);
+
// In order to correctly resolve intra-doc links we need to
// pick a base AST node to work from. If the documentation for
// this module came from an inner comment (//!) then we anchor
// we've already pushed this node onto the resolution stack but
// for outer comments we explicitly try and resolve against the
// parent_node first.
- let base_node = if item.is_mod() && item.attrs.inner_docs {
- self.mod_ids.last().copied()
- } else {
- parent_node
- };
+ let base_node =
+ if item.is_mod() && inner_docs { self.mod_ids.last().copied() } else { parent_node };
let mut module_id = if let Some(id) = base_node {
id
&ori_link.range,
&item.attrs,
)
- .unwrap_or_else(|| span_of_attrs(&item.attrs).unwrap_or(item.span.inner()));
+ .unwrap_or_else(|| item.attr_span(self.cx.tcx));
rustc_session::parse::feature_err(
&self.cx.tcx.sess.parse_sess,
}
};
- let attrs = &item.attrs;
- let sp = span_of_attrs(attrs).unwrap_or(item.span.inner());
+ let sp = item.attr_span(tcx);
tcx.struct_span_lint_hir(lint, hir_id, sp, |lint| {
let mut diag = lint.build(msg);
- let span = super::source_span_for_markdown_range(tcx, dox, link_range, attrs);
+ let span = super::source_span_for_markdown_range(tcx, dox, link_range, &item.attrs);
if let Some(sp) = span {
diag.set_span(sp);
/// Report an anchor failure.
fn anchor_failure(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>, failure: AnchorFailure) {
- let msg = match failure {
+ let (msg, anchor_idx) = match failure {
AnchorFailure::MultipleAnchors => {
- format!("`{}` contains multiple anchors", diag_info.ori_link)
+ (format!("`{}` contains multiple anchors", diag_info.ori_link), 1)
}
- AnchorFailure::RustdocAnchorConflict(res) => format!(
- "`{}` contains an anchor, but links to {kind}s are already anchored",
- diag_info.ori_link,
- kind = res.descr(),
+ AnchorFailure::RustdocAnchorConflict(res) => (
+ format!(
+ "`{}` contains an anchor, but links to {kind}s are already anchored",
+ diag_info.ori_link,
+ kind = res.descr(),
+ ),
+ 0,
),
};
report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, &msg, &diag_info, |diag, sp| {
- if let Some(sp) = sp {
- diag.span_label(sp, "contains invalid anchor");
+ if let Some(mut sp) = sp {
+ if let Some((fragment_offset, _)) =
+ diag_info.ori_link.char_indices().filter(|(_, x)| *x == '#').nth(anchor_idx)
+ {
+ sp = sp.with_lo(sp.lo() + rustc_span::BytePos(fragment_offset as _));
+ }
+ diag.span_label(sp, "invalid anchor");
}
if let AnchorFailure::RustdocAnchorConflict(Res::Primitive(_)) = failure {
diag.note("this restriction may be lifted in a future release");
//! - MISSING_DOC_CODE_EXAMPLES: this lint is **UNSTABLE** and looks for public items missing doctests
//! - PRIVATE_DOC_TESTS: this lint is **STABLE** and looks for private items with doctests.
-use super::{span_of_attrs, Pass};
+use super::Pass;
use crate::clean;
use crate::clean::*;
use crate::core::DocContext;
if tests.found_tests == 0 && cx.tcx.sess.is_nightly_build() {
if should_have_doc_example(cx, &item) {
debug!("reporting error for {:?} (hir_id={:?})", item, hir_id);
- let sp = span_of_attrs(&item.attrs).unwrap_or(item.span.inner());
+ let sp = item.attr_span(cx.tcx);
cx.tcx.struct_span_lint_hir(
crate::lint::MISSING_DOC_CODE_EXAMPLES,
hir_id,
cx.tcx.struct_span_lint_hir(
crate::lint::PRIVATE_DOC_TESTS,
hir_id,
- span_of_attrs(&item.attrs).unwrap_or(item.span.inner()),
+ item.attr_span(cx.tcx),
|lint| lint.build("documentation test in private item").emit(),
);
}
-use super::{span_of_attrs, Pass};
+use super::Pass;
use crate::clean::*;
use crate::core::DocContext;
use crate::fold::DocFolder;
let sp = match super::source_span_for_markdown_range(tcx, &dox, range, &item.attrs)
{
Some(sp) => sp,
- None => span_of_attrs(&item.attrs).unwrap_or(item.span.inner()),
+ None => item.attr_span(tcx),
};
tcx.struct_span_lint_hir(crate::lint::INVALID_HTML_TAGS, hir_id, sp, |lint| {
lint.build(msg).emit()
fn fold_item(&mut self, mut item: Item) -> Option<Item> {
let old_parent_cfg = self.parent_cfg.clone();
- let new_cfg = match (self.parent_cfg.take(), item.attrs.cfg.take()) {
+ let new_cfg = match (self.parent_cfg.take(), item.cfg.take()) {
(None, None) => None,
(Some(rc), None) | (None, Some(rc)) => Some(rc),
(Some(mut a), Some(b)) => {
}
};
self.parent_cfg = new_cfg.clone();
- item.attrs.cfg = new_cfg;
+ item.cfg = new_cfg;
let result = self.fold_item_recur(item);
self.parent_cfg = old_parent_cfg;
use rustc_span::symbol::sym;
use std::mem;
-use crate::clean::Item;
-use crate::clean::{self, AttributesExt, NestedAttributesExt};
+use crate::clean;
+use crate::clean::{Item, NestedAttributesExt};
use crate::core::DocContext;
use crate::fold::{DocFolder, StripItem};
use crate::passes::{ImplStripper, Pass};
}
crate fn visit(mut self, krate: &'tcx hir::Crate<'_>) -> Module<'tcx> {
+ let span = krate.item.inner;
let mut top_level_module = self.visit_mod_contents(
- krate.item.inner,
- &Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Public },
+ span,
+ &Spanned { span, node: hir::VisibilityKind::Public },
hir::CRATE_HIR_ID,
&krate.item,
self.cx.tcx.crate_name,
);
- top_level_module.is_crate = true;
// Attach the crate's exported macros to the top-level module.
// In the case of macros 2.0 (`pub macro`), and for built-in `derive`s or attributes as
// well (_e.g._, `Copy`), these are wrongly bundled in there too, so we need to fix that by
fn visit_mod_contents(
&mut self,
span: Span,
- vis: &'tcx hir::Visibility<'_>,
+ vis: &hir::Visibility<'_>,
id: hir::HirId,
m: &'tcx hir::Mod<'tcx>,
name: Symbol,
om
}
- /// Tries to resolve the target of a `crate use` statement and inlines the
+ /// Tries to resolve the target of a `pub use` statement and inlines the
/// target if it is defined locally and would not be documented otherwise,
/// or when it is specifically requested with `please_inline`.
/// (the latter is the case when the import is marked `doc(inline)`)
|| use_attrs.lists(sym::doc).has_word(sym::hidden);
// For cross-crate impl inlining we need to know whether items are
- // reachable in documentation -- a previously nonreachable item can be
+ // reachable in documentation -- a previously unreachable item can be
// made reachable by cross-crate inlining which we're checking here.
// (this is done here because we need to know this upfront).
if !res_did.is_local() && !is_no_inline {
-Subproject commit 5ef9f9948fca7cb39dd6c1935ca4e819fb7a0db2
+Subproject commit 0ed6038a318e34e3d76a9e55bdebc4cfd17f902a
--- /dev/null
+// compile-flags: -O --crate-type=rlib
+#![feature(platform_intrinsics, repr_simd)]
+
+extern "platform-intrinsic" {
+ fn simd_fabs<T>(x: T) -> T;
+ fn simd_eq<T, U>(x: T, y: T) -> U;
+}
+
+#[repr(simd)]
+pub struct V([f32; 4]);
+
+#[repr(simd)]
+pub struct M([i32; 4]);
+
+#[no_mangle]
+// CHECK-LABEL: @is_infinite
+pub fn is_infinite(v: V) -> M {
+ // CHECK: fabs
+ // CHECK: cmp oeq
+ unsafe {
+ simd_eq(simd_fabs(v), V([f32::INFINITY; 4]))
+ }
+}
// CHECK-LABEL: @fabs_32x2
#[no_mangle]
pub unsafe fn fabs_32x2(a: f32x2) -> f32x2 {
- // CHECK: call fast <2 x float> @llvm.fabs.v2f32
+ // CHECK: call <2 x float> @llvm.fabs.v2f32
simd_fabs(a)
}
// CHECK-LABEL: @fabs_32x4
#[no_mangle]
pub unsafe fn fabs_32x4(a: f32x4) -> f32x4 {
- // CHECK: call fast <4 x float> @llvm.fabs.v4f32
+ // CHECK: call <4 x float> @llvm.fabs.v4f32
simd_fabs(a)
}
// CHECK-LABEL: @fabs_32x8
#[no_mangle]
pub unsafe fn fabs_32x8(a: f32x8) -> f32x8 {
- // CHECK: call fast <8 x float> @llvm.fabs.v8f32
+ // CHECK: call <8 x float> @llvm.fabs.v8f32
simd_fabs(a)
}
// CHECK-LABEL: @fabs_32x16
#[no_mangle]
pub unsafe fn fabs_32x16(a: f32x16) -> f32x16 {
- // CHECK: call fast <16 x float> @llvm.fabs.v16f32
+ // CHECK: call <16 x float> @llvm.fabs.v16f32
simd_fabs(a)
}
// CHECK-LABEL: @fabs_64x4
#[no_mangle]
pub unsafe fn fabs_64x4(a: f64x4) -> f64x4 {
- // CHECK: call fast <4 x double> @llvm.fabs.v4f64
+ // CHECK: call <4 x double> @llvm.fabs.v4f64
simd_fabs(a)
}
// CHECK-LABEL: @fabs_64x2
#[no_mangle]
pub unsafe fn fabs_64x2(a: f64x2) -> f64x2 {
- // CHECK: call fast <2 x double> @llvm.fabs.v2f64
+ // CHECK: call <2 x double> @llvm.fabs.v2f64
simd_fabs(a)
}
// CHECK-LABEL: @fabs_64x8
#[no_mangle]
pub unsafe fn fabs_64x8(a: f64x8) -> f64x8 {
- // CHECK: call fast <8 x double> @llvm.fabs.v8f64
+ // CHECK: call <8 x double> @llvm.fabs.v8f64
simd_fabs(a)
}
// CHECK-LABEL: @ceil_32x2
#[no_mangle]
pub unsafe fn ceil_32x2(a: f32x2) -> f32x2 {
- // CHECK: call fast <2 x float> @llvm.ceil.v2f32
+ // CHECK: call <2 x float> @llvm.ceil.v2f32
simd_ceil(a)
}
// CHECK-LABEL: @ceil_32x4
#[no_mangle]
pub unsafe fn ceil_32x4(a: f32x4) -> f32x4 {
- // CHECK: call fast <4 x float> @llvm.ceil.v4f32
+ // CHECK: call <4 x float> @llvm.ceil.v4f32
simd_ceil(a)
}
// CHECK-LABEL: @ceil_32x8
#[no_mangle]
pub unsafe fn ceil_32x8(a: f32x8) -> f32x8 {
- // CHECK: call fast <8 x float> @llvm.ceil.v8f32
+ // CHECK: call <8 x float> @llvm.ceil.v8f32
simd_ceil(a)
}
// CHECK-LABEL: @ceil_32x16
#[no_mangle]
pub unsafe fn ceil_32x16(a: f32x16) -> f32x16 {
- // CHECK: call fast <16 x float> @llvm.ceil.v16f32
+ // CHECK: call <16 x float> @llvm.ceil.v16f32
simd_ceil(a)
}
// CHECK-LABEL: @ceil_64x4
#[no_mangle]
pub unsafe fn ceil_64x4(a: f64x4) -> f64x4 {
- // CHECK: call fast <4 x double> @llvm.ceil.v4f64
+ // CHECK: call <4 x double> @llvm.ceil.v4f64
simd_ceil(a)
}
// CHECK-LABEL: @ceil_64x2
#[no_mangle]
pub unsafe fn ceil_64x2(a: f64x2) -> f64x2 {
- // CHECK: call fast <2 x double> @llvm.ceil.v2f64
+ // CHECK: call <2 x double> @llvm.ceil.v2f64
simd_ceil(a)
}
// CHECK-LABEL: @ceil_64x8
#[no_mangle]
pub unsafe fn ceil_64x8(a: f64x8) -> f64x8 {
- // CHECK: call fast <8 x double> @llvm.ceil.v8f64
+ // CHECK: call <8 x double> @llvm.ceil.v8f64
simd_ceil(a)
}
// CHECK-LABEL: @fcos_32x2
#[no_mangle]
pub unsafe fn fcos_32x2(a: f32x2) -> f32x2 {
- // CHECK: call fast <2 x float> @llvm.cos.v2f32
+ // CHECK: call <2 x float> @llvm.cos.v2f32
simd_fcos(a)
}
// CHECK-LABEL: @fcos_32x4
#[no_mangle]
pub unsafe fn fcos_32x4(a: f32x4) -> f32x4 {
- // CHECK: call fast <4 x float> @llvm.cos.v4f32
+ // CHECK: call <4 x float> @llvm.cos.v4f32
simd_fcos(a)
}
// CHECK-LABEL: @fcos_32x8
#[no_mangle]
pub unsafe fn fcos_32x8(a: f32x8) -> f32x8 {
- // CHECK: call fast <8 x float> @llvm.cos.v8f32
+ // CHECK: call <8 x float> @llvm.cos.v8f32
simd_fcos(a)
}
// CHECK-LABEL: @fcos_32x16
#[no_mangle]
pub unsafe fn fcos_32x16(a: f32x16) -> f32x16 {
- // CHECK: call fast <16 x float> @llvm.cos.v16f32
+ // CHECK: call <16 x float> @llvm.cos.v16f32
simd_fcos(a)
}
// CHECK-LABEL: @fcos_64x4
#[no_mangle]
pub unsafe fn fcos_64x4(a: f64x4) -> f64x4 {
- // CHECK: call fast <4 x double> @llvm.cos.v4f64
+ // CHECK: call <4 x double> @llvm.cos.v4f64
simd_fcos(a)
}
// CHECK-LABEL: @fcos_64x2
#[no_mangle]
pub unsafe fn fcos_64x2(a: f64x2) -> f64x2 {
- // CHECK: call fast <2 x double> @llvm.cos.v2f64
+ // CHECK: call <2 x double> @llvm.cos.v2f64
simd_fcos(a)
}
// CHECK-LABEL: @fcos_64x8
#[no_mangle]
pub unsafe fn fcos_64x8(a: f64x8) -> f64x8 {
- // CHECK: call fast <8 x double> @llvm.cos.v8f64
+ // CHECK: call <8 x double> @llvm.cos.v8f64
simd_fcos(a)
}
// CHECK-LABEL: @exp_32x2
#[no_mangle]
pub unsafe fn exp_32x2(a: f32x2) -> f32x2 {
- // CHECK: call fast <2 x float> @llvm.exp.v2f32
+ // CHECK: call <2 x float> @llvm.exp.v2f32
simd_fexp(a)
}
// CHECK-LABEL: @exp_32x4
#[no_mangle]
pub unsafe fn exp_32x4(a: f32x4) -> f32x4 {
- // CHECK: call fast <4 x float> @llvm.exp.v4f32
+ // CHECK: call <4 x float> @llvm.exp.v4f32
simd_fexp(a)
}
// CHECK-LABEL: @exp_32x8
#[no_mangle]
pub unsafe fn exp_32x8(a: f32x8) -> f32x8 {
- // CHECK: call fast <8 x float> @llvm.exp.v8f32
+ // CHECK: call <8 x float> @llvm.exp.v8f32
simd_fexp(a)
}
// CHECK-LABEL: @exp_32x16
#[no_mangle]
pub unsafe fn exp_32x16(a: f32x16) -> f32x16 {
- // CHECK: call fast <16 x float> @llvm.exp.v16f32
+ // CHECK: call <16 x float> @llvm.exp.v16f32
simd_fexp(a)
}
// CHECK-LABEL: @exp_64x4
#[no_mangle]
pub unsafe fn exp_64x4(a: f64x4) -> f64x4 {
- // CHECK: call fast <4 x double> @llvm.exp.v4f64
+ // CHECK: call <4 x double> @llvm.exp.v4f64
simd_fexp(a)
}
// CHECK-LABEL: @exp_64x2
#[no_mangle]
pub unsafe fn exp_64x2(a: f64x2) -> f64x2 {
- // CHECK: call fast <2 x double> @llvm.exp.v2f64
+ // CHECK: call <2 x double> @llvm.exp.v2f64
simd_fexp(a)
}
// CHECK-LABEL: @exp_64x8
#[no_mangle]
pub unsafe fn exp_64x8(a: f64x8) -> f64x8 {
- // CHECK: call fast <8 x double> @llvm.exp.v8f64
+ // CHECK: call <8 x double> @llvm.exp.v8f64
simd_fexp(a)
}
// CHECK-LABEL: @exp2_32x2
#[no_mangle]
pub unsafe fn exp2_32x2(a: f32x2) -> f32x2 {
- // CHECK: call fast <2 x float> @llvm.exp2.v2f32
+ // CHECK: call <2 x float> @llvm.exp2.v2f32
simd_fexp2(a)
}
// CHECK-LABEL: @exp2_32x4
#[no_mangle]
pub unsafe fn exp2_32x4(a: f32x4) -> f32x4 {
- // CHECK: call fast <4 x float> @llvm.exp2.v4f32
+ // CHECK: call <4 x float> @llvm.exp2.v4f32
simd_fexp2(a)
}
// CHECK-LABEL: @exp2_32x8
#[no_mangle]
pub unsafe fn exp2_32x8(a: f32x8) -> f32x8 {
- // CHECK: call fast <8 x float> @llvm.exp2.v8f32
+ // CHECK: call <8 x float> @llvm.exp2.v8f32
simd_fexp2(a)
}
// CHECK-LABEL: @exp2_32x16
#[no_mangle]
pub unsafe fn exp2_32x16(a: f32x16) -> f32x16 {
- // CHECK: call fast <16 x float> @llvm.exp2.v16f32
+ // CHECK: call <16 x float> @llvm.exp2.v16f32
simd_fexp2(a)
}
// CHECK-LABEL: @exp2_64x4
#[no_mangle]
pub unsafe fn exp2_64x4(a: f64x4) -> f64x4 {
- // CHECK: call fast <4 x double> @llvm.exp2.v4f64
+ // CHECK: call <4 x double> @llvm.exp2.v4f64
simd_fexp2(a)
}
// CHECK-LABEL: @exp2_64x2
#[no_mangle]
pub unsafe fn exp2_64x2(a: f64x2) -> f64x2 {
- // CHECK: call fast <2 x double> @llvm.exp2.v2f64
+ // CHECK: call <2 x double> @llvm.exp2.v2f64
simd_fexp2(a)
}
// CHECK-LABEL: @exp2_64x8
#[no_mangle]
pub unsafe fn exp2_64x8(a: f64x8) -> f64x8 {
- // CHECK: call fast <8 x double> @llvm.exp2.v8f64
+ // CHECK: call <8 x double> @llvm.exp2.v8f64
simd_fexp2(a)
}
// CHECK-LABEL: @floor_32x2
#[no_mangle]
pub unsafe fn floor_32x2(a: f32x2) -> f32x2 {
- // CHECK: call fast <2 x float> @llvm.floor.v2f32
+ // CHECK: call <2 x float> @llvm.floor.v2f32
simd_floor(a)
}
// CHECK-LABEL: @floor_32x4
#[no_mangle]
pub unsafe fn floor_32x4(a: f32x4) -> f32x4 {
- // CHECK: call fast <4 x float> @llvm.floor.v4f32
+ // CHECK: call <4 x float> @llvm.floor.v4f32
simd_floor(a)
}
// CHECK-LABEL: @floor_32x8
#[no_mangle]
pub unsafe fn floor_32x8(a: f32x8) -> f32x8 {
- // CHECK: call fast <8 x float> @llvm.floor.v8f32
+ // CHECK: call <8 x float> @llvm.floor.v8f32
simd_floor(a)
}
// CHECK-LABEL: @floor_32x16
#[no_mangle]
pub unsafe fn floor_32x16(a: f32x16) -> f32x16 {
- // CHECK: call fast <16 x float> @llvm.floor.v16f32
+ // CHECK: call <16 x float> @llvm.floor.v16f32
simd_floor(a)
}
// CHECK-LABEL: @floor_64x4
#[no_mangle]
pub unsafe fn floor_64x4(a: f64x4) -> f64x4 {
- // CHECK: call fast <4 x double> @llvm.floor.v4f64
+ // CHECK: call <4 x double> @llvm.floor.v4f64
simd_floor(a)
}
// CHECK-LABEL: @floor_64x2
#[no_mangle]
pub unsafe fn floor_64x2(a: f64x2) -> f64x2 {
- // CHECK: call fast <2 x double> @llvm.floor.v2f64
+ // CHECK: call <2 x double> @llvm.floor.v2f64
simd_floor(a)
}
// CHECK-LABEL: @floor_64x8
#[no_mangle]
pub unsafe fn floor_64x8(a: f64x8) -> f64x8 {
- // CHECK: call fast <8 x double> @llvm.floor.v8f64
+ // CHECK: call <8 x double> @llvm.floor.v8f64
simd_floor(a)
}
// CHECK-LABEL: @fma_32x2
#[no_mangle]
pub unsafe fn fma_32x2(a: f32x2, b: f32x2, c: f32x2) -> f32x2 {
- // CHECK: call fast <2 x float> @llvm.fma.v2f32
+ // CHECK: call <2 x float> @llvm.fma.v2f32
simd_fma(a, b, c)
}
// CHECK-LABEL: @fma_32x4
#[no_mangle]
pub unsafe fn fma_32x4(a: f32x4, b: f32x4, c: f32x4) -> f32x4 {
- // CHECK: call fast <4 x float> @llvm.fma.v4f32
+ // CHECK: call <4 x float> @llvm.fma.v4f32
simd_fma(a, b, c)
}
// CHECK-LABEL: @fma_32x8
#[no_mangle]
pub unsafe fn fma_32x8(a: f32x8, b: f32x8, c: f32x8) -> f32x8 {
- // CHECK: call fast <8 x float> @llvm.fma.v8f32
+ // CHECK: call <8 x float> @llvm.fma.v8f32
simd_fma(a, b, c)
}
// CHECK-LABEL: @fma_32x16
#[no_mangle]
pub unsafe fn fma_32x16(a: f32x16, b: f32x16, c: f32x16) -> f32x16 {
- // CHECK: call fast <16 x float> @llvm.fma.v16f32
+ // CHECK: call <16 x float> @llvm.fma.v16f32
simd_fma(a, b, c)
}
// CHECK-LABEL: @fma_64x4
#[no_mangle]
pub unsafe fn fma_64x4(a: f64x4, b: f64x4, c: f64x4) -> f64x4 {
- // CHECK: call fast <4 x double> @llvm.fma.v4f64
+ // CHECK: call <4 x double> @llvm.fma.v4f64
simd_fma(a, b, c)
}
// CHECK-LABEL: @fma_64x2
#[no_mangle]
pub unsafe fn fma_64x2(a: f64x2, b: f64x2, c: f64x2) -> f64x2 {
- // CHECK: call fast <2 x double> @llvm.fma.v2f64
+ // CHECK: call <2 x double> @llvm.fma.v2f64
simd_fma(a, b, c)
}
// CHECK-LABEL: @fma_64x8
#[no_mangle]
pub unsafe fn fma_64x8(a: f64x8, b: f64x8, c: f64x8) -> f64x8 {
- // CHECK: call fast <8 x double> @llvm.fma.v8f64
+ // CHECK: call <8 x double> @llvm.fma.v8f64
simd_fma(a, b, c)
}
// CHECK-LABEL: @fsqrt_32x2
#[no_mangle]
pub unsafe fn fsqrt_32x2(a: f32x2) -> f32x2 {
- // CHECK: call fast <2 x float> @llvm.sqrt.v2f32
+ // CHECK: call <2 x float> @llvm.sqrt.v2f32
simd_fsqrt(a)
}
// CHECK-LABEL: @fsqrt_32x4
#[no_mangle]
pub unsafe fn fsqrt_32x4(a: f32x4) -> f32x4 {
- // CHECK: call fast <4 x float> @llvm.sqrt.v4f32
+ // CHECK: call <4 x float> @llvm.sqrt.v4f32
simd_fsqrt(a)
}
// CHECK-LABEL: @fsqrt_32x8
#[no_mangle]
pub unsafe fn fsqrt_32x8(a: f32x8) -> f32x8 {
- // CHECK: call fast <8 x float> @llvm.sqrt.v8f32
+ // CHECK: call <8 x float> @llvm.sqrt.v8f32
simd_fsqrt(a)
}
// CHECK-LABEL: @fsqrt_32x16
#[no_mangle]
pub unsafe fn fsqrt_32x16(a: f32x16) -> f32x16 {
- // CHECK: call fast <16 x float> @llvm.sqrt.v16f32
+ // CHECK: call <16 x float> @llvm.sqrt.v16f32
simd_fsqrt(a)
}
// CHECK-LABEL: @fsqrt_64x4
#[no_mangle]
pub unsafe fn fsqrt_64x4(a: f64x4) -> f64x4 {
- // CHECK: call fast <4 x double> @llvm.sqrt.v4f64
+ // CHECK: call <4 x double> @llvm.sqrt.v4f64
simd_fsqrt(a)
}
// CHECK-LABEL: @fsqrt_64x2
#[no_mangle]
pub unsafe fn fsqrt_64x2(a: f64x2) -> f64x2 {
- // CHECK: call fast <2 x double> @llvm.sqrt.v2f64
+ // CHECK: call <2 x double> @llvm.sqrt.v2f64
simd_fsqrt(a)
}
// CHECK-LABEL: @fsqrt_64x8
#[no_mangle]
pub unsafe fn fsqrt_64x8(a: f64x8) -> f64x8 {
- // CHECK: call fast <8 x double> @llvm.sqrt.v8f64
+ // CHECK: call <8 x double> @llvm.sqrt.v8f64
simd_fsqrt(a)
}
// CHECK-LABEL: @log_32x2
#[no_mangle]
pub unsafe fn log_32x2(a: f32x2) -> f32x2 {
- // CHECK: call fast <2 x float> @llvm.log.v2f32
+ // CHECK: call <2 x float> @llvm.log.v2f32
simd_flog(a)
}
// CHECK-LABEL: @log_32x4
#[no_mangle]
pub unsafe fn log_32x4(a: f32x4) -> f32x4 {
- // CHECK: call fast <4 x float> @llvm.log.v4f32
+ // CHECK: call <4 x float> @llvm.log.v4f32
simd_flog(a)
}
// CHECK-LABEL: @log_32x8
#[no_mangle]
pub unsafe fn log_32x8(a: f32x8) -> f32x8 {
- // CHECK: call fast <8 x float> @llvm.log.v8f32
+ // CHECK: call <8 x float> @llvm.log.v8f32
simd_flog(a)
}
// CHECK-LABEL: @log_32x16
#[no_mangle]
pub unsafe fn log_32x16(a: f32x16) -> f32x16 {
- // CHECK: call fast <16 x float> @llvm.log.v16f32
+ // CHECK: call <16 x float> @llvm.log.v16f32
simd_flog(a)
}
// CHECK-LABEL: @log_64x4
#[no_mangle]
pub unsafe fn log_64x4(a: f64x4) -> f64x4 {
- // CHECK: call fast <4 x double> @llvm.log.v4f64
+ // CHECK: call <4 x double> @llvm.log.v4f64
simd_flog(a)
}
// CHECK-LABEL: @log_64x2
#[no_mangle]
pub unsafe fn log_64x2(a: f64x2) -> f64x2 {
- // CHECK: call fast <2 x double> @llvm.log.v2f64
+ // CHECK: call <2 x double> @llvm.log.v2f64
simd_flog(a)
}
// CHECK-LABEL: @log_64x8
#[no_mangle]
pub unsafe fn log_64x8(a: f64x8) -> f64x8 {
- // CHECK: call fast <8 x double> @llvm.log.v8f64
+ // CHECK: call <8 x double> @llvm.log.v8f64
simd_flog(a)
}
// CHECK-LABEL: @log10_32x2
#[no_mangle]
pub unsafe fn log10_32x2(a: f32x2) -> f32x2 {
- // CHECK: call fast <2 x float> @llvm.log10.v2f32
+ // CHECK: call <2 x float> @llvm.log10.v2f32
simd_flog10(a)
}
// CHECK-LABEL: @log10_32x4
#[no_mangle]
pub unsafe fn log10_32x4(a: f32x4) -> f32x4 {
- // CHECK: call fast <4 x float> @llvm.log10.v4f32
+ // CHECK: call <4 x float> @llvm.log10.v4f32
simd_flog10(a)
}
// CHECK-LABEL: @log10_32x8
#[no_mangle]
pub unsafe fn log10_32x8(a: f32x8) -> f32x8 {
- // CHECK: call fast <8 x float> @llvm.log10.v8f32
+ // CHECK: call <8 x float> @llvm.log10.v8f32
simd_flog10(a)
}
// CHECK-LABEL: @log10_32x16
#[no_mangle]
pub unsafe fn log10_32x16(a: f32x16) -> f32x16 {
- // CHECK: call fast <16 x float> @llvm.log10.v16f32
+ // CHECK: call <16 x float> @llvm.log10.v16f32
simd_flog10(a)
}
// CHECK-LABEL: @log10_64x4
#[no_mangle]
pub unsafe fn log10_64x4(a: f64x4) -> f64x4 {
- // CHECK: call fast <4 x double> @llvm.log10.v4f64
+ // CHECK: call <4 x double> @llvm.log10.v4f64
simd_flog10(a)
}
// CHECK-LABEL: @log10_64x2
#[no_mangle]
pub unsafe fn log10_64x2(a: f64x2) -> f64x2 {
- // CHECK: call fast <2 x double> @llvm.log10.v2f64
+ // CHECK: call <2 x double> @llvm.log10.v2f64
simd_flog10(a)
}
// CHECK-LABEL: @log10_64x8
#[no_mangle]
pub unsafe fn log10_64x8(a: f64x8) -> f64x8 {
- // CHECK: call fast <8 x double> @llvm.log10.v8f64
+ // CHECK: call <8 x double> @llvm.log10.v8f64
simd_flog10(a)
}
// CHECK-LABEL: @log2_32x2
#[no_mangle]
pub unsafe fn log2_32x2(a: f32x2) -> f32x2 {
- // CHECK: call fast <2 x float> @llvm.log2.v2f32
+ // CHECK: call <2 x float> @llvm.log2.v2f32
simd_flog2(a)
}
// CHECK-LABEL: @log2_32x4
#[no_mangle]
pub unsafe fn log2_32x4(a: f32x4) -> f32x4 {
- // CHECK: call fast <4 x float> @llvm.log2.v4f32
+ // CHECK: call <4 x float> @llvm.log2.v4f32
simd_flog2(a)
}
// CHECK-LABEL: @log2_32x8
#[no_mangle]
pub unsafe fn log2_32x8(a: f32x8) -> f32x8 {
- // CHECK: call fast <8 x float> @llvm.log2.v8f32
+ // CHECK: call <8 x float> @llvm.log2.v8f32
simd_flog2(a)
}
// CHECK-LABEL: @log2_32x16
#[no_mangle]
pub unsafe fn log2_32x16(a: f32x16) -> f32x16 {
- // CHECK: call fast <16 x float> @llvm.log2.v16f32
+ // CHECK: call <16 x float> @llvm.log2.v16f32
simd_flog2(a)
}
// CHECK-LABEL: @log2_64x4
#[no_mangle]
pub unsafe fn log2_64x4(a: f64x4) -> f64x4 {
- // CHECK: call fast <4 x double> @llvm.log2.v4f64
+ // CHECK: call <4 x double> @llvm.log2.v4f64
simd_flog2(a)
}
// CHECK-LABEL: @log2_64x2
#[no_mangle]
pub unsafe fn log2_64x2(a: f64x2) -> f64x2 {
- // CHECK: call fast <2 x double> @llvm.log2.v2f64
+ // CHECK: call <2 x double> @llvm.log2.v2f64
simd_flog2(a)
}
// CHECK-LABEL: @log2_64x8
#[no_mangle]
pub unsafe fn log2_64x8(a: f64x8) -> f64x8 {
- // CHECK: call fast <8 x double> @llvm.log2.v8f64
+ // CHECK: call <8 x double> @llvm.log2.v8f64
simd_flog2(a)
}
// CHECK-LABEL: @fpow_32x2
#[no_mangle]
pub unsafe fn fpow_32x2(a: f32x2, b: f32x2) -> f32x2 {
- // CHECK: call fast <2 x float> @llvm.pow.v2f32
+ // CHECK: call <2 x float> @llvm.pow.v2f32
simd_fpow(a, b)
}
// CHECK-LABEL: @fpow_32x4
#[no_mangle]
pub unsafe fn fpow_32x4(a: f32x4, b: f32x4) -> f32x4 {
- // CHECK: call fast <4 x float> @llvm.pow.v4f32
+ // CHECK: call <4 x float> @llvm.pow.v4f32
simd_fpow(a, b)
}
// CHECK-LABEL: @fpow_32x8
#[no_mangle]
pub unsafe fn fpow_32x8(a: f32x8, b: f32x8) -> f32x8 {
- // CHECK: call fast <8 x float> @llvm.pow.v8f32
+ // CHECK: call <8 x float> @llvm.pow.v8f32
simd_fpow(a, b)
}
// CHECK-LABEL: @fpow_32x16
#[no_mangle]
pub unsafe fn fpow_32x16(a: f32x16, b: f32x16) -> f32x16 {
- // CHECK: call fast <16 x float> @llvm.pow.v16f32
+ // CHECK: call <16 x float> @llvm.pow.v16f32
simd_fpow(a, b)
}
// CHECK-LABEL: @fpow_64x4
#[no_mangle]
pub unsafe fn fpow_64x4(a: f64x4, b: f64x4) -> f64x4 {
- // CHECK: call fast <4 x double> @llvm.pow.v4f64
+ // CHECK: call <4 x double> @llvm.pow.v4f64
simd_fpow(a, b)
}
// CHECK-LABEL: @fpow_64x2
#[no_mangle]
pub unsafe fn fpow_64x2(a: f64x2, b: f64x2) -> f64x2 {
- // CHECK: call fast <2 x double> @llvm.pow.v2f64
+ // CHECK: call <2 x double> @llvm.pow.v2f64
simd_fpow(a, b)
}
// CHECK-LABEL: @fpow_64x8
#[no_mangle]
pub unsafe fn fpow_64x8(a: f64x8, b: f64x8) -> f64x8 {
- // CHECK: call fast <8 x double> @llvm.pow.v8f64
+ // CHECK: call <8 x double> @llvm.pow.v8f64
simd_fpow(a, b)
}
// CHECK-LABEL: @fpowi_32x2
#[no_mangle]
pub unsafe fn fpowi_32x2(a: f32x2, b: i32) -> f32x2 {
- // CHECK: call fast <2 x float> @llvm.powi.v2f32
+ // CHECK: call <2 x float> @llvm.powi.v2f32
simd_fpowi(a, b)
}
// CHECK-LABEL: @fpowi_32x4
#[no_mangle]
pub unsafe fn fpowi_32x4(a: f32x4, b: i32) -> f32x4 {
- // CHECK: call fast <4 x float> @llvm.powi.v4f32
+ // CHECK: call <4 x float> @llvm.powi.v4f32
simd_fpowi(a, b)
}
// CHECK-LABEL: @fpowi_32x8
#[no_mangle]
pub unsafe fn fpowi_32x8(a: f32x8, b: i32) -> f32x8 {
- // CHECK: call fast <8 x float> @llvm.powi.v8f32
+ // CHECK: call <8 x float> @llvm.powi.v8f32
simd_fpowi(a, b)
}
// CHECK-LABEL: @fpowi_32x16
#[no_mangle]
pub unsafe fn fpowi_32x16(a: f32x16, b: i32) -> f32x16 {
- // CHECK: call fast <16 x float> @llvm.powi.v16f32
+ // CHECK: call <16 x float> @llvm.powi.v16f32
simd_fpowi(a, b)
}
// CHECK-LABEL: @fpowi_64x4
#[no_mangle]
pub unsafe fn fpowi_64x4(a: f64x4, b: i32) -> f64x4 {
- // CHECK: call fast <4 x double> @llvm.powi.v4f64
+ // CHECK: call <4 x double> @llvm.powi.v4f64
simd_fpowi(a, b)
}
// CHECK-LABEL: @fpowi_64x2
#[no_mangle]
pub unsafe fn fpowi_64x2(a: f64x2, b: i32) -> f64x2 {
- // CHECK: call fast <2 x double> @llvm.powi.v2f64
+ // CHECK: call <2 x double> @llvm.powi.v2f64
simd_fpowi(a, b)
}
// CHECK-LABEL: @fpowi_64x8
#[no_mangle]
pub unsafe fn fpowi_64x8(a: f64x8, b: i32) -> f64x8 {
- // CHECK: call fast <8 x double> @llvm.powi.v8f64
+ // CHECK: call <8 x double> @llvm.powi.v8f64
simd_fpowi(a, b)
}
// CHECK-LABEL: @fsin_32x2
#[no_mangle]
pub unsafe fn fsin_32x2(a: f32x2) -> f32x2 {
- // CHECK: call fast <2 x float> @llvm.sin.v2f32
+ // CHECK: call <2 x float> @llvm.sin.v2f32
simd_fsin(a)
}
// CHECK-LABEL: @fsin_32x4
#[no_mangle]
pub unsafe fn fsin_32x4(a: f32x4) -> f32x4 {
- // CHECK: call fast <4 x float> @llvm.sin.v4f32
+ // CHECK: call <4 x float> @llvm.sin.v4f32
simd_fsin(a)
}
// CHECK-LABEL: @fsin_32x8
#[no_mangle]
pub unsafe fn fsin_32x8(a: f32x8) -> f32x8 {
- // CHECK: call fast <8 x float> @llvm.sin.v8f32
+ // CHECK: call <8 x float> @llvm.sin.v8f32
simd_fsin(a)
}
// CHECK-LABEL: @fsin_32x16
#[no_mangle]
pub unsafe fn fsin_32x16(a: f32x16) -> f32x16 {
- // CHECK: call fast <16 x float> @llvm.sin.v16f32
+ // CHECK: call <16 x float> @llvm.sin.v16f32
simd_fsin(a)
}
// CHECK-LABEL: @fsin_64x4
#[no_mangle]
pub unsafe fn fsin_64x4(a: f64x4) -> f64x4 {
- // CHECK: call fast <4 x double> @llvm.sin.v4f64
+ // CHECK: call <4 x double> @llvm.sin.v4f64
simd_fsin(a)
}
// CHECK-LABEL: @fsin_64x2
#[no_mangle]
pub unsafe fn fsin_64x2(a: f64x2) -> f64x2 {
- // CHECK: call fast <2 x double> @llvm.sin.v2f64
+ // CHECK: call <2 x double> @llvm.sin.v2f64
simd_fsin(a)
}
// CHECK-LABEL: @fsin_64x8
#[no_mangle]
pub unsafe fn fsin_64x8(a: f64x8) -> f64x8 {
- // CHECK: call fast <8 x double> @llvm.sin.v8f64
+ // CHECK: call <8 x double> @llvm.sin.v8f64
simd_fsin(a)
}
+++ /dev/null
-// only-wasm32
-// compile-flags: -C target-feature=+nontrapping-fptoint
-#![crate_type = "lib"]
-
-// CHECK-LABEL: @cast_f64_i64
-#[no_mangle]
-pub fn cast_f64_i64(a: f64) -> i64 {
- // CHECK: tail call i64 @llvm.wasm.trunc.saturate.signed.i64.f64(double {{.*}})
- // CHECK-NEXT: ret i64 {{.*}}
- a as _
-}
-
-// CHECK-LABEL: @cast_f64_i32
-#[no_mangle]
-pub fn cast_f64_i32(a: f64) -> i32 {
- // CHECK: tail call i32 @llvm.wasm.trunc.saturate.signed.i32.f64(double {{.*}})
- // CHECK-NEXT: ret i32 {{.*}}
- a as _
-}
-
-// CHECK-LABEL: @cast_f32_i64
-#[no_mangle]
-pub fn cast_f32_i64(a: f32) -> i64 {
- // CHECK: tail call i64 @llvm.wasm.trunc.saturate.signed.i64.f32(float {{.*}})
- // CHECK-NEXT: ret i64 {{.*}}
- a as _
-}
-
-// CHECK-LABEL: @cast_f32_i32
-#[no_mangle]
-pub fn cast_f32_i32(a: f32) -> i32 {
- // CHECK: tail call i32 @llvm.wasm.trunc.saturate.signed.i32.f32(float {{.*}})
- // CHECK-NEXT: ret i32 {{.*}}
- a as _
-}
-
-
-// CHECK-LABEL: @cast_f64_u64
-#[no_mangle]
-pub fn cast_f64_u64(a: f64) -> u64 {
- // CHECK: tail call i64 @llvm.wasm.trunc.saturate.unsigned.i64.f64(double {{.*}})
- // CHECK-NEXT: ret i64 {{.*}}
- a as _
-}
-
-// CHECK-LABEL: @cast_f64_u32
-#[no_mangle]
-pub fn cast_f64_u32(a: f64) -> u32 {
- // CHECK: tail call i32 @llvm.wasm.trunc.saturate.unsigned.i32.f64(double {{.*}})
- // CHECK-NEXT: ret i32 {{.*}}
- a as _
-}
-
-// CHECK-LABEL: @cast_f32_u64
-#[no_mangle]
-pub fn cast_f32_u64(a: f32) -> u64 {
- // CHECK: tail call i64 @llvm.wasm.trunc.saturate.unsigned.i64.f32(float {{.*}})
- // CHECK-NEXT: ret i64 {{.*}}
- a as _
-}
-
-// CHECK-LABEL: @cast_f32_u32
-#[no_mangle]
-pub fn cast_f32_u32(a: f32) -> u32 {
- // CHECK: tail call i32 @llvm.wasm.trunc.saturate.unsigned.i32.f32(float {{.*}})
- // CHECK-NEXT: ret i32 {{.*}}
- a as _
-}
-
-// CHECK-LABEL: @cast_f32_u8
-#[no_mangle]
-pub fn cast_f32_u8(a: f32) -> u8 {
- // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
- // CHECK: fptoui float {{.*}} to i8
- // CHECK-NEXT: select i1 {{.*}}, i8 {{.*}}, i8 {{.*}}
- // CHECK-NEXT: ret i8 {{.*}}
- a as _
-}
-
-
-
-// CHECK-LABEL: @cast_unchecked_f64_i64
-#[no_mangle]
-pub unsafe fn cast_unchecked_f64_i64(a: f64) -> i64 {
- // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
- // CHECK: fptosi double {{.*}} to i64
- // CHECK-NEXT: ret i64 {{.*}}
- a.to_int_unchecked()
-}
-
-// CHECK-LABEL: @cast_unchecked_f64_i32
-#[no_mangle]
-pub unsafe fn cast_unchecked_f64_i32(a: f64) -> i32 {
- // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
- // CHECK: fptosi double {{.*}} to i32
- // CHECK-NEXT: ret i32 {{.*}}
- a.to_int_unchecked()
-}
-
-// CHECK-LABEL: @cast_unchecked_f32_i64
-#[no_mangle]
-pub unsafe fn cast_unchecked_f32_i64(a: f32) -> i64 {
- // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
- // CHECK: fptosi float {{.*}} to i64
- // CHECK-NEXT: ret i64 {{.*}}
- a.to_int_unchecked()
-}
-
-// CHECK-LABEL: @cast_unchecked_f32_i32
-#[no_mangle]
-pub unsafe fn cast_unchecked_f32_i32(a: f32) -> i32 {
- // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
- // CHECK: fptosi float {{.*}} to i32
- // CHECK-NEXT: ret i32 {{.*}}
- a.to_int_unchecked()
-}
-
-
-// CHECK-LABEL: @cast_unchecked_f64_u64
-#[no_mangle]
-pub unsafe fn cast_unchecked_f64_u64(a: f64) -> u64 {
- // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
- // CHECK: fptoui double {{.*}} to i64
- // CHECK-NEXT: ret i64 {{.*}}
- a.to_int_unchecked()
-}
-
-// CHECK-LABEL: @cast_unchecked_f64_u32
-#[no_mangle]
-pub unsafe fn cast_unchecked_f64_u32(a: f64) -> u32 {
- // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
- // CHECK: fptoui double {{.*}} to i32
- // CHECK-NEXT: ret i32 {{.*}}
- a.to_int_unchecked()
-}
-
-// CHECK-LABEL: @cast_unchecked_f32_u64
-#[no_mangle]
-pub unsafe fn cast_unchecked_f32_u64(a: f32) -> u64 {
- // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
- // CHECK: fptoui float {{.*}} to i64
- // CHECK-NEXT: ret i64 {{.*}}
- a.to_int_unchecked()
-}
-
-// CHECK-LABEL: @cast_unchecked_f32_u32
-#[no_mangle]
-pub unsafe fn cast_unchecked_f32_u32(a: f32) -> u32 {
- // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
- // CHECK: fptoui float {{.*}} to i32
- // CHECK-NEXT: ret i32 {{.*}}
- a.to_int_unchecked()
-}
-
-// CHECK-LABEL: @cast_unchecked_f32_u8
-#[no_mangle]
-pub unsafe fn cast_unchecked_f32_u8(a: f32) -> u8 {
- // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
- // CHECK: fptoui float {{.*}} to i8
- // CHECK-NEXT: ret i8 {{.*}}
- a.to_int_unchecked()
-}
// only-wasm32
// compile-flags: -C target-feature=-nontrapping-fptoint
+// min-llvm-version: 12.0
#![crate_type = "lib"]
// CHECK-LABEL: @cast_f64_i64
pub fn cast_f64_i64(a: f64) -> i64 {
// CHECK-NOT: fptosi double {{.*}} to i64
// CHECK-NOT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}}
- // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
+ // CHECK: {{.*}} call {{.*}} @llvm.fptosi.sat.i64.f64{{.*}}
a as _
}
pub fn cast_f64_i32(a: f64) -> i32 {
// CHECK-NOT: fptosi double {{.*}} to i32
// CHECK-NOT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}}
- // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
+ // CHECK: {{.*}} call {{.*}} @llvm.fptosi.sat.i32.f64{{.*}}
a as _
}
pub fn cast_f32_i64(a: f32) -> i64 {
// CHECK-NOT: fptosi float {{.*}} to i64
// CHECK-NOT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}}
- // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
+ // CHECK: {{.*}} call {{.*}} @llvm.fptosi.sat.i64.f32{{.*}}
a as _
}
pub fn cast_f32_i32(a: f32) -> i32 {
// CHECK-NOT: fptosi float {{.*}} to i32
// CHECK-NOT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}}
- // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
+ // CHECK: {{.*}} call {{.*}} @llvm.fptosi.sat.i32.f32{{.*}}
a as _
}
pub fn cast_f64_u64(a: f64) -> u64 {
// CHECK-NOT: fptoui double {{.*}} to i64
// CHECK-NOT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}}
- // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
+ // CHECK: {{.*}} call {{.*}} @llvm.fptoui.sat.i64.f64{{.*}}
a as _
}
pub fn cast_f64_u32(a: f64) -> u32 {
// CHECK-NOT: fptoui double {{.*}} to i32
// CHECK-NOT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}}
- // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
+ // CHECK: {{.*}} call {{.*}} @llvm.fptoui.sat.i32.f64{{.*}}
a as _
}
pub fn cast_f32_u64(a: f32) -> u64 {
// CHECK-NOT: fptoui float {{.*}} to i64
// CHECK-NOT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}}
- // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
+ // CHECK: {{.*}} call {{.*}} @llvm.fptoui.sat.i64.f32{{.*}}
a as _
}
pub fn cast_f32_u32(a: f32) -> u32 {
// CHECK-NOT: fptoui float {{.*}} to i32
// CHECK-NOT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}}
- // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
+ // CHECK: {{.*}} call {{.*}} @llvm.fptoui.sat.i32.f32{{.*}}
a as _
}
// CHECK-LABEL: @cast_f32_u8
#[no_mangle]
pub fn cast_f32_u8(a: f32) -> u8 {
- // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
- // CHECK: fptoui float {{.*}} to i8
- // CHECK-NEXT: select i1 {{.*}}, i8 {{.*}}, i8 {{.*}}
+ // CHECK-NOT: fptoui float {{.*}} to i8
+ // CHECK-NOT: select i1 {{.*}}, i8 {{.*}}, i8 {{.*}}
+ // CHECK: {{.*}} call {{.*}} @llvm.fptoui.sat.i8.f32{{.*}}
a as _
}
// compile-flags:-g
-#![feature(non_ascii_idents)]
-
// This test checks whether debuginfo generation can handle multi-byte UTF-8
// characters at the end of a block. There's no need to do anything in the
// debugger -- just make sure that the compiler doesn't crash.
--- /dev/null
+// revisions: cfail1 cfail2
+// build-pass
+
+#![crate_type="lib"]
+#![crate_type="cdylib"]
+
+#[allow(unused_imports)]
+use std::alloc::System;
+
+#[cfg(cfail1)]
+#[global_allocator]
+static ALLOC: System = System;
1| |#![allow(unused_assignments, dead_code)]
2| |
- 3| |// compile-flags: --edition=2018 -C opt-level=1 # fix in rustc_mir/monomorphize/partitioning/mod.rs
+ 3| |// compile-flags: --edition=2018 -C opt-level=1
4| |
5| 1|async fn c(x: u8) -> u8 {
6| 1| if x == 8 {
--- /dev/null
+ 1| |// compile-flags: --edition=2018
+ 2| |
+ 3| |use core::{
+ 4| | future::Future,
+ 5| | marker::Send,
+ 6| | pin::Pin,
+ 7| |};
+ 8| |
+ 9| 1|fn non_async_func() {
+ 10| 1| println!("non_async_func was covered");
+ 11| 1| let b = true;
+ 12| 1| if b {
+ 13| 1| println!("non_async_func println in block");
+ 14| 1| }
+ 15| 1|}
+ 16| |
+ 17| |// FIXME(#83985): The auto-generated closure in an async function is failing to include
+ 18| |// the println!() and `let` assignment lines in the coverage code region(s), as it does in the
+ 19| |// non-async function above, unless the `println!()` is inside a covered block.
+ 20| 1|async fn async_func() {
+ 21| 1| println!("async_func was covered");
+ 22| 1| let b = true;
+ 23| 1| if b {
+ 24| 1| println!("async_func println in block");
+ 25| 1| }
+ ^0
+ 26| 1|}
+ 27| |
+ 28| |// FIXME(#83985): As above, this async function only has the `println!()` macro call, which is not
+ 29| |// showing coverage, so the entire async closure _appears_ uncovered; but this is not exactly true.
+ 30| |// It's only certain kinds of lines and/or their context that results in missing coverage.
+ 31| 1|async fn async_func_just_println() {
+ 32| 1| println!("async_func_just_println was covered");
+ 33| 1|}
+ 34| |
+ 35| 1|fn main() {
+ 36| 1| println!("codecovsample::main");
+ 37| 1|
+ 38| 1| non_async_func();
+ 39| 1|
+ 40| 1| executor::block_on(async_func());
+ 41| 1| executor::block_on(async_func_just_println());
+ 42| 1|}
+ 43| |
+ 44| |mod executor {
+ 45| | use core::{
+ 46| | future::Future,
+ 47| | pin::Pin,
+ 48| | task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
+ 49| | };
+ 50| |
+ 51| 2| pub fn block_on<F: Future>(mut future: F) -> F::Output {
+ 52| 2| let mut future = unsafe { Pin::new_unchecked(&mut future) };
+ 53| 2| use std::hint::unreachable_unchecked;
+ 54| 2| static VTABLE: RawWakerVTable = RawWakerVTable::new(
+ 55| 2| |_| unsafe { unreachable_unchecked() }, // clone
+ ^0
+ 56| 2| |_| unsafe { unreachable_unchecked() }, // wake
+ ^0
+ 57| 2| |_| unsafe { unreachable_unchecked() }, // wake_by_ref
+ ^0
+ 58| 2| |_| (),
+ 59| 2| );
+ 60| 2| let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
+ 61| 2| let mut context = Context::from_waker(&waker);
+ 62| |
+ 63| | loop {
+ 64| 2| if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
+ 65| 2| break val;
+ 66| 0| }
+ 67| | }
+ 68| 2| }
+ ------------------
+ | async2::executor::block_on::<core::future::from_generator::GenFuture<async2::async_func::{closure#0}>>:
+ | 51| 1| pub fn block_on<F: Future>(mut future: F) -> F::Output {
+ | 52| 1| let mut future = unsafe { Pin::new_unchecked(&mut future) };
+ | 53| 1| use std::hint::unreachable_unchecked;
+ | 54| 1| static VTABLE: RawWakerVTable = RawWakerVTable::new(
+ | 55| 1| |_| unsafe { unreachable_unchecked() }, // clone
+ | 56| 1| |_| unsafe { unreachable_unchecked() }, // wake
+ | 57| 1| |_| unsafe { unreachable_unchecked() }, // wake_by_ref
+ | 58| 1| |_| (),
+ | 59| 1| );
+ | 60| 1| let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
+ | 61| 1| let mut context = Context::from_waker(&waker);
+ | 62| |
+ | 63| | loop {
+ | 64| 1| if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
+ | 65| 1| break val;
+ | 66| 0| }
+ | 67| | }
+ | 68| 1| }
+ ------------------
+ | async2::executor::block_on::<core::future::from_generator::GenFuture<async2::async_func_just_println::{closure#0}>>:
+ | 51| 1| pub fn block_on<F: Future>(mut future: F) -> F::Output {
+ | 52| 1| let mut future = unsafe { Pin::new_unchecked(&mut future) };
+ | 53| 1| use std::hint::unreachable_unchecked;
+ | 54| 1| static VTABLE: RawWakerVTable = RawWakerVTable::new(
+ | 55| 1| |_| unsafe { unreachable_unchecked() }, // clone
+ | 56| 1| |_| unsafe { unreachable_unchecked() }, // wake
+ | 57| 1| |_| unsafe { unreachable_unchecked() }, // wake_by_ref
+ | 58| 1| |_| (),
+ | 59| 1| );
+ | 60| 1| let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
+ | 61| 1| let mut context = Context::from_waker(&waker);
+ | 62| |
+ | 63| | loop {
+ | 64| 1| if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
+ | 65| 1| break val;
+ | 66| 0| }
+ | 67| | }
+ | 68| 1| }
+ ------------------
+ 69| |}
+
--- /dev/null
+ 1| |#![allow(unused_assignments, unused_variables)]
+ 2| |
+ 3| 1|fn main() {
+ 4| 1| let is_true = std::env::args().len() == 1;
+ 5| 1|
+ 6| 1| let mut x = 0;
+ 7| 11| for _ in 0..10 {
+ ^10
+ 8| 10| match is_true {
+ 9| | true => {
+ 10| 10| continue;
+ 11| | }
+ 12| 0| _ => {
+ 13| 0| x = 1;
+ 14| 0| }
+ 15| 0| }
+ 16| 0| x = 3;
+ 17| | }
+ 18| 11| for _ in 0..10 {
+ ^10
+ 19| 10| match is_true {
+ 20| 0| false => {
+ 21| 0| x = 1;
+ 22| 0| }
+ 23| | _ => {
+ 24| 10| continue;
+ 25| | }
+ 26| | }
+ 27| 0| x = 3;
+ 28| | }
+ 29| 11| for _ in 0..10 {
+ ^10
+ 30| 10| match is_true {
+ 31| 10| true => {
+ 32| 10| x = 1;
+ 33| 10| }
+ 34| | _ => {
+ 35| 0| continue;
+ 36| | }
+ 37| | }
+ 38| 10| x = 3;
+ 39| | }
+ 40| 11| for _ in 0..10 {
+ ^10
+ 41| 10| if is_true {
+ 42| 10| continue;
+ 43| 0| }
+ 44| 0| x = 3;
+ 45| | }
+ 46| 11| for _ in 0..10 {
+ ^10
+ 47| 10| match is_true {
+ 48| 0| false => {
+ 49| 0| x = 1;
+ 50| 0| }
+ 51| 10| _ => {
+ 52| 10| let _ = x;
+ 53| 10| }
+ 54| | }
+ 55| 10| x = 3;
+ 56| | }
+ 57| 1| for _ in 0..10 {
+ 58| 1| match is_true {
+ 59| 0| false => {
+ 60| 0| x = 1;
+ 61| 0| }
+ 62| | _ => {
+ 63| 1| break;
+ 64| | }
+ 65| | }
+ 66| 0| x = 3;
+ 67| | }
+ 68| | let _ = x;
+ 69| 1|}
+
--- /dev/null
+ 1| |// Shows that rust-lang/rust/83601 is resolved
+ 2| |
+ 3| 3|#[derive(Debug, PartialEq, Eq)]
+ ^2
+ ------------------
+ | <issue_83601::Foo as core::cmp::PartialEq>::eq:
+ | 3| 2|#[derive(Debug, PartialEq, Eq)]
+ ------------------
+ | Unexecuted instantiation: <issue_83601::Foo as core::cmp::PartialEq>::ne
+ ------------------
+ 4| |struct Foo(u32);
+ 5| |
+ 6| 1|fn main() {
+ 7| 1| let bar = Foo(1);
+ 8| 0| assert_eq!(bar, Foo(1));
+ 9| 1| let baz = Foo(0);
+ 10| 0| assert_ne!(baz, Foo(1));
+ 11| 1| println!("{:?}", Foo(1));
+ 12| 1| println!("{:?}", bar);
+ 13| 1| println!("{:?}", baz);
+ 14| 1|}
+
--- /dev/null
+ 1| |// Enables `no_coverage` on the entire crate
+ 2| |#![feature(no_coverage)]
+ 3| |
+ 4| |#[no_coverage]
+ 5| |fn do_not_add_coverage_1() {
+ 6| | println!("called but not covered");
+ 7| |}
+ 8| |
+ 9| |#[no_coverage]
+ 10| |fn do_not_add_coverage_2() {
+ 11| | println!("called but not covered");
+ 12| |}
+ 13| |
+ 14| 1|fn main() {
+ 15| 1| do_not_add_coverage_1();
+ 16| 1| do_not_add_coverage_2();
+ 17| 1|}
+
--- /dev/null
+ 1| |// Enables `no_coverage` on individual functions
+ 2| |
+ 3| |#[feature(no_coverage)]
+ 4| |#[no_coverage]
+ 5| |fn do_not_add_coverage_1() {
+ 6| | println!("called but not covered");
+ 7| |}
+ 8| |
+ 9| |#[no_coverage]
+ 10| |#[feature(no_coverage)]
+ 11| |fn do_not_add_coverage_2() {
+ 12| | println!("called but not covered");
+ 13| |}
+ 14| |
+ 15| 1|fn main() {
+ 16| 1| do_not_add_coverage_1();
+ 17| 1| do_not_add_coverage_2();
+ 18| 1|}
+
2| |// structure of this test.
3| |
4| 2|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
- ^0 ^0 ^0 ^0 ^1 ^1 ^0^0
+ ^0 ^0 ^0 ^1 ^1 ^0^0
------------------
| Unexecuted instantiation: <partial_eq::Version as core::cmp::PartialEq>::ne
------------------
9| | }
10| 6|}
11| |
- 12| 1|fn main() -> Result<(),()> {
+ 12| 1|fn test1() -> Result<(),()> {
13| 1| let mut
14| 1| countdown = 10
15| | ;
34| | }
35| 0| Ok(())
36| 1|}
+ 37| |
+ 38| |struct Thing1;
+ 39| |impl Thing1 {
+ 40| 18| fn get_thing_2(&self, return_error: bool) -> Result<Thing2,()> {
+ 41| 18| if return_error {
+ 42| 1| Err(())
+ 43| | } else {
+ 44| 17| Ok(Thing2{})
+ 45| | }
+ 46| 18| }
+ 47| |}
+ 48| |
+ 49| |struct Thing2;
+ 50| |impl Thing2 {
+ 51| 17| fn call(&self, return_error: bool) -> Result<u32,()> {
+ 52| 17| if return_error {
+ 53| 2| Err(())
+ 54| | } else {
+ 55| 15| Ok(57)
+ 56| | }
+ 57| 17| }
+ 58| |}
+ 59| |
+ 60| 1|fn test2() -> Result<(),()> {
+ 61| 1| let thing1 = Thing1{};
+ 62| 1| let mut
+ 63| 1| countdown = 10
+ 64| | ;
+ 65| | for
+ 66| 6| _
+ 67| | in
+ 68| 6| 0..10
+ 69| | {
+ 70| 6| countdown
+ 71| 6| -= 1
+ 72| 6| ;
+ 73| 6| if
+ 74| 6| countdown < 5
+ 75| | {
+ 76| 1| thing1.get_thing_2(/*err=*/ false)?.call(/*err=*/ true).expect_err("call should fail");
+ ^0
+ 77| 1| thing1
+ 78| 1| .
+ 79| 1| get_thing_2(/*return_error=*/ false)
+ 80| 0| ?
+ 81| | .
+ 82| 1| call(/*return_error=*/ true)
+ 83| 1| .
+ 84| 1| expect_err(
+ 85| 1| "call should fail"
+ 86| 1| );
+ 87| 1| let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ true)?;
+ ^0 ^0 ^0
+ 88| 0| assert_eq!(val, 57);
+ 89| 0| let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ false)?;
+ 90| 0| assert_eq!(val, 57);
+ 91| | }
+ 92| | else
+ 93| | {
+ 94| 5| let val = thing1.get_thing_2(/*return_error=*/ false)?.call(/*return_error=*/ false)?;
+ ^0 ^0
+ 95| 5| assert_eq!(val, 57);
+ 96| 5| let val = thing1
+ 97| 5| .get_thing_2(/*return_error=*/ false)?
+ ^0
+ 98| 5| .call(/*return_error=*/ false)?;
+ ^0
+ 99| 5| assert_eq!(val, 57);
+ 100| 5| let val = thing1
+ 101| 5| .get_thing_2(/*return_error=*/ false)
+ 102| 0| ?
+ 103| 5| .call(/*return_error=*/ false)
+ 104| 0| ?
+ 105| | ;
+ 106| 5| assert_eq!(val, 57);
+ 107| | }
+ 108| | }
+ 109| 0| Ok(())
+ 110| 1|}
+ 111| |
+ 112| 1|fn main() -> Result<(),()> {
+ 113| 1| test1().expect_err("test1 should fail");
+ 114| 1| test2()
+ 115| 1| ?
+ 116| | ;
+ 117| 0| Ok(())
+ 118| 1|}
18| 2| println!("used_only_from_bin_crate_generic_function with {:?}", arg);
19| 2|}
------------------
- | used_crate::used_only_from_bin_crate_generic_function::<&alloc::vec::Vec<i32>>:
+ | used_crate::used_only_from_bin_crate_generic_function::<&str>:
| 17| 1|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) {
| 18| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg);
| 19| 1|}
------------------
- | used_crate::used_only_from_bin_crate_generic_function::<&str>:
+ | used_crate::used_only_from_bin_crate_generic_function::<&alloc::vec::Vec<i32>>:
| 17| 1|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) {
| 18| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg);
| 19| 1|}
#![allow(unused_assignments, dead_code)]
-// compile-flags: --edition=2018 -C opt-level=1 # fix in rustc_mir/monomorphize/partitioning/mod.rs
+// compile-flags: --edition=2018 -C opt-level=1
async fn c(x: u8) -> u8 {
if x == 8 {
--- /dev/null
+// compile-flags: --edition=2018
+
+use core::{
+ future::Future,
+ marker::Send,
+ pin::Pin,
+};
+
+fn non_async_func() {
+ println!("non_async_func was covered");
+ let b = true;
+ if b {
+ println!("non_async_func println in block");
+ }
+}
+
+// FIXME(#83985): The auto-generated closure in an async function is failing to include
+// the println!() and `let` assignment lines in the coverage code region(s), as it does in the
+// non-async function above, unless the `println!()` is inside a covered block.
+async fn async_func() {
+ println!("async_func was covered");
+ let b = true;
+ if b {
+ println!("async_func println in block");
+ }
+}
+
+// FIXME(#83985): As above, this async function only has the `println!()` macro call, which is not
+// showing coverage, so the entire async closure _appears_ uncovered; but this is not exactly true.
+// It's only certain kinds of lines and/or their context that results in missing coverage.
+async fn async_func_just_println() {
+ println!("async_func_just_println was covered");
+}
+
+fn main() {
+ println!("codecovsample::main");
+
+ non_async_func();
+
+ executor::block_on(async_func());
+ executor::block_on(async_func_just_println());
+}
+
+mod executor {
+ use core::{
+ future::Future,
+ pin::Pin,
+ task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
+ };
+
+ pub fn block_on<F: Future>(mut future: F) -> F::Output {
+ let mut future = unsafe { Pin::new_unchecked(&mut future) };
+ use std::hint::unreachable_unchecked;
+ static VTABLE: RawWakerVTable = RawWakerVTable::new(
+ |_| unsafe { unreachable_unchecked() }, // clone
+ |_| unsafe { unreachable_unchecked() }, // wake
+ |_| unsafe { unreachable_unchecked() }, // wake_by_ref
+ |_| (),
+ );
+ let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
+ let mut context = Context::from_waker(&waker);
+
+ loop {
+ if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
+ break val;
+ }
+ }
+ }
+}
--- /dev/null
+#![allow(unused_assignments, unused_variables)]
+
+fn main() {
+ let is_true = std::env::args().len() == 1;
+
+ let mut x = 0;
+ for _ in 0..10 {
+ match is_true {
+ true => {
+ continue;
+ }
+ _ => {
+ x = 1;
+ }
+ }
+ x = 3;
+ }
+ for _ in 0..10 {
+ match is_true {
+ false => {
+ x = 1;
+ }
+ _ => {
+ continue;
+ }
+ }
+ x = 3;
+ }
+ for _ in 0..10 {
+ match is_true {
+ true => {
+ x = 1;
+ }
+ _ => {
+ continue;
+ }
+ }
+ x = 3;
+ }
+ for _ in 0..10 {
+ if is_true {
+ continue;
+ }
+ x = 3;
+ }
+ for _ in 0..10 {
+ match is_true {
+ false => {
+ x = 1;
+ }
+ _ => {
+ let _ = x;
+ }
+ }
+ x = 3;
+ }
+ for _ in 0..10 {
+ match is_true {
+ false => {
+ x = 1;
+ }
+ _ => {
+ break;
+ }
+ }
+ x = 3;
+ }
+ let _ = x;
+}
--- /dev/null
+// Shows that rust-lang/rust/83601 is resolved
+
+#[derive(Debug, PartialEq, Eq)]
+struct Foo(u32);
+
+fn main() {
+ let bar = Foo(1);
+ assert_eq!(bar, Foo(1));
+ let baz = Foo(0);
+ assert_ne!(baz, Foo(1));
+ println!("{:?}", Foo(1));
+ println!("{:?}", bar);
+ println!("{:?}", baz);
+}
--- /dev/null
+// Enables `no_coverage` on the entire crate
+#![feature(no_coverage)]
+
+#[no_coverage]
+fn do_not_add_coverage_1() {
+ println!("called but not covered");
+}
+
+#[no_coverage]
+fn do_not_add_coverage_2() {
+ println!("called but not covered");
+}
+
+fn main() {
+ do_not_add_coverage_1();
+ do_not_add_coverage_2();
+}
--- /dev/null
+// Enables `no_coverage` on individual functions
+
+#[feature(no_coverage)]
+#[no_coverage]
+fn do_not_add_coverage_1() {
+ println!("called but not covered");
+}
+
+#[no_coverage]
+#[feature(no_coverage)]
+fn do_not_add_coverage_2() {
+ println!("called but not covered");
+}
+
+fn main() {
+ do_not_add_coverage_1();
+ do_not_add_coverage_2();
+}
}
}
-fn main() -> Result<(),()> {
+fn test1() -> Result<(),()> {
let mut
countdown = 10
;
}
Ok(())
}
+
+struct Thing1;
+impl Thing1 {
+ fn get_thing_2(&self, return_error: bool) -> Result<Thing2,()> {
+ if return_error {
+ Err(())
+ } else {
+ Ok(Thing2{})
+ }
+ }
+}
+
+struct Thing2;
+impl Thing2 {
+ fn call(&self, return_error: bool) -> Result<u32,()> {
+ if return_error {
+ Err(())
+ } else {
+ Ok(57)
+ }
+ }
+}
+
+fn test2() -> Result<(),()> {
+ let thing1 = Thing1{};
+ let mut
+ countdown = 10
+ ;
+ for
+ _
+ in
+ 0..10
+ {
+ countdown
+ -= 1
+ ;
+ if
+ countdown < 5
+ {
+ thing1.get_thing_2(/*err=*/ false)?.call(/*err=*/ true).expect_err("call should fail");
+ thing1
+ .
+ get_thing_2(/*return_error=*/ false)
+ ?
+ .
+ call(/*return_error=*/ true)
+ .
+ expect_err(
+ "call should fail"
+ );
+ let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ true)?;
+ assert_eq!(val, 57);
+ let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ false)?;
+ assert_eq!(val, 57);
+ }
+ else
+ {
+ let val = thing1.get_thing_2(/*return_error=*/ false)?.call(/*return_error=*/ false)?;
+ assert_eq!(val, 57);
+ let val = thing1
+ .get_thing_2(/*return_error=*/ false)?
+ .call(/*return_error=*/ false)?;
+ assert_eq!(val, 57);
+ let val = thing1
+ .get_thing_2(/*return_error=*/ false)
+ ?
+ .call(/*return_error=*/ false)
+ ?
+ ;
+ assert_eq!(val, 57);
+ }
+ }
+ Ok(())
+}
+
+fn main() -> Result<(),()> {
+ test1().expect_err("test1 should fail");
+ test2()
+ ?
+ ;
+ Ok(())
+}
--- /dev/null
+// This test ensures that the element corresponding to the hash is displayed.
+goto: file://|DOC_PATH|/struct.Foo.html#method.borrow
+// In the blanket implementations list, "Borrow" is the second one, hence the ":nth(2)".
+assert: ("#blanket-implementations-list > details:nth-child(2)", "open", "")
+// Please note the "\" below is needed because otherwise ".borrow" would be interpreted as
+// a class selector.
+assert: ("#method\.borrow", {"display": "flex"})
+// We first check that the impl block is open by default.
+assert: ("#implementations + details", "open", "")
+// We collapse it.
+click: "#implementations + details > summary"
+// We check that it was collapsed as expected.
+assert-false: ("#implementations + details", "open", "")
+// To ensure that we will click on the currently hidden method.
+assert: (".sidebar-links > a", "must_use")
+click: ".sidebar-links > a"
+// We check that the impl block was opened as expected so that we can see the method.
+assert: ("#implementations + details", "open", "")
--- /dev/null
+// This test ensures that the impl blocks are open by default.
+goto: file://|DOC_PATH|/struct.Foo.html
+assert: ("#main > details.implementors-toggle", "open", "")
impl Foo {
#[must_use]
- pub fn must_use(&self) -> bool { true }
+ pub fn must_use(&self) -> bool {
+ true
+ }
}
/// Just a normal enum.
/// let x = 12;
/// ```
pub fn check_list_code_block() {}
+
+pub enum AnEnum {
+ WithVariants { and: usize, sub: usize, variants: usize },
+}
+++ /dev/null
-// Check that the attributes are well positioned when javascript is disabled (since
-// there is no toggle to display)
-javascript: false
-goto: file://|DOC_PATH|/struct.Foo.html
-assert: (".attributes", {"margin-left": "0px"})
--> $DIR/anchors.rs:47:6
|
LL | /// [prim@usize#x]
- | ^^^^^^^^^^^^ contains invalid anchor
+ | ^^^^^^^^^^--
+ | |
+ | invalid anchor
|
note: the lint level is defined here
--> $DIR/anchors.rs:1:9
--> $DIR/anchors.rs:25:15
|
LL | /// Or maybe [Foo::f#hola].
- | ^^^^^^^^^^^ contains invalid anchor
+ | ^^^^^^-----
+ | |
+ | invalid anchor
error: `hello#people#!` contains multiple anchors
--> $DIR/anchors.rs:31:28
|
LL | /// Another anchor error: [hello#people#!].
- | ^^^^^^^^^^^^^^ contains invalid anchor
+ | ^^^^^^^^^^^^--
+ | |
+ | invalid anchor
error: `Enum::A#whatever` contains an anchor, but links to variants are already anchored
--> $DIR/anchors.rs:37:28
|
LL | /// Damn enum's variants: [Enum::A#whatever].
- | ^^^^^^^^^^^^^^^^ contains invalid anchor
+ | ^^^^^^^---------
+ | |
+ | invalid anchor
error: `u32#hello` contains an anchor, but links to builtin types are already anchored
--> $DIR/anchors.rs:43:6
|
LL | /// [u32#hello]
- | ^^^^^^^^^ contains invalid anchor
+ | ^^^------
+ | |
+ | invalid anchor
|
= note: this restriction may be lifted in a future release
= note: see https://github.com/rust-lang/rust/issues/83083 for more information
--> $DIR/double-anchor.rs:5:18
|
LL | /// docs [label][with#anchor#error]
- | ^^^^^^^^^^^^^^^^^ contains invalid anchor
+ | ^^^^^^^^^^^------
+ | |
+ | invalid anchor
|
= note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default
type Output: ?Sized;
// @has - '//*[@id="tymethod.index"]//code' \
// "fn index<'a>(&'a self, index: I) -> &'a Self::Output"
- // @has - '//*[@id="tymethod.index"]//code//a[@href="../assoc_types/trait.Index.html#associatedtype.Output"]' \
+ // @has - '//*[@id="tymethod.index"]//code//a[@href="trait.Index.html#associatedtype.Output"]' \
// "Output"
fn index<'a>(&'a self, index: I) -> &'a Self::Output;
}
// @has assoc_types/fn.use_output.html
// @has - '//*[@class="rust fn"]' '-> &T::Output'
-// @has - '//*[@class="rust fn"]//a[@href="../assoc_types/trait.Index.html#associatedtype.Output"]' 'Output'
+// @has - '//*[@class="rust fn"]//a[@href="trait.Index.html#associatedtype.Output"]' 'Output'
pub fn use_output<T: Index<usize>>(obj: &T, index: usize) -> &T::Output {
obj.index(index)
}
// @has assoc_types/fn.use_input.html
// @has - '//*[@class="rust fn"]' 'T::Input'
-// @has - '//*[@class="rust fn"]//a[@href="../assoc_types/trait.Feed.html#associatedtype.Input"]' 'Input'
+// @has - '//*[@class="rust fn"]//a[@href="trait.Feed.html#associatedtype.Input"]' 'Input'
pub fn use_input<T: Feed>(_feed: &T, _element: T::Input) { }
// @has assoc_types/fn.cmp_input.html
// @has - '//*[@class="rust fn"]' 'where T::Input: PartialEq<U::Input>'
-// @has - '//*[@class="rust fn"]//a[@href="../assoc_types/trait.Feed.html#associatedtype.Input"]' 'Input'
+// @has - '//*[@class="rust fn"]//a[@href="trait.Feed.html#associatedtype.Input"]' 'Input'
pub fn cmp_input<T: Feed, U: Feed>(a: &T::Input, b: &U::Input) -> bool
where T::Input: PartialEq<U::Input>
{
#![crate_name = "foo"]
-// @has foo/fn.f.html '//*[@class="docblock attributes"]' '#[no_mangle]'
+// @has foo/fn.f.html '//*[@class="rust fn"]' '#[no_mangle]'
#[no_mangle]
pub extern "C" fn f() {}
-// @has foo/fn.g.html '//*[@class="docblock attributes"]' '#[export_name = "bar"]'
+// @has foo/fn.g.html '//*[@class="rust fn"]' '#[export_name = "bar"]'
#[export_name = "bar"]
pub extern "C" fn g() {}
-// @matches foo/enum.Foo.html '//*[@class="docblock attributes top-attr"]' \
-// '(?m)\A#\[repr\(i64\)\]\n#\[must_use\]\Z'
+// @matches foo/enum.Foo.html '//*[@class="rust enum"]' \
+// '#\[repr\(i64\)\]\n#\[must_use\]'
#[repr(i64)]
#[must_use]
pub enum Foo {
Bar,
}
-// @has foo/struct.Repr.html '//*[@class="docblock attributes top-attr"]' '#[repr(C, align(8))]'
+// @has foo/struct.Repr.html '//*[@class="docblock type-decl"]' '#[repr(C, align(8))]'
#[repr(C, align(8))]
pub struct Repr;
--- /dev/null
+// compile-flags: --crate-type lib --edition 2018
+
+#[doc(primitive = "usize")]
+/// This is the built-in type `usize`.
+mod usize {
+}
pub struct Foo;
-// @has foo/struct.Bar.html '//a[@href="../foo/struct.Foo.html"]' 'Foo'
+// @has foo/struct.Bar.html '//a[@href="struct.Foo.html"]' 'Foo'
/// Code-styled reference to [`Foo`].
pub struct Bar;
inner: T,
}
-// @has foo/struct.Simd.html '//div[@id="trait-implementations-list"]/h3/code' 'impl Add<Simd<u8, 16_usize>> for Simd<u8, 16>'
+// @has foo/struct.Simd.html '//div[@id="trait-implementations-list"]//h3/code' 'impl Add<Simd<u8, 16_usize>> for Simd<u8, 16>'
impl Add for Simd<u8, 16> {
type Output = Self;
--- /dev/null
+// aux-build:primitive-doc.rs
+// compile-flags: --extern-html-root-url=primitive_doc=../ -Z unstable-options
+
+#![no_std]
+
+extern crate primitive_doc;
+
+// @has 'cross_crate_primitive_doc/fn.foo.html' '//a[@href="../primitive_doc/primitive.usize.html"]' 'usize'
+pub fn foo() -> usize { 0 }
+// compile-flags: --document-private-items
+
#![feature(decl_macro)]
// @has decl_macro/macro.my_macro.html //pre 'pub macro my_macro() {'
pub macro by_example_single {
($foo:expr) => {}
}
+
+mod a {
+ mod b {
+ // @has decl_macro/a/b/macro.by_example_vis.html //pre 'pub(super) macro by_example_vis($foo:expr) {'
+ pub(in super) macro by_example_vis {
+ ($foo:expr) => {}
+ }
+ mod c {
+ // @has decl_macro/a/b/c/macro.by_example_vis_named.html //pre 'pub(in a) macro by_example_vis_named($foo:expr) {'
+ pub(in a) macro by_example_vis_named {
+ ($foo:expr) => {}
+ }
+ }
+ }
+}
#![crate_name = "foo"]
-// @has foo/trait.Foo.html '//a[@href="../foo/trait.Foo.html#tymethod.req"]' 'req'
-// @has foo/trait.Foo.html '//a[@href="../foo/trait.Foo.html#method.prov"]' 'prov'
+// @has foo/trait.Foo.html '//a[@href="trait.Foo.html#tymethod.req"]' 'req'
+// @has foo/trait.Foo.html '//a[@href="trait.Foo.html#method.prov"]' 'prov'
/// Always make sure to implement [`req`], but you don't have to implement [`prov`].
///
// @has issue_33054/impls/struct.Foo.html
// @has - '//code' 'impl Foo'
// @has - '//code' 'impl Bar for Foo'
-// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
-// @count - '//*[@id="main"]/*[@class="impl"]' 1
+// @count - '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1
+// @count - '//*[@id="main"]/details/summary/*[@class="impl"]' 1
// @has issue_33054/impls/bar/trait.Bar.html
// @has - '//code' 'impl Bar for Foo'
// @count - '//*[@class="struct"]' 1
--- /dev/null
+#![crate_name = "foo"]
+
+// @has foo/struct.Foo.html
+// @has - '//div[@id="synthetic-implementations-list"]/h3[@id="impl-Send"]' 'impl Send for Foo'
+pub struct Foo;
+
+pub trait EmptyTrait {}
+
+// @has - '//div[@id="trait-implementations-list"]/h3[@id="impl-EmptyTrait"]' 'impl EmptyTrait for Foo'
+impl EmptyTrait for Foo {}
+
+pub trait NotEmpty {
+ fn foo(&self);
+}
+
+// @has - '//div[@id="trait-implementations-list"]/details/summary/h3[@id="impl-NotEmpty"]' 'impl NotEmpty for Foo'
+impl NotEmpty for Foo {
+ fn foo(&self) {}
+}
extern crate cross_crate_self;
-// @has self/struct.S.html '//a[@href="../self/struct.S.html#method.f"]' "Self::f"
-// @has self/struct.S.html '//a[@href="../self/struct.S.html"]' "Self"
+// @has self/struct.S.html '//a[@href="struct.S.html#method.f"]' "Self::f"
+// @has self/struct.S.html '//a[@href="struct.S.html"]' "Self"
// @has self/struct.S.html '//a[@href="../cross_crate_self/index.html"]' "crate"
pub use cross_crate_self::S;
pub struct Something;
// @has anchors/struct.SomeOtherType.html
-// @has - '//a/@href' '../anchors/struct.Something.html#Anchor!'
+// @has - '//a/@href' 'struct.Something.html#Anchor!'
/// I want...
///
}
/// Link to [UsesDefaults::T] and [UsesDefaults::f]
-// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="../associated_defaults/struct.UsesDefaults.html#associatedtype.T"]' 'UsesDefaults::T'
-// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="../associated_defaults/struct.UsesDefaults.html#method.f"]' 'UsesDefaults::f'
+// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="struct.UsesDefaults.html#associatedtype.T"]' 'UsesDefaults::T'
+// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="struct.UsesDefaults.html#method.f"]' 'UsesDefaults::f'
pub struct UsesDefaults;
impl TraitWithDefault for UsesDefaults {}
/// Link to [OverridesDefaults::T] and [OverridesDefaults::f]
-// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="../associated_defaults/struct.OverridesDefaults.html#associatedtype.T"]' 'OverridesDefaults::T'
-// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="../associated_defaults/struct.OverridesDefaults.html#method.f"]' 'OverridesDefaults::f'
+// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="struct.OverridesDefaults.html#associatedtype.T"]' 'OverridesDefaults::T'
+// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="struct.OverridesDefaults.html#method.f"]' 'OverridesDefaults::f'
pub struct OverridesDefaults;
impl TraitWithDefault for OverridesDefaults {
type T = bool;
pub fn foo() {}
/// Link to [MyStruct], [link from struct][MyStruct::method], [MyStruct::clone], [MyStruct::Input]
-// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html"]' 'MyStruct'
-// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#method.method"]' 'link from struct'
-// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#method.clone"]' 'MyStruct::clone'
-// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#associatedtype.Input"]' 'MyStruct::Input'
+// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html"]' 'MyStruct'
+// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.method"]' 'link from struct'
+// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.clone"]' 'MyStruct::clone'
+// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#associatedtype.Input"]' 'MyStruct::Input'
pub struct MyStruct { foo: () }
impl Clone for MyStruct {
type Input = usize;
/// [link from method][MyStruct::method] on method
- // @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#method.method"]' 'link from method'
+ // @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.method"]' 'link from method'
fn method(i: usize) {
}
}
// @has basic/index.html
-// @has - '//a/@href' '../basic/struct.ThisType.html'
-// @has - '//a/@href' '../basic/struct.ThisType.html#method.this_method'
-// @has - '//a/@href' '../basic/enum.ThisEnum.html'
-// @has - '//a/@href' '../basic/enum.ThisEnum.html#variant.ThisVariant'
-// @has - '//a/@href' '../basic/trait.ThisTrait.html'
-// @has - '//a/@href' '../basic/trait.ThisTrait.html#tymethod.this_associated_method'
-// @has - '//a/@href' '../basic/trait.ThisTrait.html#associatedtype.ThisAssociatedType'
-// @has - '//a/@href' '../basic/trait.ThisTrait.html#associatedconstant.THIS_ASSOCIATED_CONST'
-// @has - '//a/@href' '../basic/trait.ThisTrait.html'
-// @has - '//a/@href' '../basic/type.ThisAlias.html'
-// @has - '//a/@href' '../basic/union.ThisUnion.html'
-// @has - '//a/@href' '../basic/fn.this_function.html'
-// @has - '//a/@href' '../basic/constant.THIS_CONST.html'
-// @has - '//a/@href' '../basic/static.THIS_STATIC.html'
-// @has - '//a/@href' '../basic/macro.this_macro.html'
-// @has - '//a/@href' '../basic/trait.SoAmbiguous.html'
-// @has - '//a/@href' '../basic/fn.SoAmbiguous.html'
+// @has - '//a/@href' 'struct.ThisType.html'
+// @has - '//a/@href' 'struct.ThisType.html#method.this_method'
+// @has - '//a/@href' 'enum.ThisEnum.html'
+// @has - '//a/@href' 'enum.ThisEnum.html#variant.ThisVariant'
+// @has - '//a/@href' 'trait.ThisTrait.html'
+// @has - '//a/@href' 'trait.ThisTrait.html#tymethod.this_associated_method'
+// @has - '//a/@href' 'trait.ThisTrait.html#associatedtype.ThisAssociatedType'
+// @has - '//a/@href' 'trait.ThisTrait.html#associatedconstant.THIS_ASSOCIATED_CONST'
+// @has - '//a/@href' 'trait.ThisTrait.html'
+// @has - '//a/@href' 'type.ThisAlias.html'
+// @has - '//a/@href' 'union.ThisUnion.html'
+// @has - '//a/@href' 'fn.this_function.html'
+// @has - '//a/@href' 'constant.THIS_CONST.html'
+// @has - '//a/@href' 'static.THIS_STATIC.html'
+// @has - '//a/@href' 'macro.this_macro.html'
+// @has - '//a/@href' 'trait.SoAmbiguous.html'
+// @has - '//a/@href' 'fn.SoAmbiguous.html'
//! In this crate we would like to link to:
//!
//! * [`ThisType`](ThisType)
() => {};
}
-// @has basic/struct.ThisType.html '//a/@href' '../basic/macro.this_macro.html'
+// @has basic/struct.ThisType.html '//a/@href' 'macro.this_macro.html'
/// another link to [`this_macro!()`]
pub struct ThisType;
pub fn SoAmbiguous() {}
-// @has basic/struct.SomeOtherType.html '//a/@href' '../basic/struct.ThisType.html'
-// @has - '//a/@href' '../basic/struct.ThisType.html#method.this_method'
-// @has - '//a/@href' '../basic/enum.ThisEnum.html'
-// @has - '//a/@href' '../basic/enum.ThisEnum.html#variant.ThisVariant'
+// @has basic/struct.SomeOtherType.html '//a/@href' 'struct.ThisType.html'
+// @has - '//a/@href' 'struct.ThisType.html#method.this_method'
+// @has - '//a/@href' 'enum.ThisEnum.html'
+// @has - '//a/@href' 'enum.ThisEnum.html#variant.ThisVariant'
/// Shortcut links for:
/// * [`ThisType`]
/// * [`ThisType::this_method`]
extern crate my_rand;
-// @has 'additional_doc/trait.Rng.html' '//a[@href="../additional_doc/trait.Rng.html"]' 'Rng'
+// @has 'additional_doc/trait.Rng.html' '//a[@href="trait.Rng.html"]' 'Rng'
// @has 'additional_doc/trait.Rng.html' '//a[@href="../my_rand/trait.RngCore.html"]' 'RngCore'
/// This is an [`Rng`].
pub use my_rand::Rng;
extern crate hidden_dep;
-// @has 'hidden/struct.Ready.html' '//a/@href' '../hidden/fn.ready.html'
+// @has 'hidden/struct.Ready.html' '//a/@href' 'fn.ready.html'
pub use hidden_dep::future::{ready, Ready};
// NOTE: we re-exported both `Foo` and `Bar` here,
// NOTE: so they are inlined and therefore we link to the current module.
-// @has 'submodule_outer/trait.Foo.html' '//a[@href="../submodule_outer/bar/trait.Bar.html"]' 'Bar'
-// @has 'submodule_outer/trait.Foo.html' '//a[@href="../submodule_outer/trait.Baz.html"]' 'Baz'
+// @has 'submodule_outer/trait.Foo.html' '//a[@href="bar/trait.Bar.html"]' 'Bar'
+// @has 'submodule_outer/trait.Foo.html' '//a[@href="trait.Baz.html"]' 'Baz'
pub use ::bar_::{Foo, Baz};
// first try backticks
/// Trait: [`trait@Name`], fn: [`fn@Name`], [`Name`][`macro@Name`]
// @has disambiguators_removed/struct.AtDisambiguator.html
-// @has - '//a[@href="../disambiguators_removed/trait.Name.html"][code]' "Name"
-// @has - '//a[@href="../disambiguators_removed/fn.Name.html"][code]' "Name"
-// @has - '//a[@href="../disambiguators_removed/macro.Name.html"][code]' "Name"
+// @has - '//a[@href="trait.Name.html"][code]' "Name"
+// @has - '//a[@href="fn.Name.html"][code]' "Name"
+// @has - '//a[@href="macro.Name.html"][code]' "Name"
pub struct AtDisambiguator;
/// fn: [`Name()`], macro: [`Name!`]
// @has disambiguators_removed/struct.SymbolDisambiguator.html
-// @has - '//a[@href="../disambiguators_removed/fn.Name.html"][code]' "Name()"
-// @has - '//a[@href="../disambiguators_removed/macro.Name.html"][code]' "Name!"
+// @has - '//a[@href="fn.Name.html"][code]' "Name()"
+// @has - '//a[@href="macro.Name.html"][code]' "Name!"
pub struct SymbolDisambiguator;
// Now make sure that backticks aren't added if they weren't already there
/// [fn@Name]
// @has disambiguators_removed/trait.Name.html
-// @has - '//a[@href="../disambiguators_removed/fn.Name.html"]' "Name"
-// @!has - '//a[@href="../disambiguators_removed/fn.Name.html"][code]' "Name"
+// @has - '//a[@href="fn.Name.html"]' "Name"
+// @!has - '//a[@href="fn.Name.html"][code]' "Name"
// FIXME: this will turn !() into ! alone
/// [Name!()]
-// @has - '//a[@href="../disambiguators_removed/macro.Name.html"]' "Name!"
+// @has - '//a[@href="macro.Name.html"]' "Name!"
pub trait Name {}
#[allow(non_snake_case)]
// Try collapsed reference links
/// [macro@Name][]
// @has disambiguators_removed/fn.Name.html
-// @has - '//a[@href="../disambiguators_removed/macro.Name.html"]' "Name"
+// @has - '//a[@href="macro.Name.html"]' "Name"
// Try links that have the same text as a generated URL
-/// Weird URL aligned [../disambiguators_removed/macro.Name.html][trait@Name]
-// @has - '//a[@href="../disambiguators_removed/trait.Name.html"]' "../disambiguators_removed/macro.Name.html"
+/// Weird URL aligned [macro.Name.html][trait@Name]
+// @has - '//a[@href="trait.Name.html"]' "macro.Name.html"
pub fn Name() {}
#[macro_export]
// Rustdoc doesn't currently handle links that have weird interspersing of inline code blocks.
/// [fn@Na`m`e]
// @has disambiguators_removed/macro.Name.html
-// @has - '//a[@href="../disambiguators_removed/fn.Name.html"]' "fn@Name"
+// @has - '//a[@href="fn.Name.html"]' "fn@Name"
// It also doesn't handle any case where the code block isn't the whole link text:
/// [trait@`Name`]
-// @has - '//a[@href="../disambiguators_removed/trait.Name.html"]' "trait@Name"
+// @has - '//a[@href="trait.Name.html"]' "trait@Name"
macro_rules! Name {
() => ()
}
/// I want [Foo::X::y].
pub fn foo() {}
-// @has foo/fn.foo.html '//a/@href' '../foo/enum.Foo.html#variant.X.field.y'
+// @has foo/fn.foo.html '//a/@href' 'enum.Foo.html#variant.X.field.y'
// @has 'extern_type/foreigntype.ExternType.html'
// @has 'extern_type/fn.links_to_extern_type.html' \
-// 'href="../extern_type/foreigntype.ExternType.html#method.f"'
+// 'href="foreigntype.ExternType.html#method.f"'
/// See also [ExternType::f]
pub fn links_to_extern_type() {}
},
}
-// @has foo/enum.Foo.html '//a/@href' '../foo/enum.Foo.html#variant.Bar.field.abc'
+// @has foo/enum.Foo.html '//a/@href' 'enum.Foo.html#variant.Bar.field.abc'
}
pub mod foo {}
-// @has mod_ambiguity/struct.A.html '//a/@href' '../mod_ambiguity/foo/index.html'
+// @has mod_ambiguity/struct.A.html '//a/@href' 'foo/index.html'
/// Module is [`module@foo`]
pub struct A;
-// @has mod_ambiguity/struct.B.html '//a/@href' '../mod_ambiguity/fn.foo.html'
+// @has mod_ambiguity/struct.B.html '//a/@href' 'fn.foo.html'
/// Function is [`fn@foo`]
pub struct B;
pub struct MyString;
/// See also [crate::char] and [mod@char]
-// @has prim_precedence/struct.MyString2.html '//*[@href="../prim_precedence/char/index.html"]' 'crate::char'
-// @has - '//*[@href="../prim_precedence/char/index.html"]' 'mod@char'
+// @has prim_precedence/struct.MyString2.html '//*[@href="char/index.html"]' 'crate::char'
+// @has - '//*[@href="char/index.html"]' 'mod@char'
pub struct MyString2;
// make sure to update `rustdoc-ui/intra-doc/private.rs` if you update this file
/// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x]
-// @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html"]' 'DontDocMe'
-// @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html#method.f"]' 'DontDocMe::f'
-// @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html#structfield.x"]' 'DontDocMe::x'
+// @has private/struct.DocMe.html '//*a[@href="struct.DontDocMe.html"]' 'DontDocMe'
+// @has private/struct.DocMe.html '//*a[@href="struct.DontDocMe.html#method.f"]' 'DontDocMe::f'
+// @has private/struct.DocMe.html '//*a[@href="struct.DontDocMe.html#structfield.x"]' 'DontDocMe::x'
pub struct DocMe;
struct DontDocMe {
x: usize,
use proc_macro_macro::{DeriveB, attr_b};
// @has proc_macro/struct.Foo.html
-// @has - '//a/@href' '../proc_macro/derive.DeriveA.html'
-// @has - '//a/@href' '../proc_macro/attr.attr_a.html'
-// @has - '//a/@href' '../proc_macro/trait.DeriveTrait.html'
+// @has - '//a/@href' 'derive.DeriveA.html'
+// @has - '//a/@href' 'attr.attr_a.html'
+// @has - '//a/@href' 'trait.DeriveTrait.html'
// @has - '//a/@href' '../proc_macro_macro/derive.DeriveB.html'
// @has - '//a/@href' '../proc_macro_macro/attr.attr_b.html'
/// Link to [DeriveA], [attr_a], [DeriveB], [attr_b], [DeriveTrait]
pub struct Foo;
// @has proc_macro/struct.Bar.html
-// @has - '//a/@href' '../proc_macro/derive.DeriveA.html'
-// @has - '//a/@href' '../proc_macro/attr.attr_a.html'
+// @has - '//a/@href' 'derive.DeriveA.html'
+// @has - '//a/@href' 'attr.attr_a.html'
/// Link to [deriveA](derive@DeriveA) [attr](macro@attr_a)
pub struct Bar;
// @has outer/index.html
// @ has - '//a[@href="https://doc.rust-lang.org/nightly/std/env/fn.var.html"]' "std::env"
-// @ has - '//a[@href="../outer/fn.f.html"]' "g"
+// @ has - '//a[@href="fn.f.html"]' "g"
pub use f as g;
// FIXME: same as above
impl S {
/// See [Self::b].
// @has raw_ident_self/impl/struct.S.html
- // @has - '//a[@href="../../raw_ident_self/impl/struct.S.html#method.b"]' 'Self::b'
+ // @has - '//a[@href="struct.S.html#method.b"]' 'Self::b'
pub fn a() {}
pub fn b() {}
#![crate_name = "foo"]
extern crate inner;
-// @has foo/struct.Inner.html '//a[@href="../foo/fn.with_code.html"]' 'crate::with_code'
+// @has foo/struct.Inner.html '//a[@href="fn.with_code.html"]' 'crate::with_code'
/// [crate::with_code]
-// @has - '//a[@href="../foo/fn.with_code.html"]' 'different text'
+// @has - '//a[@href="fn.with_code.html"]' 'different text'
/// [different text][with_code]
-// @has - '//a[@href="../foo/fn.me_too.html"]' 'me_too'
+// @has - '//a[@href="fn.me_too.html"]' 'me_too'
#[doc = "[me_too]"]
-// @has - '//a[@href="../foo/fn.me_three.html"]' 'reference link'
+// @has - '//a[@href="fn.me_three.html"]' 'reference link'
/// This [reference link]
#[doc = "has an attr in the way"]
///
#![crate_name = "foo"]
-// @has foo/index.html '//a/@href' '../foo/struct.Foo.html#method.new'
-// @has foo/struct.Foo.html '//a/@href' '../foo/struct.Foo.html#method.new'
+// @has foo/index.html '//a/@href' 'struct.Foo.html#method.new'
+// @has foo/struct.Foo.html '//a/@href' 'struct.Foo.html#method.new'
/// Use [`new`] to create a new instance.
///
}
}
-// @has foo/index.html '//a/@href' '../foo/struct.Bar.html#method.new2'
-// @has foo/struct.Bar.html '//a/@href' '../foo/struct.Bar.html#method.new2'
+// @has foo/index.html '//a/@href' 'struct.Bar.html#method.new2'
+// @has foo/struct.Bar.html '//a/@href' 'struct.Bar.html#method.new2'
/// Use [`new2`] to create a new instance.
///
}
pub struct MyStruct {
- // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#structfield.struct_field'
+ // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#structfield.struct_field'
/// [`struct_field`]
///
}
pub enum MyEnum {
- // @has foo/enum.MyEnum.html '//a/@href' '../foo/enum.MyEnum.html#variant.EnumVariant'
+ // @has foo/enum.MyEnum.html '//a/@href' 'enum.MyEnum.html#variant.EnumVariant'
/// [`EnumVariant`]
///
}
pub union MyUnion {
- // @has foo/union.MyUnion.html '//a/@href' '../foo/union.MyUnion.html#structfield.union_field'
+ // @has foo/union.MyUnion.html '//a/@href' 'union.MyUnion.html#structfield.union_field'
/// [`union_field`]
///
}
pub trait MyTrait {
- // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#associatedtype.AssoType'
+ // @has foo/trait.MyTrait.html '//a/@href' 'trait.MyTrait.html#associatedtype.AssoType'
/// [`AssoType`]
///
/// [`AssoType`]: Self::AssoType
type AssoType;
- // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#associatedconstant.ASSO_CONST'
+ // @has foo/trait.MyTrait.html '//a/@href' 'trait.MyTrait.html#associatedconstant.ASSO_CONST'
/// [`ASSO_CONST`]
///
/// [`ASSO_CONST`]: Self::ASSO_CONST
const ASSO_CONST: i32 = 1;
- // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#method.asso_fn'
+ // @has foo/trait.MyTrait.html '//a/@href' 'trait.MyTrait.html#method.asso_fn'
/// [`asso_fn`]
///
}
impl MyStruct {
- // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.for_impl'
+ // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#method.for_impl'
/// [`for_impl`]
///
}
impl MyTrait for MyStruct {
- // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedtype.AssoType'
+ // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedtype.AssoType'
/// [`AssoType`]
///
/// [`AssoType`]: Self::AssoType
type AssoType = u32;
- // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedconstant.ASSO_CONST'
+ // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedconstant.ASSO_CONST'
/// [`ASSO_CONST`]
///
/// [`ASSO_CONST`]: Self::ASSO_CONST
const ASSO_CONST: i32 = 10;
- // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.asso_fn'
+ // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#method.asso_fn'
/// [`asso_fn`]
///
impl MyTrait for MyStruct {
-// @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedtype.AssoType'
+// @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedtype.AssoType'
/// [`AssoType`]
///
/// [`AssoType`]: MyStruct::AssoType
type AssoType = u32;
-// @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedconstant.ASSO_CONST'
+// @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedconstant.ASSO_CONST'
/// [`ASSO_CONST`]
///
/// [`ASSO_CONST`]: MyStruct::ASSO_CONST
const ASSO_CONST: i32 = 10;
-// @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.trait_fn'
+// @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#method.trait_fn'
/// [`trait_fn`]
///
/// Link to [S::assoc_fn()]
/// Link to [Default::default()]
-// @has trait_item/struct.S.html '//*[@href="../trait_item/struct.S.html#method.assoc_fn"]' 'S::assoc_fn()'
+// @has trait_item/struct.S.html '//*[@href="struct.S.html#method.assoc_fn"]' 'S::assoc_fn()'
// @has - '//*[@href="https://doc.rust-lang.org/nightly/core/default/trait.Default.html#tymethod.default"]' 'Default::default()'
pub struct S;
#![crate_name = "foo"]
-// @has foo/enum.E1.html '//a/@href' '../foo/enum.E1.html#variant.A'
+// @has foo/enum.E1.html '//a/@href' 'enum.E1.html#variant.A'
/// [Self::A::b]
pub enum E1 {
A { b: usize }
}
-// @has foo/enum.E2.html '//a/@href' '../foo/enum.E2.html#variant.A'
+// @has foo/enum.E2.html '//a/@href' 'enum.E2.html#variant.A'
/// [Self::A::b]
pub enum E2 {
pub trait Blah { }
// @count issue_21474/struct.What.html \
-// '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
+// '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1
pub struct What;
}
impl Bar for Foo {
- // @has - '//*[@href="../issue_28478/trait.Bar.html#associatedtype.Bar"]' 'Bar'
- // @has - '//*[@href="../issue_28478/trait.Bar.html#associatedconstant.Baz"]' 'Baz'
- // @has - '//*[@href="../issue_28478/trait.Bar.html#tymethod.bar"]' 'bar'
+ // @has - '//*[@href="trait.Bar.html#associatedtype.Bar"]' 'Bar'
+ // @has - '//*[@href="trait.Bar.html#associatedconstant.Baz"]' 'Baz'
+ // @has - '//*[@href="trait.Bar.html#tymethod.bar"]' 'bar'
fn bar() {}
- // @has - '//*[@href="../issue_28478/trait.Bar.html#method.baz"]' 'baz'
+ // @has - '//*[@href="trait.Bar.html#method.baz"]' 'baz'
}
fn my_string(&self) -> String;
}
-// @has - "//div[@id='implementors-list']/h3[@id='impl-MyTrait']//code" "impl<T> MyTrait for T where T: Debug"
+// @has - "//div[@id='implementors-list']//h3[@id='impl-MyTrait']//code" "impl<T> MyTrait for T where T: Debug"
impl<T> MyTrait for T where T: fmt::Debug {
fn my_string(&self) -> String {
format!("{:?}", self)
// @has 'foo/struct.Foo1.html'
pub struct Foo1;
-// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
+// @count - '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1
// @has - '//*[@class="impl"]' "impl Bar<Foo1, &'static Foo1> for Foo1"
impl Bar<Foo1, &'static Foo1> for Foo1 {}
// @has 'foo/struct.Foo2.html'
pub struct Foo2;
-// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
+// @count - '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1
// @has - '//*[@class="impl"]' "impl Bar<&'static Foo2, Foo2> for u8"
impl Bar<&'static Foo2, Foo2> for u8 {}
// @has issue_50159/struct.Switch.html
// @has - '//code' 'impl<B> Send for Switch<B> where <B as Signal>::Item: Send'
// @has - '//code' 'impl<B> Sync for Switch<B> where <B as Signal>::Item: Sync'
-// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 0
-// @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 5
+// @count - '//*[@id="implementations-list"]//*[@class="impl"]' 0
+// @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 5
pub struct Switch<B: Signal> {
pub inner: <B as Signal2>::Item2,
}
}
// @has issue_51236/struct.Owned.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<T> Send for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<T> Send for \
// Owned<T> where <T as Owned<'static>>::Reader: Send"
pub struct Owned<T> where T: for<'a> ::traits::Owned<'a> {
marker: PhantomData<<T as ::traits::Owned<'static>>::Reader>,
}
}
-// @has issue_53812/trait.MyIterator.html '//*[@id="implementors-list"]//h3[1]' 'MyStruct<[T; 0]>'
-// @has - '//*[@id="implementors-list"]//h3[2]' 'MyStruct<[T; 1]>'
-// @has - '//*[@id="implementors-list"]//h3[3]' 'MyStruct<[T; 2]>'
-// @has - '//*[@id="implementors-list"]//h3[4]' 'MyStruct<[T; 3]>'
-// @has - '//*[@id="implementors-list"]//h3[5]' 'MyStruct<[T; 10]>'
+// @has issue_53812/trait.MyIterator.html '//*[@id="implementors-list"]/h3[1]' 'MyStruct<[T; 0]>'
+// @has - '//*[@id="implementors-list"]/h3[2]' 'MyStruct<[T; 1]>'
+// @has - '//*[@id="implementors-list"]/h3[3]' 'MyStruct<[T; 2]>'
+// @has - '//*[@id="implementors-list"]/h3[4]' 'MyStruct<[T; 3]>'
+// @has - '//*[@id="implementors-list"]/h3[5]' 'MyStruct<[T; 10]>'
array_impls! { 10 3 2 1 0 }
// @has issue_54705/struct.ScopeFutureContents.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'scope, S> \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'scope, S> \
// Send for ScopeFutureContents<'scope, S> where S: Sync"
//
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'scope, S> \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'scope, S> \
// Sync for ScopeFutureContents<'scope, S> where S: Sync"
pub struct ScopeFutureContents<'scope, S>
where S: ScopeHandle<'scope>,
#![feature(negative_impls)]
// @has issue_55321/struct.A.html
-// @has - '//*[@id="trait-implementations-list"]/*[@class="impl"]//code' "impl !Send for A"
-// @has - '//*[@id="trait-implementations-list"]/*[@class="impl"]//code' "impl !Sync for A"
+// @has - '//*[@id="trait-implementations-list"]//*[@class="impl"]//code' "impl !Send for A"
+// @has - '//*[@id="trait-implementations-list"]//*[@class="impl"]//code' "impl !Sync for A"
pub struct A();
impl !Send for A {}
impl !Sync for A {}
// @has issue_55321/struct.B.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<T> !Send for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<T> !Send for \
// B<T>"
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<T> !Sync for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<T> !Sync for \
// B<T>"
pub struct B<T: ?Sized>(A, Box<T>);
// @has issue_55364/subone/index.html
// These foo/bar links in the module's documentation should refer inside `subone`
-// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.foo.html"]' 'foo'
-// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.bar.html"]' 'bar'
+// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
+// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
pub mod subone {
//! See either [foo] or [bar].
// This should refer to subone's `bar`
// @has issue_55364/subone/fn.foo.html
- // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.bar.html"]' 'bar'
+ // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
/// See [bar]
pub fn foo() {}
// This should refer to subone's `foo`
// @has issue_55364/subone/fn.bar.html
- // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.foo.html"]' 'foo'
+ // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
/// See [foo]
pub fn bar() {}
}
// @has issue_55364/subtwo/index.html
// These foo/bar links in the module's documentation should not reference inside `subtwo`
-// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.foo.html"]' 'foo'
-// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.bar.html"]' 'bar'
+// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
+// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
// Instead it should be referencing the top level functions
-// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.foo.html"]' 'foo'
-// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.bar.html"]' 'bar'
+// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo'
+// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
// Though there should be such links later
// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td/a[@class="fn"][@href="fn.foo.html"]' 'foo'
// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td/a[@class="fn"][@href="fn.bar.html"]' 'bar'
// Despite the module's docs referring to the top level foo/bar,
// this should refer to subtwo's `bar`
// @has issue_55364/subtwo/fn.foo.html
- // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.bar.html"]' 'bar'
+ // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
/// See [bar]
pub fn foo() {}
// Despite the module's docs referring to the top level foo/bar,
// this should refer to subtwo's `foo`
// @has issue_55364/subtwo/fn.bar.html
- // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.foo.html"]' 'foo'
+ // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
/// See [foo]
pub fn bar() {}
}
// @has issue_55364/subthree/index.html
// This module should also refer to the top level foo/bar
-// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.foo.html"]' 'foo'
-// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.bar.html"]' 'bar'
+// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo'
+// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
pub mod subthree {
//! See either [foo][super::foo] or [bar][super::bar]
}
// Next we go *deeper* - In order to ensure it's not just "this or parent"
// we test `crate::` and a `super::super::...` chain
// @has issue_55364/subfour/subfive/subsix/subseven/subeight/index.html
-// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../../issue_55364/subone/fn.foo.html"]' 'other foo'
-// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../../issue_55364/subtwo/fn.bar.html"]' 'other bar'
+// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../subone/fn.foo.html"]' 'other foo'
+// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../subtwo/fn.bar.html"]' 'other bar'
pub mod subfour {
pub mod subfive {
pub mod subsix {
}
// @has issue_56822/struct.Parser.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'a> Send for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'a> Send for \
// Parser<'a>"
pub struct Parser<'a> {
field: <Wrapper<Inner<'a, u8>> as MyTrait>::Output
{}
// @has issue_60726/struct.IntoIter.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<T> !Send for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<T> !Send for \
// IntoIter<T>"
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<T> !Sync for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<T> !Sync for \
// IntoIter<T>"
pub struct IntoIter<T>{
hello:DynTrait<FooInterface<T>>,
}
impl Default for Body {
- // @has foo/struct.Body.html '//a/@href' '../foo/struct.Body.html#method.empty'
+ // @has foo/struct.Body.html '//a/@href' 'struct.Body.html#method.empty'
/// Returns [`Body::empty()`](Body::empty).
fn default() -> Body {
--- /dev/null
+#![allow(unused)]
+
+// @has 'item_hide_threshold/struct.PubStruct.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0
+pub struct PubStruct {
+ pub a: usize,
+ pub b: usize,
+}
+
+// @has 'item_hide_threshold/struct.BigPubStruct.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
+// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show fields'
+pub struct BigPubStruct {
+ pub a: usize,
+ pub b: usize,
+ pub c: usize,
+ pub d: usize,
+ pub e: usize,
+ pub f: usize,
+ pub g: usize,
+ pub h: usize,
+ pub i: usize,
+ pub j: usize,
+ pub k: usize,
+ pub l: usize,
+ pub m: usize,
+}
+
+// @has 'item_hide_threshold/union.BigUnion.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
+// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show fields'
+pub union BigUnion {
+ pub a: usize,
+ pub b: usize,
+ pub c: usize,
+ pub d: usize,
+ pub e: usize,
+ pub f: usize,
+ pub g: usize,
+ pub h: usize,
+ pub i: usize,
+ pub j: usize,
+ pub k: usize,
+ pub l: usize,
+ pub m: usize,
+}
+
+// @has 'item_hide_threshold/union.Union.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0
+pub union Union {
+ pub a: usize,
+ pub b: usize,
+ pub c: usize,
+}
+
+// @has 'item_hide_threshold/struct.PrivStruct.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0
+// @has - '//div[@class="docblock type-decl"]' 'fields omitted'
+pub struct PrivStruct {
+ a: usize,
+ b: usize,
+}
+
+// @has 'item_hide_threshold/enum.Enum.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
+// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show fields'
+pub enum Enum {
+ A, B, C,
+ D {
+ a: u8,
+ b: u8
+ }
+}
+
+// @has 'item_hide_threshold/enum.LargeEnum.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
+// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show variants'
+pub enum LargeEnum {
+ A, B, C, D, E, F(u8), G, H, I, J, K, L, M
+}
+
+// @has 'item_hide_threshold/trait.Trait.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0
+pub trait Trait {
+ type A;
+ #[must_use]
+ fn foo();
+ fn bar();
+}
+
+// @has 'item_hide_threshold/trait.GinormousTrait.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
+// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show associated items'
+pub trait GinormousTrait {
+ type A;
+ type B;
+ type C;
+ type D;
+ type E;
+ type F;
+ type G;
+ type H;
+ type I;
+ type J;
+ type K;
+ type L;
+ type M;
+ const N: usize = 1;
+ #[must_use]
+ fn foo();
+ fn bar();
+}
+
+// @has 'item_hide_threshold/trait.HugeTrait.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
+// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show associated constants and methods'
+pub trait HugeTrait {
+ type A;
+ const M: usize = 1;
+ const N: usize = 1;
+ const O: usize = 1;
+ const P: usize = 1;
+ const Q: usize = 1;
+ const R: usize = 1;
+ const S: usize = 1;
+ const T: usize = 1;
+ const U: usize = 1;
+ const V: usize = 1;
+ const W: usize = 1;
+ const X: usize = 1;
+ #[must_use]
+ fn foo();
+ fn bar();
+}
+
+// @has 'item_hide_threshold/trait.BigTrait.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
+// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show methods'
+pub trait BigTrait {
+ type A;
+ #[must_use]
+ fn foo();
+ fn bar();
+ fn baz();
+ fn quux();
+ fn frob();
+ fn greeble();
+ fn blap();
+ fn whoop();
+ fn pow();
+ fn bang();
+ fn oomph();
+ fn argh();
+ fn wap();
+ fn ouch();
+}
#![crate_name = "foo"]
-// @has foo/index.html '//a[@href="../foo/foo/constant.FIRSTCONST.html"]' 'foo::FIRSTCONST'
-// @has foo/index.html '//a[@href="../foo/struct.Bar.html#associatedconstant.CONST"]' 'Bar::CONST'
+// @has foo/index.html '//a[@href="foo/constant.FIRSTCONST.html"]' 'foo::FIRSTCONST'
+// @has foo/index.html '//a[@href="struct.Bar.html#associatedconstant.CONST"]' 'Bar::CONST'
//! We have here [`foo::FIRSTCONST`] and [`Bar::CONST`].
+++ /dev/null
-#![crate_name = "foo"]
-
-// @has foo/primitive.u8.html '//head/title' 'u8 - Rust'
-// @!has - '//head/title' 'foo'
-#[doc(primitive = "u8")]
-/// `u8` docs
-mod u8 {}
// @has some_macros/foo/index.html
mod foo {
// @has - '//code' 'pub use some_proc_macro;'
- // @has - '//a/@href' '../../some_macros/macro.some_proc_macro.html'
+ // @has - '//a/@href' '../macro.some_proc_macro.html'
pub use some_proc_macro;
// @has - '//code' 'pub use some_proc_attr;'
- // @has - '//a/@href' '../../some_macros/attr.some_proc_attr.html'
+ // @has - '//a/@href' '../attr.some_proc_attr.html'
pub use some_proc_attr;
// @has - '//code' 'pub use some_derive;'
- // @has - '//a/@href' '../../some_macros/derive.SomeDerive.html'
+ // @has - '//a/@href' '../derive.SomeDerive.html'
pub use some_derive;
}
///
/// [name]: mod
/// [other name]: crate::internal::mod
- // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="../../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name'
- // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="../../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'other name'
+ // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="struct.mod.html"]' 'name'
+ // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="struct.mod.html"]' 'other name'
pub struct B;
}
/// See [name].
///
/// [name]: internal::mod
-// @has 'raw_ident_eliminate_r_hashtag/struct.A.html' '//*a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name'
+// @has 'raw_ident_eliminate_r_hashtag/struct.A.html' '//*a[@href="internal/struct.mod.html"]' 'name'
pub struct A;
--- /dev/null
+#![crate_name = "foo"]
+#![feature(doc_cfg)]
+
+pub mod tag {
+ #[deprecated(since = "0.1.8", note = "Use bar() instead")]
+ pub trait Deprecated {}
+
+ #[doc(cfg(feature = "sync"))]
+ pub trait Portability {}
+
+ #[deprecated(since = "0.1.8", note = "Use bar() instead")]
+ #[doc(cfg(feature = "sync"))]
+ pub trait Both {}
+
+ pub trait None {}
+}
+
+// @has foo/mod1/index.html
+pub mod mod1 {
+ // @has - '//code' 'pub use tag::Deprecated;'
+ // @has - '//span' 'Deprecated'
+ // @!has - '//span' 'sync'
+ pub use tag::Deprecated;
+}
+
+// @has foo/mod2/index.html
+pub mod mod2 {
+ // @has - '//code' 'pub use tag::Portability;'
+ // @!has - '//span' 'Deprecated'
+ // @has - '//span' 'sync'
+ pub use tag::Portability;
+}
+
+// @has foo/mod3/index.html
+pub mod mod3 {
+ // @has - '//code' 'pub use tag::Both;'
+ // @has - '//span' 'Deprecated'
+ // @has - '//span' 'sync'
+ pub use tag::Both;
+}
+
+// @has foo/mod4/index.html
+pub mod mod4 {
+ // @has - '//code' 'pub use tag::None;'
+ // @!has - '//span' 'Deprecated'
+ // @!has - '//span' 'sync'
+ pub use tag::None;
+}
--- /dev/null
+#![crate_name = "foo"]
+#![feature(doc_cfg)]
+#![feature(staged_api)]
+#![stable(feature = "rust1", since = "1.0.0")]
+
+#[stable(feature = "rust1", since = "1.0.0")]
+pub mod tag {
+ #[unstable(feature = "humans", issue = "none")]
+ pub trait Unstable {}
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[doc(cfg(feature = "sync"))]
+ pub trait Portability {}
+
+ #[unstable(feature = "humans", issue = "none")]
+ #[doc(cfg(feature = "sync"))]
+ pub trait Both {}
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub trait None {}
+}
+
+// @has foo/mod1/index.html
+#[stable(feature = "rust1", since = "1.0.0")]
+pub mod mod1 {
+ // @has - '//code' 'pub use tag::Unstable;'
+ // @has - '//span' 'Experimental'
+ // @!has - '//span' 'sync'
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub use tag::Unstable;
+}
+
+// @has foo/mod2/index.html
+#[stable(feature = "rust1", since = "1.0.0")]
+pub mod mod2 {
+ // @has - '//code' 'pub use tag::Portability;'
+ // @!has - '//span' 'Experimental'
+ // @has - '//span' 'sync'
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub use tag::Portability;
+}
+
+// @has foo/mod3/index.html
+#[stable(feature = "rust1", since = "1.0.0")]
+pub mod mod3 {
+ // @has - '//code' 'pub use tag::Both;'
+ // @has - '//span' 'Experimental'
+ // @has - '//span' 'sync'
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub use tag::Both;
+}
+
+// @has foo/mod4/index.html
+#[stable(feature = "rust1", since = "1.0.0")]
+pub mod mod4 {
+ // @has - '//code' 'pub use tag::None;'
+ // @!has - '//span' 'Experimental'
+ // @!has - '//span' 'sync'
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub use tag::None;
+}
#![crate_name = "foo"]
-// @has foo/index.html '//*[@class="docblock"]/p/a[@href="../foo/struct.Foo.html#structfield.bar"]' 'Foo::bar'
-// @has foo/index.html '//*[@class="docblock"]/p/a[@href="../foo/union.Bar.html#structfield.foo"]' 'Bar::foo'
-// @has foo/index.html '//*[@class="docblock"]/p/a[@href="../foo/enum.Uniooon.html#variant.X"]' 'Uniooon::X'
+// @has foo/index.html '//*[@class="docblock"]/p/a[@href="struct.Foo.html#structfield.bar"]' 'Foo::bar'
+// @has foo/index.html '//*[@class="docblock"]/p/a[@href="union.Bar.html#structfield.foo"]' 'Bar::foo'
+// @has foo/index.html '//*[@class="docblock"]/p/a[@href="enum.Uniooon.html#variant.X"]' 'Uniooon::X'
//! Test with [Foo::bar], [Bar::foo], [Uniooon::X]
// @has basic/struct.Foo.html
// @has - '//code' 'impl<T> Send for Foo<T> where T: Send'
// @has - '//code' 'impl<T> Sync for Foo<T> where T: Sync'
-// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 0
-// @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 5
+// @count - '//*[@id="implementations-list"]//*[@class="impl"]' 0
+// @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 5
pub struct Foo<T> {
field: T,
}
}
// @has complex/struct.NotOuter.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'a, T, K: \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'a, T, K: \
// ?Sized> Send for Outer<'a, T, K> where K: for<'b> Fn((&'b bool, &'a u8)) \
// -> &'b i8, T: MyTrait<'a>, <T as MyTrait<'a>>::MyItem: Copy, 'a: 'static"
{}
// @has lifetimes/struct.Foo.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'c, K> Send \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'c, K> Send \
// for Foo<'c, K> where K: for<'b> Fn(&'b bool) -> &'c u8, 'c: 'static"
//
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'c, K> Sync \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'c, K> Sync \
// for Foo<'c, K> where K: Sync"
pub struct Foo<'c, K: 'c> {
inner_field: Inner<'c, K>,
// @has manual/struct.Foo.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' 'impl<T> Sync for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' 'impl<T> Sync for \
// Foo<T> where T: Sync'
//
-// @has - '//*[@id="trait-implementations-list"]/*[@class="impl"]//code' \
+// @has - '//*[@id="trait-implementations-list"]//*[@class="impl"]//code' \
// 'impl<T> Send for Foo<T>'
//
-// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
-// @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 4
+// @count - '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1
+// @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 4
pub struct Foo<T> {
field: T,
}
}
// @has negative/struct.Outer.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<T> !Send for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<T> !Send for \
// Outer<T>"
//
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<T> \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<T> \
// !Sync for Outer<T>"
pub struct Outer<T: Copy> {
inner_field: Inner<T>,
}
// @has nested/struct.Foo.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' 'impl<T> Send for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' 'impl<T> Send for \
// Foo<T> where T: Copy'
//
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' \
// 'impl<T> Sync for Foo<T> where T: Sync'
pub struct Foo<T> {
inner_field: Inner<T>,
}
// @has no_redundancy/struct.Outer.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<T> Send for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<T> Send for \
// Outer<T> where T: Copy + Send"
pub struct Outer<T> {
inner_field: Inner<T>,
}
// @has project/struct.Foo.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'c, K> Send \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'c, K> Send \
// for Foo<'c, K> where K: MyTrait<MyItem = bool>, 'c: 'static"
//
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'c, K> Sync \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'c, K> Sync \
// for Foo<'c, K> where K: MyTrait, <K as MyTrait>::MyItem: OtherTrait, 'c: 'static,"
pub struct Foo<'c, K: 'c> {
inner_field: Inner<'c, K>,
// @has self_referential/struct.WriteAndThen.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<P1> Send for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<P1> Send for \
// WriteAndThen<P1> where <P1 as Pattern>::Value: Send"
pub struct WriteAndThen<P1>(pub P1::Value,pub <Constrain<P1, Wrapper<P1::Value>> as Pattern>::Value)
where P1: Pattern;
}
// @has static_region/struct.Owned.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<T> Send for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<T> Send for \
// Owned<T> where <T as OwnedTrait<'static>>::Reader: Send"
pub struct Owned<T> where T: OwnedTrait<'static> {
marker: <T as OwnedTrait<'static>>::Reader,
--- /dev/null
+#![crate_name = "foo"]
+#![feature(doc_keyword)]
+
+// tests for the html <title> element
+
+// @has foo/index.html '//head/title' 'foo - Rust'
+
+// @has foo/fn.widget_count.html '//head/title' 'widget_count in foo - Rust'
+/// blah
+pub fn widget_count() {}
+
+// @has foo/struct.Widget.html '//head/title' 'Widget in foo - Rust'
+pub struct Widget;
+
+// @has foo/constant.ANSWER.html '//head/title' 'ANSWER in foo - Rust'
+pub const ANSWER: u8 = 42;
+
+// @has foo/blah/index.html '//head/title' 'foo::blah - Rust'
+pub mod blah {
+ // @has foo/blah/struct.Widget.html '//head/title' 'Widget in foo::blah - Rust'
+ pub struct Widget;
+
+ // @has foo/blah/trait.Awesome.html '//head/title' 'Awesome in foo::blah - Rust'
+ pub trait Awesome {}
+
+ // @has foo/blah/fn.make_widget.html '//head/title' 'make_widget in foo::blah - Rust'
+ pub fn make_widget() {}
+
+ // @has foo/macro.cool_macro.html '//head/title' 'cool_macro in foo - Rust'
+ #[macro_export]
+ macro_rules! cool_macro {
+ ($t:tt) => { $t }
+ }
+}
+
+// @has foo/keyword.continue.html '//head/title' 'continue - Rust'
+#[doc(keyword = "continue")]
+mod continue_keyword {}
+
+// @has foo/primitive.u8.html '//head/title' 'u8 - Rust'
+// @!has - '//head/title' 'foo'
+#[doc(primitive = "u8")]
+/// `u8` docs
+mod u8 {}
pub trait Foo {
- // @has foo/trait.Foo.html '//h3[@id="tymethod.foo"]//span[@class="docblock attributes"]' '#[must_use]'
+ // @has foo/trait.Foo.html '//h3[@id="tymethod.foo"]//div[@class="code-attribute"]' '#[must_use]'
#[must_use]
fn foo();
}
pub struct Bar;
impl Bar {
- // @has foo/struct.Bar.html '//h4[@id="method.bar"]//span[@class="docblock attributes"]' '#[must_use]'
+ // @has foo/struct.Bar.html '//h4[@id="method.bar"]//div[@class="code-attribute"]' '#[must_use]'
#[must_use]
pub fn bar() {}
- // @has foo/struct.Bar.html '//h4[@id="method.bar2"]//span[@class="docblock attributes"]' '#[must_use]'
+ // @has foo/struct.Bar.html '//h4[@id="method.bar2"]//div[@class="code-attribute"]' '#[must_use]'
#[must_use]
pub fn bar2() {}
}
impl MyTrait for MyStruct {
// @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-3"]//a[@class="type"]/@href' #associatedtype.Assoc
// @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-3"]//a[@class="anchor"]/@href' #associatedtype.Assoc-3
- // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedtype.Assoc"]//a[@class="type"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#associatedtype.Assoc
+ // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedtype.Assoc"]//a[@class="type"]/@href' trait.MyTrait.html#associatedtype.Assoc
// @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedtype.Assoc"]//a[@class="anchor"]/@href' #associatedtype.Assoc
type Assoc = bool;
// @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-3"]//a[@class="constant"]/@href' #associatedconstant.VALUE
// @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-3"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-3
- // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedconstant.VALUE"]//a[@class="constant"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#associatedconstant.VALUE
+ // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedconstant.VALUE"]//a[@class="constant"]/@href' trait.MyTrait.html#associatedconstant.VALUE
// @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedconstant.VALUE"]//a[@class="anchor"]/@href' #associatedconstant.VALUE
const VALUE: u32 = 20;
// @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function-2"]//a[@class="fnname"]/@href' #tymethod.trait_function
// @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function-2"]//a[@class="anchor"]/@href' #method.trait_function-2
- // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.trait_function"]//a[@class="fnname"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#tymethod.trait_function
+ // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.trait_function"]//a[@class="fnname"]/@href' trait.MyTrait.html#tymethod.trait_function
// @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.trait_function"]//a[@class="anchor"]/@href' #method.trait_function
fn trait_function(&self) {}
// @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-3"]//a[@class="fnname"]/@href' #method.defaulted_override
// @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-3"]//a[@class="anchor"]/@href' #method.defaulted_override-3
- // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted_override"]//a[@class="fnname"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#method.defaulted_override
+ // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted_override"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted_override
// @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted_override"]//a[@class="anchor"]/@href' #method.defaulted_override
fn defaulted_override(&self) {}
- // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted"]//a[@class="fnname"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#method.defaulted
+ // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted
// @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted"]//a[@class="anchor"]/@href' #method.defaulted
}
-// @has trait_self_link/trait.Foo.html //a/@href ../trait_self_link/trait.Foo.html
+// @has trait_self_link/trait.Foo.html //a/@href trait.Foo.html
pub trait Foo {}
pub struct Bar;
fn test() {
let v: isize;
- //~^ HELP make this binding mutable
+ //~^ HELP consider making this binding mutable
//~| SUGGESTION mut v
v = 1; //~ NOTE first assignment
println!("v={}", v);
--> $DIR/assign-imm-local-twice.rs:7:5
|
LL | let v: isize;
- | - help: make this binding mutable: `mut v`
+ | - help: consider making this binding mutable: `mut v`
...
LL | v = 1;
| ----- first assignment to `v`
--- /dev/null
+// check-pass
+
+#![allow(incomplete_features)]
+#![feature(associated_type_bounds)]
+#![feature(generic_associated_types)]
+
+trait MP {
+ type T<'a>;
+}
+struct S(String);
+impl MP for S {
+ type T<'a> = &'a str;
+}
+
+trait SR: MP {
+ fn sr<IM>(&self) -> i32
+ where
+ for<'a> IM: T<T: U<<Self as MP>::T<'a>>>;
+}
+
+trait T {
+ type T;
+}
+trait U<X> {}
+
+fn main() {}
--- /dev/null
+// check-pass
+
+#![feature(associated_type_bounds)]
+
+trait A<'a, 'b> {}
+
+trait B<'a, 'b, 'c> {}
+
+fn err<'u, 'a, F>()
+where
+ for<'b> F: Iterator<Item: for<'c> B<'a, 'b, 'c> + for<'c> A<'a, 'c>>,
+{
+}
+
+fn main() {}
--- /dev/null
+#![feature(associated_type_bounds)]
+
+trait TraitA<'a> {
+ type AsA;
+}
+
+trait TraitB<'a, 'b> {
+ type AsB;
+}
+
+trait TraitC<'a, 'b, 'c> {}
+
+struct X;
+
+impl<'a, 'b, 'c> TraitC<'a, 'b, 'c> for X {}
+
+struct Y;
+
+impl<'a, 'b> TraitB<'a, 'b> for Y {
+ type AsB = X;
+}
+
+struct Z;
+
+impl<'a> TraitA<'a> for Z {
+ type AsA = Y;
+}
+
+fn foo<T>()
+where
+ for<'a> T: TraitA<'a, AsA: for<'b> TraitB<'a, 'b, AsB: for<'c> TraitC<'a, 'b, 'c>>>,
+{
+}
+
+fn main() {
+ foo::<Z>();
+ //~^ ERROR: the trait bound `for<'a, 'b> <Z as TraitA<'a>>::AsA: TraitB<'a, 'b>` is not satisfied
+ //~| ERROR: the trait bound `for<'a, 'b, 'c> <<Z as TraitA<'a>>::AsA as TraitB<'a, 'b>>::AsB: TraitC<'a, 'b, 'c>` is not satisfied
+}
--- /dev/null
+error[E0277]: the trait bound `for<'a, 'b> <Z as TraitA<'a>>::AsA: TraitB<'a, 'b>` is not satisfied
+ --> $DIR/issue-83017.rs:36:5
+ |
+LL | fn foo<T>()
+ | --- required by a bound in this
+LL | where
+LL | for<'a> T: TraitA<'a, AsA: for<'b> TraitB<'a, 'b, AsB: for<'c> TraitC<'a, 'b, 'c>>>,
+ | ------------------------------------------------------- required by this bound in `foo`
+...
+LL | foo::<Z>();
+ | ^^^^^^^^ the trait `for<'a, 'b> TraitB<'a, 'b>` is not implemented for `<Z as TraitA<'a>>::AsA`
+
+error[E0277]: the trait bound `for<'a, 'b, 'c> <<Z as TraitA<'a>>::AsA as TraitB<'a, 'b>>::AsB: TraitC<'a, 'b, 'c>` is not satisfied
+ --> $DIR/issue-83017.rs:36:5
+ |
+LL | fn foo<T>()
+ | --- required by a bound in this
+LL | where
+LL | for<'a> T: TraitA<'a, AsA: for<'b> TraitB<'a, 'b, AsB: for<'c> TraitC<'a, 'b, 'c>>>,
+ | -------------------------- required by this bound in `foo`
+...
+LL | foo::<Z>();
+ | ^^^^^^^^ the trait `for<'a, 'b, 'c> TraitC<'a, 'b, 'c>` is not implemented for `<<Z as TraitA<'a>>::AsA as TraitB<'a, 'b>>::AsB`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
trait T {
async fn foo() {} //~ ERROR functions in traits cannot be declared `async`
async fn bar(&self) {} //~ ERROR functions in traits cannot be declared `async`
+ async fn baz() { //~ ERROR functions in traits cannot be declared `async`
+ // Nested item must not ICE.
+ fn a() {}
+ }
}
fn main() {}
= note: `async` trait functions are not currently supported
= note: consider using the `async-trait` crate: https://crates.io/crates/async-trait
-error: aborting due to 2 previous errors
+error[E0706]: functions in traits cannot be declared `async`
+ --> $DIR/async-trait-fn.rs:5:5
+ |
+LL | async fn baz() {
+ | ^----
+ | |
+ | _____`async` because of this
+ | |
+LL | | // Nested item must not ICE.
+LL | | fn a() {}
+LL | | }
+ | |_____^
+ |
+ = note: `async` trait functions are not currently supported
+ = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait
+
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0706`.
| -
| |
| first assignment to `x`
- | help: make this binding mutable: `mut x`
+ | help: consider making this binding mutable: `mut x`
LL | x += 1;
| ^^^^^^ cannot assign twice to immutable variable
--- /dev/null
+#![deny(large_assignments)]
+#![feature(large_assignments)]
+#![move_size_limit = "1000"]
+// build-fail
+// only-x86_64
+
+// edition:2018
+
+fn main() {
+ let x = async { //~ ERROR large_assignments
+ let y = [0; 9999];
+ dbg!(y);
+ thing(&y).await;
+ dbg!(y);
+ };
+ let z = (x, 42); //~ ERROR large_assignments
+ //~^ ERROR large_assignments
+ let a = z.0; //~ ERROR large_assignments
+ let b = z.1;
+}
+
+async fn thing(y: &[u8]) {
+ dbg!(y);
+}
--- /dev/null
+error: moving 10024 bytes
+ --> $DIR/large_moves.rs:10:13
+ |
+LL | let x = async {
+ | _____________^
+LL | | let y = [0; 9999];
+LL | | dbg!(y);
+LL | | thing(&y).await;
+LL | | dbg!(y);
+LL | | };
+ | |_____^ value moved from here
+ |
+note: the lint level is defined here
+ --> $DIR/large_moves.rs:1:9
+ |
+LL | #![deny(large_assignments)]
+ | ^^^^^^^^^^^^^^^^^
+
+error: moving 10024 bytes
+ --> $DIR/large_moves.rs:16:14
+ |
+LL | let z = (x, 42);
+ | ^ value moved from here
+
+error: moving 10024 bytes
+ --> $DIR/large_moves.rs:16:13
+ |
+LL | let z = (x, 42);
+ | ^^^^^^^ value moved from here
+
+error: moving 10024 bytes
+ --> $DIR/large_moves.rs:18:13
+ |
+LL | let a = z.0;
+ | ^^^ value moved from here
+
+error: aborting due to 4 previous errors
+
+++ /dev/null
-// run-pass
-
-#![feature(main)]
-
-pub fn main() {
- panic!()
-}
-
-#[main]
-fn foo() {
-}
+++ /dev/null
-// run-pass
-// pretty-expanded FIXME #23616
-
-#![feature(main)]
-
-#[main]
-fn foo() {
-}
+++ /dev/null
-// run-pass
-// pretty-expanded FIXME #23616
-
-#![feature(main)]
-
-#[main]
-fn foo() {
-}
+++ /dev/null
-static i: String = 10;
-//~^ ERROR mismatched types
-//~| expected struct `String`, found integer
-fn main() { println!("{}", i); }
+++ /dev/null
-error[E0308]: mismatched types
- --> $DIR/bad-const-type.rs:1:20
- |
-LL | static i: String = 10;
- | ^^
- | |
- | expected struct `String`, found integer
- | help: try using a conversion method: `10.to_string()`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0308`.
+++ /dev/null
-extern crate krate-name-here;
-//~^ ERROR crate name using dashes are not valid in `extern crate` statements
-//~| ERROR can't find crate for `krate_name_here`
-
-fn main() {}
+++ /dev/null
-error: crate name using dashes are not valid in `extern crate` statements
- --> $DIR/bad-crate-name.rs:1:14
- |
-LL | extern crate krate-name-here;
- | ^^^^^^^^^^^^^^^ dash-separated idents are not valid
- |
-help: if the original crate name uses dashes you need to use underscores in the code
- |
-LL | extern crate krate_name_here;
- | ^ ^
-
-error[E0463]: can't find crate for `krate_name_here`
- --> $DIR/bad-crate-name.rs:1:1
- |
-LL | extern crate krate-name-here;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't find crate
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0463`.
+++ /dev/null
-// error-pattern: can't capture dynamic environment in a fn item
-fn foo() {
- let x: isize;
- fn bar() { log(debug, x); }
-}
-fn main() { foo(); }
+++ /dev/null
-error[E0434]: can't capture dynamic environment in a fn item
- --> $DIR/bad-env-capture.rs:4:27
- |
-LL | fn bar() { log(debug, x); }
- | ^
- |
- = help: use the `|| { ... }` closure form instead
-
-error[E0425]: cannot find function `log` in this scope
- --> $DIR/bad-env-capture.rs:4:16
- |
-LL | fn bar() { log(debug, x); }
- | ^^^ not found in this scope
-
-error[E0425]: cannot find value `debug` in this scope
- --> $DIR/bad-env-capture.rs:4:20
- |
-LL | fn bar() { log(debug, x); }
- | ^^^^^ not found in this scope
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0425, E0434.
-For more information about an error, try `rustc --explain E0425`.
+++ /dev/null
-// error-pattern: can't capture dynamic environment in a fn item
-fn foo(x: isize) {
- fn bar() { log(debug, x); }
-}
-fn main() { foo(2); }
+++ /dev/null
-error[E0434]: can't capture dynamic environment in a fn item
- --> $DIR/bad-env-capture2.rs:3:27
- |
-LL | fn bar() { log(debug, x); }
- | ^
- |
- = help: use the `|| { ... }` closure form instead
-
-error[E0425]: cannot find function `log` in this scope
- --> $DIR/bad-env-capture2.rs:3:16
- |
-LL | fn bar() { log(debug, x); }
- | ^^^ not found in this scope
-
-error[E0425]: cannot find value `debug` in this scope
- --> $DIR/bad-env-capture2.rs:3:20
- |
-LL | fn bar() { log(debug, x); }
- | ^^^^^ not found in this scope
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0425, E0434.
-For more information about an error, try `rustc --explain E0425`.
+++ /dev/null
-// error-pattern: can't capture dynamic environment in a fn item
-fn foo(x: isize) {
- fn mth() {
- fn bar() { log(debug, x); }
- }
-}
-
-fn main() { foo(2); }
+++ /dev/null
-error[E0434]: can't capture dynamic environment in a fn item
- --> $DIR/bad-env-capture3.rs:4:31
- |
-LL | fn bar() { log(debug, x); }
- | ^
- |
- = help: use the `|| { ... }` closure form instead
-
-error[E0425]: cannot find function `log` in this scope
- --> $DIR/bad-env-capture3.rs:4:20
- |
-LL | fn bar() { log(debug, x); }
- | ^^^ not found in this scope
-
-error[E0425]: cannot find value `debug` in this scope
- --> $DIR/bad-env-capture3.rs:4:24
- |
-LL | fn bar() { log(debug, x); }
- | ^^^^^ not found in this scope
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0425, E0434.
-For more information about an error, try `rustc --explain E0425`.
+++ /dev/null
-fn main() {
- 1 = 2; //~ ERROR invalid left-hand side of assignment
- 1 += 2; //~ ERROR invalid left-hand side of assignment
- (1, 2) = (3, 4); //~ ERROR destructuring assignments are unstable
- //~| ERROR invalid left-hand side of assignment
- //~| ERROR invalid left-hand side of assignment
-
- let (a, b) = (1, 2);
- (a, b) = (3, 4); //~ ERROR destructuring assignments are unstable
-
- None = Some(3); //~ ERROR invalid left-hand side of assignment
-}
+++ /dev/null
-error[E0658]: destructuring assignments are unstable
- --> $DIR/bad-expr-lhs.rs:4:12
- |
-LL | (1, 2) = (3, 4);
- | ------ ^
- | |
- | cannot assign to this expression
- |
- = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
- = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
-
-error[E0658]: destructuring assignments are unstable
- --> $DIR/bad-expr-lhs.rs:9:12
- |
-LL | (a, b) = (3, 4);
- | ------ ^
- | |
- | cannot assign to this expression
- |
- = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
- = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
-
-error[E0070]: invalid left-hand side of assignment
- --> $DIR/bad-expr-lhs.rs:2:7
- |
-LL | 1 = 2;
- | - ^
- | |
- | cannot assign to this expression
-
-error[E0067]: invalid left-hand side of assignment
- --> $DIR/bad-expr-lhs.rs:3:7
- |
-LL | 1 += 2;
- | - ^^
- | |
- | cannot assign to this expression
-
-error[E0070]: invalid left-hand side of assignment
- --> $DIR/bad-expr-lhs.rs:4:12
- |
-LL | (1, 2) = (3, 4);
- | - ^
- | |
- | cannot assign to this expression
-
-error[E0070]: invalid left-hand side of assignment
- --> $DIR/bad-expr-lhs.rs:4:12
- |
-LL | (1, 2) = (3, 4);
- | - ^
- | |
- | cannot assign to this expression
-
-error[E0070]: invalid left-hand side of assignment
- --> $DIR/bad-expr-lhs.rs:11:10
- |
-LL | None = Some(3);
- | ---- ^
- | |
- | cannot assign to this expression
-
-error: aborting due to 7 previous errors
-
-Some errors have detailed explanations: E0067, E0070, E0658.
-For more information about an error, try `rustc --explain E0067`.
+++ /dev/null
-mod m1 {}
-
-fn main(arguments: Vec<String>) { //~ ERROR `main` function has wrong type
- log(debug, m1::arguments);
- //~^ ERROR cannot find function `log` in this scope
- //~| ERROR cannot find value `debug` in this scope
- //~| ERROR cannot find value `arguments` in module `m1`
-}
+++ /dev/null
-error[E0425]: cannot find function `log` in this scope
- --> $DIR/bad-expr-path.rs:4:5
- |
-LL | log(debug, m1::arguments);
- | ^^^ not found in this scope
-
-error[E0425]: cannot find value `debug` in this scope
- --> $DIR/bad-expr-path.rs:4:9
- |
-LL | log(debug, m1::arguments);
- | ^^^^^ not found in this scope
-
-error[E0425]: cannot find value `arguments` in module `m1`
- --> $DIR/bad-expr-path.rs:4:20
- |
-LL | log(debug, m1::arguments);
- | ^^^^^^^^^ not found in `m1`
-
-error[E0580]: `main` function has wrong type
- --> $DIR/bad-expr-path.rs:3:1
- |
-LL | fn main(arguments: Vec<String>) {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters
- |
- = note: expected fn pointer `fn()`
- found fn pointer `fn(Vec<String>)`
-
-error: aborting due to 4 previous errors
-
-Some errors have detailed explanations: E0425, E0580.
-For more information about an error, try `rustc --explain E0425`.
+++ /dev/null
-mod m1 {
- pub mod arguments {}
-}
-
-fn main(arguments: Vec<String>) { //~ ERROR `main` function has wrong type
- log(debug, m1::arguments);
- //~^ ERROR cannot find function `log` in this scope
- //~| ERROR cannot find value `debug` in this scope
- //~| ERROR expected value, found module `m1::arguments`
-}
+++ /dev/null
-error[E0425]: cannot find function `log` in this scope
- --> $DIR/bad-expr-path2.rs:6:5
- |
-LL | log(debug, m1::arguments);
- | ^^^ not found in this scope
-
-error[E0425]: cannot find value `debug` in this scope
- --> $DIR/bad-expr-path2.rs:6:9
- |
-LL | log(debug, m1::arguments);
- | ^^^^^ not found in this scope
-
-error[E0423]: expected value, found module `m1::arguments`
- --> $DIR/bad-expr-path2.rs:6:16
- |
-LL | log(debug, m1::arguments);
- | ^^^^^^^^^^^^^ not a value
-
-error[E0580]: `main` function has wrong type
- --> $DIR/bad-expr-path2.rs:5:1
- |
-LL | fn main(arguments: Vec<String>) {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters
- |
- = note: expected fn pointer `fn()`
- found fn pointer `fn(Vec<String>)`
-
-error: aborting due to 4 previous errors
-
-Some errors have detailed explanations: E0423, E0425, E0580.
-For more information about an error, try `rustc --explain E0423`.
+++ /dev/null
-#[link()] //~ ERROR: specified without `name =
-#[link(name = "")] //~ ERROR: with empty name
-#[link(name = "foo")]
-#[link(name = "foo", kind = "bar")] //~ ERROR: unknown kind
-extern "C" {}
-
-fn main() {}
+++ /dev/null
-error[E0459]: `#[link(...)]` specified without `name = "foo"`
- --> $DIR/bad-extern-link-attrs.rs:1:1
- |
-LL | #[link()]
- | ^^^^^^^^^ missing `name` argument
-
-error[E0454]: `#[link(name = "")]` given with empty name
- --> $DIR/bad-extern-link-attrs.rs:2:1
- |
-LL | #[link(name = "")]
- | ^^^^^^^^^^^^^^^^^^ empty name given
-
-error[E0458]: unknown kind: `bar`
- --> $DIR/bad-extern-link-attrs.rs:4:22
- |
-LL | #[link(name = "foo", kind = "bar")]
- | ---------------------^^^^^^^^^^^^--
- | |
- | unknown kind
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0454, E0458, E0459.
-For more information about an error, try `rustc --explain E0454`.
+++ /dev/null
-// build-fail
-
-#![feature(repr_simd, platform_intrinsics, core_intrinsics)]
-#![allow(warnings)]
-#![crate_type = "rlib"]
-
-// Bad monomorphizations could previously cause LLVM asserts even though the
-// error was caught in the compiler.
-
-extern "platform-intrinsic" {
- fn simd_add<T>(x: T, y: T) -> T;
-}
-
-use std::intrinsics;
-
-#[derive(Copy, Clone)]
-pub struct Foo(i64);
-
-pub fn test_cttz(v: Foo) -> Foo {
- intrinsics::cttz(v)
- //~^ ERROR `cttz` intrinsic: expected basic integer type, found `Foo`
-}
-
-pub unsafe fn test_fadd_fast(a: Foo, b: Foo) -> Foo {
- intrinsics::fadd_fast(a, b)
- //~^ ERROR `fadd_fast` intrinsic: expected basic float type, found `Foo`
-}
-
-pub unsafe fn test_simd_add(a: Foo, b: Foo) -> Foo {
- simd_add(a, b)
- //~^ ERROR `simd_add` intrinsic: expected SIMD input type, found non-SIMD `Foo`
-}
+++ /dev/null
-error[E0511]: invalid monomorphization of `cttz` intrinsic: expected basic integer type, found `Foo`
- --> $DIR/bad-intrinsic-monomorphization.rs:20:5
- |
-LL | intrinsics::cttz(v)
- | ^^^^^^^^^^^^^^^^^^^
-
-error[E0511]: invalid monomorphization of `fadd_fast` intrinsic: expected basic float type, found `Foo`
- --> $DIR/bad-intrinsic-monomorphization.rs:25:5
- |
-LL | intrinsics::fadd_fast(a, b)
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0511]: invalid monomorphization of `simd_add` intrinsic: expected SIMD input type, found non-SIMD `Foo`
- --> $DIR/bad-intrinsic-monomorphization.rs:30:5
- |
-LL | simd_add(a, b)
- | ^^^^^^^^^^^^^^
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0511`.
+++ /dev/null
-// compile-flags: --cap-lints test
-// error-pattern: unknown lint level: `test`
-
-fn main() {}
+++ /dev/null
-error: unknown lint level: `test`
-
+++ /dev/null
-// compile-flags: --cap-lints deny
-
-#![warn(unused)]
-#![deny(warnings)]
-
-use std::option; //~ ERROR
-
-fn main() {}
+++ /dev/null
-error: unused import: `std::option`
- --> $DIR/bad-lint-cap2.rs:6:5
- |
-LL | use std::option;
- | ^^^^^^^^^^^
- |
-note: the lint level is defined here
- --> $DIR/bad-lint-cap2.rs:4:9
- |
-LL | #![deny(warnings)]
- | ^^^^^^^^
- = note: `#[deny(unused_imports)]` implied by `#[deny(warnings)]`
-
-error: aborting due to previous error
-
+++ /dev/null
-// check-pass
-// compile-flags: --cap-lints warn
-
-#![warn(unused)]
-#![deny(warnings)]
-
-use std::option; //~ WARN
-
-fn main() {}
+++ /dev/null
-warning: unused import: `std::option`
- --> $DIR/bad-lint-cap3.rs:7:5
- |
-LL | use std::option;
- | ^^^^^^^^^^^
- |
-note: the lint level is defined here
- --> $DIR/bad-lint-cap3.rs:5:9
- |
-LL | #![deny(warnings)]
- | ^^^^^^^^
- = note: `#[warn(unused_imports)]` implied by `#[warn(warnings)]`
-
-warning: 1 warning emitted
-
+++ /dev/null
-fn main(x: isize) { } //~ ERROR: `main` function has wrong type [E0580]
+++ /dev/null
-error[E0580]: `main` function has wrong type
- --> $DIR/bad-main.rs:1:1
- |
-LL | fn main(x: isize) { }
- | ^^^^^^^^^^^^^^^^^ incorrect number of function parameters
- |
- = note: expected fn pointer `fn()`
- found fn pointer `fn(isize)`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0580`.
+++ /dev/null
-fn foo<T:'static>() {
- 1.bar::<T>(); //~ ERROR `T` cannot be sent between threads safely
-}
-
-trait Bar {
- fn bar<T:Send>(&self);
-}
-
-impl Bar for usize {
- fn bar<T:Send>(&self) {
- }
-}
-
-fn main() {}
+++ /dev/null
-error[E0277]: `T` cannot be sent between threads safely
- --> $DIR/bad-method-typaram-kind.rs:2:7
- |
-LL | 1.bar::<T>();
- | ^^^ `T` cannot be sent between threads safely
- |
-help: consider further restricting this bound
- |
-LL | fn foo<T:'static + std::marker::Send>() {
- | ^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0277`.
+++ /dev/null
-struct S<T> {
- contents: T,
-}
-
-impl<T> S<T> {
- fn new<U>(x: T, _: U) -> S<T> {
- S {
- contents: x,
- }
- }
-}
-
-trait Trait<T> {
- fn new<U>(x: T, y: U) -> Self;
-}
-
-struct S2 {
- contents: isize,
-}
-
-impl Trait<isize> for S2 {
- fn new<U>(x: isize, _: U) -> S2 {
- S2 {
- contents: x,
- }
- }
-}
-
-fn foo<'a>() {
- let _ = S::new::<isize,f64>(1, 1.0);
- //~^ ERROR this associated function takes 1 type argument but 2 type arguments were supplied
-
- let _ = S::<'a,isize>::new::<f64>(1, 1.0);
- //~^ ERROR this struct takes 0 lifetime arguments but 1 lifetime argument was supplied
-
- let _: S2 = Trait::new::<isize,f64>(1, 1.0);
- //~^ ERROR this associated function takes 1 type argument but 2 type arguments were supplied
-
- let _: S2 = Trait::<'a,isize>::new::<f64,f64>(1, 1.0);
- //~^ ERROR this trait takes 0 lifetime arguments but 1 lifetime argument was supplied
- //~| ERROR this associated function takes 1 type argument but 2 type arguments were supplied
-}
-
-fn main() {}
+++ /dev/null
-error[E0107]: this associated function takes 1 type argument but 2 type arguments were supplied
- --> $DIR/bad-mid-path-type-params.rs:30:16
- |
-LL | let _ = S::new::<isize,f64>(1, 1.0);
- | ^^^ ---- help: remove this type argument
- | |
- | expected 1 type argument
- |
-note: associated function defined here, with 1 type parameter: `U`
- --> $DIR/bad-mid-path-type-params.rs:6:8
- |
-LL | fn new<U>(x: T, _: U) -> S<T> {
- | ^^^ -
-
-error[E0107]: this struct takes 0 lifetime arguments but 1 lifetime argument was supplied
- --> $DIR/bad-mid-path-type-params.rs:33:13
- |
-LL | let _ = S::<'a,isize>::new::<f64>(1, 1.0);
- | ^ --- help: remove this lifetime argument
- | |
- | expected 0 lifetime arguments
- |
-note: struct defined here, with 0 lifetime parameters
- --> $DIR/bad-mid-path-type-params.rs:1:8
- |
-LL | struct S<T> {
- | ^
-
-error[E0107]: this associated function takes 1 type argument but 2 type arguments were supplied
- --> $DIR/bad-mid-path-type-params.rs:36:24
- |
-LL | let _: S2 = Trait::new::<isize,f64>(1, 1.0);
- | ^^^ ---- help: remove this type argument
- | |
- | expected 1 type argument
- |
-note: associated function defined here, with 1 type parameter: `U`
- --> $DIR/bad-mid-path-type-params.rs:14:8
- |
-LL | fn new<U>(x: T, y: U) -> Self;
- | ^^^ -
-
-error[E0107]: this trait takes 0 lifetime arguments but 1 lifetime argument was supplied
- --> $DIR/bad-mid-path-type-params.rs:39:17
- |
-LL | let _: S2 = Trait::<'a,isize>::new::<f64,f64>(1, 1.0);
- | ^^^^^ --- help: remove this lifetime argument
- | |
- | expected 0 lifetime arguments
- |
-note: trait defined here, with 0 lifetime parameters
- --> $DIR/bad-mid-path-type-params.rs:13:7
- |
-LL | trait Trait<T> {
- | ^^^^^
-
-error[E0107]: this associated function takes 1 type argument but 2 type arguments were supplied
- --> $DIR/bad-mid-path-type-params.rs:39:36
- |
-LL | let _: S2 = Trait::<'a,isize>::new::<f64,f64>(1, 1.0);
- | ^^^ ---- help: remove this type argument
- | |
- | expected 1 type argument
- |
-note: associated function defined here, with 1 type parameter: `U`
- --> $DIR/bad-mid-path-type-params.rs:14:8
- |
-LL | fn new<U>(x: T, y: U) -> Self;
- | ^^^ -
-
-error: aborting due to 5 previous errors
-
-For more information about this error, try `rustc --explain E0107`.
+++ /dev/null
-fn main() {
- let foo = thing::len(Vec::new());
- //~^ ERROR failed to resolve: use of undeclared crate or module `thing`
-
- let foo = foo::bar::baz();
- //~^ ERROR failed to resolve: use of undeclared crate or module `foo`
-}
+++ /dev/null
-error[E0433]: failed to resolve: use of undeclared crate or module `thing`
- --> $DIR/bad-module.rs:2:15
- |
-LL | let foo = thing::len(Vec::new());
- | ^^^^^ use of undeclared crate or module `thing`
-
-error[E0433]: failed to resolve: use of undeclared crate or module `foo`
- --> $DIR/bad-module.rs:5:15
- |
-LL | let foo = foo::bar::baz();
- | ^^^ use of undeclared crate or module `foo`
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0433`.
+++ /dev/null
-trait Trait {}
-
-pub fn main() {
- let x: Vec<dyn Trait + Sized> = Vec::new();
- //~^ ERROR only auto traits can be used as additional traits in a trait object
- //~| ERROR the size for values of type
- //~| ERROR the size for values of type
- //~| ERROR the size for values of type
-}
+++ /dev/null
-error[E0225]: only auto traits can be used as additional traits in a trait object
- --> $DIR/bad-sized.rs:4:28
- |
-LL | let x: Vec<dyn Trait + Sized> = Vec::new();
- | ----- ^^^^^ additional non-auto trait
- | |
- | first non-auto trait
- |
- = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Trait + Sized {}`
- = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
-
-error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time
- --> $DIR/bad-sized.rs:4:12
- |
-LL | let x: Vec<dyn Trait + Sized> = Vec::new();
- | ^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
- |
- ::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
- |
-LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
- | - required by this bound in `Vec`
- |
- = help: the trait `Sized` is not implemented for `dyn Trait`
-
-error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time
- --> $DIR/bad-sized.rs:4:37
- |
-LL | let x: Vec<dyn Trait + Sized> = Vec::new();
- | ^^^^^^^^ doesn't have a size known at compile-time
- |
- = help: the trait `Sized` is not implemented for `dyn Trait`
- = note: required by `Vec::<T>::new`
-
-error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time
- --> $DIR/bad-sized.rs:4:37
- |
-LL | let x: Vec<dyn Trait + Sized> = Vec::new();
- | ^^^ doesn't have a size known at compile-time
- |
- ::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
- |
-LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
- | - required by this bound in `Vec`
- |
- = help: the trait `Sized` is not implemented for `dyn Trait`
-
-error: aborting due to 4 previous errors
-
-Some errors have detailed explanations: E0225, E0277.
-For more information about an error, try `rustc --explain E0225`.
+++ /dev/null
-fn foo<T>() {
- fn bar(b: T) { } //~ ERROR can't use generic parameters from outer
-}
-fn main() { }
+++ /dev/null
-error[E0401]: can't use generic parameters from outer function
- --> $DIR/bad-type-env-capture.rs:2:15
- |
-LL | fn foo<T>() {
- | - type parameter from outer function
-LL | fn bar(b: T) { }
- | --- ^ use of generic parameter from outer function
- | |
- | help: try using a local generic parameter instead: `bar<T>`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0401`.
| -
| |
| first assignment to `x`
- | help: make this binding mutable: `mut x`
+ | help: consider making this binding mutable: `mut x`
LL | unsafe {
LL | llvm_asm!("nop" : "=r"(x));
| ^ cannot assign twice to immutable variable
| -
| |
| first assignment to `x`
- | help: make this binding mutable: `mut x`
+ | help: consider making this binding mutable: `mut x`
LL | unsafe {
LL | llvm_asm!("nop" : "+r"(x));
| ^ cannot assign twice to immutable variable
| -
| |
| first assignment to `x`
- | help: make this binding mutable: `mut x`
+ | help: consider making this binding mutable: `mut x`
LL | x += 1;
| ^^^^^^ cannot assign twice to immutable variable
| -
| |
| first assignment to `x`
- | help: make this binding mutable: `mut x`
+ | help: consider making this binding mutable: `mut x`
LL | x += 1;
| ^^^^^^ cannot assign twice to immutable variable
| -
| |
| first assignment to `x`
- | help: make this binding mutable: `mut x`
+ | help: consider making this binding mutable: `mut x`
LL | x += 1;
| ^^^^^^ cannot assign twice to immutable variable
| -
| |
| first assignment to `x`
- | help: make this binding mutable: `mut x`
+ | help: consider making this binding mutable: `mut x`
LL | x += 1;
| ^^^^^^ cannot assign twice to immutable variable
| -
| |
| first assignment to `x`
- | help: make this binding mutable: `mut x`
+ | help: consider making this binding mutable: `mut x`
LL | x += 1;
| ^^^^^^ cannot assign twice to immutable variable
--> $DIR/immutable-arg.rs:2:5
|
LL | fn foo(_x: u32) {
- | -- help: make this binding mutable: `mut _x`
+ | -- help: consider making this binding mutable: `mut _x`
LL | _x = 4;
| ^^^^^^ cannot assign to immutable argument
fn test_drop_replace() {
let b: Box<isize>;
- //~^ HELP make this binding mutable
+ //~^ HELP consider making this binding mutable
//~| SUGGESTION mut b
b = Box::new(1); //~ NOTE first assignment
b = Box::new(2); //~ ERROR cannot assign twice to immutable variable `b`
fn test_call() {
let b = Box::new(1); //~ NOTE first assignment
- //~| HELP make this binding mutable
+ //~| HELP consider making this binding mutable
//~| SUGGESTION mut b
b = Box::new(2); //~ ERROR cannot assign twice to immutable variable `b`
//~| NOTE cannot assign twice to immutable
}
-fn test_args(b: Box<i32>) { //~ HELP make this binding mutable
+fn test_args(b: Box<i32>) { //~ HELP consider making this binding mutable
//~| SUGGESTION mut b
b = Box::new(2); //~ ERROR cannot assign to immutable argument `b`
//~| NOTE cannot assign to immutable argument
--> $DIR/issue-45199.rs:6:5
|
LL | let b: Box<isize>;
- | - help: make this binding mutable: `mut b`
+ | - help: consider making this binding mutable: `mut b`
...
LL | b = Box::new(1);
| - first assignment to `b`
| -
| |
| first assignment to `b`
- | help: make this binding mutable: `mut b`
+ | help: consider making this binding mutable: `mut b`
...
LL | b = Box::new(2);
| ^ cannot assign twice to immutable variable
--> $DIR/issue-45199.rs:20:5
|
LL | fn test_args(b: Box<i32>) {
- | - help: make this binding mutable: `mut b`
+ | - help: consider making this binding mutable: `mut b`
LL |
LL | b = Box::new(2);
| ^ cannot assign to immutable argument
--- /dev/null
+// rust-lang/rust#83309: The compiler tries to suggest potential
+// methods that return `&mut` items. However, when it doesn't
+// find such methods, it still tries to add suggestions
+// which then fails an assertion later because there was
+// no suggestions to make.
+
+
+fn main() {
+ for v in Query.iter_mut() {
+ //~^ NOTE this iterator yields `&` references
+ *v -= 1;
+ //~^ ERROR cannot assign to `*v` which is behind a `&` reference
+ //~| NOTE `v` is a `&` reference, so the data it refers to cannot be written
+ }
+}
+
+pub struct Query;
+pub struct QueryIter<'a>(&'a i32);
+
+impl Query {
+ pub fn iter_mut<'a>(&'a mut self) -> QueryIter<'a> {
+ todo!();
+ }
+}
+
+impl<'a> Iterator for QueryIter<'a> {
+ type Item = &'a i32;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ todo!();
+ }
+}
--- /dev/null
+error[E0594]: cannot assign to `*v` which is behind a `&` reference
+ --> $DIR/issue-83309-ice-immut-in-for-loop.rs:11:9
+ |
+LL | for v in Query.iter_mut() {
+ | ---------------- this iterator yields `&` references
+LL |
+LL | *v -= 1;
+ | ^^^^^^^ `v` is a `&` reference, so the data it refers to cannot be written
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0594`.
// gate-test-const_raw_ptr_to_usize_cast
+// revisions: with_feature without_feature
+
+#![cfg_attr(with_feature, feature(const_raw_ptr_to_usize_cast))]
fn main() {
- const X: u32 = unsafe {
- main as u32 //~ ERROR casting pointers to integers in constants is unstable
+ const X: usize = unsafe {
+ main as usize //[without_feature]~ ERROR casting pointers to integers in constants is unstable
};
const Y: u32 = 0;
- const Z: u32 = unsafe {
- &Y as *const u32 as u32 //~ ERROR is unstable
+ const Z: usize = unsafe {
+ &Y as *const u32 as usize //[without_feature]~ ERROR is unstable
+ };
+ // Cast in `const` without `unsafe` block
+ const SAFE: usize = {
+ &Y as *const u32 as usize //[without_feature]~ ERROR is unstable
+ //[with_feature]~^ ERROR cast of pointer to int is unsafe and requires unsafe
};
}
+
+// Cast in `const fn` without `unsafe` block
+const fn test() -> usize {
+ &0 as *const i32 as usize //[without_feature]~ ERROR is unstable
+ //[with_feature]~^ ERROR cast of pointer to int is unsafe and requires unsafe
+}
+++ /dev/null
-error[E0658]: casting pointers to integers in constants is unstable
- --> $DIR/cast-ptr-to-int-const.rs:5:9
- |
-LL | main as u32
- | ^^^^^^^^^^^
- |
- = note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information
- = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable
-
-error[E0658]: casting pointers to integers in constants is unstable
- --> $DIR/cast-ptr-to-int-const.rs:9:9
- |
-LL | &Y as *const u32 as u32
- | ^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information
- = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0658`.
--- /dev/null
+error[E0133]: cast of pointer to int is unsafe and requires unsafe function or block
+ --> $DIR/cast-ptr-to-int-const.rs:16:9
+ |
+LL | &Y as *const u32 as usize
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ cast of pointer to int
+ |
+ = note: casting pointers to integers in constants
+
+error[E0133]: cast of pointer to int is unsafe and requires unsafe function or block
+ --> $DIR/cast-ptr-to-int-const.rs:23:5
+ |
+LL | &0 as *const i32 as usize
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ cast of pointer to int
+ |
+ = note: casting pointers to integers in constants
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0133`.
--- /dev/null
+error[E0658]: casting pointers to integers in constants is unstable
+ --> $DIR/cast-ptr-to-int-const.rs:8:9
+ |
+LL | main as usize
+ | ^^^^^^^^^^^^^
+ |
+ = note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information
+ = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable
+
+error[E0658]: casting pointers to integers in constants is unstable
+ --> $DIR/cast-ptr-to-int-const.rs:12:9
+ |
+LL | &Y as *const u32 as usize
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information
+ = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable
+
+error[E0658]: casting pointers to integers in constants is unstable
+ --> $DIR/cast-ptr-to-int-const.rs:16:9
+ |
+LL | &Y as *const u32 as usize
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information
+ = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable
+
+error[E0658]: casting pointers to integers in constant functions is unstable
+ --> $DIR/cast-ptr-to-int-const.rs:23:5
+ |
+LL | &0 as *const i32 as usize
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information
+ = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
// run-pass
+// Remove this file when `std::raw` is removed.
+// The replacement pointer metadata APIs are tested in library/core/tests/ptr.rs
+#![allow(deprecated)]
#![feature(raw)]
use std::mem;
assert_eq!(b, d);
assert_eq!(c, d as usize);
-
}
--- /dev/null
+// run-rustfix
+
+struct Something {
+ pub field: u32,
+}
+
+fn main() {
+ let mut something = Something { field: 1337 };
+
+ let _pointer_to_something = &something as *const Something;
+ //~^ ERROR: non-primitive cast
+
+ let _mut_pointer_to_something = &mut something as *mut Something;
+ //~^ ERROR: non-primitive cast
+}
--- /dev/null
+// run-rustfix
+
+struct Something {
+ pub field: u32,
+}
+
+fn main() {
+ let mut something = Something { field: 1337 };
+
+ let _pointer_to_something = something as *const Something;
+ //~^ ERROR: non-primitive cast
+
+ let _mut_pointer_to_something = something as *mut Something;
+ //~^ ERROR: non-primitive cast
+}
--- /dev/null
+error[E0605]: non-primitive cast: `Something` as `*const Something`
+ --> $DIR/issue-84213.rs:10:33
+ |
+LL | let _pointer_to_something = something as *const Something;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast
+ |
+help: borrow the value for the cast to be valid
+ |
+LL | let _pointer_to_something = &something as *const Something;
+ | ^
+
+error[E0605]: non-primitive cast: `Something` as `*mut Something`
+ --> $DIR/issue-84213.rs:13:37
+ |
+LL | let _mut_pointer_to_something = something as *mut Something;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast
+ |
+help: borrow the value for the cast to be valid
+ |
+LL | let _mut_pointer_to_something = &mut something as *mut Something;
+ | ^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0605`.
--- /dev/null
+// test for issue 84128
+// missing suggestion for similar ADT type with diffetent generic paramenter
+// on closure ReturnNoExpression
+
+struct Foo<T>(T);
+
+fn main() {
+ || {
+ if false {
+ return Foo(0);
+ }
+
+ Foo(())
+ //~^ ERROR mismatched types [E0308]
+ };
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/issue-84128.rs:13:13
+ |
+LL | Foo(())
+ | ^^ expected integer, found `()`
+ |
+note: return type inferred to be `{integer}` here
+ --> $DIR/issue-84128.rs:10:20
+ |
+LL | return Foo(0);
+ | ^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
| ^^^ type aliases cannot be used as traits
|
help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias
- --> $DIR/two_files_data.rs:5:1
|
-LL | type Bar = dyn Foo;
- | ^^^^^^^^^^^^^^^^^^^
+LL | trait Bar = dyn Foo;
+ |
error: aborting due to previous error
-#![feature(non_ascii_idents)]
-
fn main() {
let _ = ("a̐éö̲", 0u7); //~ ERROR invalid width
let _ = ("아あ", 1i42); //~ ERROR invalid width
error: invalid width `7` for integer literal
- --> $DIR/unicode_2.rs:4:25
+ --> $DIR/unicode_2.rs:2:25
|
LL | let _ = ("a̐éö̲", 0u7);
| ^^^
= help: valid widths are 8, 16, 32, 64 and 128
error: invalid width `42` for integer literal
- --> $DIR/unicode_2.rs:5:20
+ --> $DIR/unicode_2.rs:3:20
|
LL | let _ = ("아あ", 1i42);
| ^^^^
= help: valid widths are 8, 16, 32, 64 and 128
error[E0425]: cannot find value `a̐é` in this scope
- --> $DIR/unicode_2.rs:6:13
+ --> $DIR/unicode_2.rs:4:13
|
LL | let _ = a̐é;
| ^^ not found in this scope
| -
| |
| first assignment to `x`
- | help: make this binding mutable: `mut x`
+ | help: consider making this binding mutable: `mut x`
LL | x = 43;
| ^^^^^^ cannot assign twice to immutable variable
-#![feature(const_generics)]
+#![cfg_attr(full, feature(const_generics))]
#![feature(const_generics_defaults)]
#![allow(incomplete_features)]
--- /dev/null
+error: constant expression depends on a generic parameter
+ --> $DIR/complex-generic-default-expr.rs:6:34
+ |
+LL | struct Foo<const N: usize, const M: usize = { N + 1 }>;
+ | ^
+ |
+ = note: this may fail depending on what value the parameter takes
+
+error: constant expression depends on a generic parameter
+ --> $DIR/complex-generic-default-expr.rs:10:21
+ |
+LL | struct Bar<T, const TYPE_SIZE: usize = { std::mem::size_of::<T>() }>(T);
+ | ^^^^^^^^^
+ |
+ = note: this may fail depending on what value the parameter takes
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+error: generic parameters may not be used in const operations
+ --> $DIR/complex-generic-default-expr.rs:6:47
+ |
+LL | struct Foo<const N: usize, const M: usize = { N + 1 }>;
+ | ^ cannot perform const operation using `N`
+ |
+ = help: const parameters may only be used as standalone arguments, i.e. `N`
+ = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions
+
+error: generic parameters may not be used in const operations
+ --> $DIR/complex-generic-default-expr.rs:10:62
+ |
+LL | struct Bar<T, const TYPE_SIZE: usize = { std::mem::size_of::<T>() }>(T);
+ | ^ cannot perform const operation using `T`
+ |
+ = note: type parameters may not be used in const expressions
+ = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))]
+#![feature(const_generics_defaults)]
+#![allow(incomplete_features)]
+
+struct Foo<const N: usize, const M: usize = { N + 1 }>;
+//[full]~^ ERROR constant expression depends on a generic parameter
+//[min]~^^ ERROR generic parameters may not be used in const operations
+
+struct Bar<T, const TYPE_SIZE: usize = { std::mem::size_of::<T>() }>(T);
+//[full]~^ ERROR constant expression depends on a generic parameter
+//[min]~^^ ERROR generic parameters may not be used in const operations
+
+fn main() {}
// run-pass
-
-#![feature(const_generics)]
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))]
#![feature(const_generics_defaults)]
#![allow(incomplete_features)]
--- /dev/null
+// run-pass
+#![feature(const_generics_defaults)]
+#![allow(incomplete_features)]
+struct Foo<const N: usize, const M: usize = N>([u8; N], [u8; M]);
+
+fn foo<const N: usize>() -> Foo<N> {
+ let x = [0; N];
+ Foo(x, x)
+}
+
+// To check that we actually apply the correct substs for const param defaults.
+fn concrete_foo() -> Foo<13> {
+ Foo(Default::default(), Default::default())
+}
+
+
+fn main() {
+ let val = foo::<13>();
+ assert_eq!(val.0, val.1);
+
+ let val = concrete_foo();
+ assert_eq!(val.0, val.1);
+}
--- /dev/null
+// run-pass
+#![feature(const_generics_defaults)]
+#![allow(incomplete_features)]
+struct Foo<const N: usize, T = [u8; N]>(T);
+
+impl<const N: usize> Foo<N> {
+ fn new() -> Self {
+ Foo([0; N])
+ }
+}
+
+fn main() {
+ assert_eq!(Foo::new().0, [0; 10]);
+}
--- /dev/null
+error: defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+ --> $DIR/default-on-impl.rs:8:12
+ |
+LL | impl<const N: usize = 1> Foo<N> {}
+ | ^
+
+error: aborting due to previous error
+
--- /dev/null
+error: defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+ --> $DIR/default-on-impl.rs:8:12
+ |
+LL | impl<const N: usize = 1> Foo<N> {}
+ | ^
+
+error: aborting due to previous error
+
--- /dev/null
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))]
+#![feature(const_generics_defaults)]
+#![allow(incomplete_features)]
+
+struct Foo<const N: usize>;
+
+impl<const N: usize = 1> Foo<N> {}
+//~^ ERROR defaults for const parameters are only allowed
+
+fn main() {}
--- /dev/null
+#![feature(const_generics_defaults)]
+#![allow(incomplete_features)]
+struct Foo<const N: u8 = { 255 + 1 }>;
+//~^ ERROR evaluation of constant value failed
+fn main() {}
--- /dev/null
+error[E0080]: evaluation of constant value failed
+ --> $DIR/default-param-wf-concrete.rs:3:28
+ |
+LL | struct Foo<const N: u8 = { 255 + 1 }>;
+ | ^^^^^^^ attempt to compute `u8::MAX + 1_u8`, which would overflow
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0080`.
// aux-build:const_defaulty.rs
// check-pass
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))]
#![feature(const_generics_defaults)]
#![allow(incomplete_features)]
error: lifetime parameters must be declared prior to const parameters
- --> $DIR/intermixed-lifetime.rs:6:28
+ --> $DIR/intermixed-lifetime.rs:7:28
|
LL | struct Foo<const N: usize, 'a, T = u32>(&'a (), T);
| -----------------^^---------- help: reorder the parameters: lifetimes, then consts and types: `<'a, const N: usize, T = u32>`
error: lifetime parameters must be declared prior to const parameters
- --> $DIR/intermixed-lifetime.rs:6:28
+ --> $DIR/intermixed-lifetime.rs:7:28
|
LL | struct Foo<const N: usize, 'a, T = u32>(&'a (), T);
- | -----------------^^---------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>`
+ | -----------------^^---------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, const N: usize, T = u32>`
-error: type parameters must be declared prior to const parameters
- --> $DIR/intermixed-lifetime.rs:6:32
- |
-LL | struct Foo<const N: usize, 'a, T = u32>(&'a (), T);
- | ---------------------^------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>`
-
-error: lifetime parameters must be declared prior to const parameters
+error: lifetime parameters must be declared prior to type parameters
--> $DIR/intermixed-lifetime.rs:10:37
|
LL | struct Bar<const N: usize, T = u32, 'a>(&'a (), T);
- | --------------------------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>`
-
-error: type parameters must be declared prior to const parameters
- --> $DIR/intermixed-lifetime.rs:10:28
- |
-LL | struct Bar<const N: usize, T = u32, 'a>(&'a (), T);
- | -----------------^----------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>`
+ | --------------------------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, const N: usize, T = u32>`
-error: aborting due to 4 previous errors
+error: aborting due to 2 previous errors
-// revisions: full min
// Checks that lifetimes cannot be interspersed between consts and types.
+// revisions: full min
#![cfg_attr(full, feature(const_generics))]
-#![cfg_attr(full, allow(incomplete_features))]
+#![feature(const_generics_defaults)]
+#![allow(incomplete_features)]
struct Foo<const N: usize, 'a, T = u32>(&'a (), T);
//~^ Error lifetime parameters must be declared prior to const parameters
-//[min]~^^ Error type parameters must be declared prior to const parameters
struct Bar<const N: usize, T = u32, 'a>(&'a (), T);
-//[full]~^ Error lifetime parameters must be declared prior to type parameters
-//[min]~^^ Error type parameters must be declared prior to const parameters
-//[min]~| Error lifetime parameters must be declared prior to const parameters
+//~^ Error lifetime parameters must be declared prior to type parameters
fn main() {}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/mismatch.rs:12:28
+ |
+LL | let e: Example::<13> = ();
+ | ------------- ^^ expected struct `Example`, found `()`
+ | |
+ | expected due to this
+
+error[E0308]: mismatched types
+ --> $DIR/mismatch.rs:14:34
+ |
+LL | let e: Example2::<u32, 13> = ();
+ | ------------------- ^^ expected struct `Example2`, found `()`
+ | |
+ | expected due to this
+ |
+ = note: expected struct `Example2`
+ found unit type `()`
+
+error[E0308]: mismatched types
+ --> $DIR/mismatch.rs:16:34
+ |
+LL | let e: Example3::<13, u32> = ();
+ | ------------------- ^^ expected struct `Example3`, found `()`
+ | |
+ | expected due to this
+ |
+ = note: expected struct `Example3`
+ found unit type `()`
+
+error[E0308]: mismatched types
+ --> $DIR/mismatch.rs:18:28
+ |
+LL | let e: Example3::<7> = ();
+ | ------------- ^^ expected struct `Example3`, found `()`
+ | |
+ | expected due to this
+ |
+ = note: expected struct `Example3<7_usize>`
+ found unit type `()`
+
+error[E0308]: mismatched types
+ --> $DIR/mismatch.rs:22:28
+ |
+LL | let e: Example4::<7> = ();
+ | ------------- ^^ expected struct `Example4`, found `()`
+ | |
+ | expected due to this
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/mismatch.rs:12:28
+ |
+LL | let e: Example::<13> = ();
+ | ------------- ^^ expected struct `Example`, found `()`
+ | |
+ | expected due to this
+
+error[E0308]: mismatched types
+ --> $DIR/mismatch.rs:14:34
+ |
+LL | let e: Example2::<u32, 13> = ();
+ | ------------------- ^^ expected struct `Example2`, found `()`
+ | |
+ | expected due to this
+ |
+ = note: expected struct `Example2`
+ found unit type `()`
+
+error[E0308]: mismatched types
+ --> $DIR/mismatch.rs:16:34
+ |
+LL | let e: Example3::<13, u32> = ();
+ | ------------------- ^^ expected struct `Example3`, found `()`
+ | |
+ | expected due to this
+ |
+ = note: expected struct `Example3`
+ found unit type `()`
+
+error[E0308]: mismatched types
+ --> $DIR/mismatch.rs:18:28
+ |
+LL | let e: Example3::<7> = ();
+ | ------------- ^^ expected struct `Example3`, found `()`
+ | |
+ | expected due to this
+ |
+ = note: expected struct `Example3<7_usize>`
+ found unit type `()`
+
+error[E0308]: mismatched types
+ --> $DIR/mismatch.rs:22:28
+ |
+LL | let e: Example4::<7> = ();
+ | ------------- ^^ expected struct `Example4`, found `()`
+ | |
+ | expected due to this
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
-#![feature(const_generics)]
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))]
#![feature(const_generics_defaults)]
#![allow(incomplete_features)]
+++ /dev/null
-error[E0308]: mismatched types
- --> $DIR/mismatch.rs:11:28
- |
-LL | let e: Example::<13> = ();
- | ------------- ^^ expected struct `Example`, found `()`
- | |
- | expected due to this
-
-error[E0308]: mismatched types
- --> $DIR/mismatch.rs:13:34
- |
-LL | let e: Example2::<u32, 13> = ();
- | ------------------- ^^ expected struct `Example2`, found `()`
- | |
- | expected due to this
- |
- = note: expected struct `Example2`
- found unit type `()`
-
-error[E0308]: mismatched types
- --> $DIR/mismatch.rs:15:34
- |
-LL | let e: Example3::<13, u32> = ();
- | ------------------- ^^ expected struct `Example3`, found `()`
- | |
- | expected due to this
- |
- = note: expected struct `Example3`
- found unit type `()`
-
-error[E0308]: mismatched types
- --> $DIR/mismatch.rs:17:28
- |
-LL | let e: Example3::<7> = ();
- | ------------- ^^ expected struct `Example3`, found `()`
- | |
- | expected due to this
- |
- = note: expected struct `Example3<7_usize>`
- found unit type `()`
-
-error[E0308]: mismatched types
- --> $DIR/mismatch.rs:21:28
- |
-LL | let e: Example4::<7> = ();
- | ------------- ^^ expected struct `Example4`, found `()`
- | |
- | expected due to this
-
-error: aborting due to 5 previous errors
-
-For more information about this error, try `rustc --explain E0308`.
fn foo<const SIZE: usize = 5>() {}
-struct Range<const FROM: usize = 0, const LEN: usize = 0, const TO: usize = {FROM + LEN}>;
+struct Range<const FROM: usize = 0, const LEN: usize = 0, const TO: usize = FROM>;
fn foo<const SIZE : usize = 5>() { }
struct Range<const FROM : usize = 0, const LEN : usize = 0, const TO : usize =
- { FROM + LEN }>;
+ FROM>;
#![allow(incomplete_features)]
#[repr(C)]
-pub struct Loaf<T: Sized, const N: usize = 1usize> {
+pub struct Loaf<T: Sized, const N: usize = 1> {
head: [T; N],
slice: [T],
}
+++ /dev/null
-error: type parameters must be declared prior to const parameters
- --> $DIR/simple-defaults.rs:8:40
- |
-LL | struct FixedOutput<'a, const N: usize, T=u32> {
- | ---------------------^----- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>`
-
-error: aborting due to previous error
-
-// [full] run-pass
-// revisions: min full
-// Checks some basic test cases for defaults.
+// run-pass
+// Checks that type param defaults are allowed after const params.
+// revisions: full min
#![cfg_attr(full, feature(const_generics))]
-#![cfg_attr(full, allow(incomplete_features))]
+#![feature(const_generics_defaults)]
+#![allow(incomplete_features)]
#![allow(dead_code)]
struct FixedOutput<'a, const N: usize, T=u32> {
- //[min]~^ ERROR type parameters must be declared prior to const parameters
out: &'a [T; N],
}
--- /dev/null
+// check-pass
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))]
+#![feature(const_generics_defaults)]
+#![allow(incomplete_features)]
+
+struct N;
+
+struct Foo<const N: usize = 1, T = N>(T);
+
+impl Foo {
+ fn new() -> Self {
+ Foo(N)
+ }
+}
+
+fn main() {
+ let Foo::<1, N>(N) = Foo::new();
+}
error: generic parameters with a default must be trailing
- --> $DIR/wrong-order.rs:4:10
+ --> $DIR/wrong-order.rs:6:10
|
LL | struct A<T = u32, const N: usize> {
| ^
- |
- = note: using type defaults and const parameters in the same parameter list is currently not permitted
-
-warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
- --> $DIR/wrong-order.rs:2:27
- |
-LL | #![cfg_attr(full, feature(const_generics))]
- | ^^^^^^^^^^^^^^
- |
- = note: `#[warn(incomplete_features)]` on by default
- = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
-error: aborting due to previous error; 1 warning emitted
+error: aborting due to previous error
error: generic parameters with a default must be trailing
- --> $DIR/wrong-order.rs:4:10
+ --> $DIR/wrong-order.rs:6:10
|
LL | struct A<T = u32, const N: usize> {
| ^
- |
- = note: using type defaults and const parameters in the same parameter list is currently not permitted
error: aborting due to previous error
// revisions: full min
-#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(full, feature(const_generics))]
+#![feature(const_generics_defaults)]
+#![allow(incomplete_features)]
struct A<T = u32, const N: usize> {
//~^ ERROR generic parameters with a default must be trailing
--- /dev/null
+// check-pass
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))]
+#![cfg_attr(full, allow(incomplete_features))]
+
+trait T<const A: usize> {
+ fn f();
+}
+struct S;
+
+impl T<0usize> for S {
+ fn f() {}
+}
+
+fn main() {
+ let _err = <S as T<0usize>>::f();
+}
--- /dev/null
+// Regression test for #84408.
+// check-pass
+
+#![feature(const_generics, const_evaluatable_checked)]
+#![allow(incomplete_features)]
+
+trait Melon<const X: usize> {
+ fn new(arr: [i32; X]) -> Self;
+ fn change<T: Melon<X>>(self) -> T;
+}
+
+struct Foo([i32; 5]);
+struct Bar<const A: usize, const B: usize>([i32; A + B])
+where
+ [(); A + B]: ;
+
+impl Melon<5> for Foo {
+ fn new(arr: [i32; 5]) -> Self {
+ Foo(arr)
+ }
+ fn change<T: Melon<5>>(self) -> T {
+ T::new(self.0)
+ }
+}
+
+impl<const A: usize, const B: usize> Melon<{ A + B }> for Bar<A, B>
+where
+ [(); A + B]: ,
+{
+ fn new(arr: [i32; A + B]) -> Self {
+ Bar(arr)
+ }
+ fn change<T: Melon<{ A + B }>>(self) -> T {
+ T::new(self.0)
+ }
+}
+
+fn main() {}
+++ /dev/null
-// check-pass
-// revisions: full min
-#![cfg_attr(full, feature(const_generics))]
-#![cfg_attr(full, allow(incomplete_features))]
-
-trait T<const A: usize> {
- fn f();
-}
-struct S;
-
-impl T<0usize> for S {
- fn f() {}
-}
-
-fn main() {
- let _err = <S as T<0usize>>::f();
-}
error: generic parameters with a default must be trailing
- --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:12
+ --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:8:12
|
LL | struct Bar<T = [u8; N], const N: usize>(T);
| ^
|
= note: using type defaults and const parameters in the same parameter list is currently not permitted
-error: constant values inside of type parameter defaults must not depend on generic parameters
- --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:6:44
- |
-LL | struct Foo<T, U = [u8; std::mem::size_of::<T>()]>(T, U);
- | ^ the anonymous constant must not depend on the parameter `T`
-
-error: constant values inside of type parameter defaults must not depend on generic parameters
- --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:21
+error[E0128]: generic parameters with a default cannot use forward declared identifiers
+ --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:8:21
|
LL | struct Bar<T = [u8; N], const N: usize>(T);
- | ^ the anonymous constant must not depend on the parameter `N`
+ | ^ defaulted generic parameters cannot be forward declared
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
+For more information about this error, try `rustc --explain E0128`.
error: generic parameters with a default must be trailing
- --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:12
+ --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:8:12
|
LL | struct Bar<T = [u8; N], const N: usize>(T);
| ^
= note: using type defaults and const parameters in the same parameter list is currently not permitted
error: generic parameters may not be used in const operations
- --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:6:44
+ --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:5:44
|
LL | struct Foo<T, U = [u8; std::mem::size_of::<T>()]>(T, U);
| ^ cannot perform const operation using `T`
= note: type parameters may not be used in const expressions
= help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions
-error: constant values inside of type parameter defaults must not depend on generic parameters
- --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:21
+error[E0128]: generic parameters with a default cannot use forward declared identifiers
+ --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:8:21
|
LL | struct Bar<T = [u8; N], const N: usize>(T);
- | ^ the anonymous constant must not depend on the parameter `N`
+ | ^ defaulted generic parameters cannot be forward declared
error: aborting due to 3 previous errors
+For more information about this error, try `rustc --explain E0128`.
// revisions: full min
-
#![cfg_attr(full, feature(const_generics))]
#![cfg_attr(full, allow(incomplete_features))]
struct Foo<T, U = [u8; std::mem::size_of::<T>()]>(T, U);
-//[full]~^ ERROR constant values inside of type parameter defaults
-//[min]~^^ ERROR generic parameters may not be used in const operations
+//[min]~^ ERROR generic parameters may not be used in const operations
-// FIXME(const_generics_defaults): We still don't know how to deal with type defaults.
struct Bar<T = [u8; N], const N: usize>(T);
-//~^ ERROR constant values inside of type parameter defaults
+//~^ ERROR generic parameters with a default cannot use forward declared identifiers
//~| ERROR generic parameters with a default
fn main() {}
// run-pass
// Test a ZST enum whose dicriminant is ~0i128. This caused an ICE when casting to a i32.
-#![feature(test)]
+#![feature(bench_black_box)]
use std::hint::black_box;
#[derive(Copy, Clone)]
// issue-49296: Unsafe shenigans in constants can result in missing errors
-#![feature(const_fn)]
#![feature(const_fn_union)]
+#![feature(const_fn_trait_bound)]
const unsafe fn transmute<T: Copy, U: Copy>(t: T) -> U {
#[repr(C)]
// Test that const fn is illegal in a trait declaration, whether or
-// not a default is provided.
+// not a default is provided, and even with the feature gate.
#![feature(const_fn)]
// A very basic test of const fn functionality.
-#![feature(const_fn, const_indexing)]
+#![feature(const_indexing)]
+#![feature(const_fn_trait_bound)]
const fn add(x: u32, y: u32) -> u32 {
x + y
// run-pass
#![feature(const_discriminant)]
-#![feature(test)]
+#![feature(bench_black_box)]
#![allow(dead_code)]
use std::mem::{discriminant, Discriminant};
--- /dev/null
+error: fatal error triggered by #[rustc_error]
+ --> $DIR/const_fn_trait_bound.rs:17:1
+ |
+LL | fn main() {}
+ | ^^^^^^^^^
+
+error: aborting due to previous error
+
--- /dev/null
+// gate-test-const_fn_trait_bound
+
+// revisions: stock gated
+
+#![feature(rustc_attrs)]
+#![cfg_attr(gated, feature(const_fn_trait_bound))]
+
+const fn test1<T: std::ops::Add>() {}
+//[stock]~^ trait bounds
+const fn test2(_x: &dyn Send) {}
+//[stock]~^ trait bounds
+const fn test3() -> &'static dyn Send { loop {} }
+//[stock]~^ trait bounds
+
+
+#[rustc_error]
+fn main() {} //[gated]~ fatal error triggered by #[rustc_error]
--- /dev/null
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
+ --> $DIR/const_fn_trait_bound.rs:8:16
+ |
+LL | const fn test1<T: std::ops::Add>() {}
+ | ^
+ |
+ = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
+
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
+ --> $DIR/const_fn_trait_bound.rs:10:16
+ |
+LL | const fn test2(_x: &dyn Send) {}
+ | ^^
+ |
+ = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
+
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
+ --> $DIR/const_fn_trait_bound.rs:12:21
+ |
+LL | const fn test3() -> &'static dyn Send { loop {} }
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
--- /dev/null
+error: fatal error triggered by #[rustc_error]
+ --> $DIR/const_fn_unsize.rs:16:1
+ |
+LL | fn main() {}
+ | ^^^^^^^^^
+
+error: aborting due to previous error
+
--- /dev/null
+// gate-test-const_fn_unsize
+
+// revisions: stock gated
+
+#![feature(rustc_attrs)]
+#![cfg_attr(gated, feature(const_fn_unsize))]
+
+use std::ptr::NonNull;
+
+const fn test() {
+ let _x = NonNull::<[i32; 0]>::dangling() as NonNull<[i32]>;
+ //[stock]~^ unsizing cast
+}
+
+#[rustc_error]
+fn main() {} //[gated]~ fatal error triggered by #[rustc_error]
--- /dev/null
+error[E0658]: unsizing casts to types besides slices are not allowed in const fn
+ --> $DIR/const_fn_unsize.rs:11:14
+ |
+LL | let _x = NonNull::<[i32; 0]>::dangling() as NonNull<[i32]>;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #64992 <https://github.com/rust-lang/rust/issues/64992> for more information
+ = help: add `#![feature(const_fn_unsize)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
--> $DIR/min_const_fn.rs:84:16
|
LL | const fn foo11<T: std::fmt::Display>(t: T) -> T { t }
| ^
|
= note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
- = help: add `#![feature(const_fn)]` to the crate attributes to enable
+ = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
--> $DIR/min_const_fn.rs:86:18
|
LL | const fn foo11_2<T: Send>(t: T) -> T { t }
| ^
|
= note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
- = help: add `#![feature(const_fn)]` to the crate attributes to enable
+ = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error[E0013]: constant functions cannot refer to statics
--> $DIR/min_const_fn.rs:90:27
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
--> $DIR/min_const_fn.rs:110:6
|
LL | impl<T: std::fmt::Debug> Foo<T> {
| ^
|
= note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
- = help: add `#![feature(const_fn)]` to the crate attributes to enable
+ = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
--> $DIR/min_const_fn.rs:115:6
|
LL | impl<T: std::fmt::Debug + Sized> Foo<T> {
| ^
|
= note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
- = help: add `#![feature(const_fn)]` to the crate attributes to enable
+ = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
--> $DIR/min_const_fn.rs:120:6
|
LL | impl<T: Sync + Sized> Foo<T> {
| ^
|
= note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
- = help: add `#![feature(const_fn)]` to the crate attributes to enable
+ = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
--> $DIR/min_const_fn.rs:126:34
|
LL | const fn no_apit2(_x: AlanTuring<impl std::fmt::Debug>) {}
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
- = help: add `#![feature(const_fn)]` to the crate attributes to enable
+ = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error[E0493]: destructors cannot be evaluated at compile-time
--> $DIR/min_const_fn.rs:126:19
| |
| constant functions cannot evaluate destructors
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
--> $DIR/min_const_fn.rs:129:22
|
LL | const fn no_apit(_x: impl std::fmt::Debug) {}
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
- = help: add `#![feature(const_fn)]` to the crate attributes to enable
+ = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error[E0493]: destructors cannot be evaluated at compile-time
--> $DIR/min_const_fn.rs:129:18
| |
| constant functions cannot evaluate destructors
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
--> $DIR/min_const_fn.rs:132:23
|
LL | const fn no_dyn_trait(_x: &dyn std::fmt::Debug) {}
| ^^
|
= note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
- = help: add `#![feature(const_fn)]` to the crate attributes to enable
+ = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
--> $DIR/min_const_fn.rs:134:32
|
LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
- = help: add `#![feature(const_fn)]` to the crate attributes to enable
+ = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
-error[E0723]: unsizing casts to types besides slices are not allowed in const fn
+error[E0658]: unsizing casts to types besides slices are not allowed in const fn
--> $DIR/min_const_fn.rs:134:63
|
LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
| ^^^
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
- = help: add `#![feature(const_fn)]` to the crate attributes to enable
+ = note: see issue #64992 <https://github.com/rust-lang/rust/issues/64992> for more information
+ = help: add `#![feature(const_fn_unsize)]` to the crate attributes to enable
-error[E0723]: unsizing casts to types besides slices are not allowed in const fn
+error[E0658]: unsizing casts to types besides slices are not allowed in const fn
--> $DIR/min_const_fn.rs:134:63
|
LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
| ^^^
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
- = help: add `#![feature(const_fn)]` to the crate attributes to enable
+ = note: see issue #64992 <https://github.com/rust-lang/rust/issues/64992> for more information
+ = help: add `#![feature(const_fn_unsize)]` to the crate attributes to enable
-error[E0723]: unsizing casts to types besides slices are not allowed in const fn
+error[E0658]: unsizing casts to types besides slices are not allowed in const fn
--> $DIR/min_const_fn.rs:141:42
|
LL | const fn really_no_traits_i_mean_it() { (&() as &dyn std::fmt::Debug, ()).1 }
| ^^^
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
- = help: add `#![feature(const_fn)]` to the crate attributes to enable
+ = note: see issue #64992 <https://github.com/rust-lang/rust/issues/64992> for more information
+ = help: add `#![feature(const_fn_unsize)]` to the crate attributes to enable
error[E0658]: function pointers cannot appear in constant functions
--> $DIR/min_const_fn.rs:144:21
error: aborting due to 39 previous errors
-Some errors have detailed explanations: E0013, E0493, E0658, E0723.
+Some errors have detailed explanations: E0013, E0493, E0658.
For more information about an error, try `rustc --explain E0013`.
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
--> $DIR/min_const_fn_dyn.rs:9:5
|
LL | x.0.field;
| ^^^^^^^^^
|
= note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
- = help: add `#![feature(const_fn)]` to the crate attributes to enable
+ = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
-error[E0723]: unsizing casts to types besides slices are not allowed in const fn
+error[E0658]: unsizing casts to types besides slices are not allowed in const fn
--> $DIR/min_const_fn_dyn.rs:12:66
|
LL | const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasDyn { field: &0 }) }
| ^^
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
- = help: add `#![feature(const_fn)]` to the crate attributes to enable
+ = note: see issue #64992 <https://github.com/rust-lang/rust/issues/64992> for more information
+ = help: add `#![feature(const_fn_unsize)]` to the crate attributes to enable
error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0723`.
+For more information about this error, try `rustc --explain E0658`.
-error[E0723]: unsizing casts to types besides slices are not allowed in const fn
+error[E0658]: unsizing casts to types besides slices are not allowed in const fn
--> $DIR/unsizing-cast-non-null.rs:6:5
|
LL | NonNull::<[T; 0]>::dangling()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
- = help: add `#![feature(const_fn)]` to the crate attributes to enable
+ = note: see issue #64992 <https://github.com/rust-lang/rust/issues/64992> for more information
+ = help: add `#![feature(const_fn_unsize)]` to the crate attributes to enable
error: aborting due to previous error
-For more information about this error, try `rustc --explain E0723`.
+For more information about this error, try `rustc --explain E0658`.
#![stable(feature = "core", since = "1.6.0")]
#![feature(rustc_const_unstable)]
#![feature(staged_api)]
-#![feature(const_fn)]
+#![feature(const_fn_trait_bound)]
enum Opt<T> {
Some(T),
--- /dev/null
+// compile-flags: --target x86_64-unknown-uefi
+// rustc-env:CARGO=/usr/bin/cargo
+// rustc-env:RUSTUP_HOME=/home/bors/.rustup
+#![no_core]
+extern crate core;
+//~^ ERROR can't find crate for `core`
+//~| NOTE can't find crate
+//~| NOTE target may not be installed
+//~| HELP consider building the standard library from source with `cargo build -Zbuild-std`
+//~| HELP consider downloading the target with `rustup target add x86_64-unknown-uefi`
+fn main() {}
--- /dev/null
+error[E0463]: can't find crate for `core`
+ --> $DIR/missing-std.rs:5:1
+ |
+LL | extern crate core;
+ | ^^^^^^^^^^^^^^^^^^ can't find crate
+ |
+ = note: the `x86_64-unknown-uefi` target may not be installed
+ = help: consider downloading the target with `rustup target add x86_64-unknown-uefi`
+ = help: consider building the standard library from source with `cargo build -Zbuild-std`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0463`.
--- /dev/null
+// check-pass
+#![allow(incomplete_features)]
+#![feature(const_generics_defaults)]
+
+#[derive(Clone, PartialEq, Debug)]
+struct Example<T, const N: usize = 1usize>([T; N]);
+
+fn main() {
+ let a = Example([(); 16]);
+ let b = a.clone();
+ if a != b {
+ let _c = format!("{:?}", a);
+ }
+}
--- /dev/null
+fn main() {
+ 1 = 2; //~ ERROR invalid left-hand side of assignment
+ 1 += 2; //~ ERROR invalid left-hand side of assignment
+ (1, 2) = (3, 4); //~ ERROR destructuring assignments are unstable
+ //~| ERROR invalid left-hand side of assignment
+ //~| ERROR invalid left-hand side of assignment
+
+ let (a, b) = (1, 2);
+ (a, b) = (3, 4); //~ ERROR destructuring assignments are unstable
+
+ None = Some(3); //~ ERROR invalid left-hand side of assignment
+}
--- /dev/null
+error[E0658]: destructuring assignments are unstable
+ --> $DIR/bad-expr-lhs.rs:4:12
+ |
+LL | (1, 2) = (3, 4);
+ | ------ ^
+ | |
+ | cannot assign to this expression
+ |
+ = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+ = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+ --> $DIR/bad-expr-lhs.rs:9:12
+ |
+LL | (a, b) = (3, 4);
+ | ------ ^
+ | |
+ | cannot assign to this expression
+ |
+ = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+ = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0070]: invalid left-hand side of assignment
+ --> $DIR/bad-expr-lhs.rs:2:7
+ |
+LL | 1 = 2;
+ | - ^
+ | |
+ | cannot assign to this expression
+
+error[E0067]: invalid left-hand side of assignment
+ --> $DIR/bad-expr-lhs.rs:3:7
+ |
+LL | 1 += 2;
+ | - ^^
+ | |
+ | cannot assign to this expression
+
+error[E0070]: invalid left-hand side of assignment
+ --> $DIR/bad-expr-lhs.rs:4:12
+ |
+LL | (1, 2) = (3, 4);
+ | - ^
+ | |
+ | cannot assign to this expression
+
+error[E0070]: invalid left-hand side of assignment
+ --> $DIR/bad-expr-lhs.rs:4:12
+ |
+LL | (1, 2) = (3, 4);
+ | - ^
+ | |
+ | cannot assign to this expression
+
+error[E0070]: invalid left-hand side of assignment
+ --> $DIR/bad-expr-lhs.rs:11:10
+ |
+LL | None = Some(3);
+ | ---- ^
+ | |
+ | cannot assign to this expression
+
+error: aborting due to 7 previous errors
+
+Some errors have detailed explanations: E0067, E0070, E0658.
+For more information about an error, try `rustc --explain E0067`.
--- /dev/null
+#[macro_use] mod bleh {
+ pub macro_rules! foo { //~ ERROR can't qualify macro_rules invocation
+ ($n:ident) => (
+ fn $n () -> i32 {
+ 1
+ }
+ )
+ }
+
+}
+
+foo!(meh);
+
+fn main() {
+ println!("{}", meh());
+}
--- /dev/null
+error: can't qualify macro_rules invocation with `pub`
+ --> $DIR/pub-macro-rules.rs:2:5
+ |
+LL | pub macro_rules! foo {
+ | ^^^ help: try exporting the macro: `#[macro_export]`
+
+error: aborting due to previous error
+
+++ /dev/null
-#![feature(main)]
-
-#[main]
-fn foo() {}
-
-#[main]
-fn f() {}
-//~^ ERROR E0137
+++ /dev/null
-error[E0137]: multiple functions with a `#[main]` attribute
- --> $DIR/E0137.rs:7:1
- |
-LL | fn foo() {}
- | ----------- first `#[main]` function
-...
-LL | fn f() {}
- | ^^^^^^^^^ additional `#[main]` function
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0137`.
= help: unsized fn params are gated as an unstable feature
help: function arguments must have a statically known size, borrowed types always have a known size
|
-LL | fn f(&p: Path) { }
- | ^
+LL | fn f(p: &Path) { }
+ | ^
error[E0277]: the trait bound `i32: Foo` is not satisfied
--> $DIR/E0277.rs:15:15
const fn foo() -> usize { 0 } // ok
trait Foo {
- const fn foo() -> u32; //~ ERROR const fn is unstable
- //~| ERROR functions in traits cannot be declared const
- const fn bar() -> u32 { 0 } //~ ERROR const fn is unstable
- //~| ERROR functions in traits cannot be declared const
+ const fn foo() -> u32; //~ ERROR functions in traits cannot be declared const
+ const fn bar() -> u32 { 0 } //~ ERROR functions in traits cannot be declared const
}
impl Foo for u32 {
| ^^^^^ functions in traits cannot be const
error[E0379]: functions in traits cannot be declared const
- --> $DIR/feature-gate-const_fn.rs:8:5
+ --> $DIR/feature-gate-const_fn.rs:7:5
|
LL | const fn bar() -> u32 { 0 }
| ^^^^^ functions in traits cannot be const
error[E0379]: functions in traits cannot be declared const
- --> $DIR/feature-gate-const_fn.rs:13:5
+ --> $DIR/feature-gate-const_fn.rs:11:5
|
LL | const fn foo() -> u32 { 0 }
| ^^^^^ functions in traits cannot be const
-error[E0658]: const fn is unstable
- --> $DIR/feature-gate-const_fn.rs:6:5
- |
-LL | const fn foo() -> u32;
- | ^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
- = help: add `#![feature(const_fn)]` to the crate attributes to enable
-
-error[E0658]: const fn is unstable
- --> $DIR/feature-gate-const_fn.rs:8:5
- |
-LL | const fn bar() -> u32 { 0 }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
- = help: add `#![feature(const_fn)]` to the crate attributes to enable
-
-error: aborting due to 5 previous errors
+error: aborting due to 3 previous errors
-Some errors have detailed explanations: E0379, E0658.
-For more information about an error, try `rustc --explain E0379`.
+For more information about this error, try `rustc --explain E0379`.
--- /dev/null
+// check that `move_size_limit is feature-gated
+
+#![move_size_limit = "42"] //~ ERROR the `#[move_size_limit]` attribute is an experimental feature
+
+fn main() {}
--- /dev/null
+error[E0658]: the `#[move_size_limit]` attribute is an experimental feature
+ --> $DIR/feature-gate-large-assignments.rs:3:1
+ |
+LL | #![move_size_limit = "42"]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #83518 <https://github.com/rust-lang/rust/issues/83518> for more information
+ = help: add `#![feature(large_assignments)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
+++ /dev/null
-#[main]
-fn foo() {} //~ ERROR: declaration of a non-standard `#[main]` function may change over time
+++ /dev/null
-error[E0658]: declaration of a non-standard `#[main]` function may change over time, for now a top-level `fn main()` is required
- --> $DIR/feature-gate-main.rs:2:1
- |
-LL | fn foo() {}
- | ^^^^^^^^^^^
- |
- = note: see issue #29634 <https://github.com/rust-lang/rust/issues/29634> for more information
- = help: add `#![feature(main)]` to the crate attributes to enable
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0658`.
const fn foo() -> usize { 0 } // stabilized
trait Foo {
- const fn foo() -> u32; //~ ERROR const fn is unstable
- //~| ERROR functions in traits cannot be declared const
- const fn bar() -> u32 { 0 } //~ ERROR const fn is unstable
- //~| ERROR functions in traits cannot be declared const
+ const fn foo() -> u32; //~ ERROR functions in traits cannot be declared const
+ const fn bar() -> u32 { 0 } //~ ERROR functions in traits cannot be declared const
}
impl Foo for u32 {
| ^^^^^ functions in traits cannot be const
error[E0379]: functions in traits cannot be declared const
- --> $DIR/feature-gate-min_const_fn.rs:8:5
+ --> $DIR/feature-gate-min_const_fn.rs:7:5
|
LL | const fn bar() -> u32 { 0 }
| ^^^^^ functions in traits cannot be const
error[E0379]: functions in traits cannot be declared const
- --> $DIR/feature-gate-min_const_fn.rs:13:5
+ --> $DIR/feature-gate-min_const_fn.rs:11:5
|
LL | const fn foo() -> u32 { 0 }
| ^^^^^ functions in traits cannot be const
-error[E0658]: const fn is unstable
- --> $DIR/feature-gate-min_const_fn.rs:6:5
- |
-LL | const fn foo() -> u32;
- | ^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
- = help: add `#![feature(const_fn)]` to the crate attributes to enable
-
-error[E0658]: const fn is unstable
- --> $DIR/feature-gate-min_const_fn.rs:8:5
- |
-LL | const fn bar() -> u32 { 0 }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
- = help: add `#![feature(const_fn)]` to the crate attributes to enable
-
-error: aborting due to 5 previous errors
+error: aborting due to 3 previous errors
-Some errors have detailed explanations: E0379, E0658.
-For more information about an error, try `rustc --explain E0379`.
+For more information about this error, try `rustc --explain E0379`.
--- /dev/null
+#![crate_type = "lib"]
+
+#[no_coverage]
+#[feature(no_coverage)] // does not have to be enabled before `#[no_coverage]`
+fn no_coverage_is_enabled_on_this_function() {}
+
+#[no_coverage] //~ ERROR the `#[no_coverage]` attribute is an experimental feature
+fn requires_feature_no_coverage() {}
--- /dev/null
+error[E0658]: the `#[no_coverage]` attribute is an experimental feature
+ --> $DIR/feature-gate-no_coverage.rs:7:1
+ |
+LL | #[no_coverage]
+ | ^^^^^^^^^^^^^^
+ |
+ = note: see issue #84605 <https://github.com/rust-lang/rust/issues/84605> for more information
+ = help: add `#![feature(no_coverage)]` to the crate attributes to enable
+ = help: or, alternatively, add `#[feature(no_coverage)]` to the function
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
+++ /dev/null
-extern crate core as bäz; //~ ERROR non-ascii idents
-
-use föö::bar; //~ ERROR non-ascii idents
-
-mod föö { //~ ERROR non-ascii idents
- pub fn bar() {}
-}
-
-fn bär( //~ ERROR non-ascii idents
- bäz: isize //~ ERROR non-ascii idents
- ) {
- let _ö: isize; //~ ERROR non-ascii idents
-
- match (1, 2) {
- (_ä, _) => {} //~ ERROR non-ascii idents
- }
-}
-
-struct Föö { //~ ERROR non-ascii idents
- föö: isize //~ ERROR non-ascii idents
-}
-
-enum Bär { //~ ERROR non-ascii idents
- Bäz { //~ ERROR non-ascii idents
- qüx: isize //~ ERROR non-ascii idents
- }
-}
-
-extern "C" {
- fn qüx(); //~ ERROR non-ascii idents
- //~^ ERROR items in `extern` blocks
-}
-
-fn main() {}
+++ /dev/null
-error: items in `extern` blocks cannot use non-ascii identifiers
- --> $DIR/feature-gate-non_ascii_idents.rs:30:8
- |
-LL | extern "C" {
- | ---------- in this `extern` block
-LL | fn qüx();
- | ^^^
- |
- = note: This limitation may be lifted in the future; see issue #83942 <https://github.com/rust-lang/rust/issues/83942> for more information
-
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/feature-gate-non_ascii_idents.rs:1:22
- |
-LL | extern crate core as bäz;
- | ^^^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/feature-gate-non_ascii_idents.rs:3:5
- |
-LL | use föö::bar;
- | ^^^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/feature-gate-non_ascii_idents.rs:5:5
- |
-LL | mod föö {
- | ^^^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/feature-gate-non_ascii_idents.rs:9:4
- |
-LL | fn bär(
- | ^^^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/feature-gate-non_ascii_idents.rs:10:5
- |
-LL | bäz: isize
- | ^^^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/feature-gate-non_ascii_idents.rs:12:9
- |
-LL | let _ö: isize;
- | ^^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/feature-gate-non_ascii_idents.rs:15:10
- |
-LL | (_ä, _) => {}
- | ^^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/feature-gate-non_ascii_idents.rs:19:8
- |
-LL | struct Föö {
- | ^^^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/feature-gate-non_ascii_idents.rs:20:5
- |
-LL | föö: isize
- | ^^^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/feature-gate-non_ascii_idents.rs:23:6
- |
-LL | enum Bär {
- | ^^^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/feature-gate-non_ascii_idents.rs:24:5
- |
-LL | Bäz {
- | ^^^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/feature-gate-non_ascii_idents.rs:25:9
- |
-LL | qüx: isize
- | ^^^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/feature-gate-non_ascii_idents.rs:30:8
- |
-LL | fn qüx();
- | ^^^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error: aborting due to 14 previous errors
-
-For more information about this error, try `rustc --explain E0658`.
+++ /dev/null
-pub macro_rules! m1 { () => {} } //~ ERROR `pub` on `macro_rules` items is unstable
-
-#[cfg(FALSE)]
-pub macro_rules! m2 { () => {} } //~ ERROR `pub` on `macro_rules` items is unstable
-
-pub(crate) macro_rules! m3 { () => {} } //~ ERROR `pub` on `macro_rules` items is unstable
-
-pub(in self) macro_rules! m4 { () => {} } //~ ERROR `pub` on `macro_rules` items is unstable
-
-fn main() {}
+++ /dev/null
-error[E0658]: `pub` on `macro_rules` items is unstable
- --> $DIR/feature-gate-pub_macro_rules.rs:1:1
- |
-LL | pub macro_rules! m1 { () => {} }
- | ^^^
- |
- = note: see issue #78855 <https://github.com/rust-lang/rust/issues/78855> for more information
- = help: add `#![feature(pub_macro_rules)]` to the crate attributes to enable
-
-error[E0658]: `pub` on `macro_rules` items is unstable
- --> $DIR/feature-gate-pub_macro_rules.rs:4:1
- |
-LL | pub macro_rules! m2 { () => {} }
- | ^^^
- |
- = note: see issue #78855 <https://github.com/rust-lang/rust/issues/78855> for more information
- = help: add `#![feature(pub_macro_rules)]` to the crate attributes to enable
-
-error[E0658]: `pub` on `macro_rules` items is unstable
- --> $DIR/feature-gate-pub_macro_rules.rs:6:1
- |
-LL | pub(crate) macro_rules! m3 { () => {} }
- | ^^^^^^^^^^
- |
- = note: see issue #78855 <https://github.com/rust-lang/rust/issues/78855> for more information
- = help: add `#![feature(pub_macro_rules)]` to the crate attributes to enable
-
-error[E0658]: `pub` on `macro_rules` items is unstable
- --> $DIR/feature-gate-pub_macro_rules.rs:8:1
- |
-LL | pub(in self) macro_rules! m4 { () => {} }
- | ^^^^^^^^^^^^
- |
- = note: see issue #78855 <https://github.com/rust-lang/rust/issues/78855> for more information
- = help: add `#![feature(pub_macro_rules)]` to the crate attributes to enable
-
-error: aborting due to 4 previous errors
-
-For more information about this error, try `rustc --explain E0658`.
= help: unsized fn params are gated as an unstable feature
help: function arguments must have a statically known size, borrowed types always have a known size
|
-LL | fn foo(&x: dyn Foo) {
- | ^
+LL | fn foo(x: &dyn Foo) {
+ | ^
error[E0277]: the size for values of type `(dyn Foo + 'static)` cannot be known at compilation time
--> $DIR/feature-gate-unsized_fn_params.rs:24:5
= help: unsized fn params are gated as an unstable feature
help: function arguments must have a statically known size, borrowed types always have a known size
|
-LL | fn f(&f: dyn FnOnce()) {}
- | ^
+LL | fn f(f: &dyn FnOnce()) {}
+ | ^
error: aborting due to previous error
#![macro_export]
//~^ ERROR: `macro_export` attribute cannot be used at crate level
-#![main]
-//~^ ERROR: `main` attribute cannot be used at crate level
+#![rustc_main] //~ ERROR: the `#[rustc_main]` attribute is used internally to specify
+//~^ ERROR: `rustc_main` attribute cannot be used at crate level
#![start]
//~^ ERROR: `start` attribute cannot be used at crate level
#![repr()]
//~| NOTE not a function or static
}
-#[main]
-//~^ ERROR: `main` attribute can only be used on functions
-mod main {
- mod inner { #![main] }
- //~^ ERROR: `main` attribute can only be used on functions
-
- // for `fn f()` case, see feature-gate-main.rs
-
- #[main] struct S;
- //~^ ERROR: `main` attribute can only be used on functions
-
- #[main] type T = S;
- //~^ ERROR: `main` attribute can only be used on functions
-
- #[main] impl S { }
- //~^ ERROR: `main` attribute can only be used on functions
-}
-
#[start]
//~^ ERROR: `start` attribute can only be used on functions
mod start {
+error[E0658]: the `#[rustc_main]` attribute is used internally to specify test entry point function
+ --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:14:1
+ |
+LL | #![rustc_main]
+ | ^^^^^^^^^^^^^^
+ |
+ = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
+
error: attribute must be of the form `#[inline]` or `#[inline(always|never)]`
--> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:40:5
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
-error: `main` attribute can only be used on functions
- --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:109:1
- |
-LL | #[main]
- | ^^^^^^^
-
-error: `main` attribute can only be used on functions
- --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:112:17
- |
-LL | mod inner { #![main] }
- | ^^^^^^^^
-
-error: `main` attribute can only be used on functions
- --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:117:5
- |
-LL | #[main] struct S;
- | ^^^^^^^
-
-error: `main` attribute can only be used on functions
- --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:120:5
- |
-LL | #[main] type T = S;
- | ^^^^^^^
-
-error: `main` attribute can only be used on functions
- --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:123:5
- |
-LL | #[main] impl S { }
- | ^^^^^^^
-
error: `start` attribute can only be used on functions
- --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:127:1
+ --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:109:1
|
LL | #[start]
| ^^^^^^^^
error: `start` attribute can only be used on functions
- --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:130:17
+ --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:112:17
|
LL | mod inner { #![start] }
| ^^^^^^^^^
error: `start` attribute can only be used on functions
- --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:135:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:117:5
|
LL | #[start] struct S;
| ^^^^^^^^
error: `start` attribute can only be used on functions
- --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:138:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:120:5
|
LL | #[start] type T = S;
| ^^^^^^^^
error: `start` attribute can only be used on functions
- --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:141:5
+ --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:123:5
|
LL | #[start] impl S { }
| ^^^^^^^^
LL | #![macro_export]
| ^^^^^^^^^^^^^^^^
-error: `main` attribute cannot be used at crate level
+error: `rustc_main` attribute cannot be used at crate level
--> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:14:1
|
-LL | #![main]
- | ^^^^^^^^
+LL | #![rustc_main]
+ | ^^^^^^^^^^^^^^
error: `start` attribute cannot be used at crate level
--> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:16:1
LL | #[export_name = "2200"] impl S { }
| ^^^^^^^^^^^^^^^^^^^^^^^ ---------- not a function or static
-error: aborting due to 36 previous errors
+error: aborting due to 32 previous errors
-For more information about this error, try `rustc --explain E0518`.
+Some errors have detailed explanations: E0518, E0658.
+For more information about an error, try `rustc --explain E0518`.
--- /dev/null
+thread_local!(static X: u32 = const { 0 });
+//~^ ERROR: use of unstable library feature 'thread_local_const_init'
+
+fn main() {}
--- /dev/null
+error[E0658]: use of unstable library feature 'thread_local_const_init'
+ --> $DIR/thread-local-const-init.rs:1:1
+ |
+LL | thread_local!(static X: u32 = const { 0 });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #84223 <https://github.com/rust-lang/rust/issues/84223> for more information
+ = help: add `#![feature(thread_local_const_init)]` to the crate attributes to enable
+ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
--- /dev/null
+fn main(x: isize) { } //~ ERROR: `main` function has wrong type [E0580]
--- /dev/null
+error[E0580]: `main` function has wrong type
+ --> $DIR/bad-main.rs:1:1
+ |
+LL | fn main(x: isize) { }
+ | ^^^^^^^^^^^^^^^^^ incorrect number of function parameters
+ |
+ = note: expected fn pointer `fn()`
+ found fn pointer `fn(isize)`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0580`.
-// check-pass
-
#![feature(generic_associated_types)]
//~^ WARNING: the feature `generic_associated_types` is incomplete
#![feature(associated_type_defaults)]
}
fn f(_arg : Box<dyn for<'a> Foo<A<'a> = &'a ()>>) {}
+//~^ the trait `Foo` cannot be made into an object
fn main() {
warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
- --> $DIR/gat-in-trait-path.rs:3:12
+ --> $DIR/gat-in-trait-path.rs:1:12
|
LL | #![feature(generic_associated_types)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information
-warning: 1 warning emitted
+error[E0038]: the trait `Foo` cannot be made into an object
+ --> $DIR/gat-in-trait-path.rs:22:13
+ |
+LL | fn f(_arg : Box<dyn for<'a> Foo<A<'a> = &'a ()>>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Foo` cannot be made into an object
+ |
+ = help: consider moving `A` to another trait
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+ --> $DIR/gat-in-trait-path.rs:6:10
+ |
+LL | trait Foo {
+ | --- this trait cannot be made into an object...
+LL | type A<'a> where Self: 'a;
+ | ^ ...because it contains the generic associated type `A`
+
+error: aborting due to previous error; 1 warning emitted
+For more information about this error, try `rustc --explain E0038`.
-// check-pass
-
#![feature(generic_associated_types)]
//~^ WARNING: the feature `generic_associated_types` is incomplete
}
fn _func1<'a>(_x: Box<dyn X<Y<'a>=&'a ()>>) {}
+//~^ ERROR the trait `X` cannot be made into an object
fn main() {}
warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
- --> $DIR/issue-67510-pass.rs:3:12
+ --> $DIR/issue-67510-pass.rs:1:12
|
LL | #![feature(generic_associated_types)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information
-warning: 1 warning emitted
+error[E0038]: the trait `X` cannot be made into an object
+ --> $DIR/issue-67510-pass.rs:8:19
+ |
+LL | fn _func1<'a>(_x: Box<dyn X<Y<'a>=&'a ()>>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object
+ |
+ = help: consider moving `Y` to another trait
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+ --> $DIR/issue-67510-pass.rs:5:10
+ |
+LL | trait X {
+ | - this trait cannot be made into an object...
+LL | type Y<'a>;
+ | ^ ...because it contains the generic associated type `Y`
+
+error: aborting due to previous error; 1 warning emitted
+For more information about this error, try `rustc --explain E0038`.
--- /dev/null
+// check-pass
+
+#![allow(incomplete_features)]
+#![feature(generic_associated_types)]
+
+trait Document {
+ type Cursor<'a>: DocCursor<'a>;
+
+ fn cursor(&self) -> Self::Cursor<'_>;
+}
+
+struct DocumentImpl {}
+
+impl Document for DocumentImpl {
+ type Cursor<'a> = DocCursorImpl<'a>;
+
+ fn cursor(&self) -> Self::Cursor<'_> {
+ DocCursorImpl {
+ document: &self,
+ }
+ }
+}
+
+
+trait DocCursor<'a> {}
+
+struct DocCursorImpl<'a> {
+ document: &'a DocumentImpl,
+}
+
+impl<'a> DocCursor<'a> for DocCursorImpl<'a> {}
+
+struct Lexer<'d, Cursor>
+where
+ Cursor: DocCursor<'d>,
+{
+ cursor: Cursor,
+ _phantom: std::marker::PhantomData<&'d ()>,
+}
+
+
+impl<'d, Cursor> Lexer<'d, Cursor>
+where
+ Cursor: DocCursor<'d>,
+{
+ pub fn from<Doc>(document: &'d Doc) -> Lexer<'d, Cursor>
+ where
+ Doc: Document<Cursor<'d> = Cursor>,
+ {
+ Lexer {
+ cursor: document.cursor(),
+ _phantom: std::marker::PhantomData,
+ }
+ }
+}
+
+pub fn main() {
+ let doc = DocumentImpl {};
+ let lexer: Lexer<'_, DocCursorImpl<'_>> = Lexer::from(&doc);
+}
--- /dev/null
+#![allow(incomplete_features)]
+#![feature(generic_associated_types)]
+
+trait Document {
+ type Cursor<'a>: DocCursor<'a>;
+
+ fn cursor(&self) -> Self::Cursor<'_>;
+}
+
+struct DocumentImpl {}
+
+impl Document for DocumentImpl {
+ type Cursor<'a> = DocCursorImpl<'a>;
+
+ fn cursor(&self) -> Self::Cursor<'_> {
+ DocCursorImpl {
+ document: &self,
+ }
+ }
+}
+
+
+trait DocCursor<'a> {}
+
+struct DocCursorImpl<'a> {
+ document: &'a DocumentImpl,
+}
+
+impl<'a> DocCursor<'a> for DocCursorImpl<'a> {}
+
+struct Lexer<'d, Cursor>
+where
+ Cursor: DocCursor<'d>,
+{
+ cursor: Cursor,
+ _phantom: std::marker::PhantomData<&'d ()>,
+}
+
+
+impl<'d, Cursor> Lexer<'d, Cursor>
+where
+ Cursor: DocCursor<'d>,
+{
+ pub fn from<Doc>(document: &'d Doc) -> Lexer<'d, Cursor>
+ where
+ Doc: Document<Cursor<'d> = Cursor>,
+ {
+ Lexer {
+ cursor: document.cursor(),
+ _phantom: std::marker::PhantomData,
+ }
+ }
+}
+
+fn create_doc() -> impl Document<Cursor<'_> = DocCursorImpl<'_>> {
+ //~^ ERROR: missing lifetime specifier
+ DocumentImpl {}
+}
+
+pub fn main() {
+ let doc = create_doc();
+ let lexer: Lexer<'_, DocCursorImpl<'_>> = Lexer::from(&doc);
+}
--- /dev/null
+error[E0106]: missing lifetime specifier
+ --> $DIR/issue-70304.rs:55:41
+ |
+LL | fn create_doc() -> impl Document<Cursor<'_> = DocCursorImpl<'_>> {
+ | ^^ expected named lifetime parameter
+ |
+ = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the `'static` lifetime
+ |
+LL | fn create_doc() -> impl Document<Cursor<'static> = DocCursorImpl<'_>> {
+ | ^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0106`.
--- /dev/null
+#![allow(incomplete_features)]
+#![feature(generic_associated_types)]
+
+trait Provider {
+ type A<'a>;
+ //~^ ERROR: missing generics for associated type
+}
+
+impl Provider for () {
+ type A<'a> = ();
+}
+
+struct Holder<B> {
+ inner: Box<dyn Provider<A = B>>,
+}
+
+fn main() {
+ Holder {
+ inner: Box::new(()),
+ };
+}
--- /dev/null
+error[E0107]: missing generics for associated type `Provider::A`
+ --> $DIR/issue-71176.rs:5:10
+ |
+LL | type A<'a>;
+ | ^ expected 1 lifetime argument
+ |
+note: associated type defined here, with 1 lifetime parameter: `'a`
+ --> $DIR/issue-71176.rs:5:10
+ |
+LL | type A<'a>;
+ | ^ --
+help: use angle brackets to add missing lifetime argument
+ |
+LL | type A<'a><'a>;
+ | ^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0107`.
fn main() {
let sub: Box<dyn SuperTrait<SubType = SubStruct>> = Box::new(SuperStruct::new(0));
+ //~^ ERROR the trait `SuperTrait` cannot be made into an object
+ //~^^ ERROR the trait `SuperTrait` cannot be made into an object
}
LL | type SubType<'a><'a>: SubTrait;
| ^^^^
-error: aborting due to previous error; 1 warning emitted
+error[E0038]: the trait `SuperTrait` cannot be made into an object
+ --> $DIR/issue-76535.rs:38:14
+ |
+LL | let sub: Box<dyn SuperTrait<SubType = SubStruct>> = Box::new(SuperStruct::new(0));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SuperTrait` cannot be made into an object
+ |
+ = help: consider moving `SubType` to another trait
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+ --> $DIR/issue-76535.rs:7:10
+ |
+LL | pub trait SuperTrait {
+ | ---------- this trait cannot be made into an object...
+LL | type SubType<'a>: SubTrait;
+ | ^^^^^^^ ...because it contains the generic associated type `SubType`
+
+error[E0038]: the trait `SuperTrait` cannot be made into an object
+ --> $DIR/issue-76535.rs:38:57
+ |
+LL | let sub: Box<dyn SuperTrait<SubType = SubStruct>> = Box::new(SuperStruct::new(0));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SuperTrait` cannot be made into an object
+ |
+ = help: consider moving `SubType` to another trait
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+ --> $DIR/issue-76535.rs:7:10
+ |
+LL | pub trait SuperTrait {
+ | ---------- this trait cannot be made into an object...
+LL | type SubType<'a>: SubTrait;
+ | ^^^^^^^ ...because it contains the generic associated type `SubType`
+ = note: required because of the requirements on the impl of `CoerceUnsized<Box<dyn SuperTrait<SubType = SubStruct<'_>>>>` for `Box<SuperStruct>`
+ = note: required by cast to type `Box<dyn SuperTrait<SubType = SubStruct<'_>>>`
+
+error: aborting due to 3 previous errors; 1 warning emitted
-For more information about this error, try `rustc --explain E0107`.
+Some errors have detailed explanations: E0038, E0107.
+For more information about an error, try `rustc --explain E0038`.
--- /dev/null
+#![allow(incomplete_features)]
+#![feature(generic_associated_types)]
+
+trait CollectionFamily {
+ type Member<T>;
+ //~^ ERROR: missing generics for associated type
+}
+fn floatify() {
+ Box::new(Family) as &dyn CollectionFamily<Member=usize>
+ //~^ the trait `CollectionFamily` cannot be made into an object
+}
+
+struct Family;
+
+fn main() {}
--- /dev/null
+error[E0107]: missing generics for associated type `CollectionFamily::Member`
+ --> $DIR/issue-78671.rs:5:10
+ |
+LL | type Member<T>;
+ | ^^^^^^ expected 1 type argument
+ |
+note: associated type defined here, with 1 type parameter: `T`
+ --> $DIR/issue-78671.rs:5:10
+ |
+LL | type Member<T>;
+ | ^^^^^^ -
+help: use angle brackets to add missing type argument
+ |
+LL | type Member<T><T>;
+ | ^^^
+
+error[E0038]: the trait `CollectionFamily` cannot be made into an object
+ --> $DIR/issue-78671.rs:9:25
+ |
+LL | Box::new(Family) as &dyn CollectionFamily<Member=usize>
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `CollectionFamily` cannot be made into an object
+ |
+ = help: consider moving `Member` to another trait
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+ --> $DIR/issue-78671.rs:5:10
+ |
+LL | trait CollectionFamily {
+ | ---------------- this trait cannot be made into an object...
+LL | type Member<T>;
+ | ^^^^^^ ...because it contains the generic associated type `Member`
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0038, E0107.
+For more information about an error, try `rustc --explain E0038`.
fn main() {
let m = Box::new(std::collections::BTreeMap::<u8, u8>::new())
as Box<dyn MapLike<u8, u8, VRefCont = dyn RefCont<'_, u8>>>;
- //~^^ ERROR type mismatch resolving
+ //~^^ the trait `MapLike` cannot be made into an object
+ //~^^ the trait `MapLike` cannot be made into an object
}
LL | type VRefCont<'a><'a>: RefCont<'a, V>;
| ^^^^
-error[E0271]: type mismatch resolving `<BTreeMap<u8, u8> as MapLike<u8, u8>>::VRefCont<'static> == (dyn RefCont<'_, u8> + 'static)`
+error[E0038]: the trait `MapLike` cannot be made into an object
+ --> $DIR/issue-79422.rs:44:12
+ |
+LL | as Box<dyn MapLike<u8, u8, VRefCont = dyn RefCont<'_, u8>>>;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `MapLike` cannot be made into an object
+ |
+ = help: consider moving `VRefCont` to another trait
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+ --> $DIR/issue-79422.rs:21:10
+ |
+LL | trait MapLike<K, V> {
+ | ------- this trait cannot be made into an object...
+LL | type VRefCont<'a>: RefCont<'a, V>;
+ | ^^^^^^^^ ...because it contains the generic associated type `VRefCont`
+
+error[E0038]: the trait `MapLike` cannot be made into an object
--> $DIR/issue-79422.rs:43:13
|
LL | let m = Box::new(std::collections::BTreeMap::<u8, u8>::new())
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn RefCont`, found reference
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `MapLike` cannot be made into an object
+ |
+ = help: consider moving `VRefCont` to another trait
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+ --> $DIR/issue-79422.rs:21:10
|
- = note: expected trait object `(dyn RefCont<'_, u8> + 'static)`
- found reference `&'static u8`
- = note: required for the cast to the object type `dyn MapLike<u8, u8, VRefCont = (dyn RefCont<'_, u8> + 'static)>`
+LL | trait MapLike<K, V> {
+ | ------- this trait cannot be made into an object...
+LL | type VRefCont<'a>: RefCont<'a, V>;
+ | ^^^^^^^^ ...because it contains the generic associated type `VRefCont`
+ = note: required because of the requirements on the impl of `CoerceUnsized<Box<dyn MapLike<u8, u8, VRefCont = (dyn RefCont<'_, u8> + 'static)>>>` for `Box<BTreeMap<u8, u8>>`
+ = note: required by cast to type `Box<dyn MapLike<u8, u8, VRefCont = (dyn RefCont<'_, u8> + 'static)>>`
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
-Some errors have detailed explanations: E0107, E0271.
-For more information about an error, try `rustc --explain E0107`.
+Some errors have detailed explanations: E0038, E0107.
+For more information about an error, try `rustc --explain E0038`.
--- /dev/null
+#![allow(incomplete_features)]
+#![feature(generic_associated_types)]
+
+trait Monad {
+ type Unwrapped;
+ type Wrapped<B>;
+ //~^ ERROR: missing generics for associated type `Monad::Wrapped`
+
+ fn bind<B, F>(self, f: F) -> Self::Wrapped<B> {
+ todo!()
+ }
+}
+
+fn join<MOuter, MInner, A>(outer: MOuter) -> MOuter::Wrapped<A>
+where
+ MOuter: Monad<Unwrapped = MInner>,
+ MInner: Monad<Unwrapped = A, Wrapped = MOuter::Wrapped<A>>,
+{
+ outer.bind(|inner| inner)
+}
+
+fn main() {
+ assert_eq!(join(Some(Some(true))), Some(true));
+}
--- /dev/null
+error[E0107]: missing generics for associated type `Monad::Wrapped`
+ --> $DIR/issue-79636-1.rs:6:10
+ |
+LL | type Wrapped<B>;
+ | ^^^^^^^ expected 1 type argument
+ |
+note: associated type defined here, with 1 type parameter: `B`
+ --> $DIR/issue-79636-1.rs:6:10
+ |
+LL | type Wrapped<B>;
+ | ^^^^^^^ -
+help: use angle brackets to add missing type argument
+ |
+LL | type Wrapped<B><B>;
+ | ^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0107`.
--- /dev/null
+#![allow(incomplete_features)]
+#![feature(generic_associated_types)]
+
+trait SomeTrait {
+ type Wrapped<A>: SomeTrait;
+ //~^ ERROR: missing generics for associated type `SomeTrait::Wrapped`
+
+ fn f() -> ();
+}
+
+fn program<W>() -> ()
+where
+ W: SomeTrait<Wrapped = W>,
+{
+ return W::f();
+}
+
+fn main() {}
--- /dev/null
+error[E0107]: missing generics for associated type `SomeTrait::Wrapped`
+ --> $DIR/issue-79636-2.rs:5:10
+ |
+LL | type Wrapped<A>: SomeTrait;
+ | ^^^^^^^ expected 1 type argument
+ |
+note: associated type defined here, with 1 type parameter: `A`
+ --> $DIR/issue-79636-2.rs:5:10
+ |
+LL | type Wrapped<A>: SomeTrait;
+ | ^^^^^^^ -
+help: use angle brackets to add missing type argument
+ |
+LL | type Wrapped<A><A>: SomeTrait;
+ | ^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0107`.
--- /dev/null
+#![feature(generic_associated_types)]
+#![allow(incomplete_features)]
+
+trait StreamingIterator {
+ type Item<'a> where Self: 'a;
+ fn size_hint(&self) -> (usize, Option<usize>);
+ // Uncommenting makes `StreamingIterator` not object safe
+// fn next(&mut self) -> Self::Item<'_>;
+}
+
+fn min_size(x: &mut dyn for<'a> StreamingIterator<Item<'a> = &'a i32>) -> usize {
+ //~^ the trait `StreamingIterator` cannot be made into an object
+ x.size_hint().0
+}
+
+fn main() {}
--- /dev/null
+error[E0038]: the trait `StreamingIterator` cannot be made into an object
+ --> $DIR/trait-objects.rs:11:16
+ |
+LL | fn min_size(x: &mut dyn for<'a> StreamingIterator<Item<'a> = &'a i32>) -> usize {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `StreamingIterator` cannot be made into an object
+ |
+ = help: consider moving `Item` to another trait
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+ --> $DIR/trait-objects.rs:5:10
+ |
+LL | trait StreamingIterator {
+ | ----------------- this trait cannot be made into an object...
+LL | type Item<'a> where Self: 'a;
+ | ^^^^ ...because it contains the generic associated type `Item`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0038`.
--- /dev/null
+struct S<T> {
+ contents: T,
+}
+
+impl<T> S<T> {
+ fn new<U>(x: T, _: U) -> S<T> {
+ S {
+ contents: x,
+ }
+ }
+}
+
+trait Trait<T> {
+ fn new<U>(x: T, y: U) -> Self;
+}
+
+struct S2 {
+ contents: isize,
+}
+
+impl Trait<isize> for S2 {
+ fn new<U>(x: isize, _: U) -> S2 {
+ S2 {
+ contents: x,
+ }
+ }
+}
+
+fn foo<'a>() {
+ let _ = S::new::<isize,f64>(1, 1.0);
+ //~^ ERROR this associated function takes 1 type argument but 2 type arguments were supplied
+
+ let _ = S::<'a,isize>::new::<f64>(1, 1.0);
+ //~^ ERROR this struct takes 0 lifetime arguments but 1 lifetime argument was supplied
+
+ let _: S2 = Trait::new::<isize,f64>(1, 1.0);
+ //~^ ERROR this associated function takes 1 type argument but 2 type arguments were supplied
+
+ let _: S2 = Trait::<'a,isize>::new::<f64,f64>(1, 1.0);
+ //~^ ERROR this trait takes 0 lifetime arguments but 1 lifetime argument was supplied
+ //~| ERROR this associated function takes 1 type argument but 2 type arguments were supplied
+}
+
+fn main() {}
--- /dev/null
+error[E0107]: this associated function takes 1 type argument but 2 type arguments were supplied
+ --> $DIR/bad-mid-path-type-params.rs:30:16
+ |
+LL | let _ = S::new::<isize,f64>(1, 1.0);
+ | ^^^ ---- help: remove this type argument
+ | |
+ | expected 1 type argument
+ |
+note: associated function defined here, with 1 type parameter: `U`
+ --> $DIR/bad-mid-path-type-params.rs:6:8
+ |
+LL | fn new<U>(x: T, _: U) -> S<T> {
+ | ^^^ -
+
+error[E0107]: this struct takes 0 lifetime arguments but 1 lifetime argument was supplied
+ --> $DIR/bad-mid-path-type-params.rs:33:13
+ |
+LL | let _ = S::<'a,isize>::new::<f64>(1, 1.0);
+ | ^ --- help: remove this lifetime argument
+ | |
+ | expected 0 lifetime arguments
+ |
+note: struct defined here, with 0 lifetime parameters
+ --> $DIR/bad-mid-path-type-params.rs:1:8
+ |
+LL | struct S<T> {
+ | ^
+
+error[E0107]: this associated function takes 1 type argument but 2 type arguments were supplied
+ --> $DIR/bad-mid-path-type-params.rs:36:24
+ |
+LL | let _: S2 = Trait::new::<isize,f64>(1, 1.0);
+ | ^^^ ---- help: remove this type argument
+ | |
+ | expected 1 type argument
+ |
+note: associated function defined here, with 1 type parameter: `U`
+ --> $DIR/bad-mid-path-type-params.rs:14:8
+ |
+LL | fn new<U>(x: T, y: U) -> Self;
+ | ^^^ -
+
+error[E0107]: this trait takes 0 lifetime arguments but 1 lifetime argument was supplied
+ --> $DIR/bad-mid-path-type-params.rs:39:17
+ |
+LL | let _: S2 = Trait::<'a,isize>::new::<f64,f64>(1, 1.0);
+ | ^^^^^ --- help: remove this lifetime argument
+ | |
+ | expected 0 lifetime arguments
+ |
+note: trait defined here, with 0 lifetime parameters
+ --> $DIR/bad-mid-path-type-params.rs:13:7
+ |
+LL | trait Trait<T> {
+ | ^^^^^
+
+error[E0107]: this associated function takes 1 type argument but 2 type arguments were supplied
+ --> $DIR/bad-mid-path-type-params.rs:39:36
+ |
+LL | let _: S2 = Trait::<'a,isize>::new::<f64,f64>(1, 1.0);
+ | ^^^ ---- help: remove this type argument
+ | |
+ | expected 1 type argument
+ |
+note: associated function defined here, with 1 type parameter: `U`
+ --> $DIR/bad-mid-path-type-params.rs:14:8
+ |
+LL | fn new<U>(x: T, y: U) -> Self;
+ | ^^^ -
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0107`.
--- /dev/null
+fn foo<U>() {}
+
+fn main() {
+ foo::<main>()
+ //~^ ERROR constant provided when a type was expected
+}
--- /dev/null
+error[E0747]: constant provided when a type was expected
+ --> $DIR/generic-function-item-where-type.rs:4:11
+ |
+LL | foo::<main>()
+ | ^^^^
+ |
+ = help: `main` is a function item, not a type
+ = help: function item types cannot be named directly
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0747`.
struct Foo<A, B = Vec<C>, C>(A, B, C);
//~^ ERROR generic parameters with a default must be trailing
+//~| ERROR generic parameters with a default cannot use
fn main() {}
LL | struct Foo<A, B = Vec<C>, C>(A, B, C);
| ^
-error: aborting due to 2 previous errors
+error[E0128]: generic parameters with a default cannot use forward declared identifiers
+ --> $DIR/generic-non-trailing-defaults.rs:6:23
+ |
+LL | struct Foo<A, B = Vec<C>, C>(A, B, C);
+ | ^ defaulted generic parameters cannot be forward declared
+
+error: aborting due to 3 previous errors
+For more information about this error, try `rustc --explain E0128`.
error[E0599]: no method named `f` found for unit type `()` in the current scope
--> $DIR/trait_items.rs:17:24
|
+LL | fn f(&self) {}
+ | - the method is available for `()` here
+...
LL | fn f() { ::baz::m!(); }
| ------------ in this macro invocation
...
error[E0599]: no method named `method` found for type `char` in the current scope
--> $DIR/no-method-suggested-traits.rs:30:9
|
+LL | fn method(&self) {}
+ | ------ the method is available for `char` here
+...
LL | 'a'.method();
| ^^^^^^ method not found in `char`
|
|
LL | 1i32.method();
| ^^^^^^ method not found in `i32`
+ |
+ ::: $DIR/auxiliary/no_method_suggested_traits.rs:8:12
+ |
+LL | fn method(&self) {}
+ | ------ the method is available for `i32` here
|
= help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
-// `#[macro_export] macro_rules` that doesn't originate from macro expansions can be placed
-// into the root module soon enough to act as usual items and shadow globs and preludes.
+// Crate-local macro expanded `macro_export` macros cannot be accessed with module-relative paths.
-#![feature(decl_macro)]
-
-// `macro_export` shadows globs
-use inner1::*;
-
-mod inner1 {
- pub macro exported() {}
-}
-
-exported!();
-
-mod deep {
- fn deep() {
- type Deeper = [u8; {
- #[macro_export]
- macro_rules! exported {
- () => ( struct Б; ) //~ ERROR non-ascii idents are not fully supported
- }
-
- 0
- }];
+macro_rules! define_exported { () => {
+ #[macro_export]
+ macro_rules! exported {
+ () => ()
}
-}
+}}
-// `macro_export` shadows std prelude
-fn main() {
- panic!();
-}
+define_exported!();
-mod inner3 {
- #[macro_export]
- macro_rules! panic {
- () => ( struct Г; ) //~ ERROR non-ascii idents are not fully supported
- }
+mod m {
+ use exported;
+ //~^ ERROR macro-expanded `macro_export` macros from the current crate cannot
+ //~| WARN this was previously accepted
}
-// `macro_export` shadows builtin macros
-include!();
-
-mod inner4 {
- #[macro_export]
- macro_rules! include {
- () => ( struct Д; ) //~ ERROR non-ascii idents are not fully supported
- }
+fn main() {
+ ::exported!();
+ //~^ ERROR macro-expanded `macro_export` macros from the current crate cannot
+ //~| WARN this was previously accepted
}
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/local-modularized-tricky-fail-2.rs:20:32
+error: macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths
+ --> $DIR/local-modularized-tricky-fail-2.rs:13:9
|
-LL | exported!();
- | ------------ in this macro invocation
-...
-LL | () => ( struct Б; )
- | ^
+LL | use exported;
+ | ^^^^^^^^
|
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
- = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/local-modularized-tricky-fail-2.rs:36:24
+ = note: `#[deny(macro_expanded_macro_exports_accessed_by_absolute_paths)]` on by default
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #52234 <https://github.com/rust-lang/rust/issues/52234>
+note: the macro is defined here
+ --> $DIR/local-modularized-tricky-fail-2.rs:5:5
|
-LL | panic!();
- | --------- in this macro invocation
+LL | / macro_rules! exported {
+LL | | () => ()
+LL | | }
+ | |_____^
...
-LL | () => ( struct Г; )
- | ^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
+LL | define_exported!();
+ | ------------------- in this macro invocation
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/local-modularized-tricky-fail-2.rs:46:24
+error: macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths
+ --> $DIR/local-modularized-tricky-fail-2.rs:19:5
|
-LL | include!();
- | ----------- in this macro invocation
-...
-LL | () => ( struct Д; )
- | ^
+LL | ::exported!();
+ | ^^^^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #52234 <https://github.com/rust-lang/rust/issues/52234>
+note: the macro is defined here
+ --> $DIR/local-modularized-tricky-fail-2.rs:5:5
|
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
+LL | / macro_rules! exported {
+LL | | () => ()
+LL | | }
+ | |_____^
+...
+LL | define_exported!();
+ | ------------------- in this macro invocation
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0658`.
+++ /dev/null
-// Crate-local macro expanded `macro_export` macros cannot be accessed with module-relative paths.
-
-macro_rules! define_exported { () => {
- #[macro_export]
- macro_rules! exported {
- () => ()
- }
-}}
-
-define_exported!();
-
-mod m {
- use exported;
- //~^ ERROR macro-expanded `macro_export` macros from the current crate cannot
- //~| WARN this was previously accepted
-}
-
-fn main() {
- ::exported!();
- //~^ ERROR macro-expanded `macro_export` macros from the current crate cannot
- //~| WARN this was previously accepted
-}
+++ /dev/null
-error: macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths
- --> $DIR/local-modularized-tricky-fail-3.rs:13:9
- |
-LL | use exported;
- | ^^^^^^^^
- |
- = note: `#[deny(macro_expanded_macro_exports_accessed_by_absolute_paths)]` on by default
- = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
- = note: for more information, see issue #52234 <https://github.com/rust-lang/rust/issues/52234>
-note: the macro is defined here
- --> $DIR/local-modularized-tricky-fail-3.rs:5:5
- |
-LL | / macro_rules! exported {
-LL | | () => ()
-LL | | }
- | |_____^
-...
-LL | define_exported!();
- | ------------------- in this macro invocation
- = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths
- --> $DIR/local-modularized-tricky-fail-3.rs:19:5
- |
-LL | ::exported!();
- | ^^^^^^^^^^
- |
- = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
- = note: for more information, see issue #52234 <https://github.com/rust-lang/rust/issues/52234>
-note: the macro is defined here
- --> $DIR/local-modularized-tricky-fail-3.rs:5:5
- |
-LL | / macro_rules! exported {
-LL | | () => ()
-LL | | }
- | |_____^
-...
-LL | define_exported!();
- | ------------------- in this macro invocation
- = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to 2 previous errors
-
--- /dev/null
+// build-pass (FIXME(62277): could be check-pass?)
+
+macro_rules! define_exported { () => {
+ #[macro_export]
+ macro_rules! exported {
+ () => ()
+ }
+}}
+
+mod inner1 {
+ use super::*;
+ exported!();
+}
+
+mod inner2 {
+ define_exported!();
+}
+
+fn main() {}
--- /dev/null
+// check-pass
+//
+// `#[macro_export] macro_rules` that doesn't originate from macro expansions can be placed
+// into the root module soon enough to act as usual items and shadow globs and preludes.
+
+#![feature(decl_macro)]
+
+// `macro_export` shadows globs
+use inner1::*;
+
+mod inner1 {
+ pub macro exported() {}
+}
+
+exported!();
+
+mod deep {
+ fn deep() {
+ type Deeper = [u8; {
+ #[macro_export]
+ macro_rules! exported {
+ () => ( struct Б; )
+ }
+
+ 0
+ }];
+ }
+}
+
+// `macro_export` shadows std prelude
+fn main() {
+ panic!();
+}
+
+mod inner3 {
+ #[macro_export]
+ macro_rules! panic {
+ () => ( struct Г; )
+ }
+}
+
+// `macro_export` shadows builtin macros
+include!();
+
+mod inner4 {
+ #[macro_export]
+ macro_rules! include {
+ () => ( struct Д; )
+ }
+}
+++ /dev/null
-// build-pass (FIXME(62277): could be check-pass?)
-
-macro_rules! define_exported { () => {
- #[macro_export]
- macro_rules! exported {
- () => ()
- }
-}}
-
-mod inner1 {
- use super::*;
- exported!();
-}
-
-mod inner2 {
- define_exported!();
-}
-
-fn main() {}
--- /dev/null
+// build-fail
+
+#![feature(repr_simd, platform_intrinsics, core_intrinsics)]
+#![allow(warnings)]
+#![crate_type = "rlib"]
+
+// Bad monomorphizations could previously cause LLVM asserts even though the
+// error was caught in the compiler.
+
+extern "platform-intrinsic" {
+ fn simd_add<T>(x: T, y: T) -> T;
+}
+
+use std::intrinsics;
+
+#[derive(Copy, Clone)]
+pub struct Foo(i64);
+
+pub fn test_cttz(v: Foo) -> Foo {
+ intrinsics::cttz(v)
+ //~^ ERROR `cttz` intrinsic: expected basic integer type, found `Foo`
+}
+
+pub unsafe fn test_fadd_fast(a: Foo, b: Foo) -> Foo {
+ intrinsics::fadd_fast(a, b)
+ //~^ ERROR `fadd_fast` intrinsic: expected basic float type, found `Foo`
+}
+
+pub unsafe fn test_simd_add(a: Foo, b: Foo) -> Foo {
+ simd_add(a, b)
+ //~^ ERROR `simd_add` intrinsic: expected SIMD input type, found non-SIMD `Foo`
+}
--- /dev/null
+error[E0511]: invalid monomorphization of `cttz` intrinsic: expected basic integer type, found `Foo`
+ --> $DIR/bad-intrinsic-monomorphization.rs:20:5
+ |
+LL | intrinsics::cttz(v)
+ | ^^^^^^^^^^^^^^^^^^^
+
+error[E0511]: invalid monomorphization of `fadd_fast` intrinsic: expected basic float type, found `Foo`
+ --> $DIR/bad-intrinsic-monomorphization.rs:25:5
+ |
+LL | intrinsics::fadd_fast(a, b)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0511]: invalid monomorphization of `simd_add` intrinsic: expected SIMD input type, found non-SIMD `Foo`
+ --> $DIR/bad-intrinsic-monomorphization.rs:30:5
+ |
+LL | simd_add(a, b)
+ | ^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0511`.
// run-pass
// ignore-wasm32-bare seems not important to test here
-#![feature(intrinsics, main)]
+#![feature(intrinsics)]
mod rusti {
extern "rust-intrinsic" {
target_os = "solaris",
target_os = "vxworks"))]
mod m {
- #[main]
#[cfg(target_arch = "x86")]
pub fn main() {
unsafe {
}
}
- #[main]
#[cfg(not(target_arch = "x86"))]
pub fn main() {
unsafe {
#[cfg(target_env = "sgx")]
mod m {
- #[main]
#[cfg(target_arch = "x86_64")]
pub fn main() {
unsafe {
#[cfg(target_os = "windows")]
mod m {
- #[main]
pub fn main() {
unsafe {
assert_eq!(::rusti::pref_align_of::<u64>(), 8);
}
}
}
+
+fn main() {
+ m::main();
+}
|
LL | ().a();
| ^ method not found in `()`
+ |
+ ::: $DIR/auxiliary/xcrate-issue-43189-a.rs:5:8
+ |
+LL | fn a(&self) {}
+ | - the method is available for `()` here
|
= help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
-#![feature(non_ascii_idents)]
-
pub fn main () {}
fn საჭმელად_გემრიელი_სადილი ( ) -> isize { //~ ERROR mismatched types
error[E0308]: mismatched types
- --> $DIR/issue-44023.rs:5:36
+ --> $DIR/issue-44023.rs:3:36
|
LL | fn საჭმელად_გემრიელი_სადილი ( ) -> isize {
| ------------------------ ^^^^^ expected `isize`, found `()`
|
LL | reexported_trait::FooStruct.trait_method();
| ^^^^^^^^^^^^ method not found in `FooStruct`
+ |
+ ::: $DIR/auxiliary/reexported-trait.rs:3:12
+ |
+LL | fn trait_method(&self) {
+ | ------------ the method is available for `FooStruct` here
|
= help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
|
LL | reexported_trait::FooStruct.trait_method_b();
| ^^^^^^^^^^^^^^ method not found in `FooStruct`
+ |
+ ::: $DIR/auxiliary/reexported-trait.rs:7:12
+ |
+LL | fn trait_method_b(&self) {
+ | -------------- the method is available for `FooStruct` here
|
= help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
r: dyn A + 'static
}
-fn new_struct(r: dyn A + 'static)
- -> Struct { //~^ ERROR the size for values of type
- //~^ ERROR the size for values of type
+fn new_struct(
+ r: dyn A + 'static //~ ERROR the size for values of type
+) -> Struct { //~ ERROR the size for values of type
Struct { r: r }
}
error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at compilation time
- --> $DIR/issue-5883.rs:7:15
+ --> $DIR/issue-5883.rs:8:5
|
-LL | fn new_struct(r: dyn A + 'static)
- | ^ doesn't have a size known at compile-time
+LL | r: dyn A + 'static
+ | ^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn A + 'static)`
= help: unsized fn params are gated as an unstable feature
help: function arguments must have a statically known size, borrowed types always have a known size
|
-LL | fn new_struct(&r: dyn A + 'static)
- | ^
+LL | r: &dyn A + 'static
+ | ^
error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at compilation time
- --> $DIR/issue-5883.rs:8:8
+ --> $DIR/issue-5883.rs:9:6
|
-LL | -> Struct {
- | ^^^^^^ doesn't have a size known at compile-time
-LL |
+LL | ) -> Struct {
+ | ^^^^^^ doesn't have a size known at compile-time
LL | Struct { r: r }
| --------------- this returned value is of type `Struct`
|
+// check-pass
+
fn main() {
for _ in [0..1] {}
-//~^ ERROR is not an iterator
for _ in [0..=1] {}
-//~^ ERROR is not an iterator
for _ in [0..] {}
-//~^ ERROR is not an iterator
for _ in [..1] {}
-//~^ ERROR is not an iterator
for _ in [..=1] {}
-//~^ ERROR is not an iterator
let start = 0;
let end = 0;
for _ in [start..end] {}
-//~^ ERROR is not an iterator
let array_of_range = [start..end];
for _ in array_of_range {}
-//~^ ERROR is not an iterator
for _ in [0..1, 2..3] {}
-//~^ ERROR is not an iterator
for _ in [0..=1] {}
-//~^ ERROR is not an iterator
}
+++ /dev/null
-error[E0277]: `[std::ops::Range<{integer}>; 1]` is not an iterator
- --> $DIR/array-of-ranges.rs:2:14
- |
-LL | for _ in [0..1] {}
- | ^^^^^^ if you meant to iterate between two values, remove the square brackets
- |
- = help: the trait `Iterator` is not implemented for `[std::ops::Range<{integer}>; 1]`
- = note: `[start..end]` is an array of one `Range`; you might have meant to have a `Range` without the brackets: `start..end`
- = note: required because of the requirements on the impl of `IntoIterator` for `[std::ops::Range<{integer}>; 1]`
- = note: required by `into_iter`
-
-error[E0277]: `[RangeInclusive<{integer}>; 1]` is not an iterator
- --> $DIR/array-of-ranges.rs:4:14
- |
-LL | for _ in [0..=1] {}
- | ^^^^^^^ if you meant to iterate between two values, remove the square brackets
- |
- = help: the trait `Iterator` is not implemented for `[RangeInclusive<{integer}>; 1]`
- = note: `[start..=end]` is an array of one `RangeInclusive`; you might have meant to have a `RangeInclusive` without the brackets: `start..=end`
- = note: required because of the requirements on the impl of `IntoIterator` for `[RangeInclusive<{integer}>; 1]`
- = note: required by `into_iter`
-
-error[E0277]: `[RangeFrom<{integer}>; 1]` is not an iterator
- --> $DIR/array-of-ranges.rs:6:14
- |
-LL | for _ in [0..] {}
- | ^^^^^ if you meant to iterate from a value onwards, remove the square brackets
- |
- = help: the trait `Iterator` is not implemented for `[RangeFrom<{integer}>; 1]`
- = note: `[start..]` is an array of one `RangeFrom`; you might have meant to have a `RangeFrom` without the brackets: `start..`, keeping in mind that iterating over an unbounded iterator will run forever unless you `break` or `return` from within the loop
- = note: required because of the requirements on the impl of `IntoIterator` for `[RangeFrom<{integer}>; 1]`
- = note: required by `into_iter`
-
-error[E0277]: `[RangeTo<{integer}>; 1]` is not an iterator
- --> $DIR/array-of-ranges.rs:8:14
- |
-LL | for _ in [..1] {}
- | ^^^^^ if you meant to iterate until a value, remove the square brackets and add a starting value
- |
- = help: the trait `Iterator` is not implemented for `[RangeTo<{integer}>; 1]`
- = note: `[..end]` is an array of one `RangeTo`; you might have meant to have a bounded `Range` without the brackets: `0..end`
- = note: required because of the requirements on the impl of `IntoIterator` for `[RangeTo<{integer}>; 1]`
- = note: required by `into_iter`
-
-error[E0277]: `[RangeToInclusive<{integer}>; 1]` is not an iterator
- --> $DIR/array-of-ranges.rs:10:14
- |
-LL | for _ in [..=1] {}
- | ^^^^^^ if you meant to iterate until a value (including it), remove the square brackets and add a starting value
- |
- = help: the trait `Iterator` is not implemented for `[RangeToInclusive<{integer}>; 1]`
- = note: `[..=end]` is an array of one `RangeToInclusive`; you might have meant to have a bounded `RangeInclusive` without the brackets: `0..=end`
- = note: required because of the requirements on the impl of `IntoIterator` for `[RangeToInclusive<{integer}>; 1]`
- = note: required by `into_iter`
-
-error[E0277]: `[std::ops::Range<{integer}>; 1]` is not an iterator
- --> $DIR/array-of-ranges.rs:14:14
- |
-LL | for _ in [start..end] {}
- | ^^^^^^^^^^^^ if you meant to iterate between two values, remove the square brackets
- |
- = help: the trait `Iterator` is not implemented for `[std::ops::Range<{integer}>; 1]`
- = note: `[start..end]` is an array of one `Range`; you might have meant to have a `Range` without the brackets: `start..end`
- = note: required because of the requirements on the impl of `IntoIterator` for `[std::ops::Range<{integer}>; 1]`
- = note: required by `into_iter`
-
-error[E0277]: `[std::ops::Range<{integer}>; 1]` is not an iterator
- --> $DIR/array-of-ranges.rs:17:14
- |
-LL | for _ in array_of_range {}
- | ^^^^^^^^^^^^^^ if you meant to iterate between two values, remove the square brackets
- |
- = help: the trait `Iterator` is not implemented for `[std::ops::Range<{integer}>; 1]`
- = note: `[start..end]` is an array of one `Range`; you might have meant to have a `Range` without the brackets: `start..end`
- = note: required because of the requirements on the impl of `IntoIterator` for `[std::ops::Range<{integer}>; 1]`
- = note: required by `into_iter`
-
-error[E0277]: `[std::ops::Range<{integer}>; 2]` is not an iterator
- --> $DIR/array-of-ranges.rs:19:14
- |
-LL | for _ in [0..1, 2..3] {}
- | ^^^^^^^^^^^^ arrays do not yet implement `IntoIterator`; try using `std::array::IntoIter::new(arr)`
- |
- = help: the trait `Iterator` is not implemented for `[std::ops::Range<{integer}>; 2]`
- = note: see <https://github.com/rust-lang/rust/pull/65819> for more details
- = note: required because of the requirements on the impl of `IntoIterator` for `[std::ops::Range<{integer}>; 2]`
- = note: required by `into_iter`
-
-error[E0277]: `[RangeInclusive<{integer}>; 1]` is not an iterator
- --> $DIR/array-of-ranges.rs:21:14
- |
-LL | for _ in [0..=1] {}
- | ^^^^^^^ if you meant to iterate between two values, remove the square brackets
- |
- = help: the trait `Iterator` is not implemented for `[RangeInclusive<{integer}>; 1]`
- = note: `[start..=end]` is an array of one `RangeInclusive`; you might have meant to have a `RangeInclusive` without the brackets: `start..=end`
- = note: required because of the requirements on the impl of `IntoIterator` for `[RangeInclusive<{integer}>; 1]`
- = note: required by `into_iter`
-
-error: aborting due to 9 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
+// check-pass
+
fn main() {
for _ in [1, 2] {}
-//~^ ERROR is not an iterator
let x = [1, 2];
for _ in x {}
-//~^ ERROR is not an iterator
for _ in [1.0, 2.0] {}
-//~^ ERROR is not an iterator
}
+++ /dev/null
-error[E0277]: `[{integer}; 2]` is not an iterator
- --> $DIR/array.rs:2:14
- |
-LL | for _ in [1, 2] {}
- | ^^^^^^ arrays do not yet implement `IntoIterator`; try using `std::array::IntoIter::new(arr)`
- |
- = help: the trait `Iterator` is not implemented for `[{integer}; 2]`
- = note: see <https://github.com/rust-lang/rust/pull/65819> for more details
- = note: required because of the requirements on the impl of `IntoIterator` for `[{integer}; 2]`
- = note: required by `into_iter`
-
-error[E0277]: `[{integer}; 2]` is not an iterator
- --> $DIR/array.rs:5:14
- |
-LL | for _ in x {}
- | ^ arrays do not yet implement `IntoIterator`; try using `std::array::IntoIter::new(arr)`
- |
- = help: the trait `Iterator` is not implemented for `[{integer}; 2]`
- = note: see <https://github.com/rust-lang/rust/pull/65819> for more details
- = note: required because of the requirements on the impl of `IntoIterator` for `[{integer}; 2]`
- = note: required by `into_iter`
-
-error[E0277]: `[{float}; 2]` is not an iterator
- --> $DIR/array.rs:7:14
- |
-LL | for _ in [1.0, 2.0] {}
- | ^^^^^^^^^^ arrays do not yet implement `IntoIterator`; try using `std::array::IntoIter::new(arr)`
- |
- = help: the trait `Iterator` is not implemented for `[{float}; 2]`
- = note: see <https://github.com/rust-lang/rust/pull/65819> for more details
- = note: required because of the requirements on the impl of `IntoIterator` for `[{float}; 2]`
- = note: required by `into_iter`
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// check-pass
+// edition:2018
+
+use std::array::IntoIter;
+use std::ops::Deref;
+use std::rc::Rc;
+use std::slice::Iter;
+
+fn main() {
+ let array = [0; 10];
+
+ // Before 2021, the method dispatched to `IntoIterator for &[T; N]`,
+ // which we continue to support for compatibility.
+ let _: Iter<'_, i32> = array.into_iter();
+ //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
+ //~| WARNING this was previously accepted by the compiler but is being phased out
+
+ let _: Iter<'_, i32> = Box::new(array).into_iter();
+ //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
+ //~| WARNING this was previously accepted by the compiler but is being phased out
+
+ // The `array_into_iter` lint doesn't cover other wrappers that deref to an array.
+ let _: Iter<'_, i32> = Rc::new(array).into_iter();
+ let _: Iter<'_, i32> = Array(array).into_iter();
+
+ // But you can always use the trait method explicitly as an array.
+ let _: IntoIter<i32, 10> = IntoIterator::into_iter(array);
+}
+
+/// User type that dereferences to an array.
+struct Array([i32; 10]);
+
+impl Deref for Array {
+ type Target = [i32; 10];
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
--- /dev/null
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+ --> $DIR/into-iter-on-arrays-2018.rs:14:34
+ |
+LL | let _: Iter<'_, i32> = array.into_iter();
+ | ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+ |
+ = note: `#[warn(array_into_iter)]` on by default
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+ --> $DIR/into-iter-on-arrays-2018.rs:18:44
+ |
+LL | let _: Iter<'_, i32> = Box::new(array).into_iter();
+ | ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+warning: 2 warnings emitted
+
+Future incompatibility report: Future breakage date: None, diagnostic:
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+ --> $DIR/into-iter-on-arrays-2018.rs:14:34
+ |
+LL | let _: Iter<'_, i32> = array.into_iter();
+ | ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+ |
+ = note: `#[warn(array_into_iter)]` on by default
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+Future breakage date: None, diagnostic:
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+ --> $DIR/into-iter-on-arrays-2018.rs:18:44
+ |
+LL | let _: Iter<'_, i32> = Box::new(array).into_iter();
+ | ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
--- /dev/null
+// check-pass
+// edition:2021
+// compile-flags: -Zunstable-options
+
+use std::array::IntoIter;
+use std::ops::Deref;
+use std::rc::Rc;
+
+fn main() {
+ let array = [0; 10];
+
+ // In 2021, the method dispatches to `IntoIterator for [T; N]`.
+ let _: IntoIter<i32, 10> = array.into_iter();
+ let _: IntoIter<i32, 10> = Box::new(array).into_iter();
+
+ // The `array_into_iter` lint doesn't cover other wrappers that deref to an array.
+ let _: IntoIter<i32, 10> = Rc::new(array).into_iter();
+ let _: IntoIter<i32, 10> = Array(array).into_iter();
+
+ // You can always use the trait method explicitly as an array.
+ let _: IntoIter<i32, 10> = IntoIterator::into_iter(array);
+}
+
+/// User type that dereferences to an array.
+struct Array([i32; 10]);
+
+impl Deref for Array {
+ type Target = [i32; 10];
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
--- /dev/null
+// check-fail
+
+struct Foo {}
+impl Foo {
+ fn bar(foo: Foo<Target = usize>) {}
+ //~^ associated type bindings are not allowed here
+}
+fn main() {}
--- /dev/null
+error[E0229]: associated type bindings are not allowed here
+ --> $DIR/issue-83753-invalid-associated-type-supertrait-hrtb.rs:5:21
+ |
+LL | fn bar(foo: Foo<Target = usize>) {}
+ | ^^^^^^^^^^^^^^ associated type not allowed here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0229`.
--- /dev/null
+// check-fail
+
+static STATIC_VAR_FIVE: &One();
+//~^ cannot find type
+//~| free static item without body
+
+fn main() {}
--- /dev/null
+error: free static item without body
+ --> $DIR/issue-83907-invalid-fn-like-path.rs:3:1
+ |
+LL | static STATIC_VAR_FIVE: &One();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: provide a definition for the static: `= <expr>;`
+
+error[E0412]: cannot find type `One` in this scope
+ --> $DIR/issue-83907-invalid-fn-like-path.rs:3:26
+ |
+LL | static STATIC_VAR_FIVE: &One();
+ | ^^^ not found in this scope
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0412`.
--> $DIR/ex3-both-anon-regions-one-is-struct-2.rs:4:5
|
LL | fn foo(mut x: Ref, y: &u32) {
- | - help: make this binding mutable: `mut y`
+ | - help: consider making this binding mutable: `mut y`
LL | y = x.b;
| ^^^^^^^ cannot assign to immutable argument
--> $DIR/liveness-assign-imm-local-notes.rs:10:9
|
LL | let x;
- | - help: make this binding mutable: `mut x`
+ | - help: consider making this binding mutable: `mut x`
...
LL | x = 2;
| ----- first assignment to `x`
--> $DIR/liveness-assign-imm-local-notes.rs:21:13
|
LL | let x;
- | - help: make this binding mutable: `mut x`
+ | - help: consider making this binding mutable: `mut x`
...
LL | x = 2;
| ----- first assignment to `x`
--> $DIR/liveness-assign-imm-local-notes.rs:30:13
|
LL | let x;
- | - help: make this binding mutable: `mut x`
+ | - help: consider making this binding mutable: `mut x`
...
LL | x = 1;
| ^^^^^ cannot assign twice to immutable variable
--> $DIR/liveness-assign-imm-local-notes.rs:32:13
|
LL | let x;
- | - help: make this binding mutable: `mut x`
+ | - help: consider making this binding mutable: `mut x`
...
LL | x = 1;
| ----- first assignment to `x`
--- /dev/null
+#[link()] //~ ERROR: specified without `name =
+#[link(name = "")] //~ ERROR: with empty name
+#[link(name = "foo")]
+#[link(name = "foo", kind = "bar")] //~ ERROR: unknown kind
+extern "C" {}
+
+fn main() {}
--- /dev/null
+error[E0459]: `#[link(...)]` specified without `name = "foo"`
+ --> $DIR/bad-extern-link-attrs.rs:1:1
+ |
+LL | #[link()]
+ | ^^^^^^^^^ missing `name` argument
+
+error[E0454]: `#[link(name = "")]` given with empty name
+ --> $DIR/bad-extern-link-attrs.rs:2:1
+ |
+LL | #[link(name = "")]
+ | ^^^^^^^^^^^^^^^^^^ empty name given
+
+error[E0458]: unknown kind: `bar`
+ --> $DIR/bad-extern-link-attrs.rs:4:22
+ |
+LL | #[link(name = "foo", kind = "bar")]
+ | ---------------------^^^^^^^^^^^^--
+ | |
+ | unknown kind
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0454, E0458, E0459.
+For more information about an error, try `rustc --explain E0454`.
--- /dev/null
+// compile-flags: --cap-lints test
+// error-pattern: unknown lint level: `test`
+
+fn main() {}
--- /dev/null
+error: unknown lint level: `test`
+
--- /dev/null
+// compile-flags: --cap-lints deny
+
+#![warn(unused)]
+#![deny(warnings)]
+
+use std::option; //~ ERROR
+
+fn main() {}
--- /dev/null
+error: unused import: `std::option`
+ --> $DIR/bad-lint-cap2.rs:6:5
+ |
+LL | use std::option;
+ | ^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/bad-lint-cap2.rs:4:9
+ |
+LL | #![deny(warnings)]
+ | ^^^^^^^^
+ = note: `#[deny(unused_imports)]` implied by `#[deny(warnings)]`
+
+error: aborting due to previous error
+
--- /dev/null
+// check-pass
+// compile-flags: --cap-lints warn
+
+#![warn(unused)]
+#![deny(warnings)]
+
+use std::option; //~ WARN
+
+fn main() {}
--- /dev/null
+warning: unused import: `std::option`
+ --> $DIR/bad-lint-cap3.rs:7:5
+ |
+LL | use std::option;
+ | ^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/bad-lint-cap3.rs:5:9
+ |
+LL | #![deny(warnings)]
+ | ^^^^^^^^
+ = note: `#[warn(unused_imports)]` implied by `#[warn(warnings)]`
+
+warning: 1 warning emitted
+
#![allow(unused_variables)]
#![deny(dead_code)]
-#![feature(main, start)]
+#![feature(rustc_attrs, start)]
struct Foo;
fn dead_fn() {} //~ ERROR: function is never used
-#[main]
+#[rustc_main]
fn dead_fn2() {} //~ ERROR: function is never used
fn used_fn() {}
--- /dev/null
+// check-pass
+#![warn(rustc::internal)]
+
+#[allow(rustc::foo::bar::default_hash_types)]
+//~^ WARN unknown lint: `rustc::foo::bar::default_hash_types`
+//~| HELP did you mean
+//~| SUGGESTION rustc::default_hash_types
+#[allow(rustc::foo::default_hash_types)]
+//~^ WARN unknown lint: `rustc::foo::default_hash_types`
+//~| HELP did you mean
+//~| SUGGESTION rustc::default_hash_types
+fn main() {
+ let _ = std::collections::HashMap::<String, String>::new();
+ //~^ WARN Prefer FxHashMap over HashMap, it has better performance
+ //~| HELP use
+}
--- /dev/null
+warning: unknown lint: `rustc::foo::bar::default_hash_types`
+ --> $DIR/issue-83477.rs:4:9
+ |
+LL | #[allow(rustc::foo::bar::default_hash_types)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `rustc::default_hash_types`
+ |
+ = note: `#[warn(unknown_lints)]` on by default
+
+warning: unknown lint: `rustc::foo::default_hash_types`
+ --> $DIR/issue-83477.rs:8:9
+ |
+LL | #[allow(rustc::foo::default_hash_types)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `rustc::default_hash_types`
+
+warning: Prefer FxHashMap over HashMap, it has better performance
+ --> $DIR/issue-83477.rs:13:31
+ |
+LL | let _ = std::collections::HashMap::<String, String>::new();
+ | ^^^^^^^ help: use: `FxHashMap`
+ |
+note: the lint level is defined here
+ --> $DIR/issue-83477.rs:2:9
+ |
+LL | #![warn(rustc::internal)]
+ | ^^^^^^^^^^^^^^^
+ = note: `#[warn(rustc::default_hash_types)]` implied by `#[warn(rustc::internal)]`
+ = note: a `use rustc_data_structures::fx::FxHashMap` may be necessary
+
+warning: 3 warnings emitted
+
#![allow(dead_code)]
// pretty-expanded FIXME #23616
-#![feature(non_ascii_idents)]
#![deny(non_snake_case)]
// This name is neither upper nor lower case
#![allow(dead_code)]
#![forbid(non_camel_case_types)]
-#![feature(non_ascii_idents)]
// Some scripts (e.g., hiragana) don't have a concept of
// upper/lowercase
error: type `χa` should have an upper camel case name
- --> $DIR/lint-nonstandard-style-unicode-1.rs:15:8
+ --> $DIR/lint-nonstandard-style-unicode-1.rs:14:8
|
LL | struct χa;
| ^^ help: convert the identifier to upper camel case: `Χa`
| ^^^^^^^^^^^^^^^^^^^^
error: type `__χa` should have an upper camel case name
- --> $DIR/lint-nonstandard-style-unicode-1.rs:23:8
+ --> $DIR/lint-nonstandard-style-unicode-1.rs:22:8
|
LL | struct __χa;
| ^^^^ help: convert the identifier to upper camel case: `Χa`
error: type `对__否` should have an upper camel case name
- --> $DIR/lint-nonstandard-style-unicode-1.rs:28:8
+ --> $DIR/lint-nonstandard-style-unicode-1.rs:27:8
|
LL | struct 对__否;
| ^^^^^^ help: convert the identifier to upper camel case: `对_否`
error: type `ヒ__χ` should have an upper camel case name
- --> $DIR/lint-nonstandard-style-unicode-1.rs:31:8
+ --> $DIR/lint-nonstandard-style-unicode-1.rs:30:8
|
LL | struct ヒ__χ;
| ^^^^^ help: convert the identifier to upper camel case: `ヒΧ`
error: type `Hello_你好` should have an upper camel case name
- --> $DIR/lint-nonstandard-style-unicode-1.rs:37:8
+ --> $DIR/lint-nonstandard-style-unicode-1.rs:36:8
|
LL | struct Hello_你好;
| ^^^^^^^^^^ help: convert the identifier to upper camel case: `Hello你好`
error: type `Hello_World` should have an upper camel case name
- --> $DIR/lint-nonstandard-style-unicode-1.rs:40:8
+ --> $DIR/lint-nonstandard-style-unicode-1.rs:39:8
|
LL | struct Hello_World;
| ^^^^^^^^^^^ help: convert the identifier to upper camel case: `HelloWorld`
error: type `你_ӟ` should have an upper camel case name
- --> $DIR/lint-nonstandard-style-unicode-1.rs:43:8
+ --> $DIR/lint-nonstandard-style-unicode-1.rs:42:8
|
LL | struct 你_ӟ;
| ^^^^ help: convert the identifier to upper camel case: `你Ӟ`
#![allow(dead_code)]
#![forbid(non_snake_case)]
-#![feature(non_ascii_idents)]
// Some scripts (e.g., hiragana) don't have a concept of
// upper/lowercase
error: function `Ц` should have a snake case name
- --> $DIR/lint-nonstandard-style-unicode-2.rs:18:4
+ --> $DIR/lint-nonstandard-style-unicode-2.rs:17:4
|
LL | fn Ц() {}
| ^ help: convert the identifier to snake case: `ц`
| ^^^^^^^^^^^^^^
error: function `分__隔` should have a snake case name
- --> $DIR/lint-nonstandard-style-unicode-2.rs:23:4
+ --> $DIR/lint-nonstandard-style-unicode-2.rs:22:4
|
LL | fn 分__隔() {}
| ^^^^^^ help: convert the identifier to snake case: `分_隔`
#![allow(dead_code)]
#![forbid(non_upper_case_globals)]
-#![feature(non_ascii_idents)]
// Some scripts (e.g., hiragana) don't have a concept of
// upper/lowercase
error: static variable `τεχ` should have an upper case name
- --> $DIR/lint-nonstandard-style-unicode-3.rs:18:8
+ --> $DIR/lint-nonstandard-style-unicode-3.rs:17:8
|
LL | static τεχ: f32 = 3.14159265;
| ^^^ help: convert the identifier to upper case: `ΤΕΧ`
-#![feature(non_ascii_idents)]
#![deny(confusable_idents)]
#![allow(uncommon_codepoints, non_upper_case_globals)]
error: identifier pair considered confusable between `s` and `s`
- --> $DIR/lint-confusable-idents.rs:9:9
+ --> $DIR/lint-confusable-idents.rs:8:9
|
LL | const s: usize = 42;
| -- this is where the previous identifier occurred
| ^
|
note: the lint level is defined here
- --> $DIR/lint-confusable-idents.rs:2:9
+ --> $DIR/lint-confusable-idents.rs:1:9
|
LL | #![deny(confusable_idents)]
| ^^^^^^^^^^^^^^^^^
error: identifier pair considered confusable between `s_s` and `s_s`
- --> $DIR/lint-confusable-idents.rs:10:9
+ --> $DIR/lint-confusable-idents.rs:9:9
|
LL | const s_s: usize = 42;
| --- this is where the previous identifier occurred
// check-pass
-#![feature(non_ascii_idents)]
#![deny(mixed_script_confusables)]
struct ΑctuallyNotLatin;
-#![feature(non_ascii_idents)]
#![deny(mixed_script_confusables)]
struct ΑctuallyNotLatin;
error: The usage of Script Group `Greek` in this crate consists solely of mixed script confusables
- --> $DIR/lint-mixed-script-confusables.rs:4:8
+ --> $DIR/lint-mixed-script-confusables.rs:3:8
|
LL | struct ΑctuallyNotLatin;
| ^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
- --> $DIR/lint-mixed-script-confusables.rs:2:9
+ --> $DIR/lint-mixed-script-confusables.rs:1:9
|
LL | #![deny(mixed_script_confusables)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
= note: Please recheck to make sure their usages are indeed what you want.
error: The usage of Script Group `Cyrillic` in this crate consists solely of mixed script confusables
- --> $DIR/lint-mixed-script-confusables.rs:11:5
+ --> $DIR/lint-mixed-script-confusables.rs:10:5
|
LL | mod роре {
| ^^^^
= note: Please recheck to make sure their usages are indeed what you want.
error: The usage of Script Group `Japanese, Katakana` in this crate consists solely of mixed script confusables
- --> $DIR/lint-mixed-script-confusables.rs:13:11
+ --> $DIR/lint-mixed-script-confusables.rs:12:11
|
LL | const エ: &'static str = "アイウ";
| ^^
-#![feature(non_ascii_idents)]
#![deny(non_ascii_idents)]
const חלודה: usize = 2; //~ ERROR identifier contains non-ASCII characters
error: identifier contains non-ASCII characters
- --> $DIR/lint-non-ascii-idents.rs:4:7
+ --> $DIR/lint-non-ascii-idents.rs:3:7
|
LL | const חלודה: usize = 2;
| ^^^^^
|
note: the lint level is defined here
- --> $DIR/lint-non-ascii-idents.rs:2:9
+ --> $DIR/lint-non-ascii-idents.rs:1:9
|
LL | #![deny(non_ascii_idents)]
| ^^^^^^^^^^^^^^^^
error: identifier contains non-ASCII characters
- --> $DIR/lint-non-ascii-idents.rs:6:4
+ --> $DIR/lint-non-ascii-idents.rs:5:4
|
LL | fn coöperation() {}
| ^^^^^^^^^^^
error: identifier contains non-ASCII characters
- --> $DIR/lint-non-ascii-idents.rs:9:9
+ --> $DIR/lint-non-ascii-idents.rs:8:9
|
LL | let naïveté = 2;
| ^^^^^^^
-#![feature(non_ascii_idents)]
#![deny(uncommon_codepoints)]
const µ: f64 = 0.000001; //~ ERROR identifier contains uncommon Unicode codepoints
error: identifier contains uncommon Unicode codepoints
- --> $DIR/lint-uncommon-codepoints.rs:4:7
+ --> $DIR/lint-uncommon-codepoints.rs:3:7
|
LL | const µ: f64 = 0.000001;
| ^
|
note: the lint level is defined here
- --> $DIR/lint-uncommon-codepoints.rs:2:9
+ --> $DIR/lint-uncommon-codepoints.rs:1:9
|
LL | #![deny(uncommon_codepoints)]
| ^^^^^^^^^^^^^^^^^^^
error: identifier contains uncommon Unicode codepoints
- --> $DIR/lint-uncommon-codepoints.rs:6:4
+ --> $DIR/lint-uncommon-codepoints.rs:5:4
|
LL | fn dijkstra() {}
| ^^^^^^^
error: identifier contains uncommon Unicode codepoints
- --> $DIR/lint-uncommon-codepoints.rs:9:9
+ --> $DIR/lint-uncommon-codepoints.rs:8:9
|
LL | let ㇻㇲㇳ = "rust";
| ^^^^^^
// check-pass
-#![feature(non_ascii_idents)]
#![allow(uncommon_codepoints, unused)]
struct 𝕟𝕠𝕥𝕒𝕔𝕒𝕞𝕖𝕝;
warning: type `𝕟𝕠𝕥𝕒𝕔𝕒𝕞𝕖𝕝` should have an upper camel case name
- --> $DIR/special-upper-lower-cases.rs:11:8
+ --> $DIR/special-upper-lower-cases.rs:10:8
|
LL | struct 𝕟𝕠𝕥𝕒𝕔𝕒𝕞𝕖𝕝;
| ^^^^^^^^^ should have an UpperCamelCase name
= note: `#[warn(non_camel_case_types)]` on by default
warning: type `𝕟𝕠𝕥_𝕒_𝕔𝕒𝕞𝕖𝕝` should have an upper camel case name
- --> $DIR/special-upper-lower-cases.rs:15:8
+ --> $DIR/special-upper-lower-cases.rs:14:8
|
LL | struct 𝕟𝕠𝕥_𝕒_𝕔𝕒𝕞𝕖𝕝;
| ^^^^^^^^^^^ should have an UpperCamelCase name
warning: static variable `𝗻𝗼𝗻𝘂𝗽𝗽𝗲𝗿𝗰𝗮𝘀𝗲` should have an upper case name
- --> $DIR/special-upper-lower-cases.rs:18:8
+ --> $DIR/special-upper-lower-cases.rs:17:8
|
LL | static 𝗻𝗼𝗻𝘂𝗽𝗽𝗲𝗿𝗰𝗮𝘀𝗲: i32 = 1;
| ^^^^^^^^^^^^ should have an UPPER_CASE name
= note: `#[warn(non_upper_case_globals)]` on by default
warning: variable `𝓢𝓝𝓐𝓐𝓐𝓐𝓚𝓔𝓢` should have a snake case name
- --> $DIR/special-upper-lower-cases.rs:22:9
+ --> $DIR/special-upper-lower-cases.rs:21:9
|
LL | let 𝓢𝓝𝓐𝓐𝓐𝓐𝓚𝓔𝓢 = 1;
| ^^^^^^^^^ should have a snake_case name
fn test() {
let v: isize;
- //~^ HELP make this binding mutable
+ //~^ HELP consider making this binding mutable
//~| SUGGESTION mut v
loop {
v = 1; //~ ERROR cannot assign twice to immutable variable `v`
--> $DIR/liveness-assign-imm-local-in-loop.rs:6:9
|
LL | let v: isize;
- | - help: make this binding mutable: `mut v`
+ | - help: consider making this binding mutable: `mut v`
...
LL | v = 1;
| ^^^^^ cannot assign twice to immutable variable
fn test() {
let v: isize;
- //~^ HELP make this binding mutable
+ //~^ HELP consider making this binding mutable
//~| SUGGESTION mut v
v = 2; //~ NOTE first assignment
v += 1; //~ ERROR cannot assign twice to immutable variable `v`
--> $DIR/liveness-assign-imm-local-in-op-eq.rs:6:5
|
LL | let v: isize;
- | - help: make this binding mutable: `mut v`
+ | - help: consider making this binding mutable: `mut v`
...
LL | v = 2;
| ----- first assignment to `v`
fn test() {
let b = Box::new(1); //~ NOTE first assignment
- //~| HELP make this binding mutable
+ //~| HELP consider making this binding mutable
//~| SUGGESTION mut b
drop(b);
b = Box::new(2); //~ ERROR cannot assign twice to immutable variable `b`
| -
| |
| first assignment to `b`
- | help: make this binding mutable: `mut b`
+ | help: consider making this binding mutable: `mut b`
...
LL | b = Box::new(2);
| ^ cannot assign twice to immutable variable
fn test() {
let v: isize = 1; //~ NOTE first assignment
- //~| HELP make this binding mutable
+ //~| HELP consider making this binding mutable
//~| SUGGESTION mut v
v.clone();
v = 2; //~ ERROR cannot assign twice to immutable variable `v`
| -
| |
| first assignment to `v`
- | help: make this binding mutable: `mut v`
+ | help: consider making this binding mutable: `mut v`
...
LL | v = 2;
| ^^^^^ cannot assign twice to immutable variable
--> $DIR/llvm-asm-out-assign-imm.rs:25:39
|
LL | let x: isize;
- | - help: make this binding mutable: `mut x`
+ | - help: consider making this binding mutable: `mut x`
LL | x = 1;
| ----- first assignment to `x`
...
--- /dev/null
+fn main() {
+ let a: i8 = loop {
+ 1 //~ ERROR mismatched types
+ };
+
+ let b: i8 = loop {
+ break 1;
+ };
+}
+
+fn foo() -> i8 {
+ let a: i8 = loop {
+ 1 //~ ERROR mismatched types
+ };
+
+ let b: i8 = loop {
+ break 1;
+ };
+
+ loop {
+ 1 //~ ERROR mismatched types
+ }
+
+ loop {
+ return 1;
+ }
+
+ loop {
+ 1 //~ ERROR mismatched types
+ }
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/loop-no-implicit-break.rs:3:9
+ |
+LL | 1
+ | ^ expected `()`, found integer
+ |
+help: you might have meant to break the loop with this value
+ |
+LL | break 1;
+ | ^^^^^ ^
+
+error[E0308]: mismatched types
+ --> $DIR/loop-no-implicit-break.rs:13:9
+ |
+LL | 1
+ | ^ expected `()`, found integer
+ |
+help: you might have meant to break the loop with this value
+ |
+LL | break 1;
+ | ^^^^^ ^
+
+error[E0308]: mismatched types
+ --> $DIR/loop-no-implicit-break.rs:21:9
+ |
+LL | 1
+ | ^ expected `()`, found integer
+ |
+help: you might have meant to return this value
+ |
+LL | return 1;
+ | ^^^^^^ ^
+
+error[E0308]: mismatched types
+ --> $DIR/loop-no-implicit-break.rs:29:9
+ |
+LL | 1
+ | ^ expected `()`, found integer
+ |
+help: you might have meant to return this value
+ |
+LL | return 1;
+ | ^^^^^^ ^
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// Regression test for issue #84195
+// Checks that we properly fire lints that occur inside
+// anon consts.
+
+#![deny(semicolon_in_expressions_from_macros)]
+
+macro_rules! len {
+ () => { 0; }; //~ ERROR trailing semicolon
+ //~| WARN this was previously accepted
+}
+
+fn main() {
+ let val: [u8; len!()] = [];
+}
--- /dev/null
+error: trailing semicolon in macro used in expression position
+ --> $DIR/issue-84195-lint-anon-const.rs:8:14
+ |
+LL | () => { 0; };
+ | ^
+...
+LL | let val: [u8; len!()] = [];
+ | ------ in this macro invocation
+ |
+note: the lint level is defined here
+ --> $DIR/issue-84195-lint-anon-const.rs:5:9
+ |
+LL | #![deny(semicolon_in_expressions_from_macros)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
+++ /dev/null
-#![feature(decl_macro)]
-#![feature(pub_macro_rules)]
-
-#[macro_export]
-macro m1() {} //~ ERROR `#[macro_export]` cannot be used on `macro` items
-
-#[macro_export]
-pub macro_rules! m2 { () => {} }
-//~^ ERROR `#[macro_export]` cannot be used on `macro_rules` with `pub`
-
-fn main() {}
+++ /dev/null
-error: `#[macro_export]` cannot be used on `macro` items
- --> $DIR/macro-export-on-modularized-macros.rs:5:1
- |
-LL | macro m1() {}
- | ^^^^^^^^^^^^^
-
-error: `#[macro_export]` cannot be used on `macro_rules` with `pub`
- --> $DIR/macro-export-on-modularized-macros.rs:8:1
- |
-LL | pub macro_rules! m2 { () => {} }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 2 previous errors
-
+++ /dev/null
-#![feature(pub_macro_rules)]
-
-#[macro_use]
-mod m {
- pub macro_rules! mac { () => {} }
-
- // `pub` `macro_rules` cannot be redefined in the same module.
- pub macro_rules! mac { () => {} } //~ ERROR the name `mac` is defined multiple times
-
- pub(self) macro_rules! private_mac { () => {} }
-}
-
-const _: () = {
- pub macro_rules! block_mac { () => {} }
-};
-
-mod n {
- // Scope of `pub` `macro_rules` is not extended by `#[macro_use]`.
- mac!(); //~ ERROR cannot find macro `mac` in this scope
-
- // `pub` `macro_rules` doesn't put the macro into the root module, unlike `#[macro_export]`.
- crate::mac!(); //~ ERROR failed to resolve: maybe a missing crate `mac`
- crate::block_mac!(); //~ ERROR failed to resolve: maybe a missing crate `block_mac`
-
- crate::m::private_mac!(); //~ ERROR macro `private_mac` is private
-}
-
-fn main() {}
+++ /dev/null
-error[E0428]: the name `mac` is defined multiple times
- --> $DIR/pub-macro-rules-fail.rs:8:5
- |
-LL | pub macro_rules! mac { () => {} }
- | -------------------- previous definition of the macro `mac` here
-...
-LL | pub macro_rules! mac { () => {} }
- | ^^^^^^^^^^^^^^^^^^^^ `mac` redefined here
- |
- = note: `mac` must be defined only once in the macro namespace of this module
-
-error[E0433]: failed to resolve: maybe a missing crate `mac`?
- --> $DIR/pub-macro-rules-fail.rs:22:12
- |
-LL | crate::mac!();
- | ^^^ maybe a missing crate `mac`?
-
-error[E0433]: failed to resolve: maybe a missing crate `block_mac`?
- --> $DIR/pub-macro-rules-fail.rs:23:12
- |
-LL | crate::block_mac!();
- | ^^^^^^^^^ maybe a missing crate `block_mac`?
-
-error: cannot find macro `mac` in this scope
- --> $DIR/pub-macro-rules-fail.rs:19:5
- |
-LL | mac!();
- | ^^^
- |
- = note: consider importing this macro:
- m::mac
-
-error[E0603]: macro `private_mac` is private
- --> $DIR/pub-macro-rules-fail.rs:25:15
- |
-LL | crate::m::private_mac!();
- | ^^^^^^^^^^^ private macro
- |
-note: the macro `private_mac` is defined here
- --> $DIR/pub-macro-rules-fail.rs:10:5
- |
-LL | pub(self) macro_rules! private_mac { () => {} }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 5 previous errors
-
-Some errors have detailed explanations: E0428, E0433, E0603.
-For more information about an error, try `rustc --explain E0428`.
+++ /dev/null
-// check-pass
-
-#![feature(pub_macro_rules)]
-
-mod m {
- // `pub` `macro_rules` can be used earlier in item order than they are defined.
- foo!();
-
- pub macro_rules! foo { () => {} }
-
- // `pub(...)` works too.
- pub(super) macro_rules! bar { () => {} }
-}
-
-// `pub` `macro_rules` are available by module path.
-m::foo!();
-
-m::bar!();
-
-fn main() {}
mod m {
//~^ ERROR `main` function not found
- // An inferred main entry point (that doesn't use #[main])
+ // An inferred main entry point
// must appear at the top of the crate
fn main() { }
}
|
LL | / mod m {
LL | |
-LL | | // An inferred main entry point (that doesn't use #[main])
+LL | | // An inferred main entry point
LL | | // must appear at the top of the crate
LL | | fn main() { }
LL | | }
LL | fn main() { }
| ^^^^^^^^^^^^^
= note: you have one or more functions named `main` not defined at the crate level
- = help: either move the `main` function definitions or attach the `#[main]` attribute to one of them
+ = help: consider moving the `main` function definitions
error: aborting due to previous error
--- /dev/null
+// https://github.com/rust-lang/rust/issues/82329
+// compile-flags: -Zunpretty=hir,typed
+// check-pass
+
+pub fn main() {
+ if true {
+ } else if let Some(a) = Some(3) {
+ }
+}
--- /dev/null
+#[prelude_import]
+use ::std::prelude::rust_2015::*;
+#[macro_use]
+extern crate std;
+// https://github.com/rust-lang/rust/issues/82329
+// compile-flags: -Zunpretty=hir,typed
+// check-pass
+
+pub fn main() ({
+ (if (true as bool)
+ ({ } as
+ ()) else {match ((Some as
+ fn(i32) -> Option<i32> {Option::<i32>::Some})((3
+ as
+ i32))
+ as Option<i32>) {
+ Some(a) => { }
+ _ => { }
+ }} as ())
+ } as ())
--- /dev/null
+// https://github.com/rust-lang/rust/issues/84434
+// check-pass
+
+use std::path::Path;
+struct A {
+ pub func: fn(check: bool, a: &Path, b: Option<&Path>),
+}
+const MY_A: A = A {
+ func: |check, a, b| {
+ if check {
+ let _ = ();
+ } else if let Some(parent) = b.and_then(|p| p.parent()) {
+ let _ = ();
+ }
+ },
+};
+
+fn main() {}
+++ /dev/null
-#![feature(main)]
-
-#[main]
-fn bar() {
-}
-
-#[main]
-fn foo() { //~ ERROR multiple functions with a `#[main]` attribute
-}
+++ /dev/null
-error[E0137]: multiple functions with a `#[main]` attribute
- --> $DIR/multiple-main-2.rs:8:1
- |
-LL | / fn bar() {
-LL | | }
- | |_- first `#[main]` function
-...
-LL | / fn foo() {
-LL | | }
- | |_^ additional `#[main]` function
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0137`.
+++ /dev/null
-#![feature(main)]
-
-#[main]
-fn main1() {
-}
-
-mod foo {
- #[main]
- fn main2() { //~ ERROR multiple functions with a `#[main]` attribute
- }
-}
+++ /dev/null
-error[E0137]: multiple functions with a `#[main]` attribute
- --> $DIR/multiple-main-3.rs:9:5
- |
-LL | / fn main1() {
-LL | | }
- | |_- first `#[main]` function
-...
-LL | / fn main2() {
-LL | | }
- | |_____^ additional `#[main]` function
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0137`.
| -
| |
| first assignment to `x`
- | help: make this binding mutable: `mut x`
+ | help: consider making this binding mutable: `mut x`
LL | x += 1;
| ^^^^^^ cannot assign twice to immutable variable
foo(_x);
//~^ ERROR the trait bound
//~| NOTE the trait `ImplementedForUnitButNotNever` is not implemented
- //~| NOTE the trait is implemented for `()`
+ //~| NOTE this trait is implemented for `()`
+ //~| NOTE this error might have been caused
+ //~| HELP did you intend
}
fn main() {
LL | foo(_x);
| ^^^ the trait `ImplementedForUnitButNotNever` is not implemented for `!`
|
- = note: the trait is implemented for `()`. Possibly this error has been caused by changes to Rust's type-inference algorithm (see issue #48950 <https://github.com/rust-lang/rust/issues/48950> for more information). Consider whether you meant to use the type `()` here instead.
+ = note: this trait is implemented for `()`.
+ = note: this error might have been caused by changes to Rust's type-inference algorithm (see issue #48950 <https://github.com/rust-lang/rust/issues/48950> for more information).
+ = help: did you intend to use the type `()` here instead?
error: aborting due to previous error
--- /dev/null
+extern crate krate-name-here;
+//~^ ERROR crate name using dashes are not valid in `extern crate` statements
+//~| ERROR can't find crate for `krate_name_here`
+
+fn main() {}
--- /dev/null
+error: crate name using dashes are not valid in `extern crate` statements
+ --> $DIR/bad-crate-name.rs:1:14
+ |
+LL | extern crate krate-name-here;
+ | ^^^^^^^^^^^^^^^ dash-separated idents are not valid
+ |
+help: if the original crate name uses dashes you need to use underscores in the code
+ |
+LL | extern crate krate_name_here;
+ | ^ ^
+
+error[E0463]: can't find crate for `krate_name_here`
+ --> $DIR/bad-crate-name.rs:1:1
+ |
+LL | extern crate krate-name-here;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't find crate
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0463`.
// ignore-pretty issue #37195
// ignore-asmjs wasm2js does not support source maps yet
-#![feature(non_ascii_idents)]
#![allow(uncommon_codepoints)]
#[path = "issue-48508-aux.rs"]
(()é);
//~^ ERROR: expected one of `)`, `,`, `.`, `?`, or an operator
//~| ERROR: cannot find value `é` in this scope
- //~| ERROR: non-ascii idents are not fully supported
(()氷);
//~^ ERROR: expected one of `)`, `,`, `.`, `?`, or an operator
//~| ERROR: cannot find value `氷` in this scope
- //~| ERROR: non-ascii idents are not fully supported
}
| help: missing `,`
error: expected one of `)`, `,`, `.`, `?`, or an operator, found `氷`
- --> $DIR/multibyte-char-use-seperator-issue-80134.rs:8:8
+ --> $DIR/multibyte-char-use-seperator-issue-80134.rs:7:8
|
LL | (()氷);
| -^
| ^ not found in this scope
error[E0425]: cannot find value `氷` in this scope
- --> $DIR/multibyte-char-use-seperator-issue-80134.rs:8:8
+ --> $DIR/multibyte-char-use-seperator-issue-80134.rs:7:8
|
LL | (()氷);
| ^^ not found in this scope
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/multibyte-char-use-seperator-issue-80134.rs:4:8
- |
-LL | (()é);
- | ^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/multibyte-char-use-seperator-issue-80134.rs:8:8
- |
-LL | (()氷);
- | ^^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error: aborting due to 6 previous errors
+error: aborting due to 4 previous errors
-Some errors have detailed explanations: E0425, E0658.
-For more information about an error, try `rustc --explain E0425`.
+For more information about this error, try `rustc --explain E0425`.
| ---
| |
| first assignment to `_x1`
- | help: make this binding mutable: `mut _x1`
+ | help: consider making this binding mutable: `mut _x1`
LL | _x1 = U;
| ^^^^^^^ cannot assign twice to immutable variable
| ---
| |
| first assignment to `_x1`
- | help: make this binding mutable: `mut _x1`
+ | help: consider making this binding mutable: `mut _x1`
LL | _x1 = U;
| ^^^^^^^ cannot assign twice to immutable variable
|
LL | let _x: Box<Bar>;
| ^^^
+ |
+ = help: `Bar` is a function item, not a type
+ = help: function item types cannot be named directly
error: aborting due to 4 previous errors
|
LL | let _x : Box<Bar>;
| ^^^
+ |
+ = help: `Bar` is a function item, not a type
+ = help: function item types cannot be named directly
error[E0747]: constant provided when a type was expected
--> $DIR/privacy-ns2.rs:48:17
|
LL | let _x: Box<Bar>;
| ^^^
+ |
+ = help: `Bar` is a function item, not a type
+ = help: function item types cannot be named directly
error: aborting due to 8 previous errors
//~^ ERROR casting
}
+fn c() {
+ let _ = [
+ std::intrinsics::copy_nonoverlapping::<i32>,
+ std::intrinsics::copy::<i32>,
+ //~^ ERROR cannot coerce
+ ];
+}
+
fn main() {}
LL | let _ = std::mem::transmute as unsafe extern "rust-intrinsic" fn(isize) -> usize;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 2 previous errors
+error[E0308]: cannot coerce intrinsics to function pointers
+ --> $DIR/reify-intrinsic.rs:18:9
+ |
+LL | std::intrinsics::copy::<i32>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot coerce intrinsics to function pointers
+ |
+ = note: expected type `unsafe extern "rust-intrinsic" fn(_, _, _) {copy_nonoverlapping::<i32>}`
+ found fn item `unsafe extern "rust-intrinsic" fn(_, _, _) {std::intrinsics::copy::<i32>}`
+
+error: aborting due to 3 previous errors
Some errors have detailed explanations: E0308, E0606.
For more information about an error, try `rustc --explain E0308`.
--- /dev/null
+#![crate_type = "lib"]
+
+pub mod public {
+ use private_import;
+
+ // should not be suggested since it is private
+ struct Foo;
+
+ mod private_module {
+ // should not be suggested since it is private
+ pub struct Foo;
+ }
+}
+
+mod private_import {
+ // should not be suggested since it is private
+ pub struct Foo;
+}
--- /dev/null
+// error-pattern: can't capture dynamic environment in a fn item
+fn foo() {
+ let x: isize;
+ fn bar() { log(debug, x); }
+}
+fn main() { foo(); }
--- /dev/null
+error[E0434]: can't capture dynamic environment in a fn item
+ --> $DIR/bad-env-capture.rs:4:27
+ |
+LL | fn bar() { log(debug, x); }
+ | ^
+ |
+ = help: use the `|| { ... }` closure form instead
+
+error[E0425]: cannot find function `log` in this scope
+ --> $DIR/bad-env-capture.rs:4:16
+ |
+LL | fn bar() { log(debug, x); }
+ | ^^^ not found in this scope
+
+error[E0425]: cannot find value `debug` in this scope
+ --> $DIR/bad-env-capture.rs:4:20
+ |
+LL | fn bar() { log(debug, x); }
+ | ^^^^^ not found in this scope
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0425, E0434.
+For more information about an error, try `rustc --explain E0425`.
--- /dev/null
+// error-pattern: can't capture dynamic environment in a fn item
+fn foo(x: isize) {
+ fn bar() { log(debug, x); }
+}
+fn main() { foo(2); }
--- /dev/null
+error[E0434]: can't capture dynamic environment in a fn item
+ --> $DIR/bad-env-capture2.rs:3:27
+ |
+LL | fn bar() { log(debug, x); }
+ | ^
+ |
+ = help: use the `|| { ... }` closure form instead
+
+error[E0425]: cannot find function `log` in this scope
+ --> $DIR/bad-env-capture2.rs:3:16
+ |
+LL | fn bar() { log(debug, x); }
+ | ^^^ not found in this scope
+
+error[E0425]: cannot find value `debug` in this scope
+ --> $DIR/bad-env-capture2.rs:3:20
+ |
+LL | fn bar() { log(debug, x); }
+ | ^^^^^ not found in this scope
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0425, E0434.
+For more information about an error, try `rustc --explain E0425`.
--- /dev/null
+// error-pattern: can't capture dynamic environment in a fn item
+fn foo(x: isize) {
+ fn mth() {
+ fn bar() { log(debug, x); }
+ }
+}
+
+fn main() { foo(2); }
--- /dev/null
+error[E0434]: can't capture dynamic environment in a fn item
+ --> $DIR/bad-env-capture3.rs:4:31
+ |
+LL | fn bar() { log(debug, x); }
+ | ^
+ |
+ = help: use the `|| { ... }` closure form instead
+
+error[E0425]: cannot find function `log` in this scope
+ --> $DIR/bad-env-capture3.rs:4:20
+ |
+LL | fn bar() { log(debug, x); }
+ | ^^^ not found in this scope
+
+error[E0425]: cannot find value `debug` in this scope
+ --> $DIR/bad-env-capture3.rs:4:24
+ |
+LL | fn bar() { log(debug, x); }
+ | ^^^^^ not found in this scope
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0425, E0434.
+For more information about an error, try `rustc --explain E0425`.
--- /dev/null
+mod m1 {}
+
+fn main(arguments: Vec<String>) { //~ ERROR `main` function has wrong type
+ log(debug, m1::arguments);
+ //~^ ERROR cannot find function `log` in this scope
+ //~| ERROR cannot find value `debug` in this scope
+ //~| ERROR cannot find value `arguments` in module `m1`
+}
--- /dev/null
+error[E0425]: cannot find function `log` in this scope
+ --> $DIR/bad-expr-path.rs:4:5
+ |
+LL | log(debug, m1::arguments);
+ | ^^^ not found in this scope
+
+error[E0425]: cannot find value `debug` in this scope
+ --> $DIR/bad-expr-path.rs:4:9
+ |
+LL | log(debug, m1::arguments);
+ | ^^^^^ not found in this scope
+
+error[E0425]: cannot find value `arguments` in module `m1`
+ --> $DIR/bad-expr-path.rs:4:20
+ |
+LL | log(debug, m1::arguments);
+ | ^^^^^^^^^ not found in `m1`
+
+error[E0580]: `main` function has wrong type
+ --> $DIR/bad-expr-path.rs:3:1
+ |
+LL | fn main(arguments: Vec<String>) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters
+ |
+ = note: expected fn pointer `fn()`
+ found fn pointer `fn(Vec<String>)`
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0425, E0580.
+For more information about an error, try `rustc --explain E0425`.
--- /dev/null
+mod m1 {
+ pub mod arguments {}
+}
+
+fn main(arguments: Vec<String>) { //~ ERROR `main` function has wrong type
+ log(debug, m1::arguments);
+ //~^ ERROR cannot find function `log` in this scope
+ //~| ERROR cannot find value `debug` in this scope
+ //~| ERROR expected value, found module `m1::arguments`
+}
--- /dev/null
+error[E0425]: cannot find function `log` in this scope
+ --> $DIR/bad-expr-path2.rs:6:5
+ |
+LL | log(debug, m1::arguments);
+ | ^^^ not found in this scope
+
+error[E0425]: cannot find value `debug` in this scope
+ --> $DIR/bad-expr-path2.rs:6:9
+ |
+LL | log(debug, m1::arguments);
+ | ^^^^^ not found in this scope
+
+error[E0423]: expected value, found module `m1::arguments`
+ --> $DIR/bad-expr-path2.rs:6:16
+ |
+LL | log(debug, m1::arguments);
+ | ^^^^^^^^^^^^^ not a value
+
+error[E0580]: `main` function has wrong type
+ --> $DIR/bad-expr-path2.rs:5:1
+ |
+LL | fn main(arguments: Vec<String>) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters
+ |
+ = note: expected fn pointer `fn()`
+ found fn pointer `fn(Vec<String>)`
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0423, E0425, E0580.
+For more information about an error, try `rustc --explain E0423`.
--- /dev/null
+fn main() {
+ let foo = thing::len(Vec::new());
+ //~^ ERROR failed to resolve: use of undeclared crate or module `thing`
+
+ let foo = foo::bar::baz();
+ //~^ ERROR failed to resolve: use of undeclared crate or module `foo`
+}
--- /dev/null
+error[E0433]: failed to resolve: use of undeclared crate or module `thing`
+ --> $DIR/bad-module.rs:2:15
+ |
+LL | let foo = thing::len(Vec::new());
+ | ^^^^^ use of undeclared crate or module `thing`
+
+error[E0433]: failed to resolve: use of undeclared crate or module `foo`
+ --> $DIR/bad-module.rs:5:15
+ |
+LL | let foo = foo::bar::baz();
+ | ^^^ use of undeclared crate or module `foo`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0433`.
--- /dev/null
+fn foo<T>() {
+ fn bar(b: T) { } //~ ERROR can't use generic parameters from outer
+}
+fn main() { }
--- /dev/null
+error[E0401]: can't use generic parameters from outer function
+ --> $DIR/bad-type-env-capture.rs:2:15
+ |
+LL | fn foo<T>() {
+ | - type parameter from outer function
+LL | fn bar(b: T) { }
+ | --- ^ use of generic parameter from outer function
+ | |
+ | help: try using a local generic parameter instead: `bar<T>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0401`.
| ^^^ type aliases cannot be used as traits
|
help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias
- --> $DIR/issue-3907.rs:5:1
|
-LL | type Foo = dyn issue_3907::Foo;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | trait Foo = dyn issue_3907::Foo;
+ |
help: consider importing this trait instead
|
LL | use issue_3907::Foo;
= help: unsized fn params are gated as an unstable feature
help: function arguments must have a statically known size, borrowed types always have a known size
|
-LL | fn foo(&_x: K) {}
- | ^
+LL | fn foo(_x: &K) {}
+ | ^
error: aborting due to previous error
| ------- similarly named trait `I` defined here
LL | type K = dyn I;
LL | impl K for isize {}
- | ^
- | |
- | type aliases cannot be used as traits
- | help: a trait with a similar name exists: `I`
+ | ^ type aliases cannot be used as traits
|
help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias
- --> $DIR/issue-5035.rs:2:1
|
-LL | type K = dyn I;
- | ^^^^^^^^^^^^^^^
+LL | trait K = dyn I;
+ |
+help: a trait with a similar name exists
+ |
+LL | impl I for isize {}
+ | ^
error: aborting due to 2 previous errors
--- /dev/null
+// aux-build:issue-80079.rs
+
+// using a module from another crate should not cause errors to suggest private
+// items in that module
+
+extern crate issue_80079;
+
+use issue_80079::public;
+
+fn main() {
+ let _ = Foo; //~ ERROR cannot find value `Foo` in this scope
+}
--- /dev/null
+error[E0425]: cannot find value `Foo` in this scope
+ --> $DIR/issue-80079.rs:11:13
+ |
+LL | let _ = Foo;
+ | ^^^ not found in this scope
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0425`.
| ^^^^^^^^^^^^^^^^^^^^^^^ type aliases cannot be used as traits
|
help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias
- --> $DIR/unboxed-closure-sugar-nonexistent-trait.rs:4:1
|
-LL | type Typedef = isize;
- | ^^^^^^^^^^^^^^^^^^^^^
+LL | trait Typedef = isize;
+ |
error: aborting due to 2 previous errors
-#![feature(non_ascii_idents)]
-
extern crate ьаг; //~ ERROR cannot load a crate with a non-ascii name `ьаг`
fn main() {}
error: cannot load a crate with a non-ascii name `ьаг`
- --> $DIR/crate_name_nonascii_forbidden-1.rs:3:1
+ --> $DIR/crate_name_nonascii_forbidden-1.rs:1:1
|
LL | extern crate ьаг;
| ^^^^^^^^^^^^^^^^^
// compile-flags:--extern му_сгате
// edition:2018
-#![feature(non_ascii_idents)]
use му_сгате::baz; //~ ERROR cannot load a crate with a non-ascii name `му_сгате`
error: cannot load a crate with a non-ascii name `му_сгате`
- --> $DIR/crate_name_nonascii_forbidden-2.rs:5:5
+ --> $DIR/crate_name_nonascii_forbidden-2.rs:4:5
|
LL | use му_сгате::baz;
| ^^^^^^^^
#![feature(extern_types)]
-#![feature(non_ascii_idents)]
extern "C" {
type 一; //~ items in `extern` blocks cannot use non-ascii identifiers
error: items in `extern` blocks cannot use non-ascii identifiers
- --> $DIR/extern_block_nonascii_forbidden.rs:5:10
+ --> $DIR/extern_block_nonascii_forbidden.rs:4:10
|
LL | extern "C" {
| ---------- in this `extern` block
= note: This limitation may be lifted in the future; see issue #83942 <https://github.com/rust-lang/rust/issues/83942> for more information
error: items in `extern` blocks cannot use non-ascii identifiers
- --> $DIR/extern_block_nonascii_forbidden.rs:6:8
+ --> $DIR/extern_block_nonascii_forbidden.rs:5:8
|
LL | extern "C" {
| ---------- in this `extern` block
= note: This limitation may be lifted in the future; see issue #83942 <https://github.com/rust-lang/rust/issues/83942> for more information
error: items in `extern` blocks cannot use non-ascii identifiers
- --> $DIR/extern_block_nonascii_forbidden.rs:7:12
+ --> $DIR/extern_block_nonascii_forbidden.rs:6:12
|
LL | extern "C" {
| ---------- in this `extern` block
// check-pass
-#![feature(non_ascii_idents)]
struct Résumé; // ['LATIN SMALL LETTER E WITH ACUTE']
-#![feature(non_ascii_idents)]
-
mod řųśť; //~ trying to load file for
//~^ file not found for
error[E0583]: file not found for module `řųśť`
- --> $DIR/mod_file_nonascii_forbidden.rs:3:1
+ --> $DIR/mod_file_nonascii_forbidden.rs:1:1
|
LL | mod řųśť;
| ^^^^^^^^^
= help: to create the module `řųśť`, create file "$DIR/řųśť.rs"
error[E0754]: trying to load file for module `řųśť` with non-ascii identifier name
- --> $DIR/mod_file_nonascii_forbidden.rs:3:5
+ --> $DIR/mod_file_nonascii_forbidden.rs:1:5
|
LL | mod řųśť;
| ^^^^
// check-pass
-#![feature(non_ascii_idents)]
#[path="auxiliary/mod_file_nonascii_with_path_allowed-aux.rs"]
mod řųśť;
// check-pass
-#![feature(non_ascii_idents)]
mod řųśť {
const IS_GREAT: bool = true;
-#![feature(non_ascii_idents)]
-
#[no_mangle]
pub fn řųśť() {} //~ `#[no_mangle]` requires ASCII identifier
error[E0754]: `#[no_mangle]` requires ASCII identifier
- --> $DIR/no_mangle_nonascii_forbidden.rs:4:1
+ --> $DIR/no_mangle_nonascii_forbidden.rs:2:1
|
LL | pub fn řųśť() {}
| ^^^^^^^^^^^^^
// check-pass
-#![feature(const_fn)]
#![feature(const_trait_impl)]
+#![feature(const_fn_trait_bound)]
#![allow(incomplete_features)]
struct S;
// check-pass
-#![feature(const_fn)]
#![feature(const_trait_impl)]
#![feature(const_trait_bound_opt_out)]
+#![feature(const_fn_trait_bound)]
#![allow(incomplete_features)]
struct S;
// check-pass
-#![feature(const_fn)]
#![feature(const_trait_impl)]
+#![feature(const_fn_trait_bound)]
#![allow(incomplete_features)]
struct S;
#![allow(incomplete_features)]
#![feature(const_trait_impl)]
-#![feature(const_fn)]
+#![feature(const_fn_trait_bound)]
use std::marker::PhantomData;
-error[E0723]: trait methods cannot be stable const fn
+error: trait methods cannot be stable const fn
--> $DIR/stability.rs:14:5
|
LL | / fn sub(self, rhs: Self) -> Self {
error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0723`.
error[E0599]: no method named `foobar` found for type `u32` in the current scope
--> $DIR/trait-import-suggestions.rs:22:11
|
+LL | fn foobar(&self) { }
+ | ------ the method is available for `u32` here
+...
LL | x.foobar();
| ^^^^^^ method not found in `u32`
|
error[E0599]: no method named `bar` found for type `u32` in the current scope
--> $DIR/trait-import-suggestions.rs:28:7
|
+LL | fn bar(&self) { }
+ | --- the method is available for `u32` here
+...
LL | x.bar();
| ^^^ method not found in `u32`
|
// error-pattern: AddressSanitizer: stack-buffer-overflow
// error-pattern: 'xs' (line 15) <== Memory access at offset
-#![feature(test)]
+#![feature(bench_black_box)]
use std::hint::black_box;
// run-fail
// error-pattern: HWAddressSanitizer: tag-mismatch
-#![feature(test)]
+#![feature(bench_black_box)]
use std::hint::black_box;
// run-fail
// error-pattern: LeakSanitizer: detected memory leaks
-#![feature(test)]
+#![feature(bench_black_box)]
use std::hint::black_box;
use std::mem;
#![feature(core_intrinsics)]
#![feature(start)]
-#![feature(test)]
+#![feature(bench_black_box)]
use std::hint::black_box;
use std::mem::MaybeUninit;
error[E0599]: no method named `f` found for unit type `()` in the current scope
--> $DIR/shadowed-trait-methods.rs:13:8
|
+LL | pub trait T { fn f(&self) {} }
+ | - the method is available for `()` here
+...
LL | ().f()
| ^ method not found in `()`
|
fn simd_fabs<T>(x: T) -> T;
fn simd_fsin<T>(x: T) -> T;
fn simd_fcos<T>(x: T) -> T;
- fn simd_ceil<T>(x: T) -> T;
fn simd_fexp<T>(x: T) -> T;
fn simd_fexp2<T>(x: T) -> T;
- fn simd_floor<T>(x: T) -> T;
fn simd_fma<T>(x: T, y: T, z: T) -> T;
fn simd_flog<T>(x: T) -> T;
fn simd_flog10<T>(x: T) -> T;
fn simd_flog2<T>(x: T) -> T;
fn simd_fpow<T>(x: T, y: T) -> T;
fn simd_fpowi<T>(x: T, y: i32) -> T;
+
+ // rounding functions
+ fn simd_ceil<T>(x: T) -> T;
+ fn simd_floor<T>(x: T) -> T;
+ fn simd_round<T>(x: T) -> T;
+ fn simd_trunc<T>(x: T) -> T;
}
macro_rules! assert_approx_eq_f32 {
let r = simd_fcos(z);
assert_approx_eq!(x, r);
- let r = simd_ceil(h);
- assert_approx_eq!(x, r);
-
let r = simd_fexp(z);
assert_approx_eq!(x, r);
let r = simd_fexp2(z);
assert_approx_eq!(x, r);
- let r = simd_floor(h);
- assert_approx_eq!(z, r);
-
let r = simd_fma(x, h, h);
assert_approx_eq!(x, r);
let r = simd_fsin(z);
assert_approx_eq!(z, r);
+
+ // rounding functions
+ let r = simd_floor(h);
+ assert_eq!(z, r);
+
+ let r = simd_ceil(h);
+ assert_eq!(x, r);
+
+ let r = simd_round(h);
+ assert_eq!(x, r);
+
+ let r = simd_trunc(h);
+ assert_eq!(z, r);
}
}
--- /dev/null
+// Test to ensure that trait bounds are propertly
+// checked on specializable associated types
+
+#![allow(incomplete_features)]
+#![feature(specialization)]
+
+trait UncheckedCopy: Sized {
+ type Output: From<Self> + Copy + Into<Self>;
+}
+
+impl<T> UncheckedCopy for T {
+ default type Output = Self;
+ //~^ ERROR: the trait bound `T: Copy` is not satisfied
+}
+
+fn unchecked_copy<T: UncheckedCopy>(other: &T::Output) -> T {
+ (*other).into()
+}
+
+fn bug(origin: String) {
+ // Turn the String into it's Output type...
+ // Which we can just do by `.into()`, the assoc type states `From<Self>`.
+ let origin_output = origin.into();
+
+ // Make a copy of String::Output, which is a String...
+ let mut copy: String = unchecked_copy::<String>(&origin_output);
+
+ // Turn the Output type into a String again,
+ // Which we can just do by `.into()`, the assoc type states `Into<Self>`.
+ let mut origin: String = origin_output.into();
+
+ // assert both Strings use the same buffer.
+ assert_eq!(copy.as_ptr(), origin.as_ptr());
+
+ // Any use of the copy we made becomes invalid,
+ drop(origin);
+
+ // OH NO! UB UB UB UB!
+ copy.push_str(" world!");
+ println!("{}", copy);
+}
+
+fn main() {
+ bug(String::from("hello"));
+}
--- /dev/null
+error[E0277]: the trait bound `T: Copy` is not satisfied
+ --> $DIR/issue-33017.rs:12:5
+ |
+LL | type Output: From<Self> + Copy + Into<Self>;
+ | ---- required by this bound in `UncheckedCopy::Output`
+...
+LL | default type Output = Self;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T`
+ |
+help: consider restricting type parameter `T`
+ |
+LL | impl<T: std::marker::Copy> UncheckedCopy for T {
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+#![allow(incomplete_features)]
+#![feature(const_generics)]
+#![feature(const_evaluatable_checked)]
+#![feature(specialization)]
+
+pub trait Trait {
+ type Type;
+}
+
+impl<T: ?Sized> Trait for T {
+ default type Type = [u8; 1];
+}
+
+impl<T: Trait> Trait for *const T {
+ type Type = [u8; std::mem::size_of::<<T as Trait>::Type>()];
+ //~^ ERROR: unconstrained generic constant
+}
+
+fn main() {}
--- /dev/null
+error: unconstrained generic constant
+ --> $DIR/issue-51892.rs:15:5
+ |
+LL | type Type = [u8; std::mem::size_of::<<T as Trait>::Type>()];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: try adding a `where` bound using this expression: `where [(); std::mem::size_of::<<T as Trait>::Type>()]:`
+
+error: aborting due to previous error
+
--- /dev/null
+static i: String = 10;
+//~^ ERROR mismatched types
+//~| expected struct `String`, found integer
+fn main() { println!("{}", i); }
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/bad-const-type.rs:1:20
+ |
+LL | static i: String = 10;
+ | ^^
+ | |
+ | expected struct `String`, found integer
+ | help: try using a conversion method: `10.to_string()`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// run-rustfix
+
+struct Struct;
+
+fn bar(_: &Struct) -> Struct {
+ Struct
+}
+
+fn main() {
+ let foo = Some(Struct);
+ let _x: Option<Struct> = foo.as_ref().map(|s| bar(&s));
+ let _y = foo; //~ERROR use of moved value: `foo`
+}
--- /dev/null
+// run-rustfix
+
+struct Struct;
+
+fn bar(_: &Struct) -> Struct {
+ Struct
+}
+
+fn main() {
+ let foo = Some(Struct);
+ let _x: Option<Struct> = foo.map(|s| bar(&s));
+ let _y = foo; //~ERROR use of moved value: `foo`
+}
--- /dev/null
+error[E0382]: use of moved value: `foo`
+ --> $DIR/as-ref-2.rs:12:14
+ |
+LL | let foo = Some(Struct);
+ | --- move occurs because `foo` has type `Option<Struct>`, which does not implement the `Copy` trait
+LL | let _x: Option<Struct> = foo.map(|s| bar(&s));
+ | ---------------- `foo` moved due to this method call
+LL | let _y = foo;
+ | ^^^ value used here after move
+ |
+note: this function takes ownership of the receiver `self`, which moves `foo`
+ --> $SRC_DIR/core/src/option.rs:LL:COL
+ |
+LL | pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> {
+ | ^^^^
+help: consider calling `.as_ref()` to borrow the type's contents
+ |
+LL | let _x: Option<Struct> = foo.as_ref().map(|s| bar(&s));
+ | ^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
struct Foo;
+
fn takes_ref(_: &Foo) {}
fn main() {
- let ref opt = Some(Foo);
- opt.map(|arg| takes_ref(arg));
- //~^ ERROR mismatched types [E0308]
- opt.and_then(|arg| Some(takes_ref(arg)));
- //~^ ERROR mismatched types [E0308]
- let ref opt: Result<_, ()> = Ok(Foo);
- opt.map(|arg| takes_ref(arg));
- //~^ ERROR mismatched types [E0308]
- opt.and_then(|arg| Ok(takes_ref(arg)));
- //~^ ERROR mismatched types [E0308]
- let x: &Option<usize> = &Some(3);
- let y: Option<&usize> = x;
- //~^ ERROR mismatched types [E0308]
- let x: &Result<usize, usize> = &Ok(3);
- let y: Result<&usize, &usize> = x;
- //~^ ERROR mismatched types [E0308]
- // note: do not suggest because of `E: usize`
- let x: &Result<usize, usize> = &Ok(3);
- let y: Result<&usize, usize> = x;
- //~^ ERROR mismatched types [E0308]
+ let ref opt = Some(Foo);
+ opt.map(|arg| takes_ref(arg)); //~ ERROR mismatched types [E0308]
+ opt.and_then(|arg| Some(takes_ref(arg))); //~ ERROR mismatched types [E0308]
+ let ref opt: Result<_, ()> = Ok(Foo);
+ opt.map(|arg| takes_ref(arg)); //~ ERROR mismatched types [E0308]
+ opt.and_then(|arg| Ok(takes_ref(arg))); //~ ERROR mismatched types [E0308]
+ let x: &Option<usize> = &Some(3);
+ let y: Option<&usize> = x; //~ ERROR mismatched types [E0308]
+ let x: &Result<usize, usize> = &Ok(3);
+ let y: Result<&usize, &usize> = x;
+ //~^ ERROR mismatched types [E0308]
+ // note: do not suggest because of `E: usize`
+ let x: &Result<usize, usize> = &Ok(3);
+ let y: Result<&usize, usize> = x; //~ ERROR mismatched types [E0308]
}
error[E0308]: mismatched types
- --> $DIR/as-ref.rs:6:27
+ --> $DIR/as-ref.rs:7:29
|
-LL | opt.map(|arg| takes_ref(arg));
- | --- ^^^ expected `&Foo`, found struct `Foo`
- | |
- | help: consider using `as_ref` instead: `as_ref().map`
+LL | opt.map(|arg| takes_ref(arg));
+ | --- ^^^ expected `&Foo`, found struct `Foo`
+ | |
+ | help: consider using `as_ref` instead: `as_ref().map`
error[E0308]: mismatched types
- --> $DIR/as-ref.rs:8:37
+ --> $DIR/as-ref.rs:8:39
|
-LL | opt.and_then(|arg| Some(takes_ref(arg)));
- | -------- ^^^ expected `&Foo`, found struct `Foo`
- | |
- | help: consider using `as_ref` instead: `as_ref().and_then`
+LL | opt.and_then(|arg| Some(takes_ref(arg)));
+ | -------- ^^^ expected `&Foo`, found struct `Foo`
+ | |
+ | help: consider using `as_ref` instead: `as_ref().and_then`
error[E0308]: mismatched types
- --> $DIR/as-ref.rs:11:27
+ --> $DIR/as-ref.rs:10:29
|
-LL | opt.map(|arg| takes_ref(arg));
- | --- ^^^ expected `&Foo`, found struct `Foo`
- | |
- | help: consider using `as_ref` instead: `as_ref().map`
+LL | opt.map(|arg| takes_ref(arg));
+ | --- ^^^ expected `&Foo`, found struct `Foo`
+ | |
+ | help: consider using `as_ref` instead: `as_ref().map`
error[E0308]: mismatched types
- --> $DIR/as-ref.rs:13:35
+ --> $DIR/as-ref.rs:11:37
|
-LL | opt.and_then(|arg| Ok(takes_ref(arg)));
- | -------- ^^^ expected `&Foo`, found struct `Foo`
- | |
- | help: consider using `as_ref` instead: `as_ref().and_then`
+LL | opt.and_then(|arg| Ok(takes_ref(arg)));
+ | -------- ^^^ expected `&Foo`, found struct `Foo`
+ | |
+ | help: consider using `as_ref` instead: `as_ref().and_then`
error[E0308]: mismatched types
- --> $DIR/as-ref.rs:16:27
+ --> $DIR/as-ref.rs:13:29
|
-LL | let y: Option<&usize> = x;
- | -------------- ^
- | | |
- | | expected enum `Option`, found `&Option<usize>`
- | | help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`: `x.as_ref()`
- | expected due to this
+LL | let y: Option<&usize> = x;
+ | -------------- ^
+ | | |
+ | | expected enum `Option`, found `&Option<usize>`
+ | | help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`: `x.as_ref()`
+ | expected due to this
|
= note: expected enum `Option<&usize>`
found reference `&Option<usize>`
error[E0308]: mismatched types
- --> $DIR/as-ref.rs:19:35
+ --> $DIR/as-ref.rs:15:37
|
-LL | let y: Result<&usize, &usize> = x;
- | ---------------------- ^ expected enum `Result`, found reference
- | |
- | expected due to this
+LL | let y: Result<&usize, &usize> = x;
+ | ---------------------- ^ expected enum `Result`, found reference
+ | |
+ | expected due to this
|
= note: expected enum `Result<&usize, &usize>`
found reference `&Result<usize, usize>`
help: you can convert from `&Result<T, E>` to `Result<&T, &E>` using `.as_ref()`
|
-LL | let y: Result<&usize, &usize> = x.as_ref();
- | ^^^^^^^^^^
+LL | let y: Result<&usize, &usize> = x.as_ref();
+ | ^^^^^^^^^^
error[E0308]: mismatched types
- --> $DIR/as-ref.rs:23:34
+ --> $DIR/as-ref.rs:19:36
|
-LL | let y: Result<&usize, usize> = x;
- | --------------------- ^ expected enum `Result`, found reference
- | |
- | expected due to this
+LL | let y: Result<&usize, usize> = x;
+ | --------------------- ^ expected enum `Result`, found reference
+ | |
+ | expected due to this
|
= note: expected enum `Result<&usize, usize>`
found reference `&Result<usize, usize>`
--- /dev/null
+use std::hash::BuildHasher;
+
+fn next_u64() -> u64 {
+ let bh = std::collections::hash_map::RandomState::new();
+ let h = bh.build_hasher();
+ h.finish() //~ ERROR no method named `finish` found for struct `DefaultHasher`
+}
+
+fn main() {}
--- /dev/null
+error[E0599]: no method named `finish` found for struct `DefaultHasher` in the current scope
+ --> $DIR/import-trait-for-method-call.rs:6:7
+ |
+LL | h.finish()
+ | ^^^^^^ method not found in `DefaultHasher`
+ |
+ ::: $SRC_DIR/core/src/hash/mod.rs:LL:COL
+ |
+LL | fn finish(&self) -> u64;
+ | ------ the method is available for `DefaultHasher` here
+ |
+ = help: items from traits can only be used if the trait is in scope
+help: the following trait is implemented but not in scope; perhaps add a `use` for it:
+ |
+LL | use std::hash::Hasher;
+ |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
= help: unsized fn params are gated as an unstable feature
help: function arguments must have a statically known size, borrowed types always have a known size
|
-LL | fn f(&p: Path) { }
- | ^
+LL | fn f(p: &Path) { }
+ | ^
error: aborting due to previous error
// run-pass
// compile-flags: --test
-#![feature(main)]
+#![feature(rustc_attrs)]
#![deny(dead_code)]
-#[main]
+#[rustc_main]
fn foo() { panic!(); }
// run-pass
// compile-flags: --test
-#![feature(main)]
+#![feature(rustc_attrs)]
#![allow(dead_code)]
mod a {
fn b() {
(|| {
- #[main]
+ #[rustc_main]
fn c() { panic!(); }
})();
}
--- /dev/null
+// Regression test of #43913.
+
+// run-rustfix
+
+#![feature(trait_alias)]
+#![allow(bare_trait_objects, dead_code)]
+
+trait Strings = Iterator<Item=String>;
+
+struct Struct<S: Strings>(S);
+//~^ ERROR: expected trait, found type alias `Strings`
+
+fn main() {}
--- /dev/null
+// Regression test of #43913.
+
+// run-rustfix
+
+#![feature(trait_alias)]
+#![allow(bare_trait_objects, dead_code)]
+
+type Strings = Iterator<Item=String>;
+
+struct Struct<S: Strings>(S);
+//~^ ERROR: expected trait, found type alias `Strings`
+
+fn main() {}
--- /dev/null
+error[E0404]: expected trait, found type alias `Strings`
+ --> $DIR/suggest-trait-alias-instead-of-type.rs:10:18
+ |
+LL | struct Struct<S: Strings>(S);
+ | ^^^^^^^ type aliases cannot be used as traits
+ |
+help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias
+ |
+LL | trait Strings = Iterator<Item=String>;
+ |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0404`.
--- /dev/null
+fn foo<T:'static>() {
+ 1.bar::<T>(); //~ ERROR `T` cannot be sent between threads safely
+}
+
+trait Bar {
+ fn bar<T:Send>(&self);
+}
+
+impl Bar for usize {
+ fn bar<T:Send>(&self) {
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0277]: `T` cannot be sent between threads safely
+ --> $DIR/bad-method-typaram-kind.rs:2:7
+ |
+LL | 1.bar::<T>();
+ | ^^^ `T` cannot be sent between threads safely
+ |
+help: consider further restricting this bound
+ |
+LL | fn foo<T:'static + std::marker::Send>() {
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+trait Trait {}
+
+pub fn main() {
+ let x: Vec<dyn Trait + Sized> = Vec::new();
+ //~^ ERROR only auto traits can be used as additional traits in a trait object
+ //~| ERROR the size for values of type
+ //~| ERROR the size for values of type
+ //~| ERROR the size for values of type
+}
--- /dev/null
+error[E0225]: only auto traits can be used as additional traits in a trait object
+ --> $DIR/bad-sized.rs:4:28
+ |
+LL | let x: Vec<dyn Trait + Sized> = Vec::new();
+ | ----- ^^^^^ additional non-auto trait
+ | |
+ | first non-auto trait
+ |
+ = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Trait + Sized {}`
+ = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
+
+error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time
+ --> $DIR/bad-sized.rs:4:12
+ |
+LL | let x: Vec<dyn Trait + Sized> = Vec::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+ |
+ ::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ |
+LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
+ | - required by this bound in `Vec`
+ |
+ = help: the trait `Sized` is not implemented for `dyn Trait`
+
+error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time
+ --> $DIR/bad-sized.rs:4:37
+ |
+LL | let x: Vec<dyn Trait + Sized> = Vec::new();
+ | ^^^^^^^^ doesn't have a size known at compile-time
+ |
+ = help: the trait `Sized` is not implemented for `dyn Trait`
+ = note: required by `Vec::<T>::new`
+
+error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time
+ --> $DIR/bad-sized.rs:4:37
+ |
+LL | let x: Vec<dyn Trait + Sized> = Vec::new();
+ | ^^^ doesn't have a size known at compile-time
+ |
+ ::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ |
+LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
+ | - required by this bound in `Vec`
+ |
+ = help: the trait `Sized` is not implemented for `dyn Trait`
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0225, E0277.
+For more information about an error, try `rustc --explain E0225`.
= help: unsized fn params are gated as an unstable feature
help: function arguments must have a statically known size, borrowed types always have a known size
|
-LL | fn foo(&_x: Foo + Send) {
- | ^
+LL | fn foo(_x: &Foo + Send) {
+ | ^
error: aborting due to previous error; 1 warning emitted
LL | struct S;
| --------- method `b` not found for this
...
+LL | fn b(&self) { }
+ | - the method is available for `S` here
+...
LL | S.b();
| ^ method not found in `S`
|
--- /dev/null
+// Test that using typeof results in the correct type mismatch errors instead of always assuming
+// `usize`, in addition to the pre-existing "typeof is reserved and unimplemented" error
+fn main() {
+ const a: u8 = 1;
+ let b: typeof(a) = 1i8;
+ //~^ ERROR `typeof` is a reserved keyword but unimplemented
+ //~| ERROR mismatched types
+ //~| expected `u8`, found `i8`
+}
--- /dev/null
+error[E0516]: `typeof` is a reserved keyword but unimplemented
+ --> $DIR/type_mismatch.rs:5:12
+ |
+LL | let b: typeof(a) = 1i8;
+ | ^^^^^^^^^ reserved keyword
+
+error[E0308]: mismatched types
+ --> $DIR/type_mismatch.rs:5:24
+ |
+LL | let b: typeof(a) = 1i8;
+ | --------- ^^^ expected `u8`, found `i8`
+ | |
+ | expected due to this
+ |
+help: change the type of the numeric literal from `i8` to `u8`
+ |
+LL | let b: typeof(a) = 1u8;
+ | ^^^
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0308, E0516.
+For more information about an error, try `rustc --explain E0308`.
+++ /dev/null
-// run-pass
-
-#![feature(unsized_tuple_coercion)]
-
-use std::collections::HashSet;
-
-fn main() {
- let x : &(i32, i32, [i32]) = &(0, 1, [2, 3]);
- let y : &(i32, i32, [i32]) = &(0, 1, [2, 3, 4]);
- let mut a = [y, x];
- a.sort();
- assert_eq!(a, [x, y]);
-
- assert_eq!(&format!("{:?}", a), "[(0, 1, [2, 3]), (0, 1, [2, 3, 4])]");
-
- let mut h = HashSet::new();
- h.insert(x);
- h.insert(y);
- assert!(h.contains(x));
- assert!(h.contains(y));
-}
+++ /dev/null
-// run-pass
-
-#![allow(type_alias_bounds)]
-#![allow(dead_code)]
-// Test syntax checks for `?Sized` syntax.
-
-use std::marker::PhantomData;
-
-trait T1 { }
-pub trait T2 { }
-trait T3<X: T1> : T2 { }
-trait T4<X: ?Sized> { }
-trait T5<X: ?Sized, Y> { }
-trait T6<Y, X: ?Sized> { }
-trait T7<X: ?Sized, Y: ?Sized> { }
-trait T8<X: ?Sized+T2> { }
-trait T9<X: T2 + ?Sized> { }
-struct S1<X: ?Sized>(PhantomData<X>);
-enum E<X: ?Sized> { E1(PhantomData<X>) }
-impl <X: ?Sized> T1 for S1<X> {}
-fn f<X: ?Sized>() {}
-type TT<T: ?Sized> = T;
-
-pub fn main() {
-}
--- /dev/null
+// run-rustfix
+#![crate_type="lib"]
+#![allow(unused)]
+
+fn f<T: ?Sized>(t: &T) {}
+//~^ ERROR the size for values of type `T` cannot be known at compilation time
--- /dev/null
+// run-rustfix
+#![crate_type="lib"]
+#![allow(unused)]
+
+fn f<T: ?Sized>(t: T) {}
+//~^ ERROR the size for values of type `T` cannot be known at compilation time
--- /dev/null
+error[E0277]: the size for values of type `T` cannot be known at compilation time
+ --> $DIR/unsized-fn-arg.rs:5:17
+ |
+LL | fn f<T: ?Sized>(t: T) {}
+ | - ^ doesn't have a size known at compile-time
+ | |
+ | this type parameter needs to be `std::marker::Sized`
+ |
+ = help: unsized fn params are gated as an unstable feature
+help: function arguments must have a statically known size, borrowed types always have a known size
+ |
+LL | fn f<T: ?Sized>(t: &T) {}
+ | ^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// run-pass
+
+#![feature(unsized_tuple_coercion)]
+
+use std::collections::HashSet;
+
+fn main() {
+ let x : &(i32, i32, [i32]) = &(0, 1, [2, 3]);
+ let y : &(i32, i32, [i32]) = &(0, 1, [2, 3, 4]);
+ let mut a = [y, x];
+ a.sort();
+ assert_eq!(a, [x, y]);
+
+ assert_eq!(&format!("{:?}", a), "[(0, 1, [2, 3]), (0, 1, [2, 3, 4])]");
+
+ let mut h = HashSet::new();
+ h.insert(x);
+ h.insert(y);
+ assert!(h.contains(x));
+ assert!(h.contains(y));
+}
--- /dev/null
+// run-pass
+
+#![allow(type_alias_bounds)]
+#![allow(dead_code)]
+// Test syntax checks for `?Sized` syntax.
+
+use std::marker::PhantomData;
+
+trait T1 { }
+pub trait T2 { }
+trait T3<X: T1> : T2 { }
+trait T4<X: ?Sized> { }
+trait T5<X: ?Sized, Y> { }
+trait T6<Y, X: ?Sized> { }
+trait T7<X: ?Sized, Y: ?Sized> { }
+trait T8<X: ?Sized+T2> { }
+trait T9<X: T2 + ?Sized> { }
+struct S1<X: ?Sized>(PhantomData<X>);
+enum E<X: ?Sized> { E1(PhantomData<X>) }
+impl <X: ?Sized> T1 for S1<X> {}
+fn f<X: ?Sized>() {}
+type TT<T: ?Sized> = T;
+
+pub fn main() {
+}
--- /dev/null
+// run-pass
+
+#![allow(unconditional_recursion)]
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![allow(unused_imports)]
+#![feature(box_syntax)]
+
+// Test sized-ness checking in substitution.
+
+use std::marker;
+
+// Unbounded.
+fn f1<X: ?Sized>(x: &X) {
+ f1::<X>(x);
+}
+fn f2<X>(x: &X) {
+ f1::<X>(x);
+ f2::<X>(x);
+}
+
+// Bounded.
+trait T { fn dummy(&self) { } }
+fn f3<X: T+?Sized>(x: &X) {
+ f3::<X>(x);
+}
+fn f4<X: T>(x: &X) {
+ f3::<X>(x);
+ f4::<X>(x);
+}
+
+// Self type.
+trait T2 {
+ fn f() -> Box<Self>;
+}
+struct S;
+impl T2 for S {
+ fn f() -> Box<S> {
+ box S
+ }
+}
+fn f5<X: ?Sized+T2>(x: &X) {
+ let _: Box<X> = T2::f();
+}
+fn f6<X: T2>(x: &X) {
+ let _: Box<X> = T2::f();
+}
+
+trait T3 {
+ fn f() -> Box<Self>;
+}
+impl T3 for S {
+ fn f() -> Box<S> {
+ box S
+ }
+}
+fn f7<X: ?Sized+T3>(x: &X) {
+ // This is valid, but the unsized bound on X is irrelevant because any type
+ // which implements T3 must have statically known size.
+ let _: Box<X> = T3::f();
+}
+
+trait T4<X> {
+ fn dummy(&self) { }
+ fn m1(&self, x: &dyn T4<X>, y: X);
+ fn m2(&self, x: &dyn T5<X>, y: X);
+}
+trait T5<X: ?Sized> {
+ fn dummy(&self) { }
+ // not an error (for now)
+ fn m1(&self, x: &dyn T4<X>);
+ fn m2(&self, x: &dyn T5<X>);
+}
+
+trait T6<X: T> {
+ fn dummy(&self) { }
+ fn m1(&self, x: &dyn T4<X>);
+ fn m2(&self, x: &dyn T5<X>);
+}
+trait T7<X: ?Sized+T> {
+ fn dummy(&self) { }
+ // not an error (for now)
+ fn m1(&self, x: &dyn T4<X>);
+ fn m2(&self, x: &dyn T5<X>);
+}
+
+// The last field in a struct may be unsized
+struct S2<X: ?Sized> {
+ f: X,
+}
+struct S3<X: ?Sized> {
+ f1: isize,
+ f2: X,
+}
+
+pub fn main() {
+}
--- /dev/null
+// run-pass
+// Test structs with always-unsized fields.
+
+
+#![allow(warnings)]
+#![feature(box_syntax, unsize, raw)]
+
+use std::mem;
+use std::raw;
+use std::slice;
+
+struct Foo<T> {
+ f: [T],
+}
+
+struct Bar {
+ f1: usize,
+ f2: [usize],
+}
+
+struct Baz {
+ f1: usize,
+ f2: str,
+}
+
+trait Tr {
+ fn foo(&self) -> usize;
+}
+
+struct St {
+ f: usize
+}
+
+impl Tr for St {
+ fn foo(&self) -> usize {
+ self.f
+ }
+}
+
+struct Qux<'a> {
+ f: Tr+'a
+}
+
+pub fn main() {
+ let _: &Foo<f64>;
+ let _: &Bar;
+ let _: &Baz;
+
+ let _: Box<Foo<i32>>;
+ let _: Box<Bar>;
+ let _: Box<Baz>;
+
+ let _ = mem::size_of::<Box<Foo<u8>>>();
+ let _ = mem::size_of::<Box<Bar>>();
+ let _ = mem::size_of::<Box<Baz>>();
+
+ unsafe {
+ struct Foo_<T> {
+ f: [T; 3]
+ }
+
+ let data: Box<Foo_<i32>> = box Foo_{f: [1, 2, 3] };
+ let x: &Foo<i32> = mem::transmute(slice::from_raw_parts(&*data, 3));
+ assert_eq!(x.f.len(), 3);
+ assert_eq!(x.f[0], 1);
+
+ struct Baz_ {
+ f1: usize,
+ f2: [u8; 5],
+ }
+
+ let data: Box<_> = box Baz_ {
+ f1: 42, f2: ['a' as u8, 'b' as u8, 'c' as u8, 'd' as u8, 'e' as u8] };
+ let x: &Baz = mem::transmute(slice::from_raw_parts(&*data, 5));
+ assert_eq!(x.f1, 42);
+ let chs: Vec<char> = x.f2.chars().collect();
+ assert_eq!(chs.len(), 5);
+ assert_eq!(chs[0], 'a');
+ assert_eq!(chs[1], 'b');
+ assert_eq!(chs[2], 'c');
+ assert_eq!(chs[3], 'd');
+ assert_eq!(chs[4], 'e');
+
+ struct Qux_ {
+ f: St
+ }
+
+ let obj: Box<St> = box St { f: 42 };
+ let obj: &Tr = &*obj;
+ let obj: raw::TraitObject = mem::transmute(&*obj);
+ let data: Box<_> = box Qux_{ f: St { f: 234 } };
+ let x: &Qux = mem::transmute(raw::TraitObject { vtable: obj.vtable,
+ data: mem::transmute(&*data) });
+ assert_eq!(x.f.foo(), 234);
+ }
+}
--- /dev/null
+// Test sized-ness checking in substitution within fn bodies..
+
+use std::marker;
+
+// Unbounded.
+fn f1<X: ?Sized>(x: &X) {
+ f2::<X>(x);
+ //~^ ERROR the size for values of type
+}
+fn f2<X>(x: &X) {
+}
+
+// Bounded.
+trait T {
+ fn foo(&self) { }
+}
+fn f3<X: ?Sized + T>(x: &X) {
+ f4::<X>(x);
+ //~^ ERROR the size for values of type
+}
+fn f4<X: T>(x: &X) {
+}
+
+fn f5<Y>(x: &Y) {}
+fn f6<X: ?Sized>(x: &X) {}
+
+// Test with unsized struct.
+struct S<X: ?Sized> {
+ x: X,
+}
+
+fn f8<X: ?Sized>(x1: &S<X>, x2: &S<X>) {
+ f5(x1);
+ //~^ ERROR the size for values of type
+ f6(x2); // ok
+}
+
+// Test some tuples.
+fn f9<X: ?Sized>(x1: Box<S<X>>) {
+ f5(&(*x1, 34));
+ //~^ ERROR the size for values of type
+}
+
+fn f10<X: ?Sized>(x1: Box<S<X>>) {
+ f5(&(32, *x1));
+ //~^ ERROR the size for values of type
+ //~| ERROR the size for values of type
+}
+
+pub fn main() {
+}
--- /dev/null
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized3.rs:7:13
+ |
+LL | fn f1<X: ?Sized>(x: &X) {
+ | - this type parameter needs to be `std::marker::Sized`
+LL | f2::<X>(x);
+ | ^ doesn't have a size known at compile-time
+...
+LL | fn f2<X>(x: &X) {
+ | - required by this bound in `f2`
+ |
+help: consider relaxing the implicit `Sized` restriction
+ |
+LL | fn f2<X: ?Sized>(x: &X) {
+ | ^^^^^^^^
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized3.rs:18:13
+ |
+LL | fn f3<X: ?Sized + T>(x: &X) {
+ | - this type parameter needs to be `std::marker::Sized`
+LL | f4::<X>(x);
+ | ^ doesn't have a size known at compile-time
+...
+LL | fn f4<X: T>(x: &X) {
+ | - required by this bound in `f4`
+ |
+help: consider relaxing the implicit `Sized` restriction
+ |
+LL | fn f4<X: T + ?Sized>(x: &X) {
+ | ^^^^^^^^
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized3.rs:33:8
+ |
+LL | fn f5<Y>(x: &Y) {}
+ | - required by this bound in `f5`
+...
+LL | fn f8<X: ?Sized>(x1: &S<X>, x2: &S<X>) {
+ | - this type parameter needs to be `std::marker::Sized`
+LL | f5(x1);
+ | ^^ doesn't have a size known at compile-time
+ |
+note: required because it appears within the type `S<X>`
+ --> $DIR/unsized3.rs:28:8
+ |
+LL | struct S<X: ?Sized> {
+ | ^
+help: consider relaxing the implicit `Sized` restriction
+ |
+LL | fn f5<Y: ?Sized>(x: &Y) {}
+ | ^^^^^^^^
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized3.rs:40:8
+ |
+LL | fn f9<X: ?Sized>(x1: Box<S<X>>) {
+ | - this type parameter needs to be `std::marker::Sized`
+LL | f5(&(*x1, 34));
+ | ^^^^^^^^^^ doesn't have a size known at compile-time
+ |
+note: required because it appears within the type `S<X>`
+ --> $DIR/unsized3.rs:28:8
+ |
+LL | struct S<X: ?Sized> {
+ | ^
+ = note: only the last element of a tuple may have a dynamically sized type
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized3.rs:45:9
+ |
+LL | fn f10<X: ?Sized>(x1: Box<S<X>>) {
+ | - this type parameter needs to be `std::marker::Sized`
+LL | f5(&(32, *x1));
+ | ^^^^^^^^^ doesn't have a size known at compile-time
+ |
+note: required because it appears within the type `S<X>`
+ --> $DIR/unsized3.rs:28:8
+ |
+LL | struct S<X: ?Sized> {
+ | ^
+ = note: required because it appears within the type `({integer}, S<X>)`
+ = note: tuples must have a statically known size to be initialized
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized3.rs:45:8
+ |
+LL | fn f5<Y>(x: &Y) {}
+ | - required by this bound in `f5`
+...
+LL | fn f10<X: ?Sized>(x1: Box<S<X>>) {
+ | - this type parameter needs to be `std::marker::Sized`
+LL | f5(&(32, *x1));
+ | ^^^^^^^^^^ doesn't have a size known at compile-time
+ |
+note: required because it appears within the type `S<X>`
+ --> $DIR/unsized3.rs:28:8
+ |
+LL | struct S<X: ?Sized> {
+ | ^
+ = note: required because it appears within the type `({integer}, S<X>)`
+help: consider relaxing the implicit `Sized` restriction
+ |
+LL | fn f5<Y: ?Sized>(x: &Y) {}
+ | ^^^^^^^^
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// Test `?Sized` types not allowed in fields (except the last one).
+
+struct S1<X: ?Sized> {
+ f1: X,
+ //~^ ERROR the size for values of type
+ f2: isize,
+}
+struct S2<X: ?Sized> {
+ f: isize,
+ g: X,
+ //~^ ERROR the size for values of type
+ h: isize,
+}
+struct S3 {
+ f: str,
+ //~^ ERROR the size for values of type
+ g: [usize]
+}
+struct S4 {
+ f: [u8],
+ //~^ ERROR the size for values of type
+ g: usize
+}
+enum E<X: ?Sized> {
+ V1(X, isize),
+ //~^ ERROR the size for values of type
+}
+enum F<X: ?Sized> {
+ V2{f1: X, f: isize},
+ //~^ ERROR the size for values of type
+}
+
+pub fn main() {
+}
--- /dev/null
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized5.rs:4:9
+ |
+LL | struct S1<X: ?Sized> {
+ | - this type parameter needs to be `std::marker::Sized`
+LL | f1: X,
+ | ^ doesn't have a size known at compile-time
+ |
+ = note: only the last field of a struct may have a dynamically sized type
+ = help: change the field's type to have a statically known size
+help: borrowed types always have a statically known size
+ |
+LL | f1: &X,
+ | ^
+help: the `Box` type always has a statically known size and allocates its contents in the heap
+ |
+LL | f1: Box<X>,
+ | ^^^^ ^
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized5.rs:10:8
+ |
+LL | struct S2<X: ?Sized> {
+ | - this type parameter needs to be `std::marker::Sized`
+LL | f: isize,
+LL | g: X,
+ | ^ doesn't have a size known at compile-time
+ |
+ = note: only the last field of a struct may have a dynamically sized type
+ = help: change the field's type to have a statically known size
+help: borrowed types always have a statically known size
+ |
+LL | g: &X,
+ | ^
+help: the `Box` type always has a statically known size and allocates its contents in the heap
+ |
+LL | g: Box<X>,
+ | ^^^^ ^
+
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+ --> $DIR/unsized5.rs:15:8
+ |
+LL | f: str,
+ | ^^^ doesn't have a size known at compile-time
+ |
+ = help: the trait `Sized` is not implemented for `str`
+ = note: only the last field of a struct may have a dynamically sized type
+ = help: change the field's type to have a statically known size
+help: borrowed types always have a statically known size
+ |
+LL | f: &str,
+ | ^
+help: the `Box` type always has a statically known size and allocates its contents in the heap
+ |
+LL | f: Box<str>,
+ | ^^^^ ^
+
+error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
+ --> $DIR/unsized5.rs:20:8
+ |
+LL | f: [u8],
+ | ^^^^ doesn't have a size known at compile-time
+ |
+ = help: the trait `Sized` is not implemented for `[u8]`
+ = note: only the last field of a struct may have a dynamically sized type
+ = help: change the field's type to have a statically known size
+help: borrowed types always have a statically known size
+ |
+LL | f: &[u8],
+ | ^
+help: the `Box` type always has a statically known size and allocates its contents in the heap
+ |
+LL | f: Box<[u8]>,
+ | ^^^^ ^
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized5.rs:25:8
+ |
+LL | enum E<X: ?Sized> {
+ | - this type parameter needs to be `std::marker::Sized`
+LL | V1(X, isize),
+ | ^ doesn't have a size known at compile-time
+ |
+ = note: no field of an enum variant may have a dynamically sized type
+ = help: change the field's type to have a statically known size
+help: borrowed types always have a statically known size
+ |
+LL | V1(&X, isize),
+ | ^
+help: the `Box` type always has a statically known size and allocates its contents in the heap
+ |
+LL | V1(Box<X>, isize),
+ | ^^^^ ^
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized5.rs:29:12
+ |
+LL | enum F<X: ?Sized> {
+ | - this type parameter needs to be `std::marker::Sized`
+LL | V2{f1: X, f: isize},
+ | ^ doesn't have a size known at compile-time
+ |
+ = note: no field of an enum variant may have a dynamically sized type
+ = help: change the field's type to have a statically known size
+help: borrowed types always have a statically known size
+ |
+LL | V2{f1: &X, f: isize},
+ | ^
+help: the `Box` type always has a statically known size and allocates its contents in the heap
+ |
+LL | V2{f1: Box<X>, f: isize},
+ | ^^^^ ^
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// Test `?Sized` local variables.
+
+trait T {}
+
+fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) {
+ let _: W; // <-- this is OK, no bindings created, no initializer.
+ let _: (isize, (X, isize));
+ //~^ ERROR the size for values of type
+ let y: Y;
+ //~^ ERROR the size for values of type
+ let y: (isize, (Z, usize));
+ //~^ ERROR the size for values of type
+}
+fn f2<X: ?Sized, Y: ?Sized>(x: &X) {
+ let y: X;
+ //~^ ERROR the size for values of type
+ let y: (isize, (Y, isize));
+ //~^ ERROR the size for values of type
+}
+
+fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
+ let y: X = *x1;
+ //~^ ERROR the size for values of type
+ let y = *x2;
+ //~^ ERROR the size for values of type
+ let (y, z) = (*x3, 4);
+ //~^ ERROR the size for values of type
+}
+fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
+ let y: X = *x1;
+ //~^ ERROR the size for values of type
+ let y = *x2;
+ //~^ ERROR the size for values of type
+ let (y, z) = (*x3, 4);
+ //~^ ERROR the size for values of type
+}
+
+fn g1<X: ?Sized>(x: X) {}
+//~^ ERROR the size for values of type
+fn g2<X: ?Sized + T>(x: X) {}
+//~^ ERROR the size for values of type
+
+pub fn main() {
+}
--- /dev/null
+error[E0277]: the size for values of type `Y` cannot be known at compilation time
+ --> $DIR/unsized6.rs:9:9
+ |
+LL | fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) {
+ | - this type parameter needs to be `std::marker::Sized`
+...
+LL | let y: Y;
+ | ^ doesn't have a size known at compile-time
+ |
+ = note: all local variables must have a statically known size
+ = help: unsized locals are gated as an unstable feature
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized6.rs:7:12
+ |
+LL | fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) {
+ | - this type parameter needs to be `std::marker::Sized`
+LL | let _: W; // <-- this is OK, no bindings created, no initializer.
+LL | let _: (isize, (X, isize));
+ | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+ |
+ = note: only the last element of a tuple may have a dynamically sized type
+
+error[E0277]: the size for values of type `Z` cannot be known at compilation time
+ --> $DIR/unsized6.rs:11:12
+ |
+LL | fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) {
+ | - this type parameter needs to be `std::marker::Sized`
+...
+LL | let y: (isize, (Z, usize));
+ | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+ |
+ = note: only the last element of a tuple may have a dynamically sized type
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized6.rs:15:9
+ |
+LL | fn f2<X: ?Sized, Y: ?Sized>(x: &X) {
+ | - this type parameter needs to be `std::marker::Sized`
+LL | let y: X;
+ | ^ doesn't have a size known at compile-time
+ |
+ = note: all local variables must have a statically known size
+ = help: unsized locals are gated as an unstable feature
+
+error[E0277]: the size for values of type `Y` cannot be known at compilation time
+ --> $DIR/unsized6.rs:17:12
+ |
+LL | fn f2<X: ?Sized, Y: ?Sized>(x: &X) {
+ | - this type parameter needs to be `std::marker::Sized`
+...
+LL | let y: (isize, (Y, isize));
+ | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+ |
+ = note: only the last element of a tuple may have a dynamically sized type
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized6.rs:22:9
+ |
+LL | fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
+ | - this type parameter needs to be `std::marker::Sized`
+LL | let y: X = *x1;
+ | ^ doesn't have a size known at compile-time
+ |
+ = note: all local variables must have a statically known size
+ = help: unsized locals are gated as an unstable feature
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized6.rs:24:9
+ |
+LL | fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
+ | - this type parameter needs to be `std::marker::Sized`
+...
+LL | let y = *x2;
+ | ^ doesn't have a size known at compile-time
+ |
+ = note: all local variables must have a statically known size
+ = help: unsized locals are gated as an unstable feature
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized6.rs:26:10
+ |
+LL | fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
+ | - this type parameter needs to be `std::marker::Sized`
+...
+LL | let (y, z) = (*x3, 4);
+ | ^ doesn't have a size known at compile-time
+ |
+ = note: all local variables must have a statically known size
+ = help: unsized locals are gated as an unstable feature
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized6.rs:30:9
+ |
+LL | fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
+ | - this type parameter needs to be `std::marker::Sized`
+LL | let y: X = *x1;
+ | ^ doesn't have a size known at compile-time
+ |
+ = note: all local variables must have a statically known size
+ = help: unsized locals are gated as an unstable feature
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized6.rs:32:9
+ |
+LL | fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
+ | - this type parameter needs to be `std::marker::Sized`
+...
+LL | let y = *x2;
+ | ^ doesn't have a size known at compile-time
+ |
+ = note: all local variables must have a statically known size
+ = help: unsized locals are gated as an unstable feature
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized6.rs:34:10
+ |
+LL | fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
+ | - this type parameter needs to be `std::marker::Sized`
+...
+LL | let (y, z) = (*x3, 4);
+ | ^ doesn't have a size known at compile-time
+ |
+ = note: all local variables must have a statically known size
+ = help: unsized locals are gated as an unstable feature
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized6.rs:38:18
+ |
+LL | fn g1<X: ?Sized>(x: X) {}
+ | - ^ doesn't have a size known at compile-time
+ | |
+ | this type parameter needs to be `std::marker::Sized`
+ |
+ = help: unsized fn params are gated as an unstable feature
+help: function arguments must have a statically known size, borrowed types always have a known size
+ |
+LL | fn g1<X: ?Sized>(x: &X) {}
+ | ^
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized6.rs:40:22
+ |
+LL | fn g2<X: ?Sized + T>(x: X) {}
+ | - ^ doesn't have a size known at compile-time
+ | |
+ | this type parameter needs to be `std::marker::Sized`
+ |
+ = help: unsized fn params are gated as an unstable feature
+help: function arguments must have a statically known size, borrowed types always have a known size
+ |
+LL | fn g2<X: ?Sized + T>(x: &X) {}
+ | ^
+
+error: aborting due to 13 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// Test sized-ness checking in substitution in impls.
+
+trait T {}
+
+// I would like these to fail eventually.
+// impl - bounded
+trait T1<Z: T> {
+ fn dummy(&self) -> Z;
+}
+
+struct S3<Y: ?Sized>(Box<Y>);
+impl<X: ?Sized + T> T1<X> for S3<X> {
+ //~^ ERROR the size for values of type
+}
+
+fn main() { }
--- /dev/null
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+ --> $DIR/unsized7.rs:12:21
+ |
+LL | trait T1<Z: T> {
+ | - required by this bound in `T1`
+...
+LL | impl<X: ?Sized + T> T1<X> for S3<X> {
+ | - ^^^^^ doesn't have a size known at compile-time
+ | |
+ | this type parameter needs to be `std::marker::Sized`
+ |
+help: consider relaxing the implicit `Sized` restriction
+ |
+LL | trait T1<Z: T + ?Sized> {
+ | ^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
+++ /dev/null
-// run-pass
-
-#![allow(unconditional_recursion)]
-#![allow(dead_code)]
-#![allow(unused_variables)]
-#![allow(unused_imports)]
-#![feature(box_syntax)]
-
-// Test sized-ness checking in substitution.
-
-use std::marker;
-
-// Unbounded.
-fn f1<X: ?Sized>(x: &X) {
- f1::<X>(x);
-}
-fn f2<X>(x: &X) {
- f1::<X>(x);
- f2::<X>(x);
-}
-
-// Bounded.
-trait T { fn dummy(&self) { } }
-fn f3<X: T+?Sized>(x: &X) {
- f3::<X>(x);
-}
-fn f4<X: T>(x: &X) {
- f3::<X>(x);
- f4::<X>(x);
-}
-
-// Self type.
-trait T2 {
- fn f() -> Box<Self>;
-}
-struct S;
-impl T2 for S {
- fn f() -> Box<S> {
- box S
- }
-}
-fn f5<X: ?Sized+T2>(x: &X) {
- let _: Box<X> = T2::f();
-}
-fn f6<X: T2>(x: &X) {
- let _: Box<X> = T2::f();
-}
-
-trait T3 {
- fn f() -> Box<Self>;
-}
-impl T3 for S {
- fn f() -> Box<S> {
- box S
- }
-}
-fn f7<X: ?Sized+T3>(x: &X) {
- // This is valid, but the unsized bound on X is irrelevant because any type
- // which implements T3 must have statically known size.
- let _: Box<X> = T3::f();
-}
-
-trait T4<X> {
- fn dummy(&self) { }
- fn m1(&self, x: &dyn T4<X>, y: X);
- fn m2(&self, x: &dyn T5<X>, y: X);
-}
-trait T5<X: ?Sized> {
- fn dummy(&self) { }
- // not an error (for now)
- fn m1(&self, x: &dyn T4<X>);
- fn m2(&self, x: &dyn T5<X>);
-}
-
-trait T6<X: T> {
- fn dummy(&self) { }
- fn m1(&self, x: &dyn T4<X>);
- fn m2(&self, x: &dyn T5<X>);
-}
-trait T7<X: ?Sized+T> {
- fn dummy(&self) { }
- // not an error (for now)
- fn m1(&self, x: &dyn T4<X>);
- fn m2(&self, x: &dyn T5<X>);
-}
-
-// The last field in a struct may be unsized
-struct S2<X: ?Sized> {
- f: X,
-}
-struct S3<X: ?Sized> {
- f1: isize,
- f2: X,
-}
-
-pub fn main() {
-}
+++ /dev/null
-// run-pass
-// Test structs with always-unsized fields.
-
-
-#![allow(warnings)]
-#![feature(box_syntax, unsize, raw)]
-
-use std::mem;
-use std::raw;
-use std::slice;
-
-struct Foo<T> {
- f: [T],
-}
-
-struct Bar {
- f1: usize,
- f2: [usize],
-}
-
-struct Baz {
- f1: usize,
- f2: str,
-}
-
-trait Tr {
- fn foo(&self) -> usize;
-}
-
-struct St {
- f: usize
-}
-
-impl Tr for St {
- fn foo(&self) -> usize {
- self.f
- }
-}
-
-struct Qux<'a> {
- f: Tr+'a
-}
-
-pub fn main() {
- let _: &Foo<f64>;
- let _: &Bar;
- let _: &Baz;
-
- let _: Box<Foo<i32>>;
- let _: Box<Bar>;
- let _: Box<Baz>;
-
- let _ = mem::size_of::<Box<Foo<u8>>>();
- let _ = mem::size_of::<Box<Bar>>();
- let _ = mem::size_of::<Box<Baz>>();
-
- unsafe {
- struct Foo_<T> {
- f: [T; 3]
- }
-
- let data: Box<Foo_<i32>> = box Foo_{f: [1, 2, 3] };
- let x: &Foo<i32> = mem::transmute(slice::from_raw_parts(&*data, 3));
- assert_eq!(x.f.len(), 3);
- assert_eq!(x.f[0], 1);
-
- struct Baz_ {
- f1: usize,
- f2: [u8; 5],
- }
-
- let data: Box<_> = box Baz_ {
- f1: 42, f2: ['a' as u8, 'b' as u8, 'c' as u8, 'd' as u8, 'e' as u8] };
- let x: &Baz = mem::transmute(slice::from_raw_parts(&*data, 5));
- assert_eq!(x.f1, 42);
- let chs: Vec<char> = x.f2.chars().collect();
- assert_eq!(chs.len(), 5);
- assert_eq!(chs[0], 'a');
- assert_eq!(chs[1], 'b');
- assert_eq!(chs[2], 'c');
- assert_eq!(chs[3], 'd');
- assert_eq!(chs[4], 'e');
-
- struct Qux_ {
- f: St
- }
-
- let obj: Box<St> = box St { f: 42 };
- let obj: &Tr = &*obj;
- let obj: raw::TraitObject = mem::transmute(&*obj);
- let data: Box<_> = box Qux_{ f: St { f: 234 } };
- let x: &Qux = mem::transmute(raw::TraitObject { vtable: obj.vtable,
- data: mem::transmute(&*data) });
- assert_eq!(x.f.foo(), 234);
- }
-}
+++ /dev/null
-// Test sized-ness checking in substitution within fn bodies..
-
-use std::marker;
-
-// Unbounded.
-fn f1<X: ?Sized>(x: &X) {
- f2::<X>(x);
- //~^ ERROR the size for values of type
-}
-fn f2<X>(x: &X) {
-}
-
-// Bounded.
-trait T {
- fn foo(&self) { }
-}
-fn f3<X: ?Sized + T>(x: &X) {
- f4::<X>(x);
- //~^ ERROR the size for values of type
-}
-fn f4<X: T>(x: &X) {
-}
-
-fn f5<Y>(x: &Y) {}
-fn f6<X: ?Sized>(x: &X) {}
-
-// Test with unsized struct.
-struct S<X: ?Sized> {
- x: X,
-}
-
-fn f8<X: ?Sized>(x1: &S<X>, x2: &S<X>) {
- f5(x1);
- //~^ ERROR the size for values of type
- f6(x2); // ok
-}
-
-// Test some tuples.
-fn f9<X: ?Sized>(x1: Box<S<X>>) {
- f5(&(*x1, 34));
- //~^ ERROR the size for values of type
-}
-
-fn f10<X: ?Sized>(x1: Box<S<X>>) {
- f5(&(32, *x1));
- //~^ ERROR the size for values of type
- //~| ERROR the size for values of type
-}
-
-pub fn main() {
-}
+++ /dev/null
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized3.rs:7:13
- |
-LL | fn f1<X: ?Sized>(x: &X) {
- | - this type parameter needs to be `std::marker::Sized`
-LL | f2::<X>(x);
- | ^ doesn't have a size known at compile-time
-...
-LL | fn f2<X>(x: &X) {
- | - required by this bound in `f2`
- |
-help: consider relaxing the implicit `Sized` restriction
- |
-LL | fn f2<X: ?Sized>(x: &X) {
- | ^^^^^^^^
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized3.rs:18:13
- |
-LL | fn f3<X: ?Sized + T>(x: &X) {
- | - this type parameter needs to be `std::marker::Sized`
-LL | f4::<X>(x);
- | ^ doesn't have a size known at compile-time
-...
-LL | fn f4<X: T>(x: &X) {
- | - required by this bound in `f4`
- |
-help: consider relaxing the implicit `Sized` restriction
- |
-LL | fn f4<X: T + ?Sized>(x: &X) {
- | ^^^^^^^^
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized3.rs:33:8
- |
-LL | fn f5<Y>(x: &Y) {}
- | - required by this bound in `f5`
-...
-LL | fn f8<X: ?Sized>(x1: &S<X>, x2: &S<X>) {
- | - this type parameter needs to be `std::marker::Sized`
-LL | f5(x1);
- | ^^ doesn't have a size known at compile-time
- |
-note: required because it appears within the type `S<X>`
- --> $DIR/unsized3.rs:28:8
- |
-LL | struct S<X: ?Sized> {
- | ^
-help: consider relaxing the implicit `Sized` restriction
- |
-LL | fn f5<Y: ?Sized>(x: &Y) {}
- | ^^^^^^^^
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized3.rs:40:8
- |
-LL | fn f9<X: ?Sized>(x1: Box<S<X>>) {
- | - this type parameter needs to be `std::marker::Sized`
-LL | f5(&(*x1, 34));
- | ^^^^^^^^^^ doesn't have a size known at compile-time
- |
-note: required because it appears within the type `S<X>`
- --> $DIR/unsized3.rs:28:8
- |
-LL | struct S<X: ?Sized> {
- | ^
- = note: only the last element of a tuple may have a dynamically sized type
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized3.rs:45:9
- |
-LL | fn f10<X: ?Sized>(x1: Box<S<X>>) {
- | - this type parameter needs to be `std::marker::Sized`
-LL | f5(&(32, *x1));
- | ^^^^^^^^^ doesn't have a size known at compile-time
- |
-note: required because it appears within the type `S<X>`
- --> $DIR/unsized3.rs:28:8
- |
-LL | struct S<X: ?Sized> {
- | ^
- = note: required because it appears within the type `({integer}, S<X>)`
- = note: tuples must have a statically known size to be initialized
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized3.rs:45:8
- |
-LL | fn f5<Y>(x: &Y) {}
- | - required by this bound in `f5`
-...
-LL | fn f10<X: ?Sized>(x1: Box<S<X>>) {
- | - this type parameter needs to be `std::marker::Sized`
-LL | f5(&(32, *x1));
- | ^^^^^^^^^^ doesn't have a size known at compile-time
- |
-note: required because it appears within the type `S<X>`
- --> $DIR/unsized3.rs:28:8
- |
-LL | struct S<X: ?Sized> {
- | ^
- = note: required because it appears within the type `({integer}, S<X>)`
-help: consider relaxing the implicit `Sized` restriction
- |
-LL | fn f5<Y: ?Sized>(x: &Y) {}
- | ^^^^^^^^
-
-error: aborting due to 6 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
+++ /dev/null
-// Test `?Sized` types not allowed in fields (except the last one).
-
-struct S1<X: ?Sized> {
- f1: X,
- //~^ ERROR the size for values of type
- f2: isize,
-}
-struct S2<X: ?Sized> {
- f: isize,
- g: X,
- //~^ ERROR the size for values of type
- h: isize,
-}
-struct S3 {
- f: str,
- //~^ ERROR the size for values of type
- g: [usize]
-}
-struct S4 {
- f: [u8],
- //~^ ERROR the size for values of type
- g: usize
-}
-enum E<X: ?Sized> {
- V1(X, isize),
- //~^ ERROR the size for values of type
-}
-enum F<X: ?Sized> {
- V2{f1: X, f: isize},
- //~^ ERROR the size for values of type
-}
-
-pub fn main() {
-}
+++ /dev/null
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized5.rs:4:9
- |
-LL | struct S1<X: ?Sized> {
- | - this type parameter needs to be `std::marker::Sized`
-LL | f1: X,
- | ^ doesn't have a size known at compile-time
- |
- = note: only the last field of a struct may have a dynamically sized type
- = help: change the field's type to have a statically known size
-help: borrowed types always have a statically known size
- |
-LL | f1: &X,
- | ^
-help: the `Box` type always has a statically known size and allocates its contents in the heap
- |
-LL | f1: Box<X>,
- | ^^^^ ^
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized5.rs:10:8
- |
-LL | struct S2<X: ?Sized> {
- | - this type parameter needs to be `std::marker::Sized`
-LL | f: isize,
-LL | g: X,
- | ^ doesn't have a size known at compile-time
- |
- = note: only the last field of a struct may have a dynamically sized type
- = help: change the field's type to have a statically known size
-help: borrowed types always have a statically known size
- |
-LL | g: &X,
- | ^
-help: the `Box` type always has a statically known size and allocates its contents in the heap
- |
-LL | g: Box<X>,
- | ^^^^ ^
-
-error[E0277]: the size for values of type `str` cannot be known at compilation time
- --> $DIR/unsized5.rs:15:8
- |
-LL | f: str,
- | ^^^ doesn't have a size known at compile-time
- |
- = help: the trait `Sized` is not implemented for `str`
- = note: only the last field of a struct may have a dynamically sized type
- = help: change the field's type to have a statically known size
-help: borrowed types always have a statically known size
- |
-LL | f: &str,
- | ^
-help: the `Box` type always has a statically known size and allocates its contents in the heap
- |
-LL | f: Box<str>,
- | ^^^^ ^
-
-error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
- --> $DIR/unsized5.rs:20:8
- |
-LL | f: [u8],
- | ^^^^ doesn't have a size known at compile-time
- |
- = help: the trait `Sized` is not implemented for `[u8]`
- = note: only the last field of a struct may have a dynamically sized type
- = help: change the field's type to have a statically known size
-help: borrowed types always have a statically known size
- |
-LL | f: &[u8],
- | ^
-help: the `Box` type always has a statically known size and allocates its contents in the heap
- |
-LL | f: Box<[u8]>,
- | ^^^^ ^
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized5.rs:25:8
- |
-LL | enum E<X: ?Sized> {
- | - this type parameter needs to be `std::marker::Sized`
-LL | V1(X, isize),
- | ^ doesn't have a size known at compile-time
- |
- = note: no field of an enum variant may have a dynamically sized type
- = help: change the field's type to have a statically known size
-help: borrowed types always have a statically known size
- |
-LL | V1(&X, isize),
- | ^
-help: the `Box` type always has a statically known size and allocates its contents in the heap
- |
-LL | V1(Box<X>, isize),
- | ^^^^ ^
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized5.rs:29:12
- |
-LL | enum F<X: ?Sized> {
- | - this type parameter needs to be `std::marker::Sized`
-LL | V2{f1: X, f: isize},
- | ^ doesn't have a size known at compile-time
- |
- = note: no field of an enum variant may have a dynamically sized type
- = help: change the field's type to have a statically known size
-help: borrowed types always have a statically known size
- |
-LL | V2{f1: &X, f: isize},
- | ^
-help: the `Box` type always has a statically known size and allocates its contents in the heap
- |
-LL | V2{f1: Box<X>, f: isize},
- | ^^^^ ^
-
-error: aborting due to 6 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
+++ /dev/null
-// Test `?Sized` local variables.
-
-trait T {}
-
-fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) {
- let _: W; // <-- this is OK, no bindings created, no initializer.
- let _: (isize, (X, isize));
- //~^ ERROR the size for values of type
- let y: Y;
- //~^ ERROR the size for values of type
- let y: (isize, (Z, usize));
- //~^ ERROR the size for values of type
-}
-fn f2<X: ?Sized, Y: ?Sized>(x: &X) {
- let y: X;
- //~^ ERROR the size for values of type
- let y: (isize, (Y, isize));
- //~^ ERROR the size for values of type
-}
-
-fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
- let y: X = *x1;
- //~^ ERROR the size for values of type
- let y = *x2;
- //~^ ERROR the size for values of type
- let (y, z) = (*x3, 4);
- //~^ ERROR the size for values of type
-}
-fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
- let y: X = *x1;
- //~^ ERROR the size for values of type
- let y = *x2;
- //~^ ERROR the size for values of type
- let (y, z) = (*x3, 4);
- //~^ ERROR the size for values of type
-}
-
-fn g1<X: ?Sized>(x: X) {}
-//~^ ERROR the size for values of type
-fn g2<X: ?Sized + T>(x: X) {}
-//~^ ERROR the size for values of type
-
-pub fn main() {
-}
+++ /dev/null
-error[E0277]: the size for values of type `Y` cannot be known at compilation time
- --> $DIR/unsized6.rs:9:9
- |
-LL | fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) {
- | - this type parameter needs to be `std::marker::Sized`
-...
-LL | let y: Y;
- | ^ doesn't have a size known at compile-time
- |
- = note: all local variables must have a statically known size
- = help: unsized locals are gated as an unstable feature
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized6.rs:7:12
- |
-LL | fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) {
- | - this type parameter needs to be `std::marker::Sized`
-LL | let _: W; // <-- this is OK, no bindings created, no initializer.
-LL | let _: (isize, (X, isize));
- | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
- |
- = note: only the last element of a tuple may have a dynamically sized type
-
-error[E0277]: the size for values of type `Z` cannot be known at compilation time
- --> $DIR/unsized6.rs:11:12
- |
-LL | fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) {
- | - this type parameter needs to be `std::marker::Sized`
-...
-LL | let y: (isize, (Z, usize));
- | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
- |
- = note: only the last element of a tuple may have a dynamically sized type
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized6.rs:15:9
- |
-LL | fn f2<X: ?Sized, Y: ?Sized>(x: &X) {
- | - this type parameter needs to be `std::marker::Sized`
-LL | let y: X;
- | ^ doesn't have a size known at compile-time
- |
- = note: all local variables must have a statically known size
- = help: unsized locals are gated as an unstable feature
-
-error[E0277]: the size for values of type `Y` cannot be known at compilation time
- --> $DIR/unsized6.rs:17:12
- |
-LL | fn f2<X: ?Sized, Y: ?Sized>(x: &X) {
- | - this type parameter needs to be `std::marker::Sized`
-...
-LL | let y: (isize, (Y, isize));
- | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
- |
- = note: only the last element of a tuple may have a dynamically sized type
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized6.rs:22:9
- |
-LL | fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
- | - this type parameter needs to be `std::marker::Sized`
-LL | let y: X = *x1;
- | ^ doesn't have a size known at compile-time
- |
- = note: all local variables must have a statically known size
- = help: unsized locals are gated as an unstable feature
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized6.rs:24:9
- |
-LL | fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
- | - this type parameter needs to be `std::marker::Sized`
-...
-LL | let y = *x2;
- | ^ doesn't have a size known at compile-time
- |
- = note: all local variables must have a statically known size
- = help: unsized locals are gated as an unstable feature
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized6.rs:26:10
- |
-LL | fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
- | - this type parameter needs to be `std::marker::Sized`
-...
-LL | let (y, z) = (*x3, 4);
- | ^ doesn't have a size known at compile-time
- |
- = note: all local variables must have a statically known size
- = help: unsized locals are gated as an unstable feature
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized6.rs:30:9
- |
-LL | fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
- | - this type parameter needs to be `std::marker::Sized`
-LL | let y: X = *x1;
- | ^ doesn't have a size known at compile-time
- |
- = note: all local variables must have a statically known size
- = help: unsized locals are gated as an unstable feature
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized6.rs:32:9
- |
-LL | fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
- | - this type parameter needs to be `std::marker::Sized`
-...
-LL | let y = *x2;
- | ^ doesn't have a size known at compile-time
- |
- = note: all local variables must have a statically known size
- = help: unsized locals are gated as an unstable feature
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized6.rs:34:10
- |
-LL | fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
- | - this type parameter needs to be `std::marker::Sized`
-...
-LL | let (y, z) = (*x3, 4);
- | ^ doesn't have a size known at compile-time
- |
- = note: all local variables must have a statically known size
- = help: unsized locals are gated as an unstable feature
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized6.rs:38:18
- |
-LL | fn g1<X: ?Sized>(x: X) {}
- | - ^ doesn't have a size known at compile-time
- | |
- | this type parameter needs to be `std::marker::Sized`
- |
- = help: unsized fn params are gated as an unstable feature
-help: function arguments must have a statically known size, borrowed types always have a known size
- |
-LL | fn g1<X: ?Sized>(&x: X) {}
- | ^
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized6.rs:40:22
- |
-LL | fn g2<X: ?Sized + T>(x: X) {}
- | - ^ doesn't have a size known at compile-time
- | |
- | this type parameter needs to be `std::marker::Sized`
- |
- = help: unsized fn params are gated as an unstable feature
-help: function arguments must have a statically known size, borrowed types always have a known size
- |
-LL | fn g2<X: ?Sized + T>(&x: X) {}
- | ^
-
-error: aborting due to 13 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
+++ /dev/null
-// Test sized-ness checking in substitution in impls.
-
-trait T {}
-
-// I would like these to fail eventually.
-// impl - bounded
-trait T1<Z: T> {
- fn dummy(&self) -> Z;
-}
-
-struct S3<Y: ?Sized>(Box<Y>);
-impl<X: ?Sized + T> T1<X> for S3<X> {
- //~^ ERROR the size for values of type
-}
-
-fn main() { }
+++ /dev/null
-error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized7.rs:12:21
- |
-LL | trait T1<Z: T> {
- | - required by this bound in `T1`
-...
-LL | impl<X: ?Sized + T> T1<X> for S3<X> {
- | - ^^^^^ doesn't have a size known at compile-time
- | |
- | this type parameter needs to be `std::marker::Sized`
- |
-help: consider relaxing the implicit `Sized` restriction
- |
-LL | trait T1<Z: T + ?Sized> {
- | ^^^^^^^^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0277`.
//
#![allow(non_snake_case)]
-#![feature(non_ascii_idents)]
-
pub fn main() {
let ε = 0.00001f64;
let Π = 3.14f64;
-#![allow(mixed_script_confusables)]
+// check-pass
+//
+#![allow(mixed_script_confusables, non_camel_case_types)]
fn foo<
- 'β, //~ ERROR non-ascii idents are not fully supported
- γ //~ ERROR non-ascii idents are not fully supported
- //~^ WARN type parameter `γ` should have an upper camel case name
+ 'β,
+ γ
>() {}
struct X {
- δ: usize //~ ERROR non-ascii idents are not fully supported
+ δ: usize
}
pub fn main() {
- let α = 0.00001f64; //~ ERROR non-ascii idents are not fully supported
+ let α = 0.00001f64;
}
+++ /dev/null
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/utf8_idents.rs:4:5
- |
-LL | 'β,
- | ^^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/utf8_idents.rs:5:5
- |
-LL | γ
- | ^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/utf8_idents.rs:10:5
- |
-LL | δ: usize
- | ^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
- --> $DIR/utf8_idents.rs:14:9
- |
-LL | let α = 0.00001f64;
- | ^
- |
- = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
- = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-warning: type parameter `γ` should have an upper camel case name
- --> $DIR/utf8_idents.rs:5:5
- |
-LL | γ
- | ^ help: convert the identifier to upper camel case: `Γ`
- |
- = note: `#[warn(non_camel_case_types)]` on by default
-
-error: aborting due to 4 previous errors; 1 warning emitted
-
-For more information about this error, try `rustc --explain E0658`.
-Subproject commit 65d57e6f384c2317f76626eac116f683e2b63665
+Subproject commit 4369396ce7d270972955d876eaa4954bea56bcd9
We're collecting our changelog from pull request descriptions.
If your PR only includes internal changes, you can just write
`changelog: none`. Otherwise, please write a short comment
-explaining your change.
+explaining your change. Also, it's helpful for us that
+the lint name is put into brackets `[]` and backticks `` ` ` ``,
+e.g. ``[`lint_name`]``.
If your PR fixes an issue, you can add "fixes #issue_number" into this
PR description. This way the issue will be automatically closed when
---
*Please write a short comment explaining your change (or "none" for internal only changes)*
+
changelog:
run: |
MESSAGE=$(git log --format=%B -n 1)
PR=$(echo "$MESSAGE" | grep -o "#[0-9]*" | head -1 | sed -e 's/^#//')
- output=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \
- python -c "import sys, json; print(json.load(sys.stdin)['body'])" | \
- grep "^changelog: " | \
- sed "s/changelog: //g")
- if [[ -z "$output" ]]; then
+ body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \
+ python -c "import sys, json; print(json.load(sys.stdin)['body'])")
+ output=$(grep "^changelog:\s*\S" <<< "$body" | sed "s/changelog:\s*//g") || {
echo "ERROR: PR body must contain 'changelog: ...'"
exit 1
- elif [[ "$output" = "none" ]]; then
+ }
+ if [[ "$output" = "none" ]]; then
echo "WARNING: changelog is 'none'"
+ else
+ echo "changelog: $output"
fi
env:
PYTHONIOENCODING: 'utf-8'
* Replace [`find_map`] with [`manual_find_map`]
[#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
-* [`unknown_clippy_lints`] Now integrated in the `unknown_lints` rustc lint
+* `unknown_clippy_lints` Now integrated in the `unknown_lints` rustc lint
[#6653](https://github.com/rust-lang/rust-clippy/pull/6653)
### Enhancements
* Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated
as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333)
-* Deprecate [`panic_params`] lint. This is now available in rustc as `panic_fmt`
+* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panic`
[#6351](https://github.com/rust-lang/rust-clippy/pull/6351)
* Move [`map_err_ignore`] to `restriction`
[#6416](https://github.com/rust-lang/rust-clippy/pull/6416)
[#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
* Rename `zero_width_space` to [`invisible_characters`]
[#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
-* Deprecate [`drop_bounds`] (uplifted)
+* Deprecate `drop_bounds` (uplifted)
[#6111](https://github.com/rust-lang/rust-clippy/pull/6111)
* Move [`string_lit_as_bytes`] to `nursery`
[#6117](https://github.com/rust-lang/rust-clippy/pull/6117)
[#5015](https://github.com/rust-lang/rust-clippy/pull/5015)
* Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057)
* Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
-* Deprecate [`unused_label`] [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
+* Deprecate `unused_label` [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
### Enhancements
* [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934)
* [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935)
* [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956)
-* [`unknown_clippy_lints`] [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
+* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
* [`if_let_some_result`] [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
[Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for
details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714)
* Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863)
-* Deprecate [`into_iter_on_array`] [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
+* Deprecate `into_iter_on_array` [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
* Expand [`string_lit_as_bytes`] to also trigger when literal has escapes
[#4808](https://github.com/rust-lang/rust-clippy/pull/4808)
* Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842)
[1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e)
-* New lint: [`drop_bounds`] to detect `T: Drop` bounds
+* New lint: `drop_bounds` to detect `T: Drop` bounds
* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
* Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code
* Move [`get_unwrap`] to the restriction category
* New lints: [`slow_vector_initialization`], [`mem_discriminant_non_enum`],
[`redundant_clone`], [`wildcard_dependencies`],
- [`into_iter_on_ref`], [`into_iter_on_array`], [`deprecated_cfg_attr`],
+ [`into_iter_on_ref`], `into_iter_on_array`, [`deprecated_cfg_attr`],
[`mem_discriminant_non_enum`], [`cargo_common_metadata`]
* Add support for `u128` and `i128` to integer related lints
* Add float support to `mistyped_literal_suffixes`
## 0.0.166
* Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)*
-* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], [`invalid_ref`], [`option_map_or_none`],
+* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], `invalid_ref`, [`option_map_or_none`],
[`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`],
[`transmute_int_to_float`]
## 0.0.64 — 2016-04-26
* Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)*
-* New lints: [`temporary_cstring_as_ptr`], [`unsafe_removed_from_name`], and [`mem_forget`]
+* New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`]
## 0.0.63 — 2016-04-08
* Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)*
## 0.0.49 — 2016-03-09
* Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)*
-* New lints: [`overflow_check_conditional`], [`unused_label`], [`new_without_default`]
+* New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`]
## 0.0.48 — 2016-03-07
* Fixed: ICE in [`needless_range_loop`] with globals
[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
+[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
[`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
[`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
[`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
+[`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied
[`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
[`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
[`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
-[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds
[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
[`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map
[`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity
+[`flat_map_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_option
[`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
[`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
[`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
-[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array
[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
[`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering
-[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
+[`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage
[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
-[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
-[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
[`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
[`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg
[`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp
[`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord
-[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints
[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
+[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
[`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
-[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
- [IntelliJ Rust](#intellij-rust)
- [Rust Analyzer](#rust-analyzer)
- [How Clippy works](#how-clippy-works)
- - [Syncing changes between Clippy and [`rust-lang/rust`]](#syncing-changes-between-clippy-and-rust-langrust)
+ - [Syncing changes between Clippy and `rust-lang/rust`](#syncing-changes-between-clippy-and-rust-langrust)
- [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos)
- - [Performing the sync from [`rust-lang/rust`] to Clippy](#performing-the-sync-from-rust-langrust-to-clippy)
- - [Performing the sync from Clippy to [`rust-lang/rust`]](#performing-the-sync-from-clippy-to-rust-langrust)
+ - [Performing the sync from `rust-lang/rust` to Clippy](#performing-the-sync-from-rust-langrust-to-clippy)
+ - [Performing the sync from Clippy to `rust-lang/rust`](#performing-the-sync-from-clippy-to-rust-langrust)
- [Defining remotes](#defining-remotes)
- [Issue and PR triage](#issue-and-pr-triage)
- [Bors and Homu](#bors-and-homu)
_ => &block.expr,
};
// function call
- if let Some(args) = match_panic_call(cx, begin_panic_call);
- if args.len() == 1;
+ if let Some(arg) = match_panic_call(cx, begin_panic_call);
// bind the second argument of the `assert!` macro if it exists
- if let panic_message = snippet_opt(cx, args[0].span);
+ if let panic_message = snippet_opt(cx, arg.span);
// second argument of begin_panic is irrelevant
// as is the second match arm
then {
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::{ast_utils, is_direct_expn_of};
+use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// **What it does:** This lint warns about boolean comparisons in assert-like macros.
+ ///
+ /// **Why is this bad?** It is shorter to use the equivalent.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// // Bad
+ /// assert_eq!("a".is_empty(), false);
+ /// assert_ne!("a".is_empty(), true);
+ ///
+ /// // Good
+ /// assert!(!"a".is_empty());
+ /// ```
+ pub BOOL_ASSERT_COMPARISON,
+ style,
+ "Using a boolean as comparison value in an assert_* macro when there is no need"
+}
+
+declare_lint_pass!(BoolAssertComparison => [BOOL_ASSERT_COMPARISON]);
+
+fn is_bool_lit(e: &Expr) -> bool {
+ matches!(
+ e.kind,
+ ExprKind::Lit(Lit {
+ kind: LitKind::Bool(_),
+ ..
+ })
+ ) && !e.span.from_expansion()
+}
+
+impl EarlyLintPass for BoolAssertComparison {
+ fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
+ let macros = ["assert_eq", "debug_assert_eq"];
+ let inverted_macros = ["assert_ne", "debug_assert_ne"];
+
+ for mac in macros.iter().chain(inverted_macros.iter()) {
+ if let Some(span) = is_direct_expn_of(e.span, mac) {
+ if let Some([a, b]) = ast_utils::extract_assert_macro_args(e) {
+ let nb_bool_args = is_bool_lit(a) as usize + is_bool_lit(b) as usize;
+
+ if nb_bool_args != 1 {
+ // If there are two boolean arguments, we definitely don't understand
+ // what's going on, so better leave things as is...
+ //
+ // Or there is simply no boolean and then we can leave things as is!
+ return;
+ }
+
+ let non_eq_mac = &mac[..mac.len() - 3];
+ span_lint_and_sugg(
+ cx,
+ BOOL_ASSERT_COMPARISON,
+ span,
+ &format!("used `{}!` with a literal bool", mac),
+ "replace it with",
+ format!("{}!(..)", non_eq_mac),
+ Applicability::MaybeIncorrect,
+ );
+ return;
+ }
+ }
+ }
+ }
+}
}
METHODS_WITH_NEGATION
.iter()
- .cloned()
+ .copied()
.flat_map(|(a, b)| vec![(a, b), (b, a)])
.find(|&(a, _)| {
let path: &str = &path.ident.name.as_str();
use std::borrow::Cow;
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::meets_msrv;
use clippy_utils::sugg::Sugg;
+use clippy_utils::{meets_msrv, msrvs};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Mutability, TyKind};
use super::PTR_AS_PTR;
-const PTR_AS_PTR_MSRV: RustcVersion = RustcVersion::new(1, 38, 0);
-
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: &Option<RustcVersion>) {
- if !meets_msrv(msrv.as_ref(), &PTR_AS_PTR_MSRV) {
+ if !meets_msrv(msrv.as_ref(), &msrvs::POINTER_CAST) {
return;
}
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{meets_msrv, SpanlessEq};
+use clippy_utils::{meets_msrv, msrvs, SpanlessEq};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0);
-
declare_clippy_lint! {
/// **What it does:** Checks for explicit bounds checking when casting.
///
impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
- if !meets_msrv(self.msrv.as_ref(), &CHECKED_CONVERSIONS_MSRV) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::TRY_FROM) {
return;
}
if let [int] = &*tp.segments;
then {
let name = &int.ident.name.as_str();
- candidates.iter().find(|c| name == *c).cloned()
+ candidates.iter().find(|c| name == *c).copied()
} else {
None
}
if let [ty] = &*path.segments;
then {
let name = &ty.ident.name.as_str();
- INTS.iter().find(|c| name == *c).cloned()
+ INTS.iter().find(|c| name == *c).copied()
} else {
None
}
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::visitors::LocalUsedVisitor;
-use clippy_utils::{path_to_local, SpanlessEq};
+use clippy_utils::{is_lang_ctor, path_to_local, SpanlessEq};
use if_chain::if_chain;
-use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
-use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp};
+use rustc_hir::LangItem::OptionNone;
+use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{DefIdTree, TyCtxt, TypeckResults};
+use rustc_middle::ty::TypeckResults;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{MultiSpan, Span};
impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
if let ExprKind::Match(_expr, arms, _source) = expr.kind {
- if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(arm, cx.tcx)) {
+ if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
for arm in arms {
check_arm(arm, wild_arm, cx);
}
// match <local> { .. }
if let Some(binding_id) = path_to_local(strip_ref_operators(expr_in, cx.typeck_results()));
// one of the branches must be "wild-like"
- if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx));
+ if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(cx, arm_inner));
let (wild_inner_arm, non_wild_inner_arm) =
(&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]);
if !pat_contains_or(non_wild_inner_arm.pat);
/// A "wild-like" pattern is wild ("_") or `None`.
/// For this lint to apply, both the outer and inner match expressions
/// must have "wild-like" branches that can be combined.
-fn arm_is_wild_like(arm: &Arm<'_>, tcx: TyCtxt<'_>) -> bool {
+fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
if arm.guard.is_some() {
return false;
}
match arm.pat.kind {
PatKind::Binding(..) | PatKind::Wild => true,
- PatKind::Path(QPath::Resolved(None, path)) if is_none_ctor(path.res, tcx) => true,
+ PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
_ => false,
}
}
result
}
-fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool {
- if let Some(none_id) = tcx.lang_items().option_none_variant() {
- if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = res {
- if let Some(variant_id) = tcx.parent(id) {
- return variant_id == none_id;
- }
- }
- }
- false
-}
-
/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
fn strip_ref_operators<'hir>(mut expr: &'hir Expr<'hir>, typeck_results: &TypeckResults<'_>) -> &'hir Expr<'hir> {
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::implements_trait;
-use clippy_utils::{get_trait_def_id, if_sequence, parent_node_is_if_expr, paths, SpanlessEq};
+use clippy_utils::{get_trait_def_id, if_sequence, is_else_clause, paths, SpanlessEq};
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
}
// We only care about the top-most `if` in the chain
- if parent_node_is_if_expr(expr, cx) {
+ if is_else_clause(cx.tcx, expr) {
return;
}
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
use clippy_utils::{
- both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, parent_node_is_if_expr,
+ both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, is_else_clause,
run_lints, search_same, ContainsName, SpanlessEq, SpanlessHash,
};
use if_chain::if_chain;
expr: &'tcx Expr<'_>,
) {
// We only lint ifs with multiple blocks
- if blocks.len() < 2 || parent_node_is_if_expr(expr, cx) {
+ if blocks.len() < 2 || is_else_clause(cx.tcx, expr) {
return;
}
// Check if each block has shared code
let has_expr = blocks[0].expr.is_some();
- let (start_eq, mut end_eq, expr_eq) = scan_block_for_eq(cx, blocks);
+
+ let (start_eq, mut end_eq, expr_eq) = if let Some(block_eq) = scan_block_for_eq(cx, blocks) {
+ (block_eq.start_eq, block_eq.end_eq, block_eq.expr_eq)
+ } else {
+ return;
+ };
// BRANCHES_SHARING_CODE prerequisites
if has_conditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) {
}
}
-fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, usize, bool) {
+struct BlockEqual {
+ /// The amount statements that are equal from the start
+ start_eq: usize,
+ /// The amount statements that are equal from the end
+ end_eq: usize,
+ /// An indication if the block expressions are the same. This will also be true if both are
+ /// `None`
+ expr_eq: bool,
+}
+
+/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to
+/// abort any further processing and avoid duplicate lint triggers.
+fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<BlockEqual> {
let mut start_eq = usize::MAX;
let mut end_eq = usize::MAX;
let mut expr_eq = true;
"same as this",
);
- return (0, 0, false);
+ return None;
}
}
end_eq = min_block_size - start_eq;
}
- (start_eq, end_eq, expr_eq)
+ Some(BlockEqual {
+ start_eq,
+ end_eq,
+ expr_eq,
+ })
}
fn check_for_warn_of_moved_symbol(
"the replacement suggested by this lint had substantially different behavior"
}
-declare_deprecated_lint! {
- /// **What it does:** Nothing. This lint has been deprecated.
- ///
- /// **Deprecation reason:** This lint has been superseded by the warn-by-default
- /// `invalid_value` rustc lint.
- pub INVALID_REF,
- "superseded by rustc lint `invalid_value`"
-}
-
declare_deprecated_lint! {
/// **What it does:** Nothing. This lint has been deprecated.
///
"`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint"
}
-declare_deprecated_lint! {
- /// **What it does:** Nothing. This lint has been deprecated.
- ///
- /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
- /// `array_into_iter`.
- pub INTO_ITER_ON_ARRAY,
- "this lint has been uplifted to rustc and is now called `array_into_iter`"
-}
-
-declare_deprecated_lint! {
- /// **What it does:** Nothing. This lint has been deprecated.
- ///
- /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
- /// `unused_labels`.
- pub UNUSED_LABEL,
- "this lint has been uplifted to rustc and is now called `unused_labels`"
-}
-
declare_deprecated_lint! {
/// **What it does:** Nothing. This lint has been deprecated.
///
declare_deprecated_lint! {
/// **What it does:** Nothing. This lint has been deprecated.
///
- /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
- /// `drop_bounds`.
- pub DROP_BOUNDS,
- "this lint has been uplifted to rustc and is now called `drop_bounds`"
-}
-
-declare_deprecated_lint! {
- /// **What it does:** Nothing. This lint has been deprecated.
- ///
- /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
- /// `temporary_cstring_as_ptr`.
- pub TEMPORARY_CSTRING_AS_PTR,
- "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`"
-}
-
-declare_deprecated_lint! {
- /// **What it does:** Nothing. This lint has been deprecated.
- ///
- /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
- /// `panic_fmt`.
- pub PANIC_PARAMS,
- "this lint has been uplifted to rustc and is now called `panic_fmt`"
-}
-
-declare_deprecated_lint! {
- /// **What it does:** Nothing. This lint has been deprecated.
- ///
- /// **Deprecation reason:** This lint has been integrated into the `unknown_lints`
- /// rustc lint.
- pub UNKNOWN_CLIPPY_LINTS,
- "this lint has been integrated into the `unknown_lints` rustc lint"
+ /// **Deprecation reason:** This lint has been replaced by `manual_find_map`, a
+ /// more specific lint.
+ pub FIND_MAP,
+ "this lint has been replaced by `manual_find_map`, a more specific lint"
}
declare_deprecated_lint! {
/// **What it does:** Nothing. This lint has been deprecated.
///
- /// **Deprecation reason:** This lint has been replaced by `manual_find_map`, a
+ /// **Deprecation reason:** This lint has been replaced by `manual_filter_map`, a
/// more specific lint.
- pub FIND_MAP,
- "this lint has been replaced by `manual_find_map`, a more specific lint"
+ pub FILTER_MAP,
+ "this lint has been replaced by `manual_filter_map`, a more specific lint"
}
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
-use clippy_utils::ty::{is_type_diagnostic_item, match_type};
-use clippy_utils::SpanlessEq;
-use clippy_utils::{get_item_name, paths};
-use if_chain::if_chain;
+use clippy_utils::{
+ can_move_expr_to_closure_no_visit,
+ diagnostics::span_lint_and_sugg,
+ is_expr_final_block_expr, is_expr_used_or_unified, match_def_path, paths, peel_hir_expr_while,
+ source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context},
+ SpanlessEq,
+};
use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
-use rustc_hir::{BorrowKind, Expr, ExprKind, UnOp};
+use rustc_hir::{
+ intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
+ Block, Expr, ExprKind, Guard, HirId, Local, Stmt, StmtKind, UnOp,
+};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::hir::map::Map;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{Span, SyntaxContext, DUMMY_SP};
+use std::fmt::Write;
declare_clippy_lint! {
/// **What it does:** Checks for uses of `contains_key` + `insert` on `HashMap`
///
/// **Why is this bad?** Using `entry` is more efficient.
///
- /// **Known problems:** Some false negatives, eg.:
+ /// **Known problems:** The suggestion may have type inference errors in some cases. e.g.
/// ```rust
- /// # use std::collections::HashMap;
- /// # let mut map = HashMap::new();
- /// # let v = 1;
- /// # let k = 1;
- /// if !map.contains_key(&k) {
- /// map.insert(k.clone(), v);
- /// }
+ /// let mut map = std::collections::HashMap::new();
+ /// let _ = if !map.contains_key(&0) {
+ /// map.insert(0, 0)
+ /// } else {
+ /// None
+ /// };
/// ```
///
/// **Example:**
declare_lint_pass!(HashMapPass => [MAP_ENTRY]);
impl<'tcx> LateLintPass<'tcx> for HashMapPass {
+ #[allow(clippy::too_many_lines)]
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if let ExprKind::If(check, then_block, ref else_block) = expr.kind {
- if let ExprKind::Unary(UnOp::Not, check) = check.kind {
- if let Some((ty, map, key)) = check_cond(cx, check) {
- // in case of `if !m.contains_key(&k) { m.insert(k, v); }`
- // we can give a better error message
- let sole_expr = {
- else_block.is_none()
- && if let ExprKind::Block(then_block, _) = then_block.kind {
- (then_block.expr.is_some() as usize) + then_block.stmts.len() == 1
- } else {
- true
- }
- // XXXManishearth we can also check for if/else blocks containing `None`.
- };
-
- let mut visitor = InsertVisitor {
- cx,
- span: expr.span,
- ty,
- map,
- key,
- sole_expr,
- };
-
- walk_expr(&mut visitor, then_block);
+ let (cond_expr, then_expr, else_expr) = match expr.kind {
+ ExprKind::If(c, t, e) => (c, t, e),
+ _ => return,
+ };
+ let (map_ty, contains_expr) = match try_parse_contains(cx, cond_expr) {
+ Some(x) => x,
+ None => return,
+ };
+
+ let then_search = match find_insert_calls(cx, &contains_expr, then_expr) {
+ Some(x) => x,
+ None => return,
+ };
+
+ let mut app = Applicability::MachineApplicable;
+ let map_str = snippet_with_context(cx, contains_expr.map.span, contains_expr.call_ctxt, "..", &mut app).0;
+ let key_str = snippet_with_context(cx, contains_expr.key.span, contains_expr.call_ctxt, "..", &mut app).0;
+ let sugg = if let Some(else_expr) = else_expr {
+ let else_search = match find_insert_calls(cx, &contains_expr, else_expr) {
+ Some(search) => search,
+ None => return,
+ };
+
+ if then_search.edits.is_empty() && else_search.edits.is_empty() {
+ // No insertions
+ return;
+ } else if then_search.edits.is_empty() || else_search.edits.is_empty() {
+ // if .. { insert } else { .. } or if .. { .. } else { insert }
+ let ((then_str, entry_kind), else_str) = match (else_search.edits.is_empty(), contains_expr.negated) {
+ (true, true) => (
+ then_search.snippet_vacant(cx, then_expr.span, &mut app),
+ snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app),
+ ),
+ (true, false) => (
+ then_search.snippet_occupied(cx, then_expr.span, &mut app),
+ snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app),
+ ),
+ (false, true) => (
+ else_search.snippet_occupied(cx, else_expr.span, &mut app),
+ snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app),
+ ),
+ (false, false) => (
+ else_search.snippet_vacant(cx, else_expr.span, &mut app),
+ snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app),
+ ),
+ };
+ format!(
+ "if let {}::{} = {}.entry({}) {} else {}",
+ map_ty.entry_path(),
+ entry_kind,
+ map_str,
+ key_str,
+ then_str,
+ else_str,
+ )
+ } else {
+ // if .. { insert } else { insert }
+ let ((then_str, then_entry), (else_str, else_entry)) = if contains_expr.negated {
+ (
+ then_search.snippet_vacant(cx, then_expr.span, &mut app),
+ else_search.snippet_occupied(cx, else_expr.span, &mut app),
+ )
+ } else {
+ (
+ then_search.snippet_occupied(cx, then_expr.span, &mut app),
+ else_search.snippet_vacant(cx, else_expr.span, &mut app),
+ )
+ };
+ let indent_str = snippet_indent(cx, expr.span);
+ let indent_str = indent_str.as_deref().unwrap_or("");
+ format!(
+ "match {}.entry({}) {{\n{indent} {entry}::{} => {}\n\
+ {indent} {entry}::{} => {}\n{indent}}}",
+ map_str,
+ key_str,
+ then_entry,
+ reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())),
+ else_entry,
+ reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())),
+ entry = map_ty.entry_path(),
+ indent = indent_str,
+ )
+ }
+ } else {
+ if then_search.edits.is_empty() {
+ // no insertions
+ return;
+ }
+
+ // if .. { insert }
+ if !then_search.allow_insert_closure {
+ let (body_str, entry_kind) = if contains_expr.negated {
+ then_search.snippet_vacant(cx, then_expr.span, &mut app)
+ } else {
+ then_search.snippet_occupied(cx, then_expr.span, &mut app)
+ };
+ format!(
+ "if let {}::{} = {}.entry({}) {}",
+ map_ty.entry_path(),
+ entry_kind,
+ map_str,
+ key_str,
+ body_str,
+ )
+ } else if let Some(insertion) = then_search.as_single_insertion() {
+ let value_str = snippet_with_context(cx, insertion.value.span, then_expr.span.ctxt(), "..", &mut app).0;
+ if contains_expr.negated {
+ if insertion.value.can_have_side_effects() {
+ format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, value_str)
+ } else {
+ format!("{}.entry({}).or_insert({});", map_str, key_str, value_str)
+ }
+ } else {
+ // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
+ // This would need to be a different lint.
+ return;
}
- } else if let Some(else_block) = *else_block {
- if let Some((ty, map, key)) = check_cond(cx, check) {
- let mut visitor = InsertVisitor {
- cx,
- span: expr.span,
- ty,
- map,
- key,
- sole_expr: false,
- };
-
- walk_expr(&mut visitor, else_block);
+ } else {
+ let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app);
+ if contains_expr.negated {
+ format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, block_str)
+ } else {
+ // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
+ // This would need to be a different lint.
+ return;
}
}
- }
+ };
+
+ span_lint_and_sugg(
+ cx,
+ MAP_ENTRY,
+ expr.span,
+ &format!("usage of `contains_key` followed by `insert` on a `{}`", map_ty.name()),
+ "try this",
+ sugg,
+ app,
+ );
}
}
-fn check_cond<'a>(cx: &LateContext<'_>, check: &'a Expr<'a>) -> Option<(&'static str, &'a Expr<'a>, &'a Expr<'a>)> {
- if_chain! {
- if let ExprKind::MethodCall(path, _, params, _) = check.kind;
- if params.len() >= 2;
- if path.ident.name == sym!(contains_key);
- if let ExprKind::AddrOf(BorrowKind::Ref, _, key) = params[1].kind;
- then {
- let map = ¶ms[0];
- let obj_ty = cx.typeck_results().expr_ty(map).peel_refs();
+#[derive(Clone, Copy)]
+enum MapType {
+ Hash,
+ BTree,
+}
+impl MapType {
+ fn name(self) -> &'static str {
+ match self {
+ Self::Hash => "HashMap",
+ Self::BTree => "BTreeMap",
+ }
+ }
+ fn entry_path(self) -> &'static str {
+ match self {
+ Self::Hash => "std::collections::hash_map::Entry",
+ Self::BTree => "std::collections::btree_map::Entry",
+ }
+ }
+}
- return if match_type(cx, obj_ty, &paths::BTREEMAP) {
- Some(("BTreeMap", map, key))
- }
- else if is_type_diagnostic_item(cx, obj_ty, sym::hashmap_type) {
- Some(("HashMap", map, key))
- }
- else {
- None
+struct ContainsExpr<'tcx> {
+ negated: bool,
+ map: &'tcx Expr<'tcx>,
+ key: &'tcx Expr<'tcx>,
+ call_ctxt: SyntaxContext,
+}
+fn try_parse_contains(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> {
+ let mut negated = false;
+ let expr = peel_hir_expr_while(expr, |e| match e.kind {
+ ExprKind::Unary(UnOp::Not, e) => {
+ negated = !negated;
+ Some(e)
+ },
+ _ => None,
+ });
+ match expr.kind {
+ ExprKind::MethodCall(
+ _,
+ _,
+ [map, Expr {
+ kind: ExprKind::AddrOf(_, _, key),
+ span: key_span,
+ ..
+ }],
+ _,
+ ) if key_span.ctxt() == expr.span.ctxt() => {
+ let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
+ let expr = ContainsExpr {
+ negated,
+ map,
+ key,
+ call_ctxt: expr.span.ctxt(),
};
+ if match_def_path(cx, id, &paths::BTREEMAP_CONTAINS_KEY) {
+ Some((MapType::BTree, expr))
+ } else if match_def_path(cx, id, &paths::HASHMAP_CONTAINS_KEY) {
+ Some((MapType::Hash, expr))
+ } else {
+ None
+ }
+ },
+ _ => None,
+ }
+}
+
+struct InsertExpr<'tcx> {
+ map: &'tcx Expr<'tcx>,
+ key: &'tcx Expr<'tcx>,
+ value: &'tcx Expr<'tcx>,
+}
+fn try_parse_insert(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> {
+ if let ExprKind::MethodCall(_, _, [map, key, value], _) = expr.kind {
+ let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
+ if match_def_path(cx, id, &paths::BTREEMAP_INSERT) || match_def_path(cx, id, &paths::HASHMAP_INSERT) {
+ Some(InsertExpr { map, key, value })
+ } else {
+ None
}
+ } else {
+ None
}
+}
- None
+/// An edit that will need to be made to move the expression to use the entry api
+#[derive(Clone, Copy)]
+enum Edit<'tcx> {
+ /// A semicolon that needs to be removed. Used to create a closure for `insert_with`.
+ RemoveSemi(Span),
+ /// An insertion into the map.
+ Insertion(Insertion<'tcx>),
+}
+impl Edit<'tcx> {
+ fn as_insertion(self) -> Option<Insertion<'tcx>> {
+ if let Self::Insertion(i) = self { Some(i) } else { None }
+ }
+}
+#[derive(Clone, Copy)]
+struct Insertion<'tcx> {
+ call: &'tcx Expr<'tcx>,
+ value: &'tcx Expr<'tcx>,
}
-struct InsertVisitor<'a, 'tcx, 'b> {
- cx: &'a LateContext<'tcx>,
- span: Span,
- ty: &'static str,
- map: &'b Expr<'b>,
- key: &'b Expr<'b>,
- sole_expr: bool,
+/// This visitor needs to do a multiple things:
+/// * Find all usages of the map. An insertion can only be made before any other usages of the map.
+/// * Determine if there's an insertion using the same key. There's no need for the entry api
+/// otherwise.
+/// * Determine if the final statement executed is an insertion. This is needed to use
+/// `or_insert_with`.
+/// * Determine if there's any sub-expression that can't be placed in a closure.
+/// * Determine if there's only a single insert statement. `or_insert` can be used in this case.
+#[allow(clippy::struct_excessive_bools)]
+struct InsertSearcher<'cx, 'tcx> {
+ cx: &'cx LateContext<'tcx>,
+ /// The map expression used in the contains call.
+ map: &'tcx Expr<'tcx>,
+ /// The key expression used in the contains call.
+ key: &'tcx Expr<'tcx>,
+ /// The context of the top level block. All insert calls must be in the same context.
+ ctxt: SyntaxContext,
+ /// Whether this expression can be safely moved into a closure.
+ allow_insert_closure: bool,
+ /// Whether this expression can use the entry api.
+ can_use_entry: bool,
+ /// Whether this expression is the final expression in this code path. This may be a statement.
+ in_tail_pos: bool,
+ // Is this expression a single insert. A slightly better suggestion can be made in this case.
+ is_single_insert: bool,
+ /// If the visitor has seen the map being used.
+ is_map_used: bool,
+ /// The locations where changes need to be made for the suggestion.
+ edits: Vec<Edit<'tcx>>,
+ /// A stack of loops the visitor is currently in.
+ loops: Vec<HirId>,
}
+impl<'tcx> InsertSearcher<'_, 'tcx> {
+ /// Visit the expression as a branch in control flow. Multiple insert calls can be used, but
+ /// only if they are on separate code paths. This will return whether the map was used in the
+ /// given expression.
+ fn visit_cond_arm(&mut self, e: &'tcx Expr<'_>) -> bool {
+ let is_map_used = self.is_map_used;
+ let in_tail_pos = self.in_tail_pos;
+ self.visit_expr(e);
+ let res = self.is_map_used;
+ self.is_map_used = is_map_used;
+ self.in_tail_pos = in_tail_pos;
+ res
+ }
-impl<'a, 'tcx, 'b> Visitor<'tcx> for InsertVisitor<'a, 'tcx, 'b> {
- type Map = Map<'tcx>;
+ /// Visits an expression which is not itself in a tail position, but other sibling expressions
+ /// may be. e.g. if conditions
+ fn visit_non_tail_expr(&mut self, e: &'tcx Expr<'_>) {
+ let in_tail_pos = self.in_tail_pos;
+ self.in_tail_pos = false;
+ self.visit_expr(e);
+ self.in_tail_pos = in_tail_pos;
+ }
+}
+impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
+ type Map = ErasedMap<'tcx>;
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
+ match stmt.kind {
+ StmtKind::Semi(e) => {
+ self.visit_expr(e);
+
+ if self.in_tail_pos && self.allow_insert_closure {
+ // The spans are used to slice the top level expression into multiple parts. This requires that
+ // they all come from the same part of the source code.
+ if stmt.span.ctxt() == self.ctxt && e.span.ctxt() == self.ctxt {
+ self.edits
+ .push(Edit::RemoveSemi(stmt.span.trim_start(e.span).unwrap_or(DUMMY_SP)));
+ } else {
+ self.allow_insert_closure = false;
+ }
+ }
+ },
+ StmtKind::Expr(e) => self.visit_expr(e),
+ StmtKind::Local(Local { init: Some(e), .. }) => {
+ self.allow_insert_closure &= !self.in_tail_pos;
+ self.in_tail_pos = false;
+ self.is_single_insert = false;
+ self.visit_expr(e);
+ },
+ _ => {
+ self.allow_insert_closure &= !self.in_tail_pos;
+ self.is_single_insert = false;
+ },
+ }
+ }
+
+ fn visit_block(&mut self, block: &'tcx Block<'_>) {
+ // If the block is in a tail position, then the last expression (possibly a statement) is in the
+ // tail position. The rest, however, are not.
+ match (block.stmts, block.expr) {
+ ([], None) => {
+ self.allow_insert_closure &= !self.in_tail_pos;
+ },
+ ([], Some(expr)) => self.visit_expr(expr),
+ (stmts, Some(expr)) => {
+ let in_tail_pos = self.in_tail_pos;
+ self.in_tail_pos = false;
+ for stmt in stmts {
+ self.visit_stmt(stmt);
+ }
+ self.in_tail_pos = in_tail_pos;
+ self.visit_expr(expr);
+ },
+ ([stmts @ .., stmt], None) => {
+ let in_tail_pos = self.in_tail_pos;
+ self.in_tail_pos = false;
+ for stmt in stmts {
+ self.visit_stmt(stmt);
+ }
+ self.in_tail_pos = in_tail_pos;
+ self.visit_stmt(stmt);
+ },
+ }
+ }
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
- if_chain! {
- if let ExprKind::MethodCall(path, _, params, _) = expr.kind;
- if params.len() == 3;
- if path.ident.name == sym!(insert);
- if get_item_name(self.cx, self.map) == get_item_name(self.cx, ¶ms[0]);
- if SpanlessEq::new(self.cx).eq_expr(self.key, ¶ms[1]);
- if snippet_opt(self.cx, self.map.span) == snippet_opt(self.cx, params[0].span);
- then {
- span_lint_and_then(self.cx, MAP_ENTRY, self.span,
- &format!("usage of `contains_key` followed by `insert` on a `{}`", self.ty), |diag| {
- if self.sole_expr {
- let mut app = Applicability::MachineApplicable;
- let help = format!("{}.entry({}).or_insert({});",
- snippet_with_applicability(self.cx, self.map.span, "map", &mut app),
- snippet_with_applicability(self.cx, params[1].span, "..", &mut app),
- snippet_with_applicability(self.cx, params[2].span, "..", &mut app));
-
- diag.span_suggestion(
- self.span,
- "consider using",
- help,
- Applicability::MachineApplicable, // snippet
- );
+ if !self.can_use_entry {
+ return;
+ }
+
+ match try_parse_insert(self.cx, expr) {
+ Some(insert_expr) if SpanlessEq::new(self.cx).eq_expr(self.map, insert_expr.map) => {
+ // Multiple inserts, inserts with a different key, and inserts from a macro can't use the entry api.
+ if self.is_map_used
+ || !SpanlessEq::new(self.cx).eq_expr(self.key, insert_expr.key)
+ || expr.span.ctxt() != self.ctxt
+ {
+ self.can_use_entry = false;
+ return;
+ }
+
+ self.edits.push(Edit::Insertion(Insertion {
+ call: expr,
+ value: insert_expr.value,
+ }));
+ self.is_map_used = true;
+ self.allow_insert_closure &= self.in_tail_pos;
+
+ // The value doesn't affect whether there is only a single insert expression.
+ let is_single_insert = self.is_single_insert;
+ self.visit_non_tail_expr(insert_expr.value);
+ self.is_single_insert = is_single_insert;
+ },
+ _ if SpanlessEq::new(self.cx).eq_expr(self.map, expr) => {
+ self.is_map_used = true;
+ },
+ _ => match expr.kind {
+ ExprKind::If(cond_expr, then_expr, Some(else_expr)) => {
+ self.is_single_insert = false;
+ self.visit_non_tail_expr(cond_expr);
+ // Each branch may contain it's own insert expression.
+ let mut is_map_used = self.visit_cond_arm(then_expr);
+ is_map_used |= self.visit_cond_arm(else_expr);
+ self.is_map_used = is_map_used;
+ },
+ ExprKind::Match(scrutinee_expr, arms, _) => {
+ self.is_single_insert = false;
+ self.visit_non_tail_expr(scrutinee_expr);
+ // Each branch may contain it's own insert expression.
+ let mut is_map_used = self.is_map_used;
+ for arm in arms {
+ if let Some(Guard::If(guard) | Guard::IfLet(_, guard)) = arm.guard {
+ self.visit_non_tail_expr(guard)
+ }
+ is_map_used |= self.visit_cond_arm(arm.body);
}
- else {
- let help = format!("consider using `{}.entry({})`",
- snippet(self.cx, self.map.span, "map"),
- snippet(self.cx, params[1].span, ".."));
-
- diag.span_label(
- self.span,
- &help,
- );
+ self.is_map_used = is_map_used;
+ },
+ ExprKind::Loop(block, ..) => {
+ self.loops.push(expr.hir_id);
+ self.is_single_insert = false;
+ self.allow_insert_closure &= !self.in_tail_pos;
+ // Don't allow insertions inside of a loop.
+ let edit_len = self.edits.len();
+ self.visit_block(block);
+ if self.edits.len() != edit_len {
+ self.can_use_entry = false;
}
- });
- }
+ self.loops.pop();
+ },
+ ExprKind::Block(block, _) => self.visit_block(block),
+ ExprKind::InlineAsm(_) | ExprKind::LlvmInlineAsm(_) => {
+ self.can_use_entry = false;
+ },
+ _ => {
+ self.allow_insert_closure &= !self.in_tail_pos;
+ self.allow_insert_closure &= can_move_expr_to_closure_no_visit(self.cx, expr, &self.loops);
+ // Sub expressions are no longer in the tail position.
+ self.is_single_insert = false;
+ self.in_tail_pos = false;
+ walk_expr(self, expr);
+ },
+ },
}
+ }
+}
+
+struct InsertSearchResults<'tcx> {
+ edits: Vec<Edit<'tcx>>,
+ allow_insert_closure: bool,
+ is_single_insert: bool,
+}
+impl InsertSearchResults<'tcx> {
+ fn as_single_insertion(&self) -> Option<Insertion<'tcx>> {
+ self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap())
+ }
- if !self.sole_expr {
- walk_expr(self, expr);
+ fn snippet(
+ &self,
+ cx: &LateContext<'_>,
+ mut span: Span,
+ app: &mut Applicability,
+ write_wrapped: impl Fn(&mut String, Insertion<'_>, SyntaxContext, &mut Applicability),
+ ) -> String {
+ let ctxt = span.ctxt();
+ let mut res = String::new();
+ for insertion in self.edits.iter().filter_map(|e| e.as_insertion()) {
+ res.push_str(&snippet_with_applicability(
+ cx,
+ span.until(insertion.call.span),
+ "..",
+ app,
+ ));
+ if is_expr_used_or_unified(cx.tcx, insertion.call) {
+ write_wrapped(&mut res, insertion, ctxt, app);
+ } else {
+ let _ = write!(
+ res,
+ "e.insert({})",
+ snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0
+ );
+ }
+ span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP);
}
+ res.push_str(&snippet_with_applicability(cx, span, "..", app));
+ res
}
- fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
- NestedVisitorMap::None
+
+ fn snippet_occupied(&self, cx: &LateContext<'_>, span: Span, app: &mut Applicability) -> (String, &'static str) {
+ (
+ self.snippet(cx, span, app, |res, insertion, ctxt, app| {
+ // Insertion into a map would return `Some(&mut value)`, but the entry returns `&mut value`
+ let _ = write!(
+ res,
+ "Some(e.insert({}))",
+ snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0
+ );
+ }),
+ "Occupied(mut e)",
+ )
}
+
+ fn snippet_vacant(&self, cx: &LateContext<'_>, span: Span, app: &mut Applicability) -> (String, &'static str) {
+ (
+ self.snippet(cx, span, app, |res, insertion, ctxt, app| {
+ // Insertion into a map would return `None`, but the entry returns a mutable reference.
+ let _ = if is_expr_final_block_expr(cx.tcx, insertion.call) {
+ write!(
+ res,
+ "e.insert({});\n{}None",
+ snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0,
+ snippet_indent(cx, insertion.call.span).as_deref().unwrap_or(""),
+ )
+ } else {
+ write!(
+ res,
+ "{{ e.insert({}); None }}",
+ snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0,
+ )
+ };
+ }),
+ "Vacant(e)",
+ )
+ }
+
+ fn snippet_closure(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String {
+ let ctxt = span.ctxt();
+ let mut res = String::new();
+ for edit in &self.edits {
+ match *edit {
+ Edit::Insertion(insertion) => {
+ // Cut out the value from `map.insert(key, value)`
+ res.push_str(&snippet_with_applicability(
+ cx,
+ span.until(insertion.call.span),
+ "..",
+ app,
+ ));
+ res.push_str(&snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0);
+ span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP);
+ },
+ Edit::RemoveSemi(semi_span) => {
+ // Cut out the semicolon. This allows the value to be returned from the closure.
+ res.push_str(&snippet_with_applicability(cx, span.until(semi_span), "..", app));
+ span = span.trim_start(semi_span).unwrap_or(DUMMY_SP);
+ },
+ }
+ }
+ res.push_str(&snippet_with_applicability(cx, span, "..", app));
+ res
+ }
+}
+
+fn find_insert_calls(
+ cx: &LateContext<'tcx>,
+ contains_expr: &ContainsExpr<'tcx>,
+ expr: &'tcx Expr<'_>,
+) -> Option<InsertSearchResults<'tcx>> {
+ let mut s = InsertSearcher {
+ cx,
+ map: contains_expr.map,
+ key: contains_expr.key,
+ ctxt: expr.span.ctxt(),
+ edits: Vec::new(),
+ is_map_used: false,
+ allow_insert_closure: true,
+ can_use_entry: true,
+ in_tail_pos: true,
+ is_single_insert: true,
+ loops: Vec::new(),
+ };
+ s.visit_expr(expr);
+ let allow_insert_closure = s.allow_insert_closure;
+ let is_single_insert = s.is_single_insert;
+ let edits = s.edits;
+ s.can_use_entry.then(|| InsertSearchResults {
+ edits,
+ allow_insert_closure,
+ is_single_insert,
+ })
}
use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::{in_macro, match_path_ast};
+use clippy_utils::in_macro;
use rustc_ast::ast::{AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
fn is_bool_ty(ty: &Ty) -> bool {
if let TyKind::Path(None, path) = &ty.kind {
- return match_path_ast(path, &["bool"]);
+ if let [name] = path.segments.as_slice() {
+ return name.ident.name == sym::bool;
+ }
}
false
}
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::paths;
use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_expn_of, last_path_segment, match_def_path, match_function_call};
use if_chain::if_chain;
return Some(format!("{:?}.to_string()", s.as_str()));
}
} else {
- let snip = snippet(cx, format_args.span, "<arg>");
+ let sugg = Sugg::hir(cx, format_args, "<arg>");
if let ExprKind::MethodCall(path, _, _, _) = format_args.kind {
if path.ident.name == sym!(to_string) {
- return Some(format!("{}", snip));
+ return Some(format!("{}", sugg));
}
} else if let ExprKind::Binary(..) = format_args.kind {
- return Some(format!("{}", snip));
+ return Some(format!("{}", sugg));
}
- return Some(format!("{}.to_string()", snip));
+ return Some(format!("{}.to_string()", sugg.maybe_par()));
}
}
}
if let Some(s_src) = snippet_opt(cx, lit.span) {
// Simulate macro expansion, converting {{ and }} to { and }.
let s_expand = s_src.replace("{{", "{").replace("}}", "}");
- return Some(format!("{}.to_string()", s_expand))
+ return Some(format!("{}.to_string()", s_expand));
}
} else if s.as_str().is_empty() {
return on_argumentv1_new(cx, &tup[0], arms);
// the snippet should look like " else \n " with maybe comments anywhere
// it’s bad when there is a ‘\n’ after the “else”
if let Some(else_snippet) = snippet_opt(cx, else_span);
- if let Some(else_pos) = else_snippet.find("else");
- if else_snippet[else_pos..].contains('\n');
+ if let Some((pre_else, post_else)) = else_snippet.split_once("else");
+ if let Some((_, post_else_post_eol)) = post_else.split_once('\n');
+
then {
+ // Allow allman style braces `} \n else \n {`
+ if_chain! {
+ if is_block(else_);
+ if let Some((_, pre_else_post_eol)) = pre_else.split_once('\n');
+ // Exactly one eol before and after the else
+ if !pre_else_post_eol.contains('\n');
+ if !post_else_post_eol.contains('\n');
+ then {
+ return;
+ }
+ }
+
let else_desc = if is_if(else_) { "if" } else { "{..}" };
span_lint_and_note(
cx,
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::paths::INTO;
-use clippy_utils::{match_def_path, meets_msrv};
+use clippy_utils::{match_def_path, meets_msrv, msrvs};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-const FROM_OVER_INTO_MSRV: RustcVersion = RustcVersion::new(1, 41, 0);
-
declare_clippy_lint! {
/// **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead.
///
impl LateLintPass<'_> for FromOverInto {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
- if !meets_msrv(self.msrv.as_ref(), &FROM_OVER_INTO_MSRV) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::RE_REBALANCING_COHERENCE) {
return;
}
cx.tcx.sess.source_map().guess_head_span(item.span),
"an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true",
None,
- "consider to implement `From` instead",
+ &format!("consider to implement `From<{}>` instead", impl_trait_ref.self_ty()),
);
}
}
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::{match_qpath, meets_msrv, parent_node_is_if_expr};
+use clippy_utils::{is_else_clause, is_lang_ctor, meets_msrv, msrvs};
use if_chain::if_chain;
+use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-const IF_THEN_SOME_ELSE_NONE_MSRV: RustcVersion = RustcVersion::new(1, 50, 0);
-
declare_clippy_lint! {
/// **What it does:** Checks for if-else that could be written to `bool::then`.
///
impl LateLintPass<'_> for IfThenSomeElseNone {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
- if !meets_msrv(self.msrv.as_ref(), &IF_THEN_SOME_ELSE_NONE_MSRV) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::BOOL_THEN) {
return;
}
}
// We only care about the top-most `if` in the chain
- if parent_node_is_if_expr(expr, cx) {
+ if is_else_clause(cx.tcx, expr) {
return;
}
if let Some(then_expr) = then_block.expr;
if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind;
if let ExprKind::Path(ref then_call_qpath) = then_call.kind;
- if match_qpath(then_call_qpath, &clippy_utils::paths::OPTION_SOME);
+ if is_lang_ctor(cx, then_call_qpath, OptionSome);
if let ExprKind::Block(els_block, _) = els.kind;
if els_block.stmts.is_empty();
if let Some(els_expr) = els_block.expr;
- if let ExprKind::Path(ref els_call_qpath) = els_expr.kind;
- if match_qpath(els_call_qpath, &clippy_utils::paths::OPTION_NONE);
+ if let ExprKind::Path(ref qpath) = els_expr.kind;
+ if is_lang_ctor(cx, qpath, OptionNone);
then {
let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
use clippy_utils::paths;
use clippy_utils::source::{snippet, snippet_opt};
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{differing_macro_contexts, match_path};
+use clippy_utils::{differing_macro_contexts, match_def_path};
declare_clippy_lint! {
/// **What it does:** Checks for public `impl` or `fn` missing generalization
if let ExprKind::Call(fun, args) = e.kind;
if let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind;
if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind;
+ if let Some(ty_did) = ty_path.res.opt_def_id();
then {
if !TyS::same_type(self.target.ty(), self.maybe_typeck_results.unwrap().expr_ty(e)) {
return;
}
- if match_path(ty_path, &paths::HASHMAP) {
+ if match_def_path(self.cx, ty_did, &paths::HASHMAP) {
if method.ident.name == sym::new {
self.suggestions
.insert(e.span, "HashMap::default()".to_string());
),
);
}
- } else if match_path(ty_path, &paths::HASHSET) {
+ } else if match_def_path(self.cx, ty_did, &paths::HASHSET) {
if method.ident.name == sym::new {
self.suggestions
.insert(e.span, "HashSet::default()".to_string());
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{in_macro, match_qpath, SpanlessEq};
+use clippy_utils::{in_macro, SpanlessEq};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, StmtKind};
+use rustc_hir::{lang_items::LangItem, BinOpKind, Expr, ExprKind, QPath, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
// Get the variable name
let var_name = ares_path.segments[0].ident.name.as_str();
- const INT_TYPES: [&str; 5] = ["i8", "i16", "i32", "i64", "i128"];
+ const INT_TYPES: [LangItem; 5] = [
+ LangItem::I8,
+ LangItem::I16,
+ LangItem::I32,
+ LangItem::I64,
+ LangItem::Isize
+ ];
match cond_num_val.kind {
ExprKind::Lit(ref cond_lit) => {
};
}
},
- ExprKind::Path(ref cond_num_path) => {
- if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "MIN"])) {
- print_lint_and_sugg(cx, &var_name, expr);
- };
+ ExprKind::Path(QPath::TypeRelative(_, name)) => {
+ if_chain! {
+ if name.ident.as_str() == "MIN";
+ if let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id);
+ if let Some(impl_id) = cx.tcx.impl_of_method(const_id);
+ let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok());
+ if int_ids.any(|int_id| int_id == impl_id);
+ then {
+ print_lint_and_sugg(cx, &var_name, expr)
+ }
+ }
},
- ExprKind::Call(func, _) => {
- if let ExprKind::Path(ref cond_num_path) = func.kind {
- if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "min_value"])) {
- print_lint_and_sugg(cx, &var_name, expr);
+ ExprKind::Call(func, []) => {
+ if_chain! {
+ if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind;
+ if name.ident.as_str() == "min_value";
+ if let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id);
+ if let Some(impl_id) = cx.tcx.impl_of_method(func_id);
+ let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok());
+ if int_ids.any(|int_id| int_id == impl_id);
+ then {
+ print_lint_and_sugg(cx, &var_name, expr)
}
- };
+ }
},
_ => (),
}
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
use clippy_utils::source::snippet;
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashMap;
impl LateLintPass<'_> for InconsistentStructConstructor {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if_chain! {
+ if !in_macro(expr.span);
if let ExprKind::Struct(qpath, fields, base) = expr.kind;
let ty = cx.typeck_results().expr_ty(expr);
if let Some(adt_def) = ty.ty_adt_def();
use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::{implements_trait, match_type};
-use clippy_utils::{get_trait_def_id, higher, match_qpath, paths};
+use clippy_utils::{get_trait_def_id, higher, is_qpath_def_path, paths};
use rustc_hir::{BorrowKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e),
ExprKind::Call(path, _) => {
if let ExprKind::Path(ref qpath) = path.kind {
- match_qpath(qpath, &paths::REPEAT).into()
+ is_qpath_def_path(cx, qpath, path.hir_id, &paths::ITER_REPEAT).into()
} else {
Finite
}
mod bit_mask;
mod blacklisted_name;
mod blocks_in_if_conditions;
+mod bool_assert_comparison;
mod booleans;
mod bytecount;
mod cargo_common_metadata;
mod unit_return_expecting_ord;
mod unit_types;
mod unnamed_address;
+mod unnecessary_self_imports;
mod unnecessary_sort_by;
mod unnecessary_wraps;
mod unnested_or_patterns;
///
/// Used in `./src/driver.rs`.
pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) {
+ // NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
store.register_pre_expansion_pass(|| box write::Write::default());
store.register_pre_expansion_pass(|| box attrs::EarlyAttributes);
store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro);
"clippy::unsafe_vector_initialization",
"the replacement suggested by this lint had substantially different behavior",
);
- store.register_removed(
- "clippy::invalid_ref",
- "superseded by rustc lint `invalid_value`",
- );
store.register_removed(
"clippy::unused_collect",
"`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint",
);
- store.register_removed(
- "clippy::into_iter_on_array",
- "this lint has been uplifted to rustc and is now called `array_into_iter`",
- );
- store.register_removed(
- "clippy::unused_label",
- "this lint has been uplifted to rustc and is now called `unused_labels`",
- );
store.register_removed(
"clippy::replace_consts",
"associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants",
"clippy::regex_macro",
"the regex! macro has been removed from the regex crate in 2018",
);
- store.register_removed(
- "clippy::drop_bounds",
- "this lint has been uplifted to rustc and is now called `drop_bounds`",
- );
- store.register_removed(
- "clippy::temporary_cstring_as_ptr",
- "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`",
- );
- store.register_removed(
- "clippy::panic_params",
- "this lint has been uplifted to rustc and is now called `panic_fmt`",
- );
- store.register_removed(
- "clippy::unknown_clippy_lints",
- "this lint has been integrated into the `unknown_lints` rustc lint",
- );
store.register_removed(
"clippy::find_map",
"this lint has been replaced by `manual_find_map`, a more specific lint",
);
+ store.register_removed(
+ "clippy::filter_map",
+ "this lint has been replaced by `manual_filter_map`, a more specific lint",
+ );
// end deprecated lints, do not remove this comment, it’s used in `update_lints`
// begin register lints, do not remove this comment, it’s used in `update_lints`
bit_mask::VERBOSE_BIT_MASK,
blacklisted_name::BLACKLISTED_NAME,
blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
+ bool_assert_comparison::BOOL_ASSERT_COMPARISON,
booleans::LOGIC_BUG,
booleans::NONMINIMAL_BOOL,
bytecount::NAIVE_BYTECOUNT,
methods::BYTES_NTH,
methods::CHARS_LAST_CMP,
methods::CHARS_NEXT_CMP,
+ methods::CLONED_INSTEAD_OF_COPIED,
methods::CLONE_DOUBLE_REF,
methods::CLONE_ON_COPY,
methods::CLONE_ON_REF_PTR,
methods::EXPECT_FUN_CALL,
methods::EXPECT_USED,
methods::FILETYPE_IS_FILE,
- methods::FILTER_MAP,
methods::FILTER_MAP_IDENTITY,
methods::FILTER_MAP_NEXT,
methods::FILTER_NEXT,
methods::FLAT_MAP_IDENTITY,
+ methods::FLAT_MAP_OPTION,
methods::FROM_ITER_INSTEAD_OF_COLLECT,
methods::GET_UNWRAP,
methods::IMPLICIT_CLONE,
pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
precedence::PRECEDENCE,
ptr::CMP_NULL,
+ ptr::INVALID_NULL_PTR_USAGE,
ptr::MUT_FROM_REF,
ptr::PTR_ARG,
ptr_eq::PTR_EQ,
unit_types::UNIT_CMP,
unnamed_address::FN_ADDRESS_COMPARISONS,
unnamed_address::VTABLE_ADDRESS_COMPARISONS,
+ unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
unnecessary_sort_by::UNNECESSARY_SORT_BY,
unnecessary_wraps::UNNECESSARY_WRAPS,
unnested_or_patterns::UNNESTED_OR_PATTERNS,
store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback);
store.register_late_pass(|| box inconsistent_struct_constructor::InconsistentStructConstructor);
store.register_late_pass(|| box non_octal_unix_permissions::NonOctalUnixPermissions);
+ store.register_early_pass(|| box unnecessary_self_imports::UnnecessarySelfImports);
let msrv = conf.msrv.as_ref().and_then(|s| {
parse_msrv(s, None, None).or_else(|| {
store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv));
store.register_late_pass(move || box use_self::UseSelf::new(msrv));
store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv));
- store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark::new(msrv));
+ store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark);
store.register_late_pass(move || box casts::Casts::new(msrv));
store.register_early_pass(move || box unnested_or_patterns::UnnestedOrPatterns::new(msrv));
store.register_late_pass(|| box from_str_radix_10::FromStrRadix10);
store.register_late_pass(|| box manual_map::ManualMap);
store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv));
+ store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison);
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(arithmetic::FLOAT_ARITHMETIC),
LintId::of(strings::STRING_TO_STRING),
LintId::of(strings::STR_TO_STRING),
LintId::of(types::RC_BUFFER),
+ LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
LintId::of(write::PRINT_STDERR),
LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
LintId::of(matches::MATCH_WILD_ERR_ARM),
LintId::of(matches::SINGLE_MATCH_ELSE),
- LintId::of(methods::FILTER_MAP),
+ LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
LintId::of(methods::FILTER_MAP_NEXT),
+ LintId::of(methods::FLAT_MAP_OPTION),
LintId::of(methods::IMPLICIT_CLONE),
LintId::of(methods::INEFFICIENT_TO_STRING),
LintId::of(methods::MAP_FLATTEN),
LintId::of(strings::STRING_ADD_ASSIGN),
LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
+ LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
LintId::of(types::LINKEDLIST),
LintId::of(types::OPTION_OPTION),
LintId::of(unicode::NON_ASCII_LITERAL),
LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
LintId::of(blacklisted_name::BLACKLISTED_NAME),
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
+ LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
LintId::of(booleans::LOGIC_BUG),
LintId::of(booleans::NONMINIMAL_BOOL),
LintId::of(casts::CAST_REF_TO_MUT),
LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
LintId::of(precedence::PRECEDENCE),
LintId::of(ptr::CMP_NULL),
+ LintId::of(ptr::INVALID_NULL_PTR_USAGE),
LintId::of(ptr::MUT_FROM_REF),
LintId::of(ptr::PTR_ARG),
LintId::of(ptr_eq::PTR_EQ),
LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
- LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
LintId::of(transmute::WRONG_TRANSMUTE),
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
LintId::of(blacklisted_name::BLACKLISTED_NAME),
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
+ LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
LintId::of(casts::FN_TO_NUMERIC_CAST),
LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
- LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
LintId::of(types::BORROWED_BOX),
LintId::of(types::TYPE_COMPLEXITY),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
+ LintId::of(ptr::INVALID_NULL_PTR_USAGE),
LintId::of(ptr::MUT_FROM_REF),
LintId::of(ranges::REVERSED_EMPTY_RANGES),
LintId::of(regex::INVALID_REGEX),
ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
+
+ // uplifted lints
+ ls.register_renamed("clippy::invalid_ref", "invalid_value");
+ ls.register_renamed("clippy::into_iter_on_array", "array_into_iter");
+ ls.register_renamed("clippy::unused_label", "unused_labels");
+ ls.register_renamed("clippy::drop_bounds", "drop_bounds");
+ ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr");
+ ls.register_renamed("clippy::panic_params", "non_fmt_panic");
+ ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints");
}
// only exists to let the dogfood integration test works.
use super::EXPLICIT_INTO_ITER_LOOP;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{match_trait_method, paths};
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty::TyS;
-pub(super) fn check(cx: &LateContext<'_>, args: &'hir [Expr<'hir>], arg: &Expr<'_>) {
- let receiver_ty = cx.typeck_results().expr_ty(&args[0]);
- let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]);
- if !TyS::same_type(receiver_ty, receiver_ty_adjusted) {
+pub(super) fn check(cx: &LateContext<'_>, self_arg: &'hir Expr<'hir>, call_expr: &Expr<'_>) {
+ let self_ty = cx.typeck_results().expr_ty(self_arg);
+ let self_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg);
+ if !(TyS::same_type(self_ty, self_ty_adjusted) && match_trait_method(cx, call_expr, &paths::INTO_ITERATOR)) {
return;
}
let mut applicability = Applicability::MachineApplicable;
- let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
+ let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability);
span_lint_and_sugg(
cx,
EXPLICIT_INTO_ITER_LOOP,
- arg.span,
+ call_expr.span,
"it is more concise to loop over containers instead of using explicit \
iteration methods",
"to write this more concisely, try",
use rustc_middle::ty::{self, Ty, TyS};
use rustc_span::sym;
-pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) {
+pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, arg: &Expr<'_>, method_name: &str) {
let should_lint = match method_name {
- "iter" | "iter_mut" => is_ref_iterable_type(cx, &args[0]),
+ "iter" | "iter_mut" => is_ref_iterable_type(cx, self_arg),
"into_iter" if match_trait_method(cx, arg, &paths::INTO_ITERATOR) => {
- let receiver_ty = cx.typeck_results().expr_ty(&args[0]);
- let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]);
+ let receiver_ty = cx.typeck_results().expr_ty(self_arg);
+ let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg);
let ref_receiver_ty = cx.tcx.mk_ref(
cx.tcx.lifetimes.re_erased,
ty::TypeAndMut {
}
let mut applicability = Applicability::MachineApplicable;
- let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
+ let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability);
let muta = if method_name == "iter_mut" { "mut " } else { "" };
span_lint_and_sugg(
cx,
use super::utils::make_iterator_snippet;
use super::MANUAL_FLATTEN;
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::{is_ok_ctor, is_some_ctor, path_to_local_id};
+use clippy_utils::{is_lang_ctor, path_to_local_id};
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, StmtKind};
+use rustc_hir::LangItem::{OptionSome, ResultOk};
+use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, StmtKind};
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::source_map::Span;
if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind;
if path_to_local_id(match_expr, pat_hir_id);
// Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
- if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind;
- let some_ctor = is_some_ctor(cx, path.res);
- let ok_ctor = is_ok_ctor(cx, path.res);
+ if let PatKind::TupleStruct(ref qpath, _, _) = match_arms[0].pat.kind;
+ let some_ctor = is_lang_ctor(cx, qpath, OptionSome);
+ let ok_ctor = is_lang_ctor(cx, qpath, ResultOk);
if some_ctor || ok_ctor;
then {
let if_let_type = if some_ctor { "Some" } else { "Ok" };
fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) {
let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used
- if let ExprKind::MethodCall(method, _, args, _) = arg.kind {
- // just the receiver, no arguments
- if args.len() == 1 {
- let method_name = &*method.ident.as_str();
- // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
- match method_name {
- "iter" | "iter_mut" => explicit_iter_loop::check(cx, args, arg, method_name),
- "into_iter" => {
- explicit_iter_loop::check(cx, args, arg, method_name);
- explicit_into_iter_loop::check(cx, args, arg);
- },
- "next" => {
- next_loop_linted = iter_next_loop::check(cx, arg, expr);
- },
- _ => {},
- }
+ if let ExprKind::MethodCall(method, _, [self_arg], _) = arg.kind {
+ let method_name = &*method.ident.as_str();
+ // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
+ match method_name {
+ "iter" | "iter_mut" => explicit_iter_loop::check(cx, self_arg, arg, method_name),
+ "into_iter" => {
+ explicit_iter_loop::check(cx, self_arg, arg, method_name);
+ explicit_into_iter_loop::check(cx, self_arg, arg);
+ },
+ "next" => {
+ next_loop_linted = iter_next_loop::check(cx, arg, expr);
+ },
+ _ => {},
}
}
ExprKind::Binary(_, e1, e2)
| ExprKind::Assign(e1, e2, _)
| ExprKind::AssignOp(_, e1, e2)
- | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().cloned(), main_loop_id),
+ | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id),
ExprKind::Loop(b, _, _, _) => {
// Break can come from the inner loop so remove them.
absorb_break(&never_loop_block(b, main_loop_id))
body: &'tcx Expr<'_>,
expr: &'tcx Expr<'_>,
) {
+ let arg_expr = match arg.kind {
+ ExprKind::AddrOf(BorrowKind::Ref, _, ref_arg) => ref_arg,
+ ExprKind::MethodCall(method, _, args, _) if args.len() == 1 && method.ident.name == rustc_span::sym::iter => {
+ &args[0]
+ },
+ _ => return,
+ };
if_chain! {
- if let ExprKind::AddrOf(BorrowKind::Ref, _, arg_expr) = arg.kind;
if let PatKind::Binding(.., target, _) = pat.kind;
if let ExprKind::Array([arg_expression]) = arg_expr.kind;
if let ExprKind::Path(ref list_item) = arg_expression.kind;
let attrs = cx.tcx.hir().attrs(item.hir_id());
if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use));
if let Res::Def(DefKind::Mod, id) = path.res;
+ if !id.is_local();
then {
for kid in cx.tcx.item_children(id).iter() {
if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
-use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
-use clippy_utils::{in_constant, is_allowed, is_else_clause, match_def_path, match_var, paths, peel_hir_expr_refs};
+use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
+use clippy_utils::{
+ can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs,
+};
use rustc_ast::util::parser::PREC_POSTFIX;
use rustc_errors::Applicability;
-use rustc_hir::{
- def::Res,
- intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
- Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath,
-};
+use rustc_hir::LangItem::{OptionNone, OptionSome};
+use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
None => return,
};
+ // These two lints will go back and forth with each other.
if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit
&& !is_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
{
return;
}
+ // `map` won't perform any adjustments.
+ if !cx.typeck_results().expr_adjustments(some_expr).is_empty() {
+ return;
+ }
+
if !can_move_expr_to_closure(cx, some_expr) {
return;
}
}
}
-// Checks if the expression can be moved into a closure as is.
-fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
- struct V<'cx, 'tcx> {
- cx: &'cx LateContext<'tcx>,
- make_closure: bool,
- }
- impl Visitor<'tcx> for V<'_, 'tcx> {
- type Map = ErasedMap<'tcx>;
- fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
- NestedVisitorMap::None
- }
-
- fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
- match e.kind {
- ExprKind::Break(..)
- | ExprKind::Continue(_)
- | ExprKind::Ret(_)
- | ExprKind::Yield(..)
- | ExprKind::InlineAsm(_)
- | ExprKind::LlvmInlineAsm(_) => {
- self.make_closure = false;
- },
- // Accessing a field of a local value can only be done if the type isn't
- // partially moved.
- ExprKind::Field(base_expr, _)
- if matches!(
- base_expr.kind,
- ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. }))
- ) && can_partially_move_ty(self.cx, self.cx.typeck_results().expr_ty(base_expr)) =>
- {
- // TODO: check if the local has been partially moved. Assume it has for now.
- self.make_closure = false;
- return;
- }
- _ => (),
- };
- walk_expr(self, e);
- }
- }
-
- let mut v = V { cx, make_closure: true };
- v.visit_expr(expr);
- v.make_closure
-}
-
// Checks whether the expression could be passed as a function, or whether a closure is needed.
// Returns the function to be passed to `map` if it exists.
fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
match pat.kind {
PatKind::Wild => Some(OptionPat::Wild),
PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
- PatKind::Path(QPath::Resolved(None, path))
- if path
- .res
- .opt_def_id()
- .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)) =>
- {
- Some(OptionPat::None)
- },
- PatKind::TupleStruct(QPath::Resolved(None, path), [pattern], _)
- if path
- .res
- .opt_def_id()
- .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_SOME))
- && pat.span.ctxt() == ctxt =>
+ PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None),
+ PatKind::TupleStruct(ref qpath, [pattern], _)
+ if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt =>
{
Some(OptionPat::Some { pattern, ref_count })
},
match expr.kind {
ExprKind::Call(
Expr {
- kind: ExprKind::Path(QPath::Resolved(None, path)),
+ kind: ExprKind::Path(ref qpath),
..
},
[arg],
- ) if ctxt == expr.span.ctxt() => {
- if match_def_path(cx, path.res.opt_def_id()?, &paths::OPTION_SOME) {
- Some(arg)
- } else {
- None
- }
- },
+ ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(arg),
ExprKind::Block(
Block {
stmts: [],
// Checks for the `None` value.
fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
match expr.kind {
- ExprKind::Path(QPath::Resolved(None, path)) => path
- .res
- .opt_def_id()
- .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)),
+ ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
ExprKind::Block(
Block {
stmts: [],
use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::meets_msrv;
use clippy_utils::source::snippet_opt;
+use clippy_utils::{meets_msrv, msrvs};
use if_chain::if_chain;
use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind};
use rustc_errors::Applicability;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{sym, Span};
-const MANUAL_NON_EXHAUSTIVE_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
-
declare_clippy_lint! {
/// **What it does:** Checks for manual implementations of the non-exhaustive pattern.
///
impl EarlyLintPass for ManualNonExhaustive {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
- if !meets_msrv(self.msrv.as_ref(), &MANUAL_NON_EXHAUSTIVE_MSRV) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
return;
}
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{match_qpath, path_to_local_id, paths};
+use clippy_utils::{is_lang_ctor, path_to_local_id};
use if_chain::if_chain;
use rustc_errors::Applicability;
+use rustc_hir::LangItem::{ResultErr, ResultOk};
use rustc_hir::{Expr, ExprKind, PatKind};
use rustc_lint::LintContext;
use rustc_lint::{LateContext, LateLintPass};
let or_expr = &args[1];
if is_ok_wrapping(cx, &args[2]);
if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind;
- if match_qpath(err_path, &paths::RESULT_ERR);
+ if is_lang_ctor(cx, err_path, ResultErr);
if let Some(method_receiver_snippet) = snippet_opt(cx, method_receiver.span);
if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
if let Some(indent) = indent_of(cx, scrutinee.span);
fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
if let ExprKind::Path(ref qpath) = map_expr.kind {
- if match_qpath(qpath, &paths::RESULT_OK) {
+ if is_lang_ctor(cx, qpath, ResultOk) {
return true;
}
}
let body = cx.tcx.hir().body(body_id);
if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
- if match_qpath(ok_path, &paths::RESULT_OK);
+ if is_lang_ctor(cx, ok_path, ResultOk);
then { path_to_local_id(ok_arg, param_id) } else { false }
}
}
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
use clippy_utils::source::snippet;
use clippy_utils::usage::mutated_variables;
-use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, paths};
+use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, msrvs, paths};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_hir::def::Res;
use rustc_span::source_map::Spanned;
use rustc_span::Span;
-const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0);
-
declare_clippy_lint! {
/// **What it does:**
/// Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using
impl<'tcx> LateLintPass<'tcx> for ManualStrip {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) {
return;
}
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::usage::contains_return_break_continue_macro;
-use clippy_utils::{in_constant, match_qpath, path_to_local_id, paths, sugg};
+use clippy_utils::{in_constant, is_lang_ctor, path_to_local_id, sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind};
+use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
+use rustc_hir::{Arm, Expr, ExprKind, PatKind};
use rustc_lint::LintContext;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
}
fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
- fn applicable_or_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
+ fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
if_chain! {
if arms.len() == 2;
if arms.iter().all(|arm| arm.guard.is_none());
- if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)|
+ if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| {
match arm.pat.kind {
- PatKind::Path(ref some_qpath) =>
- match_qpath(some_qpath, &paths::OPTION_NONE),
- PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) =>
- match_qpath(err_qpath, &paths::RESULT_ERR),
+ PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
+ PatKind::TupleStruct(ref qpath, &[pat], _) =>
+ matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr),
_ => false,
}
- );
+ });
let unwrap_arm = &arms[1 - idx];
- if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind;
- if match_qpath(unwrap_qpath, &paths::OPTION_SOME)
- || match_qpath(unwrap_qpath, &paths::RESULT_OK);
+ if let PatKind::TupleStruct(ref qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind;
+ if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind;
if path_to_local_id(unwrap_arm.body, binding_hir_id);
if !contains_return_break_continue_macro(or_arm.body);
} else {
None
};
- if let Some(or_arm) = applicable_or_arm(match_arms);
+ if let Some(or_arm) = applicable_or_arm(cx, match_arms);
if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span);
if let Some(indent) = indent_of(cx, expr.span);
if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some();
then {
let reindented_or_body =
reindent_multiline(or_body_snippet.into(), true, Some(indent));
+
+ let suggestion = if scrutinee.span.from_expansion() {
+ // we don't want parenthesis around macro, e.g. `(some_macro!()).unwrap_or(0)`
+ sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..")
+ }
+ else {
+ sugg::Sugg::hir(cx, scrutinee, "..").maybe_par()
+ };
+
span_lint_and_sugg(
cx,
MANUAL_UNWRAP_OR, expr.span,
"replace with",
format!(
"{}.unwrap_or({})",
- sugg::Sugg::hir(cx, scrutinee, "..").maybe_par(),
+ suggestion,
reindented_or_body,
),
Applicability::MachineApplicable,
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_adjusted, is_trait_method, match_path, match_var, paths, remove_blocks};
+use clippy_utils::{is_adjusted, is_qpath_def_path, is_trait_method, match_var, paths, remove_blocks};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind};
fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
match expr.kind {
ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
- ExprKind::Path(QPath::Resolved(_, path)) => match_path(path, &paths::STD_CONVERT_IDENTITY),
+ ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
_ => false,
}
}
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
use clippy_utils::visitors::LocalUsedVisitor;
use clippy_utils::{
- get_parent_expr, in_macro, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, meets_msrv, path_to_local,
- path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, strip_pat_refs,
+ get_parent_expr, in_macro, is_allowed, is_expn_of, is_lang_ctor, is_refutable, is_wild, meets_msrv, msrvs,
+ path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks,
+ strip_pat_refs,
};
use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::def::{CtorKind, DefKind, Res};
+use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{
self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource,
Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind,
/// **Why is this bad?** It's more concise and clear to just use the proper
/// utility function
///
- /// **Known problems:** None.
+ /// **Known problems:** This will change the drop order for the matched type. Both `if let` and
+ /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the
+ /// value before entering the block. For most types this change will not matter, but for a few
+ /// types this will not be an acceptable change (e.g. locks). See the
+ /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
+ /// drop order.
///
/// **Example:**
///
MATCH_SAME_ARMS,
]);
-const MATCH_LIKE_MATCHES_MACRO_MSRV: RustcVersion = RustcVersion::new(1, 42, 0);
-
impl<'tcx> LateLintPass<'tcx> for Matches {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
redundant_pattern_match::check(cx, expr);
- if meets_msrv(self.msrv.as_ref(), &MATCH_LIKE_MATCHES_MACRO_MSRV) {
+ if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) {
if !check_match_like_matches(cx, expr) {
lint_match_arms(cx, expr);
}
let (msg, sugg) = if_chain! {
if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
- if let Some(trait_id) = cx.tcx.lang_items().structural_peq_trait();
- if ty.is_integral() || ty.is_char() || ty.is_str() || implements_trait(cx, ty, trait_id, &[]);
+ if let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait();
+ if let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait();
+ if ty.is_integral() || ty.is_char() || ty.is_str()
+ || (implements_trait(cx, ty, spe_trait_id, &[])
+ && implements_trait(cx, ty, pe_trait_id, &[ty.into()]));
then {
// scrutinee derives PartialEq and the pattern is a constant.
let pat_ref_count = match pat.kind {
Applicability::MaybeIncorrect,
),
variants => {
- let mut suggestions: Vec<_> = variants.iter().cloned().map(format_suggestion).collect();
+ let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
let message = if adt_def.is_variant_list_non_exhaustive() {
suggestions.push("_".into());
"wildcard matches known variants and will also match future added variants"
fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
- let arm_ref: Option<BindingAnnotation> = if is_none_arm(&arms[0]) {
- is_ref_some_arm(&arms[1])
- } else if is_none_arm(&arms[1]) {
- is_ref_some_arm(&arms[0])
+ let arm_ref: Option<BindingAnnotation> = if is_none_arm(cx, &arms[0]) {
+ is_ref_some_arm(cx, &arms[1])
+ } else if is_none_arm(cx, &arms[1]) {
+ is_ref_some_arm(cx, &arms[0])
} else {
None
};
/// Gets all arms that are unbounded `PatRange`s.
fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec<SpannedRange<Constant>> {
arms.iter()
- .flat_map(|arm| {
+ .filter_map(|arm| {
if let Arm { pat, guard: None, .. } = *arm {
if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
let lhs = match lhs {
}
// Checks if arm has the form `None => None`
-fn is_none_arm(arm: &Arm<'_>) -> bool {
- matches!(arm.pat.kind, PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE))
+fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
+ matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
}
// Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
-fn is_ref_some_arm(arm: &Arm<'_>) -> Option<BindingAnnotation> {
+fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotation> {
if_chain! {
- if let PatKind::TupleStruct(ref path, pats, _) = arm.pat.kind;
- if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME);
+ if let PatKind::TupleStruct(ref qpath, pats, _) = arm.pat.kind;
+ if is_lang_ctor(cx, qpath, OptionSome);
if let PatKind::Binding(rb, .., ident, _) = pats[0].kind;
if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind;
if let ExprKind::Path(ref some_path) = e.kind;
- if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1;
+ if is_lang_ctor(cx, some_path, OptionSome) && args.len() == 1;
if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind;
if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
then {
mod redundant_pattern_match {
use super::REDUNDANT_PATTERN_MATCHING;
use clippy_utils::diagnostics::span_lint_and_then;
- use clippy_utils::source::snippet;
- use clippy_utils::{is_trait_method, match_qpath, paths};
+ use clippy_utils::source::{snippet, snippet_with_applicability};
+ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
+ use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
- use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath};
+ use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
+ use rustc_hir::{
+ intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
+ Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, PatKind, QPath,
+ };
use rustc_lint::LateContext;
+ use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
use rustc_span::sym;
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Match(op, arms, ref match_source) = &expr.kind {
match match_source {
MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms),
- MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"),
- MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"),
+ MatchSource::IfLetDesugar { contains_else_clause } => {
+ find_sugg_for_if_let(cx, expr, op, &arms[0], "if", *contains_else_clause)
+ },
+ MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, &arms[0], "while", false),
_ => {},
}
}
}
+ /// Checks if the drop order for a type matters. Some std types implement drop solely to
+ /// deallocate memory. For these types, and composites containing them, changing the drop order
+ /// won't result in any observable side effects.
+ fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ if !ty.needs_drop(cx.tcx, cx.param_env) {
+ false
+ } else if !cx
+ .tcx
+ .lang_items()
+ .drop_trait()
+ .map_or(false, |id| implements_trait(cx, ty, id, &[]))
+ {
+ // This type doesn't implement drop, so no side effects here.
+ // Check if any component type has any.
+ match ty.kind() {
+ ty::Tuple(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop(cx, ty)),
+ ty::Array(ty, _) => type_needs_ordered_drop(cx, ty),
+ ty::Adt(adt, subs) => adt
+ .all_fields()
+ .map(|f| f.ty(cx.tcx, subs))
+ .any(|ty| type_needs_ordered_drop(cx, ty)),
+ _ => true,
+ }
+ }
+ // Check for std types which implement drop, but only for memory allocation.
+ else if is_type_diagnostic_item(cx, ty, sym::vec_type)
+ || is_type_lang_item(cx, ty, LangItem::OwnedBox)
+ || is_type_diagnostic_item(cx, ty, sym::Rc)
+ || is_type_diagnostic_item(cx, ty, sym::Arc)
+ || is_type_diagnostic_item(cx, ty, sym::cstring_type)
+ || match_type(cx, ty, &paths::BTREEMAP)
+ || match_type(cx, ty, &paths::LINKED_LIST)
+ || match_type(cx, ty, &paths::WEAK_RC)
+ || match_type(cx, ty, &paths::WEAK_ARC)
+ {
+ // Check all of the generic arguments.
+ if let ty::Adt(_, subs) = ty.kind() {
+ subs.types().any(|ty| type_needs_ordered_drop(cx, ty))
+ } else {
+ true
+ }
+ } else {
+ true
+ }
+ }
+
+ // Extract the generic arguments out of a type
+ fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
+ if_chain! {
+ if let ty::Adt(_, subs) = ty.kind();
+ if let Some(sub) = subs.get(index);
+ if let GenericArgKind::Type(sub_ty) = sub.unpack();
+ then {
+ Some(sub_ty)
+ } else {
+ None
+ }
+ }
+ }
+
+ // Checks if there are any temporaries created in the given expression for which drop order
+ // matters.
+ fn temporaries_need_ordered_drop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+ struct V<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ res: bool,
+ }
+ impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
+ type Map = ErasedMap<'tcx>;
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+ match expr.kind {
+ // Taking the reference of a value leaves a temporary
+ // e.g. In `&String::new()` the string is a temporary value.
+ // Remaining fields are temporary values
+ // e.g. In `(String::new(), 0).1` the string is a temporary value.
+ ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => {
+ if !matches!(expr.kind, ExprKind::Path(_)) {
+ if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
+ self.res = true;
+ } else {
+ self.visit_expr(expr);
+ }
+ }
+ },
+ // the base type is alway taken by reference.
+ // e.g. In `(vec![0])[0]` the vector is a temporary value.
+ ExprKind::Index(base, index) => {
+ if !matches!(base.kind, ExprKind::Path(_)) {
+ if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
+ self.res = true;
+ } else {
+ self.visit_expr(base);
+ }
+ }
+ self.visit_expr(index);
+ },
+ // Method calls can take self by reference.
+ // e.g. In `String::new().len()` the string is a temporary value.
+ ExprKind::MethodCall(_, _, [self_arg, args @ ..], _) => {
+ if !matches!(self_arg.kind, ExprKind::Path(_)) {
+ let self_by_ref = self
+ .cx
+ .typeck_results()
+ .type_dependent_def_id(expr.hir_id)
+ .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref());
+ if self_by_ref
+ && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg))
+ {
+ self.res = true;
+ } else {
+ self.visit_expr(self_arg)
+ }
+ }
+ args.iter().for_each(|arg| self.visit_expr(arg));
+ },
+ // Either explicitly drops values, or changes control flow.
+ ExprKind::DropTemps(_)
+ | ExprKind::Ret(_)
+ | ExprKind::Break(..)
+ | ExprKind::Yield(..)
+ | ExprKind::Block(Block { expr: None, .. }, _)
+ | ExprKind::Loop(..) => (),
+
+ // Only consider the final expression.
+ ExprKind::Block(Block { expr: Some(expr), .. }, _) => self.visit_expr(expr),
+
+ _ => walk_expr(self, expr),
+ }
+ }
+ }
+
+ let mut v = V { cx, res: false };
+ v.visit_expr(expr);
+ v.res
+ }
+
fn find_sugg_for_if_let<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
- op: &Expr<'_>,
- arms: &[Arm<'_>],
+ op: &'tcx Expr<'tcx>,
+ arm: &Arm<'_>,
keyword: &'static str,
+ has_else: bool,
) {
// also look inside refs
- let mut kind = &arms[0].pat.kind;
+ let mut kind = &arm.pat.kind;
// if we have &None for example, peel it so we can detect "if let None = x"
if let PatKind::Ref(inner, _mutability) = kind {
kind = &inner.kind;
}
- let good_method = match kind {
- PatKind::TupleStruct(ref path, patterns, _) if patterns.len() == 1 => {
- if let PatKind::Wild = patterns[0].kind {
- if match_qpath(path, &paths::RESULT_OK) {
- "is_ok()"
- } else if match_qpath(path, &paths::RESULT_ERR) {
- "is_err()"
- } else if match_qpath(path, &paths::OPTION_SOME) {
- "is_some()"
- } else if match_qpath(path, &paths::POLL_READY) {
- "is_ready()"
- } else if match_qpath(path, &paths::IPADDR_V4) {
- "is_ipv4()"
- } else if match_qpath(path, &paths::IPADDR_V6) {
- "is_ipv6()"
+ let op_ty = cx.typeck_results().expr_ty(op);
+ // Determine which function should be used, and the type contained by the corresponding
+ // variant.
+ let (good_method, inner_ty) = match kind {
+ PatKind::TupleStruct(ref path, [sub_pat], _) => {
+ if let PatKind::Wild = sub_pat.kind {
+ if is_lang_ctor(cx, path, ResultOk) {
+ ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))
+ } else if is_lang_ctor(cx, path, ResultErr) {
+ ("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty))
+ } else if is_lang_ctor(cx, path, OptionSome) {
+ ("is_some()", op_ty)
+ } else if is_lang_ctor(cx, path, PollReady) {
+ ("is_ready()", op_ty)
+ } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V4) {
+ ("is_ipv4()", op_ty)
+ } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V6) {
+ ("is_ipv6()", op_ty)
} else {
return;
}
}
},
PatKind::Path(ref path) => {
- if match_qpath(path, &paths::OPTION_NONE) {
+ let method = if is_lang_ctor(cx, path, OptionNone) {
"is_none()"
- } else if match_qpath(path, &paths::POLL_PENDING) {
+ } else if is_lang_ctor(cx, path, PollPending) {
"is_pending()"
} else {
return;
- }
+ };
+ // `None` and `Pending` don't have an inner type.
+ (method, cx.tcx.types.unit)
},
_ => return,
};
+ // If this is the last expression in a block or there is an else clause then the whole
+ // type needs to be considered, not just the inner type of the branch being matched on.
+ // Note the last expression in a block is dropped after all local bindings.
+ let check_ty = if has_else
+ || (keyword == "if" && matches!(cx.tcx.hir().parent_iter(expr.hir_id).next(), Some((_, Node::Block(..)))))
+ {
+ op_ty
+ } else {
+ inner_ty
+ };
+
+ // All temporaries created in the scrutinee expression are dropped at the same time as the
+ // scrutinee would be, so they have to be considered as well.
+ // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
+ // for the duration if body.
+ let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, op);
+
// check that `while_let_on_iterator` lint does not trigger
if_chain! {
if keyword == "while";
span_lint_and_then(
cx,
REDUNDANT_PATTERN_MATCHING,
- arms[0].pat.span,
+ arm.pat.span,
&format!("redundant pattern matching, consider using `{}`", good_method),
|diag| {
// while let ... = ... { ... }
// while let ... = ... { ... }
// ^^^^^^^^^^^^^^^^^^^
let span = expr_span.until(op_span.shrink_to_hi());
- diag.span_suggestion(
- span,
- "try this",
- format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method),
- Applicability::MachineApplicable, // snippet
- );
+
+ let mut app = if needs_drop {
+ Applicability::MaybeIncorrect
+ } else {
+ Applicability::MachineApplicable
+ };
+ let sugg = snippet_with_applicability(cx, op_span, "_", &mut app);
+
+ diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app);
+
+ if needs_drop {
+ diag.note("this will change drop order of the result, as well as all temporaries");
+ diag.note("add `#[allow(clippy::redundant_pattern_matching)]` if this is important");
+ }
},
);
}
) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
find_good_method_for_match(
+ cx,
arms,
path_left,
path_right,
)
.or_else(|| {
find_good_method_for_match(
+ cx,
arms,
path_left,
path_right,
{
if let PatKind::Wild = patterns[0].kind {
find_good_method_for_match(
+ cx,
arms,
path_left,
path_right,
)
.or_else(|| {
find_good_method_for_match(
+ cx,
arms,
path_left,
path_right,
}
}
+ #[allow(clippy::too_many_arguments)]
fn find_good_method_for_match<'a>(
+ cx: &LateContext<'_>,
arms: &[Arm<'_>],
path_left: &QPath<'_>,
path_right: &QPath<'_>,
should_be_left: &'a str,
should_be_right: &'a str,
) -> Option<&'a str> {
- let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) {
+ let body_node_pair = if is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_left)
+ && is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_right)
+ {
(&(*arms[0].body).kind, &(*arms[1].body).kind)
- } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) {
+ } else if is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_left)
+ && is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_right)
+ {
(&(*arms[1].body).kind, &(*arms[0].body).kind)
} else {
return None;
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::is_diagnostic_assoc_item;
use clippy_utils::source::{snippet, snippet_with_applicability};
-use clippy_utils::{in_macro, match_def_path, match_qpath, meets_msrv, paths};
+use clippy_utils::{in_macro, is_diag_trait_item, is_lang_ctor, match_def_path, meets_msrv, msrvs, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
+use rustc_hir::LangItem::OptionNone;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
if let ExprKind::Path(ref replacement_qpath) = src.kind {
// Check that second argument is `Option::None`
- if match_qpath(replacement_qpath, &paths::OPTION_NONE) {
+ if is_lang_ctor(cx, replacement_qpath, OptionNone) {
// Since this is a late pass (already type-checked),
// and we already know that the second argument is an
// `Option`, we do not need to check the first
sym::BinaryHeap,
];
- if std_types_symbols
- .iter()
- .any(|symbol| is_diagnostic_assoc_item(cx, def_id, *symbol))
- {
- if let QPath::TypeRelative(_, method) = path {
- if method.ident.name == sym::new {
- return true;
+ if let QPath::TypeRelative(_, method) = path {
+ if method.ident.name == sym::new {
+ if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
+ if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
+ return std_types_symbols
+ .iter()
+ .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did));
+ }
}
}
}
-
false
}
if !in_external_macro(cx.tcx.sess, expr_span);
if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
- if is_diagnostic_assoc_item(cx, repl_def_id, sym::Default)
+ if is_diag_trait_item(cx, repl_def_id, sym::Default)
|| is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
then {
}
}
-const MEM_REPLACE_WITH_DEFAULT_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
-
pub struct MemReplace {
msrv: Option<RustcVersion>,
}
then {
check_replace_option_with_none(cx, src, dest, expr.span);
check_replace_with_uninit(cx, src, dest, expr.span);
- if meets_msrv(self.msrv.as_ref(), &MEM_REPLACE_WITH_DEFAULT_MSRV) {
+ if meets_msrv(self.msrv.as_ref(), &msrvs::MEM_TAKE) {
check_replace_with_default(cx, src, dest, expr.span);
}
}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::{get_iterator_item_ty, is_copy};
+use clippy_utils::{is_trait_method, meets_msrv, msrvs};
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_semver::RustcVersion;
+use rustc_span::{sym, Span};
+
+use super::CLONED_INSTEAD_OF_COPIED;
+
+pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option<&RustcVersion>) {
+ let recv_ty = cx.typeck_results().expr_ty_adjusted(recv);
+ let inner_ty = match recv_ty.kind() {
+ // `Option<T>` -> `T`
+ ty::Adt(adt, subst)
+ if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) && meets_msrv(msrv, &msrvs::OPTION_COPIED) =>
+ {
+ subst.type_at(0)
+ },
+ _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) => {
+ match get_iterator_item_ty(cx, recv_ty) {
+ // <T as Iterator>::Item
+ Some(ty) => ty,
+ _ => return,
+ }
+ },
+ _ => return,
+ };
+ match inner_ty.kind() {
+ // &T where T: Copy
+ ty::Ref(_, ty, _) if is_copy(cx, ty) => {},
+ _ => return,
+ };
+ span_lint_and_sugg(
+ cx,
+ CLONED_INSTEAD_OF_COPIED,
+ span,
+ "used `cloned` where `copied` could be used instead",
+ "try",
+ "copied".into(),
+ Applicability::MachineApplicable,
+ )
+}
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::is_trait_method;
-use rustc_hir as hir;
-use rustc_lint::LateContext;
-use rustc_span::sym;
-
-use super::FILTER_MAP;
-
-/// lint use of `filter().flat_map()` for `Iterators`
-pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
- // lint if caller of `.filter().flat_map()` is an Iterator
- if is_trait_method(cx, expr, sym::Iterator) {
- let msg = "called `filter(..).flat_map(..)` on an `Iterator`";
- let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
- and filtering by returning `iter::empty()`";
- span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
- }
-}
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::is_trait_method;
-use rustc_hir as hir;
-use rustc_lint::LateContext;
-use rustc_span::sym;
-
-use super::FILTER_MAP;
-
-/// lint use of `filter_map().flat_map()` for `Iterators`
-pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
- // lint if caller of `.filter_map().flat_map()` is an Iterator
- if is_trait_method(cx, expr, sym::Iterator) {
- let msg = "called `filter_map(..).flat_map(..)` on an `Iterator`";
- let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
- and filtering by returning `iter::empty()`";
- span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
- }
-}
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths};
+use clippy_utils::{is_expr_path_def_path, is_trait_method, path_to_local_id, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
}
}
- if_chain! {
- if let hir::ExprKind::Path(ref qpath) = filter_map_arg.kind;
-
- if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY);
-
- then {
- apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`");
- }
+ if is_expr_path_def_path(cx, filter_map_arg, &paths::CONVERT_IDENTITY) {
+ apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`");
}
}
}
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::is_trait_method;
-use rustc_hir as hir;
-use rustc_lint::LateContext;
-use rustc_span::sym;
-
-use super::FILTER_MAP;
-
-/// lint use of `filter_map().map()` for `Iterators`
-pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
- // lint if caller of `.filter_map().map()` is an Iterator
- if is_trait_method(cx, expr, sym::Iterator) {
- let msg = "called `filter_map(..).map(..)` on an `Iterator`";
- let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead";
- span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
- }
-}
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::source::snippet;
-use clippy_utils::{is_trait_method, meets_msrv};
+use clippy_utils::{is_trait_method, meets_msrv, msrvs};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use super::FILTER_MAP_NEXT;
-const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0);
-
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
msrv: Option<&RustcVersion>,
) {
if is_trait_method(cx, expr, sym::Iterator) {
- if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) {
+ if !meets_msrv(msrv, &msrvs::ITERATOR_FIND_MAP) {
return;
}
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{is_trait_method, match_qpath, paths};
+use clippy_utils::{is_expr_path_def_path, is_trait_method, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
flat_map_span: Span,
) {
if is_trait_method(cx, expr, sym::Iterator) {
- let arg_node = &flat_map_arg.kind;
-
let apply_lint = |message: &str| {
span_lint_and_sugg(
cx,
};
if_chain! {
- if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node;
- let body = cx.tcx.hir().body(*body_id);
+ if let hir::ExprKind::Closure(_, _, body_id, _, _) = flat_map_arg.kind;
+ let body = cx.tcx.hir().body(body_id);
if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind;
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = body.value.kind;
}
}
- if_chain! {
- if let hir::ExprKind::Path(ref qpath) = arg_node;
-
- if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY);
-
- then {
- apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`");
- }
+ if is_expr_path_def_path(cx, flat_map_arg, &paths::CONVERT_IDENTITY) {
+ apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`");
}
}
}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::{source_map::Span, sym};
+
+use super::FLAT_MAP_OPTION;
+use clippy_utils::ty::is_type_diagnostic_item;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) {
+ if !is_trait_method(cx, expr, sym::Iterator) {
+ return;
+ }
+ let arg_ty = cx.typeck_results().expr_ty_adjusted(arg);
+ let sig = match arg_ty.kind() {
+ ty::Closure(_, substs) => substs.as_closure().sig(),
+ _ if arg_ty.is_fn() => arg_ty.fn_sig(cx.tcx),
+ _ => return,
+ };
+ if !is_type_diagnostic_item(cx, sig.output().skip_binder(), sym::option_type) {
+ return;
+ }
+ span_lint_and_sugg(
+ cx,
+ FLAT_MAP_OPTION,
+ span,
+ "used `flat_map` where `filter_map` could be used instead",
+ "try",
+ "filter_map".into(),
+ Applicability::MachineApplicable,
+ )
+}
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::implements_trait;
-use clippy_utils::{get_trait_def_id, match_qpath, paths, sugg};
+use clippy_utils::{is_expr_path_def_path, paths, sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
-use rustc_hir::ExprKind;
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty::Ty;
use rustc_span::sym;
use super::FROM_ITER_INSTEAD_OF_COLLECT;
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func_kind: &ExprKind<'_>) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func: &hir::Expr<'_>) {
if_chain! {
- if let hir::ExprKind::Path(path) = func_kind;
- if match_qpath(path, &["from_iter"]);
+ if is_expr_path_def_path(cx, func, &paths::FROM_ITERATOR_METHOD);
let ty = cx.typeck_results().expr_ty(expr);
let arg_ty = cx.typeck_results().expr_ty(&args[0]);
- if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR);
if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
- if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]);
+ if implements_trait(cx, arg_ty, iter_id, &[]);
then {
// `expr` implements `FromIterator` trait
let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par();
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::{is_diag_item_method, is_diag_trait_item};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
-use rustc_hir::ExprKind;
use rustc_lint::LateContext;
use rustc_middle::ty::TyS;
-use rustc_span::symbol::Symbol;
+use rustc_span::{sym, Span};
use super::IMPLICIT_CLONE;
-use clippy_utils::is_diagnostic_assoc_item;
-pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, trait_diagnostic: Symbol) {
+pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, span: Span) {
if_chain! {
- if let ExprKind::MethodCall(method_path, _, [arg], _) = &expr.kind;
+ if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+ if match method_name {
+ "to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr),
+ "to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned),
+ "to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path),
+ "to_vec" => cx.tcx.impl_of_method(method_def_id)
+ .map(|impl_did| Some(impl_did) == cx.tcx.lang_items().slice_alloc_impl())
+ == Some(true),
+ _ => false,
+ };
let return_type = cx.typeck_results().expr_ty(expr);
- let input_type = cx.typeck_results().expr_ty(arg).peel_refs();
- if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+ let input_type = cx.typeck_results().expr_ty(recv).peel_refs();
if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did));
if TyS::same_type(return_type, input_type);
- if is_diagnostic_assoc_item(cx, expr_def_id, trait_diagnostic);
then {
span_lint_and_sugg(
- cx,IMPLICIT_CLONE,method_path.ident.span,
- &format!("implicitly cloning a `{}` by calling `{}` on its dereferenced type", ty_name, method_path.ident.name),
+ cx,
+ IMPLICIT_CLONE,
+ span,
+ &format!("implicitly cloning a `{}` by calling `{}` on its dereferenced type", ty_name, method_name),
"consider using",
"clone".to_string(),
Applicability::MachineApplicable
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::match_qpath;
+use clippy_utils::is_qpath_def_path;
use clippy_utils::source::snippet_with_applicability;
use if_chain::if_chain;
use rustc_ast::ast;
// `std::T::MAX` `std::T::MIN` constants
if let hir::ExprKind::Path(path) = &expr.kind {
- if match_qpath(path, &["core", &ty_str, "MAX"][..]) {
+ if is_qpath_def_path(cx, path, expr.hir_id, &["core", &ty_str, "MAX"][..]) {
return Some(MinMax::Max);
}
- if match_qpath(path, &["core", &ty_str, "MIN"][..]) {
+ if is_qpath_def_path(cx, path, expr.hir_id, &["core", &ty_str, "MIN"][..]) {
return Some(MinMax::Min);
}
}
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::meets_msrv;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::usage::mutated_variables;
+use clippy_utils::{meets_msrv, msrvs};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use super::MAP_UNWRAP_OR;
-const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0);
-
/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
/// Return true if lint triggered
pub(super) fn check<'tcx>(
unwrap_arg: &'tcx hir::Expr<'_>,
msrv: Option<&RustcVersion>,
) -> bool {
- if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) {
- return false;
- }
// lint if the caller of `map()` is an `Option`
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type);
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::result_type);
+ if is_result && !meets_msrv(msrv, &msrvs::RESULT_MAP_OR_ELSE) {
+ return false;
+ }
+
if is_option || is_result {
// Don't make a suggestion that may fail to compile due to mutably borrowing
// the same variable twice.
mod chars_next_cmp_with_unwrap;
mod clone_on_copy;
mod clone_on_ref_ptr;
+mod cloned_instead_of_copied;
mod expect_fun_call;
mod expect_used;
mod filetype_is_file;
-mod filter_flat_map;
mod filter_map;
-mod filter_map_flat_map;
mod filter_map_identity;
-mod filter_map_map;
mod filter_map_next;
mod filter_next;
mod flat_map_identity;
+mod flat_map_option;
mod from_iter_instead_of_collect;
mod get_unwrap;
mod implicit_clone;
use rustc_span::{sym, Span};
use rustc_typeck::hir_ty_to_ty;
+declare_clippy_lint! {
+ /// **What it does:** Checks for usages of `cloned()` on an `Iterator` or `Option` where
+ /// `copied()` could be used instead.
+ ///
+ /// **Why is this bad?** `copied()` is better because it guarantees that the type being cloned
+ /// implements `Copy`.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// [1, 2, 3].iter().cloned();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// [1, 2, 3].iter().copied();
+ /// ```
+ pub CLONED_INSTEAD_OF_COPIED,
+ pedantic,
+ "used `cloned` where `copied` could be used instead"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
+ /// used instead.
+ ///
+ /// **Why is this bad?** When applicable, `filter_map()` is more clear since it shows that
+ /// `Option` is used to produce 0 or 1 items.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
+ /// ```
+ pub FLAT_MAP_OPTION,
+ pedantic,
+ "used `flat_map` where `filter_map` could be used instead"
+}
+
declare_clippy_lint! {
/// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s.
///
"using combinations of `flatten` and `map` which can usually be written as a single method call"
}
-declare_clippy_lint! {
- /// **What it does:** Checks for usage of `_.filter(_).map(_)`,
- /// `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar.
- ///
- /// **Why is this bad?** Readability, this can be written more concisely as
- /// `_.filter_map(_)`.
- ///
- /// **Known problems:** Often requires a condition + Option/Iterator creation
- /// inside the closure.
- ///
- /// **Example:**
- /// ```rust
- /// let vec = vec![1];
- ///
- /// // Bad
- /// vec.iter().filter(|x| **x == 0).map(|x| *x * 2);
- ///
- /// // Good
- /// vec.iter().filter_map(|x| if *x == 0 {
- /// Some(*x * 2)
- /// } else {
- /// None
- /// });
- /// ```
- pub FILTER_MAP,
- pedantic,
- "using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call"
-}
-
declare_clippy_lint! {
/// **What it does:** Checks for usage of `_.filter(_).map(_)` that can be written more simply
/// as `filter_map(_)`.
CLONE_ON_COPY,
CLONE_ON_REF_PTR,
CLONE_DOUBLE_REF,
+ CLONED_INSTEAD_OF_COPIED,
+ FLAT_MAP_OPTION,
INEFFICIENT_TO_STRING,
NEW_RET_NO_SELF,
SINGLE_CHAR_PATTERN,
SEARCH_IS_SOME,
FILTER_NEXT,
SKIP_WHILE_NEXT,
- FILTER_MAP,
FILTER_MAP_IDENTITY,
MANUAL_FILTER_MAP,
MANUAL_FIND_MAP,
match expr.kind {
hir::ExprKind::Call(func, args) => {
- from_iter_instead_of_collect::check(cx, expr, args, &func.kind);
+ from_iter_instead_of_collect::check(cx, expr, args, func);
},
hir::ExprKind::MethodCall(method_call, ref method_span, args, _) => {
or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
+ ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv),
("collect", []) => match method_call!(recv) {
Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2),
Some(("map", [m_recv, m_arg], _)) => {
unnecessary_filter_map::check(cx, expr, arg);
filter_map_identity::check(cx, expr, arg, span);
},
- ("flat_map", [flm_arg]) => match method_call!(recv) {
- Some(("filter", [_, _], _)) => filter_flat_map::check(cx, expr),
- Some(("filter_map", [_, _], _)) => filter_map_flat_map::check(cx, expr),
- _ => flat_map_identity::check(cx, expr, flm_arg, span),
+ ("flat_map", [arg]) => {
+ flat_map_identity::check(cx, expr, arg, span);
+ flat_map_option::check(cx, expr, arg, span);
},
("flatten", []) => {
if let Some(("map", [recv, map_arg], _)) = method_call!(recv) {
("filter", [f_arg]) => {
filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false)
},
- ("filter_map", [_]) => filter_map_map::check(cx, expr),
("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true),
_ => {},
}
}
},
("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
- ("to_os_string", []) => implicit_clone::check(cx, expr, sym::OsStr),
- ("to_owned", []) => implicit_clone::check(cx, expr, sym::ToOwned),
- ("to_path_buf", []) => implicit_clone::check(cx, expr, sym::Path),
- ("to_vec", []) => implicit_clone::check(cx, expr, sym::slice),
+ ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
+ implicit_clone::check(cx, name, expr, recv, span);
+ },
("unwrap", []) => match method_call!(recv) {
Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false),
Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true),
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks};
+use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, remove_blocks};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use super::OPTION_AS_REF_DEREF;
-const OPTION_AS_REF_DEREF_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
-
/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
is_mut: bool,
msrv: Option<&RustcVersion>,
) {
- if !meets_msrv(msrv, &OPTION_AS_REF_DEREF_MSRV) {
+ if !meets_msrv(msrv, &msrvs::OPTION_AS_DEREF) {
return;
}
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_lang_ctor;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{match_qpath, paths};
use rustc_errors::Applicability;
use rustc_hir as hir;
+use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
let (lint_name, msg, instead, hint) = {
let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind {
- match_qpath(qpath, &paths::OPTION_NONE)
+ is_lang_ctor(cx, qpath, OptionNone)
} else {
return;
};
}
let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind {
- match_qpath(qpath, &paths::OPTION_SOME)
+ is_lang_ctor(cx, qpath, OptionSome)
} else {
false
};
use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{match_def_path, match_qpath, paths};
+use clippy_utils::{is_expr_path_def_path, match_def_path, paths};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::LateContext;
if_chain! {
if let hir::ExprKind::Call(callee, args) = recv.kind;
if args.is_empty();
- if let hir::ExprKind::Path(ref path) = callee.kind;
- if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT);
+ if is_expr_path_def_path(cx, callee, &paths::MEM_MAYBEUNINIT_UNINIT);
if !is_maybe_uninit_ty_valid(cx, cx.typeck_results().expr_ty_adjusted(expr));
then {
span_lint(
use clippy_utils::diagnostics::span_lint;
use clippy_utils::usage::mutated_variables;
-use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths};
+use clippy_utils::{is_lang_ctor, is_trait_method, path_to_local_id};
use rustc_hir as hir;
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
use rustc_span::sym;
match &expr.kind {
hir::ExprKind::Call(func, args) => {
if let hir::ExprKind::Path(ref path) = func.kind {
- if match_qpath(path, &paths::OPTION_SOME) {
+ if is_lang_ctor(cx, path, OptionSome) {
if path_to_local_id(&args[0], arg_id) {
return (false, false);
}
return (true, false);
}
- // We don't know. It might do anything.
- return (true, true);
}
(true, true)
},
let else_check = check_expression(cx, arg_id, else_arm);
(if_check.0 | else_check.0, if_check.1 | else_check.1)
},
- hir::ExprKind::Path(path) if match_qpath(path, &paths::OPTION_NONE) => (false, true),
+ hir::ExprKind::Path(path) if is_lang_ctor(cx, path, OptionNone) => (false, true),
_ => (true, true),
}
}
ty::Slice(_) => true,
ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type),
- ty::Array(_, size) => size
- .try_eval_usize(cx.tcx, cx.param_env)
- .map_or(false, |size| size < 32),
+ ty::Array(_, size) => size.try_eval_usize(cx.tcx, cx.param_env).is_some(),
ty::Ref(_, inner, _) => may_slice(cx, inner),
_ => false,
}
.iter()
.all(|conv| conv.check(cx, self_ty, item_name, implements_trait, is_trait_item))
}) {
+ // don't lint if it implements a trait but not willing to check `Copy` types conventions (see #7032)
+ if implements_trait
+ && !conventions
+ .iter()
+ .any(|conv| matches!(conv, Convention::IsSelfTypeCopy(_)))
+ {
+ return;
+ }
if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
let suggestion = {
if conventions.len() > 1 {
use crate::consts::{constant, Constant};
use clippy_utils::sugg::Sugg;
use clippy_utils::{
- get_item_name, get_parent_expr, higher, in_constant, is_diagnostic_assoc_item, is_integer_const, iter_input_pats,
- last_path_segment, match_qpath, unsext, SpanlessEq,
+ expr_path_res, get_item_name, get_parent_expr, higher, in_constant, is_diag_trait_item, is_integer_const,
+ iter_input_pats, last_path_segment, match_any_def_paths, paths, unsext, SpanlessEq,
};
declare_clippy_lint! {
ExprKind::MethodCall(.., args, _) if args.len() == 1 => {
if_chain!(
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
- if is_diagnostic_assoc_item(cx, expr_def_id, sym::ToString)
- || is_diagnostic_assoc_item(cx, expr_def_id, sym::ToOwned);
+ if is_diag_trait_item(cx, expr_def_id, sym::ToString)
+ || is_diag_trait_item(cx, expr_def_id, sym::ToOwned);
then {
(cx.typeck_results().expr_ty(&args[0]), snippet(cx, args[0].span, ".."))
} else {
}
)
},
- ExprKind::Call(path, v) if v.len() == 1 => {
- if let ExprKind::Path(ref path) = path.kind {
- if match_qpath(path, &["String", "from_str"]) || match_qpath(path, &["String", "from"]) {
- (cx.typeck_results().expr_ty(&v[0]), snippet(cx, v[0].span, ".."))
- } else {
- return;
- }
+ ExprKind::Call(path, [arg]) => {
+ if expr_path_res(cx, path)
+ .opt_def_id()
+ .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM]))
+ .is_some()
+ {
+ (cx.typeck_results().expr_ty(arg), snippet(cx, arg.span, ".."))
} else {
return;
}
use clippy_utils::diagnostics::span_lint;
use clippy_utils::qualify_min_const_fn::is_min_const_fn;
use clippy_utils::ty::has_drop;
-use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, trait_ref_of_method};
+use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, msrvs, trait_ref_of_method};
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
use rustc_span::Span;
use rustc_typeck::hir_ty_to_ty;
-const MISSING_CONST_FOR_FN_MSRV: RustcVersion = RustcVersion::new(1, 37, 0);
-
declare_clippy_lint! {
/// **What it does:**
///
span: Span,
hir_id: HirId,
) {
- if !meets_msrv(self.msrv.as_ref(), &MISSING_CONST_FOR_FN_MSRV) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::CONST_IF_MATCH) {
return;
}
let mir = cx.tcx.optimized_mir(def_id);
- if let Err((span, err)) = is_min_const_fn(cx.tcx, mir) {
- if rustc_mir::const_eval::is_min_const_fn(cx.tcx, def_id.to_def_id()) {
+ if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) {
+ if cx.tcx.is_const_fn_raw(def_id.to_def_id()) {
cx.tcx.sess.span_err(span, &err);
}
} else {
use if_chain::if_chain;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{self};
+use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use std::fmt::Display;
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
-use clippy_utils::{is_expn_of, parent_node_is_if_expr};
+use clippy_utils::{is_else_clause, is_expn_of};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
snip = snip.make_return();
}
- if parent_node_is_if_expr(e, cx) {
+ if is_else_clause(cx.tcx, e) {
snip = snip.blockify()
}
spans.extend(
deref_span
.iter()
- .cloned()
+ .copied()
.map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))),
);
spans.sort_by_key(|&(span, _)| span);
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{differing_macro_contexts, is_ok_ctor, is_some_ctor, meets_msrv};
+use clippy_utils::{differing_macro_contexts, is_lang_ctor};
use if_chain::if_chain;
use rustc_errors::Applicability;
+use rustc_hir::LangItem::{OptionSome, ResultOk};
use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_semver::RustcVersion;
-use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! {
"Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result<T, E>`."
}
-const NEEDLESS_QUESTION_MARK_RESULT_MSRV: RustcVersion = RustcVersion::new(1, 13, 0);
-const NEEDLESS_QUESTION_MARK_OPTION_MSRV: RustcVersion = RustcVersion::new(1, 22, 0);
-
-pub struct NeedlessQuestionMark {
- msrv: Option<RustcVersion>,
-}
-
-impl NeedlessQuestionMark {
- #[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
- Self { msrv }
- }
-}
-
-impl_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]);
+declare_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]);
#[derive(Debug)]
enum SomeOkCall<'a> {
_ => return,
};
- if let Some(ok_some_call) = is_some_or_ok_call(self, cx, e) {
+ if let Some(ok_some_call) = is_some_or_ok_call(cx, e) {
emit_lint(cx, &ok_some_call);
}
}
if_chain! {
if let Some(expr) = expr_opt;
- if let Some(ok_some_call) = is_some_or_ok_call(self, cx, expr);
+ if let Some(ok_some_call) = is_some_or_ok_call(cx, expr);
then {
emit_lint(cx, &ok_some_call);
}
};
}
-
- extract_msrv_attr!(LateContext);
}
fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) {
);
}
-fn is_some_or_ok_call<'a>(
- nqml: &NeedlessQuestionMark,
- cx: &'a LateContext<'_>,
- expr: &'a Expr<'_>,
-) -> Option<SomeOkCall<'a>> {
+fn is_some_or_ok_call<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option<SomeOkCall<'a>> {
if_chain! {
// Check outer expression matches CALL_IDENT(ARGUMENT) format
if let ExprKind::Call(path, args) = &expr.kind;
- if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind;
- if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res);
+ if let ExprKind::Path(ref qpath) = &path.kind;
+ if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
// Extract inner expression from ARGUMENT
if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind;
let inner_is_some = is_type_diagnostic_item(cx, inner_ty, sym::option_type);
// Check for Option MSRV
- let meets_option_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV);
- if outer_is_some && inner_is_some && meets_option_msrv {
+ if outer_is_some && inner_is_some {
return Some(SomeOkCall::SomeCall(expr, inner_expr));
}
let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr);
// Must meet Result MSRV
- let meets_result_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV);
- if outer_is_result && inner_is_result && does_not_call_from && meets_result_msrv {
+ if outer_is_result && inner_is_result && does_not_call_from {
return Some(SomeOkCall::OkCall(expr, inner_expr));
}
}
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::paths;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::usage::contains_return_break_continue_macro;
-use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, match_qpath};
+use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, is_lang_ctor};
use if_chain::if_chain;
use rustc_errors::Applicability;
+use rustc_hir::LangItem::OptionSome;
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
if arms.len() == 2;
if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already
if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind;
- if match_qpath(struct_qpath, &paths::OPTION_SOME);
+ if is_lang_ctor(cx, struct_qpath, OptionSome);
if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind;
if !contains_return_break_continue_macro(arms[0].body);
if !contains_return_break_continue_macro(arms[1].body);
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{find_macro_calls, return_ty};
+use clippy_utils::{find_macro_calls, is_expn_of, return_ty};
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
use rustc_lint::{LateContext, LateLintPass};
}
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
- let panics = find_macro_calls(
+ let mut panics = find_macro_calls(
&[
"unimplemented",
"unreachable",
"assert",
"assert_eq",
"assert_ne",
- "debug_assert",
- "debug_assert_eq",
- "debug_assert_ne",
],
body,
);
+ panics.retain(|span| is_expn_of(*span, "debug_assert").is_none());
if !panics.is_empty() {
span_lint_and_then(
cx,
impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if match_panic_call(cx, expr).is_some() {
+ if match_panic_call(cx, expr).is_some() && is_expn_of(expr.span, "debug_assert").is_none() {
let span = get_outer_span(expr);
if is_expn_of(expr.span, "unimplemented").is_some() {
span_lint(
/// false positives in cases involving multiple lifetimes that are bounded by
/// each other.
///
+ /// Also, it does not take account of other similar cases where getting memory addresses
+ /// matters; namely, returning the pointer to the argument in question,
+ /// and passing the argument, as both references and pointers,
+ /// to a function that needs the memory address. For further details, refer to
+ /// [this issue](https://github.com/rust-lang/rust-clippy/issues/5953)
+ /// that explains a real case in which this false positive
+ /// led to an **undefined behaviour** introduced with unsafe code.
+ ///
/// **Example:**
///
/// ```rust
use clippy_utils::ptr::get_spans;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty};
-use clippy_utils::{is_allowed, match_qpath, paths};
+use clippy_utils::{expr_path_res, is_allowed, match_any_def_paths, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
+use rustc_span::symbol::Symbol;
use rustc_span::{sym, MultiSpan};
use std::borrow::Cow;
/// ```
pub CMP_NULL,
style,
- "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead."
+ "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
}
declare_clippy_lint! {
"fns that create mutable refs from immutable ref args"
}
-declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF]);
+declare_clippy_lint! {
+ /// **What it does:** This lint checks for invalid usages of `ptr::null`.
+ ///
+ /// **Why is this bad?** This causes undefined behavior.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```ignore
+ /// // Bad. Undefined behavior
+ /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); }
+ /// ```
+ ///
+ /// // Good
+ /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }
+ /// ```
+ pub INVALID_NULL_PTR_USAGE,
+ correctness,
+ "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead"
+}
+
+declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]);
impl<'tcx> LateLintPass<'tcx> for Ptr {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Binary(ref op, l, r) = expr.kind {
- if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(l) || is_null_path(r)) {
+ if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(cx, l) || is_null_path(cx, r)) {
span_lint(
cx,
CMP_NULL,
"comparing with null is better expressed by the `.is_null()` method",
);
}
+ } else {
+ check_invalid_ptr_usage(cx, expr);
+ }
+ }
+}
+
+fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ // (fn_path, arg_indices) - `arg_indices` are the `arg` positions where null would cause U.B.
+ const INVALID_NULL_PTR_USAGE_TABLE: [(&[&str], &[usize]); 16] = [
+ (&paths::SLICE_FROM_RAW_PARTS, &[0]),
+ (&paths::SLICE_FROM_RAW_PARTS_MUT, &[0]),
+ (&paths::PTR_COPY, &[0, 1]),
+ (&paths::PTR_COPY_NONOVERLAPPING, &[0, 1]),
+ (&paths::PTR_READ, &[0]),
+ (&paths::PTR_READ_UNALIGNED, &[0]),
+ (&paths::PTR_READ_VOLATILE, &[0]),
+ (&paths::PTR_REPLACE, &[0]),
+ (&paths::PTR_SLICE_FROM_RAW_PARTS, &[0]),
+ (&paths::PTR_SLICE_FROM_RAW_PARTS_MUT, &[0]),
+ (&paths::PTR_SWAP, &[0, 1]),
+ (&paths::PTR_SWAP_NONOVERLAPPING, &[0, 1]),
+ (&paths::PTR_WRITE, &[0]),
+ (&paths::PTR_WRITE_UNALIGNED, &[0]),
+ (&paths::PTR_WRITE_VOLATILE, &[0]),
+ (&paths::PTR_WRITE_BYTES, &[0]),
+ ];
+
+ if_chain! {
+ if let ExprKind::Call(ref fun, ref args) = expr.kind;
+ if let ExprKind::Path(ref qpath) = fun.kind;
+ if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
+ let fun_def_path = cx.get_def_path(fun_def_id).into_iter().map(Symbol::to_ident_string).collect::<Vec<_>>();
+ if let Some(&(_, arg_indices)) = INVALID_NULL_PTR_USAGE_TABLE
+ .iter()
+ .find(|&&(fn_path, _)| fn_path == fun_def_path);
+ then {
+ for &arg_idx in arg_indices {
+ if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) {
+ span_lint_and_sugg(
+ cx,
+ INVALID_NULL_PTR_USAGE,
+ arg.span,
+ "pointer must be non-null",
+ "change this to",
+ "core::ptr::NonNull::dangling().as_ptr()".to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
}
}
}
}
}
-fn is_null_path(expr: &Expr<'_>) -> bool {
- if let ExprKind::Call(pathexp, args) = expr.kind {
- if args.is_empty() {
- if let ExprKind::Path(ref path) = pathexp.kind {
- return match_qpath(path, &paths::PTR_NULL) || match_qpath(path, &paths::PTR_NULL_MUT);
- }
- }
+fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ if let ExprKind::Call(pathexp, []) = expr.kind {
+ expr_path_res(cx, pathexp).opt_def_id().map_or(false, |id| {
+ match_any_def_paths(cx, id, &[&paths::PTR_NULL, &paths::PTR_NULL_MUT]).is_some()
+ })
+ } else {
+ false
}
- false
}
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_lang_ctor;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{eq_expr_value, match_def_path, match_qpath, paths};
+use clippy_utils::{eq_expr_value, path_to_local_id};
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{def, BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind};
+use rustc_hir::LangItem::{OptionNone, OptionSome};
+use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
if Self::is_option(cx, subject);
if let PatKind::TupleStruct(path1, fields, None) = &arms[0].pat.kind;
- if match_qpath(path1, &["Some"]);
- if let PatKind::Binding(annot, _, bind, _) = &fields[0].kind;
+ if is_lang_ctor(cx, path1, OptionSome);
+ if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind;
let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
if let ExprKind::Block(block, None) = &arms[0].body.kind;
if block.stmts.is_empty();
if let Some(trailing_expr) = &block.expr;
- if let ExprKind::Path(path) = &trailing_expr.kind;
- if match_qpath(path, &[&bind.as_str()]);
+ if path_to_local_id(trailing_expr, bind_id);
if let PatKind::Wild = arms[1].pat.kind;
if Self::expression_returns_none(cx, arms[1].body);
false
},
ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr),
- ExprKind::Path(ref qp) => {
- if let Res::Def(DefKind::Ctor(def::CtorOf::Variant, def::CtorKind::Const), def_id) =
- cx.qpath_res(qp, expression.hir_id)
- {
- return match_def_path(cx, def_id, &paths::OPTION_NONE);
- }
-
- false
- },
+ ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
_ => false,
}
}
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
-use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path};
+use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, single_segment_path};
use clippy_utils::{higher, SpanlessEq};
use if_chain::if_chain;
use rustc_ast::ast::RangeLimits;
"manually reimplementing {`Range`, `RangeInclusive`}`::contains`"
}
-const MANUAL_RANGE_CONTAINS_MSRV: RustcVersion = RustcVersion::new(1, 35, 0);
-
pub struct Ranges {
msrv: Option<RustcVersion>,
}
check_range_zip_with_len(cx, path, args, expr.span);
},
ExprKind::Binary(ref op, l, r) => {
- if meets_msrv(self.msrv.as_ref(), &MANUAL_RANGE_CONTAINS_MSRV) {
+ if meets_msrv(self.msrv.as_ref(), &msrvs::RANGE_CONTAINS) {
check_possible_range_contains(cx, op.node, l, r, expr);
}
},
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::meets_msrv;
+use clippy_utils::{meets_msrv, msrvs};
use rustc_ast::ast::{Expr, ExprKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-const REDUNDANT_FIELD_NAMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0);
-
declare_clippy_lint! {
/// **What it does:** Checks for fields in struct literals where shorthands
/// could be used.
impl EarlyLintPass for RedundantFieldNames {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
- if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_FIELD_NAMES_MSRV) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::FIELD_INIT_SHORTHAND) {
return;
}
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::meets_msrv;
use clippy_utils::source::snippet;
+use clippy_utils::{meets_msrv, msrvs};
use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-const REDUNDANT_STATIC_LIFETIMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0);
-
declare_clippy_lint! {
/// **What it does:** Checks for constants and statics with an explicit `'static` lifetime.
///
impl EarlyLintPass for RedundantStaticLifetimes {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
- if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_STATIC_LIFETIMES_MSRV) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::STATIC_IN_CONST) {
return;
}
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_opt;
-use clippy_utils::{fn_def_id, in_macro, match_qpath};
+use clippy_utils::{fn_def_id, in_macro, path_to_local_id};
use if_chain::if_chain;
use rustc_ast::ast::Attribute;
use rustc_errors::Applicability;
if local.ty.is_none();
if cx.tcx.hir().attrs(local.hir_id).is_empty();
if let Some(initexpr) = &local.init;
- if let PatKind::Binding(.., ident, _) = local.pat.kind;
- if let ExprKind::Path(qpath) = &retexpr.kind;
- if match_qpath(qpath, &[&*ident.name.as_str()]);
+ if let PatKind::Binding(_, local_id, _, _) = local.pat.kind;
+ if path_to_local_id(retexpr, local_id);
if !last_statement_borrows(cx, initexpr);
if !in_external_macro(cx.sess(), initexpr.span);
if !in_external_macro(cx.sess(), retexpr.span);
},
_ => (),
},
+ ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty),
_ => (),
}
}
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
use clippy_utils::in_macro;
-use if_chain::if_chain;
-use rustc_ast::{Item, ItemKind, UseTreeKind};
+use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind, VisibilityKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::edition::Edition;
+use rustc_span::{edition::Edition, symbol::kw, Span, Symbol};
declare_clippy_lint! {
/// **What it does:** Checking for imports with single component use path.
declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]);
impl EarlyLintPass for SingleComponentPathImports {
- fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
- if_chain! {
- if !in_macro(item.span);
- if cx.sess.opts.edition >= Edition::Edition2018;
- if !item.vis.kind.is_pub();
- if let ItemKind::Use(use_tree) = &item.kind;
- if let segments = &use_tree.prefix.segments;
- if segments.len() == 1;
- if let UseTreeKind::Simple(None, _, _) = use_tree.kind;
- then {
+ fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
+ if cx.sess.opts.edition < Edition::Edition2018 {
+ return;
+ }
+ check_mod(cx, &krate.items);
+ }
+}
+
+fn check_mod(cx: &EarlyContext<'_>, items: &[P<Item>]) {
+ // keep track of imports reused with `self` keyword,
+ // such as `self::crypto_hash` in the example below
+ // ```rust,ignore
+ // use self::crypto_hash::{Algorithm, Hasher};
+ // ```
+ let mut imports_reused_with_self = Vec::new();
+
+ // keep track of single use statements
+ // such as `crypto_hash` in the example below
+ // ```rust,ignore
+ // use crypto_hash;
+ // ```
+ let mut single_use_usages = Vec::new();
+
+ // keep track of macros defined in the module as we don't want it to trigger on this (#7106)
+ // ```rust,ignore
+ // macro_rules! foo { () => {} };
+ // pub(crate) use foo;
+ // ```
+ let mut macros = Vec::new();
+
+ for item in items {
+ track_uses(
+ cx,
+ &item,
+ &mut imports_reused_with_self,
+ &mut single_use_usages,
+ &mut macros,
+ );
+ }
+
+ for single_use in &single_use_usages {
+ if !imports_reused_with_self.contains(&single_use.0) {
+ let can_suggest = single_use.2;
+ if can_suggest {
span_lint_and_sugg(
cx,
SINGLE_COMPONENT_PATH_IMPORTS,
- item.span,
+ single_use.1,
"this import is redundant",
"remove it entirely",
String::new(),
- Applicability::MachineApplicable
+ Applicability::MachineApplicable,
+ );
+ } else {
+ span_lint_and_help(
+ cx,
+ SINGLE_COMPONENT_PATH_IMPORTS,
+ single_use.1,
+ "this import is redundant",
+ None,
+ "remove this import",
);
}
}
}
}
+
+fn track_uses(
+ cx: &EarlyContext<'_>,
+ item: &Item,
+ imports_reused_with_self: &mut Vec<Symbol>,
+ single_use_usages: &mut Vec<(Symbol, Span, bool)>,
+ macros: &mut Vec<Symbol>,
+) {
+ if in_macro(item.span) || item.vis.kind.is_pub() {
+ return;
+ }
+
+ match &item.kind {
+ ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => {
+ check_mod(cx, &items);
+ },
+ ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => {
+ macros.push(item.ident.name);
+ },
+ ItemKind::Use(use_tree) => {
+ let segments = &use_tree.prefix.segments;
+
+ let should_report =
+ |name: &Symbol| !macros.contains(name) || matches!(item.vis.kind, VisibilityKind::Inherited);
+
+ // keep track of `use some_module;` usages
+ if segments.len() == 1 {
+ if let UseTreeKind::Simple(None, _, _) = use_tree.kind {
+ let name = segments[0].ident.name;
+ if should_report(&name) {
+ single_use_usages.push((name, item.span, true));
+ }
+ }
+ return;
+ }
+
+ if segments.is_empty() {
+ // keep track of `use {some_module, some_other_module};` usages
+ if let UseTreeKind::Nested(trees) = &use_tree.kind {
+ for tree in trees {
+ let segments = &tree.0.prefix.segments;
+ if segments.len() == 1 {
+ if let UseTreeKind::Simple(None, _, _) = tree.0.kind {
+ let name = segments[0].ident.name;
+ if should_report(&name) {
+ single_use_usages.push((name, tree.0.span, false));
+ }
+ }
+ }
+ }
+ }
+ } else {
+ // keep track of `use self::some_module` usages
+ if segments[0].ident.name == kw::SelfLower {
+ // simple case such as `use self::module::SomeStruct`
+ if segments.len() > 1 {
+ imports_reused_with_self.push(segments[1].ident.name);
+ return;
+ }
+
+ // nested case such as `use self::{module1::Struct1, module2::Struct2}`
+ if let UseTreeKind::Nested(trees) = &use_tree.kind {
+ for tree in trees {
+ let segments = &tree.0.prefix.segments;
+ if !segments.is_empty() {
+ imports_reused_with_self.push(segments[0].ident.name);
+ }
+ }
+ }
+ }
+ }
+ },
+ _ => {},
+ }
+}
fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> {
const FUNCTIONS: [&[&str]; 8] = [
- &paths::COPY_NONOVERLAPPING,
- &paths::COPY,
+ &paths::PTR_COPY_NONOVERLAPPING,
+ &paths::PTR_COPY,
&paths::WRITE_BYTES,
&paths::PTR_SWAP_NONOVERLAPPING,
&paths::PTR_SLICE_FROM_RAW_PARTS,
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::sugg::Sugg;
-use clippy_utils::{get_enclosing_block, match_qpath, SpanlessEq};
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{get_enclosing_block, is_expr_path_def_path, path_to_local, path_to_local_id, paths, SpanlessEq};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_lint::{LateContext, LateLintPass, Lint};
use rustc_middle::hir::map::Map;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::Symbol;
+use rustc_span::symbol::sym;
declare_clippy_lint! {
/// **What it does:** Checks slow zero-filled vector initialization
/// assigned to a variable. For example, `let mut vec = Vec::with_capacity(0)` or
/// `vec = Vec::with_capacity(0)`
struct VecAllocation<'tcx> {
- /// Symbol of the local variable name
- variable_name: Symbol,
+ /// HirId of the variable
+ local_id: HirId,
/// Reference to the expression which allocates the vector
allocation_expr: &'tcx Expr<'tcx>,
if_chain! {
if let ExprKind::Assign(left, right, _) = expr.kind;
- // Extract variable name
- if let ExprKind::Path(QPath::Resolved(_, path)) = left.kind;
- if let Some(variable_name) = path.segments.get(0);
+ // Extract variable
+ if let Some(local_id) = path_to_local(left);
// Extract len argument
- if let Some(len_arg) = Self::is_vec_with_capacity(right);
+ if let Some(len_arg) = Self::is_vec_with_capacity(cx, right);
then {
let vi = VecAllocation {
- variable_name: variable_name.ident.name,
+ local_id,
allocation_expr: right,
len_expr: len_arg,
};
// Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)`
if_chain! {
if let StmtKind::Local(local) = stmt.kind;
- if let PatKind::Binding(BindingAnnotation::Mutable, .., variable_name, None) = local.pat.kind;
+ if let PatKind::Binding(BindingAnnotation::Mutable, local_id, _, None) = local.pat.kind;
if let Some(init) = local.init;
- if let Some(len_arg) = Self::is_vec_with_capacity(init);
+ if let Some(len_arg) = Self::is_vec_with_capacity(cx, init);
then {
let vi = VecAllocation {
- variable_name: variable_name.name,
+ local_id,
allocation_expr: init,
len_expr: len_arg,
};
impl SlowVectorInit {
/// Checks if the given expression is `Vec::with_capacity(..)`. It will return the expression
/// of the first argument of `with_capacity` call if it matches or `None` if it does not.
- fn is_vec_with_capacity<'tcx>(expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+ fn is_vec_with_capacity<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
if_chain! {
- if let ExprKind::Call(func, args) = expr.kind;
- if let ExprKind::Path(ref path) = func.kind;
- if match_qpath(path, &["Vec", "with_capacity"]);
- if args.len() == 1;
-
+ if let ExprKind::Call(func, [arg]) = expr.kind;
+ if let ExprKind::Path(QPath::TypeRelative(ty, name)) = func.kind;
+ if name.ident.as_str() == "with_capacity";
+ if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::vec_type);
then {
- return Some(&args[0]);
+ Some(arg)
+ } else {
+ None
}
}
-
- None
}
/// Search initialization for the given vector
fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) {
if_chain! {
if self.initialization_found;
- if let ExprKind::MethodCall(path, _, args, _) = expr.kind;
- if let ExprKind::Path(ref qpath_subj) = args[0].kind;
- if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]);
+ if let ExprKind::MethodCall(path, _, [self_arg, extend_arg], _) = expr.kind;
+ if path_to_local_id(self_arg, self.vec_alloc.local_id);
if path.ident.name == sym!(extend);
- if let Some(extend_arg) = args.get(1);
if self.is_repeat_take(extend_arg);
then {
fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) {
if_chain! {
if self.initialization_found;
- if let ExprKind::MethodCall(path, _, args, _) = expr.kind;
- if let ExprKind::Path(ref qpath_subj) = args[0].kind;
- if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]);
+ if let ExprKind::MethodCall(path, _, [self_arg, len_arg, fill_arg], _) = expr.kind;
+ if path_to_local_id(self_arg, self.vec_alloc.local_id);
if path.ident.name == sym!(resize);
- if let (Some(len_arg), Some(fill_arg)) = (args.get(1), args.get(2));
// Check that is filled with 0
if let ExprKind::Lit(ref lit) = fill_arg.kind;
// Check that take is applied to `repeat(0)`
if let Some(repeat_expr) = take_args.get(0);
- if Self::is_repeat_zero(repeat_expr);
+ if self.is_repeat_zero(repeat_expr);
// Check that len expression is equals to `with_capacity` expression
if let Some(len_arg) = take_args.get(1);
}
/// Returns `true` if given expression is `repeat(0)`
- fn is_repeat_zero(expr: &Expr<'_>) -> bool {
+ fn is_repeat_zero(&self, expr: &Expr<'_>) -> bool {
if_chain! {
- if let ExprKind::Call(fn_expr, repeat_args) = expr.kind;
- if let ExprKind::Path(ref qpath_repeat) = fn_expr.kind;
- if match_qpath(qpath_repeat, &["repeat"]);
- if let Some(repeat_arg) = repeat_args.get(0);
+ if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind;
+ if is_expr_path_def_path(self.cx, fn_expr, &paths::ITER_REPEAT);
if let ExprKind::Lit(ref lit) = repeat_arg.kind;
if let LitKind::Int(0, _) = lit.node;
then {
- return true
+ true
+ } else {
+ false
}
}
-
- false
}
}
i: usize,
expected_loc: IdentLocation,
) {
- if let Some(binop) = binops.get(i).cloned() {
+ if let Some(binop) = binops.get(i).copied() {
// We need to try and figure out which identifier we should
// suggest using instead. Since there could be multiple
// replacement candidates in a given expression, and we're
// tracker to decide if the last group of tabs is not closed by a non-tab character
let mut is_active = false;
- let chars_array: Vec<_> = the_str.chars().collect();
+ // Note that we specifically need the char _byte_ indices here, not the positional indexes
+ // within the char array to deal with multi-byte characters properly. `char_indices` does
+ // exactly that. It provides an iterator over tuples of the form `(byte position, char)`.
+ let char_indices: Vec<_> = the_str.char_indices().collect();
- if chars_array == vec!['\t'] {
+ if let [(_, '\t')] = char_indices.as_slice() {
return vec![(0, 1)];
}
- for (index, arr) in chars_array.windows(2).enumerate() {
- let index = u32::try_from(index).expect(line_length_way_to_long);
- match arr {
- ['\t', '\t'] => {
+ for entry in char_indices.windows(2) {
+ match entry {
+ [(_, '\t'), (_, '\t')] => {
// either string starts with double tab, then we have to set it active,
// otherwise is_active is true anyway
is_active = true;
},
- [_, '\t'] => {
+ [(_, _), (index_b, '\t')] => {
// as ['\t', '\t'] is excluded, this has to be a start of a tab group,
// set indices accordingly
is_active = true;
- current_start = index + 1;
+ current_start = u32::try_from(*index_b).unwrap();
},
- ['\t', _] => {
+ [(_, '\t'), (index_b, _)] => {
// this now has to be an end of the group, hence we have to push a new tuple
is_active = false;
- spans.push((current_start, index + 1));
+ spans.push((current_start, u32::try_from(*index_b).unwrap()));
},
_ => {},
}
if is_active {
spans.push((
current_start,
- u32::try_from(the_str.chars().count()).expect(line_length_way_to_long),
+ u32::try_from(char_indices.last().unwrap().0 + 1).expect(line_length_way_to_long),
));
}
mod tests_for_get_chunks_of_tabs {
use super::get_chunks_of_tabs;
+ #[test]
+ fn test_unicode_han_string() {
+ let res = get_chunks_of_tabs(" \u{4f4d}\t");
+
+ assert_eq!(res, vec![(4, 5)]);
+ }
+
#[test]
fn test_empty_string() {
let res = get_chunks_of_tabs("");
use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{is_diagnostic_assoc_item, match_def_path, path_to_local_id, paths};
+use clippy_utils::{is_diag_trait_item, match_def_path, path_to_local_id, paths};
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
if let ExprKind::MethodCall(path, _, args, _) = expr.kind;
if path.ident.name == sym!(to_string);
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
- if is_diagnostic_assoc_item(cx, expr_def_id, sym::ToString);
+ if is_diag_trait_item(cx, expr_def_id, sym::ToString);
if path_to_local_id(&args[0], self_hir_id);
then {
span_lint(
/// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
/// ```
pub TRANSMUTE_PTR_TO_PTR,
- complexity,
+ pedantic,
"transmutes from a pointer to a pointer / a reference to a reference"
}
use crate::consts::{constant_context, Constant};
use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{match_qpath, paths};
+use clippy_utils::{is_expr_path_def_path, paths};
use if_chain::if_chain;
use rustc_ast::LitKind;
use rustc_hir::{Expr, ExprKind};
}
if_chain! {
- if let ExprKind::Call(func, args) = expr.kind;
- if let ExprKind::Path(ref path) = func.kind;
- if match_qpath(path, &paths::STD_MEM_TRANSMUTE);
- if args.len() == 1;
+ if let ExprKind::Call(func, [arg]) = expr.kind;
+ if is_expr_path_def_path(cx, func, &paths::TRANSMUTE);
then {
-
// Catching transmute over constants that resolve to `null`.
let mut const_eval_context = constant_context(cx, cx.typeck_results());
if_chain! {
- if let ExprKind::Path(ref _qpath) = args[0].kind;
- let x = const_eval_context.expr(&args[0]);
+ if let ExprKind::Path(ref _qpath) = arg.kind;
+ let x = const_eval_context.expr(arg);
if let Some(Constant::RawPtr(0)) = x;
then {
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
// Catching:
// `std::mem::transmute(0 as *const i32)`
if_chain! {
- if let ExprKind::Cast(inner_expr, _cast_ty) = args[0].kind;
+ if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind;
if let ExprKind::Lit(ref lit) = inner_expr.kind;
if let LitKind::Int(0, _) = lit.node;
then {
// Catching:
// `std::mem::transmute(std::ptr::null::<i32>())`
if_chain! {
- if let ExprKind::Call(func1, args1) = args[0].kind;
- if let ExprKind::Path(ref path1) = func1.kind;
- if match_qpath(path1, &paths::STD_PTR_NULL);
- if args1.is_empty();
+ if let ExprKind::Call(func1, []) = arg.kind;
+ if is_expr_path_def_path(cx, func1, &paths::PTR_NULL);
then {
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
}
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{snippet, snippet_with_macro_callsite};
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{differing_macro_contexts, in_macro, match_def_path, match_qpath, paths};
+use clippy_utils::{differing_macro_contexts, get_parent_expr, in_macro, is_lang_ctor, match_def_path, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
+use rustc_hir::LangItem::ResultErr;
use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
if let ExprKind::Call(err_fun, err_args) = try_arg.kind;
if let Some(err_arg) = err_args.get(0);
if let ExprKind::Path(ref err_fun_path) = err_fun.kind;
- if match_qpath(err_fun_path, &paths::RESULT_ERR);
+ if is_lang_ctor(cx, err_fun_path, ResultErr);
if let Some(return_ty) = find_return_type(cx, &expr.kind);
then {
let prefix;
} else {
snippet(cx, err_arg.span, "_")
};
+ let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) {
+ "" // already returns
+ } else {
+ "return "
+ };
let suggestion = if err_ty == expr_err_ty {
- format!("return {}{}{}", prefix, origin_snippet, suffix)
+ format!("{}{}{}{}", ret_prefix, prefix, origin_snippet, suffix)
} else {
- format!("return {}{}.into(){}", prefix, origin_snippet, suffix)
+ format!("{}{}{}.into(){}", ret_prefix, prefix, origin_snippet, suffix)
};
span_lint_and_sugg(
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
-use clippy_utils::{match_path, paths};
+use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{
_ => None,
});
then {
- if is_any_trait(inner) {
+ if is_any_trait(cx, inner) {
// Ignore `Box<Any>` types; see issue #1884 for details.
return false;
}
}
// Returns true if given type is `Any` trait.
-fn is_any_trait(t: &hir::Ty<'_>) -> bool {
+fn is_any_trait(cx: &LateContext<'_>, t: &hir::Ty<'_>) -> bool {
if_chain! {
if let TyKind::TraitObject(traits, ..) = t.kind;
if !traits.is_empty();
+ if let Some(trait_did) = traits[0].trait_ref.trait_def_id();
// Only Send/Sync can be used as additional traits, so it is enough to
// check only the first trait.
- if match_path(traits[0].trait_ref.path, &paths::ANY_TRAIT);
+ if match_def_path(cx, trait_did, &paths::ANY_TRAIT);
then {
return true;
}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_then;
+use if_chain::if_chain;
+use rustc_ast::{Item, ItemKind, UseTreeKind};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::kw;
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for imports ending in `::{self}`.
+ ///
+ /// **Why is this bad?** In most cases, this can be written much more cleanly by omitting `::{self}`.
+ ///
+ /// **Known problems:** Removing `::{self}` will cause any non-module items at the same path to also be imported.
+ /// This might cause a naming conflict (https://github.com/rust-lang/rustfmt/issues/3568). This lint makes no attempt
+ /// to detect this scenario and that is why it is a restriction lint.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// use std::io::{self};
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// use std::io;
+ /// ```
+ pub UNNECESSARY_SELF_IMPORTS,
+ restriction,
+ "imports ending in `::{self}`, which can be omitted"
+}
+
+declare_lint_pass!(UnnecessarySelfImports => [UNNECESSARY_SELF_IMPORTS]);
+
+impl EarlyLintPass for UnnecessarySelfImports {
+ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+ if_chain! {
+ if let ItemKind::Use(use_tree) = &item.kind;
+ if let UseTreeKind::Nested(nodes) = &use_tree.kind;
+ if let [(self_tree, _)] = &**nodes;
+ if let [self_seg] = &*self_tree.prefix.segments;
+ if self_seg.ident.name == kw::SelfLower;
+ if let Some(last_segment) = use_tree.prefix.segments.last();
+
+ then {
+ span_lint_and_then(
+ cx,
+ UNNECESSARY_SELF_IMPORTS,
+ item.span,
+ "import ending with `::{self}`",
+ |diag| {
+ diag.span_suggestion(
+ last_segment.span().with_hi(item.span.hi()),
+ "consider omitting `::{self}`",
+ format!(
+ "{}{};",
+ last_segment.ident,
+ if let UseTreeKind::Simple(Some(alias), ..) = self_tree.kind { format!(" as {}", alias) } else { String::new() },
+ ),
+ Applicability::MaybeIncorrect,
+ );
+ diag.note("this will slightly change semantics; any non-module items at the same path will also be imported");
+ },
+ );
+ }
+ }
+ }
+}
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
-use clippy_utils::{contains_return, in_macro, match_qpath, paths, return_ty, visitors::find_all_ret_expressions};
+use clippy_utils::{contains_return, in_macro, is_lang_ctor, return_ty, visitors::find_all_ret_expressions};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
+use rustc_hir::LangItem::{OptionSome, ResultOk};
use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
}
// Get the wrapper and inner types, if can't, abort.
- let (return_type_label, path, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() {
+ let (return_type_label, lang_item, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() {
if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did) {
- ("Option", &paths::OPTION_SOME, subst.type_at(0))
+ ("Option", OptionSome, subst.type_at(0))
} else if cx.tcx.is_diagnostic_item(sym::result_type, adt_def.did) {
- ("Result", &paths::RESULT_OK, subst.type_at(0))
+ ("Result", ResultOk, subst.type_at(0))
} else {
return;
}
if_chain! {
if !in_macro(ret_expr.span);
// Check if a function call.
- if let ExprKind::Call(func, args) = ret_expr.kind;
- // Get the Path of the function call.
- if let ExprKind::Path(ref qpath) = func.kind;
+ if let ExprKind::Call(func, [arg]) = ret_expr.kind;
// Check if OPTION_SOME or RESULT_OK, depending on return type.
- if match_qpath(qpath, path);
- if args.len() == 1;
+ if let ExprKind::Path(qpath) = &func.kind;
+ if is_lang_ctor(cx, qpath, lang_item);
// Make sure the function argument does not contain a return expression.
- if !contains_return(&args[0]);
+ if !contains_return(arg);
then {
suggs.push(
(
if inner_type.is_unit() {
"".to_string()
} else {
- snippet(cx, args[0].span.source_callsite(), "..").to_string()
+ snippet(cx, arg.span.source_callsite(), "..").to_string()
}
)
);
#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
+use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path};
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::over;
-use clippy_utils::{
- ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path},
- meets_msrv,
-};
+use clippy_utils::{meets_msrv, msrvs, over};
use rustc_ast::mut_visit::*;
use rustc_ast::ptr::P;
use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
"unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
}
-const UNNESTED_OR_PATTERNS_MSRV: RustcVersion = RustcVersion::new(1, 53, 0);
-
#[derive(Clone, Copy)]
pub struct UnnestedOrPatterns {
msrv: Option<RustcVersion>,
impl EarlyLintPass for UnnestedOrPatterns {
fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) {
- if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) {
+ if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
lint_unnested_or_patterns(cx, &a.pat);
}
}
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
- if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) {
+ if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
if let ast::ExprKind::Let(pat, _) = &e.kind {
lint_unnested_or_patterns(cx, pat);
}
}
fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) {
- if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) {
+ if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
lint_unnested_or_patterns(cx, &p.pat);
}
}
fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) {
- if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) {
+ if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
lint_unnested_or_patterns(cx, &l.pat);
}
}
};
match expr.kind {
- hir::ExprKind::Match(res, _, _) if is_try(expr).is_some() => {
+ hir::ExprKind::Match(res, _, _) if is_try(cx, expr).is_some() => {
if let hir::ExprKind::Call(func, args) = res.kind {
if matches!(
func.kind,
hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryIntoResult, _))
) {
- check_method_call(cx, &args[0], expr);
+ check_map_error(cx, &args[0], expr);
}
} else {
- check_method_call(cx, res, expr);
+ check_map_error(cx, res, expr);
}
},
-
hir::ExprKind::MethodCall(path, _, args, _) => match &*path.ident.as_str() {
"expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => {
- check_method_call(cx, &args[0], expr);
+ check_map_error(cx, &args[0], expr);
},
_ => (),
},
-
_ => (),
}
}
}
+fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
+ let mut call = call;
+ while let hir::ExprKind::MethodCall(ref path, _, ref args, _) = call.kind {
+ if matches!(&*path.ident.as_str(), "or" | "or_else" | "ok") {
+ call = &args[0];
+ } else {
+ break;
+ }
+ }
+ check_method_call(cx, call, expr);
+}
+
fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
if let hir::ExprKind::MethodCall(path, _, _, _) = call.kind {
let symbol = &*path.ident.as_str();
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
-use clippy_utils::{in_macro, meets_msrv};
+use clippy_utils::{in_macro, meets_msrv, msrvs};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
stack: Vec<StackItem>,
}
-const USE_SELF_MSRV: RustcVersion = RustcVersion::new(1, 37, 0);
-
impl UseSelf {
#[must_use]
pub fn new(msrv: Option<RustcVersion>) -> Self {
}
fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
- if in_macro(hir_ty.span) | in_impl(cx, hir_ty) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) {
+ if in_macro(hir_ty.span)
+ || in_impl(cx, hir_ty)
+ || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS)
+ {
return;
}
}
}
- if in_macro(expr.span) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) {
+ if in_macro(expr.span) || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS) {
return;
}
pub use self::helpers::Conf;
define_Conf! {
- /// Lint: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, NEEDLESS_QUESTION_MARK, PTR_AS_PTR. The minimum rust version that the project supports
+ /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR. The minimum rust version that the project supports
(msrv, "msrv": Option<String>, None),
/// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
(blacklisted_names, "blacklisted_names": Vec<String>, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
use clippy_utils::source::snippet;
use clippy_utils::ty::match_type;
use clippy_utils::{
- is_else_clause, is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, SpanlessEq,
+ is_else_clause, is_expn_of, is_expr_path_def_path, match_def_path, method_calls, path_to_res, paths, run_lints,
+ SpanlessEq,
};
use if_chain::if_chain;
use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId};
if_chain! {
if let ExprKind::Call(func, and_then_args) = expr.kind;
- if let ExprKind::Path(ref path) = func.kind;
- if match_qpath(path, &["span_lint_and_then"]);
+ if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
if and_then_args.len() == 5;
if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind;
let body = cx.tcx.hir().body(*body_id);
if_chain! {
// Check if this is a call to utils::match_type()
if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind;
- if let ExprKind::Path(fn_qpath) = &fn_path.kind;
- if match_qpath(fn_qpath, &["utils", "match_type"]);
+ if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]);
// Extract the path to the matched type
if let Some(segments) = path_to_matched_type(cx, ty_path);
let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect();
let diag_items = cx.tcx.diagnostic_items(ty_did.krate);
if let Some(item_name) = diag_items.iter().find_map(|(k, v)| if *v == ty_did { Some(k) } else { None });
then {
+ // TODO: check paths constants from external crates.
let cx_snippet = snippet(cx, context.span, "_");
let ty_snippet = snippet(cx, ty.span, "_");
cx,
MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
expr.span,
- "usage of `utils::match_type()` on a type diagnostic item",
+ "usage of `clippy_utils::ty::match_type()` on a type diagnostic item",
"try",
- format!("utils::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name),
+ format!("clippy_utils::ty::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name),
Applicability::MaybeIncorrect,
);
}
diag.multipart_suggestion(
"try this",
iter::once((comma_span.to(token_expr.span), String::new()))
- .chain(fmt_spans.iter().cloned().zip(iter::repeat(replacement)))
+ .chain(fmt_spans.iter().copied().zip(iter::repeat(replacement)))
.collect(),
Applicability::MachineApplicable,
);
#![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)]
use crate::{both, over};
+use if_chain::if_chain;
use rustc_ast::ptr::P;
use rustc_ast::{self as ast, *};
use rustc_span::symbol::Ident;
_ => false,
}
}
+
+/// Extract args from an assert-like macro.
+///
+/// Currently working with:
+/// - `assert_eq!` and `assert_ne!`
+/// - `debug_assert_eq!` and `debug_assert_ne!`
+///
+/// For example:
+///
+/// `debug_assert_eq!(a, b)` will return Some([a, b])
+pub fn extract_assert_macro_args(mut expr: &Expr) -> Option<[&Expr; 2]> {
+ if_chain! {
+ if let ExprKind::If(_, ref block, _) = expr.kind;
+ if let StmtKind::Semi(ref e) = block.stmts.get(0)?.kind;
+ then {
+ expr = e;
+ }
+ }
+ if_chain! {
+ if let ExprKind::Block(ref block, _) = expr.kind;
+ if let StmtKind::Expr(ref expr) = block.stmts.get(0)?.kind;
+ if let ExprKind::Match(ref match_expr, _) = expr.kind;
+ if let ExprKind::Tup(ref tup) = match_expr.kind;
+ if let [a, b, ..] = tup.as_slice();
+ if let (&ExprKind::AddrOf(_, _, ref a), &ExprKind::AddrOf(_, _, ref b)) = (&a.kind, &b.kind);
+ then {
+ return Some([&*a, &*b]);
+ }
+ }
+ None
+}
/// Return true if the attributes contain `#[doc(hidden)]`
pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool {
- #[allow(clippy::filter_map)]
attrs
.iter()
.filter(|attr| attr.has_name(sym::doc))
- .flat_map(ast::Attribute::meta_item_list)
+ .filter_map(ast::Attribute::meta_item_list)
.any(|l| attr::list_contains_name(&l, sym::hidden))
}
pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
match (&left.kind, &right.kind) {
(&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => {
+ // This additional check ensures that the type of the locals are equivalent even if the init
+ // expression or type have some inferred parts.
+ if let Some(typeck) = self.inner.maybe_typeck_results {
+ let l_ty = typeck.pat_ty(&l.pat);
+ let r_ty = typeck.pat_ty(&r.pat);
+ if !rustc_middle::ty::TyS::same_type(l_ty, r_ty) {
+ return false;
+ }
+ }
+
// eq_pat adds the HirIds to the locals map. We therefor call it last to make sure that
// these only get added if the init and type is equal.
both(&l.init, &r.init, |l, r| self.eq_expr(l, r))
TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
)
})
- .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().cloned()) =>
+ .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied()) =>
{
kind
},
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
extern crate rustc_ast;
extern crate rustc_ast_pretty;
+extern crate rustc_attr;
extern crate rustc_data_structures;
extern crate rustc_errors;
extern crate rustc_hir;
pub mod eager_or_lazy;
pub mod higher;
mod hir_utils;
+pub mod msrvs;
pub mod numeric_literal;
pub mod paths;
pub mod ptr;
use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
-use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
+use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
-use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor};
+use rustc_hir::LangItem::{ResultErr, ResultOk};
use rustc_hir::{
- def, Arm, BindingAnnotation, Block, Body, Constness, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem,
- ImplItemKind, Item, ItemKind, LangItem, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath,
- TraitItem, TraitItemKind, TraitRef, TyKind,
+ def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl,
+ ImplItem, ImplItemKind, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment,
+ QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
};
use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::exports::Export;
use rustc_target::abi::Integer;
use crate::consts::{constant, Constant};
-use crate::ty::is_recursively_primitive_type;
+use crate::ty::{can_partially_move_ty, is_recursively_primitive_type};
pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
if let Ok(version) = RustcVersion::parse(msrv) {
}
}
+/// Checks if a `QPath` resolves to a constructor of a `LangItem`.
+/// For example, use this to check whether a function call or a pattern is `Some(..)`.
+pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem) -> bool {
+ if let QPath::Resolved(_, path) = qpath {
+ if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
+ if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) {
+ return cx.tcx.parent(ctor_id) == Some(item_id);
+ }
+ }
+ }
+ false
+}
+
/// Returns `true` if this `span` was expanded by any macro.
#[must_use]
pub fn in_macro(span: Span) -> bool {
trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
}
-/// Checks if the method call given in `def_id` belongs to a trait or other container with a given
-/// diagnostic item
-pub fn is_diagnostic_assoc_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
- cx.tcx
- .opt_associated_item(def_id)
- .and_then(|associated_item| match associated_item.container {
- rustc_ty::TraitContainer(assoc_def_id) => Some(assoc_def_id),
- rustc_ty::ImplContainer(assoc_def_id) => match cx.tcx.type_of(assoc_def_id).kind() {
- rustc_ty::Adt(adt, _) => Some(adt.did),
- rustc_ty::Slice(_) => cx.tcx.get_diagnostic_item(sym::slice), // this isn't perfect but it works
- _ => None,
- },
- })
- .map_or(false, |assoc_def_id| cx.tcx.is_diagnostic_item(diag_item, assoc_def_id))
+/// Checks if a method is defined in an impl of a diagnostic item
+pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
+ if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
+ if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
+ return cx.tcx.is_diagnostic_item(diag_item, adt.did);
+ }
+ }
+ false
+}
+
+/// Checks if a method is in a diagnostic item trait
+pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
+ if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
+ return cx.tcx.is_diagnostic_item(diag_item, trait_did);
+ }
+ false
}
/// Checks if the method call given in `expr` belongs to the given trait.
pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
cx.typeck_results()
.type_dependent_def_id(expr.hir_id)
- .map_or(false, |did| is_diagnostic_assoc_item(cx, did, diag_item))
+ .map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
}
/// Checks if an expression references a variable of the given name.
}
}
+/// If the expression is a path, resolve it. Otherwise, return `Res::Err`.
+pub fn expr_path_res(cx: &LateContext<'_>, expr: &Expr<'_>) -> Res {
+ if let ExprKind::Path(p) = &expr.kind {
+ cx.qpath_res(p, expr.hir_id)
+ } else {
+ Res::Err
+ }
+}
+
+/// Resolves the path to a `DefId` and checks if it matches the given path.
+pub fn is_qpath_def_path(cx: &LateContext<'_>, path: &QPath<'_>, hir_id: HirId, segments: &[&str]) -> bool {
+ cx.qpath_res(path, hir_id)
+ .opt_def_id()
+ .map_or(false, |id| match_def_path(cx, id, segments))
+}
+
+/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
+pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
+ expr_path_res(cx, expr)
+ .opt_def_id()
+ .map_or(false, |id| match_def_path(cx, id, segments))
+}
+
/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
/// `QPath::Resolved.1.res.opt_def_id()`.
.all(|(a, b)| a.ident.name.as_str() == *b)
}
-/// Matches a `Path` against a slice of segment string literals, e.g.
-///
-/// # Examples
-/// ```rust,ignore
-/// match_path_ast(path, &["std", "rt", "begin_unwind"])
-/// ```
-pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool {
- path.segments
- .iter()
- .rev()
- .zip(segments.iter().rev())
- .all(|(a, b)| a.ident.name.as_str() == *b)
-}
-
/// If the expression is a path to a local, returns the canonical `HirId` of the local.
pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
None
}
+/// Checks if the top level expression can be moved into a closure as is.
+pub fn can_move_expr_to_closure_no_visit(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, jump_targets: &[HirId]) -> bool {
+ match expr.kind {
+ ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
+ | ExprKind::Continue(Destination { target_id: Ok(id), .. })
+ if jump_targets.contains(&id) =>
+ {
+ true
+ },
+ ExprKind::Break(..)
+ | ExprKind::Continue(_)
+ | ExprKind::Ret(_)
+ | ExprKind::Yield(..)
+ | ExprKind::InlineAsm(_)
+ | ExprKind::LlvmInlineAsm(_) => false,
+ // Accessing a field of a local value can only be done if the type isn't
+ // partially moved.
+ ExprKind::Field(base_expr, _)
+ if matches!(
+ base_expr.kind,
+ ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. }))
+ ) && can_partially_move_ty(cx, cx.typeck_results().expr_ty(base_expr)) =>
+ {
+ // TODO: check if the local has been partially moved. Assume it has for now.
+ false
+ }
+ _ => true,
+ }
+}
+
+/// Checks if the expression can be moved into a closure as is.
+pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+ struct V<'cx, 'tcx> {
+ cx: &'cx LateContext<'tcx>,
+ loops: Vec<HirId>,
+ allow_closure: bool,
+ }
+ impl Visitor<'tcx> for V<'_, 'tcx> {
+ type Map = ErasedMap<'tcx>;
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+ if !self.allow_closure {
+ return;
+ }
+ if let ExprKind::Loop(b, ..) = e.kind {
+ self.loops.push(e.hir_id);
+ self.visit_block(b);
+ self.loops.pop();
+ } else {
+ self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops);
+ walk_expr(self, e);
+ }
+ }
+ }
+
+ let mut v = V {
+ cx,
+ allow_closure: true,
+ loops: Vec::new(),
+ };
+ v.visit_expr(expr);
+ v.allow_closure
+}
+
/// Returns the method names and argument list of nested method call expressions that make up
/// `expr`. method/span lists are sorted with the most recent call first.
pub fn method_calls<'tcx>(
/// the function once on the given pattern.
pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
if let PatKind::Or(pats) = pat.kind {
- pats.iter().cloned().for_each(f)
+ pats.iter().copied().for_each(f)
} else {
f(pat)
}
/// Checks if a given expression is a match expression expanded from the `?`
/// operator or the `try` macro.
-pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
- fn is_ok(arm: &Arm<'_>) -> bool {
+pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+ fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
if_chain! {
if let PatKind::TupleStruct(ref path, ref pat, None) = arm.pat.kind;
- if match_qpath(path, &paths::RESULT_OK[1..]);
+ if is_lang_ctor(cx, path, ResultOk);
if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
if path_to_local_id(arm.body, hir_id);
then {
false
}
- fn is_err(arm: &Arm<'_>) -> bool {
+ fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
- match_qpath(path, &paths::RESULT_ERR[1..])
+ is_lang_ctor(cx, path, ResultErr)
} else {
false
}
if arms.len() == 2;
if arms[0].guard.is_none();
if arms[1].guard.is_none();
- if (is_ok(&arms[0]) && is_err(&arms[1])) ||
- (is_ok(&arms[1]) && is_err(&arms[0]));
+ if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) ||
+ (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
then {
return Some(expr);
}
None
}
+/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
+/// any.
+pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
+ let search_path = cx.get_def_path(did);
+ paths
+ .iter()
+ .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
+}
+
+/// Checks if the given `DefId` matches the path.
pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
- // We have to convert `syms` to `&[Symbol]` here because rustc's `match_def_path`
- // accepts only that. We should probably move to Symbols in Clippy as well.
- let syms = syms.iter().map(|p| Symbol::intern(p)).collect::<Vec<Symbol>>();
- cx.match_def_path(did, &syms)
+ // We should probably move to Symbols in Clippy as well rather than interning every time.
+ let path = cx.get_def_path(did);
+ syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
}
-pub fn match_panic_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx [Expr<'tcx>]> {
- match_function_call(cx, expr, &paths::BEGIN_PANIC)
- .or_else(|| match_function_call(cx, expr, &paths::BEGIN_PANIC_FMT))
- .or_else(|| match_function_call(cx, expr, &paths::PANIC_ANY))
- .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC))
- .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_FMT))
- .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_STR))
+pub fn match_panic_call(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+ if let ExprKind::Call(func, [arg]) = expr.kind {
+ expr_path_res(cx, func)
+ .opt_def_id()
+ .map_or(false, |id| match_panic_def_id(cx, id))
+ .then(|| arg)
+ } else {
+ None
+ }
}
pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool {
- match_def_path(cx, did, &paths::BEGIN_PANIC)
- || match_def_path(cx, did, &paths::BEGIN_PANIC_FMT)
- || match_def_path(cx, did, &paths::PANIC_ANY)
- || match_def_path(cx, did, &paths::PANICKING_PANIC)
- || match_def_path(cx, did, &paths::PANICKING_PANIC_FMT)
- || match_def_path(cx, did, &paths::PANICKING_PANIC_STR)
+ match_any_def_paths(
+ cx,
+ did,
+ &[
+ &paths::BEGIN_PANIC,
+ &paths::BEGIN_PANIC_FMT,
+ &paths::PANIC_ANY,
+ &paths::PANICKING_PANIC,
+ &paths::PANICKING_PANIC_FMT,
+ &paths::PANICKING_PANIC_STR,
+ ],
+ )
+ .is_some()
}
/// Returns the list of condition expressions and the list of blocks in a
(conds, blocks)
}
-/// This function returns true if the given expression is the `else` or `if else` part of an if
-/// statement
-pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool {
- let map = cx.tcx.hir();
- let parent_id = map.get_parent_node(expr.hir_id);
- let parent_node = map.get(parent_id);
- matches!(
- parent_node,
- Node::Expr(Expr {
- kind: ExprKind::If(_, _, _),
- ..
- })
- )
-}
-
// Finds the `#[must_use]` attribute, if any
pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
attrs.iter().find(|a| a.has_name(sym::must_use))
did.map_or(false, |did| must_use_attr(&cx.tcx.get_attrs(did)).is_some())
}
+/// Gets the node where an expression is either used, or it's type is unified with another branch.
+pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
+ let map = tcx.hir();
+ let mut child_id = expr.hir_id;
+ let mut iter = map.parent_iter(child_id);
+ loop {
+ match iter.next() {
+ None => break None,
+ Some((id, Node::Block(_))) => child_id = id,
+ Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
+ Some((_, Node::Expr(expr))) => match expr.kind {
+ ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
+ ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
+ ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
+ _ => break Some(Node::Expr(expr)),
+ },
+ Some((_, node)) => break Some(node),
+ }
+ }
+}
+
+/// Checks if the result of an expression is used, or it's type is unified with another branch.
+pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
+ !matches!(
+ get_expr_use_or_unification_node(tcx, expr),
+ None | Some(Node::Stmt(Stmt {
+ kind: StmtKind::Expr(_)
+ | StmtKind::Semi(_)
+ | StmtKind::Local(Local {
+ pat: Pat {
+ kind: PatKind::Wild,
+ ..
+ },
+ ..
+ }),
+ ..
+ }))
+ )
+}
+
+/// Checks if the expression is the final expression returned from a block.
+pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
+ matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
+}
+
pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
peel(pat, 0)
}
+/// Peels of expressions while the given closure returns `Some`.
+pub fn peel_hir_expr_while<'tcx>(
+ mut expr: &'tcx Expr<'tcx>,
+ mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
+) -> &'tcx Expr<'tcx> {
+ while let Some(e) = f(expr) {
+ expr = e;
+ }
+ expr
+}
+
/// Peels off up to the given number of references on the expression. Returns the underlying
/// expression and the number of references removed.
pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
- fn f(expr: &'a Expr<'a>, count: usize, target: usize) -> (&'a Expr<'a>, usize) {
- match expr.kind {
- ExprKind::AddrOf(_, _, expr) if count != target => f(expr, count + 1, target),
- _ => (expr, count),
- }
- }
- f(expr, 0, count)
+ let mut remaining = count;
+ let e = peel_hir_expr_while(expr, |e| match e.kind {
+ ExprKind::AddrOf(BorrowKind::Ref, _, e) if remaining != 0 => {
+ remaining -= 1;
+ Some(e)
+ },
+ _ => None,
+ });
+ (e, count - remaining)
}
/// Peels off all references on the expression. Returns the underlying expression and the number of
/// references removed.
pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
- fn f(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
- match expr.kind {
- ExprKind::AddrOf(BorrowKind::Ref, _, expr) => f(expr, count + 1),
- _ => (expr, count),
- }
- }
- f(expr, 0)
+ let mut count = 0;
+ let e = peel_hir_expr_while(expr, |e| match e.kind {
+ ExprKind::AddrOf(BorrowKind::Ref, _, e) => {
+ count += 1;
+ Some(e)
+ },
+ _ => None,
+ });
+ (e, count)
}
#[macro_export]
}
}
}
-
-/// Check if the resolution of a given path is an `Ok` variant of `Result`.
-pub fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool {
- if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() {
- if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
- if let Some(variant_id) = cx.tcx.parent(id) {
- return variant_id == ok_id;
- }
- }
- }
- false
-}
-
-/// Check if the resolution of a given path is a `Some` variant of `Option`.
-pub fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool {
- if let Some(some_id) = cx.tcx.lang_items().option_some_variant() {
- if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
- if let Some(variant_id) = cx.tcx.parent(id) {
- return variant_id == some_id;
- }
- }
- }
- false
-}
--- /dev/null
+use rustc_semver::RustcVersion;
+
+macro_rules! msrv_aliases {
+ ($($major:literal,$minor:literal,$patch:literal {
+ $($name:ident),* $(,)?
+ })*) => {
+ $($(
+ pub const $name: RustcVersion = RustcVersion::new($major, $minor, $patch);
+ )*)*
+ };
+}
+
+// names may refer to stabilized feature flags or library items
+msrv_aliases! {
+ 1,53,0 { OR_PATTERNS }
+ 1,50,0 { BOOL_THEN }
+ 1,46,0 { CONST_IF_MATCH }
+ 1,45,0 { STR_STRIP_PREFIX }
+ 1,42,0 { MATCHES_MACRO }
+ 1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
+ 1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }
+ 1,38,0 { POINTER_CAST }
+ 1,37,0 { TYPE_ALIAS_ENUM_VARIANTS }
+ 1,36,0 { ITERATOR_COPIED }
+ 1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
+ 1,34,0 { TRY_FROM }
+ 1,30,0 { ITERATOR_FIND_MAP }
+ 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST }
+}
//! Whenever possible, please consider diagnostic items over hardcoded paths.
//! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
-pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
+pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"];
pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"];
pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"];
+pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
+pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"];
pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
-pub const COPY: [&str; 4] = ["core", "intrinsics", "", "copy_nonoverlapping"];
-pub const COPY_NONOVERLAPPING: [&str; 4] = ["core", "intrinsics", "", "copy"];
pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"];
pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"];
pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"];
+pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"];
+pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"];
pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
pub const HASH: [&str; 3] = ["core", "hash", "Hash"];
pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
+pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"];
pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
+pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"];
pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
#[cfg(feature = "internal-lints")]
pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"];
pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
-pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"];
-pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"];
+pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
+pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
+pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
#[cfg(feature = "internal-lints")]
pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
#[cfg(feature = "internal-lints")]
pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"];
pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"];
+pub const PTR_COPY: [&str; 4] = ["core", "intrinsics", "", "copy"];
+pub const PTR_COPY_NONOVERLAPPING: [&str; 4] = ["core", "intrinsics", "", "copy_nonoverlapping"];
pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"];
pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"];
pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"];
pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"];
pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"];
+pub const PTR_READ: [&str; 3] = ["core", "ptr", "read"];
+pub const PTR_READ_UNALIGNED: [&str; 3] = ["core", "ptr", "read_unaligned"];
+pub const PTR_READ_VOLATILE: [&str; 3] = ["core", "ptr", "read_volatile"];
+pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"];
+pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"];
+pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"];
+pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
+pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"];
+pub const PTR_WRITE_VOLATILE: [&str; 3] = ["core", "ptr", "write_volatile"];
pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
-pub const REPEAT: [&str; 3] = ["core", "iter", "repeat"];
pub const RESULT: [&str; 3] = ["core", "result", "Result"];
pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
-pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"];
+pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
-pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"];
-pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"];
pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
+// This code used to be a part of `rustc` but moved to Clippy as a result of
+// https://github.com/rust-lang/rust/issues/76618. Because of that, it contains unused code and some
+// of terminologies might not be relevant in the context of Clippy. Note that its behavior might
+// differ from the time of `rustc` even if the name stays the same.
+
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::{
};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
+use rustc_semver::RustcVersion;
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::spec::abi::Abi::RustIntrinsic;
type McfResult = Result<(), (Span, Cow<'static, str>)>;
-pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
+pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult {
let def_id = body.source.def_id();
let mut current = def_id;
loop {
)?;
for bb in body.basic_blocks() {
- check_terminator(tcx, body, bb.terminator())?;
+ check_terminator(tcx, body, bb.terminator(), msrv)?;
for stmt in &bb.statements {
check_statement(tcx, body, def_id, stmt)?;
}
Ok(())
}
-fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult {
+fn check_terminator(
+ tcx: TyCtxt<'tcx>,
+ body: &'a Body<'tcx>,
+ terminator: &Terminator<'tcx>,
+ msrv: Option<&RustcVersion>,
+) -> McfResult {
let span = terminator.source_info.span;
match &terminator.kind {
TerminatorKind::FalseEdge { .. }
} => {
let fn_ty = func.ty(body, tcx);
if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
- if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) {
+ if !is_const_fn(tcx, fn_def_id, msrv) {
return Err((
span,
format!(
TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
}
}
+
+fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool {
+ rustc_mir::const_eval::is_const_fn(tcx, def_id)
+ && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| {
+ if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level {
+ // Checking MSRV is manually necessary because `rustc` has no such concept. This entire
+ // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`.
+ // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
+ crate::meets_msrv(
+ msrv,
+ &RustcVersion::parse(&since.as_str())
+ .expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"),
+ )
+ } else {
+ // Unstable const fn with the feature enabled.
+ msrv.is_none()
+ }
+ })
+}
snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
}
+/// Gets a snippet of the indentation of the line of a span
+pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> {
+ snippet_opt(cx, line_span(cx, span)).map(|mut s| {
+ let len = s.len() - s.trim_start().len();
+ s.truncate(len);
+ s
+ })
+}
+
// If the snippet is empty, it's an attribute that was inserted during macro
// expansion and we want to ignore those, because they could come from external
// sources that the user has no control over.
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TypeFoldable, UintTy};
use rustc_span::sym;
-use rustc_span::symbol::Symbol;
+use rustc_span::symbol::{Ident, Symbol};
use rustc_span::DUMMY_SP;
use rustc_trait_selection::traits::query::normalize::AtExt;
})
}
+/// Resolves `<T as Iterator>::Item` for `T`
+/// Do not invoke without first verifying that the type implements `Iterator`
+pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
+ cx.tcx
+ .get_diagnostic_item(sym::Iterator)
+ .and_then(|iter_did| {
+ cx.tcx.associated_items(iter_did).find_by_name_and_kind(
+ cx.tcx,
+ Ident::from_str("Item"),
+ ty::AssocKind::Type,
+ iter_did,
+ )
+ })
+ .map(|assoc| {
+ let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[]));
+ cx.tcx.normalize_erasing_regions(cx.param_env, proj)
+ })
+}
+
/// Returns true if ty has `iter` or `iter_mut` methods
pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
// FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
## Specifying the lint's minimum supported Rust version (MSRV)
-Projects supporting older versions of Rust would need to disable a lint if it
-targets features present in later versions. Support for this can be added by
-specifying an MSRV in your lint like so,
+Sometimes a lint makes suggestions that require a certain version of Rust. For example, the `manual_strip` lint suggests
+using `str::strip_prefix` and `str::strip_suffix` which is only available after Rust 1.45. In such cases, you need to
+ensure that the MSRV configured for the project is >= the MSRV of the required Rust feature. If multiple features are
+required, just use the one with a lower MSRV.
+
+First, add an MSRV alias for the required feature in [`clippy_utils::msrvs`](/clippy_utils/src/msrvs.rs). This can be
+accessed later as `msrvs::STR_STRIP_PREFIX`, for example.
```rust
-const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0);
+msrv_aliases! {
+ ..
+ 1,45,0 { STR_STRIP_PREFIX }
+}
```
-The project's MSRV will also have to be an attribute in the lint so you'll have
-to add a struct and constructor for your lint. The project's MSRV needs to be
-passed when the lint is registered in `lib.rs`
+In order to access the project-configured MSRV, you need to have an `msrv` field in the LintPass struct, and a
+constructor to initialize the field. The `msrv` value is passed to the constructor in `clippy_lints/lib.rs`.
```rust
pub struct ManualStrip {
}
```
-The project's MSRV can then be matched against the lint's `msrv` in the LintPass
+The project's MSRV can then be matched against the feature MSRV in the LintPass
using the `meets_msrv` utility function.
``` rust
-if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) {
+if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) {
return;
}
```
Here are some pointers to things you are likely going to need for every lint:
* [Clippy utils][utils] - Various helper functions. Maybe the function you need
- is already in here (`implements_trait`, `match_path`, `snippet`, etc)
+ is already in here (`implements_trait`, `match_def_path`, `snippet`, etc)
* [Clippy diagnostics][diagnostics]
* [The `if_chain` macro][if_chain]
* [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro]
// When a new lint is introduced, we can search the results for new warnings and check for false
// positives.
-#![allow(clippy::filter_map, clippy::collapsible_else_if)]
+#![allow(clippy::collapsible_else_if)]
use std::ffi::OsStr;
use std::process::Command;
use serde::{Deserialize, Serialize};
use serde_json::Value;
+#[cfg(not(windows))]
const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver";
+#[cfg(not(windows))]
const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy";
+#[cfg(windows)]
+const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver.exe";
+#[cfg(windows)]
+const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy.exe";
+
const LINTCHECK_DOWNLOADS: &str = "target/lintcheck/downloads";
const LINTCHECK_SOURCES: &str = "target/lintcheck/sources";
[toolchain]
-channel = "nightly-2021-04-08"
+channel = "nightly-2021-04-22"
components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
#![deny(clippy::internal)]
#![feature(rustc_private)]
+extern crate clippy_utils;
extern crate rustc_ast;
extern crate rustc_errors;
extern crate rustc_lint;
extern crate rustc_session;
extern crate rustc_span;
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
use rustc_ast::ast::Expr;
-use rustc_errors::{Applicability, DiagnosticBuilder};
-use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-
-#[allow(unused_variables)]
-pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
-where
- F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
-{
-}
-
-#[allow(unused_variables)]
-fn span_lint_and_help<'a, T: LintContext>(
- cx: &'a T,
- lint: &'static Lint,
- span: Span,
- msg: &str,
- option_span: Option<Span>,
- help: &str,
-) {
-}
-
-#[allow(unused_variables)]
-fn span_lint_and_note<'a, T: LintContext>(
- cx: &'a T,
- lint: &'static Lint,
- span: Span,
- msg: &str,
- note_span: Option<Span>,
- note: &str,
-) {
-}
-
-#[allow(unused_variables)]
-fn span_lint_and_sugg<'a, T: LintContext>(
- cx: &'a T,
- lint: &'static Lint,
- sp: Span,
- msg: &str,
- help: &str,
- sugg: String,
- applicability: Applicability,
-) {
-}
declare_tool_lint! {
pub clippy::TEST_LINT,
#![deny(clippy::internal)]
#![feature(rustc_private)]
+extern crate clippy_utils;
extern crate rustc_ast;
extern crate rustc_errors;
extern crate rustc_lint;
extern crate rustc_session;
extern crate rustc_span;
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
use rustc_ast::ast::Expr;
-use rustc_errors::{Applicability, DiagnosticBuilder};
-use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-
-#[allow(unused_variables)]
-pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
-where
- F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
-{
-}
-
-#[allow(unused_variables)]
-fn span_lint_and_help<'a, T: LintContext>(
- cx: &'a T,
- lint: &'static Lint,
- span: Span,
- msg: &str,
- option_span: Option<Span>,
- help: &str,
-) {
-}
-
-#[allow(unused_variables)]
-fn span_lint_and_note<'a, T: LintContext>(
- cx: &'a T,
- lint: &'static Lint,
- span: Span,
- msg: &str,
- note_span: Option<Span>,
- note: &str,
-) {
-}
-
-#[allow(unused_variables)]
-fn span_lint_and_sugg<'a, T: LintContext>(
- cx: &'a T,
- lint: &'static Lint,
- sp: Span,
- msg: &str,
- help: &str,
- sugg: String,
- applicability: Applicability,
-) {
-}
declare_tool_lint! {
pub clippy::TEST_LINT,
error: this call is collapsible
- --> $DIR/collapsible_span_lint_calls.rs:75:9
+ --> $DIR/collapsible_span_lint_calls.rs:35:9
|
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
LL | | db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable);
= note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]`
error: this call is collapsible
- --> $DIR/collapsible_span_lint_calls.rs:78:9
+ --> $DIR/collapsible_span_lint_calls.rs:38:9
|
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
LL | | db.span_help(expr.span, help_msg);
| |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)`
error: this call is collapsible
- --> $DIR/collapsible_span_lint_calls.rs:81:9
+ --> $DIR/collapsible_span_lint_calls.rs:41:9
|
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
LL | | db.help(help_msg);
| |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)`
error: this call is collspible
- --> $DIR/collapsible_span_lint_calls.rs:84:9
+ --> $DIR/collapsible_span_lint_calls.rs:44:9
|
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
LL | | db.span_note(expr.span, note_msg);
| |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)`
error: this call is collspible
- --> $DIR/collapsible_span_lint_calls.rs:87:9
+ --> $DIR/collapsible_span_lint_calls.rs:47:9
|
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
LL | | db.note(note_msg);
#![deny(clippy::internal)]
#![feature(rustc_private)]
+extern crate clippy_utils;
extern crate rustc_hir;
extern crate rustc_lint;
extern crate rustc_middle;
+
#[macro_use]
extern crate rustc_session;
+use clippy_utils::{paths, ty::match_type};
use rustc_hir::Expr;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::Ty;
-mod paths {
- pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
-}
-
-mod utils {
- use super::*;
-
- pub fn match_type(_cx: &LateContext<'_>, _ty: Ty<'_>, _path: &[&str]) -> bool {
- false
- }
-}
-
-use utils::match_type;
-
declare_lint! {
pub TEST_LINT,
Warn,
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr) {
let ty = cx.typeck_results().expr_ty(expr);
- let _ = match_type(cx, ty, &paths::VEC);
+ let _ = match_type(cx, ty, &paths::VEC); // FIXME: Doesn't lint external paths
let _ = match_type(cx, ty, &OPTION);
let _ = match_type(cx, ty, &["core", "result", "Result"]);
let rc_path = &["alloc", "rc", "Rc"];
- let _ = utils::match_type(cx, ty, rc_path);
+ let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
}
}
-error: usage of `utils::match_type()` on a type diagnostic item
- --> $DIR/match_type_on_diag_item.rs:41:17
+error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
+ --> $DIR/match_type_on_diag_item.rs:31:17
|
-LL | let _ = match_type(cx, ty, &paths::VEC);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::vec_type)`
+LL | let _ = match_type(cx, ty, &OPTION);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::option_type)`
|
note: the lint level is defined here
--> $DIR/match_type_on_diag_item.rs:1:9
| ^^^^^^^^^^^^^^^^
= note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]`
-error: usage of `utils::match_type()` on a type diagnostic item
- --> $DIR/match_type_on_diag_item.rs:42:17
- |
-LL | let _ = match_type(cx, ty, &OPTION);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::option_type)`
-
-error: usage of `utils::match_type()` on a type diagnostic item
- --> $DIR/match_type_on_diag_item.rs:43:17
+error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
+ --> $DIR/match_type_on_diag_item.rs:32:17
|
LL | let _ = match_type(cx, ty, &["core", "result", "Result"]);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::result_type)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::result_type)`
-error: usage of `utils::match_type()` on a type diagnostic item
- --> $DIR/match_type_on_diag_item.rs:46:17
+error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
+ --> $DIR/match_type_on_diag_item.rs:35:17
|
-LL | let _ = utils::match_type(cx, ty, rc_path);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::Rc)`
+LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Rc)`
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
-#![feature(asm)]
// only-x86_64
+// ignore-aarch64
+
+#![feature(asm)]
#[warn(clippy::inline_asm_x86_intel_syntax)]
mod warn_intel {
}
}
+#[cfg(target_arch = "x86_64")]
fn main() {
unsafe {
warn_att::use_asm();
error: Intel x86 assembly syntax used
- --> $DIR/asm_syntax.rs:7:9
+ --> $DIR/asm_syntax.rs:9:9
|
LL | asm!("");
| ^^^^^^^^^
= help: use AT&T x86 assembly syntax
error: Intel x86 assembly syntax used
- --> $DIR/asm_syntax.rs:8:9
+ --> $DIR/asm_syntax.rs:10:9
|
LL | asm!("", options());
| ^^^^^^^^^^^^^^^^^^^^
= help: use AT&T x86 assembly syntax
error: Intel x86 assembly syntax used
- --> $DIR/asm_syntax.rs:9:9
+ --> $DIR/asm_syntax.rs:11:9
|
LL | asm!("", options(nostack));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: use AT&T x86 assembly syntax
error: AT&T x86 assembly syntax used
- --> $DIR/asm_syntax.rs:21:9
+ --> $DIR/asm_syntax.rs:23:9
|
LL | asm!("", options(att_syntax));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: use Intel x86 assembly syntax
error: AT&T x86 assembly syntax used
- --> $DIR/asm_syntax.rs:22:9
+ --> $DIR/asm_syntax.rs:24:9
|
LL | asm!("", options(nostack, att_syntax));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
--- /dev/null
+#![warn(clippy::bool_assert_comparison)]
+
+macro_rules! a {
+ () => {
+ true
+ };
+}
+macro_rules! b {
+ () => {
+ true
+ };
+}
+
+fn main() {
+ assert_eq!("a".len(), 1);
+ assert_eq!("a".is_empty(), false);
+ assert_eq!("".is_empty(), true);
+ assert_eq!(true, "".is_empty());
+ assert_eq!(a!(), b!());
+ assert_eq!(a!(), "".is_empty());
+ assert_eq!("".is_empty(), b!());
+
+ assert_ne!("a".len(), 1);
+ assert_ne!("a".is_empty(), false);
+ assert_ne!("".is_empty(), true);
+ assert_ne!(true, "".is_empty());
+ assert_ne!(a!(), b!());
+ assert_ne!(a!(), "".is_empty());
+ assert_ne!("".is_empty(), b!());
+
+ debug_assert_eq!("a".len(), 1);
+ debug_assert_eq!("a".is_empty(), false);
+ debug_assert_eq!("".is_empty(), true);
+ debug_assert_eq!(true, "".is_empty());
+ debug_assert_eq!(a!(), b!());
+ debug_assert_eq!(a!(), "".is_empty());
+ debug_assert_eq!("".is_empty(), b!());
+
+ debug_assert_ne!("a".len(), 1);
+ debug_assert_ne!("a".is_empty(), false);
+ debug_assert_ne!("".is_empty(), true);
+ debug_assert_ne!(true, "".is_empty());
+ debug_assert_ne!(a!(), b!());
+ debug_assert_ne!(a!(), "".is_empty());
+ debug_assert_ne!("".is_empty(), b!());
+
+ // assert with error messages
+ assert_eq!("a".len(), 1, "tadam {}", 1);
+ assert_eq!("a".len(), 1, "tadam {}", true);
+ assert_eq!("a".is_empty(), false, "tadam {}", 1);
+ assert_eq!("a".is_empty(), false, "tadam {}", true);
+ assert_eq!(false, "a".is_empty(), "tadam {}", true);
+
+ debug_assert_eq!("a".len(), 1, "tadam {}", 1);
+ debug_assert_eq!("a".len(), 1, "tadam {}", true);
+ debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
+ debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
+ debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
+}
--- /dev/null
+error: used `assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:16:5
+ |
+LL | assert_eq!("a".is_empty(), false);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+ |
+ = note: `-D clippy::bool-assert-comparison` implied by `-D warnings`
+
+error: used `assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:17:5
+ |
+LL | assert_eq!("".is_empty(), true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:18:5
+ |
+LL | assert_eq!(true, "".is_empty());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_ne!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:24:5
+ |
+LL | assert_ne!("a".is_empty(), false);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_ne!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:25:5
+ |
+LL | assert_ne!("".is_empty(), true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_ne!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:26:5
+ |
+LL | assert_ne!(true, "".is_empty());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:32:5
+ |
+LL | debug_assert_eq!("a".is_empty(), false);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:33:5
+ |
+LL | debug_assert_eq!("".is_empty(), true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:34:5
+ |
+LL | debug_assert_eq!(true, "".is_empty());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_ne!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:40:5
+ |
+LL | debug_assert_ne!("a".is_empty(), false);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_ne!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:41:5
+ |
+LL | debug_assert_ne!("".is_empty(), true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_ne!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:42:5
+ |
+LL | debug_assert_ne!(true, "".is_empty());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:50:5
+ |
+LL | assert_eq!("a".is_empty(), false, "tadam {}", 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:51:5
+ |
+LL | assert_eq!("a".is_empty(), false, "tadam {}", true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:52:5
+ |
+LL | assert_eq!(false, "a".is_empty(), "tadam {}", true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:56:5
+ |
+LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:57:5
+ |
+LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:58:5
+ |
+LL | debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: aborting due to 18 previous errors
+
}
}
+fn fp_if_let_issue7054() {
+ // This shouldn't trigger the lint
+ let string;
+ let _x = if let true = true {
+ ""
+ } else if true {
+ string = "x".to_owned();
+ &string
+ } else {
+ string = "y".to_owned();
+ &string
+ };
+}
+
fn main() {}
}
}
+#[allow(clippy::vec_init_then_push)]
+fn pf_local_with_inferred_type_issue7053() {
+ if true {
+ let mut v = Vec::new();
+ v.push(0);
+ } else {
+ let mut v = Vec::new();
+ v.push("");
+ };
+}
+
fn main() {}
--- /dev/null
+// run-rustfix
+#![warn(clippy::cloned_instead_of_copied)]
+
+fn main() {
+ // yay
+ let _ = [1].iter().copied();
+ let _ = vec!["hi"].iter().copied();
+ let _ = Some(&1).copied();
+ let _ = Box::new([1].iter()).copied();
+ let _ = Box::new(Some(&1)).copied();
+
+ // nay
+ let _ = [String::new()].iter().cloned();
+ let _ = Some(&String::new()).cloned();
+}
--- /dev/null
+// run-rustfix
+#![warn(clippy::cloned_instead_of_copied)]
+
+fn main() {
+ // yay
+ let _ = [1].iter().cloned();
+ let _ = vec!["hi"].iter().cloned();
+ let _ = Some(&1).cloned();
+ let _ = Box::new([1].iter()).cloned();
+ let _ = Box::new(Some(&1)).cloned();
+
+ // nay
+ let _ = [String::new()].iter().cloned();
+ let _ = Some(&String::new()).cloned();
+}
--- /dev/null
+error: used `cloned` where `copied` could be used instead
+ --> $DIR/cloned_instead_of_copied.rs:6:24
+ |
+LL | let _ = [1].iter().cloned();
+ | ^^^^^^ help: try: `copied`
+ |
+ = note: `-D clippy::cloned-instead-of-copied` implied by `-D warnings`
+
+error: used `cloned` where `copied` could be used instead
+ --> $DIR/cloned_instead_of_copied.rs:7:31
+ |
+LL | let _ = vec!["hi"].iter().cloned();
+ | ^^^^^^ help: try: `copied`
+
+error: used `cloned` where `copied` could be used instead
+ --> $DIR/cloned_instead_of_copied.rs:8:22
+ |
+LL | let _ = Some(&1).cloned();
+ | ^^^^^^ help: try: `copied`
+
+error: used `cloned` where `copied` could be used instead
+ --> $DIR/cloned_instead_of_copied.rs:9:34
+ |
+LL | let _ = Box::new([1].iter()).cloned();
+ | ^^^^^^ help: try: `copied`
+
+error: used `cloned` where `copied` could be used instead
+ --> $DIR/cloned_instead_of_copied.rs:10:32
+ |
+LL | let _ = Box::new(Some(&1)).cloned();
+ | ^^^^^^ help: try: `copied`
+
+error: aborting due to 5 previous errors
+
--- /dev/null
+#[rustfmt::skip]
+pub struct Foo {
+ /// 位
+ /// ^ Do not remove this tab character.
+ /// It was required to trigger the ICE.
+ pub bar: u8,
+}
+
+fn main() {}
--- /dev/null
+error: using tabs in doc comments is not recommended
+ --> $DIR/ice-5835.rs:3:10
+ |
+LL | /// 位
+ | ^^^^ help: consider using four spaces per tab
+ |
+ = note: `-D clippy::tabs-in-doc-comments` implied by `-D warnings`
+
+error: aborting due to previous error
+
= help: unsized fn params are gated as an unstable feature
help: function arguments must have a statically known size, borrowed types always have a known size
|
-LL | fn bug<T>() -> impl Iterator<Item = [(); { |&x: [u8]| x }]> {
- | ^
+LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: &[u8]| x }]> {
+ | ^
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/ice-6251.rs:4:54
--- /dev/null
+// This test requires a feature gated const fn and will stop working in the future.
+
+#![feature(const_btree_new)]
+
+use std::collections::BTreeMap;
+
+struct Foo(BTreeMap<i32, i32>);
+impl Foo {
+ fn new() -> Self {
+ Self(BTreeMap::new())
+ }
+}
+
+fn main() {}
// ignore-macos
// ignore-windows
-#![feature(main)]
+#![feature(rustc_attrs)]
#[warn(clippy::main_recursion)]
#[allow(unconditional_recursion)]
-#[main]
+#[rustc_main]
fn a() {
println!("Hello, World!");
a();
#[warn(clippy::panic_params)]
#[warn(clippy::unknown_clippy_lints)]
#[warn(clippy::find_map)]
+#[warn(clippy::filter_map)]
fn main() {}
LL | #[warn(clippy::unused_collect)]
| ^^^^^^^^^^^^^^^^^^^^^^
-error: lint `clippy::invalid_ref` has been removed: superseded by rustc lint `invalid_value`
+error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
--> $DIR/deprecated.rs:5:8
|
LL | #[warn(clippy::invalid_ref)]
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
-error: lint `clippy::into_iter_on_array` has been removed: this lint has been uplifted to rustc and is now called `array_into_iter`
+error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
--> $DIR/deprecated.rs:6:8
|
LL | #[warn(clippy::into_iter_on_array)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
-error: lint `clippy::unused_label` has been removed: this lint has been uplifted to rustc and is now called `unused_labels`
+error: lint `clippy::unused_label` has been renamed to `unused_labels`
--> $DIR/deprecated.rs:7:8
|
LL | #[warn(clippy::unused_label)]
- | ^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018
--> $DIR/deprecated.rs:8:8
LL | #[warn(clippy::regex_macro)]
| ^^^^^^^^^^^^^^^^^^^
-error: lint `clippy::drop_bounds` has been removed: this lint has been uplifted to rustc and is now called `drop_bounds`
+error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
--> $DIR/deprecated.rs:9:8
|
LL | #[warn(clippy::drop_bounds)]
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
-error: lint `clippy::temporary_cstring_as_ptr` has been removed: this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`
+error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
--> $DIR/deprecated.rs:10:8
|
LL | #[warn(clippy::temporary_cstring_as_ptr)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
-error: lint `clippy::panic_params` has been removed: this lint has been uplifted to rustc and is now called `panic_fmt`
+error: lint `clippy::panic_params` has been renamed to `non_fmt_panic`
--> $DIR/deprecated.rs:11:8
|
LL | #[warn(clippy::panic_params)]
- | ^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panic`
-error: lint `clippy::unknown_clippy_lints` has been removed: this lint has been integrated into the `unknown_lints` rustc lint
+error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
--> $DIR/deprecated.rs:12:8
|
LL | #[warn(clippy::unknown_clippy_lints)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint
--> $DIR/deprecated.rs:13:8
LL | #[warn(clippy::find_map)]
| ^^^^^^^^^^^^^^^^
+error: lint `clippy::filter_map` has been removed: this lint has been replaced by `manual_filter_map`, a more specific lint
+ --> $DIR/deprecated.rs:14:8
+ |
+LL | #[warn(clippy::filter_map)]
+ | ^^^^^^^^^^^^^^^^^^
+
error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7
--> $DIR/deprecated.rs:1:8
|
LL | #[warn(clippy::unstable_as_slice)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 14 previous errors
+error: aborting due to 15 previous errors
--- /dev/null
+// run-rustfix
+
+#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
+#![warn(clippy::map_entry)]
+#![feature(asm)]
+
+use std::collections::{BTreeMap, HashMap};
+use std::hash::Hash;
+
+macro_rules! m {
+ ($e:expr) => {{ $e }};
+}
+
+macro_rules! insert {
+ ($map:expr, $key:expr, $val:expr) => {
+ $map.insert($key, $val)
+ };
+}
+
+fn foo() {}
+
+fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, m2: &mut HashMap<K, V>, k: K, k2: K, v: V, v2: V) {
+ // or_insert(v)
+ m.entry(k).or_insert(v);
+
+ // semicolon on insert, use or_insert_with(..)
+ m.entry(k).or_insert_with(|| {
+ if true {
+ v
+ } else {
+ v2
+ }
+ });
+
+ // semicolon on if, use or_insert_with(..)
+ m.entry(k).or_insert_with(|| {
+ if true {
+ v
+ } else {
+ v2
+ }
+ });
+
+ // early return, use if let
+ if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+ if true {
+ e.insert(v);
+ } else {
+ e.insert(v2);
+ return;
+ }
+ }
+
+ // use or_insert_with(..)
+ m.entry(k).or_insert_with(|| {
+ foo();
+ v
+ });
+
+ // semicolon on insert and match, use or_insert_with(..)
+ m.entry(k).or_insert_with(|| {
+ match 0 {
+ 1 if true => {
+ v
+ },
+ _ => {
+ v2
+ },
+ }
+ });
+
+ // one branch doesn't insert, use if let
+ if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+ match 0 {
+ 0 => foo(),
+ _ => {
+ e.insert(v2);
+ },
+ };
+ }
+
+ // use or_insert_with
+ m.entry(k).or_insert_with(|| {
+ foo();
+ match 0 {
+ 0 if false => {
+ v
+ },
+ 1 => {
+ foo();
+ v
+ },
+ 2 | 3 => {
+ for _ in 0..2 {
+ foo();
+ }
+ if true {
+ v
+ } else {
+ v2
+ }
+ },
+ _ => {
+ v2
+ },
+ }
+ });
+
+ // ok, insert in loop
+ if !m.contains_key(&k) {
+ for _ in 0..2 {
+ m.insert(k, v);
+ }
+ }
+
+ // macro_expansion test, use or_insert(..)
+ m.entry(m!(k)).or_insert_with(|| m!(v));
+
+ // ok, map used before insertion
+ if !m.contains_key(&k) {
+ let _ = m.len();
+ m.insert(k, v);
+ }
+
+ // ok, inline asm
+ if !m.contains_key(&k) {
+ unsafe { asm!("nop") }
+ m.insert(k, v);
+ }
+
+ // ok, different keys.
+ if !m.contains_key(&k) {
+ m.insert(k2, v);
+ }
+
+ // ok, different maps
+ if !m.contains_key(&k) {
+ m2.insert(k, v);
+ }
+
+ // ok, insert in macro
+ if !m.contains_key(&k) {
+ insert!(m, k, v);
+ }
+}
+
+fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V, v2: V) {
+ // insert then do something, use if let
+ if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) {
+ e.insert(v);
+ foo();
+ }
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+
+#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
+#![warn(clippy::map_entry)]
+#![feature(asm)]
+
+use std::collections::{BTreeMap, HashMap};
+use std::hash::Hash;
+
+macro_rules! m {
+ ($e:expr) => {{ $e }};
+}
+
+macro_rules! insert {
+ ($map:expr, $key:expr, $val:expr) => {
+ $map.insert($key, $val)
+ };
+}
+
+fn foo() {}
+
+fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, m2: &mut HashMap<K, V>, k: K, k2: K, v: V, v2: V) {
+ // or_insert(v)
+ if !m.contains_key(&k) {
+ m.insert(k, v);
+ }
+
+ // semicolon on insert, use or_insert_with(..)
+ if !m.contains_key(&k) {
+ if true {
+ m.insert(k, v);
+ } else {
+ m.insert(k, v2);
+ }
+ }
+
+ // semicolon on if, use or_insert_with(..)
+ if !m.contains_key(&k) {
+ if true {
+ m.insert(k, v)
+ } else {
+ m.insert(k, v2)
+ };
+ }
+
+ // early return, use if let
+ if !m.contains_key(&k) {
+ if true {
+ m.insert(k, v);
+ } else {
+ m.insert(k, v2);
+ return;
+ }
+ }
+
+ // use or_insert_with(..)
+ if !m.contains_key(&k) {
+ foo();
+ m.insert(k, v);
+ }
+
+ // semicolon on insert and match, use or_insert_with(..)
+ if !m.contains_key(&k) {
+ match 0 {
+ 1 if true => {
+ m.insert(k, v);
+ },
+ _ => {
+ m.insert(k, v2);
+ },
+ };
+ }
+
+ // one branch doesn't insert, use if let
+ if !m.contains_key(&k) {
+ match 0 {
+ 0 => foo(),
+ _ => {
+ m.insert(k, v2);
+ },
+ };
+ }
+
+ // use or_insert_with
+ if !m.contains_key(&k) {
+ foo();
+ match 0 {
+ 0 if false => {
+ m.insert(k, v);
+ },
+ 1 => {
+ foo();
+ m.insert(k, v);
+ },
+ 2 | 3 => {
+ for _ in 0..2 {
+ foo();
+ }
+ if true {
+ m.insert(k, v);
+ } else {
+ m.insert(k, v2);
+ };
+ },
+ _ => {
+ m.insert(k, v2);
+ },
+ }
+ }
+
+ // ok, insert in loop
+ if !m.contains_key(&k) {
+ for _ in 0..2 {
+ m.insert(k, v);
+ }
+ }
+
+ // macro_expansion test, use or_insert(..)
+ if !m.contains_key(&m!(k)) {
+ m.insert(m!(k), m!(v));
+ }
+
+ // ok, map used before insertion
+ if !m.contains_key(&k) {
+ let _ = m.len();
+ m.insert(k, v);
+ }
+
+ // ok, inline asm
+ if !m.contains_key(&k) {
+ unsafe { asm!("nop") }
+ m.insert(k, v);
+ }
+
+ // ok, different keys.
+ if !m.contains_key(&k) {
+ m.insert(k2, v);
+ }
+
+ // ok, different maps
+ if !m.contains_key(&k) {
+ m2.insert(k, v);
+ }
+
+ // ok, insert in macro
+ if !m.contains_key(&k) {
+ insert!(m, k, v);
+ }
+}
+
+fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V, v2: V) {
+ // insert then do something, use if let
+ if !m.contains_key(&k) {
+ m.insert(k, v);
+ foo();
+ }
+}
+
+fn main() {}
--- /dev/null
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry.rs:24:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | m.insert(k, v);
+LL | | }
+ | |_____^ help: try this: `m.entry(k).or_insert(v);`
+ |
+ = note: `-D clippy::map-entry` implied by `-D warnings`
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry.rs:29:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | if true {
+LL | | m.insert(k, v);
+LL | | } else {
+LL | | m.insert(k, v2);
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL | m.entry(k).or_insert_with(|| {
+LL | if true {
+LL | v
+LL | } else {
+LL | v2
+LL | }
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry.rs:38:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | if true {
+LL | | m.insert(k, v)
+LL | | } else {
+LL | | m.insert(k, v2)
+LL | | };
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL | m.entry(k).or_insert_with(|| {
+LL | if true {
+LL | v
+LL | } else {
+LL | v2
+LL | }
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry.rs:47:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | if true {
+LL | | m.insert(k, v);
+LL | | } else {
+... |
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+LL | if true {
+LL | e.insert(v);
+LL | } else {
+LL | e.insert(v2);
+LL | return;
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry.rs:57:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | foo();
+LL | | m.insert(k, v);
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL | m.entry(k).or_insert_with(|| {
+LL | foo();
+LL | v
+LL | });
+ |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry.rs:63:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | match 0 {
+LL | | 1 if true => {
+LL | | m.insert(k, v);
+... |
+LL | | };
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL | m.entry(k).or_insert_with(|| {
+LL | match 0 {
+LL | 1 if true => {
+LL | v
+LL | },
+LL | _ => {
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry.rs:75:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | match 0 {
+LL | | 0 => foo(),
+LL | | _ => {
+... |
+LL | | };
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+LL | match 0 {
+LL | 0 => foo(),
+LL | _ => {
+LL | e.insert(v2);
+LL | },
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry.rs:85:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | foo();
+LL | | match 0 {
+LL | | 0 if false => {
+... |
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL | m.entry(k).or_insert_with(|| {
+LL | foo();
+LL | match 0 {
+LL | 0 if false => {
+LL | v
+LL | },
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry.rs:119:5
+ |
+LL | / if !m.contains_key(&m!(k)) {
+LL | | m.insert(m!(k), m!(v));
+LL | | }
+ | |_____^ help: try this: `m.entry(m!(k)).or_insert_with(|| m!(v));`
+
+error: usage of `contains_key` followed by `insert` on a `BTreeMap`
+ --> $DIR/entry.rs:153:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | m.insert(k, v);
+LL | | foo();
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL | if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) {
+LL | e.insert(v);
+LL | foo();
+LL | }
+ |
+
+error: aborting due to 10 previous errors
+
+++ /dev/null
-// run-rustfix
-
-#![allow(unused, clippy::needless_pass_by_value)]
-#![warn(clippy::map_entry)]
-
-use std::collections::{BTreeMap, HashMap};
-use std::hash::Hash;
-
-fn foo() {}
-
-fn insert_if_absent0<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
- m.entry(k).or_insert(v);
-}
-
-fn main() {}
+++ /dev/null
-// run-rustfix
-
-#![allow(unused, clippy::needless_pass_by_value)]
-#![warn(clippy::map_entry)]
-
-use std::collections::{BTreeMap, HashMap};
-use std::hash::Hash;
-
-fn foo() {}
-
-fn insert_if_absent0<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
- if !m.contains_key(&k) {
- m.insert(k, v);
- }
-}
-
-fn main() {}
+++ /dev/null
-error: usage of `contains_key` followed by `insert` on a `HashMap`
- --> $DIR/entry_fixable.rs:12:5
- |
-LL | / if !m.contains_key(&k) {
-LL | | m.insert(k, v);
-LL | | }
- | |_____^ help: consider using: `m.entry(k).or_insert(v);`
- |
- = note: `-D clippy::map-entry` implied by `-D warnings`
-
-error: aborting due to previous error
-
+++ /dev/null
-#![allow(unused, clippy::needless_pass_by_value)]
-#![warn(clippy::map_entry)]
-
-use std::collections::{BTreeMap, HashMap};
-use std::hash::Hash;
-
-fn foo() {}
-
-fn insert_if_absent2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
- if !m.contains_key(&k) {
- m.insert(k, v)
- } else {
- None
- };
-}
-
-fn insert_if_present2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
- if m.contains_key(&k) {
- None
- } else {
- m.insert(k, v)
- };
-}
-
-fn insert_if_absent3<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
- if !m.contains_key(&k) {
- foo();
- m.insert(k, v)
- } else {
- None
- };
-}
-
-fn insert_if_present3<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
- if m.contains_key(&k) {
- None
- } else {
- foo();
- m.insert(k, v)
- };
-}
-
-fn insert_in_btreemap<K: Ord, V>(m: &mut BTreeMap<K, V>, k: K, v: V) {
- if !m.contains_key(&k) {
- foo();
- m.insert(k, v)
- } else {
- None
- };
-}
-
-// should not trigger
-fn insert_other_if_absent<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, o: K, v: V) {
- if !m.contains_key(&k) {
- m.insert(o, v);
- }
-}
-
-// should not trigger, because the one uses different HashMap from another one
-fn insert_from_different_map<K: Eq + Hash, V>(m: HashMap<K, V>, n: &mut HashMap<K, V>, k: K, v: V) {
- if !m.contains_key(&k) {
- n.insert(k, v);
- }
-}
-
-// should not trigger, because the one uses different HashMap from another one
-fn insert_from_different_map2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, n: &mut HashMap<K, V>, k: K, v: V) {
- if !m.contains_key(&k) {
- n.insert(k, v);
- }
-}
-
-fn main() {}
+++ /dev/null
-error: usage of `contains_key` followed by `insert` on a `HashMap`
- --> $DIR/entry_unfixable.rs:10:5
- |
-LL | / if !m.contains_key(&k) {
-LL | | m.insert(k, v)
-LL | | } else {
-LL | | None
-LL | | };
- | |_____^ consider using `m.entry(k)`
- |
- = note: `-D clippy::map-entry` implied by `-D warnings`
-
-error: usage of `contains_key` followed by `insert` on a `HashMap`
- --> $DIR/entry_unfixable.rs:18:5
- |
-LL | / if m.contains_key(&k) {
-LL | | None
-LL | | } else {
-LL | | m.insert(k, v)
-LL | | };
- | |_____^ consider using `m.entry(k)`
-
-error: usage of `contains_key` followed by `insert` on a `HashMap`
- --> $DIR/entry_unfixable.rs:26:5
- |
-LL | / if !m.contains_key(&k) {
-LL | | foo();
-LL | | m.insert(k, v)
-LL | | } else {
-LL | | None
-LL | | };
- | |_____^ consider using `m.entry(k)`
-
-error: usage of `contains_key` followed by `insert` on a `HashMap`
- --> $DIR/entry_unfixable.rs:35:5
- |
-LL | / if m.contains_key(&k) {
-LL | | None
-LL | | } else {
-LL | | foo();
-LL | | m.insert(k, v)
-LL | | };
- | |_____^ consider using `m.entry(k)`
-
-error: usage of `contains_key` followed by `insert` on a `BTreeMap`
- --> $DIR/entry_unfixable.rs:44:5
- |
-LL | / if !m.contains_key(&k) {
-LL | | foo();
-LL | | m.insert(k, v)
-LL | | } else {
-LL | | None
-LL | | };
- | |_____^ consider using `m.entry(k)`
-
-error: aborting due to 5 previous errors
-
--- /dev/null
+// run-rustfix
+
+#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
+#![warn(clippy::map_entry)]
+
+use std::collections::{BTreeMap, HashMap};
+use std::hash::Hash;
+
+macro_rules! m {
+ ($e:expr) => {{ $e }};
+}
+
+fn foo() {}
+
+fn insert_if_absent0<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2: V) {
+ match m.entry(k) {
+ std::collections::hash_map::Entry::Vacant(e) => {
+ e.insert(v);
+ }
+ std::collections::hash_map::Entry::Occupied(mut e) => {
+ e.insert(v2);
+ }
+ }
+
+ match m.entry(k) {
+ std::collections::hash_map::Entry::Occupied(mut e) => {
+ e.insert(v);
+ }
+ std::collections::hash_map::Entry::Vacant(e) => {
+ e.insert(v2);
+ }
+ }
+
+ if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+ e.insert(v);
+ } else {
+ foo();
+ }
+
+ if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+ e.insert(v);
+ } else {
+ foo();
+ }
+
+ match m.entry(k) {
+ std::collections::hash_map::Entry::Vacant(e) => {
+ e.insert(v);
+ }
+ std::collections::hash_map::Entry::Occupied(mut e) => {
+ e.insert(v2);
+ }
+ }
+
+ match m.entry(k) {
+ std::collections::hash_map::Entry::Occupied(mut e) => {
+ if true { Some(e.insert(v)) } else { Some(e.insert(v2)) }
+ }
+ std::collections::hash_map::Entry::Vacant(e) => {
+ e.insert(v);
+ None
+ }
+ };
+
+ if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+ foo();
+ Some(e.insert(v))
+ } else {
+ None
+ };
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+
+#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
+#![warn(clippy::map_entry)]
+
+use std::collections::{BTreeMap, HashMap};
+use std::hash::Hash;
+
+macro_rules! m {
+ ($e:expr) => {{ $e }};
+}
+
+fn foo() {}
+
+fn insert_if_absent0<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2: V) {
+ if !m.contains_key(&k) {
+ m.insert(k, v);
+ } else {
+ m.insert(k, v2);
+ }
+
+ if m.contains_key(&k) {
+ m.insert(k, v);
+ } else {
+ m.insert(k, v2);
+ }
+
+ if !m.contains_key(&k) {
+ m.insert(k, v);
+ } else {
+ foo();
+ }
+
+ if !m.contains_key(&k) {
+ foo();
+ } else {
+ m.insert(k, v);
+ }
+
+ if !m.contains_key(&k) {
+ m.insert(k, v);
+ } else {
+ m.insert(k, v2);
+ }
+
+ if m.contains_key(&k) {
+ if true { m.insert(k, v) } else { m.insert(k, v2) }
+ } else {
+ m.insert(k, v)
+ };
+
+ if m.contains_key(&k) {
+ foo();
+ m.insert(k, v)
+ } else {
+ None
+ };
+}
+
+fn main() {}
--- /dev/null
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:16:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | m.insert(k, v);
+LL | | } else {
+LL | | m.insert(k, v2);
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::map-entry` implied by `-D warnings`
+help: try this
+ |
+LL | match m.entry(k) {
+LL | std::collections::hash_map::Entry::Vacant(e) => {
+LL | e.insert(v);
+LL | }
+LL | std::collections::hash_map::Entry::Occupied(mut e) => {
+LL | e.insert(v2);
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:22:5
+ |
+LL | / if m.contains_key(&k) {
+LL | | m.insert(k, v);
+LL | | } else {
+LL | | m.insert(k, v2);
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL | match m.entry(k) {
+LL | std::collections::hash_map::Entry::Occupied(mut e) => {
+LL | e.insert(v);
+LL | }
+LL | std::collections::hash_map::Entry::Vacant(e) => {
+LL | e.insert(v2);
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:28:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | m.insert(k, v);
+LL | | } else {
+LL | | foo();
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+LL | e.insert(v);
+LL | } else {
+LL | foo();
+LL | }
+ |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:34:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | foo();
+LL | | } else {
+LL | | m.insert(k, v);
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL | if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+LL | e.insert(v);
+LL | } else {
+LL | foo();
+LL | }
+ |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:40:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | m.insert(k, v);
+LL | | } else {
+LL | | m.insert(k, v2);
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL | match m.entry(k) {
+LL | std::collections::hash_map::Entry::Vacant(e) => {
+LL | e.insert(v);
+LL | }
+LL | std::collections::hash_map::Entry::Occupied(mut e) => {
+LL | e.insert(v2);
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:46:5
+ |
+LL | / if m.contains_key(&k) {
+LL | | if true { m.insert(k, v) } else { m.insert(k, v2) }
+LL | | } else {
+LL | | m.insert(k, v)
+LL | | };
+ | |_____^
+ |
+help: try this
+ |
+LL | match m.entry(k) {
+LL | std::collections::hash_map::Entry::Occupied(mut e) => {
+LL | if true { Some(e.insert(v)) } else { Some(e.insert(v2)) }
+LL | }
+LL | std::collections::hash_map::Entry::Vacant(e) => {
+LL | e.insert(v);
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:52:5
+ |
+LL | / if m.contains_key(&k) {
+LL | | foo();
+LL | | m.insert(k, v)
+LL | | } else {
+LL | | None
+LL | | };
+ | |_____^
+ |
+help: try this
+ |
+LL | if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+LL | foo();
+LL | Some(e.insert(v))
+LL | } else {
+LL | None
+LL | };
+ |
+
+error: aborting due to 7 previous errors
+
+++ /dev/null
-#![warn(clippy::all, clippy::pedantic)]
-#![allow(clippy::clippy::let_underscore_drop)]
-#![allow(clippy::missing_docs_in_private_items)]
-
-fn main() {
- let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect();
-
- let _: Vec<_> = vec![5_i8; 6]
- .into_iter()
- .filter(|&x| x == 0)
- .flat_map(|x| x.checked_mul(2))
- .collect();
-
- let _: Vec<_> = vec![5_i8; 6]
- .into_iter()
- .filter_map(|x| x.checked_mul(2))
- .flat_map(|x| x.checked_mul(2))
- .collect();
-
- let _: Vec<_> = vec![5_i8; 6]
- .into_iter()
- .filter_map(|x| x.checked_mul(2))
- .map(|x| x.checked_mul(2))
- .collect();
-}
+++ /dev/null
-error: called `filter(..).flat_map(..)` on an `Iterator`
- --> $DIR/filter_methods.rs:8:21
- |
-LL | let _: Vec<_> = vec![5_i8; 6]
- | _____________________^
-LL | | .into_iter()
-LL | | .filter(|&x| x == 0)
-LL | | .flat_map(|x| x.checked_mul(2))
- | |_______________________________________^
- |
- = note: `-D clippy::filter-map` implied by `-D warnings`
- = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()`
-
-error: called `filter_map(..).flat_map(..)` on an `Iterator`
- --> $DIR/filter_methods.rs:14:21
- |
-LL | let _: Vec<_> = vec![5_i8; 6]
- | _____________________^
-LL | | .into_iter()
-LL | | .filter_map(|x| x.checked_mul(2))
-LL | | .flat_map(|x| x.checked_mul(2))
- | |_______________________________________^
- |
- = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()`
-
-error: called `filter_map(..).map(..)` on an `Iterator`
- --> $DIR/filter_methods.rs:20:21
- |
-LL | let _: Vec<_> = vec![5_i8; 6]
- | _____________________^
-LL | | .into_iter()
-LL | | .filter_map(|x| x.checked_mul(2))
-LL | | .map(|x| x.checked_mul(2))
- | |__________________________________^
- |
- = help: this is more succinctly expressed by only calling `.filter_map(..)` instead
-
-error: aborting due to 3 previous errors
-
--- /dev/null
+// run-rustfix
+#![warn(clippy::flat_map_option)]
+#![allow(clippy::redundant_closure, clippy::unnecessary_filter_map)]
+
+fn main() {
+ // yay
+ let c = |x| Some(x);
+ let _ = [1].iter().filter_map(c);
+ let _ = [1].iter().filter_map(Some);
+
+ // nay
+ let _ = [1].iter().flat_map(|_| &Some(1));
+}
--- /dev/null
+// run-rustfix
+#![warn(clippy::flat_map_option)]
+#![allow(clippy::redundant_closure, clippy::unnecessary_filter_map)]
+
+fn main() {
+ // yay
+ let c = |x| Some(x);
+ let _ = [1].iter().flat_map(c);
+ let _ = [1].iter().flat_map(Some);
+
+ // nay
+ let _ = [1].iter().flat_map(|_| &Some(1));
+}
--- /dev/null
+error: used `flat_map` where `filter_map` could be used instead
+ --> $DIR/flat_map_option.rs:8:24
+ |
+LL | let _ = [1].iter().flat_map(c);
+ | ^^^^^^^^ help: try: `filter_map`
+ |
+ = note: `-D clippy::flat-map-option` implied by `-D warnings`
+
+error: used `flat_map` where `filter_map` could be used instead
+ --> $DIR/flat_map_option.rs:9:24
+ |
+LL | let _ = [1].iter().flat_map(Some);
+ | ^^^^^^^^ help: try: `filter_map`
+
+error: aborting due to 2 previous errors
+
for _ in rr.into_iter() {}
}
}
+
+// explicit_into_iter_loop
+#[warn(clippy::explicit_into_iter_loop)]
+mod issue_6900 {
+ struct S;
+ impl S {
+ #[allow(clippy::should_implement_trait)]
+ pub fn into_iter<T>(self) -> I<T> {
+ unimplemented!()
+ }
+ }
+
+ struct I<T>(T);
+ impl<T> Iterator for I<T> {
+ type Item = T;
+ fn next(&mut self) -> Option<Self::Item> {
+ unimplemented!()
+ }
+ }
+
+ fn f() {
+ for _ in S.into_iter::<u32>() {
+ unimplemented!()
+ }
+ }
+}
for _ in rr.into_iter() {}
}
}
+
+// explicit_into_iter_loop
+#[warn(clippy::explicit_into_iter_loop)]
+mod issue_6900 {
+ struct S;
+ impl S {
+ #[allow(clippy::should_implement_trait)]
+ pub fn into_iter<T>(self) -> I<T> {
+ unimplemented!()
+ }
+ }
+
+ struct I<T>(T);
+ impl<T> Iterator for I<T> {
+ type Item = T;
+ fn next(&mut self) -> Option<Self::Item> {
+ unimplemented!()
+ }
+ }
+
+ fn f() {
+ for _ in S.into_iter::<u32>() {
+ unimplemented!()
+ }
+ }
+}
// False positive
let a = "foo".to_string();
let _ = Some(a + "bar");
+
+ // Wrap it with braces
+ let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
+ let _s: String = (&*v.join("\n")).to_string();
}
// False positive
let a = "foo".to_string();
let _ = Some(format!("{}", a + "bar"));
+
+ // Wrap it with braces
+ let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
+ let _s: String = format!("{}", &*v.join("\n"));
}
LL | let _ = Some(format!("{}", a + "bar"));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"`
-error: aborting due to 13 previous errors
+error: useless use of `format!`
+ --> $DIR/format.rs:73:22
+ |
+LL | let _s: String = format!("{}", &*v.join("/n"));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()`
+
+error: aborting due to 14 previous errors
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::from-over-into` implied by `-D warnings`
- = help: consider to implement `From` instead
+ = help: consider to implement `From<std::string::String>` instead
error: aborting due to previous error
z: i32,
}
+macro_rules! new_foo {
+ () => {
+ let x = 1;
+ let y = 1;
+ let z = 1;
+ Foo { y, x, z }
+ };
+}
+
mod without_base {
use super::Foo;
// Should lint.
Foo { x, y, z };
+ // Should NOT lint.
+ // issue #7069.
+ new_foo!();
+
// Shoule NOT lint because the order is the same as in the definition.
Foo { x, y, z };
z: i32,
}
+macro_rules! new_foo {
+ () => {
+ let x = 1;
+ let y = 1;
+ let z = 1;
+ Foo { y, x, z }
+ };
+}
+
mod without_base {
use super::Foo;
// Should lint.
Foo { y, x, z };
+ // Should NOT lint.
+ // issue #7069.
+ new_foo!();
+
// Shoule NOT lint because the order is the same as in the definition.
Foo { x, y, z };
error: struct constructor field order is inconsistent with struct definition field order
- --> $DIR/inconsistent_struct_constructor.rs:25:9
+ --> $DIR/inconsistent_struct_constructor.rs:34:9
|
LL | Foo { y, x, z };
| ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }`
= note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings`
error: struct constructor field order is inconsistent with struct definition field order
- --> $DIR/inconsistent_struct_constructor.rs:43:9
+ --> $DIR/inconsistent_struct_constructor.rs:56:9
|
LL | / Foo {
LL | | z,
--- /dev/null
+// run-rustfix
+
+fn main() {
+ unsafe {
+ let _slice: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
+ let _slice: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
+
+ let _slice: &[usize] = std::slice::from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0);
+
+ std::ptr::copy::<usize>(core::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0);
+ std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0);
+
+ std::ptr::copy_nonoverlapping::<usize>(core::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0);
+ std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0);
+
+ struct A; // zero sized struct
+ assert_eq!(std::mem::size_of::<A>(), 0);
+
+ let _a: A = std::ptr::read(core::ptr::NonNull::dangling().as_ptr());
+ let _a: A = std::ptr::read(core::ptr::NonNull::dangling().as_ptr());
+
+ let _a: A = std::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr());
+ let _a: A = std::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr());
+
+ let _a: A = std::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr());
+ let _a: A = std::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr());
+
+ let _a: A = std::ptr::replace(core::ptr::NonNull::dangling().as_ptr(), A);
+
+ let _slice: *const [usize] = std::ptr::slice_from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
+ let _slice: *const [usize] = std::ptr::slice_from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
+
+ let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0);
+
+ std::ptr::swap::<A>(core::ptr::NonNull::dangling().as_ptr(), &mut A);
+ std::ptr::swap::<A>(&mut A, core::ptr::NonNull::dangling().as_ptr());
+
+ std::ptr::swap_nonoverlapping::<A>(core::ptr::NonNull::dangling().as_ptr(), &mut A, 0);
+ std::ptr::swap_nonoverlapping::<A>(&mut A, core::ptr::NonNull::dangling().as_ptr(), 0);
+
+ std::ptr::write(core::ptr::NonNull::dangling().as_ptr(), A);
+
+ std::ptr::write_unaligned(core::ptr::NonNull::dangling().as_ptr(), A);
+
+ std::ptr::write_volatile(core::ptr::NonNull::dangling().as_ptr(), A);
+
+ std::ptr::write_bytes::<usize>(core::ptr::NonNull::dangling().as_ptr(), 42, 0);
+ }
+}
--- /dev/null
+// run-rustfix
+
+fn main() {
+ unsafe {
+ let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0);
+ let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0);
+
+ let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0);
+
+ std::ptr::copy::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0);
+ std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0);
+
+ std::ptr::copy_nonoverlapping::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0);
+ std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0);
+
+ struct A; // zero sized struct
+ assert_eq!(std::mem::size_of::<A>(), 0);
+
+ let _a: A = std::ptr::read(std::ptr::null());
+ let _a: A = std::ptr::read(std::ptr::null_mut());
+
+ let _a: A = std::ptr::read_unaligned(std::ptr::null());
+ let _a: A = std::ptr::read_unaligned(std::ptr::null_mut());
+
+ let _a: A = std::ptr::read_volatile(std::ptr::null());
+ let _a: A = std::ptr::read_volatile(std::ptr::null_mut());
+
+ let _a: A = std::ptr::replace(std::ptr::null_mut(), A);
+
+ let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null(), 0);
+ let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0);
+
+ let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0);
+
+ std::ptr::swap::<A>(std::ptr::null_mut(), &mut A);
+ std::ptr::swap::<A>(&mut A, std::ptr::null_mut());
+
+ std::ptr::swap_nonoverlapping::<A>(std::ptr::null_mut(), &mut A, 0);
+ std::ptr::swap_nonoverlapping::<A>(&mut A, std::ptr::null_mut(), 0);
+
+ std::ptr::write(std::ptr::null_mut(), A);
+
+ std::ptr::write_unaligned(std::ptr::null_mut(), A);
+
+ std::ptr::write_volatile(std::ptr::null_mut(), A);
+
+ std::ptr::write_bytes::<usize>(std::ptr::null_mut(), 42, 0);
+ }
+}
--- /dev/null
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:5:59
+ |
+LL | let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0);
+ | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+ |
+ = note: `#[deny(clippy::invalid_null_ptr_usage)]` on by default
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:6:59
+ |
+LL | let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:8:63
+ |
+LL | let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:10:33
+ |
+LL | std::ptr::copy::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0);
+ | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:11:73
+ |
+LL | std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:13:48
+ |
+LL | std::ptr::copy_nonoverlapping::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0);
+ | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:14:88
+ |
+LL | std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:19:36
+ |
+LL | let _a: A = std::ptr::read(std::ptr::null());
+ | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:20:36
+ |
+LL | let _a: A = std::ptr::read(std::ptr::null_mut());
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:22:46
+ |
+LL | let _a: A = std::ptr::read_unaligned(std::ptr::null());
+ | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:23:46
+ |
+LL | let _a: A = std::ptr::read_unaligned(std::ptr::null_mut());
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:25:45
+ |
+LL | let _a: A = std::ptr::read_volatile(std::ptr::null());
+ | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:26:45
+ |
+LL | let _a: A = std::ptr::read_volatile(std::ptr::null_mut());
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:28:39
+ |
+LL | let _a: A = std::ptr::replace(std::ptr::null_mut(), A);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:30:69
+ |
+LL | let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null(), 0);
+ | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:31:69
+ |
+LL | let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:33:73
+ |
+LL | let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:35:29
+ |
+LL | std::ptr::swap::<A>(std::ptr::null_mut(), &mut A);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:36:37
+ |
+LL | std::ptr::swap::<A>(&mut A, std::ptr::null_mut());
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:38:44
+ |
+LL | std::ptr::swap_nonoverlapping::<A>(std::ptr::null_mut(), &mut A, 0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:39:52
+ |
+LL | std::ptr::swap_nonoverlapping::<A>(&mut A, std::ptr::null_mut(), 0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:41:25
+ |
+LL | std::ptr::write(std::ptr::null_mut(), A);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:43:35
+ |
+LL | std::ptr::write_unaligned(std::ptr::null_mut(), A);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:45:34
+ |
+LL | std::ptr::write_volatile(std::ptr::null_mut(), A);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:47:40
+ |
+LL | std::ptr::write_bytes::<usize>(std::ptr::null_mut(), 42, 0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: aborting due to 25 previous errors
+
let _: Vec<u8> = std::ffi::CStr::from_ptr(std::ptr::null())
.to_bytes().to_vec();
}
+
+ // Issue #6808
+ let arr: [u8; 64] = [0; 64];
+ let _: Vec<_> = arr.to_vec();
}
.cloned()
.collect();
}
+
+ // Issue #6808
+ let arr: [u8; 64] = [0; 64];
+ let _: Vec<_> = arr.iter().cloned().collect();
}
LL | | .collect();
| |______________________^ help: try: `.to_vec()`
-error: aborting due to 3 previous errors
+error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
+ --> $DIR/iter_cloned_collect.rs:28:24
+ |
+LL | let _: Vec<_> = arr.iter().cloned().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
+
+error: aborting due to 4 previous errors
// run-rustfix
// ignore-32bit
-#![allow(unused_imports, unreachable_code, unused_variables, dead_code)]
+#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
#![allow(clippy::single_component_path_imports)]
#![warn(clippy::macro_use_imports)]
}
}
+// issue #7015, ICE due to calling `item_children` with local `DefId`
+#[macro_use]
+use a as b;
+
fn main() {}
// run-rustfix
// ignore-32bit
-#![allow(unused_imports, unreachable_code, unused_variables, dead_code)]
+#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
#![allow(clippy::single_component_path_imports)]
#![warn(clippy::macro_use_imports)]
}
}
+// issue #7015, ICE due to calling `item_children` with local `DefId`
+#[macro_use]
+use a as b;
+
fn main() {}
None => None,
};
}
+
+ // #7077
+ let s = &String::new();
+ let _: Option<&str> = match Some(s) {
+ Some(s) => Some(s),
+ None => None,
+ };
}
None => None,
};
}
+
+ // #7077
+ let s = &String::new();
+ let _: Option<&str> = match Some(s) {
+ Some(s) => Some(s),
+ None => None,
+ };
}
};
}
+mod issue6965 {
+ macro_rules! some_macro {
+ () => {
+ if 1 > 2 { Some(1) } else { None }
+ };
+ }
+
+ fn test() {
+ let _ = some_macro!().unwrap_or(0);
+ }
+}
+
fn main() {}
};
}
+mod issue6965 {
+ macro_rules! some_macro {
+ () => {
+ if 1 > 2 { Some(1) } else { None }
+ };
+ }
+
+ fn test() {
+ let _ = match some_macro!() {
+ Some(val) => val,
+ None => 0,
+ };
+ }
+}
+
fn main() {}
LL | | };
| |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")`
-error: aborting due to 13 previous errors
+error: this pattern reimplements `Option::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:201:17
+ |
+LL | let _ = match some_macro!() {
+ | _________________^
+LL | | Some(val) => val,
+LL | | None => 0,
+LL | | };
+ | |_________^ help: replace with: `some_macro!().unwrap_or(0)`
+
+error: aborting due to 14 previous errors
use std::ops::{Deref, RangeFrom};
+fn cloned_instead_of_copied() {
+ let _ = [1].iter().cloned();
+}
+
fn option_as_ref_deref() {
let mut opt = Some(String::from("123"));
error: stripping a prefix manually
- --> $DIR/min_rust_version_attr.rs:156:24
+ --> $DIR/min_rust_version_attr.rs:160:24
|
LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
| ^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::manual-strip` implied by `-D warnings`
note: the prefix was tested here
- --> $DIR/min_rust_version_attr.rs:155:9
+ --> $DIR/min_rust_version_attr.rs:159:9
|
LL | if s.starts_with("hello, ") {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: stripping a prefix manually
- --> $DIR/min_rust_version_attr.rs:168:24
+ --> $DIR/min_rust_version_attr.rs:172:24
|
LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
| ^^^^^^^^^^^^^^^^^^^^
|
note: the prefix was tested here
- --> $DIR/min_rust_version_attr.rs:167:9
+ --> $DIR/min_rust_version_attr.rs:171:9
|
LL | if s.starts_with("hello, ") {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
--- /dev/null
+// This file provides a const function that is unstably const forever.
+
+#![feature(staged_api)]
+#![stable(feature = "1", since = "1.0.0")]
+
+#[stable(feature = "1", since = "1.0.0")]
+#[rustc_const_unstable(feature = "foo", issue = "none")]
+pub const fn unstably_const_fn() {}
//! compilation error.
//! The .stderr output of this test should be empty. Otherwise it's a bug somewhere.
+// aux-build:helper.rs
+
#![warn(clippy::missing_const_for_fn)]
#![allow(incomplete_features)]
#![feature(start, const_generics)]
+#![feature(custom_inner_attributes)]
+
+extern crate helper;
struct Game;
unsafe { &*p }
}
+
+// Do not lint this because it calls a function whose constness is unstable.
+fn unstably_const_fn() {
+ helper::unstably_const_fn()
+}
+
+mod const_fn_stabilized_after_msrv {
+ #![clippy::msrv = "1.46.0"]
+
+ // Do not lint this because `u8::is_ascii_digit` is stabilized as a const function in 1.47.0.
+ fn const_fn_stabilized_after_msrv(byte: u8) {
+ byte.is_ascii_digit();
+ }
+}
#![warn(clippy::missing_const_for_fn)]
#![allow(incomplete_features, clippy::let_and_return)]
#![feature(const_generics)]
+#![feature(custom_inner_attributes)]
use std::mem::transmute;
}
}
+mod const_fn_stabilized_before_msrv {
+ #![clippy::msrv = "1.47.0"]
+
+ // This could be const because `u8::is_ascii_digit` is a stable const function in 1.47.
+ fn const_fn_stabilized_before_msrv(byte: u8) {
+ byte.is_ascii_digit();
+ }
+}
+
// Should not be const
fn main() {}
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:13:5
+ --> $DIR/could_be_const.rs:14:5
|
LL | / pub fn new() -> Self {
LL | | Self { guess: 42 }
= note: `-D clippy::missing-const-for-fn` implied by `-D warnings`
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:17:5
+ --> $DIR/could_be_const.rs:18:5
|
LL | / fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
LL | | b
| |_____^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:23:1
+ --> $DIR/could_be_const.rs:24:1
|
LL | / fn one() -> i32 {
LL | | 1
| |_^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:28:1
+ --> $DIR/could_be_const.rs:29:1
|
LL | / fn two() -> i32 {
LL | | let abc = 2;
| |_^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:34:1
+ --> $DIR/could_be_const.rs:35:1
|
LL | / fn string() -> String {
LL | | String::new()
| |_^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:39:1
+ --> $DIR/could_be_const.rs:40:1
|
LL | / unsafe fn four() -> i32 {
LL | | 4
| |_^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:44:1
+ --> $DIR/could_be_const.rs:45:1
|
LL | / fn generic<T>(t: T) -> T {
LL | | t
| |_^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:67:9
+ --> $DIR/could_be_const.rs:68:9
|
LL | / pub fn b(self, a: &A) -> B {
LL | | B
LL | | }
| |_________^
-error: aborting due to 8 previous errors
+error: this could be a `const fn`
+ --> $DIR/could_be_const.rs:78:5
+ |
+LL | / fn const_fn_stabilized_before_msrv(byte: u8) {
+LL | | byte.is_ascii_digit();
+LL | | }
+ | |_____^
+
+error: aborting due to 9 previous errors
fn main() {}
-mod question_mark_none {
- #![clippy::msrv = "1.12.0"]
- fn needless_question_mark_option() -> Option<usize> {
- struct TO {
- magic: Option<usize>,
- }
- let to = TO { magic: None };
- Some(to.magic?) // should not be triggered
- }
-
- fn needless_question_mark_result() -> Result<usize, bool> {
- struct TO {
- magic: Result<usize, bool>,
- }
- let to = TO { magic: Ok(1_usize) };
- Ok(to.magic?) // should not be triggered
- }
-
- fn main() {
- needless_question_mark_option();
- needless_question_mark_result();
- }
-}
-
-mod question_mark_result {
- #![clippy::msrv = "1.21.0"]
- fn needless_question_mark_option() -> Option<usize> {
- struct TO {
- magic: Option<usize>,
- }
- let to = TO { magic: None };
- Some(to.magic?) // should not be triggered
- }
-
- fn needless_question_mark_result() -> Result<usize, bool> {
- struct TO {
- magic: Result<usize, bool>,
- }
- let to = TO { magic: Ok(1_usize) };
- to.magic // should be triggered
- }
-
- fn main() {
- needless_question_mark_option();
- needless_question_mark_result();
- }
-}
-
-mod question_mark_both {
- #![clippy::msrv = "1.22.0"]
- fn needless_question_mark_option() -> Option<usize> {
- struct TO {
- magic: Option<usize>,
- }
- let to = TO { magic: None };
- to.magic // should be triggered
- }
-
- fn needless_question_mark_result() -> Result<usize, bool> {
- struct TO {
- magic: Result<usize, bool>,
- }
- let to = TO { magic: Ok(1_usize) };
- to.magic // should be triggered
- }
-
- fn main() {
- needless_question_mark_option();
- needless_question_mark_result();
- }
-}
-
// #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use,
// the suggestion fails to apply; do not lint
macro_rules! some_in_macro {
fn main() {}
-mod question_mark_none {
- #![clippy::msrv = "1.12.0"]
- fn needless_question_mark_option() -> Option<usize> {
- struct TO {
- magic: Option<usize>,
- }
- let to = TO { magic: None };
- Some(to.magic?) // should not be triggered
- }
-
- fn needless_question_mark_result() -> Result<usize, bool> {
- struct TO {
- magic: Result<usize, bool>,
- }
- let to = TO { magic: Ok(1_usize) };
- Ok(to.magic?) // should not be triggered
- }
-
- fn main() {
- needless_question_mark_option();
- needless_question_mark_result();
- }
-}
-
-mod question_mark_result {
- #![clippy::msrv = "1.21.0"]
- fn needless_question_mark_option() -> Option<usize> {
- struct TO {
- magic: Option<usize>,
- }
- let to = TO { magic: None };
- Some(to.magic?) // should not be triggered
- }
-
- fn needless_question_mark_result() -> Result<usize, bool> {
- struct TO {
- magic: Result<usize, bool>,
- }
- let to = TO { magic: Ok(1_usize) };
- Ok(to.magic?) // should be triggered
- }
-
- fn main() {
- needless_question_mark_option();
- needless_question_mark_result();
- }
-}
-
-mod question_mark_both {
- #![clippy::msrv = "1.22.0"]
- fn needless_question_mark_option() -> Option<usize> {
- struct TO {
- magic: Option<usize>,
- }
- let to = TO { magic: None };
- Some(to.magic?) // should be triggered
- }
-
- fn needless_question_mark_result() -> Result<usize, bool> {
- struct TO {
- magic: Result<usize, bool>,
- }
- let to = TO { magic: Ok(1_usize) };
- Ok(to.magic?) // should be triggered
- }
-
- fn main() {
- needless_question_mark_option();
- needless_question_mark_result();
- }
-}
-
// #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use,
// the suggestion fails to apply; do not lint
macro_rules! some_in_macro {
| ^^^^^^^^^^^^ help: try: `t.magic`
error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:138:9
- |
-LL | Ok(to.magic?) // should be triggered
- | ^^^^^^^^^^^^^ help: try: `to.magic`
-
-error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:154:9
- |
-LL | Some(to.magic?) // should be triggered
- | ^^^^^^^^^^^^^^^ help: try: `to.magic`
-
-error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:162:9
- |
-LL | Ok(to.magic?) // should be triggered
- | ^^^^^^^^^^^^^ help: try: `to.magic`
-
-error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:187:27
+ --> $DIR/needless_question_mark.rs:115:27
|
LL | || -> Option<_> { Some(Some($expr)?) }()
| ^^^^^^^^^^^^^^^^^^ help: try: `Some($expr)`
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-error: aborting due to 15 previous errors
+error: aborting due to 12 previous errors
// run-rustfix
+// edition:2018
#![allow(unused)]
#![allow(
}
}
-fn main() {
- let _ = test_end_of_fn();
- let _ = test_no_semicolon();
- let _ = test_if_block();
- let _ = test_match(true);
- test_closure();
+async fn async_test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ true
+}
+
+async fn async_test_no_semicolon() -> bool {
+ true
+}
+
+async fn async_test_if_block() -> bool {
+ if true {
+ true
+ } else {
+ false
+ }
+}
+
+async fn async_test_match(x: bool) -> bool {
+ match x {
+ true => false,
+ false => {
+ true
+ },
+ }
+}
+
+async fn async_test_closure() {
+ let _ = || {
+ true
+ };
+ let _ = || true;
+}
+
+async fn async_test_macro_call() -> i32 {
+ return the_answer!();
+}
+
+async fn async_test_void_fun() {
+
+}
+
+async fn async_test_void_if_fun(b: bool) {
+ if b {
+
+ } else {
+
+ }
+}
+
+async fn async_test_void_match(x: u32) {
+ match x {
+ 0 => (),
+ _ => {},
+ }
+}
+
+async fn async_read_line() -> String {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ return stdin.lock().lines().next().unwrap().unwrap();
}
+
+async fn async_borrows_but_not_last(value: bool) -> String {
+ if value {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ let _a = stdin.lock().lines().next().unwrap().unwrap();
+ String::from("test")
+ } else {
+ String::new()
+ }
+}
+
+async fn async_test_return_in_macro() {
+ needed_return!(10);
+ needed_return!(0);
+}
+
+fn main() {}
// run-rustfix
+// edition:2018
#![allow(unused)]
#![allow(
}
}
-fn main() {
- let _ = test_end_of_fn();
- let _ = test_no_semicolon();
- let _ = test_if_block();
- let _ = test_match(true);
- test_closure();
+async fn async_test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ return true;
+}
+
+async fn async_test_no_semicolon() -> bool {
+ return true;
+}
+
+async fn async_test_if_block() -> bool {
+ if true {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+async fn async_test_match(x: bool) -> bool {
+ match x {
+ true => return false,
+ false => {
+ return true;
+ },
+ }
+}
+
+async fn async_test_closure() {
+ let _ = || {
+ return true;
+ };
+ let _ = || return true;
+}
+
+async fn async_test_macro_call() -> i32 {
+ return the_answer!();
+}
+
+async fn async_test_void_fun() {
+ return;
+}
+
+async fn async_test_void_if_fun(b: bool) {
+ if b {
+ return;
+ } else {
+ return;
+ }
+}
+
+async fn async_test_void_match(x: u32) {
+ match x {
+ 0 => (),
+ _ => return,
+ }
+}
+
+async fn async_read_line() -> String {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ return stdin.lock().lines().next().unwrap().unwrap();
}
+
+async fn async_borrows_but_not_last(value: bool) -> String {
+ if value {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ let _a = stdin.lock().lines().next().unwrap().unwrap();
+ return String::from("test");
+ } else {
+ return String::new();
+ }
+}
+
+async fn async_test_return_in_macro() {
+ needed_return!(10);
+ needed_return!(0);
+}
+
+fn main() {}
error: unneeded `return` statement
- --> $DIR/needless_return.rs:23:5
+ --> $DIR/needless_return.rs:24:5
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
= note: `-D clippy::needless-return` implied by `-D warnings`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:27:5
+ --> $DIR/needless_return.rs:28:5
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:32:9
+ --> $DIR/needless_return.rs:33:9
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:34:9
+ --> $DIR/needless_return.rs:35:9
|
LL | return false;
| ^^^^^^^^^^^^^ help: remove `return`: `false`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:40:17
+ --> $DIR/needless_return.rs:41:17
|
LL | true => return false,
| ^^^^^^^^^^^^ help: remove `return`: `false`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:42:13
+ --> $DIR/needless_return.rs:43:13
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:49:9
+ --> $DIR/needless_return.rs:50:9
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:51:16
+ --> $DIR/needless_return.rs:52:16
|
LL | let _ = || return true;
| ^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:59:5
+ --> $DIR/needless_return.rs:60:5
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:64:9
+ --> $DIR/needless_return.rs:65:9
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:66:9
+ --> $DIR/needless_return.rs:67:9
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:73:14
+ --> $DIR/needless_return.rs:74:14
|
LL | _ => return,
| ^^^^^^ help: replace `return` with an empty block: `{}`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:88:9
+ --> $DIR/needless_return.rs:89:9
|
LL | return String::from("test");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:90:9
+ --> $DIR/needless_return.rs:91:9
|
LL | return String::new();
| ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:111:32
+ --> $DIR/needless_return.rs:112:32
|
LL | bar.unwrap_or_else(|_| return)
| ^^^^^^ help: replace `return` with an empty block: `{}`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:116:13
+ --> $DIR/needless_return.rs:117:13
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:118:20
+ --> $DIR/needless_return.rs:119:20
|
LL | let _ = || return;
| ^^^^^^ help: replace `return` with an empty block: `{}`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:124:32
+ --> $DIR/needless_return.rs:125:32
|
LL | res.unwrap_or_else(|_| return Foo)
| ^^^^^^^^^^ help: remove `return`: `Foo`
-error: aborting due to 18 previous errors
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:134:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:138:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:143:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:145:9
+ |
+LL | return false;
+ | ^^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:151:17
+ |
+LL | true => return false,
+ | ^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:153:13
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:160:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:162:16
+ |
+LL | let _ = || return true;
+ | ^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:170:5
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:175:9
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:177:9
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:184:14
+ |
+LL | _ => return,
+ | ^^^^^^ help: replace `return` with an empty block: `{}`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:199:9
+ |
+LL | return String::from("test");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:201:9
+ |
+LL | return String::new();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
+
+error: aborting due to 32 previous errors
#![warn(clippy::panic_in_result_fn)]
#![allow(clippy::unnecessary_wraps)]
+// debug_assert should never trigger the `panic_in_result_fn` lint
+
struct A;
impl A {
- fn result_with_debug_assert_with_message(x: i32) -> Result<bool, String> // should emit lint
- {
+ fn result_with_debug_assert_with_message(x: i32) -> Result<bool, String> {
debug_assert!(x == 5, "wrong argument");
Ok(true)
}
- fn result_with_debug_assert_eq(x: i32) -> Result<bool, String> // should emit lint
- {
+ fn result_with_debug_assert_eq(x: i32) -> Result<bool, String> {
debug_assert_eq!(x, 5);
Ok(true)
}
- fn result_with_debug_assert_ne(x: i32) -> Result<bool, String> // should emit lint
- {
+ fn result_with_debug_assert_ne(x: i32) -> Result<bool, String> {
debug_assert_ne!(x, 1);
Ok(true)
}
- fn other_with_debug_assert_with_message(x: i32) // should not emit lint
- {
+ fn other_with_debug_assert_with_message(x: i32) {
debug_assert!(x == 5, "wrong argument");
}
- fn other_with_debug_assert_eq(x: i32) // should not emit lint
- {
+ fn other_with_debug_assert_eq(x: i32) {
debug_assert_eq!(x, 5);
}
- fn other_with_debug_assert_ne(x: i32) // should not emit lint
- {
+ fn other_with_debug_assert_ne(x: i32) {
debug_assert_ne!(x, 1);
}
- fn result_without_banned_functions() -> Result<bool, String> // should not emit lint
- {
+ fn result_without_banned_functions() -> Result<bool, String> {
let debug_assert = "debug_assert!";
println!("No {}", debug_assert);
Ok(true)
+++ /dev/null
-error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
- --> $DIR/panic_in_result_fn_debug_assertions.rs:7:5
- |
-LL | / fn result_with_debug_assert_with_message(x: i32) -> Result<bool, String> // should emit lint
-LL | | {
-LL | | debug_assert!(x == 5, "wrong argument");
-LL | | Ok(true)
-LL | | }
- | |_____^
- |
- = note: `-D clippy::panic-in-result-fn` implied by `-D warnings`
- = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
-note: return Err() instead of panicking
- --> $DIR/panic_in_result_fn_debug_assertions.rs:9:9
- |
-LL | debug_assert!(x == 5, "wrong argument");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
- --> $DIR/panic_in_result_fn_debug_assertions.rs:13:5
- |
-LL | / fn result_with_debug_assert_eq(x: i32) -> Result<bool, String> // should emit lint
-LL | | {
-LL | | debug_assert_eq!(x, 5);
-LL | | Ok(true)
-LL | | }
- | |_____^
- |
- = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
-note: return Err() instead of panicking
- --> $DIR/panic_in_result_fn_debug_assertions.rs:15:9
- |
-LL | debug_assert_eq!(x, 5);
- | ^^^^^^^^^^^^^^^^^^^^^^^
- = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
- --> $DIR/panic_in_result_fn_debug_assertions.rs:19:5
- |
-LL | / fn result_with_debug_assert_ne(x: i32) -> Result<bool, String> // should emit lint
-LL | | {
-LL | | debug_assert_ne!(x, 1);
-LL | | Ok(true)
-LL | | }
- | |_____^
- |
- = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
-note: return Err() instead of panicking
- --> $DIR/panic_in_result_fn_debug_assertions.rs:21:9
- |
-LL | debug_assert_ne!(x, 1);
- | ^^^^^^^^^^^^^^^^^^^^^^^
- = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to 3 previous errors
-
#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)]
-#![allow(clippy::assertions_on_constants)]
+#![allow(clippy::assertions_on_constants, clippy::eq_op)]
extern crate core;
unreachable!();
}
+fn debug_assert() {
+ debug_assert!(true);
+ debug_assert_eq!(true, true);
+ debug_assert_ne!(true, false);
+}
+
+fn debug_assert_msg() {
+ debug_assert!(true, "test");
+ debug_assert_eq!(true, true, "test");
+ debug_assert_ne!(true, false, "test");
+}
+
fn main() {
panic();
todo();
--- /dev/null
+// run-rustfix
+
+// Issue #5746
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(clippy::if_same_then_else)]
+use std::task::Poll::{Pending, Ready};
+
+fn main() {
+ let m = std::sync::Mutex::new((0, 0));
+
+ // Result
+ if m.lock().is_ok() {}
+ if Err::<(), _>(m.lock().unwrap().0).is_err() {}
+
+ {
+ if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {}
+ }
+ if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {
+ } else {
+ }
+ if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {}
+ if Err::<std::sync::MutexGuard<()>, _>(()).is_err() {}
+
+ if Ok::<_, ()>(String::new()).is_ok() {}
+ if Err::<(), _>((String::new(), ())).is_err() {}
+
+ // Option
+ if Some(m.lock()).is_some() {}
+ if Some(m.lock().unwrap().0).is_some() {}
+
+ {
+ if None::<std::sync::MutexGuard<()>>.is_none() {}
+ }
+ if None::<std::sync::MutexGuard<()>>.is_none() {
+ } else {
+ }
+
+ if None::<std::sync::MutexGuard<()>>.is_none() {}
+
+ if Some(String::new()).is_some() {}
+ if Some((String::new(), ())).is_some() {}
+
+ // Poll
+ if Ready(m.lock()).is_ready() {}
+ if Ready(m.lock().unwrap().0).is_ready() {}
+
+ {
+ if Pending::<std::sync::MutexGuard<()>>.is_pending() {}
+ }
+ if Pending::<std::sync::MutexGuard<()>>.is_pending() {
+ } else {
+ }
+
+ if Pending::<std::sync::MutexGuard<()>>.is_pending() {}
+
+ if Ready(String::new()).is_ready() {}
+ if Ready((String::new(), ())).is_ready() {}
+}
--- /dev/null
+// run-rustfix
+
+// Issue #5746
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(clippy::if_same_then_else)]
+use std::task::Poll::{Pending, Ready};
+
+fn main() {
+ let m = std::sync::Mutex::new((0, 0));
+
+ // Result
+ if let Ok(_) = m.lock() {}
+ if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {}
+
+ {
+ if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
+ }
+ if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {
+ } else {
+ }
+ if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
+ if let Err(_) = Err::<std::sync::MutexGuard<()>, _>(()) {}
+
+ if let Ok(_) = Ok::<_, ()>(String::new()) {}
+ if let Err(_) = Err::<(), _>((String::new(), ())) {}
+
+ // Option
+ if let Some(_) = Some(m.lock()) {}
+ if let Some(_) = Some(m.lock().unwrap().0) {}
+
+ {
+ if let None = None::<std::sync::MutexGuard<()>> {}
+ }
+ if let None = None::<std::sync::MutexGuard<()>> {
+ } else {
+ }
+
+ if let None = None::<std::sync::MutexGuard<()>> {}
+
+ if let Some(_) = Some(String::new()) {}
+ if let Some(_) = Some((String::new(), ())) {}
+
+ // Poll
+ if let Ready(_) = Ready(m.lock()) {}
+ if let Ready(_) = Ready(m.lock().unwrap().0) {}
+
+ {
+ if let Pending = Pending::<std::sync::MutexGuard<()>> {}
+ }
+ if let Pending = Pending::<std::sync::MutexGuard<()>> {
+ } else {
+ }
+
+ if let Pending = Pending::<std::sync::MutexGuard<()>> {}
+
+ if let Ready(_) = Ready(String::new()) {}
+ if let Ready(_) = Ready((String::new(), ())) {}
+}
--- /dev/null
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:12:12
+ |
+LL | if let Ok(_) = m.lock() {}
+ | -------^^^^^----------- help: try this: `if m.lock().is_ok()`
+ |
+ = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_err()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:13:12
+ |
+LL | if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {}
+ | -------^^^^^^------------------------------------ help: try this: `if Err::<(), _>(m.lock().unwrap().0).is_err()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:16:16
+ |
+LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
+ | -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:18:12
+ |
+LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {
+ | -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:21:12
+ |
+LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
+ | -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()`
+
+error: redundant pattern matching, consider using `is_err()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:22:12
+ |
+LL | if let Err(_) = Err::<std::sync::MutexGuard<()>, _>(()) {}
+ | -------^^^^^^------------------------------------------ help: try this: `if Err::<std::sync::MutexGuard<()>, _>(()).is_err()`
+
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:24:12
+ |
+LL | if let Ok(_) = Ok::<_, ()>(String::new()) {}
+ | -------^^^^^----------------------------- help: try this: `if Ok::<_, ()>(String::new()).is_ok()`
+
+error: redundant pattern matching, consider using `is_err()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:25:12
+ |
+LL | if let Err(_) = Err::<(), _>((String::new(), ())) {}
+ | -------^^^^^^------------------------------------ help: try this: `if Err::<(), _>((String::new(), ())).is_err()`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:28:12
+ |
+LL | if let Some(_) = Some(m.lock()) {}
+ | -------^^^^^^^----------------- help: try this: `if Some(m.lock()).is_some()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:29:12
+ |
+LL | if let Some(_) = Some(m.lock().unwrap().0) {}
+ | -------^^^^^^^---------------------------- help: try this: `if Some(m.lock().unwrap().0).is_some()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:32:16
+ |
+LL | if let None = None::<std::sync::MutexGuard<()>> {}
+ | -------^^^^------------------------------------ help: try this: `if None::<std::sync::MutexGuard<()>>.is_none()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:34:12
+ |
+LL | if let None = None::<std::sync::MutexGuard<()>> {
+ | -------^^^^------------------------------------ help: try this: `if None::<std::sync::MutexGuard<()>>.is_none()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:38:12
+ |
+LL | if let None = None::<std::sync::MutexGuard<()>> {}
+ | -------^^^^------------------------------------ help: try this: `if None::<std::sync::MutexGuard<()>>.is_none()`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:40:12
+ |
+LL | if let Some(_) = Some(String::new()) {}
+ | -------^^^^^^^---------------------- help: try this: `if Some(String::new()).is_some()`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:41:12
+ |
+LL | if let Some(_) = Some((String::new(), ())) {}
+ | -------^^^^^^^---------------------------- help: try this: `if Some((String::new(), ())).is_some()`
+
+error: redundant pattern matching, consider using `is_ready()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:44:12
+ |
+LL | if let Ready(_) = Ready(m.lock()) {}
+ | -------^^^^^^^^------------------ help: try this: `if Ready(m.lock()).is_ready()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_ready()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:45:12
+ |
+LL | if let Ready(_) = Ready(m.lock().unwrap().0) {}
+ | -------^^^^^^^^----------------------------- help: try this: `if Ready(m.lock().unwrap().0).is_ready()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_pending()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:48:16
+ |
+LL | if let Pending = Pending::<std::sync::MutexGuard<()>> {}
+ | -------^^^^^^^--------------------------------------- help: try this: `if Pending::<std::sync::MutexGuard<()>>.is_pending()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_pending()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:50:12
+ |
+LL | if let Pending = Pending::<std::sync::MutexGuard<()>> {
+ | -------^^^^^^^--------------------------------------- help: try this: `if Pending::<std::sync::MutexGuard<()>>.is_pending()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_pending()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:54:12
+ |
+LL | if let Pending = Pending::<std::sync::MutexGuard<()>> {}
+ | -------^^^^^^^--------------------------------------- help: try this: `if Pending::<std::sync::MutexGuard<()>>.is_pending()`
+
+error: redundant pattern matching, consider using `is_ready()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:56:12
+ |
+LL | if let Ready(_) = Ready(String::new()) {}
+ | -------^^^^^^^^----------------------- help: try this: `if Ready(String::new()).is_ready()`
+
+error: redundant pattern matching, consider using `is_ready()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:57:12
+ |
+LL | if let Ready(_) = Ready((String::new(), ())) {}
+ | -------^^^^^^^^----------------------------- help: try this: `if Ready((String::new(), ())).is_ready()`
+
+error: aborting due to 22 previous errors
+
#![warn(clippy::all)]
#![warn(clippy::redundant_pattern_matching)]
-#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)]
+#![allow(
+ unused_must_use,
+ clippy::needless_bool,
+ clippy::match_like_matches_macro,
+ clippy::if_same_then_else
+)]
fn main() {
if None::<()>.is_none() {}
#![warn(clippy::all)]
#![warn(clippy::redundant_pattern_matching)]
-#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)]
+#![allow(
+ unused_must_use,
+ clippy::needless_bool,
+ clippy::match_like_matches_macro,
+ clippy::if_same_then_else
+)]
fn main() {
if let None = None::<()> {}
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:8:12
+ --> $DIR/redundant_pattern_matching_option.rs:13:12
|
LL | if let None = None::<()> {}
| -------^^^^------------- help: try this: `if None::<()>.is_none()`
= note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:10:12
+ --> $DIR/redundant_pattern_matching_option.rs:15:12
|
LL | if let Some(_) = Some(42) {}
| -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:12:12
+ --> $DIR/redundant_pattern_matching_option.rs:17:12
|
LL | if let Some(_) = Some(42) {
| -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:18:15
+ --> $DIR/redundant_pattern_matching_option.rs:23:15
|
LL | while let Some(_) = Some(42) {}
| ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:20:15
+ --> $DIR/redundant_pattern_matching_option.rs:25:15
|
LL | while let None = Some(42) {}
| ----------^^^^----------- help: try this: `while Some(42).is_none()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:22:15
+ --> $DIR/redundant_pattern_matching_option.rs:27:15
|
LL | while let None = None::<()> {}
| ----------^^^^------------- help: try this: `while None::<()>.is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:25:15
+ --> $DIR/redundant_pattern_matching_option.rs:30:15
|
LL | while let Some(_) = v.pop() {
| ----------^^^^^^^---------- help: try this: `while v.pop().is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:33:5
+ --> $DIR/redundant_pattern_matching_option.rs:38:5
|
LL | / match Some(42) {
LL | | Some(_) => true,
| |_____^ help: try this: `Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:38:5
+ --> $DIR/redundant_pattern_matching_option.rs:43:5
|
LL | / match None::<()> {
LL | | Some(_) => false,
| |_____^ help: try this: `None::<()>.is_none()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:43:13
+ --> $DIR/redundant_pattern_matching_option.rs:48:13
|
LL | let _ = match None::<()> {
| _____________^
| |_____^ help: try this: `None::<()>.is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:49:20
+ --> $DIR/redundant_pattern_matching_option.rs:54:20
|
LL | let _ = if let Some(_) = opt { true } else { false };
| -------^^^^^^^------ help: try this: `if opt.is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:53:20
+ --> $DIR/redundant_pattern_matching_option.rs:58:20
|
LL | let _ = if let Some(_) = gen_opt() {
| -------^^^^^^^------------ help: try this: `if gen_opt().is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:55:19
+ --> $DIR/redundant_pattern_matching_option.rs:60:19
|
LL | } else if let None = gen_opt() {
| -------^^^^------------ help: try this: `if gen_opt().is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:74:12
+ --> $DIR/redundant_pattern_matching_option.rs:79:12
|
LL | if let Some(_) = Some(42) {}
| -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:76:12
+ --> $DIR/redundant_pattern_matching_option.rs:81:12
|
LL | if let None = None::<()> {}
| -------^^^^------------- help: try this: `if None::<()>.is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:78:15
+ --> $DIR/redundant_pattern_matching_option.rs:83:15
|
LL | while let Some(_) = Some(42) {}
| ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:80:15
+ --> $DIR/redundant_pattern_matching_option.rs:85:15
|
LL | while let None = None::<()> {}
| ----------^^^^------------- help: try this: `while None::<()>.is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:82:5
+ --> $DIR/redundant_pattern_matching_option.rs:87:5
|
LL | / match Some(42) {
LL | | Some(_) => true,
| |_____^ help: try this: `Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:87:5
+ --> $DIR/redundant_pattern_matching_option.rs:92:5
|
LL | / match None::<()> {
LL | | Some(_) => false,
#![warn(clippy::all)]
#![warn(clippy::redundant_pattern_matching)]
-#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)]
+#![allow(
+ unused_must_use,
+ clippy::needless_bool,
+ clippy::match_like_matches_macro,
+ clippy::if_same_then_else
+)]
use std::task::Poll::{self, Pending, Ready};
#![warn(clippy::all)]
#![warn(clippy::redundant_pattern_matching)]
-#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)]
+#![allow(
+ unused_must_use,
+ clippy::needless_bool,
+ clippy::match_like_matches_macro,
+ clippy::if_same_then_else
+)]
use std::task::Poll::{self, Pending, Ready};
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:10:12
+ --> $DIR/redundant_pattern_matching_poll.rs:15:12
|
LL | if let Pending = Pending::<()> {}
| -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()`
= note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:12:12
+ --> $DIR/redundant_pattern_matching_poll.rs:17:12
|
LL | if let Ready(_) = Ready(42) {}
| -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:14:12
+ --> $DIR/redundant_pattern_matching_poll.rs:19:12
|
LL | if let Ready(_) = Ready(42) {
| -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:20:15
+ --> $DIR/redundant_pattern_matching_poll.rs:25:15
|
LL | while let Ready(_) = Ready(42) {}
| ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()`
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:22:15
+ --> $DIR/redundant_pattern_matching_poll.rs:27:15
|
LL | while let Pending = Ready(42) {}
| ----------^^^^^^^------------ help: try this: `while Ready(42).is_pending()`
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:24:15
+ --> $DIR/redundant_pattern_matching_poll.rs:29:15
|
LL | while let Pending = Pending::<()> {}
| ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:30:5
+ --> $DIR/redundant_pattern_matching_poll.rs:35:5
|
LL | / match Ready(42) {
LL | | Ready(_) => true,
| |_____^ help: try this: `Ready(42).is_ready()`
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:35:5
+ --> $DIR/redundant_pattern_matching_poll.rs:40:5
|
LL | / match Pending::<()> {
LL | | Ready(_) => false,
| |_____^ help: try this: `Pending::<()>.is_pending()`
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:40:13
+ --> $DIR/redundant_pattern_matching_poll.rs:45:13
|
LL | let _ = match Pending::<()> {
| _____________^
| |_____^ help: try this: `Pending::<()>.is_pending()`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:46:20
+ --> $DIR/redundant_pattern_matching_poll.rs:51:20
|
LL | let _ = if let Ready(_) = poll { true } else { false };
| -------^^^^^^^^------- help: try this: `if poll.is_ready()`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:50:20
+ --> $DIR/redundant_pattern_matching_poll.rs:55:20
|
LL | let _ = if let Ready(_) = gen_poll() {
| -------^^^^^^^^------------- help: try this: `if gen_poll().is_ready()`
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:52:19
+ --> $DIR/redundant_pattern_matching_poll.rs:57:19
|
LL | } else if let Pending = gen_poll() {
| -------^^^^^^^------------- help: try this: `if gen_poll().is_pending()`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:68:12
+ --> $DIR/redundant_pattern_matching_poll.rs:73:12
|
LL | if let Ready(_) = Ready(42) {}
| -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()`
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:70:12
+ --> $DIR/redundant_pattern_matching_poll.rs:75:12
|
LL | if let Pending = Pending::<()> {}
| -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:72:15
+ --> $DIR/redundant_pattern_matching_poll.rs:77:15
|
LL | while let Ready(_) = Ready(42) {}
| ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()`
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:74:15
+ --> $DIR/redundant_pattern_matching_poll.rs:79:15
|
LL | while let Pending = Pending::<()> {}
| ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()`
error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:76:5
+ --> $DIR/redundant_pattern_matching_poll.rs:81:5
|
LL | / match Ready(42) {
LL | | Ready(_) => true,
| |_____^ help: try this: `Ready(42).is_ready()`
error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:81:5
+ --> $DIR/redundant_pattern_matching_poll.rs:86:5
|
LL | / match Pending::<()> {
LL | | Ready(_) => false,
clippy::needless_bool,
clippy::match_like_matches_macro,
clippy::unnecessary_wraps,
- deprecated
+ deprecated,
+ clippy::if_same_then_else
)]
fn main() {
clippy::needless_bool,
clippy::match_like_matches_macro,
clippy::unnecessary_wraps,
- deprecated
+ deprecated,
+ clippy::if_same_then_else
)]
fn main() {
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:15:12
+ --> $DIR/redundant_pattern_matching_result.rs:16:12
|
LL | if let Ok(_) = &result {}
| -------^^^^^---------- help: try this: `if result.is_ok()`
= note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:17:12
+ --> $DIR/redundant_pattern_matching_result.rs:18:12
|
LL | if let Ok(_) = Ok::<i32, i32>(42) {}
| -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_result.rs:19:12
+ --> $DIR/redundant_pattern_matching_result.rs:20:12
|
LL | if let Err(_) = Err::<i32, i32>(42) {}
| -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:21:15
+ --> $DIR/redundant_pattern_matching_result.rs:22:15
|
LL | while let Ok(_) = Ok::<i32, i32>(10) {}
| ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_result.rs:23:15
+ --> $DIR/redundant_pattern_matching_result.rs:24:15
|
LL | while let Err(_) = Ok::<i32, i32>(10) {}
| ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:33:5
+ --> $DIR/redundant_pattern_matching_result.rs:34:5
|
LL | / match Ok::<i32, i32>(42) {
LL | | Ok(_) => true,
| |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_result.rs:38:5
+ --> $DIR/redundant_pattern_matching_result.rs:39:5
|
LL | / match Ok::<i32, i32>(42) {
LL | | Ok(_) => false,
| |_____^ help: try this: `Ok::<i32, i32>(42).is_err()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_result.rs:43:5
+ --> $DIR/redundant_pattern_matching_result.rs:44:5
|
LL | / match Err::<i32, i32>(42) {
LL | | Ok(_) => false,
| |_____^ help: try this: `Err::<i32, i32>(42).is_err()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:48:5
+ --> $DIR/redundant_pattern_matching_result.rs:49:5
|
LL | / match Err::<i32, i32>(42) {
LL | | Ok(_) => true,
| |_____^ help: try this: `Err::<i32, i32>(42).is_ok()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:53:20
+ --> $DIR/redundant_pattern_matching_result.rs:54:20
|
LL | let _ = if let Ok(_) = Ok::<usize, ()>(4) { true } else { false };
| -------^^^^^--------------------- help: try this: `if Ok::<usize, ()>(4).is_ok()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:59:20
+ --> $DIR/redundant_pattern_matching_result.rs:60:20
|
LL | let _ = if let Ok(_) = gen_res() {
| -------^^^^^------------ help: try this: `if gen_res().is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_result.rs:61:19
+ --> $DIR/redundant_pattern_matching_result.rs:62:19
|
LL | } else if let Err(_) = gen_res() {
| -------^^^^^^------------ help: try this: `if gen_res().is_err()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_result.rs:84:19
+ --> $DIR/redundant_pattern_matching_result.rs:85:19
|
LL | while let Some(_) = r#try!(result_opt()) {}
| ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_result.rs:85:16
+ --> $DIR/redundant_pattern_matching_result.rs:86:16
|
LL | if let Some(_) = r#try!(result_opt()) {}
| -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_result.rs:91:12
+ --> $DIR/redundant_pattern_matching_result.rs:92:12
|
LL | if let Some(_) = m!() {}
| -------^^^^^^^------- help: try this: `if m!().is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_result.rs:92:15
+ --> $DIR/redundant_pattern_matching_result.rs:93:15
|
LL | while let Some(_) = m!() {}
| ----------^^^^^^^------- help: try this: `while m!().is_some()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:110:12
+ --> $DIR/redundant_pattern_matching_result.rs:111:12
|
LL | if let Ok(_) = Ok::<i32, i32>(42) {}
| -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_result.rs:112:12
+ --> $DIR/redundant_pattern_matching_result.rs:113:12
|
LL | if let Err(_) = Err::<i32, i32>(42) {}
| -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:114:15
+ --> $DIR/redundant_pattern_matching_result.rs:115:15
|
LL | while let Ok(_) = Ok::<i32, i32>(10) {}
| ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_result.rs:116:15
+ --> $DIR/redundant_pattern_matching_result.rs:117:15
|
LL | while let Err(_) = Ok::<i32, i32>(10) {}
| ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching_result.rs:118:5
+ --> $DIR/redundant_pattern_matching_result.rs:119:5
|
LL | / match Ok::<i32, i32>(42) {
LL | | Ok(_) => true,
| |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching_result.rs:123:5
+ --> $DIR/redundant_pattern_matching_result.rs:124:5
|
LL | / match Err::<i32, i32>(42) {
LL | | Ok(_) => false,
-#![allow(deprecated, invalid_value)]
-#![warn(clippy::all)]
+#![allow(deprecated, invalid_value, clippy::uninit_assumed_init)]
+#![warn(clippy::mem_replace_with_uninit)]
use std::mem;
// False positive #5154, shouldn't trigger lint.
m!();
}
+
+mod hello_mod {
+
+ #[allow(dead_code)]
+ fn hello_mod() {}
+}
+
+mod hi_mod {
+ use self::regex::{Regex, RegexSet};
+ use regex;
+ #[allow(dead_code)]
+ fn hi_mod() {}
+}
// False positive #5154, shouldn't trigger lint.
m!();
}
+
+mod hello_mod {
+ use regex;
+ #[allow(dead_code)]
+ fn hello_mod() {}
+}
+
+mod hi_mod {
+ use self::regex::{Regex, RegexSet};
+ use regex;
+ #[allow(dead_code)]
+ fn hi_mod() {}
+}
+error: this import is redundant
+ --> $DIR/single_component_path_imports.rs:24:5
+ |
+LL | use regex;
+ | ^^^^^^^^^^ help: remove it entirely
+ |
+ = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
+
error: this import is redundant
--> $DIR/single_component_path_imports.rs:6:1
|
LL | use regex;
| ^^^^^^^^^^ help: remove it entirely
- |
- = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
-error: aborting due to previous error
+error: aborting due to 2 previous errors
--- /dev/null
+// run-rustfix
+// edition:2018
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+// #7106: use statements exporting a macro within a crate should not trigger lint
+
+macro_rules! m1 {
+ () => {};
+}
+pub(crate) use m1; // ok
+
+macro_rules! m2 {
+ () => {};
+}
+ // fail
+
+fn main() {
+ m1!();
+ m2!();
+}
--- /dev/null
+// run-rustfix
+// edition:2018
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+// #7106: use statements exporting a macro within a crate should not trigger lint
+
+macro_rules! m1 {
+ () => {};
+}
+pub(crate) use m1; // ok
+
+macro_rules! m2 {
+ () => {};
+}
+use m2; // fail
+
+fn main() {
+ m1!();
+ m2!();
+}
--- /dev/null
+error: this import is redundant
+ --> $DIR/single_component_path_imports_macro.rs:16:1
+ |
+LL | use m2; // fail
+ | ^^^^^^^ help: remove it entirely
+ |
+ = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
+
+error: aborting due to previous error
+
--- /dev/null
+// edition:2018
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+use regex;
+use serde as edres;
+pub use serde;
+
+fn main() {
+ regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
+}
+
+mod root_nested_use_mod {
+ use {regex, serde};
+ #[allow(dead_code)]
+ fn root_nested_use_mod() {}
+}
--- /dev/null
+error: this import is redundant
+ --> $DIR/single_component_path_imports_nested_first.rs:14:10
+ |
+LL | use {regex, serde};
+ | ^^^^^
+ |
+ = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
+ = help: remove this import
+
+error: this import is redundant
+ --> $DIR/single_component_path_imports_nested_first.rs:14:17
+ |
+LL | use {regex, serde};
+ | ^^^^^
+ |
+ = help: remove this import
+
+error: this import is redundant
+ --> $DIR/single_component_path_imports_nested_first.rs:5:1
+ |
+LL | use regex;
+ | ^^^^^^^^^^ help: remove it entirely
+
+error: aborting due to 3 previous errors
+
--- /dev/null
+// edition:2018
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+use self::regex::{Regex as xeger, RegexSet as tesxeger};
+pub use self::{
+ regex::{Regex, RegexSet},
+ some_mod::SomeType,
+};
+use regex;
+
+mod some_mod {
+ pub struct SomeType;
+}
+
+fn main() {}
--- /dev/null
+// edition:2018
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+use regex;
+
+use self::regex::{Regex as xeger, RegexSet as tesxeger};
+pub use self::{
+ regex::{Regex, RegexSet},
+ some_mod::SomeType,
+};
+
+mod some_mod {
+ pub struct SomeType;
+}
+
+fn main() {}
let item = &item1;
println!("{}", item);
}
+
+ {
+ let item = &item1;
+ println!("{:?}", item);
+ }
}
for item in &[item1] {
println!("{}", item);
}
+
+ for item in [item1].iter() {
+ println!("{:?}", item);
+ }
}
LL | }
|
-error: aborting due to previous error
+error: for loop over a single element
+ --> $DIR/single_element_loop.rs:11:5
+ |
+LL | / for item in [item1].iter() {
+LL | | println!("{:?}", item);
+LL | | }
+ | |_____^
+ |
+help: try
+ |
+LL | {
+LL | let item = &item1;
+LL | println!("{:?}", item);
+LL | }
+ |
+
+error: aborting due to 2 previous errors
Bar::A => println!(),
_ => (),
}
+
+ // issue #7038
+ struct X;
+ let x = Some(X);
+ match x {
+ None => println!(),
+ _ => (),
+ };
}
macro_rules! single_match {
LL | | }
| |_____^ help: try this: `if let Bar::A = x { println!() }`
-error: aborting due to 12 previous errors
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match.rs:142:5
+ |
+LL | / match x {
+LL | | None => println!(),
+LL | | _ => (),
+LL | | };
+ | |_____^ help: try this: `if let None = x { println!() }`
+
+error: aborting due to 13 previous errors
let current_version = env!("CARGO_PKG_VERSION").as_bytes();
- let includestr = include_bytes!("entry_unfixable.rs");
+ let includestr = include_bytes!("string_lit_as_bytes.rs");
let _ = b"string with newline\t\n";
}
let current_version = env!("CARGO_PKG_VERSION").as_bytes();
- let includestr = include_str!("entry_unfixable.rs").as_bytes();
+ let includestr = include_str!("string_lit_as_bytes.rs").as_bytes();
let _ = "string with newline\t\n".as_bytes();
}
error: calling `as_bytes()` on `include_str!(..)`
--> $DIR/string_lit_as_bytes.rs:25:22
|
-LL | let includestr = include_str!("entry_unfixable.rs").as_bytes();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("entry_unfixable.rs")`
+LL | let includestr = include_str!("string_lit_as_bytes.rs").as_bytes();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("string_lit_as_bytes.rs")`
error: calling `as_bytes()` on a string literal
--> $DIR/string_lit_as_bytes.rs:27:13
{
}
+ // This is fine, though weird. Allman style braces on the else.
if foo() {
}
else
}
if foo() {
}
+
+ // Almost Allman style braces. Lint these.
+ if foo() {
+ }
+
+ else
+ {
+
+ }
+
+ if foo() {
+ }
+ else
+
+ {
+
+ }
+
+ // #3864 - Allman style braces
+ if foo()
+ {
+ }
+ else
+ {
+ }
}
|
= note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
-error: this is an `else {..}` but the formatting might hide it
- --> $DIR/suspicious_else_formatting.rs:44:6
+error: this is an `else if` but the formatting might hide it
+ --> $DIR/suspicious_else_formatting.rs:51:6
|
-LL | }
+LL | } else
| ______^
-LL | | else
-LL | | {
+LL | | if foo() { // the span of the above error should continue here
| |____^
|
- = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
+ = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
error: this is an `else if` but the formatting might hide it
- --> $DIR/suspicious_else_formatting.rs:50:6
+ --> $DIR/suspicious_else_formatting.rs:56:6
|
-LL | } else
+LL | }
| ______^
+LL | | else
LL | | if foo() { // the span of the above error should continue here
| |____^
|
= note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
-error: this is an `else if` but the formatting might hide it
- --> $DIR/suspicious_else_formatting.rs:55:6
+error: this is an `else {..}` but the formatting might hide it
+ --> $DIR/suspicious_else_formatting.rs:83:6
|
LL | }
| ______^
+LL | |
LL | | else
-LL | | if foo() { // the span of the above error should continue here
+LL | | {
| |____^
|
- = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
+ = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
+
+error: this is an `else {..}` but the formatting might hide it
+ --> $DIR/suspicious_else_formatting.rs:91:6
+ |
+LL | }
+ | ______^
+LL | | else
+LL | |
+LL | | {
+ | |____^
+ |
+ = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
-error: aborting due to 8 previous errors
+error: aborting due to 9 previous errors
Poll::Ready(None)
}
+
+// Tests that `return` is not duplicated
+pub fn try_return(x: bool) -> Result<i32, i32> {
+ if x {
+ return Err(42);
+ }
+ Ok(0)
+}
Poll::Ready(None)
}
+
+// Tests that `return` is not duplicated
+pub fn try_return(x: bool) -> Result<i32, i32> {
+ if x {
+ return Err(42)?;
+ }
+ Ok(0)
+}
LL | Err(io::ErrorKind::NotFound)?
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))`
-error: aborting due to 10 previous errors
+error: returning an `Err(_)` with the `?` operator
+ --> $DIR/try_err.rs:167:16
+ |
+LL | return Err(42)?;
+ | ^^^^^^^^ help: try this: `Err(42)`
+
+error: aborting due to 11 previous errors
#![feature(stmt_expr_attributes)]
-use std::mem::MaybeUninit;
+use std::mem::{self, MaybeUninit};
fn main() {
let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
// This is OK, because all constitutent types are uninit-compatible.
let _: (MaybeUninit<usize>, [MaybeUninit<bool>; 2]) = unsafe { MaybeUninit::uninit().assume_init() };
+
+ // Was a false negative.
+ let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() };
}
LL | let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 2 previous errors
+error: this call for this type may be undefined behavior
+ --> $DIR/uninit.rs:24:29
+ |
+LL | let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
--- /dev/null
+// run-rustfix
+#![warn(clippy::unnecessary_self_imports)]
+#![allow(unused_imports, dead_code)]
+
+use std::collections::hash_map::{self, *};
+use std::fs as alias;
+use std::io::{self, Read};
+use std::rc;
+
+fn main() {}
--- /dev/null
+// run-rustfix
+#![warn(clippy::unnecessary_self_imports)]
+#![allow(unused_imports, dead_code)]
+
+use std::collections::hash_map::{self, *};
+use std::fs::{self as alias};
+use std::io::{self, Read};
+use std::rc::{self};
+
+fn main() {}
--- /dev/null
+error: import ending with `::{self}`
+ --> $DIR/unnecessary_self_imports.rs:6:1
+ |
+LL | use std::fs::{self as alias};
+ | ^^^^^^^^^--------------------
+ | |
+ | help: consider omitting `::{self}`: `fs as alias;`
+ |
+ = note: `-D clippy::unnecessary-self-imports` implied by `-D warnings`
+ = note: this will slightly change semantics; any non-module items at the same path will also be imported
+
+error: import ending with `::{self}`
+ --> $DIR/unnecessary_self_imports.rs:8:1
+ |
+LL | use std::rc::{self};
+ | ^^^^^^^^^-----------
+ | |
+ | help: consider omitting `::{self}`: `rc;`
+ |
+ = note: this will slightly change semantics; any non-module items at the same path will also be imported
+
+error: aborting due to 2 previous errors
+
#![allow(dead_code)]
#![warn(clippy::unused_io_amount)]
-use std::io;
+use std::io::{self, Read};
fn question_mark<T: io::Read + io::Write>(s: &mut T) -> io::Result<()> {
s.write(b"test")?;
Ok(())
}
+fn ok(file: &str) -> Option<()> {
+ let mut reader = std::fs::File::open(file).ok()?;
+ let mut result = [0u8; 0];
+ reader.read(&mut result).ok()?;
+ Some(())
+}
+
+#[allow(clippy::redundant_closure)]
+#[allow(clippy::bind_instead_of_map)]
+fn or_else(file: &str) -> io::Result<()> {
+ let mut reader = std::fs::File::open(file)?;
+ let mut result = [0u8; 0];
+ reader.read(&mut result).or_else(|err| Err(err))?;
+ Ok(())
+}
+
+#[derive(Debug)]
+enum Error {
+ Kind,
+}
+
+fn or(file: &str) -> Result<(), Error> {
+ let mut reader = std::fs::File::open(file).unwrap();
+ let mut result = [0u8; 0];
+ reader.read(&mut result).or(Err(Error::Kind))?;
+ Ok(())
+}
+
+fn combine_or(file: &str) -> Result<(), Error> {
+ let mut reader = std::fs::File::open(file).unwrap();
+ let mut result = [0u8; 0];
+ reader
+ .read(&mut result)
+ .or(Err(Error::Kind))
+ .or(Err(Error::Kind))
+ .expect("error");
+ Ok(())
+}
+
fn main() {}
LL | s.write_vectored(&[io::IoSlice::new(&[])])?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 6 previous errors
+error: read amount is not handled. Use `Read::read_exact` instead
+ --> $DIR/unused_io_amount.rs:28:5
+ |
+LL | reader.read(&mut result).ok()?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: read amount is not handled. Use `Read::read_exact` instead
+ --> $DIR/unused_io_amount.rs:37:5
+ |
+LL | reader.read(&mut result).or_else(|err| Err(err))?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: read amount is not handled. Use `Read::read_exact` instead
+ --> $DIR/unused_io_amount.rs:49:5
+ |
+LL | reader.read(&mut result).or(Err(Error::Kind))?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: read amount is not handled. Use `Read::read_exact` instead
+ --> $DIR/unused_io_amount.rs:56:5
+ |
+LL | / reader
+LL | | .read(&mut result)
+LL | | .or(Err(Error::Kind))
+LL | | .or(Err(Error::Kind))
+LL | | .expect("error");
+ | |________________________^
+
+error: aborting due to 10 previous errors
let error_kind = ErrorKind::NotFound;
match error_kind {
ErrorKind::NotFound => {},
- ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | _ => {},
+ ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | ErrorKind::Unsupported | _ => {},
}
match error_kind {
ErrorKind::NotFound => {},
ErrorKind::Interrupted => {},
ErrorKind::Other => {},
ErrorKind::UnexpectedEof => {},
+ ErrorKind::Unsupported => {},
_ => {},
}
}
ErrorKind::Interrupted => {},
ErrorKind::Other => {},
ErrorKind::UnexpectedEof => {},
+ ErrorKind::Unsupported => {},
_ => {},
}
}
--> $DIR/wildcard_enum_match_arm.rs:80:9
|
LL | _ => {},
- | ^ help: try this: `ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | _`
+ | ^ help: try this: `ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | ErrorKind::Unsupported | _`
error: aborting due to 5 previous errors
}
}
}
+
+mod issue7032 {
+ trait Foo {
+ fn from_usize(x: usize) -> Self;
+ }
+ // don't trigger
+ impl Foo for usize {
+ fn from_usize(x: usize) -> Self {
+ x
+ }
+ }
+}
opacity: 30%;
}
- p > code {
+ :not(pre) > code {
color: var(--inline-code-color);
background-color: var(--inline-code-bg);
}
props.aux_crate.push(ac);
}
- if let Some(r) = config.parse_revisions(ln) {
- props.revisions.extend(r);
- }
+ config.parse_and_update_revisions(ln, &mut props.revisions);
props.should_fail = props.should_fail || config.parse_name_directive(ln, "should-fail");
});
self.compile_flags.push(format!("--edition={}", edition));
}
- if let Some(r) = config.parse_revisions(ln) {
- self.revisions.extend(r);
- }
+ config.parse_and_update_revisions(ln, &mut self.revisions);
if self.run_flags.is_none() {
self.run_flags = config.parse_run_flags(ln);
self.parse_name_value_directive(line, "compile-flags")
}
- fn parse_revisions(&self, line: &str) -> Option<Vec<String>> {
- self.parse_name_value_directive(line, "revisions")
- .map(|r| r.split_whitespace().map(|t| t.to_string()).collect())
+ fn parse_and_update_revisions(&self, line: &str, existing: &mut Vec<String>) {
+ if let Some(raw) = self.parse_name_value_directive(line, "revisions") {
+ let mut duplicates: HashSet<_> = existing.iter().cloned().collect();
+ for revision in raw.split_whitespace().map(|r| r.to_string()) {
+ if !duplicates.insert(revision.clone()) {
+ panic!("Duplicate revision: `{}` in line `{}`", revision, raw);
+ }
+ existing.push(revision);
+ }
+ }
}
fn parse_run_flags(&self, line: &str) -> Option<String> {
assert_eq!(extract_version_range(" - 4.5.6", extract_llvm_version), None);
assert_eq!(extract_version_range("0 -", extract_llvm_version), None);
}
+
+#[test]
+#[should_panic(expected = "Duplicate revision: `rpass1` in line ` rpass1 rpass1`")]
+fn test_duplicate_revisions() {
+ let config = config();
+ parse_rs(&config, "// revisions: rpass1 rpass1");
+}
}
} else if let Some(lldb_ver) = full_version_line.strip_prefix("lldb version ") {
if let Some(idx) = lldb_ver.find(not_a_digit) {
- let version: u32 = lldb_ver[..idx].parse().unwrap();
+ let version: u32 = lldb_ver[..idx].parse().ok()?;
return Some((version * 100, full_version_line.contains("rust-enabled")));
}
}
-Subproject commit b13b79db73e3fa692fc648a8cd70f162a5eade34
+Subproject commit 5faf5a5ca059f6eb067fc86e47480f5668ac6e8c
-Subproject commit 32c0fe006dcdc13e1ca0ca31de543e4436c1299e
+Subproject commit 74d1800c25498689c5b5120a1e8495fce0cd0d0d
-Subproject commit 7be06139b632ee615fc18af04dd67947e2c794b2
+Subproject commit 617535393bb5ccc7adf0bac8a3b9a9c306454e79
[dependencies]
byteorder = { version = "1", features = ['default', 'std'] }
curl-sys = { version = "0.4.13", features = ["http2", "libnghttp2-sys"], optional = true }
-crossbeam-utils = { version = "0.7.2", features = ["nightly"] }
+crossbeam-utils = { version = "0.8.0", features = ["nightly"] }
libc = { version = "0.2.79", features = ["align"] }
# Ensure default features of libz-sys, which are disabled in some scenarios.
libz-sys = { version = "1.1.2" }
proc-macro2 = { version = "1", features = ["default"] }
quote = { version = "1", features = ["default"] }
+rand_core_0_5 = { package = "rand_core", version = "0.5.1", features = ["getrandom", "alloc", "std"] }
serde = { version = "1.0.82", features = ['derive'] }
serde_json = { version = "1.0.31", features = ["raw_value", "unbounded_depth"] }
-smallvec-0_6 = { package = "smallvec", version = "0.6.14", features = ['union', 'may_dangle'] }
smallvec = { version = "1.6.1", features = ['union', 'may_dangle'] }
syn = { version = "1", features = ['fold', 'full', 'extra-traits', 'visit', 'visit-mut'] }
url = { version = "2.0", features = ['serde'] }
return null;
}
-function loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate) {
+function loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate) {
if (searchIndex[searchIndex.length - 1].length === 0) {
searchIndex.pop();
}
ALIASES = {};
finalJS += 'window = { "currentCrate": "' + crate + '", rootPath: "../" };\n';
finalJS += loadThings(["hasOwnProperty", "onEach"], 'function', extractFunction, storageJs);
- finalJS += loadThings(arraysToLoad, 'array', extractArrayVariable, mainJs);
- finalJS += loadThings(variablesToLoad, 'variable', extractVariable, mainJs);
- finalJS += loadThings(functionsToLoad, 'function', extractFunction, mainJs);
+ finalJS += loadThings(arraysToLoad, 'array', extractArrayVariable, searchJs);
+ finalJS += loadThings(variablesToLoad, 'variable', extractVariable, searchJs);
+ finalJS += loadThings(functionsToLoad, 'function', extractFunction, searchJs);
var loaded = loadContent(finalJS);
var index = loaded.buildIndex(searchIndex.rawSearchIndex);
}
function load_files(doc_folder, resource_suffix, crate) {
- var mainJs = readFile(path.join(doc_folder, "main" + resource_suffix + ".js"));
+ var searchJs = readFile(path.join(doc_folder, "search" + resource_suffix + ".js"));
var storageJs = readFile(path.join(doc_folder, "storage" + resource_suffix + ".js"));
var searchIndex = readFile(
path.join(doc_folder, "search-index" + resource_suffix + ".js")).split("\n");
- return loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate);
+ return loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate);
}
function showHelp() {
("ryu", "Apache-2.0 OR BSL-1.0"), // rls/cargo/... (because of serde)
("bytesize", "Apache-2.0"), // cargo
("im-rc", "MPL-2.0+"), // cargo
- ("constant_time_eq", "CC0-1.0"), // rustfmt
("sized-chunks", "MPL-2.0+"), // cargo via im-rc
("bitmaps", "MPL-2.0+"), // cargo via im-rc
("crossbeam-queue", "MIT/Apache-2.0 AND BSD-2-Clause"), // rls via rayon
- ("arrayref", "BSD-2-Clause"), // cargo-miri/directories/.../rust-argon2 (redox)
("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot
("snap", "BSD-3-Clause"), // rustc
// FIXME: this dependency violates the documentation comment above:
const ENTRY_LIMIT: usize = 1000;
// FIXME: The following limits should be reduced eventually.
-const ROOT_ENTRY_LIMIT: usize = 1390;
+const ROOT_ENTRY_LIMIT: usize = 1388;
const ISSUES_ENTRY_LIMIT: usize = 2551;
fn check_entries(path: &Path, bad: &mut bool) {