/dist/
/unicode-downloads
/target
+/src/bootstrap/target
/src/tools/x/target
# Created by default with `src/ci/docker/run.sh`
/obj/
dependencies = [
"arrayvec",
"if_chain",
+ "itertools",
"rustc-semver",
]
"quote",
]
-[[package]]
-name = "ctor"
-version = "0.1.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c"
-dependencies = [
- "quote",
- "syn",
-]
-
[[package]]
name = "curl"
version = "0.4.43"
[[package]]
name = "git2"
-version = "0.14.2"
+version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3826a6e0e2215d7a41c2bfc7c9244123969273f3476b939a226aac0ab56e9e3c"
+checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1"
dependencies = [
"bitflags",
"libc",
[[package]]
name = "git2-curl"
-version = "0.15.0"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ee51709364c341fbb6fe2a385a290fb9196753bdde2fc45447d27cd31b11b13"
+checksum = "ed817a00721e2f8037ba722e60358d4956dae9cca10315fc982f967907d3b0cd"
dependencies = [
"curl",
"git2",
[[package]]
name = "libgit2-sys"
-version = "0.13.2+1.4.2"
+version = "0.14.0+1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a42de9a51a5c12e00fc0e4ca6bc2ea43582fc6418488e8f615e905d886f258b"
+checksum = "47a00859c70c8a4f7218e6d1cc32875c4b55f6799445b842b0d8ed5e4c3d959b"
dependencies = [
"cc",
"libc",
"getrandom 0.2.0",
"lazy_static",
"libc",
+ "libloading",
"log",
"measureme",
"rand 0.8.5",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
-[[package]]
-name = "output_vt100"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
-dependencies = [
- "winapi",
-]
-
[[package]]
name = "owo-colors"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
-[[package]]
-name = "pretty_assertions"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c89f989ac94207d048d92db058e4f6ec7342b0971fc58d1271ca148b799b3563"
-dependencies = [
- "ansi_term",
- "ctor",
- "diff",
- "output_vt100",
-]
-
[[package]]
name = "pretty_env_logger"
version = "0.4.0"
"rustc_serialize",
"rustc_span",
"smallvec",
+ "thin-vec",
"tracing",
]
"rustc_span",
"rustc_target",
"smallvec",
+ "thin-vec",
"tracing",
]
"rustc_span",
"rustc_target",
"smallvec",
+ "thin-vec",
"tracing",
]
"libc",
"libloading",
"measureme",
+ "object 0.29.0",
"rustc-demangle",
"rustc_ast",
"rustc_attr",
"object 0.29.0",
"pathdiff",
"regex",
- "rustc_apfloat",
"rustc_arena",
"rustc_ast",
"rustc_attr",
"stable_deref_trait",
"stacker",
"tempfile",
+ "thin-vec",
"tracing",
"winapi",
]
"rustc_target",
"rustc_type_ir",
"smallvec",
+ "thin-vec",
"tracing",
]
version = "0.0.0"
dependencies = [
"rustc_data_structures",
+ "rustc_errors",
"rustc_hir",
"rustc_index",
+ "rustc_macros",
"rustc_middle",
"rustc_session",
"rustc_span",
"rustc_session",
"rustc_span",
"rustc_target",
+ "thin-vec",
"tracing",
]
"rustc_span",
"rustc_target",
"smallvec",
+ "thin-vec",
"tracing",
]
"indexmap",
"rustc_macros",
"smallvec",
+ "thin-vec",
]
[[package]]
"punycode",
"rustc-demangle",
"rustc_data_structures",
+ "rustc_errors",
"rustc_hir",
+ "rustc_macros",
"rustc_middle",
"rustc_session",
"rustc_span",
"serde_json",
"smallvec",
"tempfile",
+ "thin-vec",
"tracing",
"tracing-subscriber",
"tracing-tree",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
+[[package]]
+name = "thin-vec"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "104c2cb3180b6fb6d5b2278768e9b88b578d32ba751ea6e8d026688a40d7ed87"
+
[[package]]
name = "thiserror"
version = "1.0.30"
[[package]]
name = "tracing"
-version = "0.1.29"
+version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105"
+checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
dependencies = [
"cfg-if 1.0.0",
"pin-project-lite",
[[package]]
name = "tracing-attributes"
-version = "0.1.18"
+version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e"
+checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2"
dependencies = [
"proc-macro2",
"quote",
[[package]]
name = "tracing-core"
-version = "0.1.21"
+version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4"
+checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7"
dependencies = [
- "lazy_static",
+ "once_cell",
+ "valuable",
]
[[package]]
[[package]]
name = "ui_test"
-version = "0.1.0"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d1f546a5883ae78da735bba529ec1116661e2f73582f23920d994dc97da3a22"
dependencies = [
"cargo_metadata 0.15.0",
"color-eyre",
"colored",
"crossbeam",
+ "diff",
"lazy_static",
- "pretty_assertions",
"regex",
"rustc_version",
"serde",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d"
+[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
[[package]]
name = "vcpkg"
version = "0.2.10"
- [Mixing Option and Result via `?` is no longer permitted in closures for inferred types.][86831]
- [Previously unsound code is no longer permitted where different constructors in branches
could require different lifetimes.][85574]
-- As previously mentioned the [`std::arch` instrinsics now uses stricter const checking][83278]
+- As previously mentioned the [`std::arch` intrinsics now uses stricter const checking][83278]
than before and may reject some previously accepted code.
- [`i128` multiplication on Cortex M0+ platforms currently unconditionally causes overflow
when compiled with `codegen-units = 1`.][86063]
- [Fixed a regression parsing `{} && false` in tail expressions.][74650]
- [Added changes to how proc-macros are expanded in `macro_rules!` that should
help to preserve more span information.][73084] These changes may cause
- compiliation errors if your macro was unhygenic or didn't correctly handle
+ compilation errors if your macro was unhygenic or didn't correctly handle
`Delimiter::None`.
- [Moved support for the CloudABI target to tier 3.][75568]
- [`linux-gnu` targets now require minimum kernel 2.6.32 and glibc 2.11.][74163]
#![feature(maybe_uninit_slice)]
#![feature(min_specialization)]
#![feature(decl_macro)]
+#![feature(pointer_byte_offsets)]
#![feature(rustc_attrs)]
#![cfg_attr(test, feature(test))]
#![feature(strict_provenance)]
unsafe {
if mem::size_of::<T>() == 0 {
- self.ptr.set((self.ptr.get() as *mut u8).wrapping_offset(1) as *mut T);
+ self.ptr.set(self.ptr.get().wrapping_byte_add(1));
let ptr = ptr::NonNull::<T>::dangling().as_ptr();
// Don't drop the object. This `write` is equivalent to `forget`.
ptr::write(ptr, object);
doctest = false
[dependencies]
-rustc_serialize = { path = "../rustc_serialize" }
-tracing = "0.1"
-rustc_span = { path = "../rustc_span" }
+bitflags = "1.2.1"
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_index = { path = "../rustc_index" }
rustc_lexer = { path = "../rustc_lexer" }
rustc_macros = { path = "../rustc_macros" }
+rustc_serialize = { path = "../rustc_serialize" }
+rustc_span = { path = "../rustc_span" }
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-bitflags = "1.2.1"
+thin-vec = "0.2.8"
+tracing = "0.1"
use crate::ptr::P;
use crate::token::{self, CommentKind, Delimiter};
use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream};
-
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::sync::Lrc;
-use rustc_data_structures::thin_vec::ThinVec;
use rustc_macros::HashStable_Generic;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use rustc_span::source_map::{respan, Spanned};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{Span, DUMMY_SP};
-
use std::cmp::Ordering;
use std::convert::TryFrom;
use std::fmt;
use std::mem;
+use thin_vec::ThinVec;
/// A "Label" is an identifier of some point in sources,
/// e.g. in the following code:
impl HasAttrs for $T {
const SUPPORTS_CUSTOM_INNER_ATTRS: bool = $inner;
+ #[inline]
fn attrs(&self) -> &[Attribute] {
&self.attrs
}
#![feature(const_trait_impl)]
#![feature(if_let_guard)]
#![cfg_attr(bootstrap, feature(label_break_value))]
+#![feature(let_chains)]
#![feature(min_specialization)]
#![feature(negative_impls)]
#![feature(slice_internals)]
#[macro_use]
extern crate rustc_macros;
+#[macro_use]
+extern crate tracing;
+
pub mod util {
pub mod classify;
pub mod comments;
let attr_annotated = if attrs.is_empty() {
tokens.create_token_stream()
} else {
- let attr_data = AttributesData { attrs: attrs.to_vec().into(), tokens: tokens.clone() };
+ let attr_data =
+ AttributesData { attrs: attrs.iter().cloned().collect(), tokens: tokens.clone() };
AttrAnnotatedTokenStream::new(vec![(
AttrAnnotatedTokenTree::Attributes(attr_data),
Spacing::Alone,
// Get the first stream, which will become the result stream.
// If it's `None`, create an empty stream.
- let mut iter = streams.drain(..);
+ let mut iter = streams.into_iter();
let mut res_stream_lrc = iter.next().unwrap().0;
// Append the subsequent elements to the result stream, after
use rustc_span::Span;
use std::ascii;
-use tracing::debug;
pub enum LitError {
NotLiteral,
[dependencies]
rustc_arena = { path = "../rustc_arena" }
-tracing = "0.1"
+rustc_ast = { path = "../rustc_ast" }
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
-rustc_hir = { path = "../rustc_hir" }
-rustc_target = { path = "../rustc_target" }
rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_errors = { path = "../rustc_errors" }
+rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
rustc_middle = { path = "../rustc_middle" }
rustc_macros = { path = "../rustc_macros" }
rustc_query_system = { path = "../rustc_query_system" }
-rustc_span = { path = "../rustc_span" }
-rustc_errors = { path = "../rustc_errors" }
rustc_session = { path = "../rustc_session" }
-rustc_ast = { path = "../rustc_ast" }
+rustc_span = { path = "../rustc_span" }
+rustc_target = { path = "../rustc_target" }
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
+thin-vec = "0.2.8"
+tracing = "0.1"
#[primary_span]
pub span: Span,
}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::inclusive_range_with_no_end)]
+pub struct InclusiveRangeWithNoEnd {
+ #[primary_span]
+ pub span: Span,
+}
use super::errors::{
AsyncGeneratorsNotSupported, AsyncNonMoveClosureNotSupported, AwaitOnlyInAsyncFnAndBlocks,
BaseExpressionDoubleDot, ClosureCannotBeStatic, FunctionalRecordUpdateDestructuringAssignemnt,
- GeneratorTooManyParameters, NotSupportedForLifetimeBinderAsyncClosure, RustcBoxAttributeError,
- UnderscoreExprLhsAssign,
+ GeneratorTooManyParameters, InclusiveRangeWithNoEnd, NotSupportedForLifetimeBinderAsyncClosure,
+ RustcBoxAttributeError, UnderscoreExprLhsAssign,
};
use super::ResolverAstLoweringExt;
use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
use crate::{FnDeclKind, ImplTraitPosition};
-
use rustc_ast::attr;
use rustc_ast::ptr::P as AstP;
use rustc_ast::*;
use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
use rustc_span::symbol::{sym, Ident};
use rustc_span::DUMMY_SP;
+use thin_vec::thin_vec;
impl<'hir> LoweringContext<'_, 'hir> {
fn lower_exprs(&mut self, exprs: &[AstP<Expr>]) -> &'hir [hir::Expr<'hir>] {
(Some(..), Some(..), HalfOpen) => hir::LangItem::Range,
(None, Some(..), Closed) => hir::LangItem::RangeToInclusive,
(Some(..), Some(..), Closed) => unreachable!(),
- (_, None, Closed) => self.diagnostic().span_fatal(span, "inclusive range with no end"),
+ (start, None, Closed) => {
+ self.tcx.sess.emit_err(InclusiveRangeWithNoEnd { span });
+ match start {
+ Some(..) => hir::LangItem::RangeFrom,
+ None => hir::LangItem::RangeFull,
+ }
+ }
};
let fields = self.arena.alloc_from_iter(
};
attr::mk_attr_outer(allow)
};
- let attrs: AttrVec = vec![attr].into();
+ let attrs: AttrVec = thin_vec![attr];
// `ControlFlow::Continue(val) => #[allow(unreachable_code)] val,`
let continue_arm = {
use rustc_span::source_map::SourceMap;
use rustc_span::{Span, DUMMY_SP};
-use tracing::debug;
-
/// A visitor that walks over the HIR and collects `Node`s into a HIR map.
pub(super) struct NodeCollector<'a, 'hir> {
/// Source map
definitions: &'a definitions::Definitions,
}
-#[tracing::instrument(level = "debug", skip(sess, definitions, bodies))]
+#[instrument(level = "debug", skip(sess, definitions, bodies))]
pub(super) fn index_hir<'hir>(
sess: &Session,
definitions: &definitions::Definitions,
}
impl<'a, 'hir> NodeCollector<'a, 'hir> {
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn insert(&mut self, span: Span, hir_id: HirId, node: Node<'hir>) {
debug_assert_eq!(self.owner, hir_id.owner);
debug_assert_ne!(hir_id.local_id.as_u32(), 0);
});
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn visit_item(&mut self, i: &'hir Item<'hir>) {
debug_assert_eq!(i.def_id, self.owner);
self.with_parent(i.hir_id(), |this| {
});
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn visit_foreign_item(&mut self, fi: &'hir ForeignItem<'hir>) {
debug_assert_eq!(fi.def_id, self.owner);
self.with_parent(fi.hir_id(), |this| {
})
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn visit_trait_item(&mut self, ti: &'hir TraitItem<'hir>) {
debug_assert_eq!(ti.def_id, self.owner);
self.with_parent(ti.hir_id(), |this| {
});
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn visit_impl_item(&mut self, ii: &'hir ImplItem<'hir>) {
debug_assert_eq!(ii.def_id, self.owner);
self.with_parent(ii.hir_id(), |this| {
//! in the HIR, especially for multiple identifiers.
#![feature(box_patterns)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(never_type)]
#![recursion_limit = "256"]
#![allow(rustc::potential_query_instability)]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
#[macro_use]
extern crate tracing;
/// Panics if no map has been pushed.
/// Remapping is used when creating lowering `-> impl Trait` return
/// types to create the resulting opaque type.
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn record_def_id_remap(&mut self, from: LocalDefId, to: LocalDefId) {
self.generics_def_id_map.last_mut().expect("no map pushed").insert(from, to);
}
}
/// Converts a lifetime into a new generic parameter.
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn lifetime_res_to_generic_param(
&mut self,
ident: Ident,
/// name resolver owing to lifetime elision; this also populates the resolver's node-id->def-id
/// map, so that later calls to `opt_node_id_to_def_id` that refer to these extra lifetime
/// parameters will be successful.
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
#[inline]
fn lower_lifetime_binder(
&mut self,
) -> hir::Ty<'hir> {
// Check whether we should interpret this as a bare trait object.
// This check mirrors the one in late resolution. We only introduce this special case in
- // the rare occurence we need to lower `Fresh` anonymous lifetimes.
+ // the rare occurrence we need to lower `Fresh` anonymous lifetimes.
// The other cases when a qpath should be opportunistically made a trait object are handled
// by `ty_path`.
if qself.is_none()
/// added explicitly in the HIR). But this includes all the lifetimes, and we only want to
/// capture the lifetimes that are referenced in the bounds. Therefore, we add *extra* lifetime parameters
/// for the lifetimes that get captured (`'x`, in our example above) and reference those.
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn lower_opaque_impl_trait(
&mut self,
span: Span,
// `make_ret_async`: if `Some`, converts `-> T` into `-> impl Future<Output = T>` in the
// return type. This is used for `async fn` declarations. The `NodeId` is the ID of the
// return type `impl Trait` item.
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn lower_fn_decl(
&mut self,
decl: &FnDecl,
// `output`: unlowered output type (`T` in `-> T`)
// `fn_def_id`: `DefId` of the parent function (used to create child impl trait definition)
// `opaque_ty_node_id`: `NodeId` of the opaque `impl Trait` type that should be created
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn lower_async_fn_ret_ty(
&mut self,
output: &FnRetTy,
self.new_named_lifetime(l.id, l.id, span, ident)
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn new_named_lifetime_with_res(
&mut self,
id: NodeId,
hir::Lifetime { hir_id: self.lower_node_id(id), span: self.lower_span(span), name }
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn new_named_lifetime(
&mut self,
id: NodeId,
hir::TraitRef { path, hir_ref_id: self.lower_node_id(p.ref_id) }
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn lower_poly_trait_ref(
&mut self,
p: &PolyTraitRef,
use rustc_span::{BytePos, Span, DUMMY_SP};
use smallvec::smallvec;
-use tracing::debug;
impl<'a, 'hir> LoweringContext<'a, 'hir> {
#[instrument(level = "trace", skip(self))]
/// Emits an error banning the `let` expression provided in the given location.
fn ban_let_expr(&self, expr: &'a Expr, forbidden_let_reason: ForbiddenLetReason) {
- self.session.emit_err(ForbiddenLet { span: expr.span, reason: forbidden_let_reason });
+ let sess = &self.session;
+ if sess.opts.unstable_features.is_nightly_build() {
+ sess.emit_err(ForbiddenLet { span: expr.span, reason: forbidden_let_reason });
+ } else {
+ sess.emit_err(ForbiddenLetStable { span: expr.span });
+ }
}
fn check_gat_where(
NotSupportedOr(Span),
/// A let chain with invalid parentheses
///
- /// For exemple, `let 1 = 1 && (expr && expr)` is allowed
+ /// For example, `let 1 = 1 && (expr && expr)` is allowed
/// but `(let 1 = 1 && (let 1 = 1 && (let 1 = 1))) && let a = 1` is not
NotSupportedParentheses(Span),
}
}
}
+#[derive(SessionDiagnostic)]
+#[diag(ast_passes::forbidden_let_stable)]
+#[note]
+pub struct ForbiddenLetStable {
+ #[primary_span]
+ pub span: Span,
+}
+
#[derive(SessionDiagnostic)]
#[diag(ast_passes::forbidden_assoc_constraint)]
pub struct ForbiddenAssocConstraint {
use rustc_span::symbol::sym;
use rustc_span::Span;
-use tracing::debug;
-
macro_rules! gate_feature_fn {
($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $help: expr) => {{
let (visitor, has_feature, span, name, explain, help) =
"`if let` guards are experimental",
"you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`"
);
+ gate_all!(let_chains, "`let` expressions in this position are unstable");
gate_all!(
async_closure,
"async closures are unstable",
#![feature(box_patterns)]
#![feature(if_let_guard)]
#![feature(iter_is_partitioned)]
+#![feature(let_chains)]
#![feature(let_else)]
#![recursion_limit = "256"]
+#[macro_use]
+extern crate tracing;
+
pub mod ast_validation;
mod errors;
pub mod feature_gate;
//! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax`
//! to this crate.
+#![feature(let_chains)]
#![feature(let_else)]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
- "({:?}: {:?}) due to {:?} ({:?})",
- self.sup, self.sub, self.locations, self.variance_info
+ "({:?}: {:?}) due to {:?} ({:?}) ({:?})",
+ self.sup, self.sub, self.locations, self.variance_info, self.category,
)
}
}
/// short a lifetime. (But sometimes it is more useful to report
/// it as a more direct conflict between the execution of a
/// `Drop::drop` with an aliasing borrow.)
+ #[instrument(level = "debug", skip(self))]
pub(crate) fn report_borrowed_value_does_not_live_long_enough(
&mut self,
location: Location,
place_span: (Place<'tcx>, Span),
kind: Option<WriteKind>,
) {
- debug!(
- "report_borrowed_value_does_not_live_long_enough(\
- {:?}, {:?}, {:?}, {:?}\
- )",
- location, borrow, place_span, kind
- );
-
let drop_span = place_span.1;
let root_place =
self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
- debug!(
- "report_borrowed_value_does_not_live_long_enough(place_desc: {:?}, explanation: {:?})",
- place_desc, explanation
- );
+ debug!(?place_desc, ?explanation);
+
let err = match (place_desc, explanation) {
// If the outlives constraint comes from inside the closure,
// for example:
err
}
+ #[instrument(level = "debug", skip(self))]
fn report_temporary_value_does_not_live_long_enough(
&mut self,
location: Location,
proper_span: Span,
explanation: BorrowExplanation<'tcx>,
) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
- debug!(
- "report_temporary_value_does_not_live_long_enough(\
- {:?}, {:?}, {:?}, {:?}\
- )",
- location, borrow, drop_span, proper_span
- );
-
if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
explanation
{
/// - second half is the place being accessed
///
/// [d]: https://rust-lang.github.io/rfcs/2094-nll.html#leveraging-intuition-framing-errors-in-terms-of-points
+ #[instrument(level = "debug", skip(self))]
pub(crate) fn explain_why_borrow_contains_point(
&self,
location: Location,
borrow: &BorrowData<'tcx>,
kind_place: Option<(WriteKind, Place<'tcx>)>,
) -> BorrowExplanation<'tcx> {
- debug!(
- "explain_why_borrow_contains_point(location={:?}, borrow={:?}, kind_place={:?})",
- location, borrow, kind_place
- );
-
let regioncx = &self.regioncx;
let body: &Body<'_> = &self.body;
let tcx = self.infcx.tcx;
let borrow_region_vid = borrow.region;
- debug!("explain_why_borrow_contains_point: borrow_region_vid={:?}", borrow_region_vid);
+ debug!(?borrow_region_vid);
let region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location);
- debug!("explain_why_borrow_contains_point: region_sub={:?}", region_sub);
+ debug!(?region_sub);
match find_use::find(body, regioncx, tcx, region_sub, location) {
Some(Cause::LiveVar(local, location)) => {
opt_place_desc,
}
} else {
- debug!(
- "explain_why_borrow_contains_point: \
- Could not generate a region name"
- );
+ debug!("Could not generate a region name");
BorrowExplanation::Unexplained
}
} else {
- debug!(
- "explain_why_borrow_contains_point: \
- Could not generate an error region vid"
- );
+ debug!("Could not generate an error region vid");
BorrowExplanation::Unexplained
}
}
use rustc_middle::ty::RegionVid;
use smallvec::SmallVec;
use std::collections::BTreeMap;
-use tracing::debug;
use crate::MirBorrowckCtxt;
hir::ExprKind::MethodCall(.., args, _) => {
// only the first closre parameter of the method. args[0] is MethodCall PathSegment
for i in 1..args.len() {
- if let hir::ExprKind::Closure(..) = args[i].kind {
+ if let hir::ExprKind::Closure(hir::Closure {
+ capture_clause: hir::CaptureBy::Ref,
+ ..
+ }) = args[i].kind
+ {
closure_span = Some(args[i].span.shrink_to_lo());
break;
}
hir::ExprKind::Block(blk, _) => {
if let Some(ref expr) = blk.expr {
// only when the block is a closure
- if let hir::ExprKind::Closure(..) = expr.kind {
+ if let hir::ExprKind::Closure(hir::Closure {
+ capture_clause: hir::CaptureBy::Ref,
+ ..
+ }) = expr.kind
+ {
closure_span = Some(expr.span.shrink_to_lo());
}
}
if let Some(closure_span) = closure_span {
diag.span_suggestion_verbose(
closure_span,
- format!("consider adding 'move' keyword before the nested closure"),
+ "consider adding 'move' keyword before the nested closure",
"move ",
Applicability::MaybeIncorrect,
);
/// *user* has a name for. In that case, we'll be able to map
/// `fr` to a `Region<'tcx>`, and that region will be one of
/// named variants.
- #[tracing::instrument(level = "trace", skip(self))]
+ #[instrument(level = "trace", skip(self))]
fn give_name_from_error_region(&self, fr: RegionVid) -> Option<RegionName> {
let error_region = self.to_error_region(fr)?;
/// | fn foo(x: &u32) { .. }
/// ------- fully elaborated type of `x` is `&'1 u32`
/// ```
- #[tracing::instrument(level = "trace", skip(self))]
+ #[instrument(level = "trace", skip(self))]
fn give_name_if_anonymous_region_appears_in_arguments(
&self,
fr: RegionVid,
/// | let x = Some(&22);
/// - fully elaborated type of `x` is `Option<&'1 u32>`
/// ```
- #[tracing::instrument(level = "trace", skip(self))]
+ #[instrument(level = "trace", skip(self))]
fn give_name_if_anonymous_region_appears_in_upvars(&self, fr: RegionVid) -> Option<RegionName> {
let upvar_index = self.regioncx.get_upvar_index_for_region(self.infcx.tcx, fr)?;
let (upvar_name, upvar_span) = self.regioncx.get_upvar_name_and_span_for_region(
/// must be a closure since, in a free fn, such an argument would
/// have to either also appear in an argument (if using elision)
/// or be early bound (named, not in argument).
- #[tracing::instrument(level = "trace", skip(self))]
+ #[instrument(level = "trace", skip(self))]
fn give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Option<RegionName> {
let tcx = self.infcx.tcx;
let hir = tcx.hir();
}
}
- #[tracing::instrument(level = "trace", skip(self))]
+ #[instrument(level = "trace", skip(self))]
fn give_name_if_anonymous_region_appears_in_yield_ty(
&self,
fr: RegionVid,
#![allow(rustc::potential_query_instability)]
#![feature(box_patterns)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(min_specialization)]
#![feature(never_type)]
}
}
+ #[instrument(level = "debug", skip(self, flow_state))]
fn check_access_for_conflict(
&mut self,
location: Location,
rw: ReadOrWrite,
flow_state: &Flows<'cx, 'tcx>,
) -> bool {
- debug!(
- "check_access_for_conflict(location={:?}, place_span={:?}, sd={:?}, rw={:?})",
- location, place_span, sd, rw,
- );
-
let mut error_reported = false;
let tcx = self.infcx.tcx;
let body = self.body;
/// Checks whether a borrow of this place is invalidated when the function
/// exits
+ #[instrument(level = "debug", skip(self))]
fn check_for_invalidation_at_exit(
&mut self,
location: Location,
borrow: &BorrowData<'tcx>,
span: Span,
) {
- debug!("check_for_invalidation_at_exit({:?})", borrow);
let place = borrow.borrowed_place;
let mut root_place = PlaceRef { local: place.local, projection: &[] };
/// access depth. The `bias` parameter is used to determine how the unknowable (comparing runtime
/// array indices, for example) should be interpreted - this depends on what the caller wants in
/// order to make the conservative choice and preserve soundness.
+#[instrument(level = "debug", skip(tcx, body))]
pub(super) fn borrow_conflicts_with_place<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
access: AccessDepth,
bias: PlaceConflictBias,
) -> bool {
- debug!(
- "borrow_conflicts_with_place({:?}, {:?}, {:?}, {:?})",
- borrow_place, access_place, access, bias,
- );
-
// This Local/Local case is handled by the more general code below, but
// it's so common that it's a speed win to check for it first.
if let Some(l1) = borrow_place.as_local() && let Some(l2) = access_place.as_local() {
for (i, (borrow_c, &access_c)) in
iter::zip(borrow_place.projection, access_place.projection).enumerate()
{
- debug!("borrow_conflicts_with_place: borrow_c = {:?}", borrow_c);
- let borrow_proj_base = &borrow_place.projection[..i];
+ debug!(?borrow_c, ?access_c);
- debug!("borrow_conflicts_with_place: access_c = {:?}", access_c);
+ let borrow_proj_base = &borrow_place.projection[..i];
// Borrow and access path both have more components.
//
// idea, at least for now, so just give up and
// report a conflict. This is unsafe code anyway so
// the user could always use raw pointers.
- debug!("borrow_conflicts_with_place: arbitrary -> conflict");
+ debug!("arbitrary -> conflict");
return true;
}
Overlap::EqualOrDisjoint => {
Overlap::Disjoint => {
// We have proven the borrow disjoint - further
// projections will remain disjoint.
- debug!("borrow_conflicts_with_place: disjoint");
+ debug!("disjoint");
return false;
}
}
/// include the CFG anyhow.
/// - For each `end('x)` element in `'r`, compute the mutual LUB, yielding
/// a result `'y`.
- #[instrument(skip(self), level = "debug")]
+ #[instrument(skip(self), level = "debug", ret)]
pub(crate) fn universal_upper_bound(&self, r: RegionVid) -> RegionVid {
debug!(r = %self.region_value_str(r));
lub = self.universal_region_relations.postdom_upper_bound(lub, ur);
}
- debug!(?lub);
-
lub
}
/// Therefore, this method should only be used in diagnostic code,
/// where displaying *some* named universal region is better than
/// falling back to 'static.
+ #[instrument(level = "debug", skip(self))]
pub(crate) fn approx_universal_upper_bound(&self, r: RegionVid) -> RegionVid {
- debug!("approx_universal_upper_bound(r={:?}={})", r, self.region_value_str(r));
+ debug!("{}", self.region_value_str(r));
// Find the smallest universal region that contains all other
// universal regions within `region`.
let static_r = self.universal_regions.fr_static;
for ur in self.scc_values.universal_regions_outlived_by(r_scc) {
let new_lub = self.universal_region_relations.postdom_upper_bound(lub, ur);
- debug!("approx_universal_upper_bound: ur={:?} lub={:?} new_lub={:?}", ur, lub, new_lub);
+ debug!(?ur, ?lub, ?new_lub);
// The upper bound of two non-static regions is static: this
// means we know nothing about the relationship between these
// two regions. Pick a 'better' one to use when constructing
}
}
- debug!("approx_universal_upper_bound: r={:?} lub={:?}", r, lub);
+ debug!(?r, ?lub);
lub
}
}
// Evaluate whether `sup_region: sub_region`.
- #[instrument(skip(self), level = "debug")]
+ #[instrument(skip(self), level = "debug", ret)]
fn eval_outlives(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool {
debug!(
- "eval_outlives: sup_region's value = {:?} universal={:?}",
+ "sup_region's value = {:?} universal={:?}",
self.region_value_str(sup_region),
self.universal_regions.is_universal_region(sup_region),
);
debug!(
- "eval_outlives: sub_region's value = {:?} universal={:?}",
+ "sub_region's value = {:?} universal={:?}",
self.region_value_str(sub_region),
self.universal_regions.is_universal_region(sub_region),
);
// true if `'sup` outlives static.
if !self.universe_compatible(sub_region_scc, sup_region_scc) {
debug!(
- "eval_outlives: sub universe `{sub_region_scc:?}` is not nameable \
+ "sub universe `{sub_region_scc:?}` is not nameable \
by super `{sup_region_scc:?}`, promoting to static",
);
});
if !universal_outlives {
- debug!(
- "eval_outlives: returning false because sub region contains a universal region not present in super"
- );
+ debug!("sub region contains a universal region not present in super");
return false;
}
if self.universal_regions.is_universal_region(sup_region) {
// Micro-opt: universal regions contain all points.
- debug!(
- "eval_outlives: returning true because super is universal and hence contains all points"
- );
+ debug!("super is universal and hence contains all points");
return true;
}
- let result = self.scc_values.contains_points(sup_region_scc, sub_region_scc);
- debug!("returning {} because of comparison between points in sup/sub", result);
- result
+ debug!("comparison between points in sup/sub");
+
+ self.scc_values.contains_points(sup_region_scc, sub_region_scc)
}
/// Once regions have been propagated, this method is used to see
}
/// Finds some region R such that `fr1: R` and `R` is live at `elem`.
- #[instrument(skip(self), level = "trace")]
+ #[instrument(skip(self), level = "trace", ret)]
pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid {
trace!(scc = ?self.constraint_sccs.scc(fr1));
trace!(universe = ?self.scc_universes[self.constraint_sccs.scc(fr1)]);
/// creating a constraint path that forces `R` to outlive
/// `from_region`, and then finding the best choices within that
/// path to blame.
+ #[instrument(level = "debug", skip(self, target_test))]
pub(crate) fn best_blame_constraint(
&self,
body: &Body<'tcx>,
from_region_origin: NllRegionVariableOrigin,
target_test: impl Fn(RegionVid) -> bool,
) -> BlameConstraint<'tcx> {
- debug!(
- "best_blame_constraint(from_region={:?}, from_region_origin={:?})",
- from_region, from_region_origin
- );
-
// Find all paths
let (path, target_region) =
self.find_constraint_paths_between_regions(from_region, target_test).unwrap();
debug!(
- "best_blame_constraint: path={:#?}",
+ "path={:#?}",
path.iter()
.map(|c| format!(
"{:?} ({:?}: {:?})",
}
})
.collect();
- debug!("best_blame_constraint: categorized_path={:#?}", categorized_path);
+ debug!("categorized_path={:#?}", categorized_path);
// To find the best span to cite, we first try to look for the
// final constraint that is interesting and where the `sup` is
let best_choice =
if blame_source { range.rev().find(find_region) } else { range.find(find_region) };
- debug!(
- "best_blame_constraint: best_choice={:?} blame_source={}",
- best_choice, blame_source
- );
+ debug!(?best_choice, ?blame_source);
if let Some(i) = best_choice {
if let Some(next) = categorized_path.get(i + 1) {
// appears to be the most interesting point to report to the
// user via an even more ad-hoc guess.
categorized_path.sort_by(|p0, p1| p0.category.cmp(&p1.category));
- debug!("best_blame_constraint: sorted_path={:#?}", categorized_path);
+ debug!("sorted_path={:#?}", categorized_path);
categorized_path.remove(0)
}
/// Calling `universal_upper_bound` for such a region gives `fr_fn_body`,
/// which has no `external_name` in which case we use `'empty` as the
/// region to pass to `infer_opaque_definition_from_instantiation`.
- #[instrument(level = "debug", skip(self, infcx))]
+ #[instrument(level = "debug", skip(self, infcx), ret)]
pub(crate) fn infer_opaque_types(
&self,
infcx: &InferCtxt<'_, 'tcx>,
/// **Any `rustc_infer::infer` operations that might generate region
/// constraints should occur within this method so that those
/// constraints can be properly localized!**
- #[instrument(skip(self, category, op), level = "trace")]
+ #[instrument(skip(self, op), level = "trace")]
pub(super) fn fully_perform_op<R, Op>(
&mut self,
locations: Locations,
let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation;
let inferred_ty = self.normalize(inferred_ty, Locations::All(span));
let annotation = self.instantiate_canonical_with_fresh_inference_vars(span, user_ty);
+ debug!(?annotation);
match annotation {
UserType::Ty(mut ty) => {
ty = self.normalize(ty, Locations::All(span));
mir_def_id: LocalDefId,
indices: &mut UniversalRegionIndices<'tcx>,
) {
- debug!("replace_late_bound_regions_with_nll_infer_vars(mir_def_id={:?})", mir_def_id);
let typeck_root_def_id = self.tcx.typeck_root_def_id(mir_def_id.to_def_id());
for_each_late_bound_region_defined_on(self.tcx, typeck_root_def_id, |r| {
- debug!("replace_late_bound_regions_with_nll_infer_vars: r={:?}", r);
+ debug!(?r);
if !indices.indices.contains_key(&r) {
let region_vid = self.next_nll_region_var(FR);
debug!(?region_vid);
doctest = false
[dependencies]
-rustc_parse_format = { path = "../rustc_parse_format" }
-tracing = "0.1"
+rustc_ast = { path = "../rustc_ast" }
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
rustc_attr = { path = "../rustc_attr" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
+rustc_expand = { path = "../rustc_expand" }
rustc_feature = { path = "../rustc_feature" }
rustc_lexer = { path = "../rustc_lexer" }
rustc_lint_defs = { path = "../rustc_lint_defs" }
rustc_macros = { path = "../rustc_macros" }
+rustc_parse_format = { path = "../rustc_parse_format" }
rustc_parse = { path = "../rustc_parse" }
-rustc_target = { path = "../rustc_target" }
rustc_session = { path = "../rustc_session" }
-smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-rustc_ast = { path = "../rustc_ast" }
-rustc_expand = { path = "../rustc_expand" }
rustc_span = { path = "../rustc_span" }
+rustc_target = { path = "../rustc_target" }
+smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
+thin-vec = "0.2.8"
+tracing = "0.1"
symbol::{sym, Ident, Symbol},
Span,
};
+use thin_vec::thin_vec;
pub(super) struct Context<'cx, 'a> {
// An optimization.
self.cx.item(
self.span,
Ident::empty(),
- vec![self.cx.attribute(attr::mk_list_item(
+ thin_vec![self.cx.attribute(attr::mk_list_item(
Ident::new(sym::allow, self.span),
vec![attr::mk_nested_word_item(Ident::new(sym::unused_imports, self.span))],
- ))]
- .into(),
+ ))],
ItemKind::Use(UseTree {
prefix: self.cx.path(self.span, self.cx.std_path(&[sym::asserting])),
kind: UseTreeKind::Nested(vec![
use crate::deriving::generic::ty::*;
use crate::deriving::generic::*;
use crate::deriving::path_std;
-
use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, VariantData};
use rustc_data_structures::fx::FxHashSet;
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Span;
+use thin_vec::thin_vec;
pub fn expand_deriving_clone(
cx: &mut ExtCtxt<'_>,
}
let inline = cx.meta_word(span, sym::inline);
- let attrs = vec![cx.attribute(inline)].into();
+ let attrs = thin_vec![cx.attribute(inline)];
let trait_def = TraitDef {
span,
path: path_std!(clone::Clone),
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
+use thin_vec::thin_vec;
pub fn expand_deriving_eq(
cx: &mut ExtCtxt<'_>,
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 no_coverage = cx.meta_word(span, sym::no_coverage);
- let attrs = vec![cx.attribute(inline), cx.attribute(doc), cx.attribute(no_coverage)].into();
+ let attrs = thin_vec![cx.attribute(inline), cx.attribute(doc), cx.attribute(no_coverage)];
let trait_def = TraitDef {
span,
path: path_std!(cmp::Eq),
use crate::deriving::generic::ty::*;
use crate::deriving::generic::*;
use crate::deriving::path_std;
-
use rustc_ast::MetaItem;
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
+use thin_vec::thin_vec;
pub fn expand_deriving_ord(
cx: &mut ExtCtxt<'_>,
push: &mut dyn FnMut(Annotatable),
) {
let inline = cx.meta_word(span, sym::inline);
- let attrs = vec![cx.attribute(inline)].into();
+ let attrs = thin_vec![cx.attribute(inline)];
let trait_def = TraitDef {
span,
path: path_std!(cmp::Ord),
use crate::deriving::generic::ty::*;
use crate::deriving::generic::*;
use crate::deriving::{path_local, path_std};
-
use rustc_ast::ptr::P;
use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::symbol::sym;
use rustc_span::Span;
+use thin_vec::thin_vec;
pub fn expand_deriving_partial_eq(
cx: &mut ExtCtxt<'_>,
// No need to generate `ne`, the default suffices, and not generating it is
// faster.
let inline = cx.meta_word(span, sym::inline);
- let attrs = vec![cx.attribute(inline)].into();
+ let attrs = thin_vec![cx.attribute(inline)];
let methods = vec![MethodDef {
name: sym::eq,
generics: Bounds::empty(),
use crate::deriving::generic::ty::*;
use crate::deriving::generic::*;
use crate::deriving::{path_std, pathvec_std};
-
use rustc_ast::MetaItem;
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
+use thin_vec::thin_vec;
pub fn expand_deriving_partial_ord(
cx: &mut ExtCtxt<'_>,
Path(Path::new_(pathvec_std!(option::Option), vec![Box::new(ordering_ty)], PathKind::Std));
let inline = cx.meta_word(span, sym::inline);
- let attrs = vec![cx.attribute(inline)].into();
+ let attrs = thin_vec![cx.attribute(inline)];
let partial_cmp_def = MethodDef {
name: sym::partial_cmp,
use crate::deriving::generic::ty::*;
use crate::deriving::generic::*;
-
use rustc_ast as ast;
use rustc_ast::{walk_list, EnumDef, VariantData};
use rustc_errors::Applicability;
use rustc_span::symbol::{kw, sym};
use rustc_span::Span;
use smallvec::SmallVec;
+use thin_vec::thin_vec;
pub fn expand_deriving_default(
cx: &mut ExtCtxt<'_>,
item.visit_with(&mut DetectNonVariantDefaultAttr { cx });
let inline = cx.meta_word(span, sym::inline);
- let attrs = vec![cx.attribute(inline)].into();
+ let attrs = thin_vec![cx.attribute(inline)];
let trait_def = TraitDef {
span,
path: Path::new(vec![kw::Default, sym::Default]),
pub use StaticFields::*;
pub use SubstructureFields::*;
-use std::cell::RefCell;
-use std::iter;
-use std::vec;
-
+use crate::deriving;
use rustc_ast::ptr::P;
use rustc_ast::{self as ast, EnumDef, Expr, Generics, PatKind};
use rustc_ast::{GenericArg, GenericParamKind, VariantData};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::Span;
-
+use std::cell::RefCell;
+use std::iter;
+use std::vec;
+use thin_vec::thin_vec;
use ty::{Bounds, Path, Ref, Self_, Ty};
-use crate::deriving;
-
pub mod ty;
pub struct TraitDef<'a> {
let self_type = cx.ty_path(path);
let attr = cx.attribute(cx.meta_word(self.span, sym::automatically_derived));
- let attrs = vec![attr].into();
+ let attrs = thin_vec![attr];
let opt_trait_ref = Some(trait_ref);
cx.item(
) {
match c {
parse::CountImplied | parse::CountIs(..) => {}
- parse::CountIsParam(i) => {
+ parse::CountIsParam(i) | parse::CountIsStar(i) => {
self.unused_names_lint.maybe_add_positional_named_arg(
self.args.get(i),
named_arg_type,
+ self
.arg_with_formatting
.iter()
- .filter(|fmt| matches!(fmt.precision, parse::CountIsParam(_)))
+ .filter(|fmt| matches!(fmt.precision, parse::CountIsStar(_)))
.count();
if self.names.is_empty() && !numbered_position_args && count != self.num_args() {
e = self.ecx.struct_span_err(
if let Some(span) = fmt.precision_span {
let span = self.fmtsp.from_inner(InnerSpan::new(span.start, span.end));
match fmt.precision {
- parse::CountIsParam(pos) if pos > self.num_args() => {
+ parse::CountIsParam(pos) if pos >= self.num_args() => {
e.span_label(
span,
&format!(
);
zero_based_note = true;
}
- parse::CountIsParam(pos) => {
+ parse::CountIsStar(pos) => {
let count = self.pieces.len()
+ self
.arg_with_formatting
.iter()
- .filter(|fmt| matches!(fmt.precision, parse::CountIsParam(_)))
+ .filter(|fmt| matches!(fmt.precision, parse::CountIsStar(_)))
.count();
e.span_label(
span,
};
match c {
parse::CountIs(i) => count(sym::Is, Some(self.ecx.expr_usize(sp, i))),
- parse::CountIsParam(i) => {
+ parse::CountIsParam(i) | parse::CountIsStar(i) => {
// This needs mapping too, as `i` is referring to a macro
// argument. If `i` is not found in `count_positions` then
// the error had already been emitted elsewhere.
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::Span;
+use thin_vec::thin_vec;
pub fn expand(
ecx: &mut ExtCtxt<'_>,
fn attrs(&self) -> AttrVec {
let special = sym::rustc_std_internal_symbol;
let special = self.cx.meta_word(self.span, special);
- vec![self.cx.attribute(special)].into()
+ thin_vec![self.cx.attribute(special)]
}
fn arg_ty(
#![feature(decl_macro)]
#![feature(if_let_guard)]
#![feature(is_sorted)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(proc_macro_internals)]
#![feature(proc_macro_quote)]
extern crate proc_macro;
+#[macro_use]
+extern crate tracing;
+
use crate::deriving::*;
use rustc_expand::base::{MacroExpanderFn, ResolverExpand, SyntaxExtensionKind};
use rustc_span::hygiene::AstPass;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::DUMMY_SP;
+use thin_vec::thin_vec;
pub fn inject(
mut krate: ast::Crate,
cx.item(
span,
ident,
- vec![cx.attribute(cx.meta_word(span, sym::macro_use))].into(),
+ thin_vec![cx.attribute(cx.meta_word(span, sym::macro_use))],
ast::ItemKind::ExternCrate(None),
),
);
let use_item = cx.item(
span,
Ident::empty(),
- vec![cx.attribute(cx.meta_word(span, sym::prelude_import))].into(),
+ thin_vec![cx.attribute(cx.meta_word(span, sym::prelude_import))],
ast::ItemKind::Use(ast::UseTree {
prefix: cx.path(span, import_path),
kind: ast::UseTreeKind::Glob,
/// The expansion from a test function to the appropriate test struct for libtest
/// Ideally, this code would be in libtest but for efficiency and error messages it lives here.
use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute};
-
use rustc_ast as ast;
use rustc_ast::attr;
use rustc_ast::ptr::P;
use rustc_session::Session;
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::Span;
-
use std::iter;
+use thin_vec::thin_vec;
// #[test_case] is used by custom test authors to mark tests
// When building for test, it needs to make the item public and gensym the name
let mut test_const = cx.item(
sp,
Ident::new(item.ident.name, sp),
- vec![
+ thin_vec![
// #[cfg(test)]
cx.attribute(attr::mk_list_item(
Ident::new(sym::cfg, attr_sp),
)),
// #[rustc_test_marker]
cx.attribute(cx.meta_word(attr_sp, sym::rustc_test_marker)),
- ]
- .into(),
+ ],
// const $ident: test::TestDescAndFn =
ast::ItemKind::Const(
ast::Defaultness::Final,
// extern crate test
let test_extern = cx.item(sp, test_id, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None));
- tracing::debug!("synthetic test item:\n{}\n", pprust::item_to_string(&test_const));
+ debug!("synthetic test item:\n{}\n", pprust::item_to_string(&test_const));
if is_stmt {
vec![
use rustc_span::{Span, DUMMY_SP};
use rustc_target::spec::PanicStrategy;
use smallvec::{smallvec, SmallVec};
-use tracing::debug;
+use thin_vec::thin_vec;
use std::{iter, mem};
let main = P(ast::Item {
ident: main_id,
- attrs: vec![main_attr].into(),
+ attrs: thin_vec![main_attr],
id: ast::DUMMY_NODE_ID,
kind: main,
vis: ast::Visibility { span: sp, kind: ast::VisibilityKind::Public, tokens: None },
let ret_place = codegen_place(fx, destination);
- // Handle special calls like instrinsics and empty drop glue.
+ // Handle special calls like intrinsics and empty drop glue.
let instance = if let ty::FnDef(def_id, substs) = *fn_ty.kind() {
let instance = ty::Instance::resolve(fx.tcx, ty::ParamEnv::reveal_all(), def_id, substs)
.unwrap()
args: &[Value],
span: Span,
) {
- let def_id =
- fx.tcx.lang_items().require(lang_item).unwrap_or_else(|s| fx.tcx.sess.span_fatal(span, &s));
+ let def_id = fx
+ .tcx
+ .lang_items()
+ .require(lang_item)
+ .unwrap_or_else(|e| fx.tcx.sess.span_fatal(span, e.to_string()));
let instance = Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx);
let symbol_name = fx.tcx.symbol_name(instance).name;
ErrorHandled::TooGeneric => {
span_bug!(
constant.span,
- "codgen encountered polymorphic constant: {:?}",
+ "codegen encountered polymorphic constant: {:?}",
err
);
}
let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()).to_vec();
data_ctx.define(bytes.into_boxed_slice());
- for &(offset, alloc_id) in alloc.relocations().iter() {
+ for &(offset, alloc_id) in alloc.provenance().iter() {
let addend = {
let endianness = tcx.data_layout.endian;
let offset = offset.bytes() as usize;
sym::transmute => {
crate::base::codegen_panic(fx, "Transmuting to uninhabited type.", source_info);
}
- _ => unimplemented!("unsupported instrinsic {}", intrinsic),
+ _ => unimplemented!("unsupported intrinsic {}", intrinsic),
}
return;
};
let size = Size::from_bytes(
4 * ret_lane_count, /* size_of([u32; ret_lane_count]) */
);
- alloc.inner().get_bytes(fx, alloc_range(offset, size)).unwrap()
+ alloc
+ .inner()
+ .get_bytes_strip_provenance(fx, alloc_range(offset, size))
+ .unwrap()
}
_ => unreachable!("{:?}", idx_const),
};
Type,
UnaryOp,
};
+use rustc_apfloat::{ieee, Float, Round, Status};
use rustc_codegen_ssa::MemFlags;
-use rustc_codegen_ssa::common::{AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope};
+use rustc_codegen_ssa::common::{
+ AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, TypeKind,
+};
use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
use rustc_codegen_ssa::mir::place::PlaceRef;
use rustc_codegen_ssa::traits::{
StaticBuilderMethods,
};
use rustc_data_structures::fx::FxHashSet;
+use rustc_middle::bug;
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
use rustc_middle::ty::layout::{FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout};
use rustc_span::Span;
val
}
- fn fptoui_sat(&mut self, _val: RValue<'gcc>, _dest_ty: Type<'gcc>) -> Option<RValue<'gcc>> {
- None
+ fn fptoui_sat(&mut self, val: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+ self.fptoint_sat(false, val, dest_ty)
}
- fn fptosi_sat(&mut self, _val: RValue<'gcc>, _dest_ty: Type<'gcc>) -> Option<RValue<'gcc>> {
- None
+ fn fptosi_sat(&mut self, val: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+ self.fptoint_sat(true, val, dest_ty)
}
fn instrprof_increment(&mut self, _fn_name: RValue<'gcc>, _hash: RValue<'gcc>, _num_counters: RValue<'gcc>, _index: RValue<'gcc>) {
}
impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
+ fn fptoint_sat(&mut self, signed: bool, val: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+ let src_ty = self.cx.val_ty(val);
+ let (float_ty, int_ty) = if self.cx.type_kind(src_ty) == TypeKind::Vector {
+ assert_eq!(self.cx.vector_length(src_ty), self.cx.vector_length(dest_ty));
+ (self.cx.element_type(src_ty), self.cx.element_type(dest_ty))
+ } else {
+ (src_ty, dest_ty)
+ };
+
+ // FIXME(jistone): the following was originally the fallback SSA implementation, before LLVM 13
+ // added native `fptosi.sat` and `fptoui.sat` conversions, but it was used by GCC as well.
+ // Now that LLVM always relies on its own, the code has been moved to GCC, but the comments are
+ // still LLVM-specific. This should be updated, and use better GCC specifics if possible.
+
+ let int_width = self.cx.int_width(int_ty);
+ let float_width = self.cx.float_width(float_ty);
+ // LLVM's fpto[su]i returns undef when the input val is infinite, NaN, or does not fit into the
+ // destination integer type after rounding towards zero. This `undef` value can cause UB in
+ // safe code (see issue #10184), so we implement a saturating conversion on top of it:
+ // Semantically, the mathematical value of the input is rounded towards zero to the next
+ // mathematical integer, and then the result is clamped into the range of the destination
+ // integer type. Positive and negative infinity are mapped to the maximum and minimum value of
+ // the destination integer type. NaN is mapped to 0.
+ //
+ // Define f_min and f_max as the largest and smallest (finite) floats that are exactly equal to
+ // a value representable in int_ty.
+ // They are exactly equal to int_ty::{MIN,MAX} if float_ty has enough significand bits.
+ // Otherwise, int_ty::MAX must be rounded towards zero, as it is one less than a power of two.
+ // int_ty::MIN, however, is either zero or a negative power of two and is thus exactly
+ // representable. Note that this only works if float_ty's exponent range is sufficiently large.
+ // f16 or 256 bit integers would break this property. Right now the smallest float type is f32
+ // with exponents ranging up to 127, which is barely enough for i128::MIN = -2^127.
+ // On the other hand, f_max works even if int_ty::MAX is greater than float_ty::MAX. Because
+ // we're rounding towards zero, we just get float_ty::MAX (which is always an integer).
+ // This already happens today with u128::MAX = 2^128 - 1 > f32::MAX.
+ let int_max = |signed: bool, int_width: u64| -> u128 {
+ let shift_amount = 128 - int_width;
+ if signed { i128::MAX as u128 >> shift_amount } else { u128::MAX >> shift_amount }
+ };
+ let int_min = |signed: bool, int_width: u64| -> i128 {
+ if signed { i128::MIN >> (128 - int_width) } else { 0 }
+ };
+
+ let compute_clamp_bounds_single = |signed: bool, int_width: u64| -> (u128, u128) {
+ let rounded_min =
+ ieee::Single::from_i128_r(int_min(signed, int_width), Round::TowardZero);
+ assert_eq!(rounded_min.status, Status::OK);
+ let rounded_max =
+ ieee::Single::from_u128_r(int_max(signed, int_width), Round::TowardZero);
+ assert!(rounded_max.value.is_finite());
+ (rounded_min.value.to_bits(), rounded_max.value.to_bits())
+ };
+ let compute_clamp_bounds_double = |signed: bool, int_width: u64| -> (u128, u128) {
+ let rounded_min =
+ ieee::Double::from_i128_r(int_min(signed, int_width), Round::TowardZero);
+ assert_eq!(rounded_min.status, Status::OK);
+ let rounded_max =
+ ieee::Double::from_u128_r(int_max(signed, int_width), Round::TowardZero);
+ assert!(rounded_max.value.is_finite());
+ (rounded_min.value.to_bits(), rounded_max.value.to_bits())
+ };
+ // To implement saturation, we perform the following steps:
+ //
+ // 1. Cast val to an integer with fpto[su]i. This may result in undef.
+ // 2. Compare val to f_min and f_max, and use the comparison results to select:
+ // a) int_ty::MIN if val < f_min or val is NaN
+ // b) int_ty::MAX if val > f_max
+ // c) the result of fpto[su]i otherwise
+ // 3. If val is NaN, return 0.0, otherwise return the result of step 2.
+ //
+ // This avoids resulting undef because values in range [f_min, f_max] by definition fit into the
+ // destination type. It creates an undef temporary, but *producing* undef is not UB. Our use of
+ // undef does not introduce any non-determinism either.
+ // More importantly, the above procedure correctly implements saturating conversion.
+ // Proof (sketch):
+ // If val is NaN, 0 is returned by definition.
+ // Otherwise, val is finite or infinite and thus can be compared with f_min and f_max.
+ // This yields three cases to consider:
+ // (1) if val in [f_min, f_max], the result of fpto[su]i is returned, which agrees with
+ // saturating conversion for inputs in that range.
+ // (2) if val > f_max, then val is larger than int_ty::MAX. This holds even if f_max is rounded
+ // (i.e., if f_max < int_ty::MAX) because in those cases, nextUp(f_max) is already larger
+ // than int_ty::MAX. Because val is larger than int_ty::MAX, the return value of int_ty::MAX
+ // is correct.
+ // (3) if val < f_min, then val is smaller than int_ty::MIN. As shown earlier, f_min exactly equals
+ // int_ty::MIN and therefore the return value of int_ty::MIN is correct.
+ // QED.
+
+ let float_bits_to_llval = |bx: &mut Self, bits| {
+ let bits_llval = match float_width {
+ 32 => bx.cx().const_u32(bits as u32),
+ 64 => bx.cx().const_u64(bits as u64),
+ n => bug!("unsupported float width {}", n),
+ };
+ bx.bitcast(bits_llval, float_ty)
+ };
+ let (f_min, f_max) = match float_width {
+ 32 => compute_clamp_bounds_single(signed, int_width),
+ 64 => compute_clamp_bounds_double(signed, int_width),
+ n => bug!("unsupported float width {}", n),
+ };
+ let f_min = float_bits_to_llval(self, f_min);
+ let f_max = float_bits_to_llval(self, f_max);
+ let int_max = self.cx.const_uint_big(int_ty, int_max(signed, int_width));
+ let int_min = self.cx.const_uint_big(int_ty, int_min(signed, int_width) as u128);
+ let zero = self.cx.const_uint(int_ty, 0);
+
+ // If we're working with vectors, constants must be "splatted": the constant is duplicated
+ // into each lane of the vector. The algorithm stays the same, we are just using the
+ // same constant across all lanes.
+ let maybe_splat = |bx: &mut Self, val| {
+ if bx.cx().type_kind(dest_ty) == TypeKind::Vector {
+ bx.vector_splat(bx.vector_length(dest_ty), val)
+ } else {
+ val
+ }
+ };
+ let f_min = maybe_splat(self, f_min);
+ let f_max = maybe_splat(self, f_max);
+ let int_max = maybe_splat(self, int_max);
+ let int_min = maybe_splat(self, int_min);
+ let zero = maybe_splat(self, zero);
+
+ // Step 1 ...
+ let fptosui_result = if signed { self.fptosi(val, dest_ty) } else { self.fptoui(val, dest_ty) };
+ let less_or_nan = self.fcmp(RealPredicate::RealULT, val, f_min);
+ let greater = self.fcmp(RealPredicate::RealOGT, val, f_max);
+
+ // Step 2: We use two comparisons and two selects, with %s1 being the
+ // result:
+ // %less_or_nan = fcmp ult %val, %f_min
+ // %greater = fcmp olt %val, %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 val is
+ // NaN). The unordered comparison ensures that s1 becomes int_ty::MIN if
+ // val 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 an
+ // ordered ("normal") comparison. Whether these optimizations will be
+ // performed is ultimately up to the backend, but at least x86 does
+ // perform them.
+ let s0 = self.select(less_or_nan, int_min, fptosui_result);
+ let s1 = self.select(greater, int_max, s0);
+
+ // Step 3: NaN replacement.
+ // For unsigned types, the above step already yielded int_ty::MIN == 0 if val is NaN.
+ // Therefore we only need to execute this step for signed integer types.
+ if signed {
+ // LLVM has no isNaN predicate, so we use (val == val) instead
+ let cmp = self.fcmp(RealPredicate::RealOEQ, val, val);
+ self.select(cmp, s1, zero)
+ } else {
+ s1
+ }
+ }
+
#[cfg(feature="master")]
pub fn shuffle_vector(&mut self, v1: RValue<'gcc>, v2: RValue<'gcc>, mask: RValue<'gcc>) -> RValue<'gcc> {
let struct_type = mask.get_type().is_struct().expect("mask of struct type");
//
// We could remove this hack whenever we decide to drop macOS 10.10 support.
if self.tcx.sess.target.options.is_like_osx {
- // The `inspect` method is okay here because we checked relocations, and
+ // The `inspect` method is okay here because we checked for provenance, and
// because we are doing this access to inspect the final interpreter state
// (not as part of the interpreter execution).
//
pub fn const_alloc_to_gcc<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, alloc: ConstAllocation<'tcx>) -> RValue<'gcc> {
let alloc = alloc.inner();
- let mut llvals = Vec::with_capacity(alloc.relocations().len() + 1);
+ let mut llvals = Vec::with_capacity(alloc.provenance().len() + 1);
let dl = cx.data_layout();
let pointer_size = dl.pointer_size.bytes() as usize;
let mut next_offset = 0;
- for &(offset, alloc_id) in alloc.relocations().iter() {
+ for &(offset, alloc_id) in alloc.provenance().iter() {
let offset = offset.bytes();
assert_eq!(offset as usize as u64, offset);
let offset = offset as usize;
if offset > next_offset {
- // This `inspect` is okay since we have checked that it is not within a relocation, it
+ // This `inspect` is okay since we have checked that it is not within a pointer with provenance, it
// is within the bounds of the allocation, and it doesn't affect interpreter execution
// (we inspect the result after interpreter execution). Any undef byte is replaced with
// some arbitrary byte value.
read_target_uint( dl.endian,
// This `inspect` is okay since it is within the bounds of the allocation, it doesn't
// affect interpreter execution (we inspect the result after interpreter execution),
- // and we properly interpret the relocation as a relocation pointer offset.
+ // and we properly interpret the provenance as a relocation pointer offset.
alloc.inspect_with_uninit_and_ptr_outside_interpreter(offset..(offset + pointer_size)),
)
.expect("const_alloc_to_llvm: could not read relocation pointer")
}
if alloc.len() >= next_offset {
let range = next_offset..alloc.len();
- // This `inspect` is okay since we have check that it is after all relocations, it is
+ // This `inspect` is okay since we have check that it is after all provenance, it is
// within the bounds of the allocation, and it doesn't affect interpreter execution (we
// inspect the result after interpreter execution). Any undef byte is replaced with some
// arbitrary byte value.
#![warn(rust_2018_idioms)]
#![warn(unused_lifetimes)]
+extern crate rustc_apfloat;
extern crate rustc_ast;
extern crate rustc_codegen_ssa;
extern crate rustc_data_structures;
libc = "0.2"
libloading = "0.7.1"
measureme = "10.0.0"
+object = { version = "0.29.0", default-features = false, features = ["std", "read_core", "archive", "coff", "elf", "macho", "pe"] }
tracing = "0.1"
rustc_middle = { path = "../rustc_middle" }
rustc-demangle = "0.1.21"
use libc::{c_char, c_uint};
use smallvec::SmallVec;
-use tracing::debug;
impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
fn codegen_inline_asm(
let output_path_z = rustc_fs_util::path_to_c_string(&output_path);
- tracing::trace!("invoking LLVMRustWriteImportLibrary");
- tracing::trace!(" dll_name {:#?}", dll_name_z);
- tracing::trace!(" output_path {}", output_path.display());
- tracing::trace!(
+ trace!("invoking LLVMRustWriteImportLibrary");
+ trace!(" dll_name {:#?}", dll_name_z);
+ trace!(" output_path {}", output_path.display());
+ trace!(
" import names: {}",
dll_imports
.iter()
use crate::back::write::{
self, save_temp_bitcode, to_llvm_opt_settings, with_llvm_pmb, DiagnosticHandlers,
};
-use crate::llvm::archive_ro::ArchiveRO;
use crate::llvm::{self, build_string, False, True};
use crate::{llvm_util, LlvmCodegenBackend, ModuleLlvm};
+use object::read::archive::ArchiveFile;
use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared};
use rustc_codegen_ssa::back::symbol_export;
use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, TargetMachineFactoryConfig};
use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind};
use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::memmap::Mmap;
use rustc_errors::{FatalError, Handler};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::bug;
use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
use rustc_session::cgu_reuse_tracker::CguReuse;
use rustc_session::config::{self, CrateType, Lto};
-use tracing::{debug, info};
use std::ffi::{CStr, CString};
use std::fs::File;
.extend(exported_symbols[&cnum].iter().filter_map(symbol_filter));
}
- let archive = ArchiveRO::open(path).expect("wanted an rlib");
+ let archive_data = unsafe {
+ Mmap::map(std::fs::File::open(&path).expect("couldn't open rlib"))
+ .expect("couldn't map rlib")
+ };
+ let archive = ArchiveFile::parse(&*archive_data).expect("wanted an rlib");
let obj_files = archive
- .iter()
- .filter_map(|child| child.ok().and_then(|c| c.name().map(|name| (name, c))))
+ .members()
+ .filter_map(|child| {
+ child.ok().and_then(|c| {
+ std::str::from_utf8(c.name()).ok().map(|name| (name.trim(), c))
+ })
+ })
.filter(|&(name, _)| looks_like_rust_object_file(name));
for (name, child) in obj_files {
info!("adding bitcode from {}", name);
- match get_bitcode_slice_from_object_data(child.data()) {
+ match get_bitcode_slice_from_object_data(
+ child.data(&*archive_data).expect("corrupt rlib"),
+ ) {
Ok(data) => {
let module = SerializedModule::FromRlib(data.to_vec());
upstream_modules.push((module, CString::new(name).unwrap()));
use rustc_span::symbol::sym;
use rustc_span::InnerSpan;
use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo};
-use tracing::debug;
use libc::{c_char, c_int, c_uint, c_void, size_t};
use std::ffi::CString;
use std::iter;
use std::ops::Deref;
use std::ptr;
-use tracing::{debug, instrument};
// All Builders must have an llfn associated with them
#[must_use]
unsafe { llvm::LLVMBuildSExt(self.llbuilder, val, dest_ty, UNNAMED) }
}
- fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> {
+ fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
self.fptoint_sat(false, val, dest_ty)
}
- fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> {
+ fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
self.fptoint_sat(true, val, dest_ty)
}
}
}
- fn fptoint_sat(
- &mut self,
- signed: bool,
- val: &'ll Value,
- dest_ty: &'ll Type,
- ) -> Option<&'ll Value> {
+ fn fptoint_sat(&mut self, signed: bool, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
let src_ty = self.cx.val_ty(val);
let (float_ty, int_ty, vector_length) = if self.cx.type_kind(src_ty) == TypeKind::Vector {
assert_eq!(self.cx.vector_length(src_ty), self.cx.vector_length(dest_ty));
format!("llvm.{}.sat.i{}.f{}", instr, int_width, float_width)
};
let f = self.declare_cfn(&name, llvm::UnnamedAddr::No, self.type_func(&[src_ty], dest_ty));
- Some(self.call(self.type_func(&[src_ty], dest_ty), f, &[val], None))
+ self.call(self.type_func(&[src_ty], dest_ty), f, &[val], None)
}
pub(crate) fn landing_pad(
use crate::llvm;
use crate::value::Value;
use rustc_codegen_ssa::traits::*;
-use tracing::debug;
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt};
use rustc_middle::ty::{self, Instance, TypeVisitable};
use libc::{c_char, c_uint};
use std::fmt::Write;
-use tracing::debug;
/*
* A note on nomenclature of linking: "extern", "foreign", and "upcall".
AddressSpace, Align, HasDataLayout, Primitive, Scalar, Size, WrappingRange,
};
use std::ops::Range;
-use tracing::debug;
pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<'_>) -> &'ll Value {
let alloc = alloc.inner();
- let mut llvals = Vec::with_capacity(alloc.relocations().len() + 1);
+ let mut llvals = Vec::with_capacity(alloc.provenance().len() + 1);
let dl = cx.data_layout();
let pointer_size = dl.pointer_size.bytes() as usize;
- // Note: this function may call `inspect_with_uninit_and_ptr_outside_interpreter`,
- // so `range` must be within the bounds of `alloc` and not contain or overlap a relocation.
+ // Note: this function may call `inspect_with_uninit_and_ptr_outside_interpreter`, so `range`
+ // must be within the bounds of `alloc` and not contain or overlap a pointer provenance.
fn append_chunks_of_init_and_uninit_bytes<'ll, 'a, 'b>(
llvals: &mut Vec<&'ll Value>,
cx: &'a CodegenCx<'ll, 'b>,
}
let mut next_offset = 0;
- for &(offset, alloc_id) in alloc.relocations().iter() {
+ for &(offset, alloc_id) in alloc.provenance().iter() {
let offset = offset.bytes();
assert_eq!(offset as usize as u64, offset);
let offset = offset as usize;
if offset > next_offset {
- // This `inspect` is okay since we have checked that it is not within a relocation, it
+ // This `inspect` is okay since we have checked that there is no provenance, it
// is within the bounds of the allocation, and it doesn't affect interpreter execution
// (we inspect the result after interpreter execution).
append_chunks_of_init_and_uninit_bytes(&mut llvals, cx, alloc, next_offset..offset);
dl.endian,
// This `inspect` is okay since it is within the bounds of the allocation, it doesn't
// affect interpreter execution (we inspect the result after interpreter execution),
- // and we properly interpret the relocation as a relocation pointer offset.
+ // and we properly interpret the provenance as a relocation pointer offset.
alloc.inspect_with_uninit_and_ptr_outside_interpreter(offset..(offset + pointer_size)),
)
.expect("const_alloc_to_llvm: could not read relocation pointer")
}
if alloc.len() >= next_offset {
let range = next_offset..alloc.len();
- // This `inspect` is okay since we have check that it is after all relocations, it is
+ // This `inspect` is okay since we have check that it is after all provenance, it is
// within the bounds of the allocation, and it doesn't affect interpreter execution (we
// inspect the result after interpreter execution).
append_chunks_of_init_and_uninit_bytes(&mut llvals, cx, alloc, range);
//
// We could remove this hack whenever we decide to drop macOS 10.10 support.
if self.tcx.sess.target.is_like_osx {
- // The `inspect` method is okay here because we checked relocations, and
+ // The `inspect` method is okay here because we checked for provenance, and
// because we are doing this access to inspect the final interpreter state
// (not as part of the interpreter execution).
//
// happens to be zero. Instead, we should only check the value of defined bytes
// and set all undefined bytes to zero if this allocation is headed for the
// BSS.
- let all_bytes_are_zero = alloc.relocations().is_empty()
+ let all_bytes_are_zero = alloc.provenance().is_empty()
&& alloc
.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())
.iter()
section.as_str().as_ptr().cast(),
section.as_str().len() as c_uint,
);
- assert!(alloc.relocations().is_empty());
+ assert!(alloc.provenance().is_empty());
- // The `inspect` method is okay here because we checked relocations, and
+ // The `inspect` method is okay here because we checked for provenance, and
// because we are doing this access to inspect the final interpreter state (not
// as part of the interpreter execution).
let bytes =
use std::ffi::CString;
-use tracing::debug;
-
/// Generates and exports the Coverage Map.
///
/// Rust Coverage Map generation supports LLVM Coverage Mapping Format versions
use std::ffi::CString;
use std::iter;
-use tracing::debug;
pub mod mapgen;
use rustc_symbol_mangling::typeid_for_trait_ref;
use rustc_target::abi::{Align, Size};
use smallvec::smallvec;
-use tracing::debug;
use libc::{c_char, c_longlong, c_uint};
use std::borrow::Cow;
use std::iter;
use std::path::{Path, PathBuf};
use std::ptr;
-use tracing::instrument;
impl PartialEq for llvm::Metadata {
fn eq(&self, other: &Self) -> bool {
use std::cell::OnceCell;
use std::cell::RefCell;
use std::iter;
-use tracing::debug;
mod create_scope_map;
pub mod gdb;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
use rustc_middle::ty::{self, DefIdTree, Ty};
-use tracing::trace;
+use trace;
use crate::common::CodegenCx;
use crate::llvm;
use rustc_middle::ty::Ty;
use rustc_symbol_mangling::typeid::typeid_for_fnabi;
use smallvec::SmallVec;
-use tracing::debug;
/// Declare a function.
///
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(hash_raw_entry)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(extern_types)]
#![feature(once_cell)]
#[macro_use]
extern crate rustc_macros;
+#[macro_use]
+extern crate tracing;
use back::write::{create_informational_target_machine, create_target_machine};
}
}
}
-
- pub fn data(&self) -> &'a [u8] {
- unsafe {
- let mut data_len = 0;
- let data_ptr = super::LLVMRustArchiveChildData(self.raw, &mut data_len);
- if data_ptr.is_null() {
- panic!("failed to read data from archive child");
- }
- slice::from_raw_parts(data_ptr as *const u8, data_len as usize)
- }
- }
}
impl<'a> Drop for Child<'a> {
AIR: &ArchiveIterator<'a>,
) -> Option<&'a mut ArchiveChild<'a>>;
pub fn LLVMRustArchiveChildName(ACR: &ArchiveChild<'_>, size: &mut size_t) -> *const c_char;
- pub fn LLVMRustArchiveChildData(ACR: &ArchiveChild<'_>, size: &mut size_t) -> *const c_char;
pub fn LLVMRustArchiveChildFree<'a>(ACR: &'a mut ArchiveChild<'a>);
pub fn LLVMRustArchiveIteratorFree<'a>(AIR: &'a mut ArchiveIterator<'a>);
pub fn LLVMRustDestroyArchive(AR: &'static mut Archive);
use rustc_target::spec::{MergeFunctions, PanicStrategy};
use smallvec::{smallvec, SmallVec};
use std::ffi::{CStr, CString};
-use tracing::debug;
use std::mem;
use std::path::Path;
use rustc_middle::ty::{self, Instance, TypeVisitable};
use rustc_session::config::CrateType;
use rustc_target::spec::RelocModel;
-use tracing::debug;
impl<'tcx> PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> {
fn predefine_static(
use rustc_target::abi::{Int, Pointer, F32, F64};
use rustc_target::abi::{PointeeInfo, Scalar, Size, TyAbiInterface, Variants};
use smallvec::{smallvec, SmallVec};
-use tracing::debug;
use std::fmt::Write;
rustc_ast = { path = "../rustc_ast" }
rustc_span = { path = "../rustc_span" }
rustc_middle = { path = "../rustc_middle" }
-rustc_apfloat = { path = "../rustc_apfloat" }
rustc_attr = { path = "../rustc_attr" }
rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
rustc_data_structures = { path = "../rustc_data_structures" }
// only the linker flavor is known; use the default linker for the selected flavor
(None, Some(flavor)) => Some((
PathBuf::from(match flavor {
- LinkerFlavor::Em => {
- if cfg!(windows) {
- "emcc.bat"
- } else {
- "emcc"
- }
- }
LinkerFlavor::Gcc => {
if cfg!(any(target_os = "solaris", target_os = "illumos")) {
// On historical Solaris systems, "cc" may have
}
}
LinkerFlavor::Ld => "ld",
- LinkerFlavor::Msvc => "link.exe",
LinkerFlavor::Lld(_) => "lld",
- LinkerFlavor::PtxLinker => "rust-ptx-linker",
- LinkerFlavor::BpfLinker => "bpf-linker",
- LinkerFlavor::L4Bender => "l4-bender",
+ LinkerFlavor::Msvc => "link.exe",
+ LinkerFlavor::EmCc => {
+ if cfg!(windows) {
+ "emcc.bat"
+ } else {
+ "emcc"
+ }
+ }
+ LinkerFlavor::Bpf => "bpf-linker",
+ LinkerFlavor::Ptx => "rust-ptx-linker",
}),
flavor,
)),
});
let flavor = if stem == "emcc" {
- LinkerFlavor::Em
+ LinkerFlavor::EmCc
} else if stem == "gcc"
|| stem.ends_with("-gcc")
|| stem == "clang"
// linker and linker flavor specified via command line have precedence over what the target
// specification specifies
- if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), sess.opts.cg.linker_flavor) {
+ let linker_flavor = sess.opts.cg.linker_flavor.map(LinkerFlavor::from_cli);
+ if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), linker_flavor) {
return ret;
}
});
}
- if flavor == LinkerFlavor::PtxLinker {
+ if flavor == LinkerFlavor::Ptx {
// Provide the linker with fallback to internal `target-cpu`.
cmd.arg("--fallback-arch");
cmd.arg(&codegen_results.crate_info.target_cpu);
- } else if flavor == LinkerFlavor::BpfLinker {
+ } else if flavor == LinkerFlavor::Bpf {
cmd.arg("--cpu");
cmd.arg(&codegen_results.crate_info.target_cpu);
cmd.arg("--cpu-features");
// to the linker args construction.
assert!(cmd.get_args().is_empty() || sess.target.vendor == "uwp");
match flavor {
- LinkerFlavor::Lld(LldFlavor::Link) | LinkerFlavor::Msvc => {
- Box::new(MsvcLinker { cmd, sess }) as Box<dyn Linker>
- }
- LinkerFlavor::Em => Box::new(EmLinker { cmd, sess }) as Box<dyn Linker>,
LinkerFlavor::Gcc => {
Box::new(GccLinker { cmd, sess, target_cpu, hinted_static: false, is_ld: false })
as Box<dyn Linker>
}
-
+ LinkerFlavor::Ld if sess.target.os == "l4re" => {
+ Box::new(L4Bender::new(cmd, sess)) as Box<dyn Linker>
+ }
LinkerFlavor::Lld(LldFlavor::Ld)
| LinkerFlavor::Lld(LldFlavor::Ld64)
| LinkerFlavor::Ld => {
Box::new(GccLinker { cmd, sess, target_cpu, hinted_static: false, is_ld: true })
as Box<dyn Linker>
}
-
+ LinkerFlavor::Lld(LldFlavor::Link) | LinkerFlavor::Msvc => {
+ Box::new(MsvcLinker { cmd, sess }) as Box<dyn Linker>
+ }
LinkerFlavor::Lld(LldFlavor::Wasm) => Box::new(WasmLd::new(cmd, sess)) as Box<dyn Linker>,
-
- LinkerFlavor::PtxLinker => Box::new(PtxLinker { cmd, sess }) as Box<dyn Linker>,
-
- LinkerFlavor::BpfLinker => Box::new(BpfLinker { cmd, sess }) as Box<dyn Linker>,
-
- LinkerFlavor::L4Bender => Box::new(L4Bender::new(cmd, sess)) as Box<dyn Linker>,
+ LinkerFlavor::EmCc => Box::new(EmLinker { cmd, sess }) as Box<dyn Linker>,
+ LinkerFlavor::Bpf => Box::new(BpfLinker { cmd, sess }) as Box<dyn Linker>,
+ LinkerFlavor::Ptx => Box::new(PtxLinker { cmd, sess }) as Box<dyn Linker>,
}
}
.map(|fnabi| (fnabi.conv, &fnabi.args[..]))
.unwrap_or((Conv::Rust, &[]));
- // Decorate symbols with prefices, suffices and total number of bytes of arguments.
+ // Decorate symbols with prefixes, suffixes and total number of bytes of arguments.
// Reference: https://docs.microsoft.com/en-us/cpp/build/reference/decorated-names?view=msvc-170
let (prefix, suffix) = match conv {
Conv::X86Fastcall => ("@", "@"),
}
});
- sess.cgu_reuse_tracker.check_expected_reuse(sess.diagnostic());
+ sess.cgu_reuse_tracker.check_expected_reuse(sess);
sess.abort_if_errors();
use crate::traits::*;
-use rustc_middle::ty::{self, subst::GenericArgKind, ExistentialPredicate, Ty, TyCtxt};
+use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
use rustc_session::config::Lto;
use rustc_symbol_mangling::typeid_for_trait_ref;
use rustc_target::abi::call::FnAbi;
&& bx.cx().sess().lto() == Lto::Fat
{
let typeid =
- bx.typeid_metadata(typeid_for_trait_ref(bx.tcx(), get_trait_ref(bx.tcx(), ty)));
+ bx.typeid_metadata(typeid_for_trait_ref(bx.tcx(), expect_dyn_trait_in_self(ty)));
let vtable_byte_offset = self.0 * bx.data_layout().pointer_size.bytes();
let type_checked_load = bx.type_checked_load(llvtable, vtable_byte_offset, typeid);
let func = bx.extract_value(type_checked_load, 0);
}
}
-fn get_trait_ref<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ty::PolyExistentialTraitRef<'tcx> {
+/// This takes a valid `self` receiver type and extracts the principal trait
+/// ref of the type.
+fn expect_dyn_trait_in_self<'tcx>(ty: Ty<'tcx>) -> ty::PolyExistentialTraitRef<'tcx> {
for arg in ty.peel_refs().walk() {
if let GenericArgKind::Type(ty) = arg.unpack() {
- if let ty::Dynamic(trait_refs, _) = ty.kind() {
- return trait_refs[0].map_bound(|trait_ref| match trait_ref {
- ExistentialPredicate::Trait(tr) => tr,
- ExistentialPredicate::Projection(proj) => proj.trait_ref(tcx),
- ExistentialPredicate::AutoTrait(_) => {
- bug!("auto traits don't have functions")
- }
- });
+ if let ty::Dynamic(data, _) = ty.kind() {
+ return data.principal().expect("expected principal trait object");
}
}
}
// errored or at least linted
ErrorHandled::Reported(_) | ErrorHandled::Linted => {}
ErrorHandled::TooGeneric => {
- span_bug!(const_.span, "codgen encountered polymorphic constant: {:?}", err)
+ span_bug!(const_.span, "codegen encountered polymorphic constant: {:?}", err)
}
}
}
use crate::common::IntPredicate;
use crate::glue;
use crate::traits::*;
-use crate::MemFlags;
use rustc_middle::mir;
use rustc_middle::mir::tcx::PlaceTy;
..
} => {
if variant_index != dataful_variant {
- if bx.cx().sess().target.arch == "arm"
- || bx.cx().sess().target.arch == "aarch64"
- {
- // FIXME(#34427): as workaround for LLVM bug on ARM,
- // use memset of 0 before assigning niche value.
- let fill_byte = bx.cx().const_u8(0);
- let size = bx.cx().const_usize(self.layout.size.bytes());
- bx.memset(self.llval, fill_byte, size, self.align, MemFlags::empty());
- }
-
let niche = self.project_field(bx, tag_field);
let niche_llty = bx.cx().immediate_backend_type(niche.layout);
let niche_value = variant_index.as_u32() - niche_variants.start().as_u32();
use super::abi::AbiBuilderMethods;
use super::asm::AsmBuilderMethods;
-use super::consts::ConstMethods;
use super::coverageinfo::CoverageInfoBuilderMethods;
use super::debuginfo::DebugInfoBuilderMethods;
use super::intrinsic::IntrinsicCallMethods;
use crate::mir::place::PlaceRef;
use crate::MemFlags;
-use rustc_apfloat::{ieee, Float, Round, Status};
use rustc_middle::ty::layout::{HasParamEnv, TyAndLayout};
use rustc_middle::ty::Ty;
use rustc_span::Span;
fn trunc(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
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 fptoui_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
+ fn fptosi_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
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;
return if signed { self.fptosi(x, dest_ty) } else { self.fptoui(x, dest_ty) };
}
- let try_sat_result =
- if signed { self.fptosi_sat(x, dest_ty) } else { self.fptoui_sat(x, dest_ty) };
- if let Some(try_sat_result) = try_sat_result {
- return try_sat_result;
- }
-
- let int_width = self.cx().int_width(int_ty);
- let float_width = self.cx().float_width(float_ty);
- // LLVM's fpto[su]i returns undef when the input x is infinite, NaN, or does not fit into the
- // destination integer type after rounding towards zero. This `undef` value can cause UB in
- // safe code (see issue #10184), so we implement a saturating conversion on top of it:
- // Semantically, the mathematical value of the input is rounded towards zero to the next
- // mathematical integer, and then the result is clamped into the range of the destination
- // integer type. Positive and negative infinity are mapped to the maximum and minimum value of
- // the destination integer type. NaN is mapped to 0.
- //
- // Define f_min and f_max as the largest and smallest (finite) floats that are exactly equal to
- // a value representable in int_ty.
- // They are exactly equal to int_ty::{MIN,MAX} if float_ty has enough significand bits.
- // Otherwise, int_ty::MAX must be rounded towards zero, as it is one less than a power of two.
- // int_ty::MIN, however, is either zero or a negative power of two and is thus exactly
- // representable. Note that this only works if float_ty's exponent range is sufficiently large.
- // f16 or 256 bit integers would break this property. Right now the smallest float type is f32
- // with exponents ranging up to 127, which is barely enough for i128::MIN = -2^127.
- // On the other hand, f_max works even if int_ty::MAX is greater than float_ty::MAX. Because
- // we're rounding towards zero, we just get float_ty::MAX (which is always an integer).
- // This already happens today with u128::MAX = 2^128 - 1 > f32::MAX.
- let int_max = |signed: bool, int_width: u64| -> u128 {
- let shift_amount = 128 - int_width;
- if signed { i128::MAX as u128 >> shift_amount } else { u128::MAX >> shift_amount }
- };
- let int_min = |signed: bool, int_width: u64| -> i128 {
- if signed { i128::MIN >> (128 - int_width) } else { 0 }
- };
-
- let compute_clamp_bounds_single = |signed: bool, int_width: u64| -> (u128, u128) {
- let rounded_min =
- ieee::Single::from_i128_r(int_min(signed, int_width), Round::TowardZero);
- assert_eq!(rounded_min.status, Status::OK);
- let rounded_max =
- ieee::Single::from_u128_r(int_max(signed, int_width), Round::TowardZero);
- assert!(rounded_max.value.is_finite());
- (rounded_min.value.to_bits(), rounded_max.value.to_bits())
- };
- let compute_clamp_bounds_double = |signed: bool, int_width: u64| -> (u128, u128) {
- let rounded_min =
- ieee::Double::from_i128_r(int_min(signed, int_width), Round::TowardZero);
- assert_eq!(rounded_min.status, Status::OK);
- let rounded_max =
- ieee::Double::from_u128_r(int_max(signed, int_width), Round::TowardZero);
- assert!(rounded_max.value.is_finite());
- (rounded_min.value.to_bits(), rounded_max.value.to_bits())
- };
- // To implement saturation, we perform the following steps:
- //
- // 1. Cast x to an integer with fpto[su]i. This may result in undef.
- // 2. Compare x to f_min and f_max, and use the comparison results to select:
- // a) int_ty::MIN if x < f_min or x is NaN
- // b) int_ty::MAX if x > f_max
- // c) the result of fpto[su]i otherwise
- // 3. If x is NaN, return 0.0, otherwise return the result of step 2.
- //
- // This avoids resulting undef because values in range [f_min, f_max] by definition fit into the
- // destination type. It creates an undef temporary, but *producing* undef is not UB. Our use of
- // undef does not introduce any non-determinism either.
- // More importantly, the above procedure correctly implements saturating conversion.
- // Proof (sketch):
- // If x is NaN, 0 is returned by definition.
- // Otherwise, x is finite or infinite and thus can be compared with f_min and f_max.
- // This yields three cases to consider:
- // (1) if x in [f_min, f_max], the result of fpto[su]i is returned, which agrees with
- // saturating conversion for inputs in that range.
- // (2) if x > f_max, then x is larger than int_ty::MAX. This holds even if f_max is rounded
- // (i.e., if f_max < int_ty::MAX) because in those cases, nextUp(f_max) is already larger
- // than int_ty::MAX. Because x is larger than int_ty::MAX, the return value of int_ty::MAX
- // is correct.
- // (3) if x < f_min, then x is smaller than int_ty::MIN. As shown earlier, f_min exactly equals
- // int_ty::MIN and therefore the return value of int_ty::MIN is correct.
- // QED.
-
- let float_bits_to_llval = |bx: &mut Self, bits| {
- let bits_llval = match float_width {
- 32 => bx.cx().const_u32(bits as u32),
- 64 => bx.cx().const_u64(bits as u64),
- n => bug!("unsupported float width {}", n),
- };
- bx.bitcast(bits_llval, float_ty)
- };
- let (f_min, f_max) = match float_width {
- 32 => compute_clamp_bounds_single(signed, int_width),
- 64 => compute_clamp_bounds_double(signed, int_width),
- n => bug!("unsupported float width {}", n),
- };
- let f_min = float_bits_to_llval(self, f_min);
- let f_max = float_bits_to_llval(self, f_max);
- let int_max = self.cx().const_uint_big(int_ty, int_max(signed, int_width));
- let int_min = self.cx().const_uint_big(int_ty, int_min(signed, int_width) as u128);
- let zero = self.cx().const_uint(int_ty, 0);
-
- // If we're working with vectors, constants must be "splatted": the constant is duplicated
- // into each lane of the vector. The algorithm stays the same, we are just using the
- // same constant across all lanes.
- let maybe_splat = |bx: &mut Self, val| {
- if bx.cx().type_kind(dest_ty) == TypeKind::Vector {
- bx.vector_splat(bx.vector_length(dest_ty), val)
- } else {
- val
- }
- };
- let f_min = maybe_splat(self, f_min);
- let f_max = maybe_splat(self, f_max);
- let int_max = maybe_splat(self, int_max);
- let int_min = maybe_splat(self, int_min);
- let zero = maybe_splat(self, zero);
-
- // Step 1 ...
- let fptosui_result = if signed { self.fptosi(x, dest_ty) } else { self.fptoui(x, dest_ty) };
- let less_or_nan = self.fcmp(RealPredicate::RealULT, x, f_min);
- let greater = self.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 an
- // ordered ("normal") comparison. Whether these optimizations will be
- // performed is ultimately up to the backend, but at least x86 does
- // perform them.
- let s0 = self.select(less_or_nan, int_min, fptosui_result);
- let s1 = self.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 = self.fcmp(RealPredicate::RealOEQ, x, x);
- self.select(cmp, s1, zero)
- } else {
- s1
- }
+ if signed { self.fptosi_sat(x, dest_ty) } else { self.fptoui_sat(x, dest_ty) }
}
fn icmp(&mut self, op: IntPredicate, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
use super::InterpCx;
use crate::interpret::{
struct_error, ErrorHandled, FrameInfo, InterpError, InterpErrorInfo, Machine, MachineStopType,
+ UnsupportedOpInfo,
};
/// The CTFE machine has some custom error kinds.
#[derive(Clone, Debug)]
pub enum ConstEvalErrKind {
- NeedsRfc(String),
ConstAccessesStatic,
ModifiedGlobal,
AssertFailure(AssertKind<ConstInt>),
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::ConstEvalErrKind::*;
match *self {
- NeedsRfc(ref msg) => {
- write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg)
- }
ConstAccessesStatic => write!(f, "constant accesses static"),
ModifiedGlobal => {
write!(f, "modifying a static's initial value from another static's initializer")
if let Some(span_msg) = span_msg {
err.span_label(self.span, span_msg);
}
+ // Add some more context for select error types.
+ match self.error {
+ InterpError::Unsupported(
+ UnsupportedOpInfo::ReadPointerAsBytes
+ | UnsupportedOpInfo::PartialPointerOverwrite(_)
+ | UnsupportedOpInfo::PartialPointerCopy(_),
+ ) => {
+ err.help("this code performed an operation that depends on the underlying bytes representing a pointer");
+ err.help("the absolute address of a pointer is not known at compile-time, so such operations are not supported");
+ }
+ _ => {}
+ }
// Add spans for the stacktrace. Don't print a single-line backtrace though.
if self.stacktrace.len() > 1 {
// Helper closure to print duplicated lines.
use crate::interpret::eval_nullary_intrinsic;
use crate::interpret::{
intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId,
- Immediate, InternKind, InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking,
- StackPopCleanup,
+ Immediate, InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy,
+ RefTracking, StackPopCleanup,
};
use rustc_hir::def::DefKind;
}
}
-#[instrument(skip(tcx), level = "debug")]
+#[instrument(skip(tcx), level = "debug", ret)]
pub(crate) fn turn_into_const_value<'tcx>(
tcx: TyCtxt<'tcx>,
constant: ConstAlloc<'tcx>,
);
// Turn this into a proper constant.
- let const_val = op_to_const(&ecx, &mplace.into());
- debug!(?const_val);
-
- const_val
+ op_to_const(&ecx, &mplace.into())
}
#[instrument(skip(tcx), level = "debug")]
ecx.tcx,
"it is undefined behavior to use this value",
|diag| {
- diag.note(NOTE_ON_UNDEFINED_BEHAVIOR_ERROR);
+ if matches!(err.error, InterpError::UndefinedBehavior(_)) {
+ diag.note(NOTE_ON_UNDEFINED_BEHAVIOR_ERROR);
+ }
diag.note(&format!(
"the raw bytes of the constant ({}",
display_allocation(
);
throw_inval!(AlreadyReported(guar));
} else {
+ // `find_mir_or_eval_fn` checks that this is a const fn before even calling us,
+ // so this should be unreachable.
let path = ecx.tcx.def_path_str(def.did);
- Err(ConstEvalErrKind::NeedsRfc(format!("calling extern function `{}`", path))
- .into())
+ bug!("trying to call extern function `{path}` at compile-time");
}
}
_ => Ok(ecx.tcx.instance_mir(instance)),
// CTFE-specific intrinsics.
let Some(ret) = target else {
- return Err(ConstEvalErrKind::NeedsRfc(format!(
- "calling intrinsic `{}`",
- intrinsic_name
- ))
- .into());
+ throw_unsup_format!("intrinsic `{intrinsic_name}` is not supported at compile-time");
};
match intrinsic_name {
sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
}
}
_ => {
- return Err(ConstEvalErrKind::NeedsRfc(format!(
- "calling intrinsic `{}`",
- intrinsic_name
- ))
- .into());
+ throw_unsup_format!(
+ "intrinsic `{intrinsic_name}` is not supported at compile-time"
+ );
}
}
_left: &ImmTy<'tcx>,
_right: &ImmTy<'tcx>,
) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
- Err(ConstEvalErrKind::NeedsRfc("pointer arithmetic or comparison".to_string()).into())
+ throw_unsup_format!("pointer arithmetic or comparison is not supported at compile-time");
}
fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
_ptr: Pointer<AllocId>,
) -> InterpResult<'tcx> {
- Err(ConstEvalErrKind::NeedsRfc("exposing pointers".to_string()).into())
+ // This is only reachable with -Zunleash-the-miri-inside-of-you.
+ throw_unsup_format!("exposing pointers is not possible at compile-time")
}
#[inline(always)]
(unsized_inner_ty, num_elems)
}
-#[instrument(skip(ecx), level = "debug")]
+#[instrument(skip(ecx), level = "debug", ret)]
fn create_pointee_place<'tcx>(
ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>,
ty: Ty<'tcx>,
let ptr = ecx.allocate_ptr(size, align, MemoryKind::Stack).unwrap();
debug!(?ptr);
- let place = MPlaceTy::from_aligned_ptr_with_meta(
+ MPlaceTy::from_aligned_ptr_with_meta(
ptr.into(),
layout,
MemPlaceMeta::Meta(Scalar::from_machine_usize(num_elems as u64, &tcx)),
- );
- debug!(?place);
-
- place
+ )
} else {
create_mplace_from_layout(ecx, ty)
}
/// Converts a `ValTree` to a `ConstValue`, which is needed after mir
/// construction has finished.
// FIXME Merge `valtree_to_const_value` and `valtree_into_mplace` into one function
-#[instrument(skip(tcx), level = "debug")]
+#[instrument(skip(tcx), level = "debug", ret)]
pub fn valtree_to_const_value<'tcx>(
tcx: TyCtxt<'tcx>,
param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
dump_place(&ecx, place.into());
intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap();
- let const_val = match ty.kind() {
+ match ty.kind() {
ty::Ref(_, _, _) => {
let ref_place = place.to_ref(&tcx);
let imm =
op_to_const(&ecx, &imm.into())
}
_ => op_to_const(&ecx, &place.into()),
- };
- debug!(?const_val);
-
- const_val
+ }
}
ty::Never
| ty::Error(_)
impl<'tcx, Prov: Provenance + 'static> LocalState<'tcx, Prov> {
/// Read the local's value or error if the local is not yet live or not live anymore.
- ///
- /// Note: This may only be invoked from the `Machine::access_local` hook and not from
- /// anywhere else. You may be invalidating machine invariants if you do!
#[inline]
pub fn access(&self) -> InterpResult<'tcx, &Operand<Prov>> {
match &self.value {
alloc.mutability = Mutability::Not;
};
// link the alloc id to the actual allocation
- leftover_allocations.extend(alloc.relocations().iter().map(|&(_, alloc_id)| alloc_id));
+ leftover_allocations.extend(alloc.provenance().iter().map(|&(_, alloc_id)| alloc_id));
let alloc = tcx.intern_const_alloc(alloc);
tcx.set_alloc_id_memory(alloc_id, alloc);
None
return Ok(true);
};
- // If there are no relocations in this allocation, it does not contain references
+ // If there is no provenance in this allocation, it does not contain references
// that point to another allocation, and we can avoid the interning walk.
if let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr, size, align)? {
- if !alloc.has_relocations() {
+ if !alloc.has_provenance() {
return Ok(false);
}
} else {
}
fn visit_value(&mut self, mplace: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
- // Handle Reference types, as these are the only relocations supported by const eval.
- // Raw pointers (and boxes) are handled by the `leftover_relocations` logic.
+ // Handle Reference types, as these are the only types with provenance supported by const eval.
+ // Raw pointers (and boxes) are handled by the `leftover_allocations` logic.
let tcx = self.ecx.tcx;
let ty = mplace.layout.ty;
if let ty::Ref(_, referenced_ty, ref_mutability) = *ty.kind() {
/// tracks where in the value we are and thus can show much better error messages.
/// Any errors here would anyway be turned into `const_err` lints, whereas validation failures
/// are hard errors.
-#[tracing::instrument(level = "debug", skip(ecx))]
+#[instrument(level = "debug", skip(ecx))]
pub fn intern_const_alloc_recursive<
'mir,
'tcx: 'mir,
// references and a `leftover_allocations` set (where we only have a todo-list here).
// So we hand-roll the interning logic here again.
match intern_kind {
- // Statics may contain mutable allocations even behind relocations.
+ // Statics may point to mutable allocations.
// Even for immutable statics it would be ok to have mutable allocations behind
// raw pointers, e.g. for `static FOO: *const AtomicUsize = &AtomicUsize::new(42)`.
InternKind::Static(_) => {}
}
let alloc = tcx.intern_const_alloc(alloc);
tcx.set_alloc_id_memory(alloc_id, alloc);
- for &(_, alloc_id) in alloc.inner().relocations().iter() {
+ for &(_, alloc_id) in alloc.inner().provenance().iter() {
if leftover_allocations.insert(alloc_id) {
todo.push(alloc_id);
}
let (a_offset, b_offset) =
match (self.ptr_try_get_alloc_id(a), self.ptr_try_get_alloc_id(b)) {
(Err(a), Err(b)) => {
- // Neither poiner points to an allocation.
+ // Neither pointer points to an allocation.
// If these are inequal or null, this *will* fail the deref check below.
(a, b)
}
let layout = self.layout_of(lhs.layout.ty.builtin_deref(true).unwrap().ty)?;
assert!(!layout.is_unsized());
- let lhs = self.read_pointer(lhs)?;
- let rhs = self.read_pointer(rhs)?;
- let lhs_bytes = self.read_bytes_ptr(lhs, layout.size)?;
- let rhs_bytes = self.read_bytes_ptr(rhs, layout.size)?;
+ let get_bytes = |this: &InterpCx<'mir, 'tcx, M>,
+ op: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
+ size|
+ -> InterpResult<'tcx, &[u8]> {
+ let ptr = this.read_pointer(op)?;
+ let Some(alloc_ref) = self.get_ptr_alloc(ptr, size, Align::ONE)? else {
+ // zero-sized access
+ return Ok(&[]);
+ };
+ if alloc_ref.has_provenance() {
+ throw_ub_format!("`raw_eq` on bytes with provenance");
+ }
+ alloc_ref.get_bytes_strip_provenance()
+ };
+
+ let lhs_bytes = get_bytes(self, lhs, layout.size)?;
+ let rhs_bytes = get_bytes(self, rhs, layout.size)?;
Ok(Scalar::from_bool(lhs_bytes == rhs_bytes))
}
}
right: &ImmTy<'tcx, Self::Provenance>,
) -> InterpResult<'tcx, (Scalar<Self::Provenance>, bool, Ty<'tcx>)>;
- /// Called to read the specified `local` from the `frame`.
- /// Since reading a ZST is not actually accessing memory or locals, this is never invoked
- /// for ZST reads.
- #[inline]
- fn access_local<'a>(
- frame: &'a Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>,
- local: mir::Local,
- ) -> InterpResult<'tcx, &'a Operand<Self::Provenance>>
- where
- 'tcx: 'mir,
- {
- frame.locals[local].access()
- }
-
/// Called to write the specified `local` from the `frame`.
/// Since writing a ZST is not actually accessing memory or locals, this is never invoked
/// for ZST reads.
+ ///
+ /// Due to borrow checker trouble, we indicate the `frame` as an index rather than an `&mut
+ /// Frame`.
#[inline]
fn access_local_mut<'a>(
ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
/// cache the result. (This relies on `AllocMap::get_or` being able to add the
/// owned allocation to the map even when the map is shared.)
///
- /// This must only fail if `alloc` contains relocations.
+ /// This must only fail if `alloc` contains provenance.
fn adjust_allocation<'b>(
ecx: &InterpCx<'mir, 'tcx, Self>,
id: AllocId,
) -> InterpResult<$tcx, Pointer<Option<AllocId>>> {
// Allow these casts, but make the pointer not dereferenceable.
// (I.e., they behave like transmutation.)
+ // This is correct because no pointers can ever be exposed in compile-time evaluation.
Ok(Pointer::from_addr(addr))
}
self.allocate_raw_ptr(alloc, kind).unwrap()
}
- /// This can fail only of `alloc` contains relocations.
+ /// This can fail only of `alloc` contains provenance.
pub fn allocate_raw_ptr(
&mut self,
alloc: Allocation,
msg,
})
}
- // Ensure we never consider the null pointer dereferencable.
+ // Ensure we never consider the null pointer dereferenceable.
if M::Provenance::OFFSET_IS_ADDR {
assert_ne!(ptr.addr(), Size::ZERO);
}
todo.extend(static_roots);
while let Some(id) = todo.pop() {
if reachable.insert(id) {
- // This is a new allocation, add its relocations to `todo`.
+ // This is a new allocation, add the allocation it points to to `todo`.
if let Some((_, alloc)) = self.memory.alloc_map.get(id) {
todo.extend(
- alloc.relocations().values().filter_map(|prov| prov.get_alloc_id()),
+ alloc.provenance().values().filter_map(|prov| prov.get_alloc_id()),
);
}
}
allocs_to_print: &mut VecDeque<AllocId>,
alloc: &Allocation<Prov, Extra>,
) -> std::fmt::Result {
- for alloc_id in alloc.relocations().values().filter_map(|prov| prov.get_alloc_id()) {
+ for alloc_id in alloc.provenance().values().filter_map(|prov| prov.get_alloc_id()) {
allocs_to_print.push_back(alloc_id);
}
write!(fmt, "{}", display_allocation(tcx, alloc))
self.write_scalar(alloc_range(offset, self.tcx.data_layout().pointer_size), val)
}
- /// Mark the entire referenced range as uninitalized
+ /// Mark the entire referenced range as uninitialized
pub fn write_uninit(&mut self) -> InterpResult<'tcx> {
Ok(self
.alloc
}
/// `range` is relative to this allocation reference, not the base of the allocation.
- pub fn check_bytes(&self, range: AllocRange) -> InterpResult<'tcx> {
+ pub fn get_bytes_strip_provenance<'b>(&'b self) -> InterpResult<'tcx, &'a [u8]> {
Ok(self
.alloc
- .check_bytes(&self.tcx, self.range.subrange(range))
+ .get_bytes_strip_provenance(&self.tcx, self.range)
.map_err(|e| e.to_interp_error(self.alloc_id))?)
}
- /// Returns whether the allocation has relocations for the entire range of the `AllocRef`.
- pub(crate) fn has_relocations(&self) -> bool {
- self.alloc.has_relocations(&self.tcx, self.range)
+ /// Returns whether the allocation has provenance anywhere in the range of the `AllocRef`.
+ pub(crate) fn has_provenance(&self) -> bool {
+ self.alloc.range_has_provenance(&self.tcx, self.range)
}
}
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
- /// Reads the given number of bytes from memory. Returns them as a slice.
+ /// Reads the given number of bytes from memory, and strips their provenance if possible.
+ /// Returns them as a slice.
///
/// Performs appropriate bounds checks.
- pub fn read_bytes_ptr(
+ pub fn read_bytes_ptr_strip_provenance(
&self,
ptr: Pointer<Option<M::Provenance>>,
size: Size,
// (We are staying inside the bounds here so all is good.)
Ok(alloc_ref
.alloc
- .get_bytes(&alloc_ref.tcx, alloc_ref.range)
+ .get_bytes_strip_provenance(&alloc_ref.tcx, alloc_ref.range)
.map_err(|e| e.to_interp_error(alloc_ref.alloc_id))?)
}
return Ok(());
};
- // This checks relocation edges on the src, which needs to happen before
- // `prepare_relocation_copy`.
- let src_bytes = src_alloc
- .get_bytes_with_uninit_and_ptr(&tcx, src_range)
- .map_err(|e| e.to_interp_error(src_alloc_id))?
- .as_ptr(); // raw ptr, so we can also get a ptr to the destination allocation
- // first copy the relocations to a temporary buffer, because
- // `get_bytes_mut` will clear the relocations, which is correct,
- // since we don't want to keep any relocations at the target.
- let relocations =
- src_alloc.prepare_relocation_copy(self, src_range, dest_offset, num_copies);
+ // Checks provenance edges on the src, which needs to happen before
+ // `prepare_provenance_copy`.
+ if src_alloc.range_has_provenance(&tcx, alloc_range(src_range.start, Size::ZERO)) {
+ throw_unsup!(PartialPointerCopy(Pointer::new(src_alloc_id, src_range.start)));
+ }
+ if src_alloc.range_has_provenance(&tcx, alloc_range(src_range.end(), Size::ZERO)) {
+ throw_unsup!(PartialPointerCopy(Pointer::new(src_alloc_id, src_range.end())));
+ }
+ let src_bytes = src_alloc.get_bytes_unchecked(src_range).as_ptr(); // raw ptr, so we can also get a ptr to the destination allocation
+ // first copy the provenance to a temporary buffer, because
+ // `get_bytes_mut` will clear the provenance, which is correct,
+ // since we don't want to keep any provenance at the target.
+ let provenance =
+ src_alloc.prepare_provenance_copy(self, src_range, dest_offset, num_copies);
// Prepare a copy of the initialization mask.
let compressed = src_alloc.compress_uninit_range(src_range);
dest_alloc
.write_uninit(&tcx, dest_range)
.map_err(|e| e.to_interp_error(dest_alloc_id))?;
- // We can forget about the relocations, this is all not initialized anyway.
+ // We can forget about the provenance, this is all not initialized anyway.
return Ok(());
}
alloc_range(dest_offset, size), // just a single copy (i.e., not full `dest_range`)
num_copies,
);
- // copy the relocations to the destination
- dest_alloc.mark_relocation_range(relocations);
+ // copy the provenance to the destination
+ dest_alloc.mark_provenance_range(provenance);
Ok(())
}
/// Turn the wide MPlace into a string (must already be dereferenced!)
pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx, &str> {
let len = mplace.len(self)?;
- let bytes = self.read_bytes_ptr(mplace.ptr, Size::from_bytes(len))?;
+ let bytes = self.read_bytes_ptr_strip_provenance(mplace.ptr, Size::from_bytes(len))?;
let str = std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?;
Ok(str)
}
}
}
- /// Read from a local. Will not actually access the local if reading from a ZST.
+ /// Read from a local.
/// Will not access memory, instead an indirect `Operand` is returned.
///
/// This is public because it is used by [priroda](https://github.com/oli-obk/priroda) to get an
layout: Option<TyAndLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
let layout = self.layout_of_local(frame, local, layout)?;
- let op = if layout.is_zst() {
- // Bypass `access_local` (helps in ConstProp)
- Operand::Immediate(Immediate::Uninit)
- } else {
- *M::access_local(frame, local)?
- };
+ let op = *frame.locals[local].access()?;
Ok(OpTy { op, layout, align: Some(layout.align.abi) })
}
//! into a place.
//! All high-level functions to write to memory work on places as destinations.
-use std::hash::Hash;
-
use rustc_ast::Mutability;
use rustc_middle::mir;
use rustc_middle::ty;
// FIXME: Working around https://github.com/rust-lang/rust/issues/54385
impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M>
where
- Prov: Provenance + Eq + Hash + 'static,
+ Prov: Provenance + 'static,
M: Machine<'mir, 'tcx, Provenance = Prov>,
{
/// Take a value, which represents a (thin or wide) reference, and make it a place.
// avoid force_allocation.
let src = match self.read_immediate_raw(src)? {
Ok(src_val) => {
- assert!(!src.layout.is_unsized(), "cannot have unsized immediates");
+ assert!(!src.layout.is_unsized(), "cannot copy unsized immediates");
assert!(
!dest.layout.is_unsized(),
"the src is sized, so the dest must also be sized"
//! This file implements "place projections"; basically a symmetric API for 3 types: MPlaceTy, OpTy, PlaceTy.
//!
-//! OpTy and PlaceTy genrally work by "let's see if we are actually an MPlaceTy, and do something custom if not".
+//! OpTy and PlaceTy generally work by "let's see if we are actually an MPlaceTy, and do something custom if not".
//! For PlaceTy, the custom thing is basically always to call `force_allocation` and then use the MPlaceTy logic anyway.
//! For OpTy, the custom thing on field pojections has to be pretty clever (since `Operand::Immediate` can have fields),
//! but for array/slice operations it only has to worry about `Operand::Uninit`. That makes the value part trivial,
//! but we still need to do bounds checking and adjust the layout. To not duplicate that with MPlaceTy, we actually
//! implement the logic on OpTy, and MPlaceTy calls that.
-use std::hash::Hash;
-
use rustc_middle::mir;
use rustc_middle::ty;
use rustc_middle::ty::layout::LayoutOf;
// FIXME: Working around https://github.com/rust-lang/rust/issues/54385
impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M>
where
- Prov: Provenance + Eq + Hash + 'static,
+ Prov: Provenance + 'static,
M: Machine<'mir, 'tcx, Provenance = Prov>,
{
//# Field access
// This makes several assumptions about what layouts we will encounter; we match what
// codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`).
let field_val: Immediate<_> = match (*base, base.layout.abi) {
+ // if the entire value is uninit, then so is the field (can happen in ConstProp)
+ (Immediate::Uninit, _) => Immediate::Uninit,
// the field contains no information, can be left uninit
_ if field_layout.is_zst() => Immediate::Uninit,
// the field covers the entire type
b_val
})
}
+ // everything else is a bug
_ => span_bug!(
self.cur_span(),
"invalid field access on immediate {}, layout {:#?}",
Len(place) => {
let src = self.eval_place(place)?;
- let mplace = self.force_allocation(&src)?;
- let len = mplace.len(self)?;
+ let op = self.place_to_op(&src)?;
+ let len = op.len(self)?;
self.write_scalar(Scalar::from_machine_usize(len, self), &dest)?;
}
// When comparing the PassMode, we have to be smart about comparing the attributes.
let arg_attr_compat = |a1: &ArgAttributes, a2: &ArgAttributes| {
// There's only one regular attribute that matters for the call ABI: InReg.
- // Everything else is things like noalias, dereferencable, nonnull, ...
+ // Everything else is things like noalias, dereferenceable, nonnull, ...
// (This also applies to pointee_size, pointee_align.)
if a1.regular.contains(ArgAttribute::InReg) != a2.regular.contains(ArgAttribute::InReg)
{
.tcx
.struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env);
let ty::Dynamic(data, ..) = receiver_tail.kind() else {
- span_bug!(self.cur_span(), "dyanmic call on non-`dyn` type {}", receiver_tail)
+ span_bug!(self.cur_span(), "dynamic call on non-`dyn` type {}", receiver_tail)
};
// Get the required information from the vtable.
Ok(vtable_ptr.into())
}
- /// Returns a high-level representation of the entires of the given vtable.
+ /// Returns a high-level representation of the entries of the given vtable.
pub fn get_vtable_entries(
&self,
vtable: Pointer<Option<M::Provenance>>,
use std::hash::Hash;
+// for the validation errors
+use super::UndefinedBehaviorInfo::*;
use super::{
- alloc_range, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy,
- Machine, MemPlaceMeta, OpTy, Scalar, ValueVisitor,
+ CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine,
+ MemPlaceMeta, OpTy, Scalar, ValueVisitor,
};
macro_rules! throw_validation_failure {
/// });
/// ```
///
+/// The patterns must be of type `UndefinedBehaviorInfo`.
/// An additional expected parameter can also be added to the failure message:
///
/// ```
// allocation here as this can only slow down builds that fail anyway.
Err(e) => match e.kind() {
$(
- $($p)|+ =>
+ InterpError::UndefinedBehavior($($p)|+) =>
throw_validation_failure!(
$where,
{ $( $what_fmt ),+ } $( expected { $( $expected_fmt ),+ } )?
Ok(try_validation!(
self.ecx.read_immediate(op),
self.path,
- err_unsup!(ReadPointerAsBytes) => { "(potentially part of) a pointer" } expected { "{expected}" },
- err_ub!(InvalidUninitBytes(None)) => { "uninitialized memory" } expected { "{expected}" }
+ InvalidUninitBytes(None) => { "uninitialized memory" } expected { "{expected}" }
))
}
let (_ty, _trait) = try_validation!(
self.ecx.get_ptr_vtable(vtable),
self.path,
- err_ub!(DanglingIntPointer(..)) |
- err_ub!(InvalidVTablePointer(..)) =>
+ DanglingIntPointer(..) |
+ InvalidVTablePointer(..) =>
{ "{vtable}" } expected { "a vtable pointer" },
);
// FIXME: check if the type/trait match what ty::Dynamic says?
}
ty::Slice(..) | ty::Str => {
- let _len = try_validation!(
- meta.unwrap_meta().to_machine_usize(self.ecx),
- self.path,
- err_unsup!(ReadPointerAsBytes) => { "non-integer slice length in wide pointer" },
- );
+ let _len = meta.unwrap_meta().to_machine_usize(self.ecx)?;
// We do not check that `len * elem_size <= isize::MAX`:
// that is only required for references, and there it falls out of the
// "dereferenceable" check performed by Stacked Borrows.
let size_and_align = try_validation!(
self.ecx.size_and_align_of_mplace(&place),
self.path,
- err_ub!(InvalidMeta(msg)) => { "invalid {} metadata: {}", kind, msg },
+ InvalidMeta(msg) => { "invalid {} metadata: {}", kind, msg },
);
let (size, align) = size_and_align
// for the purpose of validity, consider foreign types to have
CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message
),
self.path,
- err_ub!(AlignmentCheckFailed { required, has }) =>
+ AlignmentCheckFailed { required, has } =>
{
"an unaligned {kind} (required {} byte alignment but found {})",
required.bytes(),
has.bytes()
},
- err_ub!(DanglingIntPointer(0, _)) =>
+ DanglingIntPointer(0, _) =>
{ "a null {kind}" },
- err_ub!(DanglingIntPointer(i, _)) =>
+ DanglingIntPointer(i, _) =>
{ "a dangling {kind} (address {i:#x} is unallocated)" },
- err_ub!(PointerOutOfBounds { .. }) =>
+ PointerOutOfBounds { .. } =>
{ "a dangling {kind} (going beyond the bounds of its allocation)" },
// This cannot happen during const-eval (because interning already detects
// dangling pointers), but it can happen in Miri.
- err_ub!(PointerUseAfterFree(..)) =>
+ PointerUseAfterFree(..) =>
{ "a dangling {kind} (use-after-free)" },
);
// Do not allow pointers to uninhabited types.
try_validation!(
value.to_bool(),
self.path,
- err_ub!(InvalidBool(..)) =>
+ InvalidBool(..) =>
{ "{:x}", value } expected { "a boolean" },
);
Ok(true)
try_validation!(
value.to_char(),
self.path,
- err_ub!(InvalidChar(..)) =>
+ InvalidChar(..) =>
{ "{:x}", value } expected { "a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`)" },
);
Ok(true)
let _fn = try_validation!(
self.ecx.get_ptr_fn(ptr),
self.path,
- err_ub!(DanglingIntPointer(..)) |
- err_ub!(InvalidFunctionPointer(..)) =>
+ DanglingIntPointer(..) |
+ InvalidFunctionPointer(..) =>
{ "{ptr}" } expected { "a function pointer" },
);
// FIXME: Check if the signature matches
Ok(try_validation!(
this.ecx.read_discriminant(op),
this.path,
- err_ub!(InvalidTag(val)) =>
+ InvalidTag(val) =>
{ "{:x}", val } expected { "a valid enum tag" },
- err_ub!(InvalidUninitBytes(None)) =>
+ InvalidUninitBytes(None) =>
{ "uninitialized bytes" } expected { "a valid enum tag" },
- err_unsup!(ReadPointerAsBytes) =>
- { "a pointer" } expected { "a valid enum tag" },
)
.1)
})
let mplace = op.assert_mem_place(); // strings are unsized and hence never immediate
let len = mplace.len(self.ecx)?;
try_validation!(
- self.ecx.read_bytes_ptr(mplace.ptr, Size::from_bytes(len)),
+ self.ecx.read_bytes_ptr_strip_provenance(mplace.ptr, Size::from_bytes(len)),
self.path,
- err_ub!(InvalidUninitBytes(..)) => { "uninitialized data in `str`" },
- err_unsup!(ReadPointerAsBytes) => { "a pointer in `str`" },
+ InvalidUninitBytes(..) => { "uninitialized data in `str`" },
);
}
ty::Array(tys, ..) | ty::Slice(tys)
// We also accept uninit, for consistency with the slow path.
let alloc = self.ecx.get_ptr_alloc(mplace.ptr, size, mplace.align)?.expect("we already excluded size 0");
- match alloc.check_bytes(alloc_range(Size::ZERO, size)) {
+ match alloc.get_bytes_strip_provenance() {
// In the happy case, we needn't check anything else.
- Ok(()) => {}
+ Ok(_) => {}
// Some error happened, try to provide a more detailed description.
Err(err) => {
// For some errors we might be able to provide extra information.
throw_validation_failure!(self.path, { "uninitialized bytes" })
}
- err_unsup!(ReadPointerAsBytes) => {
- throw_validation_failure!(self.path, { "a pointer" } expected { "plain (non-pointer) bytes" })
- }
// Propagate upwards (that will also check for unexpected errors).
_ => return Err(err),
Ok(()) => Ok(()),
// Pass through validation failures.
Err(err) if matches!(err.kind(), err_ub!(ValidationFailure { .. })) => Err(err),
- // Also pass through InvalidProgram, those just indicate that we could not
- // validate and each caller will know best what to do with them.
- Err(err) if matches!(err.kind(), InterpError::InvalidProgram(_)) => Err(err),
- // Avoid other errors as those do not show *where* in the value the issue lies.
- Err(err) => {
+ // Complain about any other kind of UB error -- those are bad because we'd like to
+ // report them in a way that shows *where* in the value the issue lies.
+ Err(err) if matches!(err.kind(), InterpError::UndefinedBehavior(_)) => {
err.print_backtrace();
- bug!("Unexpected error during validation: {}", err);
+ bug!("Unexpected Undefined Behavior error during validation: {}", err);
}
+ // Pass through everything else.
+ Err(err) => Err(err),
}
}
#![feature(control_flow_enum)]
#![feature(decl_macro)]
#![feature(exact_size_is_empty)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(map_try_insert)]
#![feature(min_specialization)]
impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
fn phase_change(&self) -> Option<MirPhase> {
- Some(MirPhase::ConstsPromoted)
+ Some(MirPhase::Analysis(AnalysisPhase::Initial))
}
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let mut scope = body.source_scopes[body.source_info(candidate.location).scope].clone();
scope.parent_scope = None;
- let promoted = Body::new(
+ let mut promoted = Body::new(
body.source, // `promoted` gets filled in below
IndexVec::new(),
IndexVec::from_elem_n(scope, 1),
body.generator_kind(),
body.tainted_by_errors,
);
+ promoted.phase = MirPhase::Analysis(AnalysisPhase::Initial);
let promoter = Promoter {
promoted,
use rustc_middle::mir::visit::{PlaceContext, Visitor};
use rustc_middle::mir::{
traversal, AggregateKind, BasicBlock, BinOp, Body, BorrowKind, CastKind, Local, Location,
- MirPass, MirPhase, Operand, Place, PlaceElem, PlaceRef, ProjectionElem, Rvalue, SourceScope,
- Statement, StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK,
+ MirPass, MirPhase, Operand, Place, PlaceElem, PlaceRef, ProjectionElem, RuntimePhase, Rvalue,
+ SourceScope, Statement, StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK,
};
use rustc_middle::ty::fold::BottomUpFolder;
use rustc_middle::ty::subst::Subst;
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
// This check is somewhat expensive, so only run it when -Zvalidate-mir is passed.
- if self.tcx.sess.opts.unstable_opts.validate_mir && self.mir_phase < MirPhase::DropsLowered
+ if self.tcx.sess.opts.unstable_opts.validate_mir
+ && self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial)
{
// `Operand::Copy` is only supposed to be used with `Copy` types.
if let Operand::Copy(place) = operand {
self.fail(location, format!("bad index ({:?} != usize)", index_ty))
}
}
- ProjectionElem::Deref if self.mir_phase >= MirPhase::GeneratorsLowered => {
+ ProjectionElem::Deref
+ if self.mir_phase >= MirPhase::Runtime(RuntimePhase::PostCleanup) =>
+ {
let base_ty = Place::ty_from(local, proj_base, &self.body.local_decls, self.tcx).ty;
if base_ty.is_box() {
// Set off any `bug!`s in the type computation code
let _ = place.ty(&self.body.local_decls, self.tcx);
- if self.mir_phase >= MirPhase::Derefered
+ if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial)
&& place.projection.len() > 1
&& cntxt != PlaceContext::NonUse(VarDebugInfo)
&& place.projection[1..].contains(&ProjectionElem::Deref)
Rvalue::Aggregate(agg_kind, _) => {
let disallowed = match **agg_kind {
AggregateKind::Array(..) => false,
- AggregateKind::Generator(..) => self.mir_phase >= MirPhase::GeneratorsLowered,
- _ => self.mir_phase >= MirPhase::Deaggregated,
+ _ => self.mir_phase >= MirPhase::Runtime(RuntimePhase::PostCleanup),
};
if disallowed {
self.fail(
}
}
Rvalue::Ref(_, BorrowKind::Shallow, _) => {
- if self.mir_phase >= MirPhase::DropsLowered {
+ if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
self.fail(
location,
- "`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase",
+ "`Assign` statement with a `Shallow` borrow should have been removed in runtime MIR",
);
}
}
}
}
StatementKind::AscribeUserType(..) => {
- if self.mir_phase >= MirPhase::DropsLowered {
+ if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
self.fail(
location,
"`AscribeUserType` should have been removed after drop lowering phase",
}
}
StatementKind::FakeRead(..) => {
- if self.mir_phase >= MirPhase::DropsLowered {
+ if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
self.fail(
location,
"`FakeRead` should have been removed after drop lowering phase",
}
}
StatementKind::SetDiscriminant { place, .. } => {
- if self.mir_phase < MirPhase::Deaggregated {
+ if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
self.fail(location, "`SetDiscriminant`is not allowed until deaggregation");
}
let pty = place.ty(&self.body.local_decls, self.tcx).ty.kind();
}
}
StatementKind::Deinit(..) => {
- if self.mir_phase < MirPhase::Deaggregated {
+ if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
self.fail(location, "`Deinit`is not allowed until deaggregation");
}
}
}
}
TerminatorKind::DropAndReplace { target, unwind, .. } => {
- if self.mir_phase >= MirPhase::DropsLowered {
+ if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
self.fail(
location,
"`DropAndReplace` should have been removed during drop elaboration",
if self.body.generator.is_none() {
self.fail(location, "`Yield` cannot appear outside generator bodies");
}
- if self.mir_phase >= MirPhase::GeneratorsLowered {
+ if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
self.fail(location, "`Yield` should have been replaced by generator lowering");
}
self.check_edge(location, *resume, EdgeKind::Normal);
}
}
TerminatorKind::FalseEdge { real_target, imaginary_target } => {
- if self.mir_phase >= MirPhase::DropsLowered {
+ if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
self.fail(
location,
"`FalseEdge` should have been removed after drop elaboration",
self.check_edge(location, *imaginary_target, EdgeKind::Normal);
}
TerminatorKind::FalseUnwind { real_target, unwind } => {
- if self.mir_phase >= MirPhase::DropsLowered {
+ if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
self.fail(
location,
"`FalseUnwind` should have been removed after drop elaboration",
if self.body.generator.is_none() {
self.fail(location, "`GeneratorDrop` cannot appear outside generator bodies");
}
- if self.mir_phase >= MirPhase::GeneratorsLowered {
+ if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
self.fail(
location,
"`GeneratorDrop` should have been replaced by generator lowering",
[dependencies]
arrayvec = { version = "0.7", default-features = false }
+bitflags = "1.2.1"
+cfg-if = "0.1.2"
ena = "0.14"
indexmap = { version = "1.9.1" }
-tracing = "0.1"
jobserver_crate = { version = "0.1.13", package = "jobserver" }
-rustc_serialize = { path = "../rustc_serialize" }
-rustc_macros = { path = "../rustc_macros" }
-rustc_graphviz = { path = "../rustc_graphviz" }
-cfg-if = "0.1.2"
-stable_deref_trait = "1.0.0"
-rayon = { version = "0.4.0", package = "rustc-rayon", optional = true }
+libc = "0.2"
+measureme = "10.0.0"
rayon-core = { version = "0.4.0", package = "rustc-rayon-core", optional = true }
+rayon = { version = "0.4.0", package = "rustc-rayon", optional = true }
+rustc_graphviz = { path = "../rustc_graphviz" }
rustc-hash = "1.1.0"
-smallvec = { version = "1.8.1", features = ["const_generics", "union", "may_dangle"] }
rustc_index = { path = "../rustc_index", package = "rustc_index" }
-bitflags = "1.2.1"
-measureme = "10.0.0"
-libc = "0.2"
+rustc_macros = { path = "../rustc_macros" }
+rustc_serialize = { path = "../rustc_serialize" }
+smallvec = { version = "1.8.1", features = ["const_generics", "union", "may_dangle"] }
+stable_deref_trait = "1.0.0"
stacker = "0.1.14"
tempfile = "3.2"
+thin-vec = "0.2.8"
+tracing = "0.1"
[dependencies.parking_lot]
version = "0.11"
// quality hash values, let's still combine the two values because the
// Fingerprints in DefPathHash have the StableCrateId portion which is
// the same for all DefPathHashes from the same crate. Combining the
- // two halfs makes sure we get a good quality hash in such cases too.
+ // two halves makes sure we get a good quality hash in such cases too.
self.0.wrapping_mul(3).wrapping_add(self.1)
}
// quality hash values, let's still combine the two values because the
// Fingerprints in DefPathHash have the StableCrateId portion which is
// the same for all DefPathHashes from the same crate. Combining the
- // two halfs makes sure we get a good quality hash in such cases too.
+ // two halves makes sure we get a good quality hash in such cases too.
//
// Since `Unhasher` is used only in the context of HashMaps, it is OK
// to combine the two components in an order-independent way (which is
pub mod sharded;
pub mod stack;
pub mod sync;
-pub mod thin_vec;
pub mod tiny_list;
pub mod transitive_relation;
pub mod vec_linked_list;
-use crate::thin_vec::ThinVec;
use smallvec::{Array, SmallVec};
use std::ptr;
+use thin_vec::ThinVec;
pub trait MapInPlace<T>: Sized {
fn map_in_place<F>(&mut self, mut f: F)
/// It is up to the caller to make sure that the elements are sorted by key
/// and that there are no duplicates.
#[inline]
- pub fn insert_presorted(&mut self, mut elements: Vec<(K, V)>) {
+ pub fn insert_presorted(&mut self, elements: Vec<(K, V)>) {
if elements.is_empty() {
return;
}
let start_index = self.lookup_index_for(&elements[0].0);
- let drain = match start_index {
+ let elements = match start_index {
Ok(index) => {
- let mut drain = elements.drain(..);
- self.data[index] = drain.next().unwrap();
- drain
+ let mut elements = elements.into_iter();
+ self.data[index] = elements.next().unwrap();
+ elements
}
Err(index) => {
if index == self.data.len() || elements.last().unwrap().0 < self.data[index].0 {
// We can copy the whole range without having to mix with
// existing elements.
- self.data.splice(index..index, elements.drain(..));
+ self.data.splice(index..index, elements.into_iter());
return;
}
- let mut drain = elements.drain(..);
- self.data.insert(index, drain.next().unwrap());
- drain
+ let mut elements = elements.into_iter();
+ self.data.insert(index, elements.next().unwrap());
+ elements
}
};
// Insert the rest
- for (k, v) in drain {
+ for (k, v) in elements {
self.insert(k, v);
}
}
+++ /dev/null
-use crate::stable_hasher::{HashStable, StableHasher};
-
-use std::iter::FromIterator;
-
-/// A vector type optimized for cases where this size is usually 0 (cf. `SmallVec`).
-/// The `Option<Box<..>>` wrapping allows us to represent a zero sized vector with `None`,
-/// which uses only a single (null) pointer.
-#[derive(Clone, Encodable, Decodable, Debug, Hash, Eq, PartialEq)]
-pub struct ThinVec<T>(Option<Box<Vec<T>>>);
-
-impl<T> ThinVec<T> {
- pub fn new() -> Self {
- ThinVec(None)
- }
-
- pub fn iter(&self) -> std::slice::Iter<'_, T> {
- self.into_iter()
- }
-
- pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
- self.into_iter()
- }
-
- pub fn push(&mut self, item: T) {
- match *self {
- ThinVec(Some(ref mut vec)) => vec.push(item),
- ThinVec(None) => *self = vec![item].into(),
- }
- }
-
- /// Note: if `set_len(0)` is called on a non-empty `ThinVec`, it will
- /// remain in the `Some` form. This is required for some code sequences
- /// (such as the one in `flat_map_in_place`) that call `set_len(0)` before
- /// an operation that might panic, and then call `set_len(n)` again
- /// afterwards.
- pub unsafe fn set_len(&mut self, new_len: usize) {
- match *self {
- ThinVec(None) => {
- // A prerequisite of `Vec::set_len` is that `new_len` must be
- // less than or equal to capacity(). The same applies here.
- if new_len != 0 {
- panic!("unsafe ThinVec::set_len({})", new_len);
- }
- }
- ThinVec(Some(ref mut vec)) => vec.set_len(new_len),
- }
- }
-
- pub fn insert(&mut self, index: usize, value: T) {
- match *self {
- ThinVec(None) => {
- if index == 0 {
- *self = vec![value].into();
- } else {
- panic!("invalid ThinVec::insert");
- }
- }
- ThinVec(Some(ref mut vec)) => vec.insert(index, value),
- }
- }
-
- pub fn remove(&mut self, index: usize) -> T {
- match self {
- ThinVec(None) => panic!("invalid ThinVec::remove"),
- ThinVec(Some(vec)) => vec.remove(index),
- }
- }
-
- pub fn as_slice(&self) -> &[T] {
- match self {
- ThinVec(None) => &[],
- ThinVec(Some(vec)) => vec.as_slice(),
- }
- }
-}
-
-impl<T> From<Vec<T>> for ThinVec<T> {
- fn from(vec: Vec<T>) -> Self {
- if vec.is_empty() { ThinVec(None) } else { ThinVec(Some(Box::new(vec))) }
- }
-}
-
-impl<T> Into<Vec<T>> for ThinVec<T> {
- fn into(self) -> Vec<T> {
- match self {
- ThinVec(None) => Vec::new(),
- ThinVec(Some(vec)) => *vec,
- }
- }
-}
-
-impl<T> ::std::ops::Deref for ThinVec<T> {
- type Target = [T];
- fn deref(&self) -> &[T] {
- match *self {
- ThinVec(None) => &[],
- ThinVec(Some(ref vec)) => vec,
- }
- }
-}
-
-impl<T> ::std::ops::DerefMut for ThinVec<T> {
- fn deref_mut(&mut self) -> &mut [T] {
- match *self {
- ThinVec(None) => &mut [],
- ThinVec(Some(ref mut vec)) => vec,
- }
- }
-}
-
-impl<T> FromIterator<T> for ThinVec<T> {
- fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
- // `Vec::from_iter()` should not allocate if the iterator is empty.
- let vec: Vec<_> = iter.into_iter().collect();
- if vec.is_empty() { ThinVec(None) } else { ThinVec(Some(Box::new(vec))) }
- }
-}
-
-impl<T> IntoIterator for ThinVec<T> {
- type Item = T;
- type IntoIter = std::vec::IntoIter<T>;
-
- fn into_iter(self) -> Self::IntoIter {
- // This is still performant because `Vec::new()` does not allocate.
- self.0.map_or_else(Vec::new, |ptr| *ptr).into_iter()
- }
-}
-
-impl<'a, T> IntoIterator for &'a ThinVec<T> {
- type Item = &'a T;
- type IntoIter = std::slice::Iter<'a, T>;
-
- fn into_iter(self) -> Self::IntoIter {
- self.as_ref().iter()
- }
-}
-
-impl<'a, T> IntoIterator for &'a mut ThinVec<T> {
- type Item = &'a mut T;
- type IntoIter = std::slice::IterMut<'a, T>;
-
- fn into_iter(self) -> Self::IntoIter {
- self.as_mut().iter_mut()
- }
-}
-
-impl<T> Extend<T> for ThinVec<T> {
- fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
- match *self {
- ThinVec(Some(ref mut vec)) => vec.extend(iter),
- ThinVec(None) => *self = iter.into_iter().collect::<Vec<_>>().into(),
- }
- }
-
- fn extend_one(&mut self, item: T) {
- self.push(item)
- }
-
- fn extend_reserve(&mut self, additional: usize) {
- match *self {
- ThinVec(Some(ref mut vec)) => vec.reserve(additional),
- ThinVec(None) => *self = Vec::with_capacity(additional).into(),
- }
- }
-}
-
-impl<T: HashStable<CTX>, CTX> HashStable<CTX> for ThinVec<T> {
- fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
- (**self).hash_stable(hcx, hasher)
- }
-}
-
-impl<T> Default for ThinVec<T> {
- fn default() -> Self {
- Self(None)
- }
-}
-
-#[cfg(test)]
-mod tests;
+++ /dev/null
-use super::*;
-
-impl<T> ThinVec<T> {
- fn into_vec(self) -> Vec<T> {
- self.into()
- }
-}
-
-#[test]
-fn test_from_iterator() {
- assert_eq!(std::iter::empty().collect::<ThinVec<String>>().into_vec(), Vec::<String>::new());
- assert_eq!(std::iter::once(42).collect::<ThinVec<_>>().into_vec(), vec![42]);
- assert_eq!([1, 2].into_iter().collect::<ThinVec<_>>().into_vec(), vec![1, 2]);
- assert_eq!([1, 2, 3].into_iter().collect::<ThinVec<_>>().into_vec(), vec![1, 2, 3]);
-}
-
-#[test]
-fn test_into_iterator_owned() {
- assert_eq!(ThinVec::new().into_iter().collect::<Vec<String>>(), Vec::<String>::new());
- assert_eq!(ThinVec::from(vec![1]).into_iter().collect::<Vec<_>>(), vec![1]);
- assert_eq!(ThinVec::from(vec![1, 2]).into_iter().collect::<Vec<_>>(), vec![1, 2]);
- assert_eq!(ThinVec::from(vec![1, 2, 3]).into_iter().collect::<Vec<_>>(), vec![1, 2, 3]);
-}
-
-#[test]
-fn test_into_iterator_ref() {
- assert_eq!(ThinVec::new().iter().collect::<Vec<&String>>(), Vec::<&String>::new());
- assert_eq!(ThinVec::from(vec![1]).iter().collect::<Vec<_>>(), vec![&1]);
- assert_eq!(ThinVec::from(vec![1, 2]).iter().collect::<Vec<_>>(), vec![&1, &2]);
- assert_eq!(ThinVec::from(vec![1, 2, 3]).iter().collect::<Vec<_>>(), vec![&1, &2, &3]);
-}
-
-#[test]
-fn test_into_iterator_ref_mut() {
- assert_eq!(ThinVec::new().iter_mut().collect::<Vec<&mut String>>(), Vec::<&mut String>::new());
- assert_eq!(ThinVec::from(vec![1]).iter_mut().collect::<Vec<_>>(), vec![&mut 1]);
- assert_eq!(ThinVec::from(vec![1, 2]).iter_mut().collect::<Vec<_>>(), vec![&mut 1, &mut 2]);
- assert_eq!(
- ThinVec::from(vec![1, 2, 3]).iter_mut().collect::<Vec<_>>(),
- vec![&mut 1, &mut 2, &mut 3],
- );
-}
crate-type = ["dylib"]
[dependencies]
-tracing = { version = "0.1.28" }
+tracing = { version = "0.1.35" }
serde_json = "1.0.59"
rustc_log = { path = "../rustc_log" }
rustc_middle = { path = "../rustc_middle" }
//! The various pretty-printing routines.
+use crate::session_diagnostics::UnprettyDumpFail;
use rustc_ast as ast;
use rustc_ast_pretty::pprust;
use rustc_errors::ErrorGuaranteed;
(src, src_name)
}
-fn write_or_print(out: &str, ofile: Option<&Path>) {
+fn write_or_print(out: &str, ofile: Option<&Path>, sess: &Session) {
match ofile {
None => print!("{}", out),
Some(p) => {
if let Err(e) = std::fs::write(p, out) {
- panic!("print-print failed to write {} due to {}", p.display(), e);
+ sess.emit_fatal(UnprettyDumpFail {
+ path: p.display().to_string(),
+ err: e.to_string(),
+ });
}
}
}
_ => unreachable!(),
};
- write_or_print(&out, ofile);
+ write_or_print(&out, ofile, sess);
}
pub fn print_after_hir_lowering<'tcx>(
_ => unreachable!(),
};
- write_or_print(&out, ofile);
+ write_or_print(&out, ofile, tcx.sess);
}
// In an ideal world, this would be a public function called by the driver after
_ => unreachable!(),
};
- write_or_print(&out, ofile);
+ write_or_print(&out, ofile, tcx.sess);
Ok(())
}
#[derive(SessionDiagnostic)]
#[diag(driver::rlink_no_a_file)]
pub(crate) struct RlinkNotAFile;
+
+#[derive(SessionDiagnostic)]
+#[diag(driver::unpretty_dump_fail)]
+pub(crate) struct UnprettyDumpFail {
+ pub path: String,
+ pub err: String,
+}
ast_lowering_arbitrary_expression_in_pattern =
arbitrary expressions aren't allowed in patterns
+
+ast_lowering_inclusive_range_with_no_end = inclusive range with no end
.not_supported_or = `||` operators are not supported in let chain expressions
.not_supported_parentheses = `let`s wrapped in parentheses are not supported in a context with let chains
+ast_passes_forbidden_let_stable =
+ expected expression, found statement (`let`)
+ .note = variable declaration using `let` is a statement
+
ast_passes_deprecated_where_clause_location =
where clause not allowed here
driver_rlink_rustc_version_mismatch = .rlink file was produced by rustc version `{$rustc_version}`, but the current version is `{$current_version}`
driver_rlink_no_a_file = rlink must be a file
+
+driver_unpretty_dump_fail = pretty-print failed to write `{$path}` due to error `{$err}`
--- /dev/null
+monomorphize_recursion_limit =
+ reached the recursion limit while instantiating `{$shrunk}`
+ .note = `{$def_path_str}` defined here
+
+monomorphize_written_to_path = the full type name has been written to '{$path}'
+
+monomorphize_type_length_limit = reached the type-length limit while instantiating `{$shrunk}`
+
+monomorphize_consider_type_length_limit =
+ consider adding a `#![type_length_limit="{$type_length}"]` attribute to your crate
+
+monomorphize_fatal_error = {$error_message}
+
+monomorphize_unknown_partition_strategy = unknown partitioning strategy
+
+monomorphize_symbol_already_defined = symbol `{$symbol}` is already defined
+
+monomorphize_unused_generic_params = item has unused generic parameters
+
+monomorphize_large_assignments =
+ moving {$size} bytes
+ .label = value moved from here
+ .note = The current maximum size is {$limit}, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
+
+monomorphize_requires_lang_item =
+ requires `{$lang_item}` lang_item
.label = can't leak {$vis_descr} {$kind}
.visibility_label = `{$descr}` declared as {$vis_descr}
+privacy_report_access_level = {$descr}
+
privacy_from_private_dep_in_public_interface =
{$kind} `{$descr}` from private dependency '{$krate}' in public interface
--- /dev/null
+query_system_reentrant = internal compiler error: re-entrant incremental verify failure, suppressing message
+
+query_system_increment_compilation = internal compiler error: encountered incremental compilation error with {$dep_node}
+ .help = This is a known issue with the compiler. Run {$run_cmd} to allow your project to compile
+
+query_system_increment_compilation_note1 = Please follow the instructions below to create a bug report with the provided information
+query_system_increment_compilation_note2 = See <https://github.com/rust-lang/rust/issues/84970> for more information
+
+query_system_cycle = cycle detected when {$stack_bottom}
+
+query_system_cycle_usage = cycle used when {$usage}
+
+query_system_cycle_stack_single = ...which immediately requires {$stack_bottom} again
+
+query_system_cycle_stack_multiple = ...which again requires {$stack_bottom}, completing the cycle
+
+query_system_cycle_recursive_ty_alias = type aliases cannot be recursive
+query_system_cycle_recursive_ty_alias_help1 = consider using a struct, enum, or union instead to break the cycle
+query_system_cycle_recursive_ty_alias_help2 = see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information
+
+query_system_cycle_recursive_trait_alias = trait aliases cannot be recursive
+
+query_system_cycle_which_requires = ...which requires {$desc}...
+
+query_system_query_overflow = queries overflow the depth limit!
--- /dev/null
+session_incorrect_cgu_reuse_type =
+ CGU-reuse for `{$cgu_user_name}` is `{$actual_reuse}` but should be {$at_least ->
+ [one] {"at least "}
+ *[other] {""}
+ }`{$expected_reuse}`
+
+session_cgu_not_recorded =
+ CGU-reuse for `{$cgu_user_name}` is (mangled: `{$cgu_name}`) was not recorded`
+
+session_feature_gate_error = {$explain}
+
+session_feature_diagnostic_for_issue =
+ see issue #{$n} <https://github.com/rust-lang/rust/issues/{$n}> for more information
+
+session_feature_diagnostic_help =
+ add `#![feature({$feature})]` to the crate attributes to enable
--- /dev/null
+symbol_mangling_invalid_symbol_name = symbol-name({$mangled_formatted})
+
+symbol_mangling_invalid_trait_item = demangling({$demangling_formatted})
+
+symbol_mangling_alt_invalid_trait_item = demangling-alt({$alt_demangling_formatted})
+
+symbol_mangling_invalid_def_path = def-path({$def_path})
+#![feature(let_chains)]
#![feature(once_cell)]
#![feature(rustc_attrs)]
#![feature(type_alias_impl_trait)]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
+#[macro_use]
+extern crate tracing;
+
use fluent_bundle::FluentResource;
use fluent_syntax::parser::ParserError;
use rustc_data_structures::sync::Lrc;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
-use tracing::{instrument, trace};
#[cfg(not(parallel_compiler))]
use std::cell::LazyCell as Lazy;
const_eval => "../locales/en-US/const_eval.ftl",
driver => "../locales/en-US/driver.ftl",
expand => "../locales/en-US/expand.ftl",
+ session => "../locales/en-US/session.ftl",
interface => "../locales/en-US/interface.ftl",
infer => "../locales/en-US/infer.ftl",
lint => "../locales/en-US/lint.ftl",
+ monomorphize => "../locales/en-US/monomorphize.ftl",
parser => "../locales/en-US/parser.ftl",
passes => "../locales/en-US/passes.ftl",
plugin_impl => "../locales/en-US/plugin_impl.ftl",
privacy => "../locales/en-US/privacy.ftl",
+ query_system => "../locales/en-US/query_system.ftl",
save_analysis => "../locales/en-US/save_analysis.ftl",
ty_utils => "../locales/en-US/ty_utils.ftl",
typeck => "../locales/en-US/typeck.ftl",
mir_dataflow => "../locales/en-US/mir_dataflow.ftl",
+ symbol_mangling => "../locales/en-US/symbol_mangling.ftl",
}
pub use fluent_generated::{self as fluent, DEFAULT_LOCALE_RESOURCES};
suggestion: Vec<(Span, String)>,
applicability: Applicability,
) -> &mut Self {
- assert!(!suggestion.is_empty());
- self.push_suggestion(CodeSuggestion {
- substitutions: vec![Substitution {
- parts: suggestion
- .into_iter()
- .map(|(span, snippet)| SubstitutionPart { snippet, span })
- .collect(),
- }],
- msg: self.subdiagnostic_message_to_diagnostic_message(msg),
- style: SuggestionStyle::CompletelyHidden,
+ self.multipart_suggestion_with_style(
+ msg,
+ suggestion,
applicability,
- });
- self
+ SuggestionStyle::CompletelyHidden,
+ )
}
/// Prints out a message with a suggested edit of the code.
fn sub_with_highlights<M: Into<SubdiagnosticMessage>>(
&mut self,
level: Level,
- mut message: Vec<(M, Style)>,
+ message: Vec<(M, Style)>,
span: MultiSpan,
render_span: Option<MultiSpan>,
) {
let message = message
- .drain(..)
+ .into_iter()
.map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.0), m.1))
.collect();
let sub = SubDiagnostic { level, message, span, render_span };
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::thread::panicking;
-use tracing::debug;
/// Used for emitting structured error messages and other diagnostic information.
///
use std::path::Path;
use termcolor::{Ansi, BufferWriter, ColorChoice, ColorSpec, StandardStream};
use termcolor::{Buffer, Color, WriteColor};
-use tracing::*;
/// Default column width, used in tests and when terminal dimensions cannot be determined.
const DEFAULT_COLUMN_WIDTH: usize = 140;
#![feature(drain_filter)]
#![feature(if_let_guard)]
#![feature(adt_const_params)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(never_type)]
#![feature(result_option_inspect)]
}
fn treat_err_as_bug(&self) -> bool {
- self.flags
- .treat_err_as_bug
- .map_or(false, |c| self.err_count() + self.lint_err_count >= c.get())
+ self.flags.treat_err_as_bug.map_or(false, |c| {
+ self.err_count()
+ + self.lint_err_count
+ + self.delayed_span_bugs.len()
+ + self.delayed_good_path_bugs.len()
+ >= c.get()
+ })
}
fn print_error_count(&mut self, registry: &Registry) {
// This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before
// incrementing `err_count` by one, so we need to +1 the comparing.
// FIXME: Would be nice to increment err_count in a more coherent way.
- if self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() + 1 >= c.get()) {
+ if self.flags.treat_err_as_bug.map_or(false, |c| {
+ self.err_count()
+ + self.lint_err_count
+ + self.delayed_span_bugs.len()
+ + self.delayed_good_path_bugs.len()
+ + 1
+ >= c.get()
+ }) {
// FIXME: don't abort here if report_delayed_bugs is off
self.span_bug(sp, msg);
}
/// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then
/// passed around as a reference thereafter.
fn to_fluent_args<'arg>(&self, args: &[DiagnosticArg<'arg>]) -> FluentArgs<'arg> {
- FromIterator::from_iter(args.to_vec().drain(..))
+ FromIterator::from_iter(args.iter().cloned())
}
/// Convert `DiagnosticMessage`s to a string, performing translation if necessary.
#![feature(associated_type_bounds)]
#![feature(associated_type_defaults)]
#![feature(if_let_guard)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(macro_metavar_expr)]
#![feature(proc_macro_diagnostic)]
#[macro_use]
extern crate rustc_macros;
+#[macro_use]
+extern crate tracing;
+
extern crate proc_macro as pm;
mod placeholders;
}
}
MatcherLoc::Delimited => {
- // Entering the delimeter is trivial.
+ // Entering the delimiter is trivial.
mp.idx += 1;
self.cur_mps.push(mp);
}
use std::borrow::Cow;
use std::collections::hash_map::Entry;
use std::{mem, slice};
-use tracing::debug;
pub(crate) struct ParserAnyMacro<'a> {
parser: Parser<'a>,
self.maybe_empty = false;
}
- // Adds `tok` to the set for `self`, marking sequence as non-empy.
+ // Adds `tok` to the set for `self`, marking sequence as non-empty.
fn add_one(&mut self, tt: TtHandle<'tt>) {
if !self.tokens.contains(&tt) {
self.tokens.push(tt);
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
-use rustc_errors::{Diagnostic, MultiSpan, PResult};
+use rustc_errors::{MultiSpan, PResult};
use rustc_parse::lexer::nfc_normalize;
use rustc_parse::parse_stream_from_source_str;
use rustc_session::parse::ParseSess;
use rustc_span::{BytePos, FileName, Pos, SourceFile, Span};
use pm::bridge::{
- server, DelimSpan, ExpnGlobals, Group, Ident, LitKind, Literal, Punct, TokenTree,
+ server, DelimSpan, Diagnostic, ExpnGlobals, Group, Ident, LitKind, Literal, Punct, TokenTree,
};
use pm::{Delimiter, Level, LineColumn};
use std::ops::Bound;
type FreeFunctions = FreeFunctions;
type TokenStream = TokenStream;
type SourceFile = Lrc<SourceFile>;
- type MultiSpan = Vec<Span>;
- type Diagnostic = Diagnostic;
type Span = Span;
type Symbol = Symbol;
}
span: self.call_site,
})
}
+
+ fn emit_diagnostic(&mut self, diagnostic: Diagnostic<Self::Span>) {
+ let mut diag =
+ rustc_errors::Diagnostic::new(diagnostic.level.to_internal(), diagnostic.message);
+ diag.set_span(MultiSpan::from_spans(diagnostic.spans));
+ for child in diagnostic.children {
+ diag.sub(
+ child.level.to_internal(),
+ child.message,
+ MultiSpan::from_spans(child.spans),
+ None,
+ );
+ }
+ self.sess().span_diagnostic.emit_diagnostic(&mut diag);
+ }
}
impl server::TokenStream for Rustc<'_, '_> {
}
}
-impl server::MultiSpan for Rustc<'_, '_> {
- fn new(&mut self) -> Self::MultiSpan {
- vec![]
- }
-
- fn push(&mut self, spans: &mut Self::MultiSpan, span: Self::Span) {
- spans.push(span)
- }
-}
-
-impl server::Diagnostic for Rustc<'_, '_> {
- fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic {
- let mut diag = Diagnostic::new(level.to_internal(), msg);
- diag.set_span(MultiSpan::from_spans(spans));
- diag
- }
-
- fn sub(
- &mut self,
- diag: &mut Self::Diagnostic,
- level: Level,
- msg: &str,
- spans: Self::MultiSpan,
- ) {
- diag.sub(level.to_internal(), msg, MultiSpan::from_spans(spans), None);
- }
-
- fn emit(&mut self, mut diag: Self::Diagnostic) {
- self.sess().span_diagnostic.emit_diagnostic(&mut diag);
- }
-}
-
impl server::Span for Rustc<'_, '_> {
fn debug(&mut self, span: Self::Span) -> String {
if self.ecx.ecfg.span_debug {
(accepted, item_like_imports, "1.15.0", Some(35120), None),
/// Allows `'a: { break 'a; }`.
(accepted, label_break_value, "CURRENT_RUSTC_VERSION", Some(48594), None),
- /// Allows `if/while p && let q = r && ...` chains.
- (accepted, let_chains, "1.64.0", Some(53667), None),
/// Allows `break {expr}` with a value inside `loop`s.
(accepted, loop_break_value, "1.19.0", Some(37339), None),
/// Allows use of `?` as the Kleene "at most one" operator in macros.
(active, isa_attribute, "1.48.0", Some(74727), None),
// Allows setting the threshold for the `large_assignments` lint.
(active, large_assignments, "1.52.0", Some(83518), None),
+ /// Allows `if/while p && let q = r && ...` chains.
+ (active, let_chains, "1.37.0", Some(53667), None),
/// Allows `let...else` statements.
(active, let_else, "1.56.0", Some(87335), None),
/// Allows `#[link(..., cfg(..))]`.
(incomplete, raw_dylib, "1.40.0", Some(58713), None),
/// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions.
(active, raw_ref_op, "1.41.0", Some(64490), None),
- /// Allows using the `#[register_attr]` attribute.
- (active, register_attr, "1.41.0", Some(66080), None),
/// Allows using the `#[register_tool]` attribute.
(active, register_tool, "1.41.0", Some(66079), None),
/// Allows the `#[repr(i128)]` attribute for enums.
),
gated!(ffi_pure, Normal, template!(Word), WarnFollowing, experimental!(ffi_pure)),
gated!(ffi_const, Normal, template!(Word), WarnFollowing, experimental!(ffi_const)),
- gated!(
- register_attr, CrateLevel, template!(List: "attr1, attr2, ..."), DuplicatesOk,
- experimental!(register_attr),
- ),
gated!(
register_tool, CrateLevel, template!(List: "tool1, tool2, ..."), DuplicatesOk,
experimental!(register_tool),
// Internal attributes, Testing:
// ==========================================================================
+ rustc_attr!(TEST, rustc_access_level, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_outlives, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word), WarnFollowing),
(removed, quad_precision_float, "1.0.0", None, None, None),
(removed, quote, "1.33.0", Some(29601), None, None),
(removed, reflect, "1.0.0", Some(27749), None, None),
+ /// Allows using the `#[register_attr]` attribute.
+ (removed, register_attr, "CURRENT_RUSTC_VERSION", Some(66080), None,
+ Some("removed in favor of `#![register_tool]`")),
/// Allows using the macros:
/// + `__diagnostic_used`
/// + `__register_diagnostic`
/// Single-segment custom attribute registered by a derive macro
/// but used before that derive macro was expanded (deprecated).
DeriveHelperCompat,
- /// Single-segment custom attribute registered with `#[register_attr]`.
- Registered,
}
/// What kind of definition something is; e.g., `mod` vs `struct`.
NonMacroAttrKind::DeriveHelper | NonMacroAttrKind::DeriveHelperCompat => {
"derive helper attribute"
}
- NonMacroAttrKind::Registered => "explicitly registered attribute",
}
}
pub fn article(self) -> &'static str {
- match self {
- NonMacroAttrKind::Registered => "an",
- _ => "a",
- }
+ "a"
}
/// Users of some attributes cannot mark them as used, so they are considered always used.
NonMacroAttrKind::Tool
| NonMacroAttrKind::DeriveHelper
| NonMacroAttrKind::DeriveHelperCompat => true,
- NonMacroAttrKind::Builtin(..) | NonMacroAttrKind::Registered => false,
+ NonMacroAttrKind::Builtin(..) => false,
}
}
}
use std::fmt::{self, Write};
use std::hash::Hash;
-use tracing::debug;
/// The `DefPathTable` maps `DefIndex`es to `DefKey`s and vice versa.
/// Internally the `DefPathTable` holds a tree of `DefKey`s, where each `DefKey`
--- /dev/null
+use crate::LangItem;
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable)]
+pub struct LangItemError(pub LangItem);
+
+impl ToString for LangItemError {
+ fn to_string(&self) -> String {
+ format!("requires `{}` lang_item", self.0.name())
+ }
+}
//! * Functions called by the compiler itself.
use crate::def_id::DefId;
+use crate::errors::LangItemError;
use crate::{MethodKind, Target};
use rustc_ast as ast;
/// Requires that a given `LangItem` was bound and returns the corresponding `DefId`.
/// If it wasn't bound, e.g. due to a missing `#[lang = "<it.name()>"]`,
- /// returns an error message as a string.
- pub fn require(&self, it: LangItem) -> Result<DefId, String> {
- self.items[it as usize].ok_or_else(|| format!("requires `{}` lang_item", it.name()))
+ /// returns an error encapsulating the `LangItem`.
+ pub fn require(&self, it: LangItem) -> Result<DefId, LangItemError> {
+ self.items[it as usize].ok_or_else(|| LangItemError(it))
}
/// Returns the [`DefId`]s of all lang items in a group.
#[macro_use]
extern crate rustc_macros;
+#[macro_use]
+extern crate tracing;
+
#[macro_use]
extern crate rustc_data_structures;
pub mod def_path_hash_map;
pub mod definitions;
pub mod diagnostic_items;
+pub mod errors;
pub use rustc_span::def_id;
mod hir;
pub mod hir_id;
err_count_on_creation: self.err_count_on_creation,
in_snapshot: self.in_snapshot.clone(),
universe: self.universe.clone(),
+ normalize_fn_sig_for_diagnostic: self
+ .normalize_fn_sig_for_diagnostic
+ .as_ref()
+ .map(|f| f.clone()),
}
}
}
debug!(
"canonical: region var found with vid {:?}, \
opportunistically resolved to {:?}",
- vid, r
+ vid, resolved_vid
);
let r = self.tcx.reuse_or_mk_region(r, ty::ReVar(resolved_vid));
self.canonicalize_mode.canonicalize_free_region(self, r)
Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>,
{
let query_response = self.make_query_response(inference_vars, answer, fulfill_cx)?;
+ debug!("query_response = {:#?}", query_response);
let canonical_result = self.canonicalize_response(query_response);
-
debug!("canonical_result = {:#?}", canonical_result);
Ok(self.tcx.arena.alloc(canonical_result))
debug!("ambig_errors = {:#?}", ambig_errors);
let region_obligations = self.take_registered_region_obligations();
+ debug!(?region_obligations);
let region_constraints = self.with_region_constraints(|region_constraints| {
make_query_region_constraints(
tcx,
region_constraints,
)
});
+ debug!(?region_constraints);
let certainty =
if ambig_errors.is_empty() { Certainty::Proven } else { Certainty::Ambiguous };
assert!(verifys.is_empty());
assert!(givens.is_empty());
+ debug!(?constraints);
+
let outlives: Vec<_> = constraints
.iter()
.map(|(k, _)| match *k {
/// Preconditions:
///
/// - `for_vid` is a "root vid"
- #[instrument(skip(self), level = "trace")]
+ #[instrument(skip(self), level = "trace", ret)]
fn generalize(
&self,
ty: Ty<'tcx>,
cache: SsoHashMap::new(),
};
- let ty = match generalize.relate(ty, ty) {
- Ok(ty) => ty,
- Err(e) => {
- debug!(?e, "failure");
- return Err(e);
- }
- };
+ let ty = generalize.relate(ty, ty)?;
let needs_wf = generalize.needs_wf;
- trace!(?ty, ?needs_wf, "success");
Ok(Generalization { ty, needs_wf })
}
/// Result from a generalization operation. This includes
/// not only the generalized type, but also a bool flag
/// indicating whether further WF checks are needed.
+#[derive(Debug)]
struct Generalization<'tcx> {
ty: Ty<'tcx>,
Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?))
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self), ret)]
fn tys(&mut self, t: Ty<'tcx>, _t: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
debug_assert_eq!(t, _t);
- debug!("ConstInferUnifier: t={:?}", t);
match t.kind() {
&ty::Infer(ty::TyVar(vid)) => {
.borrow_mut()
.type_variables()
.new_var(self.for_universe, origin);
- let u = self.tcx().mk_ty_var(new_var_id);
- debug!(
- "ConstInferUnifier: replacing original vid={:?} with new={:?}",
- vid, u
- );
- Ok(u)
+ Ok(self.tcx().mk_ty_var(new_var_id))
}
}
}
}
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn consts(
&mut self,
c: ty::Const<'tcx>,
_c: ty::Const<'tcx>,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
debug_assert_eq!(c, _c);
- debug!("ConstInferUnifier: c={:?}", c);
match c.kind() {
ty::ConstKind::Infer(InferConst::Var(vid)) => {
}
}
+ fn normalize_fn_sig_for_diagnostic(&self, sig: ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx> {
+ if let Some(normalize) = &self.normalize_fn_sig_for_diagnostic {
+ normalize(self, sig)
+ } else {
+ sig
+ }
+ }
+
/// Given two `fn` signatures highlight only sub-parts that are different.
fn cmp_fn_sig(
&self,
sig1: &ty::PolyFnSig<'tcx>,
sig2: &ty::PolyFnSig<'tcx>,
) -> (DiagnosticStyledString, DiagnosticStyledString) {
+ let sig1 = &self.normalize_fn_sig_for_diagnostic(*sig1);
+ let sig2 = &self.normalize_fn_sig_for_diagnostic(*sig2);
+
let get_lifetimes = |sig| {
use rustc_hir::def::Namespace;
let (_, sig, reg) = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS)
/// the message in `secondary_span` as the primary label, and apply the message that would
/// otherwise be used for the primary label on the `secondary_span` `Span`. This applies on
/// E0271, like `src/test/ui/issues/issue-39970.stderr`.
- #[tracing::instrument(
+ #[instrument(
level = "debug",
skip(self, diag, secondary_span, swap_secondary_and_primary, prefer_label)
)]
(exp_found.expected.kind(), exp_found.found.kind())
{
if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
- let path_str = format!("{:?}", exp_def);
if exp_def == &found_def {
- let opt_msg = "you can convert from `&Option<T>` to `Option<&T>` using \
- `.as_ref()`";
- let result_msg = "you can convert from `&Result<T, E>` to \
- `Result<&T, &E>` using `.as_ref()`";
let have_as_ref = &[
- ("std::option::Option", opt_msg),
- ("core::option::Option", opt_msg),
- ("std::result::Result", result_msg),
- ("core::result::Result", result_msg),
+ (
+ sym::Option,
+ "you can convert from `&Option<T>` to `Option<&T>` using \
+ `.as_ref()`",
+ ),
+ (
+ sym::Result,
+ "you can convert from `&Result<T, E>` to \
+ `Result<&T, &E>` using `.as_ref()`",
+ ),
];
- if let Some(msg) = have_as_ref
- .iter()
- .find_map(|(path, msg)| (&path_str == path).then_some(msg))
- {
+ if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
+ self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
+ }) {
let mut show_suggestion = true;
for (exp_ty, found_ty) in
iter::zip(exp_substs.types(), found_substs.types())
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
- if (a.is_var() && b.is_free_or_static()) || (b.is_var() && a.is_free_or_static()) || a == b
+ if (a.is_var() && b.is_free_or_static())
+ || (b.is_var() && a.is_free_or_static())
+ || (a.is_var() && b.is_var())
+ || a == b
{
Ok(a)
} else {
}
/// We don't want to directly use `ty_to_string` for closures as their type isn't really
-/// something users are familar with. Directly printing the `fn_sig` of closures also
+/// something users are familiar with. Directly printing the `fn_sig` of closures also
/// doesn't work as they actually use the "rust-call" API.
fn closure_as_fn_str<'tcx>(infcx: &InferCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> String {
let ty::Closure(_, substs) = ty.kind() else { unreachable!() };
// Find the index of the named region that was part of the
// error. We will then search the function parameters for a bound
// region at the right depth with the same index
- (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => {
+ (Some(rl::Region::EarlyBound(id)), ty::BrNamed(def_id, _)) => {
debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
if id == def_id {
self.found_type = Some(arg);
Some(
rl::Region::Static
| rl::Region::Free(_, _)
- | rl::Region::EarlyBound(_, _)
+ | rl::Region::EarlyBound(_)
| rl::Region::LateBound(_, _, _),
)
| None,
fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) {
match (self.tcx.named_region(lifetime.hir_id), self.bound_region) {
// the lifetime of the TyPath!
- (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => {
+ (Some(rl::Region::EarlyBound(id)), ty::BrNamed(def_id, _)) => {
debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
if id == def_id {
self.found_it = true;
(
Some(
rl::Region::Static
- | rl::Region::EarlyBound(_, _)
+ | rl::Region::EarlyBound(_)
| rl::Region::LateBound(_, _, _)
| rl::Region::Free(_, _),
)
/// For more details visit the relevant sections of the [rustc dev guide].
///
/// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html
- #[instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self), ret)]
pub fn replace_bound_vars_with_placeholders<T>(&self, binder: ty::Binder<'tcx, T>) -> T
where
T: TypeFoldable<'tcx> + Copy,
},
};
- let result = self.tcx.replace_bound_vars_uncached(binder, delegate);
- debug!(?next_universe, ?result);
- result
+ debug!(?next_universe);
+ self.tcx.replace_bound_vars_uncached(binder, delegate)
}
/// See [RegionConstraintCollector::leak_check][1].
///
/// Neither `a` nor `b` may be an inference variable (hence the
/// term "concrete regions").
- #[instrument(level = "trace", skip(self))]
+ #[instrument(level = "trace", skip(self), ret)]
fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> {
- let r = match (*a, *b) {
+ match (*a, *b) {
(ReLateBound(..), _) | (_, ReLateBound(..)) | (ReErased, _) | (_, ReErased) => {
bug!("cannot relate region: LUB({:?}, {:?})", a, b);
}
self.tcx().lifetimes.re_static
}
}
- };
-
- debug!("lub_concrete_regions({:?}, {:?}) = {:?}", a, b, r);
-
- r
+ }
}
/// After expansion is complete, go and check upper bounds (i.e.,
/// when we enter into a higher-ranked (`for<..>`) type or trait
/// bound.
universe: Cell<ty::UniverseIndex>,
+
+ normalize_fn_sig_for_diagnostic:
+ Option<Lrc<dyn Fn(&InferCtxt<'_, 'tcx>, ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx>>>,
}
/// See the `error_reporting` module for more details.
}
/// See the `region_obligations` field for more information.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub struct RegionObligation<'tcx> {
pub sub_region: ty::Region<'tcx>,
pub sup_type: Ty<'tcx>,
defining_use_anchor: DefiningAnchor,
considering_regions: bool,
fresh_typeck_results: Option<RefCell<ty::TypeckResults<'tcx>>>,
+ normalize_fn_sig_for_diagnostic:
+ Option<Lrc<dyn Fn(&InferCtxt<'_, 'tcx>, ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx>>>,
}
pub trait TyCtxtInferExt<'tcx> {
defining_use_anchor: DefiningAnchor::Error,
considering_regions: true,
fresh_typeck_results: None,
+ normalize_fn_sig_for_diagnostic: None,
}
}
}
self
}
+ pub fn with_normalize_fn_sig_for_diagnostic(
+ mut self,
+ fun: Lrc<dyn Fn(&InferCtxt<'_, 'tcx>, ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx>>,
+ ) -> Self {
+ self.normalize_fn_sig_for_diagnostic = Some(fun);
+ self
+ }
+
/// Given a canonical value `C` as a starting point, create an
/// inference context that contains each of the bound values
/// within instantiated as a fresh variable. The `f` closure is
defining_use_anchor,
considering_regions,
ref fresh_typeck_results,
+ ref normalize_fn_sig_for_diagnostic,
} = *self;
let in_progress_typeck_results = fresh_typeck_results.as_ref();
f(InferCtxt {
in_snapshot: Cell::new(false),
skip_leak_check: Cell::new(false),
universe: Cell::new(ty::UniverseIndex::ROOT),
+ normalize_fn_sig_for_diagnostic: normalize_fn_sig_for_diagnostic
+ .as_ref()
+ .map(|f| f.clone()),
})
}
}
/// `resolve_vars_if_possible` as well as `fully_resolve`.
///
/// Make sure to call [`InferCtxt::process_registered_region_obligations`]
- /// first, or preferrably use [`InferCtxt::check_region_obligations_and_report_errors`]
+ /// first, or preferably use [`InferCtxt::check_region_obligations_and_report_errors`]
/// to do both of these operations together.
pub fn resolve_regions_and_report_errors(
&self,
}
}
-impl<'tcx> fmt::Debug for RegionObligation<'tcx> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(
- f,
- "RegionObligation(sub_region={:?}, sup_type={:?})",
- self.sub_region, self.sup_type
- )
- }
-}
-
/// Replaces substs that reference param or infer variables with suitable
/// placeholders. This function is meant to remove these param and infer
/// substs when they're not actually needed to evaluate a constant.
generalizer.relate(value, value)
}
+
+ fn relate_opaques(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+ let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) };
+ let mut generalize = |ty, ty_is_expected| {
+ let var = self.infcx.next_ty_var_id_in_universe(
+ TypeVariableOrigin {
+ kind: TypeVariableOriginKind::MiscVariable,
+ span: self.delegate.span(),
+ },
+ ty::UniverseIndex::ROOT,
+ );
+ if ty_is_expected {
+ self.relate_ty_var((ty, var))
+ } else {
+ self.relate_ty_var((var, ty))
+ }
+ };
+ let (a, b) = match (a.kind(), b.kind()) {
+ (&ty::Opaque(..), _) => (a, generalize(b, false)?),
+ (_, &ty::Opaque(..)) => (generalize(a, true)?, b),
+ _ => unreachable!(),
+ };
+ self.delegate.register_opaque_type(a, b, true)?;
+ trace!(a = ?a.kind(), b = ?b.kind(), "opaque type instantiated");
+ Ok(a)
+ }
}
/// When we instantiate an inference variable with a value in
true
}
- #[instrument(skip(self, info), level = "trace")]
+ #[instrument(skip(self, info), level = "trace", ret)]
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
variance: ty::Variance,
self.ambient_variance = old_ambient_variance;
- debug!(?r);
-
Ok(r)
}
(&ty::Infer(ty::TyVar(vid)), _) => self.relate_ty_var((vid, b)),
(&ty::Opaque(a_def_id, _), &ty::Opaque(b_def_id, _)) if a_def_id == b_def_id => {
- self.infcx.super_combine_tys(self, a, b)
+ infcx.commit_if_ok(|_| infcx.super_combine_tys(self, a, b)).or_else(|err| {
+ self.tcx().sess.delay_span_bug(
+ self.delegate.span(),
+ "failure to relate an opaque to itself should result in an error later on",
+ );
+ if a_def_id.is_local() { self.relate_opaques(a, b) } else { Err(err) }
+ })
}
(&ty::Opaque(did, ..), _) | (_, &ty::Opaque(did, ..)) if did.is_local() => {
- let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) };
- let mut generalize = |ty, ty_is_expected| {
- let var = infcx.next_ty_var_id_in_universe(
- TypeVariableOrigin {
- kind: TypeVariableOriginKind::MiscVariable,
- span: self.delegate.span(),
- },
- ty::UniverseIndex::ROOT,
- );
- if ty_is_expected {
- self.relate_ty_var((ty, var))
- } else {
- self.relate_ty_var((var, ty))
- }
- };
- let (a, b) = match (a.kind(), b.kind()) {
- (&ty::Opaque(..), _) => (a, generalize(b, false)?),
- (_, &ty::Opaque(..)) => (generalize(a, true)?, b),
- _ => unreachable!(),
- };
- self.delegate.register_opaque_type(a, b, true)?;
- trace!(a = ?a.kind(), b = ?b.kind(), "opaque type instantiated");
- Ok(a)
+ self.relate_opaques(a, b)
}
(&ty::Projection(projection_ty), _)
});
}
- #[instrument(skip(self), level = "trace")]
+ #[instrument(skip(self), level = "trace", ret)]
pub fn opaque_type_origin(&self, def_id: LocalDefId, span: Span) -> Option<OpaqueTyOrigin> {
let opaque_hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
let parent_def_id = match self.defining_use_anchor {
in_definition_scope.then_some(*origin)
}
- #[instrument(skip(self), level = "trace")]
+ #[instrument(skip(self), level = "trace", ret)]
fn opaque_ty_origin_unchecked(&self, def_id: LocalDefId, span: Span) -> OpaqueTyOrigin {
- let origin = match self.tcx.hir().expect_item(def_id).kind {
+ match self.tcx.hir().expect_item(def_id).kind {
hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => origin,
ref itemkind => {
span_bug!(span, "weird opaque type: {:?}, {:#?}", def_id, itemkind)
}
- };
- trace!(?origin);
- origin
+ }
}
}
}
}
- #[instrument(level = "debug")]
+ #[instrument(level = "debug", ret)]
pub fn take_opaque_types(&mut self) -> OpaqueTypeMap<'tcx> {
std::mem::take(&mut self.opaque_types)
}
use rustc_middle::traits::query::OutlivesBound;
use rustc_middle::ty;
-#[instrument(level = "debug", skip(param_env))]
+#[instrument(level = "debug", skip(param_env), ret)]
pub fn explicit_outlives_bounds<'tcx>(
param_env: ty::ParamEnv<'tcx>,
) -> impl Iterator<Item = OutlivesBound<'tcx>> + 'tcx {
sub_region: Region<'tcx>,
cause: &ObligationCause<'tcx>,
) {
+ debug!(?sup_type, ?sub_region, ?cause);
let origin = SubregionOrigin::from_obligation_cause(cause, || {
infer::RelateParamBound(
cause.span,
/// - `origin`, the reason we need this constraint
/// - `ty`, the type `T`
/// - `region`, the region `'a`
+ #[instrument(level = "debug", skip(self))]
pub fn type_must_outlive(
&mut self,
origin: infer::SubregionOrigin<'tcx>,
ty: Ty<'tcx>,
region: ty::Region<'tcx>,
) {
- debug!("type_must_outlive(ty={:?}, region={:?}, origin={:?})", ty, region, origin);
-
assert!(!ty.has_escaping_bound_vars());
let mut components = smallvec![];
self.delegate.push_verify(origin, generic, region, verify_bound);
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn projection_must_outlive(
&mut self,
origin: infer::SubregionOrigin<'tcx>,
/// like are used. This is a particular challenge since this function is invoked
/// very late in inference and hence cannot make use of the normal inference
/// machinery.
-#[tracing::instrument(level = "debug", skip(tcx, param_env))]
+#[instrument(level = "debug", skip(tcx, param_env))]
pub fn extract_verify_if_eq<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
}
/// True if a (potentially higher-ranked) outlives
-#[tracing::instrument(level = "debug", skip(tcx, param_env))]
+#[instrument(level = "debug", skip(tcx, param_env))]
pub(super) fn can_match_erased_ty<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
/// Binds the pattern variable `br` to `value`; returns an `Err` if the pattern
/// is already bound to a different value.
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn bind(
&mut self,
br: ty::BoundRegion,
use crate::infer::combine::ConstEquateRelation;
use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind};
use crate::traits::Obligation;
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation};
use rustc_middle::ty::visit::TypeVisitable;
use rustc_middle::ty::TyVar;
Ok(infcx.tcx.mk_ty_var(var))
};
let (a, b) = if self.a_is_expected { (a, b) } else { (b, a) };
- let (a, b) = match (a.kind(), b.kind()) {
+ let (ga, gb) = match (a.kind(), b.kind()) {
(&ty::Opaque(..), _) => (a, generalize(b, true)?),
(_, &ty::Opaque(..)) => (generalize(a, false)?, b),
_ => unreachable!(),
};
self.fields.obligations.extend(
infcx
- .handle_opaque_type(a, b, true, &self.fields.trace.cause, self.param_env())?
+ .handle_opaque_type(ga, gb, true, &self.fields.trace.cause, self.param_env())
+ // Don't leak any generalized type variables out of this
+ // subtyping relation in the case of a type error.
+ .map_err(|err| {
+ let (ga, gb) = self.fields.infcx.resolve_vars_if_possible((ga, gb));
+ if let TypeError::Sorts(sorts) = err && sorts.expected == ga && sorts.found == gb {
+ TypeError::Sorts(ExpectedFound { expected: a, found: b })
+ } else {
+ err
+ }
+ })?
.obligations,
);
- Ok(a)
+ Ok(ga)
}
_ => {
}
/// The UndoLogs trait defines how we undo a particular kind of action (of type T). We can undo any
-/// action that is convertable into an UndoLog (per the From impls above).
+/// action that is convertible into an UndoLog (per the From impls above).
impl<'tcx, T> UndoLogs<T> for InferCtxtUndoLogs<'tcx>
where
UndoLog<'tcx>: From<T>,
#![feature(control_flow_enum)]
#![feature(extend_one)]
#![cfg_attr(bootstrap, feature(label_break_value))]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(min_specialization)]
#![feature(never_type)]
let ident = arg.ident().expect("multi-segment cfg key");
names_valid.insert(ident.name.to_string());
} else {
- error!("`names()` arguments must be simple identifers");
+ error!("`names()` arguments must be simple identifiers");
}
}
continue 'specs;
continue 'specs;
} else {
error!(
- "`values()` first argument must be a simple identifer"
+ "`values()` first argument must be a simple identifier"
);
}
} else if args.is_empty() {
// JUSTIFICATION: before session exists, only config
#[allow(rustc::bad_opt_access)]
pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R {
- tracing::trace!("run_compiler");
+ trace!("run_compiler");
util::run_in_thread_pool_with_globals(
config.opts.edition,
config.opts.unstable_opts.threads,
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
+#[macro_use]
+extern crate tracing;
+
mod callbacks;
mod errors;
pub mod interface;
use rustc_span::FileName;
use rustc_trait_selection::traits;
use rustc_typeck as typeck;
-use tracing::{info, warn};
use std::any::Any;
use std::cell::RefCell;
krate: &ast::Crate,
crate_name: &str,
) -> BoxedResolver {
- tracing::trace!("create_resolver");
+ trace!("create_resolver");
BoxedResolver::new(sess, move |sess, resolver_arenas| {
Resolver::new(sess, krate, crate_name, metadata_loader, resolver_arenas)
})
crate_name: &str,
resolver: &mut Resolver<'_>,
) -> Result<ast::Crate> {
- tracing::trace!("configure_and_expand");
+ trace!("configure_and_expand");
pre_expansion_lint(sess, lint_store, resolver.registered_tools(), &krate, crate_name);
rustc_builtin_macros::register_builtin_macros(resolver);
pub fn expansion(
&self,
) -> Result<&Query<(Lrc<ast::Crate>, Rc<RefCell<BoxedResolver>>, Lrc<LintStore>)>> {
- tracing::trace!("expansion");
+ trace!("expansion");
self.expansion.compute(|| {
let crate_name = self.crate_name()?.peek().clone();
let (krate, lint_store) = self.register_plugins()?.take();
use rustc_span::edition::{Edition, DEFAULT_EDITION};
use rustc_span::symbol::sym;
use rustc_span::SourceFileHashAlgorithm;
-use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy};
-use rustc_target::spec::{
- RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel,
-};
+use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, RelocModel};
+use rustc_target::spec::{RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel};
use std::collections::{BTreeMap, BTreeSet};
use std::iter::FromIterator;
untracked!(link_args, vec![String::from("abc"), String::from("def")]);
untracked!(link_self_contained, Some(true));
untracked!(linker, Some(PathBuf::from("linker")));
- untracked!(linker_flavor, Some(LinkerFlavor::Gcc));
+ untracked!(linker_flavor, Some(LinkerFlavorCli::Gcc));
untracked!(no_stack_check, true);
untracked!(remark, Passes::Some(vec![String::from("pass1"), String::from("pass2")]));
untracked!(rpath, true);
+use info;
use libloading::Library;
use rustc_ast as ast;
use rustc_codegen_ssa::traits::CodegenBackend;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::OnceLock;
use std::thread;
-use tracing::info;
/// Function pointer type that constructs a new CodegenBackend.
pub type MakeBackendFn = fn() -> Box<dyn CodegenBackend>;
use crate::nonstandard_style::{method_context, MethodLateContext};
use std::fmt::Write;
-use tracing::{debug, trace};
// hardwired lints from librustc_middle
pub use rustc_session::lint::builtin::*;
impl ExplicitOutlivesRequirements {
fn lifetimes_outliving_lifetime<'tcx>(
inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)],
- index: u32,
+ def_id: DefId,
) -> Vec<ty::Region<'tcx>> {
inferred_outlives
.iter()
.filter_map(|(pred, _)| match pred.kind().skip_binder() {
ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match *a {
- ty::ReEarlyBound(ebr) if ebr.index == index => Some(b),
+ ty::ReEarlyBound(ebr) if ebr.def_id == def_id => Some(b),
_ => None,
},
_ => None,
.filter_map(|(i, bound)| {
if let hir::GenericBound::Outlives(lifetime) = bound {
let is_inferred = match tcx.named_region(lifetime.hir_id) {
- Some(Region::EarlyBound(index, ..)) => inferred_outlives.iter().any(|r| {
- if let ty::ReEarlyBound(ebr) = **r { ebr.index == index } else { false }
+ Some(Region::EarlyBound(def_id)) => inferred_outlives.iter().any(|r| {
+ if let ty::ReEarlyBound(ebr) = **r {
+ ebr.def_id == def_id
+ } else {
+ false
+ }
}),
_ => false,
};
for (i, where_predicate) in hir_generics.predicates.iter().enumerate() {
let (relevant_lifetimes, bounds, span, in_where_clause) = match where_predicate {
hir::WherePredicate::RegionPredicate(predicate) => {
- if let Some(Region::EarlyBound(index, ..)) =
+ if let Some(Region::EarlyBound(region_def_id)) =
cx.tcx.named_region(predicate.lifetime.hir_id)
{
(
- Self::lifetimes_outliving_lifetime(inferred_outlives, index),
+ Self::lifetimes_outliving_lifetime(
+ inferred_outlives,
+ region_def_id,
+ ),
&predicate.bounds,
predicate.span,
predicate.in_where_clause,
Char if init == InitKind::Uninit => {
Some(("characters must be a valid Unicode codepoint".to_string(), None))
}
+ Int(_) | Uint(_) if init == InitKind::Uninit => {
+ Some(("integers must not be uninitialized".to_string(), None))
+ }
+ Float(_) if init == InitKind::Uninit => {
+ Some(("floats must not be uninitialized".to_string(), None))
+ }
+ RawPtr(_) if init == InitKind::Uninit => {
+ Some(("raw pointers must not be uninitialized".to_string(), None))
+ }
// Recurse and checks for some compound types.
Adt(adt_def, substs) if !adt_def.is_union() => {
// First check if this ADT has a layout attribute (like `NonNull` and friends).
}
}
}
+
+declare_lint! {
+ /// The `special_module_name` lint detects module
+ /// declarations for files that have a special meaning.
+ ///
+ /// ### Example
+ ///
+ /// ```rust,compile_fail
+ /// mod lib;
+ ///
+ /// fn main() {
+ /// lib::run();
+ /// }
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Cargo recognizes `lib.rs` and `main.rs` as the root of a
+ /// library or binary crate, so declaring them as modules
+ /// will lead to miscompilation of the crate unless configured
+ /// explicitly.
+ ///
+ /// To access a library from a binary target within the same crate,
+ /// use `your_crate_name::` as the path path instead of `lib::`:
+ ///
+ /// ```rust,compile_fail
+ /// // bar/src/lib.rs
+ /// fn run() {
+ /// // ...
+ /// }
+ ///
+ /// // bar/src/main.rs
+ /// fn main() {
+ /// bar::run();
+ /// }
+ /// ```
+ ///
+ /// Binary targets cannot be used as libraries and so declaring
+ /// one as a module is not allowed.
+ pub SPECIAL_MODULE_NAME,
+ Warn,
+ "module declarations for files with a special meaning",
+}
+
+declare_lint_pass!(SpecialModuleName => [SPECIAL_MODULE_NAME]);
+
+impl EarlyLintPass for SpecialModuleName {
+ fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &ast::Crate) {
+ for item in &krate.items {
+ if let ast::ItemKind::Mod(
+ _,
+ ast::ModKind::Unloaded | ast::ModKind::Loaded(_, ast::Inline::No, _),
+ ) = item.kind
+ {
+ if item.attrs.iter().any(|a| a.has_name(sym::path)) {
+ continue;
+ }
+
+ match item.ident.name.as_str() {
+ "lib" => cx.struct_span_lint(SPECIAL_MODULE_NAME, item.span, |lint| {
+ lint.build("found module declaration for lib.rs")
+ .note("lib.rs is the root of this crate's library target")
+ .help("to refer to it from other targets, use the library's name as the path")
+ .emit()
+ }),
+ "main" => cx.struct_span_lint(SPECIAL_MODULE_NAME, item.span, |lint| {
+ lint.build("found module declaration for main.rs")
+ .note("a binary crate cannot be used as library")
+ .emit()
+ }),
+ _ => continue
+ }
+ }
+ }
+ }
+}
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{BytePos, Span};
use rustc_target::abi;
-use tracing::debug;
use std::cell::Cell;
use std::iter;
None => {
// 1. The tool is currently running, so this lint really doesn't exist.
// FIXME: should this handle tools that never register a lint, like rustfmt?
- tracing::debug!("lints={:?}", self.by_name.keys().collect::<Vec<_>>());
+ debug!("lints={:?}", self.by_name.keys().collect::<Vec<_>>());
let tool_prefix = format!("{}::", tool_name);
return if self.by_name.keys().any(|lint| lint.starts_with(&tool_prefix)) {
self.no_lint_suggestion(&complete_name)
CheckLintNameResult::Tool(Err((Some(slice::from_ref(id)), complete_name)))
}
Some(other) => {
- tracing::debug!("got renamed lint {:?}", other);
+ debug!("got renamed lint {:?}", other);
CheckLintNameResult::NoLint(None)
}
}
use rustc_span::Span;
use std::slice;
-use tracing::debug;
macro_rules! run_early_pass { ($cx:expr, $f:ident, $($args:expr),*) => ({
$cx.pass.$f(&$cx.context, $($args),*);
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::Span;
-use tracing::debug;
declare_tool_lint! {
pub rustc::DEFAULT_HASH_TYPES,
return;
}
+ let mut found_parent_with_attr = false;
let mut found_impl = false;
- for (_, parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
+ for (hir_id, parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
+ if let Some(owner_did) = hir_id.as_owner() {
+ found_parent_with_attr = found_parent_with_attr
+ || cx.tcx.has_attr(owner_did.to_def_id(), sym::rustc_lint_diagnostics);
+ }
+
debug!(?parent);
if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent &&
let Impl { of_trait: Some(of_trait), .. } = impl_ &&
}
}
debug!(?found_impl);
- if !found_impl {
+ if !found_parent_with_attr && !found_impl {
cx.struct_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, |lint| {
lint.build(fluent::lint::diag_out_of_impl).emit();
})
}
}
debug!(?found_diagnostic_message);
- if !found_diagnostic_message {
+ if !found_parent_with_attr && !found_diagnostic_message {
cx.struct_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, |lint| {
lint.build(fluent::lint::untranslatable_diag).emit();
})
use std::any::Any;
use std::cell::Cell;
use std::slice;
-use tracing::debug;
/// Extract the `LintStore` from the query context.
/// This function exists because we've erased `LintStore` as `dyn Any` in the context.
--- /dev/null
+use crate::{LateContext, LateLintPass, LintContext};
+use rustc_errors::{Applicability, LintDiagnosticBuilder, MultiSpan};
+use rustc_hir as hir;
+use rustc_middle::ty;
+use rustc_span::Symbol;
+
+declare_lint! {
+ /// The `let_underscore_drop` lint checks for statements which don't bind
+ /// an expression which has a non-trivial Drop implementation to anything,
+ /// causing the expression to be dropped immediately instead of at end of
+ /// scope.
+ ///
+ /// ### Example
+ /// ```
+ /// struct SomeStruct;
+ /// impl Drop for SomeStruct {
+ /// fn drop(&mut self) {
+ /// println!("Dropping SomeStruct");
+ /// }
+ /// }
+ ///
+ /// fn main() {
+ /// #[warn(let_underscore_drop)]
+ /// // SomeStuct is dropped immediately instead of at end of scope,
+ /// // so "Dropping SomeStruct" is printed before "end of main".
+ /// // The order of prints would be reversed if SomeStruct was bound to
+ /// // a name (such as "_foo").
+ /// let _ = SomeStruct;
+ /// println!("end of main");
+ /// }
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Statements which assign an expression to an underscore causes the
+ /// expression to immediately drop instead of extending the expression's
+ /// lifetime to the end of the scope. This is usually unintended,
+ /// especially for types like `MutexGuard`, which are typically used to
+ /// lock a mutex for the duration of an entire scope.
+ ///
+ /// If you want to extend the expression's lifetime to the end of the scope,
+ /// assign an underscore-prefixed name (such as `_foo`) to the expression.
+ /// If you do actually want to drop the expression immediately, then
+ /// calling `std::mem::drop` on the expression is clearer and helps convey
+ /// intent.
+ pub LET_UNDERSCORE_DROP,
+ Allow,
+ "non-binding let on a type that implements `Drop`"
+}
+
+declare_lint! {
+ /// The `let_underscore_lock` lint checks for statements which don't bind
+ /// a mutex to anything, causing the lock to be released immediately instead
+ /// of at end of scope, which is typically incorrect.
+ ///
+ /// ### Example
+ /// ```compile_fail
+ /// use std::sync::{Arc, Mutex};
+ /// use std::thread;
+ /// let data = Arc::new(Mutex::new(0));
+ ///
+ /// thread::spawn(move || {
+ /// // The lock is immediately released instead of at the end of the
+ /// // scope, which is probably not intended.
+ /// let _ = data.lock().unwrap();
+ /// println!("doing some work");
+ /// let mut lock = data.lock().unwrap();
+ /// *lock += 1;
+ /// });
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Statements which assign an expression to an underscore causes the
+ /// expression to immediately drop instead of extending the expression's
+ /// lifetime to the end of the scope. This is usually unintended,
+ /// especially for types like `MutexGuard`, which are typically used to
+ /// lock a mutex for the duration of an entire scope.
+ ///
+ /// If you want to extend the expression's lifetime to the end of the scope,
+ /// assign an underscore-prefixed name (such as `_foo`) to the expression.
+ /// If you do actually want to drop the expression immediately, then
+ /// calling `std::mem::drop` on the expression is clearer and helps convey
+ /// intent.
+ pub LET_UNDERSCORE_LOCK,
+ Deny,
+ "non-binding let on a synchronization lock"
+}
+
+declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_DROP, LET_UNDERSCORE_LOCK]);
+
+const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [
+ rustc_span::sym::MutexGuard,
+ rustc_span::sym::RwLockReadGuard,
+ rustc_span::sym::RwLockWriteGuard,
+];
+
+impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
+ fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) {
+ if !matches!(local.pat.kind, hir::PatKind::Wild) {
+ return;
+ }
+ if let Some(init) = local.init {
+ let init_ty = cx.typeck_results().expr_ty(init);
+ // If the type has a trivial Drop implementation, then it doesn't
+ // matter that we drop the value immediately.
+ if !init_ty.needs_drop(cx.tcx, cx.param_env) {
+ return;
+ }
+ let is_sync_lock = match init_ty.kind() {
+ ty::Adt(adt, _) => SYNC_GUARD_SYMBOLS
+ .iter()
+ .any(|guard_symbol| cx.tcx.is_diagnostic_item(*guard_symbol, adt.did())),
+ _ => false,
+ };
+
+ if is_sync_lock {
+ let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]);
+ span.push_span_label(
+ local.pat.span,
+ "this lock is not assigned to a binding and is immediately dropped".to_string(),
+ );
+ span.push_span_label(
+ init.span,
+ "this binding will immediately drop the value assigned to it".to_string(),
+ );
+ cx.struct_span_lint(LET_UNDERSCORE_LOCK, span, |lint| {
+ build_and_emit_lint(
+ lint,
+ local,
+ init.span,
+ "non-binding let on a synchronization lock",
+ )
+ })
+ } else {
+ cx.struct_span_lint(LET_UNDERSCORE_DROP, local.span, |lint| {
+ build_and_emit_lint(
+ lint,
+ local,
+ init.span,
+ "non-binding let on a type that implements `Drop`",
+ );
+ })
+ }
+ }
+ }
+}
+
+fn build_and_emit_lint(
+ lint: LintDiagnosticBuilder<'_, ()>,
+ local: &hir::Local<'_>,
+ init_span: rustc_span::Span,
+ msg: &str,
+) {
+ lint.build(msg)
+ .span_suggestion_verbose(
+ local.pat.span,
+ "consider binding to an unused variable to avoid immediately dropping the value",
+ "_unused",
+ Applicability::MachineApplicable,
+ )
+ .multipart_suggestion(
+ "consider immediately dropping the value",
+ vec![
+ (local.span.until(init_span), "drop(".to_string()),
+ (init_span.shrink_to_hi(), ")".to_string()),
+ ],
+ Applicability::MachineApplicable,
+ )
+ .emit();
+}
use rustc_session::Session;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{Span, DUMMY_SP};
-use tracing::debug;
use crate::errors::{
MalformedAttribute, MalformedAttributeSub, OverruledAttribute, OverruledAttributeSub,
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
#![feature(iter_order_by)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(never_type)]
#![recursion_limit = "256"]
extern crate rustc_middle;
#[macro_use]
extern crate rustc_session;
+#[macro_use]
+extern crate tracing;
mod array_into_iter;
pub mod builtin;
pub mod hidden_unicode_codepoints;
mod internal;
mod late;
+mod let_underscore;
mod levels;
mod methods;
mod non_ascii_idents;
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
use hidden_unicode_codepoints::*;
use internal::*;
+use let_underscore::*;
use methods::*;
use non_ascii_idents::*;
use non_fmt_panic::NonPanicFmt;
UnusedBraces: UnusedBraces,
UnusedImportBraces: UnusedImportBraces,
UnsafeCode: UnsafeCode,
+ SpecialModuleName: SpecialModuleName,
AnonymousParameters: AnonymousParameters,
EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(),
NonCamelCaseTypes: NonCamelCaseTypes,
VariantSizeDifferences: VariantSizeDifferences,
BoxPointers: BoxPointers,
PathStatements: PathStatements,
+ LetUnderscore: LetUnderscore,
// Depends on referenced function signatures in expressions
UnusedResults: UnusedResults,
NonUpperCaseGlobals: NonUpperCaseGlobals,
REDUNDANT_SEMICOLONS
);
+ add_lint_group!("let_underscore", LET_UNDERSCORE_DROP, LET_UNDERSCORE_LOCK);
+
add_lint_group!(
"rust_2018_idioms",
BARE_TRAIT_OBJECTS,
}
/// A lint pass boxed up as a trait object.
-pub type EarlyLintPassObject = Box<dyn EarlyLintPass + sync::Send + sync::Sync + 'static>;
-pub type LateLintPassObject =
- Box<dyn for<'tcx> LateLintPass<'tcx> + sync::Send + sync::Sync + 'static>;
+pub type EarlyLintPassObject = Box<dyn EarlyLintPass + sync::Send + 'static>;
+pub type LateLintPassObject = Box<dyn for<'tcx> LateLintPass<'tcx> + sync::Send + 'static>;
use std::cmp;
use std::iter;
use std::ops::ControlFlow;
-use tracing::debug;
declare_lint! {
/// The `unused_comparisons` lint detects comparisons made useless by
},
ty::Closure(..) => {
cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
- // FIXME(davidtwco): this isn't properly translatable becauses of the
+ // FIXME(davidtwco): this isn't properly translatable because of the
// pre/post strings
lint.build(fluent::lint::unused_closure)
.set_arg("count", plural_len)
}
ty::Generator(..) => {
cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
- // FIXME(davidtwco): this isn't properly translatable becauses of the
+ // FIXME(davidtwco): this isn't properly translatable because of the
// pre/post strings
lint.build(fluent::lint::unused_generator)
.set_arg("count", plural_len)
) -> bool {
if let Some(attr) = cx.tcx.get_attr(def_id, sym::must_use) {
cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
- // FIXME(davidtwco): this isn't properly translatable becauses of the pre/post
+ // FIXME(davidtwco): this isn't properly translatable because of the pre/post
// strings
let mut err = lint.build(fluent::lint::unused_def);
err.set_arg("pre", descr_pre_path);
/// [future-incompatible]: ../index.md#future-incompatible-lints
pub REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
Warn,
- "tranparent type contains an external ZST that is marked #[non_exhaustive] or contains private fields",
+ "transparent type contains an external ZST that is marked #[non_exhaustive] or contains private fields",
@future_incompatible = FutureIncompatibleInfo {
reference: "issue #78586 <https://github.com/rust-lang/rust/issues/78586>",
};
/// All suggestions are marked with an `Applicability`. Tools use the applicability of a suggestion
/// to determine whether it should be automatically applied or if the user should be consulted
/// before applying the suggestion.
-#[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable, Serialize, Deserialize)]
+#[derive(Copy, Clone, Debug, Hash, Encodable, Decodable, Serialize, Deserialize)]
+#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum Applicability {
/// The suggestion is definitely what the user intended, or maintains the exact meaning of the code.
/// This suggestion should be automatically applied.
};
// RISC-V GCC erroneously requires libatomic for sub-word
- // atomic operations. FreeBSD uses Clang as its system
+ // atomic operations. Some BSD uses Clang as its system
// compiler and provides no libatomic in its base system so
// does not want this.
- if !target.contains("freebsd") && target.starts_with("riscv") {
+ if target.starts_with("riscv") && !target.contains("freebsd") && !target.contains("openbsd") {
println!("cargo:rustc-link-lib=atomic");
}
return Name.data();
}
-extern "C" const char *LLVMRustArchiveChildData(LLVMRustArchiveChildRef Child,
- size_t *Size) {
- StringRef Buf;
- Expected<StringRef> BufOrErr = Child->getBuffer();
- if (!BufOrErr) {
- LLVMRustSetLastError(toString(BufOrErr.takeError()).c_str());
- return nullptr;
- }
- Buf = BufOrErr.get();
- *Size = Buf.size();
- return Buf.data();
-}
-
extern "C" LLVMRustArchiveMemberRef
LLVMRustArchiveMemberNew(char *Filename, char *Name,
LLVMRustArchiveChildRef Child) {
}
}
- Ok(tokens.drain(..).collect())
+ Ok(tokens.into_iter().collect())
}
fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {
use std::collections::HashMap;
use std::fmt;
use std::str::FromStr;
-use syn::{parse_quote, spanned::Spanned, Meta, MetaList, MetaNameValue, NestedMeta, Path};
+use syn::{spanned::Spanned, Meta, MetaList, MetaNameValue, NestedMeta, Path};
use synstructure::{BindingInfo, Structure, VariantInfo};
/// Which kind of suggestion is being created?
variant,
span,
fields: fields_map,
- kind: None,
- slug: None,
+ kinds: Vec::new(),
+ slugs: Vec::new(),
code: None,
span_field: None,
applicability: None,
fields: HashMap<String, TokenStream>,
/// Subdiagnostic kind of the type/variant.
- kind: Option<(SubdiagnosticKind, proc_macro::Span)>,
+ kinds: Vec<(SubdiagnosticKind, proc_macro::Span)>,
- /// Slug of the subdiagnostic - corresponds to the Fluent identifier for the message - from the
+ /// Slugs of the subdiagnostic - corresponds to the Fluent identifier for the message - from the
/// `#[kind(slug)]` attribute on the type or variant.
- slug: Option<(Path, proc_macro::Span)>,
+ slugs: Vec<(Path, proc_macro::Span)>,
/// If a suggestion, the code to suggest as a replacement - from the `#[kind(code = "...")]`
/// attribute on the type or variant.
code: Option<(TokenStream, proc_macro::Span)>,
impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
fn identify_kind(&mut self) -> Result<(), DiagnosticDeriveError> {
- for attr in self.variant.ast().attrs {
+ for (i, attr) in self.variant.ast().attrs.iter().enumerate() {
let span = attr.span().unwrap();
let name = attr.path.segments.last().unwrap().ident.to_string();
if let Some(nested_attr) = nested_iter.next() {
match nested_attr {
NestedMeta::Meta(Meta::Path(path)) => {
- self.slug.set_once((path.clone(), span));
+ self.slugs.push((path.clone(), span));
}
NestedMeta::Meta(meta @ Meta::NameValue(_))
if matches!(
);
}
- if self.slug.is_none() {
+ if self.slugs.len() != i + 1 {
throw_span_err!(
span,
&format!(
);
}
- self.kind.set_once((kind, span));
+ self.kinds.push((kind, span));
}
Ok(())
fn generate_field_code(
&mut self,
binding: &BindingInfo<'_>,
- is_suggestion: bool,
+ have_suggestion: bool,
) -> Result<TokenStream, DiagnosticDeriveError> {
let ast = binding.ast();
self.span_field.set_once((binding.binding.clone(), span));
return Ok(quote! {});
}
- "applicability" if is_suggestion => {
+ "applicability" if have_suggestion => {
report_error_if_not_applied_to_applicability(attr, &info)?;
let binding = binding.binding.clone();
self.applicability.set_once((quote! { #binding }, span));
fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
self.identify_kind()?;
- let Some(kind) = self.kind.map(|(kind, _)| kind) else {
+ if self.kinds.is_empty() {
throw_span_err!(
self.variant.ast().ident.span().unwrap(),
"subdiagnostic kind not specified"
);
};
-
- let is_suggestion = matches!(kind, SubdiagnosticKind::Suggestion(_));
-
+ let have_suggestion =
+ self.kinds.iter().any(|(k, _)| matches!(k, SubdiagnosticKind::Suggestion(_)));
let mut args = TokenStream::new();
for binding in self.variant.bindings() {
let arg = self
- .generate_field_code(binding, is_suggestion)
+ .generate_field_code(binding, have_suggestion)
.unwrap_or_else(|v| v.to_compile_error());
args.extend(arg);
}
+ let mut tokens = TokenStream::new();
+ for ((kind, _), (slug, _)) in self.kinds.iter().zip(&self.slugs) {
+ let code = match self.code.as_ref() {
+ Some((code, _)) => Some(quote! { #code }),
+ None if have_suggestion => {
+ span_err(self.span, "suggestion without `code = \"...\"`").emit();
+ Some(quote! { /* macro error */ "..." })
+ }
+ None => None,
+ };
- // Missing slug errors will already have been reported.
- let slug = self
- .slug
- .as_ref()
- .map(|(slug, _)| slug.clone())
- .unwrap_or_else(|| parse_quote! { you::need::to::specify::a::slug });
- let code = match self.code.as_ref() {
- Some((code, _)) => Some(quote! { #code }),
- None if is_suggestion => {
- span_err(self.span, "suggestion without `code = \"...\"`").emit();
- Some(quote! { /* macro error */ "..." })
- }
- None => None,
- };
-
- let span_field = self.span_field.as_ref().map(|(span, _)| span);
- let applicability = match self.applicability.clone() {
- Some((applicability, _)) => Some(applicability),
- None if is_suggestion => {
- span_err(self.span, "suggestion without `applicability`").emit();
- Some(quote! { rustc_errors::Applicability::Unspecified })
- }
- None => None,
- };
+ let span_field = self.span_field.as_ref().map(|(span, _)| span);
+ let applicability = match self.applicability.clone() {
+ Some((applicability, _)) => Some(applicability),
+ None if have_suggestion => {
+ span_err(self.span, "suggestion without `applicability`").emit();
+ Some(quote! { rustc_errors::Applicability::Unspecified })
+ }
+ None => None,
+ };
- let diag = &self.diag;
- let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
- let message = quote! { rustc_errors::fluent::#slug };
- let call = if matches!(kind, SubdiagnosticKind::Suggestion(..)) {
- if let Some(span) = span_field {
- quote! { #diag.#name(#span, #message, #code, #applicability); }
- } else {
- span_err(self.span, "suggestion without `#[primary_span]` field").emit();
- quote! { unreachable!(); }
- }
- } else if matches!(kind, SubdiagnosticKind::Label) {
- if let Some(span) = span_field {
- quote! { #diag.#name(#span, #message); }
- } else {
- span_err(self.span, "label without `#[primary_span]` field").emit();
- quote! { unreachable!(); }
- }
- } else {
- if let Some(span) = span_field {
- quote! { #diag.#name(#span, #message); }
+ let diag = &self.diag;
+ let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
+ let message = quote! { rustc_errors::fluent::#slug };
+ let call = if matches!(kind, SubdiagnosticKind::Suggestion(..)) {
+ if let Some(span) = span_field {
+ quote! { #diag.#name(#span, #message, #code, #applicability); }
+ } else {
+ span_err(self.span, "suggestion without `#[primary_span]` field").emit();
+ quote! { unreachable!(); }
+ }
+ } else if matches!(kind, SubdiagnosticKind::Label) {
+ if let Some(span) = span_field {
+ quote! { #diag.#name(#span, #message); }
+ } else {
+ span_err(self.span, "label without `#[primary_span]` field").emit();
+ quote! { unreachable!(); }
+ }
} else {
- quote! { #diag.#name(#message); }
- }
- };
+ if let Some(span) = span_field {
+ quote! { #diag.#name(#span, #message); }
+ } else {
+ quote! { #diag.#name(#message); }
+ }
+ };
+ tokens.extend(quote! {
+ #call
+ #args
+ });
+ }
- Ok(quote! {
- #call
- #args
- })
+ Ok(tokens)
}
}
suggestion_short,
suggestion_hidden,
suggestion_verbose,
+ multipart_suggestion,
+ multipart_suggestion_short,
+ multipart_suggestion_hidden,
+ multipart_suggestion_verbose,
// field attributes
skip_arg,
primary_span,
+ suggestion_part,
applicability)] => diagnostics::session_subdiagnostic_derive
);
use std::ops::Fn;
use std::path::Path;
use std::{cmp, env};
-use tracing::{debug, info};
#[derive(Clone)]
pub struct CStore {
fn existing_match(&self, name: Symbol, hash: Option<Svh>, kind: PathKind) -> Option<CrateNum> {
for (cnum, data) in self.cstore.iter_crate_data() {
if data.name() != name {
- tracing::trace!("{} did not match {}", data.name(), name);
+ trace!("{} did not match {}", data.name(), name);
continue;
}
let name = tcx.crate_name(cnum);
let src = tcx.used_crate_source(cnum);
if src.dylib.is_some() {
- tracing::info!("adding dylib: {}", name);
+ info!("adding dylib: {}", name);
add_library(tcx, cnum, RequireDynamic, &mut formats);
let deps = tcx.dylib_dependency_formats(cnum);
for &(depnum, style) in deps.iter() {
- tracing::info!("adding {:?}: {}", style, tcx.crate_name(depnum));
+ info!("adding {:?}: {}", style, tcx.crate_name(depnum));
add_library(tcx, depnum, style, &mut formats);
}
}
&& tcx.dep_kind(cnum) == CrateDepKind::Explicit
{
assert!(src.rlib.is_some() || src.rmeta.is_some());
- tracing::info!("adding staticlib: {}", tcx.crate_name(cnum));
+ info!("adding staticlib: {}", tcx.crate_name(cnum));
add_library(tcx, cnum, RequireStatic, &mut formats);
ret[cnum.as_usize() - 1] = Linkage::Static;
}
#![feature(generators)]
#![feature(generic_associated_types)]
#![feature(iter_from_generator)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(once_cell)]
#![feature(proc_macro_internals)]
#[macro_use]
extern crate rustc_data_structures;
+#[macro_use]
+extern crate tracing;
+
pub use rmeta::{provide, provide_extern};
mod dependency_format;
use std::io::{Read, Result as IoResult, Write};
use std::path::{Path, PathBuf};
use std::{cmp, fmt, fs};
-use tracing::{debug, info};
#[derive(Clone)]
pub(crate) struct CrateLocator<'a> {
use crate::rmeta::*;
use rustc_ast as ast;
-use rustc_ast::ptr::P;
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::svh::Svh;
use rustc_session::Session;
use rustc_span::hygiene::{ExpnIndex, MacroKind};
use rustc_span::source_map::{respan, Spanned};
-use rustc_span::symbol::{sym, Ident, Symbol};
+use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{self, BytePos, ExpnId, Pos, Span, SyntaxContext, DUMMY_SP};
use proc_macro::bridge::client::ProcMacro;
use std::mem;
use std::num::NonZeroUsize;
use std::path::Path;
-use tracing::debug;
pub(super) use cstore_impl::provide;
pub use cstore_impl::provide_extern;
self.opt_item_ident(item_index, sess).expect("no encoded ident for item")
}
- fn maybe_kind(self, item_id: DefIndex) -> Option<EntryKind> {
- self.root.tables.kind.get(self, item_id).map(|k| k.decode(self))
- }
-
#[inline]
pub(super) fn map_encoded_cnum_to_current(self, cnum: CrateNum) -> CrateNum {
if cnum == LOCAL_CRATE { self.cnum } else { self.cnum_map[cnum] }
}
- fn kind(self, item_id: DefIndex) -> EntryKind {
- self.maybe_kind(item_id).unwrap_or_else(|| {
- bug!(
- "CrateMetadata::kind({:?}): id not found, in crate {:?} with number {}",
- item_id,
- self.root.name,
- self.cnum,
- )
- })
- }
-
fn def_kind(self, item_id: DefIndex) -> DefKind {
self.root.tables.opt_def_kind.get(self, item_id).unwrap_or_else(|| {
bug!(
)
}
- fn get_variant(self, kind: &EntryKind, index: DefIndex, parent_did: DefId) -> ty::VariantDef {
- let data = match kind {
- EntryKind::Variant(data) | EntryKind::Struct(data) | EntryKind::Union(data) => {
- data.decode(self)
- }
- _ => bug!(),
- };
-
+ fn get_variant(self, kind: &DefKind, index: DefIndex, parent_did: DefId) -> ty::VariantDef {
let adt_kind = match kind {
- EntryKind::Variant(_) => ty::AdtKind::Enum,
- EntryKind::Struct(..) => ty::AdtKind::Struct,
- EntryKind::Union(..) => ty::AdtKind::Union,
+ DefKind::Variant => ty::AdtKind::Enum,
+ DefKind::Struct => ty::AdtKind::Struct,
+ DefKind::Union => ty::AdtKind::Union,
_ => bug!(),
};
+ let data = self.root.tables.variant_data.get(self, index).unwrap().decode(self);
+
let variant_did =
if adt_kind == ty::AdtKind::Enum { Some(self.local_def_id(index)) } else { None };
let ctor_did = data.ctor.map(|index| self.local_def_id(index));
}
fn get_adt_def(self, item_id: DefIndex, tcx: TyCtxt<'tcx>) -> ty::AdtDef<'tcx> {
- let kind = self.kind(item_id);
+ let kind = self.def_kind(item_id);
let did = self.local_def_id(item_id);
let adt_kind = match kind {
- EntryKind::Enum => ty::AdtKind::Enum,
- EntryKind::Struct(_) => ty::AdtKind::Struct,
- EntryKind::Union(_) => ty::AdtKind::Union,
+ DefKind::Enum => ty::AdtKind::Enum,
+ DefKind::Struct => ty::AdtKind::Struct,
+ DefKind::Union => ty::AdtKind::Union,
_ => bug!("get_adt_def called on a non-ADT {:?}", did),
};
let repr = self.root.tables.repr_options.get(self, item_id).unwrap().decode(self);
.get(self, item_id)
.unwrap_or_else(LazyArray::empty)
.decode(self)
- .map(|index| self.get_variant(&self.kind(index), index, did))
+ .map(|index| self.get_variant(&self.def_kind(index), index, did))
.collect()
} else {
std::iter::once(self.get_variant(&kind, item_id, did)).collect()
let vis = self.get_visibility(child_index);
let span = self.get_span(child_index, sess);
let macro_rules = match kind {
- DefKind::Macro(..) => match self.kind(child_index) {
- EntryKind::MacroDef(_, macro_rules) => macro_rules,
- _ => unreachable!(),
- },
+ DefKind::Macro(..) => {
+ self.root.tables.macro_rules.get(self, child_index).is_some()
+ }
_ => false,
};
}
}
- match self.kind(id) {
- EntryKind::Mod(exports) => {
- for exp in exports.decode((self, sess)) {
- callback(exp);
- }
+ if let Some(exports) = self.root.tables.module_reexports.get(self, id) {
+ for exp in exports.decode((self, sess)) {
+ callback(exp);
}
- EntryKind::Enum | EntryKind::Trait => {}
- _ => bug!("`for_each_module_child` is called on a non-module: {:?}", self.def_kind(id)),
}
}
}
fn module_expansion(self, id: DefIndex, sess: &Session) -> ExpnId {
- match self.kind(id) {
- EntryKind::Mod(_) | EntryKind::Enum | EntryKind::Trait => {
- self.get_expn_that_defined(id, sess)
- }
+ match self.def_kind(id) {
+ DefKind::Mod | DefKind::Enum | DefKind::Trait => self.get_expn_that_defined(id, sess),
_ => panic!("Expected module, found {:?}", self.local_def_id(id)),
}
}
- fn get_fn_has_self_parameter(self, id: DefIndex) -> bool {
- match self.kind(id) {
- EntryKind::AssocFn { has_self, .. } => has_self,
- _ => false,
- }
+ fn get_fn_has_self_parameter(self, id: DefIndex, sess: &'a Session) -> bool {
+ self.root
+ .tables
+ .fn_arg_names
+ .get(self, id)
+ .unwrap_or_else(LazyArray::empty)
+ .decode((self, sess))
+ .nth(0)
+ .map_or(false, |ident| ident.name == kw::SelfLower)
}
fn get_associated_item_def_ids(
.map(move |child_index| self.local_def_id(child_index))
}
- fn get_associated_item(self, id: DefIndex) -> ty::AssocItem {
+ fn get_associated_item(self, id: DefIndex, sess: &'a Session) -> ty::AssocItem {
let name = self.item_name(id);
- let (kind, container, has_self) = match self.kind(id) {
- EntryKind::AssocConst(container) => (ty::AssocKind::Const, container, false),
- EntryKind::AssocFn { container, has_self } => (ty::AssocKind::Fn, container, has_self),
- EntryKind::AssocType(container) => (ty::AssocKind::Type, container, false),
- _ => bug!("cannot get associated-item of `{:?}`", id),
+ let kind = match self.def_kind(id) {
+ DefKind::AssocConst => ty::AssocKind::Const,
+ DefKind::AssocFn => ty::AssocKind::Fn,
+ DefKind::AssocTy => ty::AssocKind::Type,
+ _ => bug!("cannot get associated-item of `{:?}`", self.def_key(id)),
};
+ let has_self = self.get_fn_has_self_parameter(id, sess);
+ let container = self.root.tables.assoc_container.get(self, id).unwrap();
ty::AssocItem {
name,
}
fn get_ctor_def_id_and_kind(self, node_id: DefIndex) -> Option<(DefId, CtorKind)> {
- match self.kind(node_id) {
- EntryKind::Struct(data) | EntryKind::Variant(data) => {
- let vdata = data.decode(self);
+ match self.def_kind(node_id) {
+ DefKind::Struct | DefKind::Variant => {
+ let vdata = self.root.tables.variant_data.get(self, node_id).unwrap().decode(self);
vdata.ctor.map(|index| (self.local_def_id(index), vdata.ctor_kind))
}
_ => None,
}
fn get_macro(self, id: DefIndex, sess: &Session) -> ast::MacroDef {
- match self.kind(id) {
- EntryKind::MacroDef(mac_args, macro_rules) => {
- ast::MacroDef { body: P(mac_args.decode((self, sess))), macro_rules }
+ match self.def_kind(id) {
+ DefKind::Macro(_) => {
+ let macro_rules = self.root.tables.macro_rules.get(self, id).is_some();
+ let body =
+ self.root.tables.macro_definition.get(self, id).unwrap().decode((self, sess));
+ ast::MacroDef { macro_rules, body: ast::ptr::P(body) }
}
_ => bug!(),
}
}
fn is_foreign_item(self, id: DefIndex) -> bool {
- match self.kind(id) {
- EntryKind::ForeignStatic | EntryKind::ForeignFn => true,
- _ => false,
+ if let Some(parent) = self.def_key(id).parent {
+ matches!(self.def_kind(parent), DefKind::ForeignMod)
+ } else {
+ false
}
}
codegen_fn_attrs => { table }
impl_trait_ref => { table }
const_param_default => { table }
+ object_lifetime_default => { table }
thir_abstract_const => { table }
optimized_mir => { table }
mir_for_ctfe => { table }
associated_item_def_ids => {
tcx.arena.alloc_from_iter(cdata.get_associated_item_def_ids(def_id.index, tcx.sess))
}
- associated_item => { cdata.get_associated_item(def_id.index) }
+ associated_item => { cdata.get_associated_item(def_id.index, tcx.sess) }
inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) }
is_foreign_item => { cdata.is_foreign_item(def_id.index) }
item_attrs => { tcx.arena.alloc_from_iter(cdata.get_item_attrs(def_id.index, tcx.sess)) }
)
}
- pub fn fn_has_self_parameter_untracked(&self, def: DefId) -> bool {
- self.get_crate_data(def.krate).get_fn_has_self_parameter(def.index)
+ pub fn fn_has_self_parameter_untracked(&self, def: DefId, sess: &Session) -> bool {
+ self.get_crate_data(def.krate).get_fn_has_self_parameter(def.index, sess)
}
pub fn crate_source_untracked(&self, cnum: CrateNum) -> Lrc<CrateSource> {
use rustc_hir::definitions::DefPathData;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::lang_items;
-use rustc_hir::{AnonConst, GenericParamKind};
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::dependency_format::Linkage;
use rustc_middle::middle::exported_symbols::{
use std::iter;
use std::num::NonZeroUsize;
use std::path::{Path, PathBuf};
-use tracing::{debug, trace};
pub(super) struct EncodeContext<'a, 'tcx> {
opaque: opaque::FileEncoder,
}
}
+fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) -> bool {
+ match def_kind {
+ DefKind::Struct
+ | DefKind::Union
+ | DefKind::Enum
+ | DefKind::Variant
+ | DefKind::Ctor(..)
+ | DefKind::Field
+ | DefKind::Fn
+ | DefKind::Const
+ | DefKind::Static(..)
+ | DefKind::TyAlias
+ | DefKind::OpaqueTy
+ | DefKind::ForeignTy
+ | DefKind::Impl
+ | DefKind::AssocFn
+ | DefKind::AssocConst
+ | DefKind::Closure
+ | DefKind::Generator
+ | DefKind::ConstParam
+ | DefKind::AnonConst
+ | DefKind::InlineConst => true,
+
+ DefKind::AssocTy => {
+ let assoc_item = tcx.associated_item(def_id);
+ match assoc_item.container {
+ ty::AssocItemContainer::ImplContainer => true,
+ ty::AssocItemContainer::TraitContainer => assoc_item.defaultness(tcx).has_value(),
+ }
+ }
+ DefKind::TyParam => {
+ let hir::Node::GenericParam(param) = tcx.hir().get_by_def_id(def_id) else { bug!() };
+ let hir::GenericParamKind::Type { default, .. } = param.kind else { bug!() };
+ default.is_some()
+ }
+
+ DefKind::Trait
+ | DefKind::TraitAlias
+ | DefKind::Mod
+ | DefKind::ForeignMod
+ | DefKind::Macro(..)
+ | DefKind::Use
+ | DefKind::LifetimeParam
+ | DefKind::GlobalAsm
+ | DefKind::ExternCrate => false,
+ }
+}
+
+fn should_encode_const(def_kind: DefKind) -> bool {
+ match def_kind {
+ DefKind::Const | DefKind::AssocConst | DefKind::AnonConst => true,
+
+ DefKind::Struct
+ | DefKind::Union
+ | DefKind::Enum
+ | DefKind::Variant
+ | DefKind::Ctor(..)
+ | DefKind::Field
+ | DefKind::Fn
+ | DefKind::Static(..)
+ | DefKind::TyAlias
+ | DefKind::OpaqueTy
+ | DefKind::ForeignTy
+ | DefKind::Impl
+ | DefKind::AssocFn
+ | DefKind::Closure
+ | DefKind::Generator
+ | DefKind::ConstParam
+ | DefKind::InlineConst
+ | DefKind::AssocTy
+ | DefKind::TyParam
+ | DefKind::Trait
+ | DefKind::TraitAlias
+ | DefKind::Mod
+ | DefKind::ForeignMod
+ | DefKind::Macro(..)
+ | DefKind::Use
+ | DefKind::LifetimeParam
+ | DefKind::GlobalAsm
+ | DefKind::ExternCrate => false,
+ }
+}
+
impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
fn encode_attrs(&mut self, def_id: LocalDefId) {
let mut attrs = self
let def_kind = tcx.opt_def_kind(local_id);
let Some(def_kind) = def_kind else { continue };
self.tables.opt_def_kind.set(def_id.index, def_kind);
- record!(self.tables.def_span[def_id] <- tcx.def_span(def_id));
+ let def_span = tcx.def_span(local_id);
+ record!(self.tables.def_span[def_id] <- def_span);
self.encode_attrs(local_id);
record!(self.tables.expn_that_defined[def_id] <- self.tcx.expn_that_defined(def_id));
if let Some(ident_span) = tcx.def_ident_span(def_id) {
record_array!(self.tables.inferred_outlives_of[def_id] <- inferred_outlives);
}
}
+ if should_encode_type(tcx, local_id, def_kind) {
+ record!(self.tables.type_of[def_id] <- self.tcx.type_of(def_id));
+ }
+ if let DefKind::TyParam | DefKind::ConstParam = def_kind {
+ if let Some(default) = self.tcx.object_lifetime_default(def_id) {
+ record!(self.tables.object_lifetime_default[def_id] <- default);
+ }
+ }
if let DefKind::Trait | DefKind::TraitAlias = def_kind {
record!(self.tables.super_predicates_of[def_id] <- self.tcx.super_predicates_of(def_id));
}
}
}
- fn encode_item_type(&mut self, def_id: DefId) {
- debug!("EncodeContext::encode_item_type({:?})", def_id);
- record!(self.tables.type_of[def_id] <- self.tcx.type_of(def_id));
- }
-
fn encode_enum_variant_info(&mut self, def: ty::AdtDef<'tcx>, index: VariantIdx) {
let tcx = self.tcx;
let variant = &def.variant(index);
is_non_exhaustive: variant.is_field_list_non_exhaustive(),
};
- record!(self.tables.kind[def_id] <- EntryKind::Variant(self.lazy(data)));
+ record!(self.tables.variant_data[def_id] <- data);
self.tables.constness.set(def_id.index, hir::Constness::Const);
record_array!(self.tables.children[def_id] <- variant.fields.iter().map(|f| {
assert!(f.did.is_local());
f.did.index
}));
- self.encode_item_type(def_id);
if variant.ctor_kind == CtorKind::Fn {
// FIXME(eddyb) encode signature only in `encode_enum_variant_ctor`.
if let Some(ctor_def_id) = variant.ctor_def_id {
is_non_exhaustive: variant.is_field_list_non_exhaustive(),
};
- record!(self.tables.kind[def_id] <- EntryKind::Variant(self.lazy(data)));
+ record!(self.tables.variant_data[def_id] <- data);
self.tables.constness.set(def_id.index, hir::Constness::Const);
- self.encode_item_type(def_id);
if variant.ctor_kind == CtorKind::Fn {
record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
}
// code uses it). However, we skip encoding anything relating to child
// items - we encode information about proc-macros later on.
let reexports = if !self.is_proc_macro {
- match tcx.module_reexports(local_def_id) {
- Some(exports) => self.lazy_array(exports),
- _ => LazyArray::empty(),
- }
+ tcx.module_reexports(local_def_id).unwrap_or(&[])
} else {
- LazyArray::empty()
+ &[]
};
- record!(self.tables.kind[def_id] <- EntryKind::Mod(reexports));
+ record_array!(self.tables.module_reexports[def_id] <- reexports);
if self.is_proc_macro {
// Encode this here because we don't do it in encode_def_ids.
record!(self.tables.expn_that_defined[def_id] <- tcx.expn_that_defined(local_def_id));
}
}
- fn encode_field(
- &mut self,
- adt_def: ty::AdtDef<'tcx>,
- variant_index: VariantIdx,
- field_index: usize,
- ) {
- let variant = &adt_def.variant(variant_index);
- let field = &variant.fields[field_index];
-
- let def_id = field.did;
- debug!("EncodeContext::encode_field({:?})", def_id);
-
- record!(self.tables.kind[def_id] <- EntryKind::Field);
- self.encode_item_type(def_id);
- }
-
fn encode_struct_ctor(&mut self, adt_def: ty::AdtDef<'tcx>, def_id: DefId) {
debug!("EncodeContext::encode_struct_ctor({:?})", def_id);
let tcx = self.tcx;
};
record!(self.tables.repr_options[def_id] <- adt_def.repr());
+ record!(self.tables.variant_data[def_id] <- data);
self.tables.constness.set(def_id.index, hir::Constness::Const);
- record!(self.tables.kind[def_id] <- EntryKind::Struct(self.lazy(data)));
- self.encode_item_type(def_id);
if variant.ctor_kind == CtorKind::Fn {
record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
}
let ast_item = tcx.hir().expect_trait_item(def_id.expect_local());
self.tables.impl_defaultness.set(def_id.index, ast_item.defaultness);
let trait_item = tcx.associated_item(def_id);
+ self.tables.assoc_container.set(def_id.index, trait_item.container);
match trait_item.kind {
- ty::AssocKind::Const => {
- let rendered = rustc_hir_pretty::to_string(
- &(&self.tcx.hir() as &dyn intravisit::Map<'_>),
- |s| s.print_trait_item(ast_item),
- );
-
- record!(self.tables.kind[def_id] <- EntryKind::AssocConst(ty::AssocItemContainer::TraitContainer));
- record!(self.tables.mir_const_qualif[def_id] <- mir::ConstQualifs::default());
- record!(self.tables.rendered_const[def_id] <- rendered);
- }
+ ty::AssocKind::Const => {}
ty::AssocKind::Fn => {
let hir::TraitItemKind::Fn(m_sig, m) = &ast_item.kind else { bug!() };
match *m {
};
self.tables.asyncness.set(def_id.index, m_sig.header.asyncness);
self.tables.constness.set(def_id.index, hir::Constness::NotConst);
- record!(self.tables.kind[def_id] <- EntryKind::AssocFn {
- container: ty::AssocItemContainer::TraitContainer,
- has_self: trait_item.fn_has_self_parameter,
- });
}
ty::AssocKind::Type => {
self.encode_explicit_item_bounds(def_id);
- record!(self.tables.kind[def_id] <- EntryKind::AssocType(ty::AssocItemContainer::TraitContainer));
- }
- }
- match trait_item.kind {
- ty::AssocKind::Const | ty::AssocKind::Fn => {
- self.encode_item_type(def_id);
- }
- ty::AssocKind::Type => {
- if ast_item.defaultness.has_value() {
- self.encode_item_type(def_id);
- }
}
}
if trait_item.kind == ty::AssocKind::Fn {
let ast_item = self.tcx.hir().expect_impl_item(def_id.expect_local());
self.tables.impl_defaultness.set(def_id.index, ast_item.defaultness);
let impl_item = self.tcx.associated_item(def_id);
+ self.tables.assoc_container.set(def_id.index, impl_item.container);
match impl_item.kind {
- ty::AssocKind::Const => {
- if let hir::ImplItemKind::Const(_, body_id) = ast_item.kind {
- let qualifs = self.tcx.at(ast_item.span).mir_const_qualif(def_id);
- let const_data = self.encode_rendered_const_for_body(body_id);
-
- record!(self.tables.kind[def_id] <- EntryKind::AssocConst(ty::AssocItemContainer::ImplContainer));
- record!(self.tables.mir_const_qualif[def_id] <- qualifs);
- record!(self.tables.rendered_const[def_id] <- const_data);
- } else {
- bug!()
- }
- }
ty::AssocKind::Fn => {
let hir::ImplItemKind::Fn(ref sig, body) = ast_item.kind else { bug!() };
self.tables.asyncness.set(def_id.index, sig.header.asyncness);
hir::Constness::NotConst
};
self.tables.constness.set(def_id.index, constness);
- record!(self.tables.kind[def_id] <- EntryKind::AssocFn {
- container: ty::AssocItemContainer::ImplContainer,
- has_self: impl_item.fn_has_self_parameter,
- });
- }
- ty::AssocKind::Type => {
- record!(self.tables.kind[def_id] <- EntryKind::AssocType(ty::AssocItemContainer::ImplContainer));
}
+ ty::AssocKind::Const | ty::AssocKind::Type => {}
}
- self.encode_item_type(def_id);
if let Some(trait_item_def_id) = impl_item.trait_item_def_id {
self.tables.trait_item_def_id.set(def_id.index, trait_item_def_id.into());
}
return;
}
- let keys_and_jobs = self
- .tcx
+ let tcx = self.tcx;
+
+ let keys_and_jobs = tcx
.mir_keys(())
.iter()
.filter_map(|&def_id| {
- let (encode_const, encode_opt) = should_encode_mir(self.tcx, def_id);
+ let (encode_const, encode_opt) = should_encode_mir(tcx, def_id);
if encode_const || encode_opt {
Some((def_id, encode_const, encode_opt))
} else {
debug!("EntryBuilder::encode_mir({:?})", def_id);
if encode_opt {
- record!(self.tables.optimized_mir[def_id.to_def_id()] <- self.tcx.optimized_mir(def_id));
+ record!(self.tables.optimized_mir[def_id.to_def_id()] <- tcx.optimized_mir(def_id));
}
if encode_const {
- record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- self.tcx.mir_for_ctfe(def_id));
+ record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- tcx.mir_for_ctfe(def_id));
// FIXME(generic_const_exprs): this feels wrong to have in `encode_mir`
- let abstract_const = self.tcx.thir_abstract_const(def_id);
+ let abstract_const = tcx.thir_abstract_const(def_id);
if let Ok(Some(abstract_const)) = abstract_const {
record!(self.tables.thir_abstract_const[def_id.to_def_id()] <- abstract_const);
}
+
+ if should_encode_const(tcx.def_kind(def_id)) {
+ let qualifs = tcx.mir_const_qualif(def_id);
+ record!(self.tables.mir_const_qualif[def_id.to_def_id()] <- qualifs);
+ let body_id = tcx.hir().maybe_body_owned_by(def_id);
+ if let Some(body_id) = body_id {
+ let const_data = self.encode_rendered_const_for_body(body_id);
+ record!(self.tables.rendered_const[def_id.to_def_id()] <- const_data);
+ }
+ }
}
- record!(self.tables.promoted_mir[def_id.to_def_id()] <- self.tcx.promoted_mir(def_id));
+ record!(self.tables.promoted_mir[def_id.to_def_id()] <- tcx.promoted_mir(def_id));
let instance =
ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id.to_def_id()));
- let unused = self.tcx.unused_generic_params(instance);
+ let unused = tcx.unused_generic_params(instance);
if !unused.is_empty() {
record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused);
}
debug!("EncodeContext::encode_info_for_item({:?})", def_id);
- let entry_kind = match item.kind {
- hir::ItemKind::Static(..) => EntryKind::Static,
- hir::ItemKind::Const(_, body_id) => {
- let qualifs = self.tcx.at(item.span).mir_const_qualif(def_id);
- let const_data = self.encode_rendered_const_for_body(body_id);
- record!(self.tables.mir_const_qualif[def_id] <- qualifs);
- record!(self.tables.rendered_const[def_id] <- const_data);
- EntryKind::Const
- }
+ match item.kind {
hir::ItemKind::Fn(ref sig, .., body) => {
self.tables.asyncness.set(def_id.index, sig.header.asyncness);
record_array!(self.tables.fn_arg_names[def_id] <- self.tcx.hir().body_param_names(body));
self.tables.constness.set(def_id.index, sig.header.constness);
- EntryKind::Fn
}
hir::ItemKind::Macro(ref macro_def, _) => {
- EntryKind::MacroDef(self.lazy(&*macro_def.body), macro_def.macro_rules)
+ if macro_def.macro_rules {
+ self.tables.macro_rules.set(def_id.index, ());
+ }
+ record!(self.tables.macro_definition[def_id] <- &*macro_def.body);
}
hir::ItemKind::Mod(ref m) => {
return self.encode_info_for_mod(item.def_id, m);
}
- hir::ItemKind::ForeignMod { .. } => EntryKind::ForeignMod,
- hir::ItemKind::GlobalAsm(..) => EntryKind::GlobalAsm,
- hir::ItemKind::TyAlias(..) => EntryKind::Type,
hir::ItemKind::OpaqueTy(..) => {
self.encode_explicit_item_bounds(def_id);
- EntryKind::OpaqueTy
}
hir::ItemKind::Enum(..) => {
let adt_def = self.tcx.adt_def(def_id);
record!(self.tables.repr_options[def_id] <- adt_def.repr());
- EntryKind::Enum
}
hir::ItemKind::Struct(ref struct_def, _) => {
let adt_def = self.tcx.adt_def(def_id);
.map(|ctor_hir_id| self.tcx.hir().local_def_id(ctor_hir_id).local_def_index);
let variant = adt_def.non_enum_variant();
- EntryKind::Struct(self.lazy(VariantData {
+ record!(self.tables.variant_data[def_id] <- VariantData {
ctor_kind: variant.ctor_kind,
discr: variant.discr,
ctor,
is_non_exhaustive: variant.is_field_list_non_exhaustive(),
- }))
+ });
}
hir::ItemKind::Union(..) => {
let adt_def = self.tcx.adt_def(def_id);
record!(self.tables.repr_options[def_id] <- adt_def.repr());
let variant = adt_def.non_enum_variant();
- EntryKind::Union(self.lazy(VariantData {
+ record!(self.tables.variant_data[def_id] <- VariantData {
ctor_kind: variant.ctor_kind,
discr: variant.discr,
ctor: None,
is_non_exhaustive: variant.is_field_list_non_exhaustive(),
- }))
+ });
}
hir::ItemKind::Impl(hir::Impl { defaultness, constness, .. }) => {
self.tables.impl_defaultness.set(def_id.index, *defaultness);
let polarity = self.tcx.impl_polarity(def_id);
self.tables.impl_polarity.set(def_id.index, polarity);
-
- EntryKind::Impl
}
hir::ItemKind::Trait(..) => {
let trait_def = self.tcx.trait_def(def_id);
record!(self.tables.trait_def[def_id] <- trait_def);
-
- EntryKind::Trait
}
hir::ItemKind::TraitAlias(..) => {
let trait_def = self.tcx.trait_def(def_id);
record!(self.tables.trait_def[def_id] <- trait_def);
-
- EntryKind::TraitAlias
}
hir::ItemKind::ExternCrate(_) | hir::ItemKind::Use(..) => {
bug!("cannot encode info for item {:?}", item)
}
+ hir::ItemKind::Static(..)
+ | hir::ItemKind::Const(..)
+ | hir::ItemKind::ForeignMod { .. }
+ | hir::ItemKind::GlobalAsm(..)
+ | hir::ItemKind::TyAlias(..) => {}
};
- record!(self.tables.kind[def_id] <- entry_kind);
// FIXME(eddyb) there should be a nicer way to do this.
match item.kind {
hir::ItemKind::Enum(..) => record_array!(self.tables.children[def_id] <-
}
_ => {}
}
- match item.kind {
- hir::ItemKind::Static(..)
- | hir::ItemKind::Const(..)
- | hir::ItemKind::Fn(..)
- | hir::ItemKind::TyAlias(..)
- | hir::ItemKind::OpaqueTy(..)
- | hir::ItemKind::Enum(..)
- | hir::ItemKind::Struct(..)
- | hir::ItemKind::Union(..)
- | hir::ItemKind::Impl { .. } => self.encode_item_type(def_id),
- _ => {}
- }
if let hir::ItemKind::Fn(..) = item.kind {
record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
if tcx.is_intrinsic(def_id) {
record!(self.tables.impl_trait_ref[def_id] <- trait_ref);
}
}
- }
+ // In some cases, along with the item itself, we also
+ // encode some sub-items. Usually we want some info from the item
+ // so it's easier to do that here then to wait until we would encounter
+ // normally in the visitor walk.
+ match item.kind {
+ hir::ItemKind::Enum(..) => {
+ let def = self.tcx.adt_def(item.def_id.to_def_id());
+ for (i, variant) in def.variants().iter_enumerated() {
+ self.encode_enum_variant_info(def, i);
- fn encode_info_for_generic_param(&mut self, def_id: DefId, kind: EntryKind, encode_type: bool) {
- record!(self.tables.kind[def_id] <- kind);
- if encode_type {
- self.encode_item_type(def_id);
+ if let Some(_ctor_def_id) = variant.ctor_def_id {
+ self.encode_enum_variant_ctor(def, i);
+ }
+ }
+ }
+ hir::ItemKind::Struct(ref struct_def, _) => {
+ let def = self.tcx.adt_def(item.def_id.to_def_id());
+ // If the struct has a constructor, encode it.
+ if let Some(ctor_hir_id) = struct_def.ctor_hir_id() {
+ let ctor_def_id = self.tcx.hir().local_def_id(ctor_hir_id);
+ self.encode_struct_ctor(def, ctor_def_id.to_def_id());
+ }
+ }
+ hir::ItemKind::Impl { .. } => {
+ for &trait_item_def_id in
+ self.tcx.associated_item_def_ids(item.def_id.to_def_id()).iter()
+ {
+ self.encode_info_for_impl_item(trait_item_def_id);
+ }
+ }
+ hir::ItemKind::Trait(..) => {
+ for &item_def_id in self.tcx.associated_item_def_ids(item.def_id.to_def_id()).iter()
+ {
+ self.encode_info_for_trait_item(item_def_id);
+ }
+ }
+ _ => {}
}
}
ty::Generator(..) => {
let data = self.tcx.generator_kind(def_id).unwrap();
let generator_diagnostic_data = typeck_result.get_generator_diagnostic_data();
- record!(self.tables.kind[def_id.to_def_id()] <- EntryKind::Generator);
record!(self.tables.generator_kind[def_id.to_def_id()] <- data);
record!(self.tables.generator_diagnostic_data[def_id.to_def_id()] <- generator_diagnostic_data);
}
- ty::Closure(..) => {
- record!(self.tables.kind[def_id.to_def_id()] <- EntryKind::Closure);
+ ty::Closure(_, substs) => {
+ record!(self.tables.fn_sig[def_id.to_def_id()] <- substs.as_closure().sig());
}
_ => bug!("closure that is neither generator nor closure"),
}
- self.encode_item_type(def_id.to_def_id());
- if let ty::Closure(def_id, substs) = *ty.kind() {
- record!(self.tables.fn_sig[def_id] <- substs.as_closure().sig());
- }
- }
-
- fn encode_info_for_anon_const(&mut self, id: hir::HirId) {
- let def_id = self.tcx.hir().local_def_id(id);
- debug!("EncodeContext::encode_info_for_anon_const({:?})", def_id);
- let body_id = self.tcx.hir().body_owned_by(def_id);
- let const_data = self.encode_rendered_const_for_body(body_id);
- let qualifs = self.tcx.mir_const_qualif(def_id);
-
- record!(self.tables.kind[def_id.to_def_id()] <- EntryKind::AnonConst);
- record!(self.tables.mir_const_qualif[def_id.to_def_id()] <- qualifs);
- record!(self.tables.rendered_const[def_id.to_def_id()] <- const_data);
- self.encode_item_type(def_id.to_def_id());
}
fn encode_native_libraries(&mut self) -> LazyArray<NativeLib> {
let def_id = id.to_def_id();
self.tables.opt_def_kind.set(def_id.index, DefKind::Macro(macro_kind));
- record!(self.tables.kind[def_id] <- EntryKind::ProcMacro(macro_kind));
+ self.tables.proc_macro.set(def_id.index, macro_kind);
self.encode_attrs(id);
record!(self.tables.def_keys[def_id] <- def_key);
record!(self.tables.def_ident_span[def_id] <- span);
hir::Constness::NotConst
};
self.tables.constness.set(def_id.index, constness);
- record!(self.tables.kind[def_id] <- EntryKind::ForeignFn);
- }
- hir::ForeignItemKind::Static(..) => {
- record!(self.tables.kind[def_id] <- EntryKind::ForeignStatic);
- }
- hir::ForeignItemKind::Type => {
- record!(self.tables.kind[def_id] <- EntryKind::ForeignType);
+ record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
}
+ hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => {}
}
- self.encode_item_type(def_id);
if let hir::ForeignItemKind::Fn(..) = nitem.kind {
- record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
if tcx.is_intrinsic(def_id) {
self.tables.is_intrinsic.set(def_id.index, ());
}
intravisit::walk_expr(self, ex);
self.encode_info_for_expr(ex);
}
- fn visit_anon_const(&mut self, c: &'tcx AnonConst) {
- intravisit::walk_anon_const(self, c);
- self.encode_info_for_anon_const(c.hir_id);
- }
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
intravisit::walk_item(self, item);
match item.kind {
hir::ItemKind::ExternCrate(_) | hir::ItemKind::Use(..) => {} // ignore these
_ => self.encode_info_for_item(item.def_id.to_def_id(), item),
}
- self.encode_addl_info_for_item(item);
}
fn visit_foreign_item(&mut self, ni: &'tcx hir::ForeignItem<'tcx>) {
intravisit::walk_foreign_item(self, ni);
}
impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
- fn encode_fields(&mut self, adt_def: ty::AdtDef<'tcx>) {
- for (variant_index, variant) in adt_def.variants().iter_enumerated() {
- for (field_index, _field) in variant.fields.iter().enumerate() {
- self.encode_field(adt_def, variant_index, field_index);
- }
- }
- }
-
fn encode_info_for_generics(&mut self, generics: &hir::Generics<'tcx>) {
for param in generics.params {
let def_id = self.tcx.hir().local_def_id(param.hir_id);
match param.kind {
- GenericParamKind::Lifetime { .. } => continue,
- GenericParamKind::Type { default, .. } => {
- self.encode_info_for_generic_param(
- def_id.to_def_id(),
- EntryKind::TypeParam,
- default.is_some(),
- );
- }
- GenericParamKind::Const { ref default, .. } => {
+ hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => {}
+ hir::GenericParamKind::Const { ref default, .. } => {
let def_id = def_id.to_def_id();
- self.encode_info_for_generic_param(def_id, EntryKind::ConstParam, true);
if default.is_some() {
record!(self.tables.const_param_default[def_id] <- self.tcx.const_param_default(def_id))
}
self.encode_info_for_closure(expr.hir_id);
}
}
-
- /// In some cases, along with the item itself, we also
- /// encode some sub-items. Usually we want some info from the item
- /// so it's easier to do that here then to wait until we would encounter
- /// normally in the visitor walk.
- fn encode_addl_info_for_item(&mut self, item: &hir::Item<'_>) {
- match item.kind {
- hir::ItemKind::Static(..)
- | hir::ItemKind::Const(..)
- | hir::ItemKind::Fn(..)
- | hir::ItemKind::Macro(..)
- | hir::ItemKind::Mod(..)
- | hir::ItemKind::ForeignMod { .. }
- | hir::ItemKind::GlobalAsm(..)
- | hir::ItemKind::ExternCrate(..)
- | hir::ItemKind::Use(..)
- | hir::ItemKind::TyAlias(..)
- | hir::ItemKind::OpaqueTy(..)
- | hir::ItemKind::TraitAlias(..) => {
- // no sub-item recording needed in these cases
- }
- hir::ItemKind::Enum(..) => {
- let def = self.tcx.adt_def(item.def_id.to_def_id());
- self.encode_fields(def);
-
- for (i, variant) in def.variants().iter_enumerated() {
- self.encode_enum_variant_info(def, i);
-
- if let Some(_ctor_def_id) = variant.ctor_def_id {
- self.encode_enum_variant_ctor(def, i);
- }
- }
- }
- hir::ItemKind::Struct(ref struct_def, _) => {
- let def = self.tcx.adt_def(item.def_id.to_def_id());
- self.encode_fields(def);
-
- // If the struct has a constructor, encode it.
- if let Some(ctor_hir_id) = struct_def.ctor_hir_id() {
- let ctor_def_id = self.tcx.hir().local_def_id(ctor_hir_id);
- self.encode_struct_ctor(def, ctor_def_id.to_def_id());
- }
- }
- hir::ItemKind::Union(..) => {
- let def = self.tcx.adt_def(item.def_id.to_def_id());
- self.encode_fields(def);
- }
- hir::ItemKind::Impl { .. } => {
- for &trait_item_def_id in
- self.tcx.associated_item_def_ids(item.def_id.to_def_id()).iter()
- {
- self.encode_info_for_impl_item(trait_item_def_id);
- }
- }
- hir::ItemKind::Trait(..) => {
- for &item_def_id in self.tcx.associated_item_def_ids(item.def_id.to_def_id()).iter()
- {
- self.encode_info_for_trait_item(item_def_id);
- }
- }
- }
- }
}
/// Used to prefetch queries which will be needed later by metadata encoding.
use rustc_middle::metadata::ModChild;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
+use rustc_middle::middle::resolve_lifetime::ObjectLifetimeDefault;
use rustc_middle::mir;
use rustc_middle::ty::fast_reject::SimplifiedType;
use rustc_middle::ty::query::Providers;
}
define_tables! {
- kind: Table<DefIndex, LazyValue<EntryKind>>,
attributes: Table<DefIndex, LazyArray<ast::Attribute>>,
children: Table<DefIndex, LazyArray<DefIndex>>,
codegen_fn_attrs: Table<DefIndex, LazyValue<CodegenFnAttrs>>,
impl_trait_ref: Table<DefIndex, LazyValue<ty::TraitRef<'static>>>,
const_param_default: Table<DefIndex, LazyValue<rustc_middle::ty::Const<'static>>>,
+ object_lifetime_default: Table<DefIndex, LazyValue<ObjectLifetimeDefault>>,
optimized_mir: Table<DefIndex, LazyValue<mir::Body<'static>>>,
mir_for_ctfe: Table<DefIndex, LazyValue<mir::Body<'static>>>,
promoted_mir: Table<DefIndex, LazyValue<IndexVec<mir::Promoted, mir::Body<'static>>>>,
proc_macro_quoted_spans: Table<usize, LazyValue<Span>>,
generator_diagnostic_data: Table<DefIndex, LazyValue<GeneratorDiagnosticData<'static>>>,
may_have_doc_links: Table<DefIndex, ()>,
-}
-
-#[derive(Copy, Clone, MetadataEncodable, MetadataDecodable)]
-enum EntryKind {
- AnonConst,
- Const,
- Static,
- ForeignStatic,
- ForeignMod,
- ForeignType,
- GlobalAsm,
- Type,
- TypeParam,
- ConstParam,
- OpaqueTy,
- Enum,
- Field,
- Variant(LazyValue<VariantData>),
- Struct(LazyValue<VariantData>),
- Union(LazyValue<VariantData>),
- Fn,
- ForeignFn,
- Mod(LazyArray<ModChild>),
- MacroDef(LazyValue<ast::MacArgs>, /*macro_rules*/ bool),
- ProcMacro(MacroKind),
- Closure,
- Generator,
- Trait,
- Impl,
- AssocFn { container: ty::AssocItemContainer, has_self: bool },
- AssocType(ty::AssocItemContainer),
- AssocConst(ty::AssocItemContainer),
- TraitAlias,
+ variant_data: Table<DefIndex, LazyValue<VariantData>>,
+ assoc_container: Table<DefIndex, ty::AssocItemContainer>,
+ // Slot is full when macro is macro_rules.
+ macro_rules: Table<DefIndex, ()>,
+ macro_definition: Table<DefIndex, LazyValue<ast::MacArgs>>,
+ proc_macro: Table<DefIndex, MacroKind>,
+ module_reexports: Table<DefIndex, LazyArray<ModChild>>,
}
#[derive(TyEncodable, TyDecodable)]
trivially_parameterized_over_tcx! {
VariantData,
- EntryKind,
RawDefId,
TraitImpls,
IncoherentImpls,
use std::convert::TryInto;
use std::marker::PhantomData;
use std::num::NonZeroUsize;
-use tracing::debug;
/// Helper trait, for encoding to, and decoding from, a fixed number of bytes.
/// Used mainly for Lazy positions and lengths.
}
match b[0] - 1 {
$(${index()} => Some($($pat)*),)*
- _ => panic!("Unexpected ImplPolarity code: {:?}", b[0]),
+ _ => panic!("Unexpected {} code: {:?}", stringify!($ty), b[0]),
}
}
}
}
+fixed_size_enum! {
+ ty::AssocItemContainer {
+ ( TraitContainer )
+ ( ImplContainer )
+ }
+}
+
+fixed_size_enum! {
+ MacroKind {
+ ( Attr )
+ ( Bang )
+ ( Derive )
+ }
+}
+
// We directly encode `DefPathHash` because a `LazyValue` would incur a 25% cost.
impl FixedSizeEncoding for Option<DefPathHash> {
type ByteArray = [u8; 16];
doctest = false
[dependencies]
-rustc_arena = { path = "../rustc_arena" }
bitflags = "1.2.1"
+chalk-ir = "0.80.0"
either = "1.5.0"
gsgdt = "0.1.2"
-tracing = "0.1"
-rustc-rayon = { version = "0.4.0", optional = true }
-rustc-rayon-core = { version = "0.4.0", optional = true }
polonius-engine = "0.13.0"
+rand = "0.8.4"
+rand_xoshiro = "0.6.0"
rustc_apfloat = { path = "../rustc_apfloat" }
+rustc_arena = { path = "../rustc_arena" }
+rustc_ast = { path = "../rustc_ast" }
rustc_attr = { path = "../rustc_attr" }
-rustc_feature = { path = "../rustc_feature" }
-rustc_hir = { path = "../rustc_hir" }
-rustc_target = { path = "../rustc_target" }
-rustc_macros = { path = "../rustc_macros" }
rustc_data_structures = { path = "../rustc_data_structures" }
-rustc_query_system = { path = "../rustc_query_system" }
rustc_errors = { path = "../rustc_errors" }
+rustc_feature = { path = "../rustc_feature" }
rustc_graphviz = { path = "../rustc_graphviz" }
+rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
+rustc_macros = { path = "../rustc_macros" }
+rustc_query_system = { path = "../rustc_query_system" }
+rustc-rayon-core = { version = "0.4.0", optional = true }
+rustc-rayon = { version = "0.4.0", optional = true }
rustc_serialize = { path = "../rustc_serialize" }
-rustc_ast = { path = "../rustc_ast" }
-rustc_span = { path = "../rustc_span" }
-chalk-ir = "0.80.0"
-smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
rustc_session = { path = "../rustc_session" }
+rustc_span = { path = "../rustc_span" }
+rustc_target = { path = "../rustc_target" }
rustc_type_ir = { path = "../rustc_type_ir" }
-rand = "0.8.4"
-rand_xoshiro = "0.6.0"
+smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
+thin-vec = "0.2.8"
+tracing = "0.1"
[features]
rustc_use_parallel_compiler = ["rustc-rayon", "rustc-rayon-core"]
let def_kind = self.tcx.def_kind(def_id);
match def_kind {
DefKind::Trait | DefKind::TraitAlias => def_id,
- DefKind::TyParam | DefKind::ConstParam => self.tcx.local_parent(def_id),
+ DefKind::LifetimeParam | DefKind::TyParam | DefKind::ConstParam => {
+ self.tcx.local_parent(def_id)
+ }
_ => bug!("ty_param_owner: {:?} is a {:?} not a type parameter", def_id, def_kind),
}
}
let def_kind = self.tcx.def_kind(def_id);
match def_kind {
DefKind::Trait | DefKind::TraitAlias => kw::SelfUpper,
- DefKind::TyParam | DefKind::ConstParam => self.tcx.item_name(def_id.to_def_id()),
+ DefKind::LifetimeParam | DefKind::TyParam | DefKind::ConstParam => {
+ self.tcx.item_name(def_id.to_def_id())
+ }
_ => bug!("ty_param_name: {:?} is a {:?} not a type parameter", def_id, def_kind),
}
}
#![feature(extern_types)]
#![feature(new_uninit)]
#![feature(once_cell)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(min_specialization)]
#![feature(trusted_len)]
/// Returns the `DefId` for a given `LangItem`.
/// If not found, fatally aborts compilation.
pub fn require_lang_item(self, lang_item: LangItem, span: Option<Span>) -> DefId {
- self.lang_items().require(lang_item).unwrap_or_else(|msg| {
+ self.lang_items().require(lang_item).unwrap_or_else(|err| {
if let Some(span) = span {
- self.sess.span_fatal(span, &msg)
+ self.sess.span_fatal(span, err.to_string())
} else {
- self.sess.fatal(&msg)
+ self.sess.fatal(err.to_string())
}
})
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug, HashStable)]
pub enum Region {
Static,
- EarlyBound(/* index */ u32, /* lifetime decl */ DefId),
+ EarlyBound(/* lifetime decl */ DefId),
LateBound(ty::DebruijnIndex, /* late-bound index */ u32, /* lifetime decl */ DefId),
Free(DefId, /* lifetime decl */ DefId),
}
}
}
-pub type ObjectLifetimeDefault = Set1<Region>;
+#[derive(Copy, Clone, Debug, HashStable, Encodable, Decodable)]
+pub enum ObjectLifetimeDefault {
+ Empty,
+ Static,
+ Ambiguous,
+ Param(DefId),
+}
/// Maps the id of each lifetime reference to the lifetime decl
/// that it corresponds to.
///
/// You will only ever need this if you have also called [`BasicBlocks::as_mut_preserves_cfg`].
/// All other methods that allow you to mutate the basic blocks also call this method
- /// themselves, thereby avoiding any risk of accidentaly cache invalidation.
+ /// themselves, thereby avoiding any risk of accidentally cache invalidation.
pub fn invalidate_cfg_cache(&mut self) {
self.predecessor_cache.invalidate();
self.switch_source_cache.invalidate();
/// The actual bytes of the allocation.
/// Note that the bytes of a pointer represent the offset of the pointer.
bytes: Box<[u8]>,
- /// Maps from byte addresses to extra data for each pointer.
+ /// Maps from byte addresses to extra provenance data for each pointer.
/// Only the first byte of a pointer is inserted into the map; i.e.,
/// every entry in this map applies to `pointer_size` consecutive bytes starting
/// at the given offset.
- relocations: Relocations<Prov>,
+ provenance: ProvenanceMap<Prov>,
/// Denotes which part of this allocation is initialized.
init_mask: InitMask,
/// The alignment of the allocation to detect unaligned reads.
}
// Hash the other fields as usual.
- self.relocations.hash(state);
+ self.provenance.hash(state);
self.init_mask.hash(state);
self.align.hash(state);
self.mutability.hash(state);
ReadPointerAsBytes,
/// Partially overwriting a pointer.
PartialPointerOverwrite(Size),
+ /// Partially copying a pointer.
+ PartialPointerCopy(Size),
/// Using uninitialized data where it is not allowed.
InvalidUninitBytes(Option<UninitBytesAccess>),
}
PartialPointerOverwrite(offset) => InterpError::Unsupported(
UnsupportedOpInfo::PartialPointerOverwrite(Pointer::new(alloc_id, offset)),
),
+ PartialPointerCopy(offset) => InterpError::Unsupported(
+ UnsupportedOpInfo::PartialPointerCopy(Pointer::new(alloc_id, offset)),
+ ),
InvalidUninitBytes(info) => InterpError::UndefinedBehavior(
UndefinedBehaviorInfo::InvalidUninitBytes(info.map(|b| (alloc_id, b))),
),
let size = Size::from_bytes(bytes.len());
Self {
bytes,
- relocations: Relocations::new(),
+ provenance: ProvenanceMap::new(),
init_mask: InitMask::new(size, true),
align,
mutability,
let bytes = unsafe { bytes.assume_init() };
Ok(Allocation {
bytes,
- relocations: Relocations::new(),
+ provenance: ProvenanceMap::new(),
init_mask: InitMask::new(size, false),
align,
mutability: Mutability::Mut,
) -> Result<Allocation<Prov, Extra>, Err> {
// Compute new pointer provenance, which also adjusts the bytes.
let mut bytes = self.bytes;
- let mut new_relocations = Vec::with_capacity(self.relocations.0.len());
+ let mut new_provenance = Vec::with_capacity(self.provenance.0.len());
let ptr_size = cx.data_layout().pointer_size.bytes_usize();
let endian = cx.data_layout().endian;
- for &(offset, alloc_id) in self.relocations.iter() {
+ for &(offset, alloc_id) in self.provenance.iter() {
let idx = offset.bytes_usize();
let ptr_bytes = &mut bytes[idx..idx + ptr_size];
let bits = read_target_uint(endian, ptr_bytes).unwrap();
let (ptr_prov, ptr_offset) =
adjust_ptr(Pointer::new(alloc_id, Size::from_bytes(bits)))?.into_parts();
write_target_uint(endian, ptr_bytes, ptr_offset.bytes().into()).unwrap();
- new_relocations.push((offset, ptr_prov));
+ new_provenance.push((offset, ptr_prov));
}
// Create allocation.
Ok(Allocation {
bytes,
- relocations: Relocations::from_presorted(new_relocations),
+ provenance: ProvenanceMap::from_presorted(new_provenance),
init_mask: self.init_mask,
align: self.align,
mutability: self.mutability,
Size::from_bytes(self.len())
}
- /// Looks at a slice which may describe uninitialized bytes or describe a relocation. This differs
- /// from `get_bytes_with_uninit_and_ptr` in that it does no relocation checks (even on the
+ /// Looks at a slice which may contain uninitialized bytes or provenance. This differs
+ /// from `get_bytes_with_uninit_and_ptr` in that it does no provenance checks (even on the
/// edges) at all.
/// This must not be used for reads affecting the interpreter execution.
pub fn inspect_with_uninit_and_ptr_outside_interpreter(&self, range: Range<usize>) -> &[u8] {
&self.init_mask
}
- /// Returns the relocation list.
- pub fn relocations(&self) -> &Relocations<Prov> {
- &self.relocations
+ /// Returns the provenance map.
+ pub fn provenance(&self) -> &ProvenanceMap<Prov> {
+ &self.provenance
}
}
/// Byte accessors.
impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
/// This is the entirely abstraction-violating way to just grab the raw bytes without
- /// caring about relocations. It just deduplicates some code between `read_scalar`
- /// and `get_bytes_internal`.
- fn get_bytes_even_more_internal(&self, range: AllocRange) -> &[u8] {
- &self.bytes[range.start.bytes_usize()..range.end().bytes_usize()]
- }
-
- /// The last argument controls whether we error out when there are uninitialized or pointer
- /// bytes. However, we *always* error when there are relocations overlapping the edges of the
- /// range.
- ///
- /// You should never call this, call `get_bytes` or `get_bytes_with_uninit_and_ptr` instead,
+ /// caring about provenance or initialization.
///
/// This function also guarantees that the resulting pointer will remain stable
/// even when new allocations are pushed to the `HashMap`. `mem_copy_repeatedly` relies
/// on that.
- ///
- /// It is the caller's responsibility to check bounds and alignment beforehand.
- fn get_bytes_internal(
- &self,
- cx: &impl HasDataLayout,
- range: AllocRange,
- check_init_and_ptr: bool,
- ) -> AllocResult<&[u8]> {
- if check_init_and_ptr {
- self.check_init(range)?;
- self.check_relocations(cx, range)?;
- } else {
- // We still don't want relocations on the *edges*.
- self.check_relocation_edges(cx, range)?;
- }
-
- Ok(self.get_bytes_even_more_internal(range))
+ #[inline]
+ pub fn get_bytes_unchecked(&self, range: AllocRange) -> &[u8] {
+ &self.bytes[range.start.bytes_usize()..range.end().bytes_usize()]
}
- /// Checks that these bytes are initialized and not pointer bytes, and then return them
- /// as a slice.
+ /// Checks that these bytes are initialized, and then strip provenance (if possible) and return
+ /// them.
///
/// It is the caller's responsibility to check bounds and alignment beforehand.
/// Most likely, you want to use the `PlaceTy` and `OperandTy`-based methods
/// on `InterpCx` instead.
#[inline]
- pub fn get_bytes(&self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult<&[u8]> {
- self.get_bytes_internal(cx, range, true)
- }
-
- /// It is the caller's responsibility to handle uninitialized and pointer bytes.
- /// However, this still checks that there are no relocations on the *edges*.
- ///
- /// It is the caller's responsibility to check bounds and alignment beforehand.
- #[inline]
- pub fn get_bytes_with_uninit_and_ptr(
+ pub fn get_bytes_strip_provenance(
&self,
cx: &impl HasDataLayout,
range: AllocRange,
) -> AllocResult<&[u8]> {
- self.get_bytes_internal(cx, range, false)
+ self.check_init(range)?;
+ if !Prov::OFFSET_IS_ADDR {
+ if self.range_has_provenance(cx, range) {
+ return Err(AllocError::ReadPointerAsBytes);
+ }
+ }
+ Ok(self.get_bytes_unchecked(range))
}
- /// Just calling this already marks everything as defined and removes relocations,
+ /// Just calling this already marks everything as defined and removes provenance,
/// so be sure to actually put data there!
///
/// It is the caller's responsibility to check bounds and alignment beforehand.
range: AllocRange,
) -> AllocResult<&mut [u8]> {
self.mark_init(range, true);
- self.clear_relocations(cx, range)?;
+ self.clear_provenance(cx, range)?;
Ok(&mut self.bytes[range.start.bytes_usize()..range.end().bytes_usize()])
}
range: AllocRange,
) -> AllocResult<*mut [u8]> {
self.mark_init(range, true);
- self.clear_relocations(cx, range)?;
+ self.clear_provenance(cx, range)?;
assert!(range.end().bytes_usize() <= self.bytes.len()); // need to do our own bounds-check
let begin_ptr = self.bytes.as_mut_ptr().wrapping_add(range.start.bytes_usize());
/// Reading and writing.
impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
- /// Validates that this memory range is initiailized and contains no relocations.
- pub fn check_bytes(&self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult {
- // This implicitly does all the checking we are asking for.
- self.get_bytes(cx, range)?;
- Ok(())
- }
-
/// Reads a *non-ZST* scalar.
///
/// If `read_provenance` is `true`, this will also read provenance; otherwise (if the machine
range: AllocRange,
read_provenance: bool,
) -> AllocResult<Scalar<Prov>> {
- if read_provenance {
- assert_eq!(range.size, cx.data_layout().pointer_size);
- }
-
// First and foremost, if anything is uninit, bail.
if self.is_init(range).is_err() {
return Err(AllocError::InvalidUninitBytes(None));
}
- // If we are doing a pointer read, and there is a relocation exactly where we
- // are reading, then we can put data and relocation back together and return that.
- if read_provenance && let Some(&prov) = self.relocations.get(&range.start) {
- // We already checked init and relocations, so we can use this function.
- let bytes = self.get_bytes_even_more_internal(range);
- let bits = read_target_uint(cx.data_layout().endian, bytes).unwrap();
- let ptr = Pointer::new(prov, Size::from_bytes(bits));
- return Ok(Scalar::from_pointer(ptr, cx));
- }
+ // Get the integer part of the result. We HAVE TO check provenance before returning this!
+ let bytes = self.get_bytes_unchecked(range);
+ let bits = read_target_uint(cx.data_layout().endian, bytes).unwrap();
- // If we are *not* reading a pointer, and we can just ignore relocations,
- // then do exactly that.
- if !read_provenance && Prov::OFFSET_IS_ADDR {
- // We just strip provenance.
- let bytes = self.get_bytes_even_more_internal(range);
- let bits = read_target_uint(cx.data_layout().endian, bytes).unwrap();
- return Ok(Scalar::from_uint(bits, range.size));
+ if read_provenance {
+ assert_eq!(range.size, cx.data_layout().pointer_size);
+
+ // When reading data with provenance, the easy case is finding provenance exactly where we
+ // are reading, then we can put data and provenance back together and return that.
+ if let Some(&prov) = self.provenance.get(&range.start) {
+ // Now we can return the bits, with their appropriate provenance.
+ let ptr = Pointer::new(prov, Size::from_bytes(bits));
+ return Ok(Scalar::from_pointer(ptr, cx));
+ }
+
+ // If we can work on pointers byte-wise, join the byte-wise provenances.
+ if Prov::OFFSET_IS_ADDR {
+ let mut prov = self.offset_get_provenance(cx, range.start);
+ for offset in 1..range.size.bytes() {
+ let this_prov =
+ self.offset_get_provenance(cx, range.start + Size::from_bytes(offset));
+ prov = Prov::join(prov, this_prov);
+ }
+ // Now use this provenance.
+ let ptr = Pointer::new(prov, Size::from_bytes(bits));
+ return Ok(Scalar::from_maybe_pointer(ptr, cx));
+ }
+ } else {
+ // We are *not* reading a pointer.
+ // If we can just ignore provenance, do exactly that.
+ if Prov::OFFSET_IS_ADDR {
+ // We just strip provenance.
+ return Ok(Scalar::from_uint(bits, range.size));
+ }
}
- // It's complicated. Better make sure there is no provenance anywhere.
- // FIXME: If !OFFSET_IS_ADDR, this is the best we can do. But if OFFSET_IS_ADDR, then
- // `read_pointer` is true and we ideally would distinguish the following two cases:
- // - The entire `range` is covered by 2 relocations for the same provenance.
- // Then we should return a pointer with that provenance.
- // - The range has inhomogeneous provenance. Then we should return just the
- // underlying bits.
- let bytes = self.get_bytes(cx, range)?;
- let bits = read_target_uint(cx.data_layout().endian, bytes).unwrap();
+ // Fallback path for when we cannot treat provenance bytewise or ignore it.
+ assert!(!Prov::OFFSET_IS_ADDR);
+ if self.range_has_provenance(cx, range) {
+ return Err(AllocError::ReadPointerAsBytes);
+ }
+ // There is no provenance, we can just return the bits.
Ok(Scalar::from_uint(bits, range.size))
}
let dst = self.get_bytes_mut(cx, range)?;
write_target_uint(endian, dst, bytes).unwrap();
- // See if we have to also write a relocation.
+ // See if we have to also store some provenance.
if let Some(provenance) = provenance {
- self.relocations.0.insert(range.start, provenance);
+ self.provenance.0.insert(range.start, provenance);
}
Ok(())
/// Write "uninit" to the given memory range.
pub fn write_uninit(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult {
self.mark_init(range, false);
- self.clear_relocations(cx, range)?;
+ self.clear_provenance(cx, range)?;
return Ok(());
}
}
-/// Relocations.
+/// Provenance.
impl<Prov: Copy, Extra> Allocation<Prov, Extra> {
- /// Returns all relocations overlapping with the given pointer-offset pair.
- fn get_relocations(&self, cx: &impl HasDataLayout, range: AllocRange) -> &[(Size, Prov)] {
+ /// Returns all provenance overlapping with the given pointer-offset pair.
+ fn range_get_provenance(&self, cx: &impl HasDataLayout, range: AllocRange) -> &[(Size, Prov)] {
// We have to go back `pointer_size - 1` bytes, as that one would still overlap with
// the beginning of this range.
let start = range.start.bytes().saturating_sub(cx.data_layout().pointer_size.bytes() - 1);
- self.relocations.range(Size::from_bytes(start)..range.end())
+ self.provenance.range(Size::from_bytes(start)..range.end())
}
- /// Returns whether this allocation has relocations overlapping with the given range.
- ///
- /// Note: this function exists to allow `get_relocations` to be private, in order to somewhat
- /// limit access to relocations outside of the `Allocation` abstraction.
- ///
- pub fn has_relocations(&self, cx: &impl HasDataLayout, range: AllocRange) -> bool {
- !self.get_relocations(cx, range).is_empty()
+ /// Get the provenance of a single byte.
+ fn offset_get_provenance(&self, cx: &impl HasDataLayout, offset: Size) -> Option<Prov> {
+ let prov = self.range_get_provenance(cx, alloc_range(offset, Size::from_bytes(1)));
+ assert!(prov.len() <= 1);
+ prov.first().map(|(_offset, prov)| *prov)
}
- /// Checks that there are no relocations overlapping with the given range.
- #[inline(always)]
- fn check_relocations(&self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult {
- if self.has_relocations(cx, range) { Err(AllocError::ReadPointerAsBytes) } else { Ok(()) }
+ /// Returns whether this allocation has progrnance overlapping with the given range.
+ ///
+ /// Note: this function exists to allow `range_get_provenance` to be private, in order to somewhat
+ /// limit access to provenance outside of the `Allocation` abstraction.
+ ///
+ pub fn range_has_provenance(&self, cx: &impl HasDataLayout, range: AllocRange) -> bool {
+ !self.range_get_provenance(cx, range).is_empty()
}
- /// Removes all relocations inside the given range.
- /// If there are relocations overlapping with the edges, they
+ /// Removes all provenance inside the given range.
+ /// If there is provenance overlapping with the edges, it
/// are removed as well *and* the bytes they cover are marked as
/// uninitialized. This is a somewhat odd "spooky action at a distance",
/// but it allows strictly more code to run than if we would just error
/// immediately in that case.
- fn clear_relocations(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult
+ fn clear_provenance(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult
where
Prov: Provenance,
{
- // Find the start and end of the given range and its outermost relocations.
+ // Find the start and end of the given range and its outermost provenance.
let (first, last) = {
- // Find all relocations overlapping the given range.
- let relocations = self.get_relocations(cx, range);
- if relocations.is_empty() {
+ // Find all provenance overlapping the given range.
+ let provenance = self.range_get_provenance(cx, range);
+ if provenance.is_empty() {
return Ok(());
}
(
- relocations.first().unwrap().0,
- relocations.last().unwrap().0 + cx.data_layout().pointer_size,
+ provenance.first().unwrap().0,
+ provenance.last().unwrap().0 + cx.data_layout().pointer_size,
)
};
let start = range.start;
let end = range.end();
- // We need to handle clearing the relocations from parts of a pointer.
- // FIXME: Miri should preserve partial relocations; see
+ // We need to handle clearing the provenance from parts of a pointer.
+ // FIXME: Miri should preserve partial provenance; see
// https://github.com/rust-lang/miri/issues/2181.
if first < start {
if Prov::ERR_ON_PARTIAL_PTR_OVERWRITE {
self.init_mask.set_range(end, last, false);
}
- // Forget all the relocations.
- // Since relocations do not overlap, we know that removing until `last` (exclusive) is fine,
- // i.e., this will not remove any other relocations just after the ones we care about.
- self.relocations.0.remove_range(first..last);
-
- Ok(())
- }
+ // Forget all the provenance.
+ // Since provenance do not overlap, we know that removing until `last` (exclusive) is fine,
+ // i.e., this will not remove any other provenance just after the ones we care about.
+ self.provenance.0.remove_range(first..last);
- /// Errors if there are relocations overlapping with the edges of the
- /// given memory range.
- #[inline]
- fn check_relocation_edges(&self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult {
- self.check_relocations(cx, alloc_range(range.start, Size::ZERO))?;
- self.check_relocations(cx, alloc_range(range.end(), Size::ZERO))?;
Ok(())
}
}
-/// "Relocations" stores the provenance information of pointers stored in memory.
+/// Stores the provenance information of pointers stored in memory.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
-pub struct Relocations<Prov = AllocId>(SortedMap<Size, Prov>);
+pub struct ProvenanceMap<Prov = AllocId>(SortedMap<Size, Prov>);
-impl<Prov> Relocations<Prov> {
+impl<Prov> ProvenanceMap<Prov> {
pub fn new() -> Self {
- Relocations(SortedMap::new())
+ ProvenanceMap(SortedMap::new())
}
- // The caller must guarantee that the given relocations are already sorted
+ // The caller must guarantee that the given provenance list is already sorted
// by address and contain no duplicates.
pub fn from_presorted(r: Vec<(Size, Prov)>) -> Self {
- Relocations(SortedMap::from_presorted_elements(r))
+ ProvenanceMap(SortedMap::from_presorted_elements(r))
}
}
-impl<Prov> Deref for Relocations<Prov> {
+impl<Prov> Deref for ProvenanceMap<Prov> {
type Target = SortedMap<Size, Prov>;
fn deref(&self) -> &Self::Target {
}
}
-/// A partial, owned list of relocations to transfer into another allocation.
+/// A partial, owned list of provenance to transfer into another allocation.
///
/// Offsets are already adjusted to the destination allocation.
-pub struct AllocationRelocations<Prov> {
- dest_relocations: Vec<(Size, Prov)>,
+pub struct AllocationProvenance<Prov> {
+ dest_provenance: Vec<(Size, Prov)>,
}
impl<Prov: Copy, Extra> Allocation<Prov, Extra> {
- pub fn prepare_relocation_copy(
+ pub fn prepare_provenance_copy(
&self,
cx: &impl HasDataLayout,
src: AllocRange,
dest: Size,
count: u64,
- ) -> AllocationRelocations<Prov> {
- let relocations = self.get_relocations(cx, src);
- if relocations.is_empty() {
- return AllocationRelocations { dest_relocations: Vec::new() };
+ ) -> AllocationProvenance<Prov> {
+ let provenance = self.range_get_provenance(cx, src);
+ if provenance.is_empty() {
+ return AllocationProvenance { dest_provenance: Vec::new() };
}
let size = src.size;
- let mut new_relocations = Vec::with_capacity(relocations.len() * (count as usize));
+ let mut new_provenance = Vec::with_capacity(provenance.len() * (count as usize));
// If `count` is large, this is rather wasteful -- we are allocating a big array here, which
// is mostly filled with redundant information since it's just N copies of the same `Prov`s
- // at slightly adjusted offsets. The reason we do this is so that in `mark_relocation_range`
+ // at slightly adjusted offsets. The reason we do this is so that in `mark_provenance_range`
// we can use `insert_presorted`. That wouldn't work with an `Iterator` that just produces
- // the right sequence of relocations for all N copies.
+ // the right sequence of provenance for all N copies.
for i in 0..count {
- new_relocations.extend(relocations.iter().map(|&(offset, reloc)| {
+ new_provenance.extend(provenance.iter().map(|&(offset, reloc)| {
// compute offset for current repetition
let dest_offset = dest + size * i; // `Size` operations
(
}));
}
- AllocationRelocations { dest_relocations: new_relocations }
+ AllocationProvenance { dest_provenance: new_provenance }
}
- /// Applies a relocation copy.
- /// The affected range, as defined in the parameters to `prepare_relocation_copy` is expected
- /// to be clear of relocations.
+ /// Applies a provenance copy.
+ /// The affected range, as defined in the parameters to `prepare_provenance_copy` is expected
+ /// to be clear of provenance.
///
/// This is dangerous to use as it can violate internal `Allocation` invariants!
/// It only exists to support an efficient implementation of `mem_copy_repeatedly`.
- pub fn mark_relocation_range(&mut self, relocations: AllocationRelocations<Prov>) {
- self.relocations.0.insert_presorted(relocations.dest_relocations);
+ pub fn mark_provenance_range(&mut self, provenance: AllocationProvenance<Prov>) {
+ self.provenance.0.insert_presorted(provenance.dest_provenance);
}
}
pub enum UnsupportedOpInfo {
/// Free-form case. Only for errors that are never caught!
Unsupported(String),
- /// Encountered a pointer where we needed raw bytes.
- ReadPointerAsBytes,
/// Overwriting parts of a pointer; the resulting state cannot be represented in our
/// `Allocation` data structure. See <https://github.com/rust-lang/miri/issues/2181>.
PartialPointerOverwrite(Pointer<AllocId>),
+ /// Attempting to `copy` parts of a pointer to somewhere else; the resulting state cannot be
+ /// represented in our `Allocation` data structure. See
+ /// <https://github.com/rust-lang/miri/issues/2181>.
+ PartialPointerCopy(Pointer<AllocId>),
//
// The variants below are only reachable from CTFE/const prop, miri will never emit them.
//
+ /// Encountered a pointer where we needed raw bytes.
+ ReadPointerAsBytes,
/// Accessing thread local statics
ThreadLocalStatic(DefId),
/// Accessing an unsupported extern static.
use UnsupportedOpInfo::*;
match self {
Unsupported(ref msg) => write!(f, "{msg}"),
- ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes"),
PartialPointerOverwrite(ptr) => {
write!(f, "unable to overwrite parts of a pointer in memory at {ptr:?}")
}
+ PartialPointerCopy(ptr) => {
+ write!(f, "unable to copy parts of a pointer from memory at {ptr:?}")
+ }
+ ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes"),
ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({did:?})"),
ReadExternStatic(did) => write!(f, "cannot read from extern static ({did:?})"),
}
pub use self::allocation::{
alloc_range, AllocRange, Allocation, ConstAllocation, InitChunk, InitChunkIter, InitMask,
- Relocations,
+ ProvenanceMap,
};
pub use self::pointer::{Pointer, PointerArithmetic, Provenance};
/// pointer), but `derive` adds some unnecessary bounds.
pub trait Provenance: Copy + fmt::Debug {
/// Says whether the `offset` field of `Pointer`s with this provenance is the actual physical address.
- /// If `true, ptr-to-int casts work by simply discarding the provenance.
- /// If `false`, ptr-to-int casts are not supported. The offset *must* be relative in that case.
+ /// - If `false`, the offset *must* be relative. This means the bytes representing a pointer are
+ /// different from what the Abstract Machine prescribes, so the interpreter must prevent any
+ /// operation that would inspect the underlying bytes of a pointer, such as ptr-to-int
+ /// transmutation. A `ReadPointerAsBytes` error will be raised in such situations.
+ /// - If `true`, the interpreter will permit operations to inspect the underlying bytes of a
+ /// pointer, and implement ptr-to-int transmutation by stripping provenance.
const OFFSET_IS_ADDR: bool;
/// We also use this trait to control whether to abort execution when a pointer is being partially overwritten
/// Otherwise this function is best-effort (but must agree with `Machine::ptr_get_alloc`).
/// (Identifying the offset in that allocation, however, is harder -- use `Memory::ptr_get_alloc` for that.)
fn get_alloc_id(self) -> Option<AllocId>;
+
+ /// Defines the 'join' of provenance: what happens when doing a pointer load and different bytes have different provenance.
+ fn join(left: Option<Self>, right: Option<Self>) -> Option<Self>;
}
impl Provenance for AllocId {
fn get_alloc_id(self) -> Option<AllocId> {
Some(self)
}
+
+ fn join(_left: Option<Self>, _right: Option<Self>) -> Option<Self> {
+ panic!("merging provenance is not supported when `OFFSET_IS_ADDR` is false")
+ }
}
/// Represents a pointer in the Miri engine.
/// The raw bytes of a simple value.
Int(ScalarInt),
- /// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of
- /// relocations, but a `Scalar` is only large enough to contain one, so we just represent the
- /// relocation and its associated offset together as a `Pointer` here.
+ /// A pointer.
///
/// We also store the size of the pointer, such that a `Scalar` always knows how big it is.
/// The size is always the pointer size of the current target, but this is not information
if let ConstValue::Slice { data, start, end } = val {
let len = end - start;
data.inner()
- .get_bytes(
+ .get_bytes_strip_provenance(
cx,
AllocRange { start: Size::from_bytes(start), size: Size::from_bytes(len) },
)
impl MirPhase {
/// Gets the index of the current MirPhase within the set of all `MirPhase`s.
+ ///
+ /// FIXME(JakobDegen): Return a `(usize, usize)` instead.
pub fn phase_index(&self) -> usize {
- *self as usize
+ const BUILT_PHASE_COUNT: usize = 1;
+ const ANALYSIS_PHASE_COUNT: usize = 2;
+ match self {
+ MirPhase::Built => 1,
+ MirPhase::Analysis(analysis_phase) => {
+ 1 + BUILT_PHASE_COUNT + (*analysis_phase as usize)
+ }
+ MirPhase::Runtime(runtime_phase) => {
+ 1 + BUILT_PHASE_COUNT + ANALYSIS_PHASE_COUNT + (*runtime_phase as usize)
+ }
+ }
}
}
// Once we stop implementing `Ord` for `DefId`,
// this impl will be unnecessary. Until then, we'll
// leave this impl in place to prevent re-adding a
-// dependnecy on the `Ord` impl for `DefId`
+// dependency on the `Ord` impl for `DefId`
impl<'tcx> !PartialOrd for PlaceRef<'tcx> {}
impl<'tcx> Place<'tcx> {
Self::from_opt_const_arg_anon_const(tcx, ty::WithOptConstParam::unknown(def_id), param_env)
}
- #[instrument(skip(tcx), level = "debug")]
+ #[instrument(skip(tcx), level = "debug", ret)]
pub fn from_inline_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let body_id = match tcx.hir().get(hir_id) {
let substs =
ty::InlineConstSubsts::new(tcx, ty::InlineConstSubstsParts { parent_substs, ty })
.substs;
- let uneval_const = tcx.mk_const(ty::ConstS {
+ debug_assert!(!substs.has_free_regions());
+ Self::Ty(tcx.mk_const(ty::ConstS {
kind: ty::ConstKind::Unevaluated(ty::Unevaluated {
def: ty::WithOptConstParam::unknown(def_id).to_global(),
substs,
promoted: None,
}),
ty,
- });
- debug!(?uneval_const);
- debug_assert!(!uneval_const.has_free_regions());
-
- Self::Ty(uneval_const)
+ }))
}
- #[instrument(skip(tcx), level = "debug")]
+ #[instrument(skip(tcx), level = "debug", ret)]
fn from_opt_const_arg_anon_const(
tcx: TyCtxt<'tcx>,
def: ty::WithOptConstParam<LocalDefId>,
match tcx.const_eval_resolve(param_env, uneval, Some(span)) {
Ok(val) => {
- debug!("evaluated const value: {:?}", val);
+ debug!("evaluated const value");
Self::Val(val, ty)
}
Err(_) => {
debug!("error encountered during evaluation");
// Error was handled in `const_eval_resolve`. Here we just create a
// new unevaluated const and error hard later in codegen
- let ty_const = tcx.mk_const(ty::ConstS {
+ Self::Ty(tcx.mk_const(ty::ConstS {
kind: ty::ConstKind::Unevaluated(ty::Unevaluated {
def: def.to_global(),
substs: InternalSubsts::identity_for_item(tcx, def.did.to_def_id()),
promoted: None,
}),
ty,
- });
- debug!(?ty_const);
-
- Self::Ty(ty_const)
+ }))
}
}
}
match inner.kind() {
ty::Slice(t) => {
if *t == u8_type {
- // The `inspect` here is okay since we checked the bounds, and there are
- // no relocations (we have an active slice reference here). We don't use
+ // The `inspect` here is okay since we checked the bounds, and `u8` carries
+ // no provenance (we have an active slice reference here). We don't use
// this result to affect interpreter execution.
let byte_str = data
.inner()
}
}
ty::Str => {
- // The `inspect` here is okay since we checked the bounds, and there are no
- // relocations (we have an active `str` reference here). We don't use this
+ // The `inspect` here is okay since we checked the bounds, and `str` carries
+ // no provenance (we have an active `str` reference here). We don't use this
// result to affect interpreter execution.
let slice = data
.inner()
let n = n.kind().try_to_bits(tcx.data_layout.pointer_size).unwrap();
// cast is ok because we already checked for pointer size (32 or 64 bit) above
let range = AllocRange { start: offset, size: Size::from_bytes(n) };
- let byte_str = alloc.inner().get_bytes(&tcx, range).unwrap();
+ let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap();
fmt.write_str("*")?;
pretty_print_byte_str(fmt, byte_str)?;
return Ok(());
fn alloc_ids_from_alloc(
alloc: ConstAllocation<'_>,
) -> impl DoubleEndedIterator<Item = AllocId> + '_ {
- alloc.inner().relocations().values().map(|id| *id)
+ alloc.inner().provenance().values().map(|id| *id)
}
fn alloc_ids_from_const_val(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ {
/// If the allocation is small enough to fit into a single line, no start address is given.
/// After the hex dump, an ascii dump follows, replacing all unprintable characters (control
/// characters or characters whose value is larger than 127) with a `.`
-/// This also prints relocations adequately.
+/// This also prints provenance adequately.
pub fn display_allocation<'a, 'tcx, Prov, Extra>(
tcx: TyCtxt<'tcx>,
alloc: &'a Allocation<Prov, Extra>,
if i != line_start {
write!(w, " ")?;
}
- if let Some(&prov) = alloc.relocations().get(&i) {
- // Memory with a relocation must be defined
+ if let Some(&prov) = alloc.provenance().get(&i) {
+ // Memory with provenance must be defined
assert!(alloc.init_mask().is_range_initialized(i, i + ptr_size).is_ok());
let j = i.bytes_usize();
let offset = alloc
.inspect_with_uninit_and_ptr_outside_interpreter(j..j + ptr_size.bytes_usize());
let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap();
let offset = Size::from_bytes(offset);
- let relocation_width = |bytes| bytes * 3;
+ let provenance_width = |bytes| bytes * 3;
let ptr = Pointer::new(prov, offset);
let mut target = format!("{:?}", ptr);
- if target.len() > relocation_width(ptr_size.bytes_usize() - 1) {
+ if target.len() > provenance_width(ptr_size.bytes_usize() - 1) {
// This is too long, try to save some space.
target = format!("{:#?}", ptr);
}
if ((i - line_start) + ptr_size).bytes_usize() > BYTES_PER_LINE {
- // This branch handles the situation where a relocation starts in the current line
+ // This branch handles the situation where a provenance starts in the current line
// but ends in the next one.
let remainder = Size::from_bytes(BYTES_PER_LINE) - (i - line_start);
let overflow = ptr_size - remainder;
- let remainder_width = relocation_width(remainder.bytes_usize()) - 2;
- let overflow_width = relocation_width(overflow.bytes_usize() - 1) + 1;
+ let remainder_width = provenance_width(remainder.bytes_usize()) - 2;
+ let overflow_width = provenance_width(overflow.bytes_usize() - 1) + 1;
ascii.push('╾');
for _ in 0..remainder.bytes() - 1 {
ascii.push('─');
}
if overflow_width > remainder_width && overflow_width >= target.len() {
- // The case where the relocation fits into the part in the next line
+ // The case where the provenance fits into the part in the next line
write!(w, "╾{0:─^1$}", "", remainder_width)?;
line_start =
write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
i += ptr_size;
continue;
} else {
- // This branch handles a relocation that starts and ends in the current line.
- let relocation_width = relocation_width(ptr_size.bytes_usize() - 1);
- oversized_ptr(&mut target, relocation_width);
+ // This branch handles a provenance that starts and ends in the current line.
+ let provenance_width = provenance_width(ptr_size.bytes_usize() - 1);
+ oversized_ptr(&mut target, provenance_width);
ascii.push('╾');
- write!(w, "╾{0:─^1$}╼", target, relocation_width)?;
+ write!(w, "╾{0:─^1$}╼", target, provenance_width)?;
for _ in 0..ptr_size.bytes() - 2 {
ascii.push('─');
}
} else if alloc.init_mask().is_range_initialized(i, i + Size::from_bytes(1)).is_ok() {
let j = i.bytes_usize();
- // Checked definedness (and thus range) and relocations. This access also doesn't
+ // Checked definedness (and thus range) and provenance. This access also doesn't
// influence interpreter execution but is only for debugging.
let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0];
write!(w, "{:02x}", c)?;
use rustc_span::Span;
use rustc_target::asm::InlineAsmRegOrRegClass;
-/// The various "big phases" that MIR goes through.
+/// Represents the "flavors" of MIR.
///
-/// These phases all describe dialects of MIR. Since all MIR uses the same datastructures, the
-/// dialects forbid certain variants or values in certain phases. The sections below summarize the
-/// changes, but do not document them thoroughly. The full documentation is found in the appropriate
-/// documentation for the thing the change is affecting.
+/// All flavors of MIR use the same data structure, but there are some important differences. These
+/// differences come in two forms: Dialects and phases.
///
-/// Warning: ordering of variants is significant.
+/// Dialects represent a stronger distinction than phases. This is because the transitions between
+/// dialects are semantic changes, and therefore technically *lowerings* between distinct IRs. In
+/// other words, the same [`Body`](crate::mir::Body) might be well-formed for multiple dialects, but
+/// have different semantic meaning and different behavior at runtime.
+///
+/// Each dialect additionally has a number of phases. However, phase changes never involve semantic
+/// changes. If some MIR is well-formed both before and after a phase change, it is also guaranteed
+/// that it has the same semantic meaning. In this sense, phase changes can only add additional
+/// restrictions on what MIR is well-formed.
+///
+/// When adding phases, remember to update [`MirPhase::phase_index`].
#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(HashStable)]
pub enum MirPhase {
- /// The dialect of MIR used during all phases before `DropsLowered` is the same. This is also
- /// the MIR that analysis such as borrowck uses.
- ///
- /// One important thing to remember about the behavior of this section of MIR is that drop terminators
- /// (including drop and replace) are *conditional*. The elaborate drops pass will then replace each
- /// instance of a drop terminator with a nop, an unconditional drop, or a drop conditioned on a drop
- /// flag. Of course, this means that it is important that the drop elaboration can accurately recognize
- /// when things are initialized and when things are de-initialized. That means any code running on this
- /// version of MIR must be sure to produce output that drop elaboration can reason about. See the
- /// section on the drop terminatorss for more details.
- Built = 0,
- // FIXME(oli-obk): it's unclear whether we still need this phase (and its corresponding query).
- // We used to have this for pre-miri MIR based const eval.
- Const = 1,
- /// This phase checks the MIR for promotable elements and takes them out of the main MIR body
- /// by creating a new MIR body per promoted element. After this phase (and thus the termination
- /// of the `mir_promoted` query), these promoted elements are available in the `promoted_mir`
- /// query.
- ConstsPromoted = 2,
- /// After this projections may only contain deref projections as the first element.
- Derefered = 3,
- /// Beginning with this phase, the following variants are disallowed:
- /// * [`TerminatorKind::DropAndReplace`]
+ /// The MIR that is generated by MIR building.
+ ///
+ /// The only things that operate on this dialect are unsafeck, the various MIR lints, and const
+ /// qualifs.
+ ///
+ /// This has no distinct phases.
+ Built,
+ /// The MIR used for most analysis.
+ ///
+ /// The only semantic change between analysis and built MIR is constant promotion. In built MIR,
+ /// sequences of statements that would generally be subject to constant promotion are
+ /// semantically constants, while in analysis MIR all constants are explicit.
+ ///
+ /// The result of const promotion is available from the `mir_promoted` and `promoted_mir` queries.
+ ///
+ /// This is the version of MIR used by borrowck and friends.
+ Analysis(AnalysisPhase),
+ /// The MIR used for CTFE, optimizations, and codegen.
+ ///
+ /// The semantic changes that occur in the lowering from analysis to runtime MIR are as follows:
+ ///
+ /// - Drops: In analysis MIR, `Drop` terminators represent *conditional* drops; roughly speaking,
+ /// if dataflow analysis determines that the place being dropped is uninitialized, the drop will
+ /// not be executed. The exact semantics of this aren't written down anywhere, which means they
+ /// are essentially "what drop elaboration does." In runtime MIR, the drops are unconditional;
+ /// when a `Drop` terminator is reached, if the type has drop glue that drop glue is always
+ /// executed. This may be UB if the underlying place is not initialized.
+ /// - Packed drops: Places might in general be misaligned - in most cases this is UB, the exception
+ /// is fields of packed structs. In analysis MIR, `Drop(P)` for a `P` that might be misaligned
+ /// for this reason implicitly moves `P` to a temporary before dropping. Runtime MIR has no such
+ /// rules, and dropping a misaligned place is simply UB.
+ /// - Unwinding: in analysis MIR, unwinding from a function which may not unwind aborts. In runtime
+ /// MIR, this is UB.
+ /// - Retags: If `-Zmir-emit-retag` is enabled, analysis MIR has "implicit" retags in the same way
+ /// that Rust itself has them. Where exactly these are is generally subject to change, and so we
+ /// don't document this here. Runtime MIR has all retags explicit.
+ /// - Generator bodies: In analysis MIR, locals may actually be behind a pointer that user code has
+ /// access to. This occurs in generator bodies. Such locals do not behave like other locals,
+ /// because they eg may be aliased in surprising ways. Runtime MIR has no such special locals -
+ /// all generator bodies are lowered and so all places that look like locals really are locals.
+ /// - Const prop lints: The lint pass which reports eg `200_u8 + 200_u8` as an error is run as a
+ /// part of analysis to runtime MIR lowering. This means that transformations which may supress
+ /// such errors may not run on analysis MIR.
+ Runtime(RuntimePhase),
+}
+
+/// See [`MirPhase::Analysis`].
+#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(HashStable)]
+pub enum AnalysisPhase {
+ Initial = 0,
+ /// Beginning in this phase, the following variants are disallowed:
/// * [`TerminatorKind::FalseUnwind`]
/// * [`TerminatorKind::FalseEdge`]
/// * [`StatementKind::FakeRead`]
/// * [`StatementKind::AscribeUserType`]
/// * [`Rvalue::Ref`] with `BorrowKind::Shallow`
///
- /// And the following variant is allowed:
- /// * [`StatementKind::Retag`]
- ///
- /// Furthermore, `Drop` now uses explicit drop flags visible in the MIR and reaching a `Drop`
- /// terminator means that the auto-generated drop glue will be invoked. Also, `Copy` operands
- /// are allowed for non-`Copy` types.
- DropsLowered = 4,
- /// Beginning with this phase, the following variant is disallowed:
+ /// Furthermore, `Deref` projections must be the first projection within any place (if they
+ /// appear at all)
+ PostCleanup = 1,
+}
+
+/// See [`MirPhase::Runtime`].
+#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(HashStable)]
+pub enum RuntimePhase {
+ /// In addition to the semantic changes, beginning with this phase, the following variants are
+ /// disallowed:
+ /// * [`TerminatorKind::DropAndReplace`]
+ /// * [`TerminatorKind::Yield`]
+ /// * [`TerminatorKind::GeneratorDrop`]
/// * [`Rvalue::Aggregate`] for any `AggregateKind` except `Array`
///
- /// And the following variant is allowed:
+ /// And the following variants are allowed:
+ /// * [`StatementKind::Retag`]
/// * [`StatementKind::SetDiscriminant`]
- Deaggregated = 5,
- /// Before this phase, generators are in the "source code" form, featuring `yield` statements
- /// and such. With this phase change, they are transformed into a proper state machine. Running
- /// optimizations before this change can be potentially dangerous because the source code is to
- /// some extent a "lie." In particular, `yield` terminators effectively make the value of all
- /// locals visible to the caller. This means that dead store elimination before them, or code
- /// motion across them, is not correct in general. This is also exasperated by type checking
- /// having pre-computed a list of the types that it thinks are ok to be live across a yield
- /// point - this is necessary to decide eg whether autotraits are implemented. Introducing new
- /// types across a yield point will lead to ICEs becaues of this.
- ///
- /// Beginning with this phase, the following variants are disallowed:
- /// * [`TerminatorKind::Yield`]
- /// * [`TerminatorKind::GeneratorDrop`]
+ /// * [`StatementKind::Deinit`]
+ ///
+ /// Furthermore, `Copy` operands are allowed for non-`Copy` types.
+ Initial = 0,
+ /// Beginning with this phase, the following variant is disallowed:
/// * [`ProjectionElem::Deref`] of `Box`
- GeneratorsLowered = 6,
- Optimized = 7,
+ PostCleanup = 1,
+ Optimized = 2,
}
///////////////////////////////////////////////////////////////////////////
/// First, all three operands are evaluated. `src` and `dest` must each be a reference, pointer,
/// or `Box` pointing to the same type `T`. `count` must evaluate to a `usize`. Then, `src` and
/// `dest` are dereferenced, and `count * size_of::<T>()` bytes beginning with the first byte of
- /// the `src` place are copied to the continguous range of bytes beginning with the first byte
+ /// the `src` place are copied to the contiguous range of bytes beginning with the first byte
/// of `dest`.
///
/// **Needs clarification**: In what order are operands computed and dereferenced? It should
/// Some(closure_def_id).
/// Otherwise, the value of the optional LocalDefId will be None.
//
- // We can use LocaDefId here since fake read statements are removed
+ // We can use LocalDefId here since fake read statements are removed
// before codegen in the `CleanupNonCodegenStatements` pass.
ForMatchedPlace(Option<LocalDefId>),
/// Used by rustdoc.
query rendered_const(def_id: DefId) -> String {
storage(ArenaCacheSelector<'tcx>)
- desc { |tcx| "rendering constant intializer of `{}`", tcx.def_path_str(def_id) }
+ desc { |tcx| "rendering constant initializer of `{}`", tcx.def_path_str(def_id) }
cache_on_disk_if { def_id.is_local() }
separate_provide_extern
}
/// 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(_: LocalDefId) -> Option<&'tcx [ObjectLifetimeDefault]> {
- desc { "looking up lifetime defaults for a region on an item" }
+ query object_lifetime_default(key: DefId) -> Option<ObjectLifetimeDefault> {
+ desc { "looking up lifetime defaults for generic parameter `{:?}`", key }
+ separate_provide_extern
}
query late_bound_vars_map(_: LocalDefId)
-> Option<&'tcx FxHashMap<ItemLocalId, Vec<ty::BoundVariableKind>>> {
matches!(self, Node::Trait(..))
}
- /// Trys to find the associated item that implements `trait_item_def_id`
+ /// Tries to find the associated item that implements `trait_item_def_id`
/// defined in this node.
///
/// If this returns `None`, the item can potentially still be found in
tcx: TyCtxt<'tcx>,
def: ty::WithOptConstParam<LocalDefId>,
) -> Self {
- debug!("Const::from_anon_const(def={:?})", def);
-
let body_id = match tcx.hir().get_by_def_id(def.did) {
hir::Node::AnonConst(ac) => ac.body,
_ => span_bug!(
// Create a dependency to the crate to be sure we re-execute this when the amount of
// definitions change.
self.ensure().hir_crate(());
- // Leak a read lock once we start iterating on definitions, to prevent adding new onces
+ // Leak a read lock once we start iterating on definitions, to prevent adding new ones
// while iterating. If some query needs to add definitions, it should be `ensure`d above.
let definitions = self.definitions.leak();
definitions.iter_local_def_id()
}
pub fn def_path_table(self) -> &'tcx rustc_hir::definitions::DefPathTable {
- // Create a dependency to the crate to be sure we reexcute this when the amount of
+ // Create a dependency to the crate to be sure we re-execute this when the amount of
// definitions change.
self.ensure().hir_crate(());
- // Leak a read lock once we start iterating on definitions, to prevent adding new onces
+ // Leak a read lock once we start iterating on definitions, to prevent adding new ones
// while iterating. If some query needs to add definitions, it should be `ensure`d above.
let definitions = self.definitions.leak();
definitions.def_path_table()
pub fn def_path_hash_to_def_index_map(
self,
) -> &'tcx rustc_hir::def_path_hash_map::DefPathHashMap {
- // Create a dependency to the crate to be sure we reexcute this when the amount of
+ // Create a dependency to the crate to be sure we re-execute this when the amount of
// definitions change.
self.ensure().hir_crate(());
- // Leak a read lock once we start iterating on definitions, to prevent adding new onces
+ // Leak a read lock once we start iterating on definitions, to prevent adding new ones
// while iterating. If some query needs to add definitions, it should be `ensure`d above.
let definitions = self.definitions.leak();
definitions.def_path_hash_to_def_index_map()
use crate::dep_graph::TaskDepsRef;
use crate::ty::query;
use rustc_data_structures::sync::{self, Lock};
- use rustc_data_structures::thin_vec::ThinVec;
use rustc_errors::Diagnostic;
use std::mem;
+ use thin_vec::ThinVec;
#[cfg(not(parallel_compiler))]
use std::cell::Cell;
}
ty::Slice(ty) if ty.is_simple_ty() => format!("slice `{}`", self).into(),
ty::Slice(_) => "slice".into(),
- ty::RawPtr(_) => "*-ptr".into(),
+ ty::RawPtr(tymut) => {
+ let tymut_string = match tymut.mutbl {
+ hir::Mutability::Mut => tymut.to_string(),
+ hir::Mutability::Not => format!("const {}", tymut.ty),
+ };
+
+ if tymut_string != "_" && (tymut.ty.is_simple_text() || tymut_string.len() < "const raw pointer".len()) {
+ format!("`*{}`", tymut_string).into()
+ } else {
+ // Unknown type name, it's long or has type arguments
+ "raw pointer".into()
+ }
+ },
ty::Ref(_, ty, mutbl) => {
let tymut = ty::TypeAndMut { ty, mutbl };
let tymut_string = tymut.to_string();
+
if tymut_string != "_"
&& (ty.is_simple_text() || tymut_string.len() < "mutable reference".len())
{
t
}
- #[instrument(skip(self), level = "debug")]
+ #[instrument(skip(self), level = "debug", ret)]
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
match *r {
ty::ReLateBound(debruijn, _) if debruijn < self.current_index => {
-use crate::middle::resolve_lifetime::ObjectLifetimeDefault;
use crate::ty;
use crate::ty::subst::{Subst, SubstsRef};
use crate::ty::EarlyBinder;
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]
pub enum GenericParamDefKind {
Lifetime,
- Type { has_default: bool, object_lifetime_default: ObjectLifetimeDefault, synthetic: bool },
+ Type { has_default: bool, synthetic: bool },
Const { has_default: bool },
}
// Filter the default arguments.
//
// This currently uses structural equality instead
- // of semantic equivalance. While not ideal, that's
+ // of semantic equivalence. While not ideal, that's
// good enough for now as this should only be used
// for diagnostics anyways.
own_params.end -= self
}
// `Relocations` with default type parameters is a sorted map.
-impl<'a, Prov> HashStable<StableHashingContext<'a>> for mir::interpret::Relocations<Prov>
+impl<'a, Prov> HashStable<StableHashingContext<'a>> for mir::interpret::ProvenanceMap<Prov>
where
Prov: HashStable<StableHashingContext<'a>>,
{
// * the element type and length of the single array field, if
// the first field is of array type, or
//
- // * the homogenous field type and the number of fields.
+ // * the homogeneous field type and the number of fields.
let (e_ty, e_len, is_array) = if let ty::Array(e_ty, _) = f0_ty.kind() {
// First ADT field is an array:
}
impl<'tcx> NormalizeAfterErasingRegionsFolder<'tcx> {
- #[instrument(skip(self), level = "debug")]
fn normalize_generic_arg_after_erasing_regions(
&self,
arg: ty::GenericArg<'tcx>,
) -> ty::GenericArg<'tcx> {
let arg = self.param_env.and(arg);
- debug!(?arg);
self.tcx.try_normalize_generic_arg_after_erasing_regions(arg).unwrap_or_else(|_| bug!(
"Failed to normalize {:?}, maybe try to call `try_normalize_erasing_regions` instead",
crate::metadata::ModChild,
crate::middle::codegen_fn_attrs::CodegenFnAttrs,
crate::middle::exported_symbols::SymbolExportInfo,
+ crate::middle::resolve_lifetime::ObjectLifetimeDefault,
crate::mir::ConstQualifs,
+ ty::AssocItemContainer,
ty::Generics,
ty::ImplPolarity,
ty::ReprOptions,
let range =
AllocRange { start: offset, size: Size::from_bytes(len) };
if let Ok(byte_str) =
- alloc.inner().get_bytes(&self.tcx(), range)
+ alloc.inner().get_bytes_strip_provenance(&self.tcx(), range)
{
p!(pretty_print_byte_str(byte_str))
} else {
}
Ok(self)
}
+
+ fn pretty_closure_as_impl(
+ mut self,
+ closure: ty::ClosureSubsts<'tcx>,
+ ) -> Result<Self::Const, Self::Error> {
+ let sig = closure.sig();
+ let kind = closure.kind_ty().to_opt_closure_kind().unwrap_or(ty::ClosureKind::Fn);
+
+ write!(self, "impl ")?;
+ self.wrap_binder(&sig, |sig, mut cx| {
+ define_scoped_cx!(cx);
+
+ p!(print(kind), "(");
+ for (i, arg) in sig.inputs()[0].tuple_fields().iter().enumerate() {
+ if i > 0 {
+ p!(", ");
+ }
+ p!(print(arg));
+ }
+ p!(")");
+
+ if !sig.output().is_unit() {
+ p!(" -> ", print(sig.output()));
+ }
+
+ Ok(cx)
+ })
+ }
}
// HACK(eddyb) boxed to avoid moving around a large struct by-value.
}
}
+#[derive(Debug, Copy, Clone, TypeFoldable, TypeVisitable, Lift)]
+pub struct PrintClosureAsImpl<'tcx> {
+ pub closure: ty::ClosureSubsts<'tcx>,
+}
+
forward_display_to_print! {
ty::Region<'tcx>,
Ty<'tcx>,
p!(print(self.0.trait_ref.print_only_trait_path()));
}
+ PrintClosureAsImpl<'tcx> {
+ p!(pretty_closure_as_impl(self.closure))
+ }
+
ty::ParamTy {
p!(write("{}", self.name))
}
}
}
+impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for &[T] {
+ fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+ self.iter().try_for_each(|t| t.visit_with(visitor))
+ }
+}
+
impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box<[T]> {
fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> {
self.try_map_id(|t| t.try_fold_with(folder))
_ => bug!("closure_sig_as_fn_ptr_ty is not a fn-ptr: {:?}", ty.kind()),
}
}
+
+ pub fn print_as_impl_trait(self) -> ty::print::PrintClosureAsImpl<'tcx> {
+ ty::print::PrintClosureAsImpl { closure: self }
+ }
}
/// Similar to `ClosureSubsts`; see the above documentation for more.
}
// Query provider for `incoherent_impls`.
-#[instrument(level = "debug", skip(tcx))]
pub(super) fn incoherent_impls_provider(tcx: TyCtxt<'_>, simp: SimplifiedType) -> &[DefId] {
let mut impls = Vec::new();
}
/// Expands the given impl trait type, stopping if the type is recursive.
- #[instrument(skip(self), level = "debug")]
+ #[instrument(skip(self), level = "debug", ret)]
pub fn try_expand_impl_trait_type(
self,
def_id: DefId,
};
let expanded_type = visitor.expand_opaque_ty(def_id, substs).unwrap();
- trace!(?expanded_type);
if visitor.found_recursion { Err(expanded_type) } else { Ok(expanded_type) }
}
self.has_vars_bound_at_or_above(ty::INNERMOST)
}
- #[instrument(level = "trace")]
+ #[instrument(level = "trace", ret)]
fn has_type_flags(&self, flags: TypeFlags) -> bool {
self.visit_with(&mut HasTypeFlagsVisitor { flags }).break_value() == Some(FoundFlags)
}
type BreakTy = FoundFlags;
#[inline]
- #[instrument(skip(self), level = "trace")]
+ #[instrument(skip(self), level = "trace", ret)]
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
let flags = t.flags();
trace!(t.flags=?t.flags());
}
#[inline]
- #[instrument(skip(self), level = "trace")]
+ #[instrument(skip(self), level = "trace", ret)]
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
let flags = r.type_flags();
trace!(r.flags=?flags);
}
#[inline]
- #[instrument(level = "trace")]
+ #[instrument(level = "trace", ret)]
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
let flags = FlagComputation::for_const(c);
trace!(r.flags=?flags);
}
#[inline]
- #[instrument(level = "trace")]
+ #[instrument(level = "trace", ret)]
fn visit_unevaluated(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow<Self::BreakTy> {
let flags = FlagComputation::for_unevaluated_const(uv);
trace!(r.flags=?flags);
}
#[inline]
- #[instrument(level = "trace")]
+ #[instrument(level = "trace", ret)]
fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
debug!(
"HasTypeFlagsVisitor: predicate={:?} predicate.flags={:?} self.flags={:?}",
///
/// * From each pre-binding block to the next pre-binding block.
/// * From each otherwise block to the next pre-binding block.
- #[tracing::instrument(level = "debug", skip(self, arms))]
+ #[instrument(level = "debug", skip(self, arms))]
pub(crate) fn match_expr(
&mut self,
destination: Place<'tcx>,
let local_id = self.var_local_id(var, for_guard);
let source_info = self.source_info(span);
self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) });
- // Altough there is almost always scope for given variable in corner cases
+ // Although there is almost always scope for given variable in corner cases
// like #92893 we might get variable with no scope.
if let Some(region_scope) = self.region_scope_tree.var_scope(var.0.local_id) && schedule_drop{
self.schedule_drop(span, region_scope, local_id, DropKind::Storage);
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(if_let_guard)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(min_specialization)]
#![feature(once_cell)]
// the overall method call for better diagnostics. args[0]
// is guaranteed to exist, since a method call always has a receiver.
let old_adjustment_span = self.adjustment_span.replace((args[0].hir_id, expr_span));
- tracing::info!("Using method span: {:?}", expr.span);
+ info!("Using method span: {:?}", expr.span);
let args = self.mirror_exprs(args);
self.adjustment_span = old_adjustment_span;
ExprKind::Call {
}
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
pub(crate) fn pattern_from_hir(&mut self, p: &hir::Pat<'_>) -> Pat<'tcx> {
let p = match self.tcx.hir().get(p.hir_id) {
Node::Pat(p) => p,
/// Converts an evaluated constant to a pattern (if possible).
/// This means aggregate values (like structs and enums) are converted
/// to a pattern that matches the value (as if you'd compared via structural equality).
- #[instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self), ret)]
pub(super) fn const_to_pat(
&self,
cv: mir::ConstantKind<'tcx>,
span: Span,
mir_structural_match_violation: bool,
) -> Pat<'tcx> {
- let pat = self.tcx.infer_ctxt().enter(|infcx| {
+ self.tcx.infer_ctxt().enter(|infcx| {
let mut convert = ConstToPat::new(self, id, span, infcx);
convert.to_pat(cv, mir_structural_match_violation)
- });
-
- debug!(?pat);
- pat
+ })
}
}
/// `is_under_guard` is used to inform if the pattern has a guard. If it
/// has one it must not be inserted into the matrix. This shouldn't be
/// relied on for soundness.
-#[instrument(level = "debug", skip(cx, matrix, hir_id))]
+#[instrument(level = "debug", skip(cx, matrix, hir_id), ret)]
fn is_useful<'p, 'tcx>(
cx: &MatchCheckCtxt<'p, 'tcx>,
matrix: &Matrix<'p, 'tcx>,
v.head().set_reachable();
}
- debug!(?ret);
ret
}
throw_machine_stop_str!("pointer arithmetic or comparisons aren't supported in ConstProp")
}
- fn access_local<'a>(
- frame: &'a Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>,
- local: Local,
- ) -> InterpResult<'tcx, &'a interpret::Operand<Self::Provenance>> {
- let l = &frame.locals[local];
-
- if matches!(
- l.value,
- LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit))
- ) {
- // For us "uninit" means "we don't know its value, might be initiailized or not".
- // So stop here.
- throw_machine_stop_str!("tried to access alocal with unknown value ")
- }
-
- l.access()
- }
-
fn access_local_mut<'a>(
ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
frame: usize,
fn get_const(&self, place: Place<'tcx>) -> Option<OpTy<'tcx>> {
let op = match self.ecx.eval_place_to_op(place, None) {
- Ok(op) => op,
+ Ok(op) => {
+ if matches!(*op, interpret::Operand::Immediate(Immediate::Uninit)) {
+ // Make sure nobody accidentally uses this value.
+ return None;
+ }
+ op
+ }
Err(e) => {
trace!("get_const failed: {}", e);
return None;
if rvalue.needs_subst() {
return None;
}
+ if !rvalue
+ .ty(&self.ecx.frame().body.local_decls, *self.ecx.tcx)
+ .is_sized(self.ecx.tcx, self.param_env)
+ {
+ // the interpreter doesn't support unsized locals (only unsized arguments),
+ // but rustc does (in a kinda broken way), so we have to skip them here
+ return None;
+ }
if self.tcx.sess.mir_opt_level() >= 4 {
self.eval_rvalue_with_identities(rvalue, place)
self.use_ecx(|this| match rvalue {
Rvalue::BinaryOp(op, box (left, right))
| Rvalue::CheckedBinaryOp(op, box (left, right)) => {
- let l = this.ecx.eval_operand(left, None);
- let r = this.ecx.eval_operand(right, None);
+ let l = this.ecx.eval_operand(left, None).and_then(|x| this.ecx.read_immediate(&x));
+ let r =
+ this.ecx.eval_operand(right, None).and_then(|x| this.ecx.read_immediate(&x));
let const_arg = match (l, r) {
- (Ok(ref x), Err(_)) | (Err(_), Ok(ref x)) => this.ecx.read_immediate(x)?,
- (Err(e), Err(_)) => return Err(e),
- (Ok(_), Ok(_)) => return this.ecx.eval_rvalue_into_place(rvalue, place),
+ (Ok(x), Err(_)) | (Err(_), Ok(x)) => x, // exactly one side is known
+ (Err(e), Err(_)) => return Err(e), // neither side is known
+ (Ok(_), Ok(_)) => return this.ecx.eval_rvalue_into_place(rvalue, place), // both sides are known
};
if !matches!(const_arg.layout.abi, abi::Abi::Scalar(..)) {
// We cannot handle Scalar Pair stuff.
- return this.ecx.eval_rvalue_into_place(rvalue, place);
+ // No point in calling `eval_rvalue_into_place`, since only one side is known
+ throw_machine_stop_str!("cannot optimize this")
}
let arg_value = const_arg.to_scalar().to_bits(const_arg.layout.size)?;
this.ecx.write_immediate(*const_arg, &dest)
}
}
- _ => this.ecx.eval_rvalue_into_place(rvalue, place),
+ _ => throw_machine_stop_str!("cannot optimize this"),
}
}
_ => this.ecx.eval_rvalue_into_place(rvalue, place),
if let Some(ref value) = self.eval_operand(&cond) {
trace!("assertion on {:?} should be {:?}", value, expected);
let expected = Scalar::from_bool(*expected);
- let value_const = self.ecx.read_scalar(&value).unwrap();
+ let Ok(value_const) = self.ecx.read_scalar(&value) else {
+ // FIXME should be used use_ecx rather than a local match... but we have
+ // quite a few of these read_scalar/read_immediate that need fixing.
+ return
+ };
if expected != value_const {
// Poison all places this operand references so that further code
// doesn't use the invalid value
use crate::const_prop::ConstPropMode;
use crate::MirLint;
use rustc_const_eval::const_eval::ConstEvalErr;
+use rustc_const_eval::interpret::Immediate;
use rustc_const_eval::interpret::{
self, InterpCx, InterpResult, LocalState, LocalValue, MemoryKind, OpTy, Scalar, StackPopCleanup,
};
fn get_const(&self, place: Place<'tcx>) -> Option<OpTy<'tcx>> {
let op = match self.ecx.eval_place_to_op(place, None) {
- Ok(op) => op,
+ Ok(op) => {
+ if matches!(*op, interpret::Operand::Immediate(Immediate::Uninit)) {
+ // Make sure nobody accidentally uses this value.
+ return None;
+ }
+ op
+ }
Err(e) => {
trace!("get_const failed: {}", e);
return None;
if rvalue.needs_subst() {
return None;
}
+ if !rvalue
+ .ty(&self.ecx.frame().body.local_decls, *self.ecx.tcx)
+ .is_sized(self.ecx.tcx, self.param_env)
+ {
+ // the interpreter doesn't support unsized locals (only unsized arguments),
+ // but rustc does (in a kinda broken way), so we have to skip them here
+ return None;
+ }
self.use_ecx(source_info, |this| this.ecx.eval_rvalue_into_place(rvalue, place))
}
if let Some(ref value) = self.eval_operand(&cond, source_info) {
trace!("assertion on {:?} should be {:?}", value, expected);
let expected = Scalar::from_bool(*expected);
- let value_const = self.ecx.read_scalar(&value).unwrap();
+ let Ok(value_const) = self.ecx.read_scalar(&value) else {
+ // FIXME should be used use_ecx rather than a local match... but we have
+ // quite a few of these read_scalar/read_immediate that need fixing.
+ return
+ };
if expected != value_const {
enum DbgVal<T> {
Val(T),
let mut eval_to_int = |op| {
// This can be `None` if the lhs wasn't const propagated and we just
// triggered the assert on the value of the rhs.
- self.eval_operand(op, source_info).map_or(DbgVal::Underscore, |op| {
- DbgVal::Val(self.ecx.read_immediate(&op).unwrap().to_const_int())
- })
+ self.eval_operand(op, source_info)
+ .and_then(|op| self.ecx.read_immediate(&op).ok())
+ .map_or(DbgVal::Underscore, |op| DbgVal::Val(op.to_const_int()))
};
let msg = match msg {
AssertKind::DivisionByZero(op) => {
pub struct Deaggregator;
impl<'tcx> MirPass<'tcx> for Deaggregator {
- fn phase_change(&self) -> Option<MirPhase> {
- Some(MirPhase::Deaggregated)
- }
-
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
for bb in basic_blocks {
impl<'tcx> MirPass<'tcx> for Derefer {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
deref_finder(tcx, body);
- body.phase = MirPhase::Derefered;
}
}
pub struct ElaborateDrops;
impl<'tcx> MirPass<'tcx> for ElaborateDrops {
- fn phase_change(&self) -> Option<MirPhase> {
- Some(MirPhase::DropsLowered)
- }
-
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
debug!("elaborate_drops({:?} @ {:?})", body.source, body.span);
}
impl<'tcx> MirPass<'tcx> for StateTransform {
- fn phase_change(&self) -> Option<MirPhase> {
- Some(MirPhase::GeneratorsLowered)
- }
-
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let Some(yield_ty) = body.yield_ty() else {
// This only applies to generators
#![allow(rustc::potential_query_instability)]
#![feature(box_patterns)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(map_try_insert)]
#![feature(min_specialization)]
use rustc_hir::intravisit::{self, Visitor};
use rustc_index::vec::IndexVec;
use rustc_middle::mir::visit::Visitor as _;
-use rustc_middle::mir::{traversal, Body, ConstQualifs, MirPass, MirPhase, Promoted};
+use rustc_middle::mir::{
+ traversal, AnalysisPhase, Body, ConstQualifs, MirPass, MirPhase, Promoted, RuntimePhase,
+};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
}
/// Make MIR ready for const evaluation. This is run on all MIR, not just on consts!
+/// FIXME(oli-obk): it's unclear whether we still need this phase (and its corresponding query).
+/// We used to have this for pre-miri MIR based const eval.
fn mir_const<'tcx>(
tcx: TyCtxt<'tcx>,
def: ty::WithOptConstParam<LocalDefId>,
// What we need to do constant evaluation.
&simplify::SimplifyCfg::new("initial"),
&rustc_peek::SanityCheck, // Just a lint
- &marker::PhaseChange(MirPhase::Const),
],
);
tcx.alloc_steal_mir(body)
pm::run_passes(
tcx,
&mut body,
- &[&const_prop::ConstProp, &marker::PhaseChange(MirPhase::Optimized)],
+ &[
+ &const_prop::ConstProp,
+ &marker::PhaseChange(MirPhase::Runtime(RuntimePhase::Optimized)),
+ ],
);
}
}
body.tainted_by_errors = Some(error_reported);
}
- // IMPORTANT
- pm::run_passes(tcx, &mut body, &[&remove_false_edges::RemoveFalseEdges]);
+ run_analysis_to_runtime_passes(tcx, &mut body);
+
+ tcx.alloc_steal_mir(body)
+}
+
+fn run_analysis_to_runtime_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+ assert!(body.phase == MirPhase::Analysis(AnalysisPhase::Initial));
+ let did = body.source.def_id();
+
+ debug!("analysis_mir_cleanup({:?})", did);
+ run_analysis_cleanup_passes(tcx, body);
+ assert!(body.phase == MirPhase::Analysis(AnalysisPhase::PostCleanup));
// Do a little drop elaboration before const-checking if `const_precise_live_drops` is enabled.
if check_consts::post_drop_elaboration::checking_enabled(&ConstCx::new(tcx, &body)) {
pm::run_passes(
tcx,
- &mut body,
+ body,
&[
- &simplify::SimplifyCfg::new("remove-false-edges"),
&remove_uninit_drops::RemoveUninitDrops,
+ &simplify::SimplifyCfg::new("remove-false-edges"),
],
);
check_consts::post_drop_elaboration::check_live_drops(tcx, &body); // FIXME: make this a MIR lint
}
- run_post_borrowck_cleanup_passes(tcx, &mut body);
- assert!(body.phase == MirPhase::Deaggregated);
- tcx.alloc_steal_mir(body)
+ debug!("runtime_mir_lowering({:?})", did);
+ run_runtime_lowering_passes(tcx, body);
+ assert!(body.phase == MirPhase::Runtime(RuntimePhase::Initial));
+
+ debug!("runtime_mir_cleanup({:?})", did);
+ run_runtime_cleanup_passes(tcx, body);
+ assert!(body.phase == MirPhase::Runtime(RuntimePhase::PostCleanup));
}
-/// After this series of passes, no lifetime analysis based on borrowing can be done.
-fn run_post_borrowck_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
- debug!("post_borrowck_cleanup({:?})", body.source.def_id());
+// FIXME(JakobDegen): Can we make these lists of passes consts?
- let post_borrowck_cleanup: &[&dyn MirPass<'tcx>] = &[
- // Remove all things only needed by analysis
+/// After this series of passes, no lifetime analysis based on borrowing can be done.
+fn run_analysis_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+ let passes: &[&dyn MirPass<'tcx>] = &[
+ &remove_false_edges::RemoveFalseEdges,
&simplify_branches::SimplifyConstCondition::new("initial"),
&remove_noop_landing_pads::RemoveNoopLandingPads,
&cleanup_post_borrowck::CleanupNonCodegenStatements,
&simplify::SimplifyCfg::new("early-opt"),
&deref_separator::Derefer,
+ &marker::PhaseChange(MirPhase::Analysis(AnalysisPhase::PostCleanup)),
+ ];
+
+ pm::run_passes(tcx, body, passes);
+}
+
+/// Returns the sequence of passes that lowers analysis to runtime MIR.
+fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+ let passes: &[&dyn MirPass<'tcx>] = &[
// These next passes must be executed together
&add_call_guards::CriticalCallEdges,
&elaborate_drops::ElaborateDrops,
// `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late,
// but before optimizations begin.
&elaborate_box_derefs::ElaborateBoxDerefs,
+ &generator::StateTransform,
&add_retag::AddRetag,
- &lower_intrinsics::LowerIntrinsics,
- &simplify::SimplifyCfg::new("elaborate-drops"),
- // `Deaggregator` is conceptually part of MIR building, some backends rely on it happening
- // and it can help optimizations.
+ // Deaggregator is necessary for const prop. We may want to consider implementing
+ // CTFE support for aggregates.
&deaggregator::Deaggregator,
&Lint(const_prop_lint::ConstProp),
+ &marker::PhaseChange(MirPhase::Runtime(RuntimePhase::Initial)),
+ ];
+ pm::run_passes_no_validate(tcx, body, passes);
+}
+
+/// Returns the sequence of passes that do the initial cleanup of runtime MIR.
+fn run_runtime_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+ let passes: &[&dyn MirPass<'tcx>] = &[
+ &elaborate_box_derefs::ElaborateBoxDerefs,
+ &lower_intrinsics::LowerIntrinsics,
+ &simplify::SimplifyCfg::new("elaborate-drops"),
+ &marker::PhaseChange(MirPhase::Runtime(RuntimePhase::PostCleanup)),
];
- pm::run_passes(tcx, body, post_borrowck_cleanup);
+ pm::run_passes(tcx, body, passes);
}
fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
WithMinOptLevel(1, x)
}
- // Lowering generator control-flow and variables has to happen before we do anything else
- // to them. We run some optimizations before that, because they may be harder to do on the state
- // machine than on MIR with async primitives.
+ // The main optimizations that we do on MIR.
pm::run_passes(
tcx,
body,
&uninhabited_enum_branching::UninhabitedEnumBranching,
&o1(simplify::SimplifyCfg::new("after-uninhabited-enum-branching")),
&inline::Inline,
- &generator::StateTransform,
- ],
- );
-
- assert!(body.phase == MirPhase::GeneratorsLowered);
-
- // The main optimizations that we do on MIR.
- pm::run_passes(
- tcx,
- body,
- &[
&remove_storage_markers::RemoveStorageMarkers,
&remove_zsts::RemoveZsts,
&const_goto::ConstGoto,
&deduplicate_blocks::DeduplicateBlocks,
// Some cleanup necessary at least for LLVM and potentially other codegen backends.
&add_call_guards::CriticalCallEdges,
- &marker::PhaseChange(MirPhase::Optimized),
+ &marker::PhaseChange(MirPhase::Runtime(RuntimePhase::Optimized)),
// Dump the end result for testing and debugging purposes.
&dump_mir::Marker("PreCodegen"),
],
if let Some(error_reported) = tainted_by_errors {
body.tainted_by_errors = Some(error_reported);
}
- run_post_borrowck_cleanup_passes(tcx, body);
+ run_analysis_to_runtime_passes(tcx, body);
}
debug_assert!(!promoted.has_free_regions(), "Free regions in promoted MIR");
use std::borrow::Cow;
-use rustc_middle::mir::{self, Body, MirPhase};
+use rustc_middle::mir::{self, Body, MirPhase, RuntimePhase};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
}
}
+/// Run the sequence of passes without validating the MIR after each pass. The MIR is still
+/// validated at the end.
+pub fn run_passes_no_validate<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ body: &mut Body<'tcx>,
+ passes: &[&dyn MirPass<'tcx>],
+) {
+ run_passes_inner(tcx, body, passes, false);
+}
+
pub fn run_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn MirPass<'tcx>]) {
+ run_passes_inner(tcx, body, passes, true);
+}
+
+fn run_passes_inner<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ body: &mut Body<'tcx>,
+ passes: &[&dyn MirPass<'tcx>],
+ validate_each: bool,
+) {
let start_phase = body.phase;
let mut cnt = 0;
- let validate = tcx.sess.opts.unstable_opts.validate_mir;
+ let validate = validate_each & tcx.sess.opts.unstable_opts.validate_mir;
let overridden_passes = &tcx.sess.opts.unstable_opts.mir_enable_passes;
trace!(?overridden_passes);
- if validate {
- validate_body(tcx, body, format!("start of phase transition from {:?}", start_phase));
- }
-
for pass in passes {
let name = pass.name();
- if let Some((_, polarity)) = overridden_passes.iter().rev().find(|(s, _)| s == &*name) {
- trace!(
- pass = %name,
- "{} as requested by flag",
- if *polarity { "Running" } else { "Not running" },
- );
- if !polarity {
- continue;
- }
- } else {
- if !pass.is_enabled(&tcx.sess) {
- continue;
- }
- }
- let dump_enabled = pass.is_mir_dump_enabled();
+ // Gather information about what we should be doing for this pass
+ let overriden =
+ overridden_passes.iter().rev().find(|(s, _)| s == &*name).map(|(_name, polarity)| {
+ trace!(
+ pass = %name,
+ "{} as requested by flag",
+ if *polarity { "Running" } else { "Not running" },
+ );
+ *polarity
+ });
+ let is_enabled = overriden.unwrap_or_else(|| pass.is_enabled(&tcx.sess));
+ let new_phase = pass.phase_change();
+ let dump_enabled = (is_enabled && pass.is_mir_dump_enabled()) || new_phase.is_some();
+ let validate = (validate && is_enabled)
+ || new_phase == Some(MirPhase::Runtime(RuntimePhase::Optimized));
if dump_enabled {
dump_mir(tcx, body, start_phase, &name, cnt, false);
}
-
- pass.run_pass(tcx, body);
-
+ if is_enabled {
+ pass.run_pass(tcx, body);
+ }
if dump_enabled {
dump_mir(tcx, body, start_phase, &name, cnt, true);
cnt += 1;
}
-
if let Some(new_phase) = pass.phase_change() {
if body.phase >= new_phase {
panic!("Invalid MIR phase transition from {:?} to {:?}", body.phase, new_phase);
body.phase = new_phase;
}
-
if validate {
- validate_body(tcx, body, format!("after pass {}", pass.name()));
+ validate_body(tcx, body, format!("after pass {}", name));
}
}
-
- if validate || body.phase == MirPhase::Optimized {
- validate_body(tcx, body, format!("end of phase transition to {:?}", body.phase));
- }
}
pub fn validate_body<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, when: String) {
cnt: usize,
is_after: bool,
) {
- let phase_index = phase as u32;
+ let phase_index = phase.phase_index();
mir::dump_mir(
tcx,
use crate::util::expand_aggregate;
use crate::{
- abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, marker, pass_manager as pm,
- remove_noop_landing_pads, simplify,
+ abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, marker,
+ pass_manager as pm, remove_noop_landing_pads, simplify,
};
use rustc_middle::mir::patch::MirPatch;
use rustc_mir_dataflow::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle};
&mut result,
&[
&add_moves_for_packed_drops::AddMovesForPackedDrops,
+ &deref_separator::Derefer,
&remove_noop_landing_pads::RemoveNoopLandingPads,
&simplify::SimplifyCfg::new("make_shim"),
&add_call_guards::CriticalCallEdges,
&abort_unwinding_calls::AbortUnwindingCalls,
- &marker::PhaseChange(MirPhase::Const),
+ &marker::PhaseChange(MirPhase::Runtime(RuntimePhase::Optimized)),
],
);
doctest = false
[dependencies]
-smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
+smallvec = { version = "1.8.1", features = [ "union", "may_dangle" ] }
tracing = "0.1"
rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_errors = { path = "../rustc_errors" }
rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
+rustc_macros = { path = "../rustc_macros" }
rustc_middle = { path = "../rustc_middle" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
use std::ops::Range;
use std::path::PathBuf;
+use crate::errors::{LargeAssignmentsLint, RecursionLimit, RequiresLangItem, TypeLengthLimit};
+
#[derive(PartialEq)]
pub enum MonoItemCollectionMode {
Eager,
// We've been here already, no need to search again.
return;
}
- debug!("BEGIN collect_items_rec({})", starting_point.node);
let mut neighbors = MonoItems { compute_inlining: true, tcx, items: Vec::new() };
let recursion_depth_reset;
recursion_depth_reset = None;
if let Ok(alloc) = tcx.eval_static_initializer(def_id) {
- for &id in alloc.inner().relocations().values() {
+ for &id in alloc.inner().provenance().values() {
collect_miri(tcx, id, &mut neighbors);
}
}
if let Some((def_id, depth)) = recursion_depth_reset {
recursion_depths.insert(def_id, depth);
}
-
- debug!("END collect_items_rec({})", starting_point.node);
}
/// Format instance name that is already known to be too long for rustc.
// more than the recursion limit is assumed to be causing an
// infinite expansion.
if !recursion_limit.value_within_limit(adjusted_recursion_depth) {
+ let def_span = tcx.def_span(def_id);
+ let def_path_str = tcx.def_path_str(def_id);
let (shrunk, written_to_path) = shrunk_instance_name(tcx, &instance, 32, 32);
- let error = format!("reached the recursion limit while instantiating `{}`", shrunk);
- let mut err = tcx.sess.struct_span_fatal(span, &error);
- err.span_note(
- tcx.def_span(def_id),
- &format!("`{}` defined here", tcx.def_path_str(def_id)),
- );
- if let Some(path) = written_to_path {
- err.note(&format!("the full type name has been written to '{}'", path.display()));
- }
- err.emit()
+ let mut path = PathBuf::new();
+ let was_written = if written_to_path.is_some() {
+ path = written_to_path.unwrap();
+ Some(())
+ } else {
+ None
+ };
+ tcx.sess.emit_fatal(RecursionLimit {
+ span,
+ shrunk,
+ def_span,
+ def_path_str,
+ was_written,
+ path,
+ });
}
recursion_depths.insert(def_id, recursion_depth + 1);
// Bail out in these cases to avoid that bad user experience.
if !tcx.type_length_limit().value_within_limit(type_length) {
let (shrunk, written_to_path) = shrunk_instance_name(tcx, &instance, 32, 32);
- let msg = format!("reached the type-length limit while instantiating `{}`", shrunk);
- let mut diag = tcx.sess.struct_span_fatal(tcx.def_span(instance.def_id()), &msg);
- if let Some(path) = written_to_path {
- diag.note(&format!("the full type name has been written to '{}'", path.display()));
- }
- diag.help(&format!(
- "consider adding a `#![type_length_limit=\"{}\"]` attribute to your crate",
- type_length
- ));
- diag.emit()
+ let span = tcx.def_span(instance.def_id());
+ let mut path = PathBuf::new();
+ let was_written = if written_to_path.is_some() {
+ path = written_to_path.unwrap();
+ Some(())
+ } else {
+ None
+ };
+ tcx.sess.emit_fatal(TypeLengthLimit { span, shrunk, was_written, path, type_length });
}
}
// but correct span? This would make the lint at least accept crate-level lint attributes.
return;
};
- self.tcx.struct_span_lint_hir(
+ self.tcx.emit_spanned_lint(
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.note(&format!(r#"The current maximum size is {}, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`"#, limit.bytes()));
- err.emit();
+ LargeAssignmentsLint {
+ span: source_info.span,
+ size: layout.size.bytes(),
+ limit: limit.bytes(),
},
- );
+ )
}
}
}
}
}
-#[instrument(skip(tcx), level = "debug")]
+#[instrument(skip(tcx), level = "debug", ret)]
fn create_fn_mono_item<'tcx>(
tcx: TyCtxt<'tcx>,
instance: Instance<'tcx>,
source: Span,
) -> Spanned<MonoItem<'tcx>> {
- debug!("create_fn_mono_item(instance={})", instance);
-
let def_id = instance.def_id();
if tcx.sess.opts.unstable_opts.profile_closures && def_id.is_local() && tcx.is_closure(def_id) {
crate::util::dump_closure_profile(tcx, instance);
}
- let respanned = respan(source, MonoItem::Fn(instance.polymorphize(tcx)));
- debug!(?respanned);
-
- respanned
+ respan(source, MonoItem::Fn(instance.polymorphize(tcx)))
}
/// Creates a `MonoItem` for each method that is referenced by the vtable for
#[instrument(skip(self), level = "debug")]
fn push_if_root(&mut self, def_id: LocalDefId) {
if self.is_root(def_id) {
- debug!("RootCollector::push_if_root: found root def_id={:?}", def_id);
+ debug!("found root");
let instance = Instance::mono(self.tcx, def_id.to_def_id());
self.output.push(create_fn_mono_item(self.tcx, instance, DUMMY_SP));
let start_def_id = match self.tcx.lang_items().require(LangItem::Start) {
Ok(s) => s,
- Err(err) => self.tcx.sess.fatal(&err),
+ Err(lang_item_err) => {
+ self.tcx
+ .sess
+ .emit_fatal(RequiresLangItem { lang_item: lang_item_err.0.name().to_string() });
+ }
};
let main_ret_ty = self.tcx.fn_sig(main_def_id).output();
}
GlobalAlloc::Memory(alloc) => {
trace!("collecting {:?} with {:#?}", alloc_id, alloc);
- for &inner in alloc.inner().relocations().values() {
+ for &inner in alloc.inner().provenance().values() {
rustc_data_structures::stack::ensure_sufficient_stack(|| {
collect_miri(tcx, inner, output);
});
match value {
ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => collect_miri(tcx, ptr.provenance, output),
ConstValue::Slice { data: alloc, start: _, end: _ } | ConstValue::ByRef { alloc, .. } => {
- for &id in alloc.inner().relocations().values() {
+ for &id in alloc.inner().provenance().values() {
collect_miri(tcx, id, output);
}
}
--- /dev/null
+use std::path::PathBuf;
+
+use rustc_errors::ErrorGuaranteed;
+use rustc_macros::{LintDiagnostic, SessionDiagnostic};
+use rustc_session::SessionDiagnostic;
+use rustc_span::Span;
+
+#[derive(SessionDiagnostic)]
+#[diag(monomorphize::recursion_limit)]
+pub struct RecursionLimit {
+ #[primary_span]
+ pub span: Span,
+ pub shrunk: String,
+ #[note]
+ pub def_span: Span,
+ pub def_path_str: String,
+ #[note(monomorphize::written_to_path)]
+ pub was_written: Option<()>,
+ pub path: PathBuf,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(monomorphize::type_length_limit)]
+#[help(monomorphize::consider_type_length_limit)]
+pub struct TypeLengthLimit {
+ #[primary_span]
+ pub span: Span,
+ pub shrunk: String,
+ #[note(monomorphize::written_to_path)]
+ pub was_written: Option<()>,
+ pub path: PathBuf,
+ pub type_length: usize,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(monomorphize::requires_lang_item)]
+pub struct RequiresLangItem {
+ pub lang_item: String,
+}
+
+pub struct UnusedGenericParams {
+ pub span: Span,
+ pub param_spans: Vec<Span>,
+ pub param_names: Vec<String>,
+}
+
+impl SessionDiagnostic<'_> for UnusedGenericParams {
+ fn into_diagnostic(
+ self,
+ sess: &'_ rustc_session::parse::ParseSess,
+ ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
+ let mut diag = sess.struct_err(rustc_errors::fluent::monomorphize::unused_generic_params);
+ diag.set_span(self.span);
+ for (span, name) in self.param_spans.into_iter().zip(self.param_names) {
+ // FIXME: I can figure out how to do a label with a fluent string with a fixed message,
+ // or a label with a dynamic value in a hard-coded string, but I haven't figured out
+ // how to combine the two. 😢
+ diag.span_label(span, format!("generic parameter `{}` is unused", name));
+ }
+ diag
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(monomorphize::large_assignments)]
+#[note]
+pub struct LargeAssignmentsLint {
+ #[label]
+ pub span: Span,
+ pub size: u64,
+ pub limit: u64,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(monomorphize::unknown_partition_strategy)]
+pub struct UnknownPartitionStrategy;
+
+#[derive(SessionDiagnostic)]
+#[diag(monomorphize::symbol_already_defined)]
+pub struct SymbolAlreadyDefined {
+ #[primary_span]
+ pub span: Option<Span>,
+ pub symbol: String,
+}
#![feature(let_else)]
#![recursion_limit = "256"]
#![allow(rustc::potential_query_instability)]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
#[macro_use]
extern crate tracing;
use rustc_middle::ty::{self, Ty, TyCtxt};
mod collector;
+mod errors;
mod partitioning;
mod polymorphize;
mod util;
use crate::collector::InliningMap;
use crate::collector::{self, MonoItemCollectionMode};
+use crate::errors::{SymbolAlreadyDefined, UnknownPartitionStrategy};
pub struct PartitioningCx<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
match strategy {
"default" => Box::new(default::DefaultPartitioning),
- _ => tcx.sess.fatal("unknown partitioning strategy"),
+ _ => {
+ tcx.sess.emit_fatal(UnknownPartitionStrategy);
+ }
}
}
(span1, span2) => span1.or(span2),
};
- let error_message = format!("symbol `{}` is already defined", sym1);
-
- if let Some(span) = span {
- tcx.sess.span_fatal(span, &error_message)
- } else {
- tcx.sess.fatal(&error_message)
- }
+ tcx.sess.emit_fatal(SymbolAlreadyDefined { span, symbol: sym1.to_string() });
}
}
}
use std::convert::TryInto;
use std::ops::ControlFlow;
+use crate::errors::UnusedGenericParams;
+
/// Provide implementations of queries relating to polymorphization analysis.
pub fn provide(providers: &mut Providers) {
providers.unused_generic_params = unused_generic_params;
///
/// Returns a bitset where bits representing unused parameters are set (`is_empty` indicates all
/// parameters are used).
-#[instrument(level = "debug", skip(tcx))]
fn unused_generic_params<'tcx>(
tcx: TyCtxt<'tcx>,
instance: ty::InstanceDef<'tcx>,
_ => tcx.def_span(def_id),
};
- let mut err = tcx.sess.struct_span_err(fn_span, "item has unused generic parameters");
-
+ let mut param_spans = Vec::new();
+ let mut param_names = Vec::new();
let mut next_generics = Some(generics);
while let Some(generics) = next_generics {
for param in &generics.params {
if unused_parameters.contains(param.index).unwrap_or(false) {
debug!(?param);
let def_span = tcx.def_span(param.def_id);
- err.span_label(def_span, &format!("generic parameter `{}` is unused", param.name));
+ param_spans.push(def_span);
+ param_names.push(param.name.to_string());
}
}
next_generics = generics.parent.map(|did| tcx.generics_of(did));
}
- err.emit();
+ tcx.sess.emit_err(UnusedGenericParams { span: fn_span, param_spans, param_names });
}
/// Visitor used to aggregate generic parameter uses.
.append(true)
.open(&format!("closure_profile_{}.csv", std::process::id()))
else {
- eprintln!("Cound't open file for writing closure profile");
+ eprintln!("Couldn't open file for writing closure profile");
return;
};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{edition::Edition, BytePos, Pos, Span};
-use tracing::debug;
-
mod tokentrees;
mod unescape_error_reporting;
mod unicode_chars;
range: Range<usize>,
error: EscapeError,
) {
- tracing::debug!(
+ debug!(
"emit_unescape_error: {:?}, {:?}, {:?}, {:?}, {:?}",
- lit,
- span_with_quotes,
- mode,
- range,
- error
+ lit, span_with_quotes, mode, range, error
);
let last_char = || {
let c = lit[range.clone()].chars().rev().next().unwrap();
#![feature(array_windows)]
#![feature(box_patterns)]
#![feature(if_let_guard)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(never_type)]
#![feature(rustc_attrs)]
use rustc_span::{sym, BytePos, Span};
use std::convert::TryInto;
-use tracing::debug;
-
// Public for rustfmt usage
#[derive(Debug)]
pub enum InnerAttrPolicy<'a> {
&& matches!(self.capture_state.capturing, Capturing::Yes)
&& has_cfg_or_cfg_attr(final_attrs)
{
- let attr_data = AttributesData { attrs: final_attrs.to_vec().into(), tokens };
+ let attr_data = AttributesData { attrs: final_attrs.iter().cloned().collect(), tokens };
// Replace the entire AST node that we just parsed, including attributes,
// with a `FlatToken::AttrTarget`. If this AST node is inside an item
use std::mem::take;
use crate::parser;
-use tracing::{debug, trace};
const TURBOFISH_SUGGESTION_STR: &str =
"use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments";
// Stitch the list of outer attributes onto the return value.
// A little bit ugly, but the best way given the current code
// structure
- self.parse_dot_or_call_expr_with_(e0, lo).map(|expr| {
- expr.map(|mut expr| {
- attrs.extend(expr.attrs);
- expr.attrs = attrs;
- expr
+ let res = self.parse_dot_or_call_expr_with_(e0, lo);
+ if attrs.is_empty() {
+ res
+ } else {
+ res.map(|expr| {
+ expr.map(|mut expr| {
+ attrs.extend(expr.attrs);
+ expr.attrs = attrs;
+ expr
+ })
})
- })
+ }
}
fn parse_dot_or_call_expr_with_(&mut self, mut e: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
Applicability::MachineApplicable,
);
- // Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to supress future errors about `break 'label`.
+ // Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to suppress future errors about `break 'label`.
let stmt = self.mk_stmt(span, StmtKind::Expr(expr));
let blk = self.mk_block(vec![stmt], BlockCheckMode::Default, span);
self.mk_expr(span, ExprKind::Block(blk, label))
/// Parses the condition of a `if` or `while` expression.
fn parse_cond_expr(&mut self) -> PResult<'a, P<Expr>> {
- self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, None)
+ let cond =
+ self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, None)?;
+
+ if let ExprKind::Let(..) = cond.kind {
+ // Remove the last feature gating of a `let` expression since it's stable.
+ self.sess.gated_spans.ungate_last(sym::let_chains, cond.span);
+ }
+
+ Ok(cond)
}
/// Parses a `let $pat = $expr` pseudo-expression.
this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into())
})?;
let span = lo.to(expr.span);
+ self.sess.gated_spans.gate(sym::let_chains, span);
Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span)))
}
}
pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
- // Used to check the `let_chains` and `if_let_guard` features mostly by scaning
+ // Used to check the `let_chains` and `if_let_guard` features mostly by scanning
// `&&` tokens.
- fn check_let_expr(expr: &Expr) -> bool {
+ fn check_let_expr(expr: &Expr) -> (bool, bool) {
match expr.kind {
ExprKind::Binary(BinOp { node: BinOpKind::And, .. }, ref lhs, ref rhs) => {
- check_let_expr(lhs) || check_let_expr(rhs)
+ let lhs_rslt = check_let_expr(lhs);
+ let rhs_rslt = check_let_expr(rhs);
+ (lhs_rslt.0 || rhs_rslt.0, false)
}
- ExprKind::Let(..) => true,
- _ => false,
+ ExprKind::Let(..) => (true, true),
+ _ => (false, true),
}
}
let attrs = self.parse_outer_attributes()?;
let guard = if this.eat_keyword(kw::If) {
let if_span = this.prev_token.span;
let cond = this.parse_expr_res(Restrictions::ALLOW_LET, None)?;
- if check_let_expr(&cond) {
+ let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond);
+ if has_let_expr {
+ if does_not_have_bin_op {
+ // Remove the last feature gating of a `let` expression since it's stable.
+ this.sess.gated_spans.ungate_last(sym::let_chains, cond.span);
+ }
let span = if_span.to(cond.span);
this.sess.gated_spans.gate(sym::if_let_guard, span);
}
use std::convert::TryFrom;
use std::mem;
-use tracing::debug;
impl<'a> Parser<'a> {
/// Parses a source module as a crate. This is the main entry point for the parser.
use rustc_session::parse::ParseSess;
use rustc_span::source_map::{Span, DUMMY_SP};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use tracing::debug;
use std::ops::Range;
use std::{cmp, mem, slice};
if delim != Delimiter::Invisible {
return (Token::new(token::OpenDelim(delim), sp.open), Spacing::Alone);
}
- // No open delimeter to return; continue on to the next iteration.
+ // No open delimiter to return; continue on to the next iteration.
}
};
} else if let Some(frame) = self.stack.pop() {
use rustc_span::symbol::{kw, sym, Ident};
use std::mem;
-use tracing::debug;
/// Specifies how to parse a path.
#[derive(Copy, Clone, PartialEq)]
CountIsName(&'a str, InnerSpan),
/// The count is specified by the argument at the given index.
CountIsParam(usize),
+ /// The count is specified by a star (like in `{:.*}`) that refers to the argument at the given index.
+ CountIsStar(usize),
/// The count is implied and cannot be explicitly specified.
CountImplied,
}
// We can do this immediately as `position` is resolved later.
let i = self.curarg;
self.curarg += 1;
- spec.precision = CountIsParam(i);
+ spec.precision = CountIsStar(i);
} else {
spec.precision = self.count(start + 1);
}
fill: None,
align: AlignUnknown,
flags: 0,
- precision: CountIsParam(0),
+ precision: CountIsStar(0),
precision_span: Some(InnerSpan { start: 3, end: 5 }),
width: CountImplied,
width_span: None,
use rustc_hir::{self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID};
use rustc_hir::{MethodKind, Target};
use rustc_middle::hir::nested_filter;
+use rustc_middle::middle::resolve_lifetime::ObjectLifetimeDefault;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::lint::builtin::{
sym::no_implicit_prelude => {
self.check_generic_attr(hir_id, attr, target, &[Target::Mod])
}
+ sym::rustc_object_lifetime_default => {
+ self.check_object_lifetime_default(hir_id, span)
+ }
_ => {}
}
}
}
+ /// Debugging aid for `object_lifetime_default` query.
+ fn check_object_lifetime_default(&self, hir_id: HirId, span: Span) {
+ let tcx = self.tcx;
+ if let Some(generics) = tcx.hir().get_generics(tcx.hir().local_def_id(hir_id)) {
+ let object_lifetime_default_reprs: String = generics
+ .params
+ .iter()
+ .filter_map(|p| {
+ let param_id = tcx.hir().local_def_id(p.hir_id);
+ let default = tcx.object_lifetime_default(param_id)?;
+ Some(match default {
+ ObjectLifetimeDefault::Empty => "BaseDefault".to_owned(),
+ ObjectLifetimeDefault::Static => "'static".to_owned(),
+ ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(),
+ ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(),
+ })
+ })
+ .collect::<Vec<String>>()
+ .join(",");
+
+ tcx.sess.span_err(span, &object_lifetime_default_reprs);
+ }
+ }
+
/// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid.
fn check_track_caller(
&self,
///
/// For example, `ast::Visitor` has `visit_ident`, but `Ident`s are always
/// stored inline within other AST nodes, so we don't implement `visit_ident`
-/// here. In constrast, we do implement `visit_expr` because `ast::Expr` is
+/// here. In contrast, we do implement `visit_expr` because `ast::Expr` is
/// always stored as `P<ast::Expr>`, and every such expression should be
/// measured separately.
///
#![allow(rustc::potential_query_instability)]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(iter_intersperse)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(map_try_insert)]
#![feature(min_specialization)]
Upvar(HirId, Symbol),
}
+struct CollectLitsVisitor<'tcx> {
+ lit_exprs: Vec<&'tcx hir::Expr<'tcx>>,
+}
+
+impl<'tcx> Visitor<'tcx> for CollectLitsVisitor<'tcx> {
+ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+ if let hir::ExprKind::Lit(_) = expr.kind {
+ self.lit_exprs.push(expr);
+ }
+ intravisit::walk_expr(self, expr);
+ }
+}
+
struct IrMaps<'tcx> {
tcx: TyCtxt<'tcx>,
live_node_map: HirIdMap<LiveNode>,
impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
- self.check_unused_vars_in_pat(&local.pat, None, |spans, hir_id, ln, var| {
+ self.check_unused_vars_in_pat(&local.pat, None, None, |spans, hir_id, ln, var| {
if local.init.is_some() {
self.warn_about_dead_assign(spans, hir_id, ln, var);
}
}
fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
- self.check_unused_vars_in_pat(&arm.pat, None, |_, _, _, _| {});
+ self.check_unused_vars_in_pat(&arm.pat, None, None, |_, _, _, _| {});
intravisit::walk_arm(self, arm);
}
}
}
hir::ExprKind::Let(let_expr) => {
- this.check_unused_vars_in_pat(let_expr.pat, None, |_, _, _, _| {});
+ this.check_unused_vars_in_pat(let_expr.pat, None, None, |_, _, _, _| {});
}
// no correctness conditions related to liveness
fn warn_about_unused_args(&self, body: &hir::Body<'_>, entry_ln: LiveNode) {
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_unused_assign(hir_id, spans, var, |name| {
- format!("value passed to `{}` is never read", name)
- });
- }
- });
+ self.check_unused_vars_in_pat(
+ &p.pat,
+ Some(entry_ln),
+ Some(body),
+ |spans, hir_id, ln, var| {
+ if !self.live_on_entry(ln, var) {
+ self.report_unused_assign(hir_id, spans, var, |name| {
+ format!("value passed to `{}` is never read", name)
+ });
+ }
+ },
+ );
}
}
&self,
pat: &hir::Pat<'_>,
entry_ln: Option<LiveNode>,
+ opt_body: Option<&hir::Body<'_>>,
on_used_on_entry: impl Fn(Vec<Span>, HirId, LiveNode, Variable),
) {
// In an or-pattern, only consider the variable; any later patterns must have the same
hir_ids_and_spans.into_iter().map(|(_, _, ident_span)| ident_span).collect();
on_used_on_entry(spans, id, ln, var);
} else {
- self.report_unused(hir_ids_and_spans, ln, var, can_remove);
+ self.report_unused(hir_ids_and_spans, ln, var, can_remove, pat, opt_body);
}
}
}
- #[tracing::instrument(skip(self), level = "INFO")]
+ #[instrument(skip(self), level = "INFO")]
fn report_unused(
&self,
hir_ids_and_spans: Vec<(HirId, Span, Span)>,
ln: LiveNode,
var: Variable,
can_remove: bool,
+ pat: &hir::Pat<'_>,
+ opt_body: Option<&hir::Body<'_>>,
) {
let first_hir_id = hir_ids_and_spans[0].0;
.collect::<Vec<_>>(),
|lint| {
let mut err = lint.build(&format!("unused variable: `{}`", name));
+ if self.has_added_lit_match_name_span(&name, opt_body, &mut err) {
+ err.span_label(pat.span, "unused variable");
+ }
err.multipart_suggestion(
"if this is intentional, prefix it with an underscore",
non_shorthands,
}
}
+ fn has_added_lit_match_name_span(
+ &self,
+ name: &str,
+ opt_body: Option<&hir::Body<'_>>,
+ err: &mut rustc_errors::DiagnosticBuilder<'_, ()>,
+ ) -> bool {
+ let mut has_litstring = false;
+ let Some(opt_body) = opt_body else {return false;};
+ let mut visitor = CollectLitsVisitor { lit_exprs: vec![] };
+ intravisit::walk_body(&mut visitor, opt_body);
+ for lit_expr in visitor.lit_exprs {
+ let hir::ExprKind::Lit(litx) = &lit_expr.kind else { continue };
+ let rustc_ast::LitKind::Str(syb, _) = litx.node else{ continue; };
+ let name_str: &str = syb.as_str();
+ let mut name_pa = String::from("{");
+ name_pa.push_str(&name);
+ name_pa.push('}');
+ if name_str.contains(&name_pa) {
+ err.span_label(
+ lit_expr.span,
+ "you might have meant to use string interpolation in this string literal",
+ );
+ err.multipart_suggestion(
+ "string interpolation only works in `format!` invocations",
+ vec![
+ (lit_expr.span.shrink_to_lo(), "format!(".to_string()),
+ (lit_expr.span.shrink_to_hi(), ")".to_string()),
+ ],
+ Applicability::MachineApplicable,
+ );
+ has_litstring = true;
+ }
+ }
+ has_litstring
+ }
+
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_unused_assign(hir_id, spans, var, |name| {
pub vis_span: Span,
}
+#[derive(SessionDiagnostic)]
+#[diag(privacy::report_access_level)]
+pub struct ReportAccessLevel {
+ #[primary_span]
+ pub span: Span,
+ pub descr: String,
+}
+
#[derive(LintDiagnostic)]
#[diag(privacy::from_private_dep_in_public_interface)]
pub struct FromPrivateDependencyInPublicInterface<'a> {
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
+#[macro_use]
+extern crate tracing;
+
mod errors;
use rustc_ast::MacroDef;
use rustc_middle::ty::{TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
use rustc_session::lint;
use rustc_span::hygiene::Transparency;
-use rustc_span::symbol::{kw, Ident};
+use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Span;
use std::marker::PhantomData;
use errors::{
FieldIsPrivate, FieldIsPrivateLabel, FromPrivateDependencyInPublicInterface, InPublicInterface,
- InPublicInterfaceTraits, ItemIsPrivate, PrivateInPublicLint, UnnamedItemIsPrivate,
+ InPublicInterfaceTraits, ItemIsPrivate, PrivateInPublicLint, ReportAccessLevel,
+ UnnamedItemIsPrivate,
};
////////////////////////////////////////////////////////////////////////////////
}
}
+////////////////////////////////////////////////////////////////////////////////
+/// Visitor, used for AccessLevels table checking
+////////////////////////////////////////////////////////////////////////////////
+pub struct TestReachabilityVisitor<'tcx, 'a> {
+ tcx: TyCtxt<'tcx>,
+ access_levels: &'a AccessLevels,
+}
+
+impl<'tcx, 'a> TestReachabilityVisitor<'tcx, 'a> {
+ fn access_level_diagnostic(&mut self, def_id: LocalDefId) {
+ if self.tcx.has_attr(def_id.to_def_id(), sym::rustc_access_level) {
+ let access_level = format!("{:?}", self.access_levels.map.get(&def_id));
+ let span = self.tcx.def_span(def_id.to_def_id());
+ self.tcx.sess.emit_err(ReportAccessLevel { span, descr: access_level });
+ }
+ }
+}
+
+impl<'tcx, 'a> Visitor<'tcx> for TestReachabilityVisitor<'tcx, 'a> {
+ fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
+ self.access_level_diagnostic(item.def_id);
+
+ match item.kind {
+ hir::ItemKind::Enum(ref def, _) => {
+ for variant in def.variants.iter() {
+ let variant_id = self.tcx.hir().local_def_id(variant.id);
+ self.access_level_diagnostic(variant_id);
+ for field in variant.data.fields() {
+ let def_id = self.tcx.hir().local_def_id(field.hir_id);
+ self.access_level_diagnostic(def_id);
+ }
+ }
+ }
+ hir::ItemKind::Struct(ref def, _) | hir::ItemKind::Union(ref def, _) => {
+ for field in def.fields() {
+ let def_id = self.tcx.hir().local_def_id(field.hir_id);
+ self.access_level_diagnostic(def_id);
+ }
+ }
+ _ => {}
+ }
+ }
+
+ fn visit_trait_item(&mut self, item: &'tcx hir::TraitItem<'tcx>) {
+ self.access_level_diagnostic(item.def_id);
+ }
+ fn visit_impl_item(&mut self, item: &'tcx hir::ImplItem<'tcx>) {
+ self.access_level_diagnostic(item.def_id);
+ }
+ fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
+ self.access_level_diagnostic(item.def_id);
+ }
+}
+
//////////////////////////////////////////////////////////////////////////////////////
/// Name privacy visitor, checks privacy and reports violations.
/// Most of name privacy checks are performed during the main resolution phase,
fn leaks_private_dep(&self, item_id: DefId) -> bool {
let ret = self.required_visibility.is_public() && self.tcx.is_private_dep(item_id.krate);
- tracing::debug!("leaks_private_dep(item_id={:?})={}", item_id, ret);
+ debug!("leaks_private_dep(item_id={:?})={}", item_id, ret);
ret
}
}
}
}
+ let mut check_visitor = TestReachabilityVisitor { tcx, access_levels: &visitor.access_levels };
+ tcx.hir().visit_all_item_likes_in_crate(&mut check_visitor);
+
tcx.arena.alloc(visitor.access_levels)
}
[dependencies]
measureme = "10.0.0"
-rustc-rayon-core = { version = "0.4.0", optional = true }
rustc_ast = { path = "../rustc_ast" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_macros = { path = "../rustc_macros" }
rustc_middle = { path = "../rustc_middle" }
rustc_query_system = { path = "../rustc_query_system" }
+rustc-rayon-core = { version = "0.4.0", optional = true }
rustc_serialize = { path = "../rustc_serialize" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
+thin-vec = "0.2.8"
tracing = "0.1"
[features]
use crate::keys::Key;
use crate::{on_disk_cache, Queries};
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_data_structures::sync::Lock;
+use rustc_errors::{Diagnostic, Handler};
use rustc_middle::dep_graph::{self, DepKind, DepNodeIndex, SerializedDepNodeIndex};
use rustc_middle::ty::tls::{self, ImplicitCtxt};
use rustc_middle::ty::{self, TyCtxt};
use rustc_query_system::query::{
QueryContext, QueryJobId, QueryMap, QuerySideEffects, QueryStackFrame,
};
-
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_data_structures::sync::Lock;
-use rustc_data_structures::thin_vec::ThinVec;
-use rustc_errors::{Diagnostic, Handler};
-
use std::any::Any;
use std::num::NonZeroU64;
+use thin_vec::ThinVec;
#[derive(Copy, Clone)]
pub struct QueryCtxt<'tcx> {
$($(#[$attr])*
#[inline(always)]
- #[tracing::instrument(level = "trace", skip(self, tcx))]
+ #[tracing::instrument(level = "trace", skip(self, tcx), ret)]
fn $name(
&'tcx self,
tcx: TyCtxt<'tcx>,
doctest = false
[dependencies]
+parking_lot = "0.11"
rustc_arena = { path = "../rustc_arena" }
-tracing = "0.1"
-rustc-rayon-core = { version = "0.4.0", optional = true }
rustc_ast = { path = "../rustc_ast" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
rustc_macros = { path = "../rustc_macros" }
+rustc-rayon-core = { version = "0.4.0", optional = true }
rustc_serialize = { path = "../rustc_serialize" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
-parking_lot = "0.11"
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
+thin-vec = "0.2.8"
+tracing = "0.1"
[features]
rustc_use_parallel_compiler = ["rustc-rayon-core"]
--- /dev/null
+use rustc_errors::AddSubdiagnostic;
+use rustc_span::Span;
+
+pub struct CycleStack {
+ pub span: Span,
+ pub desc: String,
+}
+
+impl AddSubdiagnostic for CycleStack {
+ fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+ diag.span_note(self.span, &format!("...which requires {}...", self.desc));
+ }
+}
+
+#[derive(SessionSubdiagnostic)]
+pub enum StackCount {
+ #[note(query_system::cycle_stack_single)]
+ Single,
+ #[note(query_system::cycle_stack_multiple)]
+ Multiple,
+}
+
+#[derive(SessionSubdiagnostic)]
+pub enum Alias {
+ #[note(query_system::cycle_recursive_ty_alias)]
+ #[help(query_system::cycle_recursive_ty_alias_help1)]
+ #[help(query_system::cycle_recursive_ty_alias_help2)]
+ Ty,
+ #[note(query_system::cycle_recursive_trait_alias)]
+ Trait,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[note(query_system::cycle_usage)]
+pub struct CycleUsage {
+ #[primary_span]
+ pub span: Span,
+ pub usage: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(query_system::cycle, code = "E0391")]
+pub struct Cycle {
+ #[primary_span]
+ pub span: Span,
+ pub stack_bottom: String,
+ #[subdiagnostic]
+ pub cycle_stack: Vec<CycleStack>,
+ #[subdiagnostic]
+ pub stack_count: StackCount,
+ #[subdiagnostic]
+ pub alias: Option<Alias>,
+ #[subdiagnostic]
+ pub cycle_usage: Option<CycleUsage>,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(query_system::reentrant)]
+pub struct Reentrant;
+
+#[derive(SessionDiagnostic)]
+#[diag(query_system::increment_compilation)]
+#[help]
+#[note(query_system::increment_compilation_note1)]
+#[note(query_system::increment_compilation_note2)]
+pub struct IncrementCompilation {
+ pub run_cmd: String,
+ pub dep_node: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(query_system::query_overflow)]
+pub struct QueryOverflow;
#![feature(min_specialization)]
#![feature(extern_types)]
#![allow(rustc::potential_query_instability)]
+// #![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
#[macro_use]
extern crate tracing;
pub mod cache;
pub mod dep_graph;
+mod error;
pub mod ich;
pub mod query;
+use crate::error::CycleStack;
use crate::query::plumbing::CycleError;
use crate::query::{QueryContext, QueryStackFrame};
-use rustc_hir::def::DefKind;
use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{
- struct_span_err, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, Level,
-};
-use rustc_session::Session;
+use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, Level};
+use rustc_hir::def::DefKind;
+use rustc_session::{Session, SessionDiagnostic};
use rustc_span::Span;
use std::hash::Hash;
assert!(!stack.is_empty());
let span = stack[0].query.default_span(stack[1 % stack.len()].span);
- let mut err =
- struct_span_err!(sess, span, E0391, "cycle detected when {}", stack[0].query.description);
+
+ let mut cycle_stack = Vec::new();
+
+ use crate::error::StackCount;
+ let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple };
for i in 1..stack.len() {
let query = &stack[i].query;
let span = query.default_span(stack[(i + 1) % stack.len()].span);
- err.span_note(span, &format!("...which requires {}...", query.description));
- }
-
- if stack.len() == 1 {
- err.note(&format!("...which immediately requires {} again", stack[0].query.description));
- } else {
- err.note(&format!(
- "...which again requires {}, completing the cycle",
- stack[0].query.description
- ));
- }
-
- if stack.iter().all(|entry| {
- entry
- .query
- .def_kind
- .map_or(false, |def_kind| matches!(def_kind, DefKind::TyAlias | DefKind::TraitAlias))
- }) {
- if stack.iter().all(|entry| {
- entry.query.def_kind.map_or(false, |def_kind| matches!(def_kind, DefKind::TyAlias))
- }) {
- err.note("type aliases cannot be recursive");
- err.help("consider using a struct, enum, or union instead to break the cycle");
- err.help("see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information");
- } else {
- err.note("trait aliases cannot be recursive");
- }
+ cycle_stack.push(CycleStack { span, desc: query.description.to_owned() });
}
+ let mut cycle_usage = None;
if let Some((span, query)) = usage {
- err.span_note(query.default_span(span), &format!("cycle used when {}", query.description));
+ cycle_usage = Some(crate::error::CycleUsage {
+ span: query.default_span(span),
+ usage: query.description,
+ });
}
- err
+ let alias = if stack.iter().all(|entry| entry.query.def_kind == Some(DefKind::TyAlias)) {
+ Some(crate::error::Alias::Ty)
+ } else if stack.iter().all(|entry| entry.query.def_kind == Some(DefKind::TraitAlias)) {
+ Some(crate::error::Alias::Trait)
+ } else {
+ None
+ };
+
+ let cycle_diag = crate::error::Cycle {
+ span,
+ cycle_stack,
+ stack_bottom: stack[0].query.description.to_owned(),
+ alias,
+ cycle_usage: cycle_usage,
+ stack_count,
+ };
+
+ cycle_diag.into_diagnostic(&sess.parse_sess)
}
pub fn print_query_stack<CTX: QueryContext>(
pub use self::config::{QueryConfig, QueryDescription, QueryVTable};
use crate::dep_graph::{DepContext, DepNodeIndex, HasDepContext, SerializedDepNodeIndex};
-
use rustc_data_structures::sync::Lock;
-use rustc_data_structures::thin_vec::ThinVec;
use rustc_errors::Diagnostic;
use rustc_hir::def::DefKind;
use rustc_span::Span;
+use thin_vec::ThinVec;
/// Description of a frame in the query stack.
///
) -> R;
fn depth_limit_error(&self) {
- self.dep_context().sess().fatal("queries overflow the depth limit!");
+ self.dep_context().sess().emit_fatal(crate::error::QueryOverflow);
}
}
#[cfg(parallel_compiler)]
use rustc_data_structures::sharded::Sharded;
use rustc_data_structures::sync::Lock;
-use rustc_data_structures::thin_vec::ThinVec;
use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, FatalError};
use rustc_session::Session;
use rustc_span::{Span, DUMMY_SP};
use std::hash::Hash;
use std::mem;
use std::ptr;
+use thin_vec::ThinVec;
pub struct QueryState<K> {
#[cfg(parallel_compiler)]
let old_in_panic = INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.replace(true));
if old_in_panic {
- sess.struct_err(
- "internal compiler error: re-entrant incremental verify failure, suppressing message",
- )
- .emit();
+ sess.emit_err(crate::error::Reentrant);
} else {
- sess.struct_err(&format!("internal compiler error: encountered incremental compilation error with {:?}", dep_node))
- .help(&format!("This is a known issue with the compiler. Run {} to allow your project to compile", run_cmd))
- .note("Please follow the instructions below to create a bug report with the provided information")
- .note("See <https://github.com/rust-lang/rust/issues/84970> for more information")
- .emit();
+ sess.emit_err(crate::error::IncrementCompilation {
+ run_cmd,
+ dep_node: format!("{:?}", dep_node),
+ });
panic!("Found unstable fingerprints for {:?}: {:?}", dep_node, result);
}
+use crate::imports::ImportKind;
+use crate::NameBinding;
+use crate::NameBindingKind;
+use crate::Resolver;
use rustc_ast::ast;
use rustc_ast::visit;
use rustc_ast::visit::Visitor;
use rustc_ast::Crate;
use rustc_ast::EnumDef;
-use rustc_ast::ForeignMod;
use rustc_ast::NodeId;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::def_id::CRATE_DEF_ID;
use rustc_middle::middle::privacy::AccessLevel;
-use rustc_middle::ty::Visibility;
+use rustc_middle::ty::DefIdTree;
use rustc_span::sym;
-use crate::imports::ImportKind;
-use crate::BindingKey;
-use crate::NameBinding;
-use crate::NameBindingKind;
-use crate::Resolver;
-
pub struct AccessLevelsVisitor<'r, 'a> {
r: &'r mut Resolver<'a>,
- prev_level: Option<AccessLevel>,
changed: bool,
}
/// For now, this doesn't resolve macros (FIXME) and cannot resolve Impl, as we
/// need access to a TyCtxt for that.
pub fn compute_access_levels<'c>(r: &'r mut Resolver<'a>, krate: &'c Crate) {
- let mut visitor =
- AccessLevelsVisitor { r, changed: false, prev_level: Some(AccessLevel::Public) };
+ let mut visitor = AccessLevelsVisitor { r, changed: false };
visitor.set_access_level_def_id(CRATE_DEF_ID, Some(AccessLevel::Public));
- visitor.set_exports_access_level(CRATE_DEF_ID);
+ visitor.set_bindings_access_level(CRATE_DEF_ID);
while visitor.changed {
visitor.reset();
visit::walk_crate(&mut visitor, krate);
}
- tracing::info!("resolve::access_levels: {:#?}", r.access_levels);
+ info!("resolve::access_levels: {:#?}", r.access_levels);
}
fn reset(&mut self) {
self.changed = false;
- self.prev_level = Some(AccessLevel::Public);
}
- /// Update the access level of the exports of the given module accordingly. The module access
+ /// Update the access level of the bindings in the given module accordingly. The module access
/// level has to be Exported or Public.
/// This will also follow `use` chains (see PrivacyVisitor::set_import_binding_access_level).
- fn set_exports_access_level(&mut self, module_id: LocalDefId) {
+ fn set_bindings_access_level(&mut self, module_id: LocalDefId) {
assert!(self.r.module_map.contains_key(&&module_id.to_def_id()));
-
+ let module_level = self.r.access_levels.map.get(&module_id).copied();
+ if !module_level.is_some() {
+ return;
+ }
// Set the given binding access level to `AccessLevel::Public` and
// sets the rest of the `use` chain to `AccessLevel::Exported` until
// we hit the actual exported item.
}
};
- let module_level = self.r.access_levels.map.get(&module_id).copied();
- assert!(module_level >= Some(AccessLevel::Exported));
-
- if let Some(exports) = self.r.reexport_map.get(&module_id) {
- let pub_exports = exports
- .iter()
- .filter(|ex| ex.vis == Visibility::Public)
- .cloned()
- .collect::<Vec<_>>();
-
- let module = self.r.get_module(module_id.to_def_id()).unwrap();
- for export in pub_exports.into_iter() {
- if let Some(export_def_id) = export.res.opt_def_id().and_then(|id| id.as_local()) {
- self.set_access_level_def_id(export_def_id, Some(AccessLevel::Exported));
- }
-
- if let Some(ns) = export.res.ns() {
- let key = BindingKey { ident: export.ident, ns, disambiguator: 0 };
- let name_res = self.r.resolution(module, key);
- if let Some(binding) = name_res.borrow().binding() {
- set_import_binding_access_level(self, binding, module_level)
- }
+ let module = self.r.get_module(module_id.to_def_id()).unwrap();
+ let resolutions = self.r.resolutions(module);
+
+ for (.., name_resolution) in resolutions.borrow().iter() {
+ if let Some(binding) = name_resolution.borrow().binding() && binding.vis.is_public() && !binding.is_ambiguity() {
+ let access_level = match binding.is_import() {
+ true => {
+ set_import_binding_access_level(self, binding, module_level);
+ Some(AccessLevel::Exported)
+ },
+ false => module_level,
+ };
+ if let Some(def_id) = binding.res().opt_def_id().and_then(|id| id.as_local()) {
+ self.set_access_level_def_id(def_id, access_level);
}
}
}
impl<'r, 'ast> Visitor<'ast> for AccessLevelsVisitor<'ast, 'r> {
fn visit_item(&mut self, item: &'ast ast::Item) {
- let inherited_item_level = match item.kind {
+ let def_id = self.r.local_def_id(item.id);
+ // Set access level of nested items.
+ // If it's a mod, also make the visitor walk all of its items
+ match item.kind {
// Resolved in rustc_privacy when types are available
ast::ItemKind::Impl(..) => return,
- // Only exported `macro_rules!` items are public, but they always are
- ast::ItemKind::MacroDef(ref macro_def) if macro_def.macro_rules => {
- let is_macro_export =
- item.attrs.iter().any(|attr| attr.has_name(sym::macro_export));
- if is_macro_export { Some(AccessLevel::Public) } else { None }
- }
-
- // Foreign modules inherit level from parents.
- ast::ItemKind::ForeignMod(..) => self.prev_level,
-
- // Other `pub` items inherit levels from parents.
- ast::ItemKind::ExternCrate(..)
- | ast::ItemKind::Use(..)
- | ast::ItemKind::Static(..)
- | ast::ItemKind::Const(..)
- | ast::ItemKind::Fn(..)
- | ast::ItemKind::Mod(..)
- | ast::ItemKind::GlobalAsm(..)
- | ast::ItemKind::TyAlias(..)
- | ast::ItemKind::Enum(..)
- | ast::ItemKind::Struct(..)
- | ast::ItemKind::Union(..)
- | ast::ItemKind::Trait(..)
- | ast::ItemKind::TraitAlias(..)
- | ast::ItemKind::MacroDef(..) => {
- if item.vis.kind.is_pub() {
- self.prev_level
- } else {
- None
- }
- }
-
// Should be unreachable at this stage
ast::ItemKind::MacCall(..) => panic!(
"ast::ItemKind::MacCall encountered, this should not anymore appear at this stage"
),
- };
- let access_level = self.set_access_level(item.id, inherited_item_level);
+ // Foreign modules inherit level from parents.
+ ast::ItemKind::ForeignMod(..) => {
+ let parent_level =
+ self.r.access_levels.map.get(&self.r.local_parent(def_id)).copied();
+ self.set_access_level(item.id, parent_level);
+ }
- // Set access level of nested items.
- // If it's a mod, also make the visitor walk all of its items
- match item.kind {
- ast::ItemKind::Mod(..) => {
- if access_level.is_some() {
- self.set_exports_access_level(self.r.local_def_id(item.id));
+ // Only exported `macro_rules!` items are public, but they always are
+ ast::ItemKind::MacroDef(ref macro_def) if macro_def.macro_rules => {
+ if item.attrs.iter().any(|attr| attr.has_name(sym::macro_export)) {
+ self.set_access_level(item.id, Some(AccessLevel::Public));
}
+ }
- let orig_level = std::mem::replace(&mut self.prev_level, access_level);
+ ast::ItemKind::Mod(..) => {
+ self.set_bindings_access_level(def_id);
visit::walk_item(self, item);
- self.prev_level = orig_level;
}
- ast::ItemKind::ForeignMod(ForeignMod { ref items, .. }) => {
- for nested in items {
- if nested.vis.kind.is_pub() {
- self.set_access_level(nested.id, access_level);
- }
- }
- }
ast::ItemKind::Enum(EnumDef { ref variants }, _) => {
+ self.set_bindings_access_level(def_id);
for variant in variants {
- let variant_level = self.set_access_level(variant.id, access_level);
- if let Some(ctor_id) = variant.data.ctor_id() {
- self.set_access_level(ctor_id, access_level);
- }
-
+ let variant_def_id = self.r.local_def_id(variant.id);
+ let variant_level = self.r.access_levels.map.get(&variant_def_id).copied();
for field in variant.data.fields() {
self.set_access_level(field.id, variant_level);
}
}
}
- ast::ItemKind::Struct(ref def, _) | ast::ItemKind::Union(ref def, _) => {
- if let Some(ctor_id) = def.ctor_id() {
- self.set_access_level(ctor_id, access_level);
- }
+ ast::ItemKind::Struct(ref def, _) | ast::ItemKind::Union(ref def, _) => {
+ let inherited_level = self.r.access_levels.map.get(&def_id).copied();
for field in def.fields() {
if field.vis.kind.is_pub() {
- self.set_access_level(field.id, access_level);
+ self.set_access_level(field.id, inherited_level);
}
}
}
- ast::ItemKind::Trait(ref trait_kind) => {
- for nested in trait_kind.items.iter() {
- self.set_access_level(nested.id, access_level);
- }
+
+ ast::ItemKind::Trait(..) => {
+ self.set_bindings_access_level(def_id);
}
ast::ItemKind::ExternCrate(..)
| ast::ItemKind::TraitAlias(..)
| ast::ItemKind::MacroDef(..)
| ast::ItemKind::Fn(..) => return,
-
- // Unreachable kinds
- ast::ItemKind::Impl(..) | ast::ItemKind::MacCall(..) => unreachable!(),
}
}
}
use std::cell::Cell;
use std::ptr;
-use tracing::debug;
type Res = def::Res<NodeId>;
self.insert_field_names(def_id, field_names);
}
Res::Def(DefKind::AssocFn, def_id) => {
- if cstore.fn_has_self_parameter_untracked(def_id) {
+ if cstore.fn_has_self_parameter_untracked(def_id, self.r.session) {
self.r.has_self.insert(def_id);
}
}
use rustc_span::hygiene::LocalExpnId;
use rustc_span::symbol::sym;
use rustc_span::Span;
-use tracing::debug;
pub(crate) fn collect_definitions(
resolver: &mut Resolver<'_>,
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, Span};
-use tracing::debug;
use crate::imports::{Import, ImportKind, ImportResolver};
use crate::late::{PatternSource, Rib};
Scope::Module(module, _) => {
this.add_module_candidates(module, &mut suggestions, filter_fn);
}
- Scope::RegisteredAttrs => {
- let res = Res::NonMacroAttr(NonMacroAttrKind::Registered);
- if filter_fn(res) {
- suggestions.extend(
- this.registered_attrs
- .iter()
- .map(|ident| TypoSuggestion::typo_from_res(ident.name, res)),
- );
- }
- }
Scope::MacroUsePrelude => {
suggestions.extend(this.macro_use_prelude.iter().filter_map(
|(name, binding)| {
}
Scope::CrateRoot => true,
Scope::Module(..) => true,
- Scope::RegisteredAttrs => use_prelude,
Scope::MacroUsePrelude => use_prelude || rust_2015,
Scope::BuiltinAttrs => true,
Scope::ExternPrelude => use_prelude || is_absolute_path,
match ns {
TypeNS => Scope::ExternPrelude,
ValueNS => Scope::StdLibPrelude,
- MacroNS => Scope::RegisteredAttrs,
+ MacroNS => Scope::MacroUsePrelude,
}
}
}
}
- Scope::RegisteredAttrs => Scope::MacroUsePrelude,
Scope::MacroUsePrelude => Scope::StdLibPrelude,
Scope::BuiltinAttrs => break, // nowhere else to search
Scope::ExternPrelude if is_absolute_path => break,
///
/// Invariant: This must only be called during main resolution, not during
/// import resolution.
- #[tracing::instrument(level = "debug", skip(self, ribs))]
+ #[instrument(level = "debug", skip(self, ribs))]
pub(crate) fn resolve_ident_in_lexical_scope(
&mut self,
mut ident: Ident,
/// expansion and import resolution (perhaps they can be merged in the future).
/// The function is used for resolving initial segments of macro paths (e.g., `foo` in
/// `foo::bar!(); or `foo!();`) and also for import paths on 2018 edition.
- #[tracing::instrument(level = "debug", skip(self, scope_set))]
+ #[instrument(level = "debug", skip(self, scope_set))]
pub(crate) fn early_resolve_ident_in_lexical_scope(
&mut self,
orig_ident: Ident,
Err((Determinacy::Determined, _)) => Err(Determinacy::Determined),
}
}
- Scope::RegisteredAttrs => match this.registered_attrs.get(&ident).cloned() {
- Some(ident) => ok(
- Res::NonMacroAttr(NonMacroAttrKind::Registered),
- ident.span,
- this.arenas,
- ),
- None => Err(Determinacy::Determined),
- },
Scope::MacroUsePrelude => {
match this.macro_use_prelude.get(&ident.name).cloned() {
Some(binding) => Ok((binding, Flags::MISC_FROM_PRELUDE)),
Err(Determinacy::determined(determinacy == Determinacy::Determined || force))
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
pub(crate) fn maybe_resolve_ident_in_module(
&mut self,
module: ModuleOrUniformRoot<'a>,
.map_err(|(determinacy, _)| determinacy)
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
pub(crate) fn resolve_ident_in_module(
&mut self,
module: ModuleOrUniformRoot<'a>,
.map_err(|(determinacy, _)| determinacy)
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn resolve_ident_in_module_ext(
&mut self,
module: ModuleOrUniformRoot<'a>,
)
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn resolve_ident_in_module_unadjusted(
&mut self,
module: ModuleOrUniformRoot<'a>,
/// Attempts to resolve `ident` in namespaces `ns` of `module`.
/// Invariant: if `finalize` is `Some`, expansion and import resolution must be complete.
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn resolve_ident_in_module_unadjusted_ext(
&mut self,
module: ModuleOrUniformRoot<'a>,
}
/// Validate a local resolution (from ribs).
- #[tracing::instrument(level = "debug", skip(self, all_ribs))]
+ #[instrument(level = "debug", skip(self, all_ribs))]
fn validate_res_from_ribs(
&mut self,
rib_index: usize,
res
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
pub(crate) fn maybe_resolve_path(
&mut self,
path: &[Segment],
self.resolve_path_with_ribs(path, opt_ns, parent_scope, None, None, None)
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
pub(crate) fn resolve_path(
&mut self,
path: &[Segment],
use rustc_span::symbol::{kw, Ident, Symbol};
use rustc_span::Span;
-use tracing::*;
-
use std::cell::Cell;
use std::{mem, ptr};
if let Some(def_id) = module.opt_def_id() {
let mut reexports = Vec::new();
- module.for_each_child(self.r, |_, ident, _, binding| {
- // FIXME: Consider changing the binding inserted by `#[macro_export] macro_rules`
- // into the crate root to actual `NameBindingKind::Import`.
- if binding.is_import()
- || matches!(binding.kind, NameBindingKind::Res(_, _is_macro_export @ true))
- {
- let res = binding.res().expect_non_local();
- // Ambiguous imports are treated as errors at this point and are
- // not exposed to other crates (see #36837 for more details).
- if res != def::Res::Err && !binding.is_ambiguity() {
- reexports.push(ModChild {
- ident,
- res,
- vis: binding.vis,
- span: binding.span,
- macro_rules: false,
- });
- }
+ module.for_each_child(self.r, |this, ident, _, binding| {
+ if let Some(res) = this.is_reexport(binding) {
+ reexports.push(ModChild {
+ ident,
+ res,
+ vis: binding.vis,
+ span: binding.span,
+ macro_rules: false,
+ });
}
});
use rustc_span::source_map::{respan, Spanned};
use std::collections::{hash_map::Entry, BTreeSet};
use std::mem::{replace, take};
-use tracing::debug;
mod diagnostics;
pub(crate) mod lifetimes;
})
}
- #[tracing::instrument(level = "debug", skip(self, work))]
+ #[instrument(level = "debug", skip(self, work))]
fn with_lifetime_rib<T>(
&mut self,
kind: LifetimeRibKind,
ret
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn resolve_lifetime(&mut self, lifetime: &'ast Lifetime, use_ctxt: visit::LifetimeCtxt) {
let ident = lifetime.ident;
self.record_lifetime_res(lifetime.id, LifetimeRes::Error, LifetimeElisionCandidate::Named);
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn resolve_anonymous_lifetime(&mut self, lifetime: &Lifetime, elided: bool) {
debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime);
self.report_missing_lifetime_specifiers(vec![missing_lifetime], None);
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn resolve_elided_lifetime(&mut self, anchor_id: NodeId, span: Span) {
let id = self.r.next_node_id();
let lt = Lifetime { id, ident: Ident::new(kw::UnderscoreLifetime, span) };
self.resolve_anonymous_lifetime(<, true);
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn create_fresh_lifetime(&mut self, id: NodeId, ident: Ident, binder: NodeId) -> LifetimeRes {
debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
debug!(?ident.span);
res
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn resolve_elided_lifetimes_in_path(
&mut self,
path_id: NodeId,
}
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn record_lifetime_res(
&mut self,
id: NodeId,
}
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn record_lifetime_param(&mut self, id: NodeId, res: LifetimeRes) {
if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) {
panic!(
}
/// Perform resolution of a function signature, accounting for lifetime elision.
- #[tracing::instrument(level = "debug", skip(self, inputs))]
+ #[instrument(level = "debug", skip(self, inputs))]
fn resolve_fn_signature(
&mut self,
fn_id: NodeId,
source: PathSource<'ast>,
finalize: Finalize,
) -> PartialRes {
- tracing::debug!(
+ debug!(
"smart_resolve_path_fragment(qself={:?}, path={:?}, finalize={:?})",
- qself,
- path,
- finalize,
+ qself, path, finalize,
);
let ns = source.namespace();
use std::iter;
use std::ops::Deref;
-use tracing::debug;
-
type Res = def::Res<ast::NodeId>;
/// A field or associated item from self type suggested in case of resolution failure.
}
};
- let mut suggestable_variants = variants
+ let suggestable_variants = variants
.iter()
.filter(|(_, def_id, kind)| !needs_placeholder(*def_id, *kind))
.map(|(variant, _, kind)| (path_names_to_string(variant), kind))
CtorKind::Fictive => format!("({} {{}})", variant),
})
.collect::<Vec<_>>();
+ let no_suggestable_variant = suggestable_variants.is_empty();
- if !suggestable_variants.is_empty() {
+ if !no_suggestable_variant {
let msg = if suggestable_variants.len() == 1 {
"you might have meant to use the following enum variant"
} else {
err.span_suggestions(
span,
msg,
- suggestable_variants.drain(..),
+ suggestable_variants.into_iter(),
Applicability::MaybeIncorrect,
);
}
.collect::<Vec<_>>();
if !suggestable_variants_with_placeholders.is_empty() {
- let msg = match (
- suggestable_variants.is_empty(),
- suggestable_variants_with_placeholders.len(),
- ) {
- (true, 1) => "the following enum variant is available",
- (true, _) => "the following enum variants are available",
- (false, 1) => "alternatively, the following enum variant is available",
- (false, _) => "alternatively, the following enum variants are also available",
- };
+ let msg =
+ match (no_suggestable_variant, suggestable_variants_with_placeholders.len()) {
+ (true, 1) => "the following enum variant is available",
+ (true, _) => "the following enum variants are available",
+ (false, 1) => "alternatively, the following enum variant is available",
+ (false, _) => {
+ "alternatively, the following enum variants are also available"
+ }
+ };
err.span_suggestions(
span,
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{DefIdMap, LocalDefId};
+use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{GenericArg, GenericParam, GenericParamKind, HirIdMap, LifetimeName, Node};
use rustc_middle::bug;
use rustc_middle::hir::map::Map;
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::resolve_lifetime::*;
-use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
+use rustc_middle::ty::{self, DefIdTree, TyCtxt};
use rustc_span::def_id::DefId;
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
-use std::borrow::Cow;
use std::fmt;
-use std::mem::take;
trait RegionExt {
- fn early(hir_map: Map<'_>, index: &mut u32, param: &GenericParam<'_>) -> (LocalDefId, Region);
+ fn early(hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region);
fn late(index: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region);
fn shifted(self, amount: u32) -> Region;
fn shifted_out_to_binder(self, binder: ty::DebruijnIndex) -> Region;
-
- fn subst<'a, L>(self, params: L, map: &NamedRegionMap) -> Option<Region>
- where
- L: Iterator<Item = &'a hir::Lifetime>;
}
impl RegionExt for Region {
- fn early(hir_map: Map<'_>, index: &mut u32, param: &GenericParam<'_>) -> (LocalDefId, Region) {
- let i = *index;
- *index += 1;
+ fn early(hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region) {
let def_id = hir_map.local_def_id(param.hir_id);
- debug!("Region::early: index={} def_id={:?}", i, def_id);
- (def_id, Region::EarlyBound(i, def_id.to_def_id()))
+ debug!("Region::early: def_id={:?}", def_id);
+ (def_id, Region::EarlyBound(def_id.to_def_id()))
}
fn late(idx: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region) {
match *self {
Region::Static => None,
- Region::EarlyBound(_, id) | Region::LateBound(_, _, id) | Region::Free(_, id) => {
- Some(id)
- }
+ Region::EarlyBound(id) | Region::LateBound(_, _, id) | Region::Free(_, id) => Some(id),
}
}
_ => self,
}
}
-
- fn subst<'a, L>(self, mut params: L, map: &NamedRegionMap) -> Option<Region>
- where
- L: Iterator<Item = &'a hir::Lifetime>,
- {
- if let Region::EarlyBound(index, _) = self {
- params.nth(index as usize).and_then(|lifetime| map.defs.get(&lifetime.hir_id).cloned())
- } else {
- Some(self)
- }
- }
}
/// Maps the id of each lifetime reference to the lifetime decl
/// be false if the `Item` we are resolving lifetimes for is not a trait or
/// we eventually need lifetimes resolve for trait items.
trait_definition_only: bool,
-
- /// Cache for cross-crate per-definition object lifetime defaults.
- xcrate_object_lifetime_defaults: DefIdMap<Vec<ObjectLifetimeDefault>>,
}
#[derive(Debug)]
/// for diagnostics.
lifetimes: FxIndexMap<LocalDefId, Region>,
- /// if we extend this scope with another scope, what is the next index
- /// we should use for an early-bound region?
- next_early_index: u32,
-
- /// Whether or not this binder would serve as the parent
- /// binder for opaque types introduced within. For example:
- ///
- /// ```text
- /// fn foo<'a>() -> impl for<'b> Trait<Item = impl Trait2<'a>>
- /// ```
- ///
- /// Here, the opaque types we create for the `impl Trait`
- /// and `impl Trait2` references will both have the `foo` item
- /// as their parent. When we get to `impl Trait2`, we find
- /// that it is nested within the `for<>` binder -- this flag
- /// allows us to skip that when looking for the parent binder
- /// of the resulting opaque type.
- opaque_type_parent: bool,
-
scope_type: BinderScopeType,
/// The late bound vars for a given item are stored by `HirId` to be
impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
- Scope::Binder {
- lifetimes,
- next_early_index,
- opaque_type_parent,
- scope_type,
- hir_id,
- where_bound_origin,
- s: _,
- } => f
+ Scope::Binder { lifetimes, scope_type, hir_id, where_bound_origin, s: _ } => f
.debug_struct("Binder")
.field("lifetimes", lifetimes)
- .field("next_early_index", next_early_index)
- .field("opaque_type_parent", opaque_type_parent)
.field("scope_type", scope_type)
.field("hir_id", hir_id)
.field("where_bound_origin", where_bound_origin)
named_region_map: |tcx, id| resolve_lifetimes_for(tcx, id).defs.get(&id),
is_late_bound_map,
- object_lifetime_defaults: |tcx, id| match tcx.hir().find_by_def_id(id) {
- Some(Node::Item(item)) => compute_object_lifetime_defaults(tcx, item),
- _ => None,
- },
+ object_lifetime_default,
late_bound_vars_map: |tcx, id| resolve_lifetimes_for(tcx, id).late_bound_vars.get(&id),
..*providers
/// lifetimes into a single binder.) This requires us to resolve the
/// *trait definition* of `Sub`; basically just enough lifetime information
/// to look at the supertraits.
-#[tracing::instrument(level = "debug", skip(tcx))]
+#[instrument(level = "debug", skip(tcx))]
fn resolve_lifetimes_trait_definition(
tcx: TyCtxt<'_>,
local_def_id: LocalDefId,
/// Computes the `ResolveLifetimes` map that contains data for an entire `Item`.
/// You should not read the result of this query directly, but rather use
/// `named_region_map`, `is_late_bound_map`, etc.
-#[tracing::instrument(level = "debug", skip(tcx))]
+#[instrument(level = "debug", skip(tcx))]
fn resolve_lifetimes(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> ResolveLifetimes {
convert_named_region_map(do_resolve(tcx, local_def_id, false))
}
map: &mut named_region_map,
scope: ROOT_SCOPE,
trait_definition_only,
- xcrate_object_lifetime_defaults: Default::default(),
};
visitor.visit_item(item);
item
}
-/// In traits, there is an implicit `Self` type parameter which comes before the generics.
-/// We have to account for this when computing the index of the other generic parameters.
-/// This function returns whether there is such an implicit parameter defined on the given item.
-fn sub_items_have_self_param(node: &hir::ItemKind<'_>) -> bool {
- matches!(*node, hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..))
-}
-
fn late_region_as_bound_region<'tcx>(tcx: TyCtxt<'tcx>, region: &Region) -> ty::BoundVariableKind {
match region {
Region::LateBound(_, _, def_id) => {
}
}
- let next_early_index = self.next_early_index();
let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) =
bound_generic_params
.iter()
hir_id: e.hir_id,
lifetimes,
s: self.scope,
- next_early_index,
- opaque_type_parent: false,
scope_type: BinderScopeType::Normal,
where_bound_origin: None,
};
}
match item.kind {
hir::ItemKind::Fn(_, ref generics, _) => {
- self.visit_early_late(None, item.hir_id(), generics, |this| {
+ self.visit_early_late(item.hir_id(), generics, |this| {
intravisit::walk_item(this, item);
});
}
| hir::ItemKind::TraitAlias(ref generics, ..)
| hir::ItemKind::Impl(hir::Impl { ref generics, .. }) => {
// These kinds of items have only early-bound lifetime parameters.
- let mut index = if sub_items_have_self_param(&item.kind) {
- 1 // Self comes before lifetimes
- } else {
- 0
- };
- let mut non_lifetime_count = 0;
let lifetimes = generics
.params
.iter()
.filter_map(|param| match param.kind {
GenericParamKind::Lifetime { .. } => {
- Some(Region::early(self.tcx.hir(), &mut index, param))
- }
- GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
- non_lifetime_count += 1;
- None
+ Some(Region::early(self.tcx.hir(), param))
}
+ GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None,
})
.collect();
self.map.late_bound_vars.insert(item.hir_id(), vec![]);
let scope = Scope::Binder {
hir_id: item.hir_id(),
lifetimes,
- next_early_index: index + non_lifetime_count,
- opaque_type_parent: true,
scope_type: BinderScopeType::Normal,
s: ROOT_SCOPE,
where_bound_origin: None,
fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
match item.kind {
hir::ForeignItemKind::Fn(_, _, ref generics) => {
- self.visit_early_late(None, item.hir_id(), generics, |this| {
+ self.visit_early_late(item.hir_id(), generics, |this| {
intravisit::walk_foreign_item(this, item);
})
}
}
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
match ty.kind {
hir::TyKind::BareFn(ref c) => {
- let next_early_index = self.next_early_index();
let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) = c
.generic_params
.iter()
hir_id: ty.hir_id,
lifetimes,
s: self.scope,
- next_early_index,
- opaque_type_parent: false,
scope_type: BinderScopeType::Normal,
where_bound_origin: None,
};
// We want to start our early-bound indices at the end of the parent scope,
// not including any parent `impl Trait`s.
- let mut index = self.next_early_index_for_opaque_type();
- debug!(?index);
-
let mut lifetimes = FxIndexMap::default();
- let mut non_lifetime_count = 0;
debug!(?generics.params);
for param in generics.params {
match param.kind {
GenericParamKind::Lifetime { .. } => {
- let (def_id, reg) = Region::early(self.tcx.hir(), &mut index, ¶m);
+ let (def_id, reg) = Region::early(self.tcx.hir(), ¶m);
lifetimes.insert(def_id, reg);
}
- GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
- non_lifetime_count += 1;
- }
+ GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {}
}
}
- let next_early_index = index + non_lifetime_count;
self.map.late_bound_vars.insert(ty.hir_id, vec![]);
let scope = Scope::Binder {
hir_id: ty.hir_id,
lifetimes,
- next_early_index,
s: self.scope,
- opaque_type_parent: false,
scope_type: BinderScopeType::Normal,
where_bound_origin: None,
};
use self::hir::TraitItemKind::*;
match trait_item.kind {
Fn(_, _) => {
- let tcx = self.tcx;
- self.visit_early_late(
- Some(tcx.hir().get_parent_item(trait_item.hir_id())),
- trait_item.hir_id(),
- &trait_item.generics,
- |this| intravisit::walk_trait_item(this, trait_item),
- );
+ self.visit_early_late(trait_item.hir_id(), &trait_item.generics, |this| {
+ intravisit::walk_trait_item(this, trait_item)
+ });
}
Type(bounds, ref ty) => {
let generics = &trait_item.generics;
- let mut index = self.next_early_index();
- debug!("visit_ty: index = {}", index);
- let mut non_lifetime_count = 0;
let lifetimes = generics
.params
.iter()
.filter_map(|param| match param.kind {
GenericParamKind::Lifetime { .. } => {
- Some(Region::early(self.tcx.hir(), &mut index, param))
- }
- GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
- non_lifetime_count += 1;
- None
+ Some(Region::early(self.tcx.hir(), param))
}
+ GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None,
})
.collect();
self.map.late_bound_vars.insert(trait_item.hir_id(), vec![]);
let scope = Scope::Binder {
hir_id: trait_item.hir_id(),
lifetimes,
- next_early_index: index + non_lifetime_count,
s: self.scope,
- opaque_type_parent: true,
scope_type: BinderScopeType::Normal,
where_bound_origin: None,
};
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
use self::hir::ImplItemKind::*;
match impl_item.kind {
- Fn(..) => {
- let tcx = self.tcx;
- self.visit_early_late(
- Some(tcx.hir().get_parent_item(impl_item.hir_id())),
- impl_item.hir_id(),
- &impl_item.generics,
- |this| intravisit::walk_impl_item(this, impl_item),
- );
- }
+ Fn(..) => self.visit_early_late(impl_item.hir_id(), &impl_item.generics, |this| {
+ intravisit::walk_impl_item(this, impl_item)
+ }),
TyAlias(ref ty) => {
let generics = &impl_item.generics;
- let mut index = self.next_early_index();
- let mut non_lifetime_count = 0;
- debug!("visit_ty: index = {}", index);
let lifetimes: FxIndexMap<LocalDefId, Region> = generics
.params
.iter()
.filter_map(|param| match param.kind {
GenericParamKind::Lifetime { .. } => {
- Some(Region::early(self.tcx.hir(), &mut index, param))
- }
- GenericParamKind::Const { .. } | GenericParamKind::Type { .. } => {
- non_lifetime_count += 1;
- None
+ Some(Region::early(self.tcx.hir(), param))
}
+ GenericParamKind::Const { .. } | GenericParamKind::Type { .. } => None,
})
.collect();
self.map.late_bound_vars.insert(ty.hir_id, vec![]);
let scope = Scope::Binder {
hir_id: ty.hir_id,
lifetimes,
- next_early_index: index + non_lifetime_count,
s: self.scope,
- opaque_type_parent: true,
scope_type: BinderScopeType::Normal,
where_bound_origin: None,
};
}
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
match lifetime_ref.name {
hir::LifetimeName::Static => self.insert_lifetime(lifetime_ref, Region::Static),
})
.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
hir_id: bounded_ty.hir_id,
lifetimes,
s: this.scope,
- next_early_index,
- opaque_type_parent: false,
scope_type: BinderScopeType::Normal,
where_bound_origin: Some(origin),
};
hir_id: *hir_id,
lifetimes: FxIndexMap::default(),
s: self.scope,
- next_early_index: self.next_early_index(),
- opaque_type_parent: false,
scope_type,
where_bound_origin: None,
};
) {
debug!("visit_poly_trait_ref(trait_ref={:?})", trait_ref);
- let next_early_index = self.next_early_index();
let (mut binders, scope_type) = self.poly_trait_ref_binder_info();
let initial_bound_vars = binders.len() as u32;
hir_id: trait_ref.trait_ref.hir_ref_id,
lifetimes,
s: self.scope,
- next_early_index,
- opaque_type_parent: false,
scope_type,
where_bound_origin: None,
};
}
}
-fn compute_object_lifetime_defaults<'tcx>(
+fn object_lifetime_default<'tcx>(
tcx: TyCtxt<'tcx>,
- item: &hir::Item<'_>,
-) -> Option<&'tcx [ObjectLifetimeDefault]> {
- match item.kind {
- hir::ItemKind::Struct(_, ref generics)
- | hir::ItemKind::Union(_, ref generics)
- | hir::ItemKind::Enum(_, ref generics)
- | hir::ItemKind::OpaqueTy(hir::OpaqueTy {
- ref generics,
- origin: hir::OpaqueTyOrigin::TyAlias,
- ..
- })
- | hir::ItemKind::TyAlias(_, ref generics)
- | hir::ItemKind::Trait(_, _, ref generics, ..) => {
- let result = object_lifetime_defaults_for_item(tcx, generics);
-
- // Debugging aid.
- let attrs = tcx.hir().attrs(item.hir_id());
- if tcx.sess.contains_name(attrs, sym::rustc_object_lifetime_default) {
- let object_lifetime_default_reprs: String = result
- .iter()
- .map(|set| match *set {
- Set1::Empty => "BaseDefault".into(),
- Set1::One(Region::Static) => "'static".into(),
- Set1::One(Region::EarlyBound(mut i, _)) => generics
- .params
- .iter()
- .find_map(|param| match param.kind {
- GenericParamKind::Lifetime { .. } => {
- if i == 0 {
- return Some(param.name.ident().to_string().into());
- }
- i -= 1;
- None
- }
- _ => None,
- })
- .unwrap(),
- Set1::One(_) => bug!(),
- Set1::Many => "Ambiguous".into(),
- })
- .collect::<Vec<Cow<'static, str>>>()
- .join(",");
- tcx.sess.span_err(item.span, &object_lifetime_default_reprs);
- }
-
- Some(result)
- }
- _ => None,
- }
-}
-
-/// Scan the bounds and where-clauses on parameters to extract bounds
-/// of the form `T:'a` so as to determine the `ObjectLifetimeDefault`
-/// for each type parameter.
-fn object_lifetime_defaults_for_item<'tcx>(
- tcx: TyCtxt<'tcx>,
- generics: &hir::Generics<'_>,
-) -> &'tcx [ObjectLifetimeDefault] {
- fn add_bounds(set: &mut Set1<hir::LifetimeName>, bounds: &[hir::GenericBound<'_>]) {
- for bound in bounds {
- if let hir::GenericBound::Outlives(ref lifetime) = *bound {
- set.insert(lifetime.name.normalize_to_macros_2_0());
- }
- }
- }
-
- let process_param = |param: &hir::GenericParam<'_>| match param.kind {
+ param_def_id: DefId,
+) -> Option<ObjectLifetimeDefault> {
+ let param_def_id = param_def_id.expect_local();
+ let parent_def_id = tcx.local_parent(param_def_id);
+ let generics = tcx.hir().get_generics(parent_def_id)?;
+ let param_hir_id = tcx.local_def_id_to_hir_id(param_def_id);
+ let param = generics.params.iter().find(|p| p.hir_id == param_hir_id)?;
+
+ // Scan the bounds and where-clauses on parameters to extract bounds
+ // of the form `T:'a` so as to determine the `ObjectLifetimeDefault`
+ // for each type parameter.
+ match param.kind {
GenericParamKind::Lifetime { .. } => None,
GenericParamKind::Type { .. } => {
let mut set = Set1::Empty;
- let param_def_id = tcx.hir().local_def_id(param.hir_id);
- for predicate in generics.predicates {
- // Look for `type: ...` where clauses.
- let hir::WherePredicate::BoundPredicate(ref data) = *predicate else { continue };
-
+ // Look for `type: ...` where clauses.
+ for bound in generics.bounds_for_param(param_def_id) {
// Ignore `for<'a> type: ...` as they can change what
// lifetimes mean (although we could "just" handle it).
- if !data.bound_generic_params.is_empty() {
+ if !bound.bound_generic_params.is_empty() {
continue;
}
- let res = match data.bounded_ty.kind {
- hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => path.res,
- _ => continue,
- };
-
- if res == Res::Def(DefKind::TyParam, param_def_id.to_def_id()) {
- add_bounds(&mut set, &data.bounds);
+ for bound in bound.bounds {
+ if let hir::GenericBound::Outlives(ref lifetime) = *bound {
+ set.insert(lifetime.name.normalize_to_macros_2_0());
+ }
}
}
Some(match set {
- Set1::Empty => Set1::Empty,
- Set1::One(name) => {
- if name == hir::LifetimeName::Static {
- Set1::One(Region::Static)
- } else {
- generics
- .params
- .iter()
- .filter_map(|param| match param.kind {
- GenericParamKind::Lifetime { .. } => {
- let param_def_id = tcx.hir().local_def_id(param.hir_id);
- Some((
- param_def_id,
- hir::LifetimeName::Param(param_def_id, param.name),
- ))
- }
- _ => None,
- })
- .enumerate()
- .find(|&(_, (_, lt_name))| lt_name == name)
- .map_or(Set1::Many, |(i, (def_id, _))| {
- Set1::One(Region::EarlyBound(i as u32, def_id.to_def_id()))
- })
- }
+ Set1::Empty => ObjectLifetimeDefault::Empty,
+ Set1::One(hir::LifetimeName::Static) => ObjectLifetimeDefault::Static,
+ Set1::One(hir::LifetimeName::Param(param_def_id, _)) => {
+ ObjectLifetimeDefault::Param(param_def_id.to_def_id())
}
- Set1::Many => Set1::Many,
+ _ => ObjectLifetimeDefault::Ambiguous,
})
}
GenericParamKind::Const { .. } => {
//
// We still store a dummy value here to allow generic parameters
// in an arbitrary order.
- Some(Set1::Empty)
+ Some(ObjectLifetimeDefault::Empty)
}
- };
-
- tcx.arena.alloc_from_iter(generics.params.iter().filter_map(process_param))
+ }
}
impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
F: for<'b> FnOnce(&mut LifetimeContext<'b, 'tcx>),
{
let LifetimeContext { tcx, map, .. } = self;
- let xcrate_object_lifetime_defaults = take(&mut self.xcrate_object_lifetime_defaults);
let mut this = LifetimeContext {
tcx: *tcx,
map,
scope: &wrap_scope,
trait_definition_only: self.trait_definition_only,
- xcrate_object_lifetime_defaults,
};
- let span = tracing::debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope));
+ let span = debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope));
{
let _enter = span.enter();
f(&mut this);
}
- self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults;
}
/// Visits self by adding a scope and handling recursive walk over the contents with `walk`.
/// ordering is not important there.
fn visit_early_late<F>(
&mut self,
- parent_id: Option<LocalDefId>,
hir_id: hir::HirId,
generics: &'tcx hir::Generics<'tcx>,
walk: F,
) where
F: for<'b, 'c> FnOnce(&'b mut LifetimeContext<'c, 'tcx>),
{
- // Find the start of nested early scopes, e.g., in methods.
- let mut next_early_index = 0;
- if let Some(parent_id) = parent_id {
- let parent = self.tcx.hir().expect_item(parent_id);
- if sub_items_have_self_param(&parent.kind) {
- next_early_index += 1; // Self comes before lifetimes
- }
- match parent.kind {
- hir::ItemKind::Trait(_, _, ref generics, ..)
- | hir::ItemKind::Impl(hir::Impl { ref generics, .. }) => {
- next_early_index += generics.params.len() as u32;
- }
- _ => {}
- }
- }
-
- let mut non_lifetime_count = 0;
let mut named_late_bound_vars = 0;
let lifetimes: FxIndexMap<LocalDefId, Region> = generics
.params
named_late_bound_vars += 1;
Some(Region::late(late_bound_idx, self.tcx.hir(), param))
} else {
- Some(Region::early(self.tcx.hir(), &mut next_early_index, param))
+ Some(Region::early(self.tcx.hir(), param))
}
}
- GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
- non_lifetime_count += 1;
- None
- }
+ GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None,
})
.collect();
- let next_early_index = next_early_index + non_lifetime_count;
let binders: Vec<_> = generics
.params
let scope = Scope::Binder {
hir_id,
lifetimes,
- next_early_index,
s: self.scope,
- opaque_type_parent: true,
scope_type: BinderScopeType::Normal,
where_bound_origin: None,
};
self.with(scope, walk);
}
- fn next_early_index_helper(&self, only_opaque_type_parent: bool) -> u32 {
- let mut scope = self.scope;
- loop {
- match *scope {
- Scope::Root => return 0,
-
- Scope::Binder { next_early_index, opaque_type_parent, .. }
- if (!only_opaque_type_parent || opaque_type_parent) =>
- {
- return next_early_index;
- }
-
- Scope::Binder { s, .. }
- | Scope::Body { s, .. }
- | Scope::Elision { s, .. }
- | Scope::ObjectLifetimeDefault { s, .. }
- | Scope::Supertrait { s, .. }
- | Scope::TraitRefBoundary { s, .. } => scope = s,
- }
- }
- }
-
- /// Returns the next index one would use for an early-bound-region
- /// if extending the current scope.
- fn next_early_index(&self) -> u32 {
- self.next_early_index_helper(true)
- }
-
- /// Returns the next index one would use for an `impl Trait` that
- /// is being converted into an opaque type alias `impl Trait`. This will be the
- /// next early index from the enclosing item, for the most
- /// part. See the `opaque_type_parent` field for more info.
- fn next_early_index_for_opaque_type(&self) -> u32 {
- self.next_early_index_helper(false)
- }
-
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn resolve_lifetime_ref(
&mut self,
region_def_id: LocalDefId,
return;
}
- // We may fail to resolve higher-ranked lifetimes that are mentionned by APIT.
+ // We may fail to resolve higher-ranked lifetimes that are mentioned by APIT.
// AST-based resolution does not care for impl-trait desugaring, which are the
// responibility of lowering. This may create a mismatch between the resolution
// AST found (`region_def_id`) which points to HRTB, and what HIR allows.
);
}
+ #[instrument(level = "debug", skip(self))]
fn visit_segment_args(
&mut self,
res: Res,
depth: usize,
generic_args: &'tcx hir::GenericArgs<'tcx>,
) {
- debug!(
- "visit_segment_args(res={:?}, depth={:?}, generic_args={:?})",
- res, depth, generic_args,
- );
-
if generic_args.parenthesized {
self.visit_fn_like_elision(
generic_args.inputs(),
// Figure out if this is a type/trait segment,
// which requires object lifetime defaults.
- let parent_def_id = |this: &mut Self, def_id: DefId| {
- let def_key = this.tcx.def_key(def_id);
- DefId { krate: def_id.krate, index: def_key.parent.expect("missing parent") }
- };
let type_def_id = match res {
- Res::Def(DefKind::AssocTy, def_id) if depth == 1 => Some(parent_def_id(self, def_id)),
- Res::Def(DefKind::Variant, def_id) if depth == 0 => Some(parent_def_id(self, def_id)),
+ Res::Def(DefKind::AssocTy, def_id) if depth == 1 => Some(self.tcx.parent(def_id)),
+ Res::Def(DefKind::Variant, def_id) if depth == 0 => Some(self.tcx.parent(def_id)),
Res::Def(
DefKind::Struct
| DefKind::Union
_ => None,
};
- debug!("visit_segment_args: type_def_id={:?}", type_def_id);
+ debug!(?type_def_id);
// Compute a vector of defaults, one for each type parameter,
// per the rules given in RFCs 599 and 1156. Example:
};
let map = &self.map;
- let set_to_region = |set: &ObjectLifetimeDefault| match *set {
- Set1::Empty => {
+ let generics = self.tcx.generics_of(def_id);
+
+ // `type_def_id` points to an item, so there is nothing to inherit generics from.
+ debug_assert_eq!(generics.parent_count, 0);
+
+ let set_to_region = |set: ObjectLifetimeDefault| match set {
+ ObjectLifetimeDefault::Empty => {
if in_body {
None
} else {
Some(Region::Static)
}
}
- Set1::One(r) => {
- let lifetimes = generic_args.args.iter().filter_map(|arg| match arg {
- GenericArg::Lifetime(lt) => Some(lt),
+ ObjectLifetimeDefault::Static => Some(Region::Static),
+ ObjectLifetimeDefault::Param(param_def_id) => {
+ // This index can be used with `generic_args` since `parent_count == 0`.
+ let index = generics.param_def_id_to_index[¶m_def_id] as usize;
+ generic_args.args.get(index).and_then(|arg| match arg {
+ GenericArg::Lifetime(lt) => map.defs.get(<.hir_id).copied(),
_ => None,
- });
- r.subst(lifetimes, map)
+ })
}
- Set1::Many => None,
+ ObjectLifetimeDefault::Ambiguous => None,
};
- if let Some(def_id) = def_id.as_local() {
- let id = self.tcx.hir().local_def_id_to_hir_id(def_id);
- self.tcx
- .object_lifetime_defaults(id.owner)
- .unwrap()
- .iter()
- .map(set_to_region)
- .collect()
- } else {
- let tcx = self.tcx;
- self.xcrate_object_lifetime_defaults
- .entry(def_id)
- .or_insert_with(|| {
- tcx.generics_of(def_id)
- .params
- .iter()
- .filter_map(|param| match param.kind {
- GenericParamDefKind::Type { object_lifetime_default, .. } => {
- Some(object_lifetime_default)
- }
- GenericParamDefKind::Const { .. } => Some(Set1::Empty),
- GenericParamDefKind::Lifetime => None,
- })
- .collect()
- })
- .iter()
- .map(set_to_region)
- .collect()
- }
+ generics
+ .params
+ .iter()
+ .filter_map(|param| self.tcx.object_lifetime_default(param.def_id))
+ .map(set_to_region)
+ .collect()
});
- debug!("visit_segment_args: object_lifetime_defaults={:?}", object_lifetime_defaults);
+ debug!(?object_lifetime_defaults);
let mut i = 0;
for arg in generic_args.args {
}
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn visit_fn_like_elision(
&mut self,
inputs: &'tcx [hir::Ty<'tcx>],
self.insert_lifetime(lifetime_ref, lifetime.shifted(late_depth));
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self))]
fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: Region) {
debug!(
node = ?self.tcx.hir().node_to_string(lifetime_ref.hir_id),
#![feature(drain_filter)]
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(never_type)]
#![recursion_limit = "256"]
use std::cell::{Cell, RefCell};
use std::collections::BTreeSet;
use std::{cmp, fmt, ptr};
-use tracing::debug;
use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion};
use imports::{Import, ImportKind, ImportResolver, NameResolution};
// The node ID is for reporting the `PROC_MACRO_DERIVE_RESOLUTION_FALLBACK`
// lint if it should be reported.
Module(Module<'a>, Option<NodeId>),
- RegisteredAttrs,
MacroUsePrelude,
BuiltinAttrs,
ExternPrelude,
/// A small map keeping true kinds of built-in macros that appear to be fn-like on
/// the surface (`macro` items in libcore), but are actually attributes or derives.
builtin_macro_kinds: FxHashMap<LocalDefId, MacroKind>,
- registered_attrs: FxHashSet<Ident>,
registered_tools: RegisteredTools,
macro_use_prelude: FxHashMap<Symbol, &'a NameBinding<'a>>,
macro_map: FxHashMap<DefId, MacroData>,
}
}
- let (registered_attrs, registered_tools) =
- macros::registered_attrs_and_tools(session, &krate.attrs);
+ let registered_tools = macros::registered_tools(session, &krate.attrs);
let features = session.features_untracked();
macro_names: FxHashSet::default(),
builtin_macros: Default::default(),
builtin_macro_kinds: Default::default(),
- registered_attrs,
registered_tools,
macro_use_prelude: FxHashMap::default(),
macro_map: FxHashMap::default(),
_ => panic!("invalid arg index"),
}
}
- // Cache the lookup to avoid parsing attributes for an iterm multiple times.
+ // Cache the lookup to avoid parsing attributes for an item multiple times.
self.legacy_const_generic_args.insert(def_id, Some(ret.clone()));
return Some(ret);
}
}
self.main_def = Some(MainDefinition { res, is_import, span });
}
+
+ // Items that go to reexport table encoded to metadata and visible through it to other crates.
+ fn is_reexport(&self, binding: &NameBinding<'a>) -> Option<def::Res<!>> {
+ // FIXME: Consider changing the binding inserted by `#[macro_export] macro_rules`
+ // into the crate root to actual `NameBindingKind::Import`.
+ if binding.is_import()
+ || matches!(binding.kind, NameBindingKind::Res(_, _is_macro_export @ true))
+ {
+ let res = binding.res().expect_non_local();
+ // Ambiguous imports are treated as errors at this point and are
+ // not exposed to other crates (see #36837 for more details).
+ if res != def::Res::Err && !binding.is_ambiguity() {
+ return Some(res);
+ }
+ }
+
+ return None;
+ }
}
fn names_to_string(names: &[Symbol]) -> String {
}
}
-/// The code common between processing `#![register_tool]` and `#![register_attr]`.
-fn registered_idents(
- sess: &Session,
- attrs: &[ast::Attribute],
- attr_name: Symbol,
- descr: &str,
-) -> FxHashSet<Ident> {
- let mut registered = FxHashSet::default();
- for attr in sess.filter_by_name(attrs, attr_name) {
+pub(crate) fn registered_tools(sess: &Session, attrs: &[ast::Attribute]) -> FxHashSet<Ident> {
+ let mut registered_tools = FxHashSet::default();
+ for attr in sess.filter_by_name(attrs, sym::register_tool) {
for nested_meta in attr.meta_item_list().unwrap_or_default() {
match nested_meta.ident() {
Some(ident) => {
- if let Some(old_ident) = registered.replace(ident) {
- let msg = format!("{} `{}` was already registered", descr, ident);
+ if let Some(old_ident) = registered_tools.replace(ident) {
+ let msg = format!("{} `{}` was already registered", "tool", ident);
sess.struct_span_err(ident.span, &msg)
.span_label(old_ident.span, "already registered here")
.emit();
}
}
None => {
- let msg = format!("`{}` only accepts identifiers", attr_name);
+ let msg = format!("`{}` only accepts identifiers", sym::register_tool);
let span = nested_meta.span();
sess.struct_span_err(span, &msg).span_label(span, "not an identifier").emit();
}
}
}
}
- registered
-}
-
-pub(crate) fn registered_attrs_and_tools(
- sess: &Session,
- attrs: &[ast::Attribute],
-) -> (FxHashSet<Ident>, FxHashSet<Ident>) {
- let registered_attrs = registered_idents(sess, attrs, sym::register_attr, "attribute");
- let mut registered_tools = registered_idents(sess, attrs, sym::register_tool, "tool");
// We implicitly add `rustfmt` and `clippy` to known tools,
// but it's not an error to register them explicitly.
let predefined_tools = [sym::clippy, sym::rustfmt];
registered_tools.extend(predefined_tools.iter().cloned().map(Ident::with_dummy_span));
- (registered_attrs, registered_tools)
+ registered_tools
}
// Some feature gates for inner attributes are reported as lints for backward compatibility.
}
PathResult::Indeterminate => indeterminate = true,
// We can only be sure that a path doesn't exist after having tested all the
- // posibilities, only at that time we can return false.
+ // possibilities, only at that time we can return false.
PathResult::Failed { .. } => {}
PathResult::Module(_) => panic!("unexpected path resolution"),
}
RefKind, Relation, RelationKind, SpanData,
};
-use tracing::{debug, error};
-
#[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5213
macro_rules! down_cast_data {
($id:ident, $kind:ident, $sp:expr) => {
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
+#[macro_use]
+extern crate tracing;
+
mod dump_visitor;
mod dumper;
#[macro_use]
RefKind, Relation, RelationKind, SpanData,
};
-use tracing::{debug, error, info};
-
pub struct SaveContext<'tcx> {
tcx: TyCtxt<'tcx>,
maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>,
[dependencies]
indexmap = "1.9.1"
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
+thin-vec = "0.2.8"
[dev-dependencies]
rustc_macros = { path = "../rustc_macros" }
//! Implementations of serialization for structures found in liballoc
-use std::hash::{BuildHasher, Hash};
-
use crate::{Decodable, Decoder, Encodable, Encoder};
+use smallvec::{Array, SmallVec};
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, LinkedList, VecDeque};
+use std::hash::{BuildHasher, Hash};
use std::rc::Rc;
use std::sync::Arc;
-
-use smallvec::{Array, SmallVec};
+use thin_vec::ThinVec;
impl<S: Encoder, A: Array<Item: Encodable<S>>> Encodable<S> for SmallVec<A> {
fn encode(&self, s: &mut S) {
}
}
+impl<S: Encoder, T: Encodable<S>> Encodable<S> for ThinVec<T> {
+ fn encode(&self, s: &mut S) {
+ self.as_slice().encode(s);
+ }
+}
+
+impl<D: Decoder, T: Decodable<D>> Decodable<D> for ThinVec<T> {
+ fn decode(d: &mut D) -> ThinVec<T> {
+ let len = d.read_usize();
+ (0..len).map(|_| Decodable::decode(d)).collect()
+ }
+}
+
impl<S: Encoder, T: Encodable<S>> Encodable<S> for LinkedList<T> {
fn encode(&self, s: &mut S) {
s.emit_usize(self.len());
//! compilation. This is used for incremental compilation tests and debug
//! output.
+use crate::errors::{CguNotRecorded, IncorrectCguReuseType};
+use crate::Session;
use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
use rustc_span::{Span, Symbol};
+use std::borrow::Cow;
+use std::fmt::{self};
use std::sync::{Arc, Mutex};
-use tracing::debug;
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub enum CguReuse {
PostLto,
}
+impl fmt::Display for CguReuse {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ CguReuse::No => write!(f, "No"),
+ CguReuse::PreLto => write!(f, "PreLto "),
+ CguReuse::PostLto => write!(f, "PostLto "),
+ }
+ }
+}
+
+impl IntoDiagnosticArg for CguReuse {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
+ }
+}
+
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ComparisonKind {
Exact,
}
}
- pub fn check_expected_reuse(&self, diag: &rustc_errors::Handler) {
+ pub fn check_expected_reuse(&self, sess: &Session) {
if let Some(ref data) = self.data {
let data = data.lock().unwrap();
};
if error {
- let at_least = if at_least { "at least " } else { "" };
- let msg = format!(
- "CGU-reuse for `{cgu_user_name}` is `{actual_reuse:?}` but \
- should be {at_least}`{expected_reuse:?}`"
- );
- diag.span_err(error_span.0, &msg);
+ let at_least = if at_least { 1 } else { 0 };
+ IncorrectCguReuseType {
+ span: error_span.0,
+ cgu_user_name: &cgu_user_name,
+ actual_reuse,
+ expected_reuse,
+ at_least,
+ };
}
} else {
- let msg = format!(
- "CGU-reuse for `{cgu_user_name}` (mangled: `{cgu_name}`) was \
- not recorded"
- );
- diag.span_fatal(error_span.0, &msg)
+ sess.emit_fatal(CguNotRecorded { cgu_user_name, cgu_name });
}
}
}
use rustc_data_structures::stable_hasher::ToStableHashKey;
use rustc_target::abi::{Align, TargetDataLayout};
-use rustc_target::spec::{LinkerFlavor, SplitDebuginfo, Target, TargetTriple, TargetWarnings};
-use rustc_target::spec::{PanicStrategy, SanitizerSet, TARGETS};
+use rustc_target::spec::{PanicStrategy, SanitizerSet, SplitDebuginfo};
+use rustc_target::spec::{Target, TargetTriple, TargetWarnings, TARGETS};
use crate::parse::{CrateCheckConfig, CrateConfig};
use rustc_feature::UnstableFeatures;
}
}
- if cg.linker_flavor == Some(LinkerFlavor::L4Bender)
- && !nightly_options::is_unstable_enabled(matches)
- {
- early_error(
- error_format,
- "`l4-bender` linker flavor is unstable, `-Z unstable-options` \
- flag must also be passed to explicitly use it",
- );
- }
-
let prints = collect_print_requests(&mut cg, &mut unstable_opts, matches, error_format);
let cg = cg;
),
),
};
- tracing::debug!("got unpretty option: {first:?}");
+ debug!("got unpretty option: {first:?}");
Some(first)
}
--- /dev/null
+use std::num::NonZeroU32;
+
+use crate as rustc_session;
+use crate::cgu_reuse_tracker::CguReuse;
+use rustc_errors::MultiSpan;
+use rustc_macros::SessionDiagnostic;
+use rustc_span::{Span, Symbol};
+
+#[derive(SessionDiagnostic)]
+#[diag(session::incorrect_cgu_reuse_type)]
+pub struct IncorrectCguReuseType<'a> {
+ #[primary_span]
+ pub span: Span,
+ pub cgu_user_name: &'a str,
+ pub actual_reuse: CguReuse,
+ pub expected_reuse: CguReuse,
+ pub at_least: u8,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(session::cgu_not_recorded)]
+pub struct CguNotRecorded<'a> {
+ pub cgu_user_name: &'a str,
+ pub cgu_name: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(session::feature_gate_error, code = "E0658")]
+pub struct FeatureGateError<'a> {
+ #[primary_span]
+ pub span: MultiSpan,
+ pub explain: &'a str,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[note(session::feature_diagnostic_for_issue)]
+pub struct FeatureDiagnosticForIssue {
+ pub n: NonZeroU32,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[help(session::feature_diagnostic_help)]
+pub struct FeatureDiagnosticHelp {
+ pub feature: Symbol,
+}
use crate::search_paths::{PathKind, SearchPath};
use rustc_fs_util::fix_windows_verbatim_for_gcc;
-use tracing::debug;
#[derive(Copy, Clone)]
pub enum FileMatch {
#![feature(if_let_guard)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(min_specialization)]
#![feature(never_type)]
#[macro_use]
extern crate rustc_macros;
+pub mod errors;
+
+#[macro_use]
+extern crate tracing;
pub mod cgu_reuse_tracker;
pub mod utils;
use crate::search_paths::SearchPath;
use crate::utils::NativeLib;
use rustc_errors::LanguageIdentifier;
-use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy, SanitizerSet};
+use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet};
use rustc_target::spec::{
RelocModel, RelroLevel, SplitDebuginfo, StackProtector, TargetTriple, TlsModel,
};
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
pub const parse_cfprotection: &str = "`none`|`no`|`n` (default), `branch`, `return`, or `full`|`yes`|`y` (equivalent to `branch` and `return`)";
pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`";
- pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavor::one_of();
+ pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavorCli::one_of();
pub const parse_optimization_fuel: &str = "crate=integer";
pub const parse_mir_spanview: &str = "`statement` (default), `terminator`, or `block`";
pub const parse_instrument_coverage: &str =
true
}
- pub(crate) fn parse_linker_flavor(slot: &mut Option<LinkerFlavor>, v: Option<&str>) -> bool {
- match v.and_then(LinkerFlavor::from_str) {
+ pub(crate) fn parse_linker_flavor(slot: &mut Option<LinkerFlavorCli>, v: Option<&str>) -> bool {
+ match v.and_then(LinkerFlavorCli::from_str) {
Some(lf) => *slot = Some(lf),
_ => return false,
}
on C toolchain installed in the system"),
linker: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
"system linker to link outputs with"),
- linker_flavor: Option<LinkerFlavor> = (None, parse_linker_flavor, [UNTRACKED],
+ linker_flavor: Option<LinkerFlavorCli> = (None, parse_linker_flavor, [UNTRACKED],
"linker flavor"),
linker_plugin_lto: LinkerPluginLto = (LinkerPluginLto::Disabled,
parse_linker_plugin_lto, [TRACKED],
//! It also serves as an input to the parser itself.
use crate::config::CheckCfg;
+use crate::errors::{FeatureDiagnosticForIssue, FeatureDiagnosticHelp, FeatureGateError};
use crate::lint::{
builtin::UNSTABLE_SYNTAX_PRE_EXPANSION, BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId,
};
use rustc_data_structures::sync::{Lock, Lrc};
use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler};
use rustc_errors::{
- error_code, fallback_fluent_bundle, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId,
+ fallback_fluent_bundle, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId,
DiagnosticMessage, EmissionGuarantee, ErrorGuaranteed, MultiSpan, StashKey,
};
use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures};
.map(|err| err.cancel());
}
- let mut err = sess.span_diagnostic.struct_span_err_with_code(span, explain, error_code!(E0658));
+ let mut err = sess.create_err(FeatureGateError { span, explain });
add_feature_diagnostics_for_issue(&mut err, sess, feature, issue);
err
}
///
/// This variant allows you to control whether it is a library or language feature.
/// Almost always, you want to use this for a language feature. If so, prefer `feature_warn`.
+#[allow(rustc::diagnostic_outside_of_impl)]
+#[allow(rustc::untranslatable_diagnostic)]
pub fn feature_warn_issue<'a>(
sess: &'a ParseSess,
feature: Symbol,
issue: GateIssue,
) {
if let Some(n) = find_feature_issue(feature, issue) {
- err.note(&format!(
- "see issue #{n} <https://github.com/rust-lang/rust/issues/{n}> for more information"
- ));
+ err.subdiagnostic(FeatureDiagnosticForIssue { n });
}
// #23973: do not suggest `#![feature(...)]` if we are in beta/stable
if sess.unstable_features.is_nightly_build() {
- err.help(&format!("add `#![feature({feature})]` to the crate attributes to enable"));
+ err.subdiagnostic(FeatureDiagnosticHelp { feature });
}
}
}
#[rustc_lint_diagnostics]
+ #[allow(rustc::diagnostic_outside_of_impl)]
+ #[allow(rustc::untranslatable_diagnostic)]
pub fn struct_err(
&self,
msg: impl Into<DiagnosticMessage>,
}
#[rustc_lint_diagnostics]
+ #[allow(rustc::diagnostic_outside_of_impl)]
+ #[allow(rustc::untranslatable_diagnostic)]
pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
self.span_diagnostic.struct_warn(msg)
}
#[rustc_lint_diagnostics]
+ #[allow(rustc::diagnostic_outside_of_impl)]
+ #[allow(rustc::untranslatable_diagnostic)]
pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> {
self.span_diagnostic.struct_fatal(msg)
}
#[rustc_lint_diagnostics]
+ #[allow(rustc::diagnostic_outside_of_impl)]
+ #[allow(rustc::untranslatable_diagnostic)]
pub fn struct_diagnostic<G: EmissionGuarantee>(
&self,
msg: impl Into<DiagnosticMessage>,
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use std::fmt;
use std::hash::Hash;
-use tracing::*;
/// A `SyntaxContext` represents a chain of pairs `(ExpnId, Transparency)` named "marks".
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
use sha1::Sha1;
use sha2::Sha256;
-use tracing::debug;
-
#[cfg(test)]
mod tests;
use std::fs;
use std::io;
-use tracing::debug;
#[cfg(test)]
mod tests;
return remap_path_prefix(&self.mapping, path);
- #[instrument(level = "debug", skip(mapping))]
+ #[instrument(level = "debug", skip(mapping), ret)]
fn remap_path_prefix(mapping: &[(PathBuf, PathBuf)], path: PathBuf) -> (PathBuf, bool) {
// NOTE: We are iterating over the mapping entries from last to first
// because entries specified later on the command line should
// take precedence.
for &(ref from, ref to) in mapping.iter().rev() {
- debug!("Trying to apply {:?} => {:?}", from, to);
+ debug!("Trying to apply {from:?} => {to:?}");
if let Ok(rest) = path.strip_prefix(from) {
let remapped = if rest.as_os_str().is_empty() {
} else {
to.join(rest)
};
- debug!("Match - remapped {:?} => {:?}", path, remapped);
+ debug!("Match - remapped");
return (remapped, true);
} else {
- debug!("No match - prefix {:?} does not match {:?}", from, path);
+ debug!("No match - prefix {from:?} does not match");
}
}
- debug!("Path {:?} was not remapped", path);
+ debug!("not remapped");
(path, false)
}
}
BTreeSet,
BinaryHeap,
Borrow,
+ BorrowMut,
Break,
C,
CStr,
LinkedList,
LintPass,
Mutex,
+ MutexGuard,
N,
NonZeroI128,
NonZeroI16,
Rust,
RustcDecodable,
RustcEncodable,
+ RwLockReadGuard,
+ RwLockWriteGuard,
Send,
SeqCst,
SessionDiagnostic,
rust_eh_unregister_frames,
rust_oom,
rustc,
+ rustc_access_level,
rustc_allocator,
rustc_allocator_nounwind,
rustc_allocator_zeroed,
rustc_target = { path = "../rustc_target" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_session = { path = "../rustc_session" }
+rustc_macros = { path = "../rustc_macros" }
+rustc_errors = { path = "../rustc_errors" }
--- /dev/null
+//! Errors emitted by symbol_mangling.
+
+use rustc_macros::SessionDiagnostic;
+use rustc_span::Span;
+
+#[derive(SessionDiagnostic)]
+#[diag(symbol_mangling::invalid_symbol_name)]
+pub struct InvalidSymbolName {
+ #[primary_span]
+ pub span: Span,
+ pub mangled_formatted: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(symbol_mangling::invalid_trait_item)]
+pub struct InvalidTraitItem {
+ #[primary_span]
+ pub span: Span,
+ pub demangling_formatted: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(symbol_mangling::alt_invalid_trait_item)]
+pub struct AltInvalidTraitItem {
+ #[primary_span]
+ pub span: Span,
+ pub alt_demangling_formatted: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(symbol_mangling::invalid_def_path)]
+pub struct InvalidDefPath {
+ #[primary_span]
+ pub span: Span,
+ pub def_path: String,
+}
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeVisitable};
use rustc_middle::util::common::record_time;
-use tracing::debug;
-
use std::fmt::{self, Write};
use std::mem::{self, discriminant};
#![feature(never_type)]
#![recursion_limit = "256"]
#![allow(rustc::potential_query_instability)]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
#[macro_use]
extern crate rustc_middle;
+#[macro_use]
+extern crate tracing;
+
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::ty::{self, Instance, TyCtxt};
use rustc_session::config::SymbolManglingVersion;
-use tracing::debug;
-
mod legacy;
mod v0;
+pub mod errors;
pub mod test;
pub mod typeid;
//! def-path. This is used for unit testing the code that generates
//! paths etc in all kinds of annoying scenarios.
+use crate::errors::{AltInvalidTraitItem, InvalidDefPath, InvalidSymbolName, InvalidTraitItem};
use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{subst::InternalSubsts, Instance, TyCtxt};
tcx.erase_regions(InternalSubsts::identity_for_item(tcx, def_id)),
);
let mangled = tcx.symbol_name(instance);
- tcx.sess.span_err(attr.span, &format!("symbol-name({})", mangled));
+ tcx.sess.emit_err(InvalidSymbolName {
+ span: attr.span,
+ mangled_formatted: format!("{mangled}"),
+ });
if let Ok(demangling) = rustc_demangle::try_demangle(mangled.name) {
- tcx.sess.span_err(attr.span, &format!("demangling({})", demangling));
- tcx.sess.span_err(attr.span, &format!("demangling-alt({:#})", demangling));
+ tcx.sess.emit_err(InvalidTraitItem {
+ span: attr.span,
+ demangling_formatted: format!("{demangling}"),
+ });
+ tcx.sess.emit_err(AltInvalidTraitItem {
+ span: attr.span,
+ alt_demangling_formatted: format!("{:#}", demangling),
+ });
}
}
for attr in tcx.get_attrs(def_id.to_def_id(), DEF_PATH) {
- let path = with_no_trimmed_paths!(tcx.def_path_str(def_id.to_def_id()));
- tcx.sess.span_err(attr.span, &format!("def-path({})", path));
+ tcx.sess.emit_err(InvalidDefPath {
+ span: attr.span,
+ def_path: with_no_trimmed_paths!(tcx.def_path_str(def_id.to_def_id())),
+ });
}
}
}
panic_strategy: PanicStrategy::Abort,
position_independent_executables: true,
dynamic_linking: true,
- executables: true,
relro_level: RelroLevel::Off,
..Default::default()
},
let mut base = super::linux_base::opts();
base.os = "android".into();
base.default_dwarf_version = 2;
- base.position_independent_executables = true;
base.has_thread_local = false;
// This is for backward compatibility, see https://github.com/rust-lang/rust/issues/49867
// for context. (At that time, there was no `-C force-unwind-tables`, so the only solution
use std::{borrow::Cow, env};
-use crate::spec::{cvs, DebuginfoKind, FramePointer, SplitDebuginfo, TargetOptions};
+use crate::spec::{cvs, DebuginfoKind, FramePointer, SplitDebuginfo, StaticCow, TargetOptions};
use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor};
fn pre_link_args(os: &'static str, arch: &'static str, abi: &'static str) -> LinkArgs {
- let mut args = LinkArgs::new();
-
- let platform_name = match abi {
- "sim" => format!("{}-simulator", os),
- "macabi" => "mac-catalyst".to_string(),
- _ => os.to_string(),
+ let platform_name: StaticCow<str> = match abi {
+ "sim" => format!("{}-simulator", os).into(),
+ "macabi" => "mac-catalyst".into(),
+ _ => os.into(),
};
- let platform_version = match os.as_ref() {
+ let platform_version: StaticCow<str> = match os.as_ref() {
"ios" => ios_lld_platform_version(),
"tvos" => tvos_lld_platform_version(),
"watchos" => watchos_lld_platform_version(),
"macos" => macos_lld_platform_version(arch),
_ => unreachable!(),
- };
-
- if abi != "macabi" {
- args.insert(LinkerFlavor::Gcc, vec!["-arch".into(), arch.into()]);
}
+ .into();
- args.insert(
+ let mut args = TargetOptions::link_args(
LinkerFlavor::Lld(LldFlavor::Ld64),
- vec![
- "-arch".into(),
- arch.into(),
- "-platform_version".into(),
- platform_name.into(),
- platform_version.clone().into(),
- platform_version.into(),
- ],
+ &["-arch", arch, "-platform_version"],
);
+ // Manually add owned args unsupported by link arg building helpers.
+ args.entry(LinkerFlavor::Lld(LldFlavor::Ld64)).or_default().extend([
+ platform_name,
+ platform_version.clone(),
+ platform_version,
+ ]);
+ if abi != "macabi" {
+ super::add_link_args(&mut args, LinkerFlavor::Gcc, &["-arch", arch]);
+ }
args
}
format!("{}-apple-macosx{}.{}.0", arch, major, minor)
}
-pub fn macos_link_env_remove() -> Vec<Cow<'static, str>> {
+pub fn macos_link_env_remove() -> Vec<StaticCow<str>> {
let mut env_remove = Vec::with_capacity(2);
// Remove the `SDKROOT` environment variable if it's clearly set for the wrong platform, which
// may occur when we're linking a custom build script while targeting iOS for example.
pub fn target() -> Target {
let mut target = wasm32_unknown_emscripten::target();
- target.add_post_link_args(LinkerFlavor::Em, &["-sWASM=0", "--memory-init-file", "0"]);
+ target.add_post_link_args(LinkerFlavor::EmCc, &["-sWASM=0", "--memory-init-file", "0"]);
target
}
-use crate::spec::{LinkerFlavor, Target, TargetOptions};
+use crate::spec::{LinkerFlavor, RelocModel, Target, TargetOptions};
/// A base target for AVR devices using the GNU toolchain.
///
late_link_args: TargetOptions::link_args(LinkerFlavor::Gcc, &["-lgcc"]),
max_atomic_width: Some(0),
atomic_cas: false,
+ relocation_model: RelocModel::Static,
..TargetOptions::default()
},
}
TargetOptions {
allow_asm: true,
endian,
- linker_flavor: LinkerFlavor::BpfLinker,
+ linker_flavor: LinkerFlavor::Bpf,
atomic_cas: false,
dynamic_linking: true,
no_builtins: true,
base.crt_static_default = false;
base.has_rpath = true;
base.linker_is_gnu = false;
- base.dynamic_linking = true;
base.c_enum_min_bits = 8;
-use crate::spec::{cvs, LinkerFlavor, PanicStrategy, TargetOptions};
+use crate::spec::{cvs, LinkerFlavor, PanicStrategy, RelocModel, TargetOptions};
pub fn opts() -> TargetOptions {
TargetOptions {
os: "l4re".into(),
env: "uclibc".into(),
- linker_flavor: LinkerFlavor::L4Bender,
+ linker_flavor: LinkerFlavor::Ld,
panic_strategy: PanicStrategy::Abort,
linker: Some("l4-bender".into()),
linker_is_gnu: false,
families: cvs!["unix"],
+ relocation_model: RelocModel::Static,
..Default::default()
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum LinkerFlavor {
- Em,
Gcc,
- L4Bender,
Ld,
+ Lld(LldFlavor),
Msvc,
+ EmCc,
+ Bpf,
+ Ptx,
+}
+
+#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
+pub enum LinkerFlavorCli {
+ Gcc,
+ Ld,
Lld(LldFlavor),
- PtxLinker,
+ Msvc,
+ Em,
BpfLinker,
+ PtxLinker,
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
}
}
-impl ToJson for LinkerFlavor {
- fn to_json(&self) -> Json {
- self.desc().to_json()
+impl LinkerFlavor {
+ pub fn from_cli(cli: LinkerFlavorCli) -> LinkerFlavor {
+ match cli {
+ LinkerFlavorCli::Gcc => LinkerFlavor::Gcc,
+ LinkerFlavorCli::Ld => LinkerFlavor::Ld,
+ LinkerFlavorCli::Lld(lld_flavor) => LinkerFlavor::Lld(lld_flavor),
+ LinkerFlavorCli::Msvc => LinkerFlavor::Msvc,
+ LinkerFlavorCli::Em => LinkerFlavor::EmCc,
+ LinkerFlavorCli::BpfLinker => LinkerFlavor::Bpf,
+ LinkerFlavorCli::PtxLinker => LinkerFlavor::Ptx,
+ }
+ }
+
+ fn to_cli(self) -> LinkerFlavorCli {
+ match self {
+ LinkerFlavor::Gcc => LinkerFlavorCli::Gcc,
+ LinkerFlavor::Ld => LinkerFlavorCli::Ld,
+ LinkerFlavor::Lld(lld_flavor) => LinkerFlavorCli::Lld(lld_flavor),
+ LinkerFlavor::Msvc => LinkerFlavorCli::Msvc,
+ LinkerFlavor::EmCc => LinkerFlavorCli::Em,
+ LinkerFlavor::Bpf => LinkerFlavorCli::BpfLinker,
+ LinkerFlavor::Ptx => LinkerFlavorCli::PtxLinker,
+ }
}
}
-macro_rules! flavor_mappings {
- ($((($($flavor:tt)*), $string:expr),)*) => (
- impl LinkerFlavor {
+
+macro_rules! linker_flavor_cli_impls {
+ ($(($($flavor:tt)*) $string:literal)*) => (
+ impl LinkerFlavorCli {
pub const fn one_of() -> &'static str {
concat!("one of: ", $($string, " ",)*)
}
- pub fn from_str(s: &str) -> Option<Self> {
+ pub fn from_str(s: &str) -> Option<LinkerFlavorCli> {
Some(match s {
$($string => $($flavor)*,)*
_ => return None,
)
}
-flavor_mappings! {
- ((LinkerFlavor::Em), "em"),
- ((LinkerFlavor::Gcc), "gcc"),
- ((LinkerFlavor::L4Bender), "l4-bender"),
- ((LinkerFlavor::Ld), "ld"),
- ((LinkerFlavor::Msvc), "msvc"),
- ((LinkerFlavor::PtxLinker), "ptx-linker"),
- ((LinkerFlavor::BpfLinker), "bpf-linker"),
- ((LinkerFlavor::Lld(LldFlavor::Wasm)), "wasm-ld"),
- ((LinkerFlavor::Lld(LldFlavor::Ld64)), "ld64.lld"),
- ((LinkerFlavor::Lld(LldFlavor::Ld)), "ld.lld"),
- ((LinkerFlavor::Lld(LldFlavor::Link)), "lld-link"),
+linker_flavor_cli_impls! {
+ (LinkerFlavorCli::Gcc) "gcc"
+ (LinkerFlavorCli::Ld) "ld"
+ (LinkerFlavorCli::Lld(LldFlavor::Ld)) "ld.lld"
+ (LinkerFlavorCli::Lld(LldFlavor::Ld64)) "ld64.lld"
+ (LinkerFlavorCli::Lld(LldFlavor::Link)) "lld-link"
+ (LinkerFlavorCli::Lld(LldFlavor::Wasm)) "wasm-ld"
+ (LinkerFlavorCli::Msvc) "msvc"
+ (LinkerFlavorCli::Em) "em"
+ (LinkerFlavorCli::BpfLinker) "bpf-linker"
+ (LinkerFlavorCli::PtxLinker) "ptx-linker"
+}
+
+impl ToJson for LinkerFlavorCli {
+ fn to_json(&self) -> Json {
+ self.desc().to_json()
+ }
}
#[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable, HashStable_Generic)]
}
pub type LinkArgs = BTreeMap<LinkerFlavor, Vec<StaticCow<str>>>;
+pub type LinkArgsCli = BTreeMap<LinkerFlavorCli, Vec<StaticCow<str>>>;
/// Which kind of debuginfo does the target use?
///
}
macro_rules! supported_targets {
- ( $(($( $triple:literal, )+ $module:ident ),)+ ) => {
+ ( $(($triple:literal, $module:ident ),)+ ) => {
$(mod $module;)+
/// List of supported targets
- pub const TARGETS: &[&str] = &[$($($triple),+),+];
+ pub const TARGETS: &[&str] = &[$($triple),+];
fn load_builtin(target: &str) -> Option<Target> {
let mut t = match target {
- $( $($triple)|+ => $module::target(), )+
+ $( $triple => $module::target(), )+
_ => return None,
};
t.is_builtin = true;
$(
#[test] // `#[test]`
fn $module() {
- tests_impl::test_target(super::$module::target());
+ tests_impl::test_target(super::$module::target(), $triple);
}
)+
}
("aarch64-unknown-openbsd", aarch64_unknown_openbsd),
("i686-unknown-openbsd", i686_unknown_openbsd),
+ ("powerpc-unknown-openbsd", powerpc_unknown_openbsd),
+ ("powerpc64-unknown-openbsd", powerpc64_unknown_openbsd),
+ ("riscv64gc-unknown-openbsd", riscv64gc_unknown_openbsd),
("sparc64-unknown-openbsd", sparc64_unknown_openbsd),
("x86_64-unknown-openbsd", x86_64_unknown_openbsd),
- ("powerpc-unknown-openbsd", powerpc_unknown_openbsd),
("aarch64-unknown-netbsd", aarch64_unknown_netbsd),
("armv6-unknown-netbsd-eabihf", armv6_unknown_netbsd_eabihf),
pub abi: StaticCow<str>,
/// Vendor name to use for conditional compilation (`target_vendor`). Defaults to "unknown".
pub vendor: StaticCow<str>,
- /// Default linker flavor used if `-C linker-flavor` or `-C linker` are not passed
- /// on the command line. Defaults to `LinkerFlavor::Gcc`.
- pub linker_flavor: LinkerFlavor,
/// Linker to invoke
pub linker: Option<StaticCow<str>>,
-
+ /// Default linker flavor used if `-C linker-flavor` or `-C linker` are not passed
+ /// on the command line. Defaults to `LinkerFlavor::Gcc`.
+ pub linker_flavor: LinkerFlavor,
+ linker_flavor_json: LinkerFlavorCli,
/// LLD flavor used if `lld` (or `rust-lld`) is specified as a linker
/// without clarifying its flavor in any way.
+ /// FIXME: Merge this into `LinkerFlavor`.
pub lld_flavor: LldFlavor,
+ /// Whether the linker support GNU-like arguments such as -O. Defaults to true.
+ /// FIXME: Merge this into `LinkerFlavor`.
+ pub linker_is_gnu: bool,
- /// Linker arguments that are passed *before* any user-defined libraries.
- pub pre_link_args: LinkArgs,
/// Objects to link before and after all other object code.
pub pre_link_objects: CrtObjects,
pub post_link_objects: CrtObjects,
pub post_link_objects_self_contained: CrtObjects,
pub link_self_contained: LinkSelfContainedDefault,
+ /// Linker arguments that are passed *before* any user-defined libraries.
+ pub pre_link_args: LinkArgs,
+ pre_link_args_json: LinkArgsCli,
/// Linker arguments that are unconditionally passed after any
/// user-defined but before post-link objects. Standard platform
/// libraries that should be always be linked to, usually go here.
pub late_link_args: LinkArgs,
+ late_link_args_json: LinkArgsCli,
/// Linker arguments used in addition to `late_link_args` if at least one
/// Rust dependency is dynamically linked.
pub late_link_args_dynamic: LinkArgs,
+ late_link_args_dynamic_json: LinkArgsCli,
/// Linker arguments used in addition to `late_link_args` if all Rust
/// dependencies are statically linked.
pub late_link_args_static: LinkArgs,
+ late_link_args_static_json: LinkArgsCli,
/// Linker arguments that are unconditionally passed *after* any
/// user-defined libraries.
pub post_link_args: LinkArgs,
+ post_link_args_json: LinkArgsCli,
+
/// Optional link script applied to `dylib` and `executable` crate types.
/// This is a string containing the script, not a path. Can only be applied
/// to linkers where `linker_is_gnu` is true.
pub link_script: Option<StaticCow<str>>,
-
/// Environment variables to be set for the linker invocation.
pub link_env: StaticCow<[(StaticCow<str>, StaticCow<str>)]>,
/// Environment variables to be removed for the linker invocation.
/// Default supported version of DWARF on this platform.
/// Useful because some platforms (osx, bsd) only want up to DWARF2.
pub default_dwarf_version: u32,
- /// Whether the linker support GNU-like arguments such as -O. Defaults to true.
- pub linker_is_gnu: bool,
/// The MinGW toolchain has a known issue that prevents it from correctly
/// handling COFF object files with more than 2<sup>15</sup> sections. Since each weak
/// symbol needs its own COMDAT section, weak linkage implies a large
match flavor {
LinkerFlavor::Ld => insert(LinkerFlavor::Lld(LldFlavor::Ld)),
LinkerFlavor::Msvc => insert(LinkerFlavor::Lld(LldFlavor::Link)),
- LinkerFlavor::Lld(LldFlavor::Wasm) => {}
+ LinkerFlavor::Lld(LldFlavor::Ld64) | LinkerFlavor::Lld(LldFlavor::Wasm) => {}
LinkerFlavor::Lld(lld_flavor) => {
panic!("add_link_args: use non-LLD flavor for {:?}", lld_flavor)
}
- LinkerFlavor::Gcc
- | LinkerFlavor::Em
- | LinkerFlavor::L4Bender
- | LinkerFlavor::BpfLinker
- | LinkerFlavor::PtxLinker => {}
+ LinkerFlavor::Gcc | LinkerFlavor::EmCc | LinkerFlavor::Bpf | LinkerFlavor::Ptx => {}
}
}
fn add_post_link_args(&mut self, flavor: LinkerFlavor, args: &[&'static str]) {
add_link_args(&mut self.post_link_args, flavor, args);
}
+
+ fn update_from_cli(&mut self) {
+ self.linker_flavor = LinkerFlavor::from_cli(self.linker_flavor_json);
+ for (args, args_json) in [
+ (&mut self.pre_link_args, &self.pre_link_args_json),
+ (&mut self.late_link_args, &self.late_link_args_json),
+ (&mut self.late_link_args_dynamic, &self.late_link_args_dynamic_json),
+ (&mut self.late_link_args_static, &self.late_link_args_static_json),
+ (&mut self.post_link_args, &self.post_link_args_json),
+ ] {
+ *args = args_json
+ .iter()
+ .map(|(flavor, args)| (LinkerFlavor::from_cli(*flavor), args.clone()))
+ .collect();
+ }
+ }
+
+ fn update_to_cli(&mut self) {
+ self.linker_flavor_json = self.linker_flavor.to_cli();
+ for (args, args_json) in [
+ (&self.pre_link_args, &mut self.pre_link_args_json),
+ (&self.late_link_args, &mut self.late_link_args_json),
+ (&self.late_link_args_dynamic, &mut self.late_link_args_dynamic_json),
+ (&self.late_link_args_static, &mut self.late_link_args_static_json),
+ (&self.post_link_args, &mut self.post_link_args_json),
+ ] {
+ *args_json =
+ args.iter().map(|(flavor, args)| (flavor.to_cli(), args.clone())).collect();
+ }
+ }
}
impl Default for TargetOptions {
env: "".into(),
abi: "".into(),
vendor: "unknown".into(),
- linker_flavor: LinkerFlavor::Gcc,
linker: option_env!("CFG_DEFAULT_LINKER").map(|s| s.into()),
+ linker_flavor: LinkerFlavor::Gcc,
+ linker_flavor_json: LinkerFlavorCli::Gcc,
lld_flavor: LldFlavor::Ld,
- pre_link_args: LinkArgs::new(),
- post_link_args: LinkArgs::new(),
+ linker_is_gnu: true,
link_script: None,
asm_args: cvs![],
cpu: "generic".into(),
is_like_msvc: false,
is_like_wasm: false,
default_dwarf_version: 4,
- linker_is_gnu: true,
allows_weak_linkage: true,
has_rpath: false,
no_default_libraries: true,
pre_link_objects_self_contained: Default::default(),
post_link_objects_self_contained: Default::default(),
link_self_contained: LinkSelfContainedDefault::False,
+ pre_link_args: LinkArgs::new(),
+ pre_link_args_json: LinkArgsCli::new(),
late_link_args: LinkArgs::new(),
+ late_link_args_json: LinkArgsCli::new(),
late_link_args_dynamic: LinkArgs::new(),
+ late_link_args_dynamic_json: LinkArgsCli::new(),
late_link_args_static: LinkArgs::new(),
+ late_link_args_static_json: LinkArgsCli::new(),
+ post_link_args: LinkArgs::new(),
+ post_link_args_json: LinkArgsCli::new(),
link_env: cvs![],
link_env_remove: cvs![],
archive_format: "gnu".into(),
Some(Ok(()))
})).unwrap_or(Ok(()))
} );
- ($key_name:ident, LinkerFlavor) => ( {
- let name = (stringify!($key_name)).replace("_", "-");
- obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
- match LinkerFlavor::from_str(s) {
+ ($key_name:ident = $json_name:expr, LinkerFlavor) => ( {
+ let name = $json_name;
+ obj.remove(name).and_then(|o| o.as_str().and_then(|s| {
+ match LinkerFlavorCli::from_str(s) {
Some(linker_flavor) => base.$key_name = linker_flavor,
_ => return Some(Err(format!("'{}' is not a valid value for linker-flavor. \
- Use {}", s, LinkerFlavor::one_of()))),
+ Use {}", s, LinkerFlavorCli::one_of()))),
}
Some(Ok(()))
})).unwrap_or(Ok(()))
base.$key_name = args;
}
} );
- ($key_name:ident, link_args) => ( {
- let name = (stringify!($key_name)).replace("_", "-");
- if let Some(val) = obj.remove(&name) {
+ ($key_name:ident = $json_name:expr, link_args) => ( {
+ let name = $json_name;
+ if let Some(val) = obj.remove(name) {
let obj = val.as_object().ok_or_else(|| format!("{}: expected a \
JSON object with fields per linker-flavor.", name))?;
- let mut args = LinkArgs::new();
+ let mut args = LinkArgsCli::new();
for (k, v) in obj {
- let flavor = LinkerFlavor::from_str(&k).ok_or_else(|| {
+ let flavor = LinkerFlavorCli::from_str(&k).ok_or_else(|| {
format!("{}: '{}' is not a valid value for linker-flavor. \
Use 'em', 'gcc', 'ld' or 'msvc'", name, k)
})?;
key!(env);
key!(abi);
key!(vendor);
- key!(linker_flavor, LinkerFlavor)?;
key!(linker, optional);
+ key!(linker_flavor_json = "linker-flavor", LinkerFlavor)?;
key!(lld_flavor, LldFlavor)?;
+ key!(linker_is_gnu, bool);
key!(pre_link_objects = "pre-link-objects", link_objects);
key!(post_link_objects = "post-link-objects", link_objects);
key!(pre_link_objects_self_contained = "pre-link-objects-fallback", link_objects);
key!(post_link_objects_self_contained = "post-link-objects-fallback", link_objects);
key!(link_self_contained = "crt-objects-fallback", link_self_contained)?;
- key!(pre_link_args, link_args);
- key!(late_link_args, link_args);
- key!(late_link_args_dynamic, link_args);
- key!(late_link_args_static, link_args);
- key!(post_link_args, link_args);
+ key!(pre_link_args_json = "pre-link-args", link_args);
+ key!(late_link_args_json = "late-link-args", link_args);
+ key!(late_link_args_dynamic_json = "late-link-args-dynamic", link_args);
+ key!(late_link_args_static_json = "late-link-args-static", link_args);
+ key!(post_link_args_json = "post-link-args", link_args);
key!(link_script, optional);
key!(link_env, env);
key!(link_env_remove, list);
key!(is_like_msvc, bool);
key!(is_like_wasm, bool);
key!(default_dwarf_version, u32);
- key!(linker_is_gnu, bool);
key!(allows_weak_linkage, bool);
key!(has_rpath, bool);
key!(no_default_libraries, bool);
// This can cause unfortunate ICEs later down the line.
return Err("may not set is_builtin for targets not built-in".into());
}
+ base.update_from_cli();
+
// Each field should have been read using `Json::remove` so any keys remaining are unused.
let remaining_keys = obj.keys();
Ok((
load_builtin(target_triple).expect("built-in target")
}
TargetTriple::TargetJson { .. } => {
- panic!("built-in targets doens't support target-paths")
+ panic!("built-in targets doesn't support target-paths")
}
}
}
fn to_json(&self) -> Json {
let mut d = serde_json::Map::new();
let default: TargetOptions = Default::default();
+ let mut target = self.clone();
+ target.update_to_cli();
macro_rules! target_val {
($attr:ident) => {{
let name = (stringify!($attr)).replace("_", "-");
- d.insert(name, self.$attr.to_json());
+ d.insert(name, target.$attr.to_json());
}};
}
macro_rules! target_option_val {
($attr:ident) => {{
let name = (stringify!($attr)).replace("_", "-");
- if default.$attr != self.$attr {
- d.insert(name, self.$attr.to_json());
+ if default.$attr != target.$attr {
+ d.insert(name, target.$attr.to_json());
}
}};
- ($attr:ident, $key_name:expr) => {{
- let name = $key_name;
- if default.$attr != self.$attr {
- d.insert(name.into(), self.$attr.to_json());
+ ($attr:ident, $json_name:expr) => {{
+ let name = $json_name;
+ if default.$attr != target.$attr {
+ d.insert(name.into(), target.$attr.to_json());
}
}};
- (link_args - $attr:ident) => {{
- let name = (stringify!($attr)).replace("_", "-");
- if default.$attr != self.$attr {
- let obj = self
+ (link_args - $attr:ident, $json_name:expr) => {{
+ let name = $json_name;
+ if default.$attr != target.$attr {
+ let obj = target
.$attr
.iter()
.map(|(k, v)| (k.desc().to_string(), v.clone()))
.collect::<BTreeMap<_, _>>();
- d.insert(name, obj.to_json());
+ d.insert(name.to_string(), obj.to_json());
}
}};
(env - $attr:ident) => {{
let name = (stringify!($attr)).replace("_", "-");
- if default.$attr != self.$attr {
- let obj = self
+ if default.$attr != target.$attr {
+ let obj = target
.$attr
.iter()
.map(|&(ref k, ref v)| format!("{k}={v}"))
target_option_val!(env);
target_option_val!(abi);
target_option_val!(vendor);
- target_option_val!(linker_flavor);
target_option_val!(linker);
+ target_option_val!(linker_flavor_json, "linker-flavor");
target_option_val!(lld_flavor);
+ target_option_val!(linker_is_gnu);
target_option_val!(pre_link_objects);
target_option_val!(post_link_objects);
target_option_val!(pre_link_objects_self_contained, "pre-link-objects-fallback");
target_option_val!(post_link_objects_self_contained, "post-link-objects-fallback");
target_option_val!(link_self_contained, "crt-objects-fallback");
- target_option_val!(link_args - pre_link_args);
- target_option_val!(link_args - late_link_args);
- target_option_val!(link_args - late_link_args_dynamic);
- target_option_val!(link_args - late_link_args_static);
- target_option_val!(link_args - post_link_args);
+ target_option_val!(link_args - pre_link_args_json, "pre-link-args");
+ target_option_val!(link_args - late_link_args_json, "late-link-args");
+ target_option_val!(link_args - late_link_args_dynamic_json, "late-link-args-dynamic");
+ target_option_val!(link_args - late_link_args_static_json, "late-link-args-static");
+ target_option_val!(link_args - post_link_args_json, "post-link-args");
target_option_val!(link_script);
target_option_val!(env - link_env);
target_option_val!(link_env_remove);
target_option_val!(is_like_msvc);
target_option_val!(is_like_wasm);
target_option_val!(default_dwarf_version);
- target_option_val!(linker_is_gnu);
target_option_val!(allows_weak_linkage);
target_option_val!(has_rpath);
target_option_val!(no_default_libraries);
-use crate::spec::{LinkerFlavor, LldFlavor, SplitDebuginfo, TargetOptions};
+use crate::spec::{DebuginfoKind, LinkerFlavor, LldFlavor, SplitDebuginfo, TargetOptions};
use std::borrow::Cow;
pub fn opts() -> TargetOptions {
// where `*.pdb` files show up next to the final artifact.
split_debuginfo: SplitDebuginfo::Packed,
supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Packed]),
+ debuginfo_kind: DebuginfoKind::Pdb,
..Default::default()
}
options: TargetOptions {
os: "cuda".into(),
vendor: "nvidia".into(),
- linker_flavor: LinkerFlavor::PtxLinker,
+ linker_flavor: LinkerFlavor::Ptx,
// The linker can be installed from `crates.io`.
linker: Some("rust-ptx-linker".into()),
linker_is_gnu: false,
--- /dev/null
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::openbsd_base::opts();
+ base.cpu = "ppc64".into();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ base.max_atomic_width = Some(64);
+
+ Target {
+ llvm_target: "powerpc64-unknown-openbsd".into(),
+ pointer_width: 64,
+ data_layout: "E-m:e-i64:64-n32:64".into(),
+ arch: "powerpc64".into(),
+ options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base },
+ }
+}
use crate::abi::Endian;
-use crate::spec::{LinkerFlavor, RelocModel, Target, TargetOptions};
+use crate::spec::{LinkerFlavor, Target, TargetOptions};
pub fn target() -> Target {
let mut base = super::freebsd_base::opts();
options: TargetOptions {
endian: Endian::Big,
features: "+secure-plt".into(),
- relocation_model: RelocModel::Pic,
mcount: "_mcount".into(),
..base
},
--- /dev/null
+use crate::spec::{CodeModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "riscv64-unknown-openbsd".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(),
+ arch: "riscv64".into(),
+ options: TargetOptions {
+ code_model: Some(CodeModel::Medium),
+ cpu: "generic-rv64".into(),
+ features: "+m,+a,+f,+d,+c".into(),
+ llvm_abiname: "lp64d".into(),
+ max_atomic_width: Some(64),
+ ..super::openbsd_base::opts()
+ },
+ }
+}
use std::assert_matches::assert_matches;
// Test target self-consistency and JSON encoding/decoding roundtrip.
-pub(super) fn test_target(target: Target) {
- target.check_consistency();
- assert_eq!(Target::from_json(target.to_json()).map(|(j, _)| j), Ok(target));
+pub(super) fn test_target(mut target: Target, triple: &str) {
+ let recycled_target = Target::from_json(target.to_json()).map(|(j, _)| j);
+ target.update_to_cli();
+ target.check_consistency(triple);
+ assert_eq!(recycled_target, Ok(target));
}
impl Target {
- fn check_consistency(&self) {
+ fn check_consistency(&self, triple: &str) {
assert_eq!(self.is_like_osx, self.vendor == "apple");
assert_eq!(self.is_like_solaris, self.os == "solaris" || self.os == "illumos");
assert_eq!(self.is_like_windows, self.os == "windows" || self.os == "uefi");
assert_eq!(self.is_like_wasm, self.arch == "wasm32" || self.arch == "wasm64");
- assert!(self.is_like_windows || !self.is_like_msvc);
+ if self.is_like_msvc {
+ assert!(self.is_like_windows);
+ }
// Check that default linker flavor and lld flavor are compatible
// with some other key properties.
assert_eq!(self.is_like_osx, matches!(self.lld_flavor, LldFlavor::Ld64));
assert_eq!(self.is_like_msvc, matches!(self.lld_flavor, LldFlavor::Link));
assert_eq!(self.is_like_wasm, matches!(self.lld_flavor, LldFlavor::Wasm));
- assert_eq!(self.os == "l4re", matches!(self.linker_flavor, LinkerFlavor::L4Bender));
- assert_eq!(self.os == "emscripten", matches!(self.linker_flavor, LinkerFlavor::Em));
- assert_eq!(self.arch == "bpf", matches!(self.linker_flavor, LinkerFlavor::BpfLinker));
- assert_eq!(self.arch == "nvptx64", matches!(self.linker_flavor, LinkerFlavor::PtxLinker));
+ assert_eq!(self.os == "emscripten", matches!(self.linker_flavor, LinkerFlavor::EmCc));
+ assert_eq!(self.arch == "bpf", matches!(self.linker_flavor, LinkerFlavor::Bpf));
+ assert_eq!(self.arch == "nvptx64", matches!(self.linker_flavor, LinkerFlavor::Ptx));
for args in [
&self.pre_link_args,
LinkerFlavor::Lld(LldFlavor::Wasm) | LinkerFlavor::Gcc
)
}
- (LinkerFlavor::L4Bender, LldFlavor::Ld) => {
- assert_matches!(flavor, LinkerFlavor::L4Bender)
- }
- (LinkerFlavor::Em, LldFlavor::Wasm) => {
- assert_matches!(flavor, LinkerFlavor::Em)
+ (LinkerFlavor::EmCc, LldFlavor::Wasm) => {
+ assert_matches!(flavor, LinkerFlavor::EmCc)
}
- (LinkerFlavor::BpfLinker, LldFlavor::Ld) => {
- assert_matches!(flavor, LinkerFlavor::BpfLinker)
+ (LinkerFlavor::Bpf, LldFlavor::Ld) => {
+ assert_matches!(flavor, LinkerFlavor::Bpf)
}
- (LinkerFlavor::PtxLinker, LldFlavor::Ld) => {
- assert_matches!(flavor, LinkerFlavor::PtxLinker)
+ (LinkerFlavor::Ptx, LldFlavor::Ld) => {
+ assert_matches!(flavor, LinkerFlavor::Ptx)
}
flavors => unreachable!("unexpected flavor combination: {:?}", flavors),
}
check_noncc(LinkerFlavor::Ld);
check_noncc(LinkerFlavor::Lld(LldFlavor::Ld));
}
+ LldFlavor::Ld64 => check_noncc(LinkerFlavor::Lld(LldFlavor::Ld64)),
LldFlavor::Wasm => check_noncc(LinkerFlavor::Lld(LldFlavor::Wasm)),
- LldFlavor::Ld64 | LldFlavor::Link => {}
+ LldFlavor::Link => {}
},
_ => {}
}
);
}
- assert!(
- (self.pre_link_objects_self_contained.is_empty()
- && self.post_link_objects_self_contained.is_empty())
- || self.link_self_contained != LinkSelfContainedDefault::False
- );
+ if self.link_self_contained == LinkSelfContainedDefault::False {
+ assert!(
+ self.pre_link_objects_self_contained.is_empty()
+ && self.post_link_objects_self_contained.is_empty()
+ );
+ }
// If your target really needs to deviate from the rules below,
// except it and document the reasons.
// Keep the default "unknown" vendor instead.
assert_ne!(self.vendor, "");
+ assert_ne!(self.os, "");
if !self.can_use_os_unknown() {
// Keep the default "none" for bare metal targets instead.
assert_ne!(self.os, "unknown");
}
+
+ // Check dynamic linking stuff
+ // BPF: when targeting user space vms (like rbpf), those can load dynamic libraries.
+ if self.os == "none" && self.arch != "bpf" {
+ assert!(!self.dynamic_linking);
+ }
+ if self.only_cdylib
+ || self.crt_static_allows_dylibs
+ || !self.late_link_args_dynamic.is_empty()
+ {
+ assert!(self.dynamic_linking);
+ }
+ // Apparently PIC was slow on wasm at some point, see comments in wasm_base.rs
+ if self.dynamic_linking && !(self.is_like_wasm && self.os != "emscripten") {
+ assert_eq!(self.relocation_model, RelocModel::Pic);
+ }
+ // PIEs are supported but not enabled by default with linuxkernel target.
+ if self.position_independent_executables && !triple.ends_with("-linuxkernel") {
+ assert_eq!(self.relocation_model, RelocModel::Pic);
+ }
+ if self.relocation_model == RelocModel::Pic {
+ assert!(self.dynamic_linking || self.position_independent_executables);
+ }
+ if self.static_position_independent_executables {
+ assert!(self.position_independent_executables);
+ }
+ if self.position_independent_executables {
+ assert!(self.executables);
+ }
+
+ // Check crt static stuff
+ if self.crt_static_default || self.crt_static_allows_dylibs {
+ assert!(self.crt_static_respected);
+ }
}
// Add your target to the whitelist if it has `std` library
// the timer-interrupt. Device-drivers are required to use polling-based models. Furthermore, all
// code runs in the same environment, no process separation is supported.
-use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, StackProbeType, TargetOptions};
+use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy};
+use crate::spec::{RelocModel, StackProbeType, TargetOptions};
pub fn opts() -> TargetOptions {
let mut base = super::msvc_base::opts();
stack_probes: StackProbeType::Call,
singlethread: true,
linker: Some("rust-lld".into()),
+ relocation_model: RelocModel::Static,
..base
}
}
// Reset flags for non-Em flavors back to empty to satisfy sanity checking tests.
let pre_link_args = LinkArgs::new();
let post_link_args = TargetOptions::link_args(
- LinkerFlavor::Em,
+ LinkerFlavor::EmCc,
&["-sABORTING_MALLOC=0", "-Wl,--fatal-warnings"],
);
let opts = TargetOptions {
os: "emscripten".into(),
- linker_flavor: LinkerFlavor::Em,
+ linker_flavor: LinkerFlavor::EmCc,
// emcc emits two files - a .js file to instantiate the wasm and supply platform
// functionality, and a .wasm file.
exe_suffix: ".js".into(),
pub fn opts() -> TargetOptions {
// We cannot use `-nodefaultlibs` because compiler-rt has to be passed
// as a path since it's not added to linker search path by the default.
- // There were attemts to make it behave like libgcc (so one can just use -l<name>)
+ // There were attempts to make it behave like libgcc (so one can just use -l<name>)
// but LLVM maintainers rejected it: https://reviews.llvm.org/D51440
let pre_link_args =
TargetOptions::link_args(LinkerFlavor::Gcc, &["-nolibc", "--unwindlib=none"]);
-use crate::spec::{cvs, DebuginfoKind, TargetOptions};
+use crate::spec::{cvs, TargetOptions};
pub fn opts() -> TargetOptions {
let base = super::msvc_base::opts();
// not ever be possible for us to pass this flag.
no_default_libraries: false,
has_thread_local: true,
- debuginfo_kind: DebuginfoKind::Pdb,
..base
}
let mut base = super::l4re_base::opts();
base.cpu = "x86-64".into();
base.max_atomic_width = Some(64);
- base.crt_static_allows_dylibs = false;
- base.dynamic_linking = false;
base.panic_strategy = PanicStrategy::Abort;
Target {
// `target-cpu` compiler flags to opt-in more hardware-specific
// features.
-use super::{
- CodeModel, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, RelroLevel, StackProbeType,
- Target, TargetOptions,
-};
+use super::{CodeModel, LinkerFlavor, LldFlavor, PanicStrategy};
+use super::{RelroLevel, StackProbeType, Target, TargetOptions};
pub fn target() -> Target {
let opts = TargetOptions {
position_independent_executables: true,
static_position_independent_executables: true,
relro_level: RelroLevel::Full,
- relocation_model: RelocModel::Pic,
linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
linker: Some("rust-lld".into()),
features:
#![feature(drain_filter)]
#![feature(hash_drain_filter)]
#![cfg_attr(bootstrap, feature(label_break_value))]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(if_let_guard)]
#![feature(never_type)]
/// obligations *could be* resolved if we wanted to.
///
/// This also expects that `trait_ref` is fully normalized.
-#[instrument(level = "debug", skip(tcx))]
pub fn codegen_fulfill_obligation<'tcx>(
tcx: TyCtxt<'tcx>,
(param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>),
// (ouz-a) This is required for `type-alias-impl-trait/assoc-projection-ice.rs` to pass
let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
- debug!("Cache miss: {trait_ref:?} => {impl_source:?}");
Ok(&*tcx.arena.alloc(impl_source))
})
}
pub trait TraitEngineExt<'tcx> {
fn new(tcx: TyCtxt<'tcx>) -> Box<Self>;
+ fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box<Self>;
}
impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> {
Box::new(FulfillmentContext::new())
}
}
+
+ fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box<Self> {
+ if tcx.sess.opts.unstable_opts.chalk {
+ Box::new(ChalkFulfillmentContext::new())
+ } else {
+ Box::new(FulfillmentContext::new_in_snapshot())
+ }
+ }
}
/// Used if you want to have pleasant experience when dealing
Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx.tcx)) }
}
+ pub fn new_in_snapshot(infcx: &'a InferCtxt<'a, 'tcx>) -> Self {
+ Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new_in_snapshot(infcx.tcx)) }
+ }
+
pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) {
self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation);
}
let predicate = self.resolve_vars_if_possible(obligation.predicate);
let span = obligation.cause.span;
- debug!(?predicate, obligation.cause.code = tracing::field::debug(&obligation.cause.code()));
+ debug!(?predicate, obligation.cause.code = ?obligation.cause.code());
// Ambiguity errors are often caused as fallout from earlier errors.
// We ignore them if this `infcx` is tainted in some cases below.
use rustc_hir::intravisit::Visitor;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
-use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::hir::map;
use rustc_middle::ty::{
self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree,
real_trait_pred = parent_trait_pred;
}
- // Skipping binder here, remapping below
- let real_ty = real_trait_pred.self_ty().skip_binder();
- if self.can_eq(obligation.param_env, real_ty, arg_ty).is_err() {
+ let real_ty = real_trait_pred.self_ty();
+ // We `erase_late_bound_regions` here because `make_subregion` does not handle
+ // `ReLateBound`, and we don't particularly care about the regions.
+ if self
+ .can_eq(obligation.param_env, self.tcx.erase_late_bound_regions(real_ty), arg_ty)
+ .is_err()
+ {
continue;
}
- if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() {
+ if let ty::Ref(region, base_ty, mutbl) = *real_ty.skip_binder().kind() {
let mut autoderef = Autoderef::new(
self,
obligation.param_env,
expected: ty::PolyTraitRef<'tcx>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
pub(crate) fn build_fn_sig_ty<'tcx>(
- tcx: TyCtxt<'tcx>,
+ infcx: &InferCtxt<'_, 'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
) -> Ty<'tcx> {
let inputs = trait_ref.skip_binder().substs.type_at(1);
let sig = match inputs.kind() {
ty::Tuple(inputs)
- if tcx.fn_trait_kind_from_lang_item(trait_ref.def_id()).is_some() =>
+ if infcx.tcx.fn_trait_kind_from_lang_item(trait_ref.def_id()).is_some() =>
{
- tcx.mk_fn_sig(
+ infcx.tcx.mk_fn_sig(
inputs.iter(),
- tcx.mk_ty_infer(ty::TyVar(ty::TyVid::from_u32(0))),
+ infcx.next_ty_var(TypeVariableOrigin {
+ span: DUMMY_SP,
+ kind: TypeVariableOriginKind::MiscVariable,
+ }),
false,
hir::Unsafety::Normal,
abi::Abi::Rust,
)
}
- _ => tcx.mk_fn_sig(
+ _ => infcx.tcx.mk_fn_sig(
std::iter::once(inputs),
- tcx.mk_ty_infer(ty::TyVar(ty::TyVid::from_u32(0))),
+ infcx.next_ty_var(TypeVariableOrigin {
+ span: DUMMY_SP,
+ kind: TypeVariableOriginKind::MiscVariable,
+ }),
false,
hir::Unsafety::Normal,
abi::Abi::Rust,
),
};
- tcx.mk_fn_ptr(trait_ref.rebind(sig))
+ infcx.tcx.mk_fn_ptr(trait_ref.rebind(sig))
}
let argument_kind = match expected.skip_binder().self_ty().kind() {
let found_span = found_span.unwrap_or(span);
err.span_label(found_span, "found signature defined here");
- let expected = build_fn_sig_ty(self.tcx, expected);
- let found = build_fn_sig_ty(self.tcx, found);
+ let expected = build_fn_sig_ty(self, expected);
+ let found = build_fn_sig_ty(self, found);
- let (expected_str, found_str) =
- self.tcx.infer_ctxt().enter(|infcx| infcx.cmp(expected, found));
+ let (expected_str, found_str) = self.cmp(expected, found);
let signature_kind = format!("{argument_kind} signature");
err.note_expected_found(&signature_kind, expected_str, &signature_kind, found_str);
/// `SomeTrait` or a where-clause that lets us unify `$0` with
/// something concrete. If this fails, we'll unify `$0` with
/// `projection_ty` again.
- #[tracing::instrument(level = "debug", skip(self, infcx, param_env, cause))]
+ #[instrument(level = "debug", skip(self, infcx, param_env, cause))]
fn normalize_projection_type(
&mut self,
infcx: &InferCtxt<'_, 'tcx>,
}
}
+ #[inline(never)]
fn process_backedge<'c, I>(
&mut self,
cycle: I,
/// If successful, this may result in additional obligations.
///
/// See [poly_project_and_unify_type] for an explanation of the return value.
-#[tracing::instrument(level = "debug", skip(selcx))]
+#[instrument(level = "debug", skip(selcx))]
fn project_and_unify_type<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionObligation<'tcx>,
///
/// IMPORTANT:
/// - `obligation` must be fully normalized
-#[tracing::instrument(level = "info", skip(selcx))]
+#[instrument(level = "info", skip(selcx))]
fn project<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
);
}
-#[tracing::instrument(
+#[instrument(
level = "debug",
skip(selcx, candidate_set, ctor, env_predicates, potentially_unnormalized_candidates)
)]
}
}
-#[tracing::instrument(level = "debug", skip(selcx, obligation, candidate_set))]
+#[instrument(level = "debug", skip(selcx, obligation, candidate_set))]
fn assemble_candidates_from_impls<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
use super::{EvaluatedCandidate, SelectionCandidateSet, SelectionContext, TraitObligationStack};
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
- #[instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self), ret)]
pub(super) fn candidate_from_obligation<'o>(
&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
if let Some(c) =
self.check_candidate_cache(stack.obligation.param_env, cache_fresh_trait_pred)
{
- debug!(candidate = ?c, "CACHE HIT");
+ debug!("CACHE HIT");
return c;
}
let (candidate, dep_node) =
self.in_task(|this| this.candidate_from_obligation_no_cache(stack));
- debug!(?candidate, "CACHE MISS");
+ debug!("CACHE MISS");
self.insert_candidate_cache(
stack.obligation.param_env,
cache_fresh_trait_pred,
Ok(candidates)
}
- #[tracing::instrument(level = "debug", skip(self, candidates))]
+ #[instrument(level = "debug", skip(self, candidates))]
fn assemble_candidates_from_projected_tys(
&mut self,
obligation: &TraitObligation<'tcx>,
/// supplied to find out whether it is listed among them.
///
/// Never affects the inference environment.
- #[tracing::instrument(level = "debug", skip(self, stack, candidates))]
+ #[instrument(level = "debug", skip(self, stack, candidates))]
fn assemble_candidates_from_caller_bounds<'o>(
&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
};
}
- #[tracing::instrument(level = "debug", skip(self, obligation, candidates))]
+ #[instrument(level = "debug", skip(self, obligation, candidates))]
fn assemble_candidates_for_transmutability(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates.vec.push(TransmutabilityCandidate);
}
- #[tracing::instrument(level = "debug", skip(self, obligation, candidates))]
+ #[instrument(level = "debug", skip(self, obligation, candidates))]
fn assemble_candidates_for_trait_alias(
&mut self,
obligation: &TraitObligation<'tcx>,
/// Assembles the trait which are built-in to the language itself:
/// `Copy`, `Clone` and `Sized`.
- #[tracing::instrument(level = "debug", skip(self, candidates))]
+ #[instrument(level = "debug", skip(self, candidates))]
fn assemble_builtin_bound_candidates(
&mut self,
conditions: BuiltinImplConditions<'tcx>,
/// Attempts to satisfy the obligation. If successful, this will affect the surrounding
/// type environment by performing unification.
- #[instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self), ret)]
pub fn select(
&mut self,
obligation: &TraitObligation<'tcx>,
Err(SelectionError::Overflow(OverflowError::Canonical))
}
Err(e) => Err(e),
- Ok(candidate) => {
- debug!(?candidate, "confirmed");
- Ok(Some(candidate))
- }
+ Ok(candidate) => Ok(Some(candidate)),
}
}
level = "debug",
skip(self, previous_stack),
fields(previous_stack = ?previous_stack.head())
+ ret,
)]
fn evaluate_predicate_recursively<'o>(
&mut self,
None => self.check_recursion_limit(&obligation, &obligation)?,
}
- let result = ensure_sufficient_stack(|| {
+ ensure_sufficient_stack(|| {
let bound_predicate = obligation.predicate.kind();
match bound_predicate.skip_binder() {
ty::PredicateKind::Trait(t) => {
bug!("TypeWellFormedFromEnv is only used for chalk")
}
}
- });
-
- debug!("finished: {:?} from {:?}", result, obligation);
-
- result
+ })
}
- #[instrument(skip(self, previous_stack), level = "debug")]
+ #[instrument(skip(self, previous_stack), level = "debug", ret)]
fn evaluate_trait_predicate_recursively<'o>(
&mut self,
previous_stack: TraitObligationStackList<'o, 'tcx>,
// If a trait predicate is in the (local or global) evaluation cache,
// then we know it holds without cycles.
if let Some(result) = self.check_evaluation_cache(param_env, fresh_trait_pred) {
- debug!(?result, "CACHE HIT");
+ debug!("CACHE HIT");
return Ok(result);
}
if let Some(result) = stack.cache().get_provisional(fresh_trait_pred) {
- debug!(?result, "PROVISIONAL CACHE HIT");
+ debug!("PROVISIONAL CACHE HIT");
stack.update_reached_depth(result.reached_depth);
return Ok(result.result);
}
let reached_depth = stack.reached_depth.get();
if reached_depth >= stack.depth {
- debug!(?result, "CACHE MISS");
+ debug!("CACHE MISS");
self.insert_evaluation_cache(param_env, fresh_trait_pred, dep_node, result);
stack.cache().on_completion(stack.dfn);
} else {
- debug!(?result, "PROVISIONAL");
+ debug!("PROVISIONAL");
debug!(
"caching provisionally because {:?} \
is a cycle participant (at depth {}, reached depth {})",
#[instrument(
level = "debug",
skip(self, stack),
- fields(depth = stack.obligation.recursion_depth)
+ fields(depth = stack.obligation.recursion_depth),
+ ret
)]
fn evaluate_candidate<'o>(
&mut self,
result = result.max(EvaluatedToOkModuloRegions);
}
- debug!(?result);
Ok(result)
}
/// a projection, look at the bounds of `T::Bar`, see if we can find a
/// `Baz` bound. We return indexes into the list returned by
/// `tcx.item_bounds` for any applicable bounds.
- #[instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self), ret)]
fn match_projection_obligation_against_definition_bounds(
&mut self,
obligation: &TraitObligation<'tcx>,
// unnecessary ambiguity.
let mut distinct_normalized_bounds = FxHashSet::default();
- let matching_bounds = bounds
+ bounds
.iter()
.enumerate()
.filter_map(|(idx, bound)| {
}
None
})
- .collect();
-
- debug!(?matching_bounds);
- matching_bounds
+ .collect()
}
/// Equates the trait in `obligation` with trait bound. If the two traits
}
}
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self), ret)]
fn match_impl(
&mut self,
impl_def_id: DefId,
.at(&cause, obligation.param_env)
.define_opaque_types(false)
.eq(placeholder_obligation_trait_ref, impl_trait_ref)
- .map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{}`", e))?;
+ .map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{e}`"))?;
nested_obligations.extend(obligations);
if !self.intercrate
&& self.tcx().impl_polarity(impl_def_id) == ty::ImplPolarity::Reservation
{
- debug!("match_impl: reservation impls only apply in intercrate mode");
+ debug!("reservation impls only apply in intercrate mode");
return Err(());
}
- debug!(?impl_substs, ?nested_obligations, "match_impl: success");
Ok(Normalized { value: impl_substs, obligations: nested_obligations })
}
/// impl or trait. The obligations are substituted and fully
/// normalized. This is used when confirming an impl or default
/// impl.
- #[tracing::instrument(level = "debug", skip(self, cause, param_env))]
+ #[instrument(level = "debug", skip(self, cause, param_env))]
fn impl_or_trait_obligations(
&mut self,
cause: &ObligationCause<'tcx>,
///
/// Requires that trait definitions have been processed so that we can
/// elaborate predicates and walk supertraits.
-#[instrument(skip(tcx, predicates), level = "debug")]
+#[instrument(skip(tcx, predicates), level = "debug", ret)]
pub(crate) fn required_region_bounds<'tcx>(
tcx: TyCtxt<'tcx>,
erased_self_ty: Ty<'tcx>,
GenericArgKind::Const(..) => {
chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner))
}
- GenericArgKind::Lifetime(lt) => bug!("unexpect well formed predicate: {:?}", lt),
+ GenericArgKind::Lifetime(lt) => bug!("unexpected well formed predicate: {:?}", lt),
},
ty::PredicateKind::ObjectSafe(t) => chalk_ir::GoalData::DomainGoal(
EarlyBinder(value).subst(self.tcx(), substs)
}
+ #[instrument(level = "debug", skip(self))]
fn relate_mir_and_user_ty(
&mut self,
mir_ty: Ty<'tcx>,
let ty = tcx.type_of(def_id);
let ty = self.subst(ty, substs);
- debug!("relate_type_and_user_type: ty of def-id is {:?}", ty);
let ty = self.normalize(ty);
+ debug!("relate_type_and_user_type: ty of def-id is {:?}", ty);
self.relate(mir_ty, Variance::Invariant, ty)?;
// outlives" error messages.
let instantiated_predicates =
self.tcx().predicates_of(def_id).instantiate(self.tcx(), substs);
- debug!(?instantiated_predicates.predicates);
+ debug!(?instantiated_predicates);
for instantiated_predicate in instantiated_predicates.predicates {
let instantiated_predicate = self.normalize(instantiated_predicate);
self.prove_predicate(instantiated_predicate, span);
tcx,
)?,
AdtKind::Enum => {
- tracing::trace!(?adt_def, "treeifying enum");
+ trace!(?adt_def, "treeifying enum");
let mut tree = Tree::uninhabited();
for (idx, discr) in adt_def.discriminants(tcx) {
let clamp =
|align: Align| align.clamp(min_align, max_align).bytes().try_into().unwrap();
- let variant_span = tracing::trace_span!(
+ let variant_span = trace_span!(
"treeifying variant",
min_align = ?min_align,
max_align = ?max_align,
// The layout of the variant is prefixed by the discriminant, if any.
if let Some(discr) = discr {
- tracing::trace!(?discr, "treeifying discriminant");
+ trace!(?discr, "treeifying discriminant");
let discr_layout = alloc::Layout::from_size_align(
layout_summary.discriminant_size,
clamp(layout_summary.discriminant_align),
)
.unwrap();
- tracing::trace!(?discr_layout, "computed discriminant layout");
+ trace!(?discr_layout, "computed discriminant layout");
variant_layout = variant_layout.extend(discr_layout).unwrap().0;
tree = tree.then(Self::from_disr(discr, tcx, layout_summary.discriminant_size));
}
// Next come fields.
- let fields_span = tracing::trace_span!("treeifying fields").entered();
+ let fields_span = trace_span!("treeifying fields").entered();
for field_def in variant_def.fields.iter() {
let field_ty = field_def.ty(tcx, substs_ref);
- let _span = tracing::trace_span!("treeifying field", field = ?field_ty).entered();
+ let _span = trace_span!("treeifying field", field = ?field_ty).entered();
// begin with the field's visibility
tree = tree.then(Self::def(Def::Field(field_def)));
- // compute the field's layout charactaristics
+ // compute the field's layout characteristics
let field_layout = layout_of(tcx, field_ty)?.clamp_align(min_align, max_align);
// next comes the field's padding
drop(fields_span);
// finally: padding
- let padding_span = tracing::trace_span!("adding trailing padding").entered();
+ let padding_span = trace_span!("adding trailing padding").entered();
let padding_needed = layout_summary.total_size - variant_layout.size();
if padding_needed > 0 {
tree = tree.then(Self::padding(padding_needed));
layout.align().abi.bytes().try_into().unwrap(),
)
.unwrap();
- tracing::trace!(?ty, ?layout, "computed layout for type");
+ trace!(?ty, ?layout, "computed layout for type");
Ok(layout)
}
}
// Remove all `Def` nodes from `src`, without checking their visibility.
let src = src.prune(&|def| true);
- tracing::trace!(?src, "pruned src");
+ trace!(?src, "pruned src");
// Remove all `Def` nodes from `dst`, additionally...
let dst = if assume_visibility {
dst.prune(&|def| context.is_accessible_from(def, scope))
};
- tracing::trace!(?dst, "pruned dst");
+ trace!(?dst, "pruned dst");
// Convert `src` from a tree-based representation to an NFA-based representation.
// If the conversion fails because `src` is uninhabited, conclude that the transmutation
false
};
- tracing::trace!(?ret, "ret");
+ trace!(?ret, "ret");
ret
}
use std::collections::BTreeMap;
use std::ops::ControlFlow;
-use tracing::debug;
-
// FIXME(#86795): `BoundVarsCollector` here should **NOT** be used
// outside of `resolve_associated_item`. It's just to address #64494,
// #83765, and #85848 which are creating bound types/regions that lose
}
/// See `ParamEnv` struct definition for details.
-#[instrument(level = "debug", skip(tcx))]
fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
// The param_env of an impl Trait type is its defining function's param_env
if let Some(parent) = ty::is_impl_trait_defn(tcx, def_id) {
}
/// Don't call this directly: use ``tcx.conservative_is_privately_uninhabited`` instead.
-#[instrument(level = "debug", skip(tcx))]
pub fn conservative_is_privately_uninhabited_raw<'tcx>(
tcx: TyCtxt<'tcx>,
param_env_and: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
/// instantiated with some generic arguments providing `'a` explicitly,
/// we taint those arguments with `ExplicitLateBound::Yes` so that we
/// can provide an appropriate diagnostic later.
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, PartialEq, Debug)]
pub enum ExplicitLateBound {
Yes,
No,
/// A marker denoting that the generic arguments that were
/// provided did not match the respective generic parameters.
-#[derive(Clone, Default)]
+#[derive(Clone, Default, Debug)]
pub struct GenericArgCountMismatch {
/// Indicates whether a fatal error was reported (`Some`), or just a lint (`None`).
pub reported: Option<ErrorGuaranteed>,
/// Decorates the result of a generic argument count mismatch
/// check with whether explicit late bounds were provided.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub struct GenericArgCountResult {
pub explicit_late_bound: ExplicitLateBound,
pub correct: Result<(), GenericArgCountMismatch>,
}
impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self), ret)]
pub fn ast_region_to_region(
&self,
lifetime: &hir::Lifetime,
let tcx = self.tcx();
let lifetime_name = |def_id| tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id));
- let r = match tcx.named_region(lifetime.hir_id) {
+ match tcx.named_region(lifetime.hir_id) {
Some(rl::Region::Static) => tcx.lifetimes.re_static,
Some(rl::Region::LateBound(debruijn, index, def_id)) => {
tcx.mk_region(ty::ReLateBound(debruijn, br))
}
- Some(rl::Region::EarlyBound(index, id)) => {
- let name = lifetime_name(id.expect_local());
- tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id: id, index, name }))
+ Some(rl::Region::EarlyBound(def_id)) => {
+ let name = tcx.hir().ty_param_name(def_id.expect_local());
+ let item_def_id = tcx.hir().ty_param_owner(def_id.expect_local());
+ let generics = tcx.generics_of(item_def_id);
+ let index = generics.param_def_id_to_index[&def_id];
+ tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, index, name }))
}
Some(rl::Region::Free(scope, id)) => {
tcx.lifetimes.re_static
})
}
- };
-
- debug!("ast_region_to_region(lifetime={:?}) yields {:?}", lifetime, r);
-
- r
+ }
}
/// Given a path `path` that refers to an item `I` with the declared generics `decl_generics`,
/// `[Vec<u8>, u8]` and `generic_args` are the arguments for the associated
/// type itself: `['a]`. The returned `SubstsRef` concatenates these two
/// lists: `[Vec<u8>, u8, 'a]`.
- #[tracing::instrument(level = "debug", skip(self, span))]
+ #[instrument(level = "debug", skip(self, span), ret)]
fn create_substs_for_ast_path<'a>(
&self,
span: Span,
&mut substs_ctx,
);
- debug!(
- "create_substs_for_ast_path(generic_params={:?}, self_ty={:?}) -> {:?}",
- generics, self_ty, substs
- );
-
(substs, arg_count)
}
/// where `'a` is a bound region at depth 0. Similarly, the `poly_trait_ref` would be
/// `Bar<'a>`. The returned poly-trait-ref will have this binder instantiated explicitly,
/// however.
- #[tracing::instrument(level = "debug", skip(self, span, constness, bounds, speculative))]
+ #[instrument(level = "debug", skip(self, span, constness, bounds, speculative))]
pub(crate) fn instantiate_poly_trait_ref(
&self,
trait_ref: &hir::TraitRef<'_>,
ty::TraitRef::new(trait_def_id, substs)
}
- #[tracing::instrument(level = "debug", skip(self, span))]
+ #[instrument(level = "debug", skip(self, span))]
fn create_substs_for_ast_trait_ref<'a>(
&self,
span: Span,
/// **A note on binders:** there is an implied binder around
/// `param_ty` and `ast_bounds`. See `instantiate_poly_trait_ref`
/// for more details.
- #[tracing::instrument(level = "debug", skip(self, ast_bounds, bounds))]
+ #[instrument(level = "debug", skip(self, ast_bounds, bounds))]
pub(crate) fn add_bounds<'hir, I: Iterator<Item = &'hir hir::GenericBound<'hir>>>(
&self,
param_ty: Ty<'tcx>,
/// **A note on binders:** given something like `T: for<'a> Iterator<Item = &'a u32>`, the
/// `trait_ref` here will be `for<'a> T: Iterator`. The `binding` data however is from *inside*
/// the binder (e.g., `&'a u32`) and hence may reference bound regions.
- #[tracing::instrument(
- level = "debug",
- skip(self, bounds, speculative, dup_bindings, path_span)
- )]
+ #[instrument(level = "debug", skip(self, bounds, speculative, dup_bindings, path_span))]
fn add_predicates_for_ast_type_binding(
&self,
hir_ref_id: hir::HirId,
/// Turns a `hir::Ty` into a `Ty`. For diagnostics' purposes we keep track of whether trait
/// objects are borrowed like `&dyn Trait` to avoid emitting redundant errors.
- #[tracing::instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self), ret)]
fn ast_ty_to_ty_inner(&self, ast_ty: &hir::Ty<'_>, borrowed: bool, in_path: bool) -> Ty<'tcx> {
let tcx = self.tcx();
hir::TyKind::Err => tcx.ty_error(),
};
- debug!(?result_ty);
-
self.record_ty(ast_ty.hir_id, result_ty, ast_ty.span);
result_ty
}
);
if !infer_replacements.is_empty() {
- diag.multipart_suggestion(&format!(
+ diag.multipart_suggestion(
+ &format!(
"try replacing `_` with the type{} in the corresponding trait method signature",
rustc_errors::pluralize!(infer_replacements.len()),
- ), infer_replacements, Applicability::MachineApplicable);
+ ),
+ infer_replacements,
+ Applicability::MachineApplicable,
+ );
}
diag.emit();
};
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- #[instrument(skip(self), level = "debug")]
+ #[instrument(skip(self), level = "debug", ret)]
pub fn check_match(
&self,
expr: &'tcx hir::Expr<'tcx>,
// We won't diverge unless the scrutinee or all arms diverge.
self.diverges.set(scrut_diverges | all_arms_diverge);
- let match_ty = coercion.complete(self);
- debug!(?match_ty);
- match_ty
+ coercion.complete(self)
}
/// When the previously checked expression (the scrutinee) diverges,
decl.output.span(),
param_env,
));
- // If we replaced declared_ret_ty with infer vars, then we must be infering
+ // If we replaced declared_ret_ty with infer vars, then we must be inferring
// an opaque type, so set a flag so we can improve diagnostics.
fcx.return_type_has_opaque = ret_ty != declared_ret_ty;
None => {
// At this point we know this discriminant is a duplicate, and was not explicitly
// assigned by the user. Here we iterate backwards to fetch the HIR for the last
- // explictly assigned discriminant, and letting the user know that this was the
+ // explicitly assigned discriminant, and letting the user know that this was the
// increment startpoint, and how many steps from there leading to the duplicate
if let Some((n, hir::Variant { span, ident, .. })) =
vs[..idx].iter().rev().enumerate().find(|v| v.1.disr_expr.is_some())
};
// Here we loop through the discriminants, comparing each discriminant to another.
- // When a duplicate is detected, we instatiate an error and point to both
+ // When a duplicate is detected, we instantiate an error and point to both
// initial and duplicate value. The duplicate discriminant is then discarded by swapping
// it with the last element and decrementing the `vec.len` (which is why we have to evaluate
// `discrs.len()` anew every iteration, and why this could be tricky to do in a functional
self.check_closure(expr, expected_kind, decl, body, gen, expected_sig)
}
- #[instrument(skip(self, expr, body, decl), level = "debug")]
+ #[instrument(skip(self, expr, body, decl), level = "debug", ret)]
fn check_closure(
&self,
expr: &hir::Expr<'_>,
},
);
- let closure_type = self.tcx.mk_closure(expr_def_id.to_def_id(), closure_substs.substs);
-
- debug!(?expr.hir_id, ?closure_type);
-
- closure_type
+ self.tcx.mk_closure(expr_def_id.to_def_id(), closure_substs.substs)
}
/// Given the expected type, figures out what it can about this closure we
/// The `cause_span` should be the span that caused us to
/// have this expected signature, or `None` if we can't readily
/// know that.
- #[instrument(level = "debug", skip(self, cause_span))]
+ #[instrument(level = "debug", skip(self, cause_span), ret)]
fn deduce_sig_from_projection(
&self,
cause_span: Option<Span>,
hir::Unsafety::Normal,
Abi::Rust,
));
- debug!(?sig);
Some(ExpectedSig { cause_span, sig })
}
/// types that the user gave into a signature.
///
/// Also, record this closure signature for later.
- #[instrument(skip(self, decl, body), level = "debug")]
+ #[instrument(skip(self, decl, body), level = "debug", ret)]
fn supplied_sig_of_closure(
&self,
hir_id: hir::HirId,
bound_vars,
);
- debug!(?result);
-
let c_result = self.inh.infcx.canonicalize_response(result);
self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result);
/// user specified. The "desugared" return type is an `impl
/// Future<Output = T>`, so we do this by searching through the
/// obligations to extract the `T`.
- #[instrument(skip(self), level = "debug")]
+ #[instrument(skip(self), level = "debug", ret)]
fn deduce_future_output_from_obligations(
&self,
expr_def_id: DefId,
);
self.register_predicates(obligations);
- debug!("deduce_future_output_from_obligations: output_ty={:?}", output_ty);
Some(output_ty)
}
/// For default associated types the normalization is not possible (the value
/// from the impl could be overridden). We also can't normalize generic
/// associated types (yet) because they contain bound parameters.
-#[tracing::instrument(level = "debug", skip(tcx))]
+#[instrument(level = "debug", skip(tcx))]
pub fn check_type_bounds<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ty: &ty::AssocItem,
///
/// N.B., this code relies on `self.diverges` to be accurate. In particular, assignments to `!`
/// will be permitted if the diverges flag is currently "always".
- #[tracing::instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))]
+ #[instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))]
pub fn demand_coerce_diag(
&self,
expr: &hir::Expr<'tcx>,
};
use crate::type_error_struct;
-use super::suggest_call_constructor;
use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
use rustc_middle::ty::error::TypeError::FieldMisMatch;
use rustc_middle::ty::subst::SubstsRef;
-use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeVisitable};
+use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitable};
use rustc_session::parse::feature_err;
use rustc_span::hygiene::DesugaringKind;
use rustc_span::lev_distance::find_best_match_for_name;
field: Ident,
) -> Ty<'tcx> {
debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field);
- let expr_t = self.check_expr(base);
- let expr_t = self.structurally_resolved_type(base.span, expr_t);
+ let base_ty = self.check_expr(base);
+ let base_ty = self.structurally_resolved_type(base.span, base_ty);
let mut private_candidate = None;
- let mut autoderef = self.autoderef(expr.span, expr_t);
- while let Some((base_t, _)) = autoderef.next() {
- debug!("base_t: {:?}", base_t);
- match base_t.kind() {
+ let mut autoderef = self.autoderef(expr.span, base_ty);
+ while let Some((deref_base_ty, _)) = autoderef.next() {
+ debug!("deref_base_ty: {:?}", deref_base_ty);
+ match deref_base_ty.kind() {
ty::Adt(base_def, substs) if !base_def.is_enum() => {
- debug!("struct named {:?}", base_t);
+ debug!("struct named {:?}", deref_base_ty);
let (ident, def_scope) =
self.tcx.adjust_ident_and_get_scope(field, base_def.did(), self.body_id);
let fields = &base_def.non_enum_variant().fields;
// (#90483) apply adjustments to avoid ExprUseVisitor from
// creating erroneous projection.
self.apply_adjustments(base, adjustments);
- self.ban_private_field_access(expr, expr_t, field, did);
+ self.ban_private_field_access(expr, base_ty, field, did);
return field_ty;
}
if field.name == kw::Empty {
- } else if self.method_exists(field, expr_t, expr.hir_id, true) {
- self.ban_take_value_of_method(expr, expr_t, field);
- } else if !expr_t.is_primitive_ty() {
- self.ban_nonexisting_field(field, base, expr, expr_t);
+ } else if self.method_exists(field, base_ty, expr.hir_id, true) {
+ self.ban_take_value_of_method(expr, base_ty, field);
+ } else if !base_ty.is_primitive_ty() {
+ self.ban_nonexisting_field(field, base, expr, base_ty);
} else {
let field_name = field.to_string();
let mut err = type_error_struct!(
self.tcx().sess,
field.span,
- expr_t,
+ base_ty,
E0610,
- "`{expr_t}` is a primitive type and therefore doesn't have fields",
+ "`{base_ty}` is a primitive type and therefore doesn't have fields",
);
let is_valid_suffix = |field: &str| {
if field == "f32" || field == "f64" {
None
}
};
- if let ty::Infer(ty::IntVar(_)) = expr_t.kind()
+ if let ty::Infer(ty::IntVar(_)) = base_ty.kind()
&& let ExprKind::Lit(Spanned {
node: ast::LitKind::Int(_, ast::LitIntType::Unsuffixed),
..
self.tcx().ty_error()
}
- fn check_call_constructor(
- &self,
- err: &mut Diagnostic,
- base: &'tcx hir::Expr<'tcx>,
- def_id: DefId,
- ) {
- if let Some(local_id) = def_id.as_local() {
- let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_id);
- let node = self.tcx.hir().get(hir_id);
-
- if let Some(fields) = node.tuple_fields() {
- let kind = match self.tcx.opt_def_kind(local_id) {
- Some(DefKind::Ctor(of, _)) => of,
- _ => return,
- };
-
- suggest_call_constructor(base.span, kind, fields.len(), err);
- }
- } else {
- // The logic here isn't smart but `associated_item_def_ids`
- // doesn't work nicely on local.
- if let DefKind::Ctor(of, _) = self.tcx.def_kind(def_id) {
- let parent_def_id = self.tcx.parent(def_id);
- let fields = self.tcx.associated_item_def_ids(parent_def_id);
- suggest_call_constructor(base.span, of, fields.len(), err);
- }
- }
- }
-
fn suggest_await_on_field_access(
&self,
err: &mut Diagnostic,
fn ban_nonexisting_field(
&self,
- field: Ident,
+ ident: Ident,
base: &'tcx hir::Expr<'tcx>,
expr: &'tcx hir::Expr<'tcx>,
- expr_t: Ty<'tcx>,
+ base_ty: Ty<'tcx>,
) {
debug!(
- "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}",
- field, base, expr, expr_t
+ "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, base_ty={:?}",
+ ident, base, expr, base_ty
);
- let mut err = self.no_such_field_err(field, expr_t, base.hir_id);
+ let mut err = self.no_such_field_err(ident, base_ty, base.hir_id);
- match *expr_t.peel_refs().kind() {
+ match *base_ty.peel_refs().kind() {
ty::Array(_, len) => {
- self.maybe_suggest_array_indexing(&mut err, expr, base, field, len);
+ self.maybe_suggest_array_indexing(&mut err, expr, base, ident, len);
}
ty::RawPtr(..) => {
- self.suggest_first_deref_field(&mut err, expr, base, field);
+ self.suggest_first_deref_field(&mut err, expr, base, ident);
}
ty::Adt(def, _) if !def.is_enum() => {
- self.suggest_fields_on_recordish(&mut err, def, field, expr.span);
+ self.suggest_fields_on_recordish(&mut err, def, ident, expr.span);
}
ty::Param(param_ty) => {
self.point_at_param_definition(&mut err, param_ty);
}
ty::Opaque(_, _) => {
- self.suggest_await_on_field_access(&mut err, field, base, expr_t.peel_refs());
- }
- ty::FnDef(def_id, _) => {
- self.check_call_constructor(&mut err, base, def_id);
+ self.suggest_await_on_field_access(&mut err, ident, base, base_ty.peel_refs());
}
_ => {}
}
- if field.name == kw::Await {
+ self.suggest_fn_call(&mut err, base, base_ty, |output_ty| {
+ if let ty::Adt(def, _) = output_ty.kind() && !def.is_enum() {
+ def.non_enum_variant().fields.iter().any(|field| {
+ field.ident(self.tcx) == ident
+ && field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx)
+ })
+ } else if let ty::Tuple(tys) = output_ty.kind()
+ && let Ok(idx) = ident.as_str().parse::<usize>()
+ {
+ idx < tys.len()
+ } else {
+ false
+ }
+ });
+
+ if ident.name == kw::Await {
// We know by construction that `<expr>.await` is either on Rust 2015
// or results in `ExprKind::Await`. Suggest switching the edition to 2018.
err.note("to `.await` a `Future`, switch to Rust 2018 or later");
if let Some((fields, substs)) =
self.get_field_candidates_considering_privacy(span, expr_t, mod_id)
{
- for candidate_field in fields {
- if let Some(mut field_path) = self.check_for_nested_field_satisfying(
- span,
- &|candidate_field, _| candidate_field.ident(self.tcx()) == field,
- candidate_field,
- substs,
- vec![],
- mod_id,
- ) {
- // field_path includes `field` that we're looking for, so pop it.
+ let candidate_fields: Vec<_> = fields
+ .filter_map(|candidate_field| {
+ self.check_for_nested_field_satisfying(
+ span,
+ &|candidate_field, _| candidate_field.ident(self.tcx()) == field,
+ candidate_field,
+ substs,
+ vec![],
+ mod_id,
+ )
+ })
+ .map(|mut field_path| {
field_path.pop();
-
- let field_path_str = field_path
+ field_path
.iter()
.map(|id| id.name.to_ident_string())
.collect::<Vec<String>>()
- .join(".");
- debug!("field_path_str: {:?}", field_path_str);
-
- err.span_suggestion_verbose(
- field.span.shrink_to_lo(),
- "one of the expressions' fields has a field of the same name",
- format!("{field_path_str}."),
- Applicability::MaybeIncorrect,
- );
- }
+ .join(".")
+ })
+ .collect::<Vec<_>>();
+
+ let len = candidate_fields.len();
+ if len > 0 {
+ err.span_suggestions(
+ field.span.shrink_to_lo(),
+ format!(
+ "{} of the expressions' fields {} a field of the same name",
+ if len > 1 { "some" } else { "one" },
+ if len > 1 { "have" } else { "has" },
+ ),
+ candidate_fields.iter().map(|path| format!("{path}.")),
+ Applicability::MaybeIncorrect,
+ );
}
}
err
self.resolve_vars_with_obligations_and_mutate_fulfillment(ty, |_| {})
}
- #[instrument(skip(self, mutate_fulfillment_errors), level = "debug")]
+ #[instrument(skip(self, mutate_fulfillment_errors), level = "debug", ret)]
pub(in super::super) fn resolve_vars_with_obligations_and_mutate_fulfillment(
&self,
mut ty: Ty<'tcx>,
// indirect dependencies that don't seem worth tracking
// precisely.
self.select_obligations_where_possible(false, mutate_fulfillment_errors);
- ty = self.resolve_vars_if_possible(ty);
-
- debug!(?ty);
- ty
+ self.resolve_vars_if_possible(ty)
}
pub(in super::super) fn record_deferred_call_resolution(
})
}
- #[tracing::instrument(level = "debug", skip(self, code, span, def_id, substs))]
+ #[instrument(level = "debug", skip(self, code, span, def_id, substs))]
fn add_required_obligations_with_code(
&self,
span: Span,
) {
let tcx = self.tcx;
- // Conceptually, we've got some number of expected inputs, and some number of provided aguments
+ // Conceptually, we've got some number of expected inputs, and some number of provided arguments
// and we can form a grid of whether each argument could satisfy a given input:
// in1 | in2 | in3 | ...
// arg1 ? | | |
use crate::astconv::AstConv;
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
+use hir::def_id::DefId;
use rustc_ast::util::parser::ExprPrecedence;
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
use rustc_hir as hir;
pointing_at_return_type
}
- /// When encountering an fn-like ctor that needs to unify with a value, check whether calling
- /// the ctor would successfully solve the type mismatch and if so, suggest it:
+ /// When encountering an fn-like type, try accessing the output of the type
+ /// // and suggesting calling it if it satisfies a predicate (i.e. if the
+ /// output has a method or a field):
/// ```compile_fail,E0308
/// fn foo(x: usize) -> usize { x }
/// let x: usize = foo; // suggest calling the `foo` function: `foo(42)`
/// ```
- fn suggest_fn_call(
+ pub(crate) fn suggest_fn_call(
&self,
err: &mut Diagnostic,
expr: &hir::Expr<'_>,
- expected: Ty<'tcx>,
found: Ty<'tcx>,
+ can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
) -> bool {
- let (def_id, output, inputs) = match *found.kind() {
- ty::FnDef(def_id, _) => {
- let fn_sig = found.fn_sig(self.tcx);
- (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len())
- }
- ty::Closure(def_id, substs) => {
- let fn_sig = substs.as_closure().sig();
- (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1)
- }
- ty::Opaque(def_id, substs) => {
- let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
- if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
- && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
- // args tuple will always be substs[1]
- && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
- {
- Some((
- pred.kind().rebind(proj.term.ty().unwrap()),
- args.len(),
- ))
- } else {
- None
- }
- });
- if let Some((output, inputs)) = sig {
- (def_id, output, inputs)
- } else {
- return false;
- }
- }
- _ => return false,
- };
-
- let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
- let output = self.normalize_associated_types_in(expr.span, output);
- if !output.is_ty_var() && self.can_coerce(output, expected) {
- let (sugg_call, mut applicability) = match inputs {
+ let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(expr, found)
+ else { return false; };
+ if can_satisfy(output) {
+ let (sugg_call, mut applicability) = match inputs.len() {
0 => ("".to_string(), Applicability::MachineApplicable),
1..=4 => (
- (0..inputs).map(|_| "_").collect::<Vec<_>>().join(", "),
- Applicability::MachineApplicable,
+ inputs
+ .iter()
+ .map(|ty| {
+ if ty.is_suggestable(self.tcx, false) {
+ format!("/* {ty} */")
+ } else {
+ "".to_string()
+ }
+ })
+ .collect::<Vec<_>>()
+ .join(", "),
+ Applicability::HasPlaceholders,
),
- _ => ("...".to_string(), Applicability::HasPlaceholders),
+ _ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
};
- let msg = match self.tcx.def_kind(def_id) {
- DefKind::Fn => "call this function",
- DefKind::Closure | DefKind::OpaqueTy => "call this closure",
- DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct",
- DefKind::Ctor(CtorOf::Variant, _) => "instantiate this tuple variant",
- _ => "call this function",
+ let msg = match def_id_or_name {
+ DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
+ DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(),
+ DefKind::Ctor(CtorOf::Variant, _) => {
+ "instantiate this tuple variant".to_string()
+ }
+ kind => format!("call this {}", kind.descr(def_id)),
+ },
+ DefIdOrName::Name(name) => format!("call this {name}"),
};
let sugg = match expr.kind {
false
}
+ fn extract_callable_info(
+ &self,
+ expr: &Expr<'_>,
+ found: Ty<'tcx>,
+ ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
+ // Autoderef is useful here because sometimes we box callables, etc.
+ let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| {
+ match *found.kind() {
+ ty::FnPtr(fn_sig) =>
+ Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())),
+ ty::FnDef(def_id, _) => {
+ let fn_sig = found.fn_sig(self.tcx);
+ Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
+ }
+ ty::Closure(def_id, substs) => {
+ let fn_sig = substs.as_closure().sig();
+ Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().map_bound(|inputs| &inputs[1..])))
+ }
+ ty::Opaque(def_id, substs) => {
+ self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
+ if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
+ && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
+ // args tuple will always be substs[1]
+ && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
+ {
+ Some((
+ DefIdOrName::DefId(def_id),
+ pred.kind().rebind(proj.term.ty().unwrap()),
+ pred.kind().rebind(args.as_slice()),
+ ))
+ } else {
+ None
+ }
+ })
+ }
+ ty::Dynamic(data, _) => {
+ data.iter().find_map(|pred| {
+ if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
+ && Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output()
+ // for existential projection, substs are shifted over by 1
+ && let ty::Tuple(args) = proj.substs.type_at(0).kind()
+ {
+ Some((
+ DefIdOrName::Name("trait object"),
+ pred.rebind(proj.term.ty().unwrap()),
+ pred.rebind(args.as_slice()),
+ ))
+ } else {
+ None
+ }
+ })
+ }
+ ty::Param(param) => {
+ let def_id = self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx).def_id;
+ self.tcx.predicates_of(self.body_id.owner).predicates.iter().find_map(|(pred, _)| {
+ if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
+ && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
+ && proj.projection_ty.self_ty() == found
+ // args tuple will always be substs[1]
+ && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
+ {
+ Some((
+ DefIdOrName::DefId(def_id),
+ pred.kind().rebind(proj.term.ty().unwrap()),
+ pred.kind().rebind(args.as_slice()),
+ ))
+ } else {
+ None
+ }
+ })
+ }
+ _ => None,
+ }
+ }) else { return None; };
+
+ let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
+ let inputs = inputs
+ .skip_binder()
+ .iter()
+ .map(|ty| {
+ self.replace_bound_vars_with_fresh_vars(
+ expr.span,
+ infer::FnCall,
+ inputs.rebind(*ty),
+ )
+ })
+ .collect();
+
+ // We don't want to register any extra obligations, which should be
+ // implied by wf, but also because that would possibly result in
+ // erroneous errors later on.
+ let infer::InferOk { value: output, obligations: _ } =
+ self.normalize_associated_types_in_as_infer_ok(expr.span, output);
+
+ if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
+ }
+
+ pub fn suggest_two_fn_call(
+ &self,
+ err: &mut Diagnostic,
+ lhs_expr: &'tcx hir::Expr<'tcx>,
+ lhs_ty: Ty<'tcx>,
+ rhs_expr: &'tcx hir::Expr<'tcx>,
+ rhs_ty: Ty<'tcx>,
+ can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool,
+ ) -> bool {
+ let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_expr, lhs_ty)
+ else { return false; };
+ let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty)
+ else { return false; };
+
+ if can_satisfy(lhs_output_ty, rhs_output_ty) {
+ let mut sugg = vec![];
+ let mut applicability = Applicability::MachineApplicable;
+
+ for (expr, inputs) in [(lhs_expr, lhs_inputs), (rhs_expr, rhs_inputs)] {
+ let (sugg_call, this_applicability) = match inputs.len() {
+ 0 => ("".to_string(), Applicability::MachineApplicable),
+ 1..=4 => (
+ inputs
+ .iter()
+ .map(|ty| {
+ if ty.is_suggestable(self.tcx, false) {
+ format!("/* {ty} */")
+ } else {
+ "/* value */".to_string()
+ }
+ })
+ .collect::<Vec<_>>()
+ .join(", "),
+ Applicability::HasPlaceholders,
+ ),
+ _ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
+ };
+
+ applicability = applicability.max(this_applicability);
+
+ match expr.kind {
+ hir::ExprKind::Call(..)
+ | hir::ExprKind::Path(..)
+ | hir::ExprKind::Index(..)
+ | hir::ExprKind::Lit(..) => {
+ sugg.extend([(expr.span.shrink_to_hi(), format!("({sugg_call})"))]);
+ }
+ hir::ExprKind::Closure { .. } => {
+ // Might be `{ expr } || { bool }`
+ applicability = Applicability::MaybeIncorrect;
+ sugg.extend([
+ (expr.span.shrink_to_lo(), "(".to_string()),
+ (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
+ ]);
+ }
+ _ => {
+ sugg.extend([
+ (expr.span.shrink_to_lo(), "(".to_string()),
+ (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
+ ]);
+ }
+ }
+ }
+
+ err.multipart_suggestion_verbose(
+ format!("use parentheses to call these"),
+ sugg,
+ applicability,
+ );
+
+ true
+ } else {
+ false
+ }
+ }
+
pub fn suggest_deref_ref_or_into(
&self,
err: &mut Diagnostic,
} else {
err.span_suggestion(sp, &msg, suggestion, applicability);
}
- } else if let (ty::FnDef(def_id, ..), true) =
- (&found.kind(), self.suggest_fn_call(err, expr, expected, found))
+ } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
+ && let ty::FnDef(def_id, ..) = &found.kind()
+ && let Some(sp) = self.tcx.hir().span_if_local(*def_id)
{
- if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
- err.span_label(sp, format!("{found} defined here"));
- }
+ err.span_label(sp, format!("{found} defined here"));
} else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
if !methods.is_empty() {
self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found));
// Only suggest changing the return type for methods that
// haven't set a return type at all (and aren't `fn main()` or an impl).
- match (
- &fn_decl.output,
- found.is_suggestable(self.tcx, false),
- can_suggest,
- expected.is_unit(),
- ) {
- (&hir::FnRetTy::DefaultReturn(span), true, true, true) => {
- err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found });
- true
- }
- (&hir::FnRetTy::DefaultReturn(span), false, true, true) => {
- // FIXME: if `found` could be `impl Iterator` or `impl Fn*`, we should suggest
- // that.
- err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span });
- true
- }
- (&hir::FnRetTy::DefaultReturn(span), _, false, true) => {
+ match &fn_decl.output {
+ &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() && !can_suggest => {
// `fn main()` must return `()`, do not suggest changing return type
err.subdiagnostic(ExpectedReturnTypeLabel::Unit { span });
- true
+ return true;
+ }
+ &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
+ if found.is_suggestable(self.tcx, false) {
+ err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() });
+ return true;
+ } else if let ty::Closure(_, substs) = found.kind()
+ // FIXME(compiler-errors): Get better at printing binders...
+ && let closure = substs.as_closure()
+ && closure.sig().is_suggestable(self.tcx, false)
+ {
+ err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: closure.print_as_impl_trait().to_string() });
+ return true;
+ } else {
+ // FIXME: if `found` could be `impl Iterator` we should suggest that.
+ err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span });
+ return true
+ }
}
- // expectation was caused by something else, not the default return
- (&hir::FnRetTy::DefaultReturn(_), _, _, false) => false,
- (&hir::FnRetTy::Return(ref ty), _, _, _) => {
+ &hir::FnRetTy::Return(ref ty) => {
// Only point to return type if the expected type is the return type, as if they
// are not, the expectation must have been caused by something else.
debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind);
self.try_suggest_return_impl_trait(err, expected, ty, fn_id);
return true;
}
- false
}
+ _ => {}
}
+ false
}
/// check whether the return type is a generic type with a trait bound
}
}
}
+
+enum DefIdOrName {
+ DefId(DefId),
+ Name(&'static str),
+}
use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt, TypeVisitable};
use rustc_span::symbol::sym;
use rustc_span::Span;
-use tracing::debug;
mod drop_ranges;
use super::callee::DeferredCallResolution;
use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::sync::Lrc;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::HirIdMap;
use rustc_span::def_id::LocalDefIdMap;
use rustc_span::{self, Span};
use rustc_trait_selection::infer::InferCtxtExt as _;
-use rustc_trait_selection::traits::{self, ObligationCause, TraitEngine, TraitEngineExt};
+use rustc_trait_selection::traits::{
+ self, ObligationCause, ObligationCtxt, TraitEngine, TraitEngineExt as _,
+};
use std::cell::RefCell;
use std::ops::Deref;
infcx: tcx
.infer_ctxt()
.ignoring_regions()
- .with_fresh_in_progress_typeck_results(hir_owner),
+ .with_fresh_in_progress_typeck_results(hir_owner)
+ .with_normalize_fn_sig_for_diagnostic(Lrc::new(move |infcx, fn_sig| {
+ if fn_sig.has_escaping_bound_vars() {
+ return fn_sig;
+ }
+ infcx.probe(|_| {
+ let ocx = ObligationCtxt::new_in_snapshot(infcx);
+ let normalized_fn_sig = ocx.normalize(
+ ObligationCause::dummy(),
+ // FIXME(compiler-errors): This is probably not the right param-env...
+ infcx.tcx.param_env(def_id),
+ fn_sig,
+ );
+ if ocx.select_all_or_error().is_empty() {
+ let normalized_fn_sig =
+ infcx.resolve_vars_if_possible(normalized_fn_sig);
+ if !normalized_fn_sig.needs_infer() {
+ return normalized_fn_sig;
+ }
+ }
+ fn_sig
+ })
+ })),
def_id,
}
}
/// * `call_expr`: the complete method call: (`foo.bar::<T1,...Tn>(...)`)
/// * `self_expr`: the self expression (`foo`)
/// * `args`: the expressions of the arguments (`a, b + 1, ...`)
- #[instrument(level = "debug", skip(self, call_expr, self_expr))]
+ #[instrument(level = "debug", skip(self))]
pub fn lookup_method(
&self,
self_ty: Ty<'tcx>,
self_expr: &'tcx hir::Expr<'tcx>,
args: &'tcx [hir::Expr<'tcx>],
) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
- debug!(
- "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
- segment.ident, self_ty, call_expr, self_expr
- );
-
let pick =
self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?;
/// In particular, it doesn't really do any probing: it simply constructs
/// an obligation for a particular trait with the given self type and checks
/// whether that trait is implemented.
- #[instrument(level = "debug", skip(self, span, opt_input_types))]
+ #[instrument(level = "debug", skip(self, span))]
pub(super) fn lookup_method_in_trait(
&self,
span: Span,
self_ty: Ty<'tcx>,
opt_input_types: Option<&[Ty<'tcx>]>,
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
- debug!(
- "lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?}, opt_input_types={:?})",
- self_ty, m_name, trait_def_id, opt_input_types
- );
-
let (obligation, substs) =
self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types);
self.construct_obligation_for_trait(
/// * `self_ty`: the type to search within (`Foo`)
/// * `self_ty_span` the span for the type being searched within (span of `Foo`)
/// * `expr_id`: the [`hir::HirId`] of the expression composing the entire call
- #[instrument(level = "debug", skip(self))]
+ #[instrument(level = "debug", skip(self), ret)]
pub fn resolve_fully_qualified_call(
&self,
span: Span,
self_ty_span: Span,
expr_id: hir::HirId,
) -> Result<(DefKind, DefId), MethodError<'tcx>> {
- debug!(
- "resolve_fully_qualified_call: method_name={:?} self_ty={:?} expr_id={:?}",
- method_name, self_ty, expr_id,
- );
-
let tcx = self.tcx;
// Check if we have an enum variant.
&pick,
);
- debug!("resolve_fully_qualified_call: pick={:?}", pick);
+ debug!(?pick);
{
let mut typeck_results = self.typeck_results.borrow_mut();
let used_trait_imports = Lrc::get_mut(&mut typeck_results.used_trait_imports).unwrap();
for import_id in pick.import_ids {
- debug!("resolve_fully_qualified_call: used_trait_import: {:?}", import_id);
+ debug!(used_trait_import=?import_id);
used_trait_imports.insert(import_id);
}
}
let def_kind = pick.item.kind.as_def_kind();
- debug!(
- "resolve_fully_qualified_call: def_kind={:?}, def_id={:?}",
- def_kind, pick.item.def_id
- );
tcx.check_stability(pick.item.def_id, Some(expr_id), span, Some(method_name.span));
Ok((def_kind, pick.item.def_id))
}
/// would result in an error (basically, the same criteria we
/// would use to decide if a method is a plausible fit for
/// ambiguity purposes).
- #[instrument(level = "debug", skip(self, scope_expr_id))]
+ #[instrument(level = "debug", skip(self))]
pub fn probe_for_return_type(
&self,
span: Span,
self_ty: Ty<'tcx>,
scope_expr_id: hir::HirId,
) -> Vec<ty::AssocItem> {
- debug!(
- "probe(self_ty={:?}, return_type={}, scope_expr_id={})",
- self_ty, return_type, scope_expr_id
- );
let method_names = self
.probe_op(
span,
.collect()
}
- #[instrument(level = "debug", skip(self, scope_expr_id))]
+ #[instrument(level = "debug", skip(self))]
pub fn probe_for_name(
&self,
span: Span,
scope_expr_id: hir::HirId,
scope: ProbeScope,
) -> PickResult<'tcx> {
- debug!(
- "probe(self_ty={:?}, item_name={}, scope_expr_id={})",
- self_ty, item_name, scope_expr_id
- );
self.probe_op(
span,
mode,
use std::iter;
use super::probe::{Mode, ProbeScope};
-use super::{super::suggest_call_constructor, CandidateSource, MethodError, NoMatchData};
+use super::{CandidateSource, MethodError, NoMatchData};
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
);
}
- if self.is_fn_ty(rcvr_ty, span) {
- if let SelfSource::MethodCall(expr) = source {
- let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() {
- if let Some(local_id) = def_id.as_local() {
- let hir_id = tcx.hir().local_def_id_to_hir_id(local_id);
- let node = tcx.hir().get(hir_id);
- let fields = node.tuple_fields();
- if let Some(fields) = fields
- && let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) {
- Some((fields.len(), of))
- } else {
- None
- }
- } else {
- // The logic here isn't smart but `associated_item_def_ids`
- // doesn't work nicely on local.
- if let DefKind::Ctor(of, _) = tcx.def_kind(def_id) {
- let parent_def_id = tcx.parent(*def_id);
- Some((tcx.associated_item_def_ids(parent_def_id).len(), of))
- } else {
- None
- }
- }
- } else {
- None
- };
-
- // If the function is a tuple constructor, we recommend that they call it
- if let Some((fields, kind)) = suggest {
- suggest_call_constructor(expr.span, kind, fields, &mut err);
- } else {
- // General case
- err.span_label(
- expr.span,
- "this is a function, perhaps you wish to call it",
- );
- }
- }
+ if let SelfSource::MethodCall(rcvr_expr) = source {
+ self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| {
+ let call_expr = self
+ .tcx
+ .hir()
+ .expect_expr(self.tcx.hir().get_parent_node(rcvr_expr.hir_id));
+ let probe = self.lookup_probe(
+ span,
+ item_name,
+ output_ty,
+ call_expr,
+ ProbeScope::AllTraits,
+ );
+ probe.is_ok()
+ });
}
let mut custom_span_label = false;
item_name: Ident,
) {
if let SelfSource::MethodCall(expr) = source
- && let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id()
- && let Some((fields, substs)) = self.get_field_candidates_considering_privacy(span, actual, mod_id)
+ && let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id()
+ && let Some((fields, substs)) =
+ self.get_field_candidates_considering_privacy(span, actual, mod_id)
{
let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
- for candidate_field in fields {
- if let Some(field_path) = self.check_for_nested_field_satisfying(
- span,
- &|_, field_ty| {
- self.lookup_probe(
- span,
- item_name,
- field_ty,
- call_expr,
- ProbeScope::AllTraits,
- )
- .is_ok()
- },
- candidate_field,
- substs,
- vec![],
- mod_id,
- ) {
- let field_path_str = field_path
+
+ let lang_items = self.tcx.lang_items();
+ let never_mention_traits = [
+ lang_items.clone_trait(),
+ lang_items.deref_trait(),
+ lang_items.deref_mut_trait(),
+ self.tcx.get_diagnostic_item(sym::AsRef),
+ self.tcx.get_diagnostic_item(sym::AsMut),
+ self.tcx.get_diagnostic_item(sym::Borrow),
+ self.tcx.get_diagnostic_item(sym::BorrowMut),
+ ];
+ let candidate_fields: Vec<_> = fields
+ .filter_map(|candidate_field| {
+ self.check_for_nested_field_satisfying(
+ span,
+ &|_, field_ty| {
+ self.lookup_probe(
+ span,
+ item_name,
+ field_ty,
+ call_expr,
+ ProbeScope::TraitsInScope,
+ )
+ .map_or(false, |pick| {
+ !never_mention_traits
+ .iter()
+ .flatten()
+ .any(|def_id| self.tcx.parent(pick.item.def_id) == *def_id)
+ })
+ },
+ candidate_field,
+ substs,
+ vec![],
+ mod_id,
+ )
+ })
+ .map(|field_path| {
+ field_path
.iter()
.map(|id| id.name.to_ident_string())
.collect::<Vec<String>>()
- .join(".");
- debug!("field_path_str: {:?}", field_path_str);
-
- err.span_suggestion_verbose(
- item_name.span.shrink_to_lo(),
- "one of the expressions' fields has a method of the same name",
- format!("{field_path_str}."),
- Applicability::MaybeIncorrect,
- );
- }
+ .join(".")
+ })
+ .collect();
+
+ let len = candidate_fields.len();
+ if len > 0 {
+ err.span_suggestions(
+ item_name.span.shrink_to_lo(),
+ format!(
+ "{} of the expressions' fields {} a method of the same name",
+ if len > 1 { "some" } else { "one" },
+ if len > 1 { "have" } else { "has" },
+ ),
+ candidate_fields.iter().map(|path| format!("{path}.")),
+ Applicability::MaybeIncorrect,
+ );
}
}
}
pub use diverges::Diverges;
pub use expectation::Expectation;
pub use fn_ctxt::*;
-use hir::def::CtorOf;
pub use inherited::{Inherited, InheritedBuilder};
use crate::astconv::AstConv;
typeck_with_fallback(tcx, def_id, fallback)
}
-#[instrument(skip(tcx, fallback))]
fn typeck_with_fallback<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
// For the wasm32 target statics with `#[link_section]` are placed into custom
// sections of the final output file, but this isn't link custom sections of
// other executable formats. Namely we can only embed a list of bytes,
- // nothing with pointers to anything else or relocations. If any relocation
- // show up, reject them here.
+ // nothing with provenance (pointers to anything else). If any provenance
+ // show up, reject it here.
// `#[link_section]` may contain arbitrary, or even undefined bytes, but it is
// the consumer's responsibility to ensure all bytes that have been read
// have defined values.
if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id())
- && alloc.inner().relocations().len() != 0
+ && alloc.inner().provenance().len() != 0
{
let msg = "statics with a custom `#[link_section]` must be a \
simple list of bytes on the wasm target with no \
generics.count() == expected + if generics.has_self { 1 } else { 0 }
})
}
-
-/// Suggests calling the constructor of a tuple struct or enum variant
-///
-/// * `snippet` - The snippet of code that references the constructor
-/// * `span` - The span of the snippet
-/// * `params` - The number of parameters the constructor accepts
-/// * `err` - A mutable diagnostic builder to add the suggestion to
-fn suggest_call_constructor(span: Span, kind: CtorOf, params: usize, err: &mut Diagnostic) {
- // Note: tuple-structs don't have named fields, so just use placeholders
- let args = vec!["_"; params].join(", ");
- let applicable = if params > 0 {
- Applicability::HasPlaceholders
- } else {
- // When n = 0, it's an empty-tuple struct/enum variant
- // so we trivially know how to construct it
- Applicability::MachineApplicable
- };
- let kind = match kind {
- CtorOf::Struct => "a struct",
- CtorOf::Variant => "an enum variant",
- };
- err.span_label(span, &format!("this is the constructor of {kind}"));
- err.multipart_suggestion(
- "call the constructor",
- vec![(span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), format!(")({args})"))],
- applicable,
- );
-}
};
let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{message}");
if !lhs_expr.span.eq(&rhs_expr.span) {
- self.add_type_neq_err_label(
- &mut err,
- lhs_expr.span,
- lhs_ty,
- rhs_ty,
- rhs_expr,
- op,
- is_assign,
- expected,
- );
- self.add_type_neq_err_label(
- &mut err,
- rhs_expr.span,
- rhs_ty,
- lhs_ty,
- lhs_expr,
- op,
- is_assign,
- expected,
- );
+ err.span_label(lhs_expr.span, lhs_ty.to_string());
+ err.span_label(rhs_expr.span, rhs_ty.to_string());
}
self.note_unmet_impls_on_type(&mut err, errors);
(err, missing_trait, use_output)
}
};
+ let is_compatible = |lhs_ty, rhs_ty| {
+ self.lookup_op_method(
+ lhs_ty,
+ Some(rhs_ty),
+ Some(rhs_expr),
+ Op::Binary(op, is_assign),
+ expected,
+ )
+ .is_ok()
+ };
+
// We should suggest `a + b` => `*a + b` if `a` is copy, and suggest
// `a += b` => `*a += b` if a is a mut ref.
- if is_assign == IsAssign::Yes
- && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
- suggest_deref_binop(lhs_deref_ty);
+ if !op.span.can_be_used_for_suggestions() {
+ // Suppress suggestions when lhs and rhs are not in the same span as the error
+ } else if is_assign == IsAssign::Yes
+ && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty)
+ {
+ suggest_deref_binop(lhs_deref_ty);
} else if is_assign == IsAssign::No
- && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind() {
- if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty, lhs_expr.span) {
+ && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind()
+ {
+ if self.type_is_copy_modulo_regions(
+ self.param_env,
+ *lhs_deref_ty,
+ lhs_expr.span,
+ ) {
suggest_deref_binop(*lhs_deref_ty);
}
+ } else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| {
+ is_compatible(lhs_ty, rhs_ty)
+ }) || self.suggest_fn_call(&mut err, rhs_expr, rhs_ty, |rhs_ty| {
+ is_compatible(lhs_ty, rhs_ty)
+ }) || self.suggest_two_fn_call(
+ &mut err,
+ rhs_expr,
+ rhs_ty,
+ lhs_expr,
+ lhs_ty,
+ |lhs_ty, rhs_ty| is_compatible(lhs_ty, rhs_ty),
+ ) {
+ // Cool
}
+
if let Some(missing_trait) = missing_trait {
let mut visitor = TypeParamVisitor(vec![]);
visitor.visit_ty(lhs_ty);
(lhs_ty, rhs_ty, return_ty)
}
- /// If one of the types is an uncalled function and calling it would yield the other type,
- /// suggest calling the function. Returns `true` if suggestion would apply (even if not given).
- fn add_type_neq_err_label(
- &self,
- err: &mut Diagnostic,
- span: Span,
- ty: Ty<'tcx>,
- other_ty: Ty<'tcx>,
- other_expr: &'tcx hir::Expr<'tcx>,
- op: hir::BinOp,
- is_assign: IsAssign,
- expected: Expectation<'tcx>,
- ) -> bool /* did we suggest to call a function because of missing parentheses? */ {
- err.span_label(span, ty.to_string());
- if let FnDef(def_id, _) = *ty.kind() {
- if !self.tcx.has_typeck_results(def_id) {
- return false;
- }
- // FIXME: Instead of exiting early when encountering bound vars in
- // the function signature, consider keeping the binder here and
- // propagating it downwards.
- let Some(fn_sig) = self.tcx.fn_sig(def_id).no_bound_vars() else {
- return false;
- };
-
- let other_ty = if let FnDef(def_id, _) = *other_ty.kind() {
- if !self.tcx.has_typeck_results(def_id) {
- return false;
- }
- // We're emitting a suggestion, so we can just ignore regions
- self.tcx.fn_sig(def_id).skip_binder().output()
- } else {
- other_ty
- };
-
- if self
- .lookup_op_method(
- fn_sig.output(),
- Some(other_ty),
- Some(other_expr),
- Op::Binary(op, is_assign),
- expected,
- )
- .is_ok()
- {
- let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() {
- ("( /* arguments */ )", Applicability::HasPlaceholders)
- } else {
- ("()", Applicability::MaybeIncorrect)
- };
-
- err.span_suggestion_verbose(
- span.shrink_to_hi(),
- "you might have forgotten to call this function",
- variable_snippet,
- applicability,
- );
- return true;
- }
- }
- false
- }
-
/// Provide actionable suggestions when trying to add two strings with incorrect types,
/// like `&str + &str`, `String + String` and `&str + &String`.
///
}
}
-#[tracing::instrument(level = "debug", skip(tcx, span, sig_if_method))]
+#[instrument(level = "debug", skip(tcx, span, sig_if_method))]
fn check_associated_item(
tcx: TyCtxt<'_>,
item_id: LocalDefId,
});
}
-#[tracing::instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))]
+#[instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))]
fn check_impl<'tcx>(
tcx: TyCtxt<'tcx>,
item: &'tcx hir::Item<'tcx>,
}
None => {
let self_ty = tcx.type_of(item.def_id);
- let self_ty = wfcx.normalize(item.span, None, self_ty);
+ let self_ty = wfcx.normalize(
+ item.span,
+ Some(WellFormedLoc::Ty(item.hir_id().expect_owner())),
+ self_ty,
+ );
wfcx.register_wf_obligation(
ast_self_ty.span,
Some(WellFormedLoc::Ty(item.hir_id().expect_owner())),
// 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() {
- wfcx.register_wf_obligation(tcx.def_span(param.def_id), None, ty.into());
+ wfcx.register_wf_obligation(
+ tcx.def_span(param.def_id),
+ Some(WellFormedLoc::Ty(param.def_id.expect_local())),
+ ty.into(),
+ );
}
}
}
wfcx.register_obligations(obligations);
}
-#[tracing::instrument(level = "debug", skip(wfcx, span, hir_decl))]
+#[instrument(level = "debug", skip(wfcx, span, hir_decl))]
fn check_fn_or_method<'tcx>(
wfcx: &WfCheckingCtxt<'_, 'tcx>,
span: Span,
);
}
- wfcx.register_wf_obligation(hir_decl.output.span(), None, sig.output().into());
+ wfcx.register_wf_obligation(
+ hir_decl.output.span(),
+ Some(WellFormedLoc::Param {
+ function: def_id,
+ param_idx: sig.inputs().len().try_into().unwrap(),
+ }),
+ sig.output().into(),
+ );
check_where_clauses(wfcx, span, def_id);
}
`self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one \
of the previous types except `Self`)";
-#[tracing::instrument(level = "debug", skip(wfcx))]
+#[instrument(level = "debug", skip(wfcx))]
fn check_method_receiver<'tcx>(
wfcx: &WfCheckingCtxt<'_, 'tcx>,
fn_sig: &hir::FnSig<'_>,
if !errors_buffer.is_empty() {
errors_buffer.sort_by_key(|diag| diag.span.primary_span());
- for mut diag in errors_buffer.drain(..) {
+ for mut diag in errors_buffer {
self.tcx().sess.diagnostic().emit_diagnostic(&mut diag);
}
}
let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, Some(span));
let unsize_trait = tcx.lang_items().require(LangItem::Unsize).unwrap_or_else(|err| {
- tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err));
+ tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err.to_string()));
});
let source = tcx.type_of(impl_did);
pure_wrt_drop: false,
kind: ty::GenericParamDefKind::Type {
has_default: false,
- object_lifetime_default: rl::Set1::Empty,
synthetic: false,
},
});
kind: ty::GenericParamDefKind::Lifetime,
}));
- let object_lifetime_defaults = tcx.object_lifetime_defaults(hir_id.owner);
-
// Now create the real type and const parameters.
let type_start = own_start - has_self as u32 + params.len() as u32;
let mut i = 0;
}
}
- let kind = ty::GenericParamDefKind::Type {
- has_default: default.is_some(),
- object_lifetime_default: object_lifetime_defaults
- .as_ref()
- .map_or(rl::Set1::Empty, |o| o[i]),
- synthetic,
- };
+ let kind = ty::GenericParamDefKind::Type { has_default: default.is_some(), synthetic };
let param_def = ty::GenericParamDef {
index: type_start + i as u32,
name: Symbol::intern(arg),
def_id,
pure_wrt_drop: false,
- kind: ty::GenericParamDefKind::Type {
- has_default: false,
- object_lifetime_default: rl::Set1::Empty,
- synthetic: false,
- },
+ kind: ty::GenericParamDefKind::Type { has_default: false, synthetic: false },
}));
}
name: Symbol::intern("<const_ty>"),
def_id,
pure_wrt_drop: false,
- kind: ty::GenericParamDefKind::Type {
- has_default: false,
- object_lifetime_default: rl::Set1::Empty,
- synthetic: false,
- },
+ kind: ty::GenericParamDefKind::Type { has_default: false, synthetic: false },
});
}
}
/// Computes the relevant generic parameter for a potential generic const argument.
///
/// This should be called using the query `tcx.opt_const_param_of`.
-#[instrument(level = "debug", skip(tcx))]
pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DefId> {
use hir::*;
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
}
#[derive(SessionSubdiagnostic)]
-pub enum AddReturnTypeSuggestion<'tcx> {
+pub enum AddReturnTypeSuggestion {
#[suggestion(
typeck::add_return_type_add,
code = "-> {found} ",
Add {
#[primary_span]
span: Span,
- found: Ty<'tcx>,
+ found: String,
},
#[suggestion(
typeck::add_return_type_missing_here,
let expr_place = return_if_err!(self.mc.cat_expr(expr));
f(self);
if let Some(els) = els {
- // borrowing because we need to test the descriminant
+ // borrowing because we need to test the discriminant
self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter());
self.walk_block(els)
}
hir::Node::ForeignItem(ForeignItem {
kind: ForeignItemKind::Static(ty, _), ..
}) => Some(*ty),
+ hir::Node::GenericParam(hir::GenericParam {
+ kind: hir::GenericParamKind::Type { default: Some(ty), .. },
+ ..
+ }) => Some(*ty),
ref node => bug!("Unexpected node {:?}", node),
},
WellFormedLoc::Param { function: _, param_idx } => {
#![feature(is_sorted)]
#![feature(iter_intersperse)]
#![cfg_attr(bootstrap, feature(label_break_value))]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(min_specialization)]
#![feature(never_type)]
if self.not_enough_args_provided() {
self.suggest_adding_args(err);
} else if self.too_many_args_provided() {
+ self.suggest_moving_args_from_assoc_fn_to_trait(err);
self.suggest_removing_args_or_generics(err);
} else {
unreachable!();
}
}
+ /// Suggests moving redundant argument(s) of an associate function to the
+ /// trait it belongs to.
+ ///
+ /// ```compile_fail
+ /// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)`
+ /// ```
+ fn suggest_moving_args_from_assoc_fn_to_trait(&self, err: &mut Diagnostic) {
+ let trait_ = match self.tcx.trait_of_item(self.def_id) {
+ Some(def_id) => def_id,
+ None => return,
+ };
+
+ // Skip suggestion when the associated function is itself generic, it is unclear
+ // how to split the provided parameters between those to suggest to the trait and
+ // those to remain on the associated type.
+ let num_assoc_fn_expected_args =
+ self.num_expected_type_or_const_args() + self.num_expected_lifetime_args();
+ if num_assoc_fn_expected_args > 0 {
+ return;
+ }
+
+ let num_assoc_fn_excess_args =
+ self.num_excess_type_or_const_args() + self.num_excess_lifetime_args();
+
+ let trait_generics = self.tcx.generics_of(trait_);
+ let num_trait_generics_except_self =
+ trait_generics.count() - if trait_generics.has_self { 1 } else { 0 };
+
+ let msg = format!(
+ "consider moving {these} generic argument{s} to the `{name}` trait, which takes up to {num} argument{s}",
+ these = pluralize!("this", num_assoc_fn_excess_args),
+ s = pluralize!(num_assoc_fn_excess_args),
+ name = self.tcx.item_name(trait_),
+ num = num_trait_generics_except_self,
+ );
+
+ if let Some(hir_id) = self.path_segment.hir_id
+ && let Some(parent_node) = self.tcx.hir().find_parent_node(hir_id)
+ && let Some(parent_node) = self.tcx.hir().find(parent_node)
+ && let hir::Node::Expr(expr) = parent_node {
+ match expr.kind {
+ hir::ExprKind::Path(ref qpath) => {
+ self.suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
+ err,
+ qpath,
+ msg,
+ num_assoc_fn_excess_args,
+ num_trait_generics_except_self
+ )
+ },
+ hir::ExprKind::MethodCall(..) => {
+ self.suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
+ err,
+ trait_,
+ expr,
+ msg,
+ num_assoc_fn_excess_args,
+ num_trait_generics_except_self
+ )
+ },
+ _ => return,
+ }
+ }
+ }
+
+ fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
+ &self,
+ err: &mut Diagnostic,
+ qpath: &'tcx hir::QPath<'tcx>,
+ msg: String,
+ num_assoc_fn_excess_args: usize,
+ num_trait_generics_except_self: usize,
+ ) {
+ if let hir::QPath::Resolved(_, path) = qpath
+ && let Some(trait_path_segment) = path.segments.get(0) {
+ let num_generic_args_supplied_to_trait = trait_path_segment.args().num_generic_params();
+
+ if num_assoc_fn_excess_args == num_trait_generics_except_self - num_generic_args_supplied_to_trait {
+ if let Some(span) = self.gen_args.span_ext()
+ && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+ let sugg = vec![
+ (self.path_segment.ident.span, format!("{}::{}", snippet, self.path_segment.ident)),
+ (span.with_lo(self.path_segment.ident.span.hi()), "".to_owned())
+ ];
+
+ err.multipart_suggestion(
+ msg,
+ sugg,
+ Applicability::MaybeIncorrect
+ );
+ }
+ }
+ }
+ }
+
+ fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
+ &self,
+ err: &mut Diagnostic,
+ trait_: DefId,
+ expr: &'tcx hir::Expr<'tcx>,
+ msg: String,
+ num_assoc_fn_excess_args: usize,
+ num_trait_generics_except_self: usize,
+ ) {
+ if let hir::ExprKind::MethodCall(_, args, _) = expr.kind {
+ assert_eq!(args.len(), 1);
+ if num_assoc_fn_excess_args == num_trait_generics_except_self {
+ if let Some(gen_args) = self.gen_args.span_ext()
+ && let Ok(gen_args) = self.tcx.sess.source_map().span_to_snippet(gen_args)
+ && let Ok(args) = self.tcx.sess.source_map().span_to_snippet(args[0].span) {
+ let sugg = format!("{}::{}::{}({})", self.tcx.item_name(trait_), gen_args, self.tcx.item_name(self.def_id), args);
+ err.span_suggestion(expr.span, msg, sugg, Applicability::MaybeIncorrect);
+ }
+ }
+ }
+ }
+
/// Suggests to remove redundant argument(s):
///
/// ```text
# target.
#llvm-config = <none> (path)
+# Override detection of whether this is a Rust-patched LLVM. This would be used
+# in conjunction with either an llvm-config or build.submodules = false.
+#llvm-has-rust-patches = if llvm-config { false } else { true }
+
# Normally the build system can find LLVM's FileCheck utility, but if
# not, you can specify an explicit file name for it.
#llvm-filecheck = "/path/to/llvm-version/bin/FileCheck"
/// that the cursor points to is unchanged, even if it is the "ghost" node.
///
/// This operation should compute in *O*(1) time.
- // `push_front` continues to point to "ghost" when it addes a node to mimic
+ // `push_front` continues to point to "ghost" when it adds a node to mimic
// the behavior of `insert_before` on an empty list.
#[unstable(feature = "linked_list_cursors", issue = "58533")]
pub fn push_front(&mut self, elt: T) {
use crate::alloc::{Allocator, Global};
use core::fmt;
use core::iter::{FusedIterator, TrustedLen};
-use core::mem;
+use core::mem::{self, ManuallyDrop};
use core::ptr::{self, NonNull};
use core::slice::{self};
pub fn allocator(&self) -> &A {
unsafe { self.vec.as_ref().allocator() }
}
+
+ /// Keep unyielded elements in the source `Vec`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(drain_keep_rest)]
+ ///
+ /// let mut vec = vec!['a', 'b', 'c'];
+ /// let mut drain = vec.drain(..);
+ ///
+ /// assert_eq!(drain.next().unwrap(), 'a');
+ ///
+ /// // This call keeps 'b' and 'c' in the vec.
+ /// drain.keep_rest();
+ ///
+ /// // If we wouldn't call `keep_rest()`,
+ /// // `vec` would be empty.
+ /// assert_eq!(vec, ['b', 'c']);
+ /// ```
+ #[unstable(feature = "drain_keep_rest", issue = "101122")]
+ pub fn keep_rest(self) {
+ // At this moment layout looks like this:
+ //
+ // [head] [yielded by next] [unyielded] [yielded by next_back] [tail]
+ // ^-- start \_________/-- unyielded_len \____/-- self.tail_len
+ // ^-- unyielded_ptr ^-- tail
+ //
+ // Normally `Drop` impl would drop [unyielded] and then move [tail] to the `start`.
+ // Here we want to
+ // 1. Move [unyielded] to `start`
+ // 2. Move [tail] to a new start at `start + len(unyielded)`
+ // 3. Update length of the original vec to `len(head) + len(unyielded) + len(tail)`
+ // a. In case of ZST, this is the only thing we want to do
+ // 4. Do *not* drop self, as everything is put in a consistent state already, there is nothing to do
+ let mut this = ManuallyDrop::new(self);
+
+ unsafe {
+ let source_vec = this.vec.as_mut();
+
+ let start = source_vec.len();
+ let tail = this.tail_start;
+
+ let unyielded_len = this.iter.len();
+ let unyielded_ptr = this.iter.as_slice().as_ptr();
+
+ // ZSTs have no identity, so we don't need to move them around.
+ let needs_move = mem::size_of::<T>() != 0;
+
+ if needs_move {
+ let start_ptr = source_vec.as_mut_ptr().add(start);
+
+ // memmove back unyielded elements
+ if unyielded_ptr != start_ptr {
+ let src = unyielded_ptr;
+ let dst = start_ptr;
+
+ ptr::copy(src, dst, unyielded_len);
+ }
+
+ // memmove back untouched tail
+ if tail != (start + unyielded_len) {
+ let src = source_vec.as_ptr().add(tail);
+ let dst = start_ptr.add(unyielded_len);
+ ptr::copy(src, dst, this.tail_len);
+ }
+ }
+
+ source_vec.set_len(start + unyielded_len + this.tail_len);
+ }
+ }
}
#[stable(feature = "vec_drain_as_slice", since = "1.46.0")]
use crate::alloc::{Allocator, Global};
-use core::ptr::{self};
-use core::slice::{self};
+use core::mem::{self, ManuallyDrop};
+use core::ptr;
+use core::slice;
use super::Vec;
pub fn allocator(&self) -> &A {
self.vec.allocator()
}
+
+ /// Keep unyielded elements in the source `Vec`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(drain_filter)]
+ /// #![feature(drain_keep_rest)]
+ ///
+ /// let mut vec = vec!['a', 'b', 'c'];
+ /// let mut drain = vec.drain_filter(|_| true);
+ ///
+ /// assert_eq!(drain.next().unwrap(), 'a');
+ ///
+ /// // This call keeps 'b' and 'c' in the vec.
+ /// drain.keep_rest();
+ ///
+ /// // If we wouldn't call `keep_rest()`,
+ /// // `vec` would be empty.
+ /// assert_eq!(vec, ['b', 'c']);
+ /// ```
+ #[unstable(feature = "drain_keep_rest", issue = "101122")]
+ pub fn keep_rest(self) {
+ // At this moment layout looks like this:
+ //
+ // _____________________/-- old_len
+ // / \
+ // [kept] [yielded] [tail]
+ // \_______/ ^-- idx
+ // \-- del
+ //
+ // Normally `Drop` impl would drop [tail] (via .for_each(drop), ie still calling `pred`)
+ //
+ // 1. Move [tail] after [kept]
+ // 2. Update length of the original vec to `old_len - del`
+ // a. In case of ZST, this is the only thing we want to do
+ // 3. Do *not* drop self, as everything is put in a consistent state already, there is nothing to do
+ let mut this = ManuallyDrop::new(self);
+
+ unsafe {
+ // ZSTs have no identity, so we don't need to move them around.
+ let needs_move = mem::size_of::<T>() != 0;
+
+ if needs_move && this.idx < this.old_len && this.del > 0 {
+ let ptr = this.vec.as_mut_ptr();
+ let src = ptr.add(this.idx);
+ let dst = src.sub(this.del);
+ let tail_len = this.old_len - this.idx;
+ src.copy_to(dst, tail_len);
+ }
+
+ let new_len = this.old_len - this.del;
+ this.vec.set_len(new_len);
+ }
+ }
}
#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")]
use crate::raw_vec::RawVec;
use core::array;
use core::fmt;
-use core::intrinsics::arith_offset;
use core::iter::{
FusedIterator, InPlaceIterable, SourceIter, TrustedLen, TrustedRandomAccessNoCoerce,
};
// purposefully don't use 'ptr.offset' because for
// vectors with 0-size elements this would return the
// same pointer.
- self.ptr = unsafe { arith_offset(self.ptr as *const i8, 1) as *mut T };
+ self.ptr = self.ptr.wrapping_byte_add(1);
// Make up a value of this ZST.
Some(unsafe { mem::zeroed() })
// SAFETY: due to unchecked casts of unsigned amounts to signed offsets the wraparound
// effectively results in unsigned pointers representing positions 0..usize::MAX,
// which is valid for ZSTs.
- self.ptr = unsafe { arith_offset(self.ptr as *const i8, step_size as isize) as *mut T }
+ self.ptr = self.ptr.wrapping_byte_add(step_size);
} else {
// SAFETY: the min() above ensures that step_size is in bounds
self.ptr = unsafe { self.ptr.add(step_size) };
return Err(unsafe { array::IntoIter::new_unchecked(raw_ary, 0..len) });
}
- self.ptr = unsafe { arith_offset(self.ptr as *const i8, N as isize) as *mut T };
+ self.ptr = self.ptr.wrapping_byte_add(N);
// Safety: ditto
return Ok(unsafe { MaybeUninit::array_assume_init(raw_ary) });
}
None
} else if mem::size_of::<T>() == 0 {
// See above for why 'ptr.offset' isn't used
- self.end = unsafe { arith_offset(self.end as *const i8, -1) as *mut T };
+ self.end = self.end.wrapping_byte_sub(1);
// Make up a value of this ZST.
Some(unsafe { mem::zeroed() })
let step_size = self.len().min(n);
if mem::size_of::<T>() == 0 {
// SAFETY: same as for advance_by()
- self.end = unsafe {
- arith_offset(self.end as *const i8, step_size.wrapping_neg() as isize) as *mut T
- }
+ self.end = self.end.wrapping_byte_sub(step_size);
} else {
// SAFETY: same as for advance_by()
self.end = unsafe { self.end.sub(step_size) };
use core::convert::TryFrom;
use core::fmt;
use core::hash::{Hash, Hasher};
-use core::intrinsics::{arith_offset, assume};
+use core::intrinsics::assume;
use core::iter;
#[cfg(not(no_global_oom_handling))]
use core::iter::FromIterator;
/// an explanation of the difference between length and capacity, see
/// *[Capacity and reallocation]*.
///
- /// If it is imporant to know the exact allocated capacity of a `Vec`,
+ /// If it is important to know the exact allocated capacity of a `Vec`,
/// always use the [`capacity`] method after construction.
///
/// For `Vec<T>` where `T` is a zero-sized type, there will be no allocation
/// an explanation of the difference between length and capacity, see
/// *[Capacity and reallocation]*.
///
- /// If it is imporant to know the exact allocated capacity of a `Vec`,
+ /// If it is important to know the exact allocated capacity of a `Vec`,
/// always use the [`capacity`] method after construction.
///
/// For `Vec<T, A>` where `T` is a zero-sized type, there will be no allocation
let alloc = ManuallyDrop::new(ptr::read(me.allocator()));
let begin = me.as_mut_ptr();
let end = if mem::size_of::<T>() == 0 {
- arith_offset(begin as *const i8, me.len() as isize) as *const T
+ begin.wrapping_byte_add(me.len())
} else {
begin.add(me.len()) as *const T
};
#![feature(bench_black_box)]
#![feature(strict_provenance)]
#![feature(once_cell)]
+#![feature(drain_keep_rest)]
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
assert_eq!(v, vec![D(0, false), D(1, false), D(6, false),]);
}
+#[test]
+fn test_drain_keep_rest() {
+ let mut v = vec![0, 1, 2, 3, 4, 5, 6];
+ let mut drain = v.drain(1..6);
+ assert_eq!(drain.next(), Some(1));
+ assert_eq!(drain.next_back(), Some(5));
+ assert_eq!(drain.next(), Some(2));
+
+ drain.keep_rest();
+ assert_eq!(v, &[0, 3, 4, 6]);
+}
+
+#[test]
+fn test_drain_keep_rest_all() {
+ let mut v = vec![0, 1, 2, 3, 4, 5, 6];
+ v.drain(1..6).keep_rest();
+ assert_eq!(v, &[0, 1, 2, 3, 4, 5, 6]);
+}
+
+#[test]
+fn test_drain_keep_rest_none() {
+ let mut v = vec![0, 1, 2, 3, 4, 5, 6];
+ let mut drain = v.drain(1..6);
+
+ drain.by_ref().for_each(drop);
+
+ drain.keep_rest();
+ assert_eq!(v, &[0, 6]);
+}
+
#[test]
fn test_splice() {
let mut v = vec![1, 2, 3, 4, 5];
assert_eq!(drop_count, 2);
}
+#[test]
+fn test_into_iter_zst() {
+ for _ in vec![[0u64; 0]].into_iter() {}
+ for _ in vec![[0u64; 0]; 5].into_iter().rev() {}
+}
+
#[test]
fn test_from_iter_specialization() {
let src: Vec<usize> = vec![0usize; 1];
assert_eq!(vec, [2, 4]);
}
+#[test]
+fn test_drain_filter_keep_rest() {
+ let mut v = vec![0, 1, 2, 3, 4, 5, 6];
+ let mut drain = v.drain_filter(|&mut x| x % 2 == 0);
+ assert_eq!(drain.next(), Some(0));
+ assert_eq!(drain.next(), Some(2));
+
+ drain.keep_rest();
+ assert_eq!(v, &[1, 3, 4, 5, 6]);
+}
+
+#[test]
+fn test_drain_filter_keep_rest_all() {
+ let mut v = vec![0, 1, 2, 3, 4, 5, 6];
+ v.drain_filter(|_| true).keep_rest();
+ assert_eq!(v, &[0, 1, 2, 3, 4, 5, 6]);
+}
+
+#[test]
+fn test_drain_filter_keep_rest_none() {
+ let mut v = vec![0, 1, 2, 3, 4, 5, 6];
+ let mut drain = v.drain_filter(|_| true);
+
+ drain.by_ref().for_each(drop);
+
+ drain.keep_rest();
+ assert_eq!(v, &[]);
+}
+
#[test]
fn test_reserve_exact() {
// This is all the same as test_reserve
/// impl Provider for SomeConcreteType {
/// fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
/// demand.provide_ref::<str>(&self.field)
- /// .provide_value::<i32>(|| self.num_field);
+ /// .provide_value::<i32>(self.num_field);
/// }
/// }
/// ```
///
/// # Examples
///
+ /// Provides an `u8`.
+ ///
+ /// ```rust
+ /// #![feature(provide_any)]
+ ///
+ /// use std::any::{Provider, Demand};
+ /// # struct SomeConcreteType { field: u8 }
+ ///
+ /// impl Provider for SomeConcreteType {
+ /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
+ /// demand.provide_value::<u8>(self.field);
+ /// }
+ /// }
+ /// ```
+ #[unstable(feature = "provide_any", issue = "96024")]
+ pub fn provide_value<T>(&mut self, value: T) -> &mut Self
+ where
+ T: 'static,
+ {
+ self.provide::<tags::Value<T>>(value)
+ }
+
+ /// Provide a value or other type with only static lifetimes computed using a closure.
+ ///
+ /// # Examples
+ ///
/// Provides a `String` by cloning.
///
/// ```rust
- /// # #![feature(provide_any)]
+ /// #![feature(provide_any)]
+ ///
/// use std::any::{Provider, Demand};
/// # struct SomeConcreteType { field: String }
///
/// impl Provider for SomeConcreteType {
/// fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
- /// demand.provide_value::<String>(|| self.field.clone());
+ /// demand.provide_value_with::<String>(|| self.field.clone());
/// }
/// }
/// ```
#[unstable(feature = "provide_any", issue = "96024")]
- pub fn provide_value<T>(&mut self, fulfil: impl FnOnce() -> T) -> &mut Self
+ pub fn provide_value_with<T>(&mut self, fulfil: impl FnOnce() -> T) -> &mut Self
where
T: 'static,
{
self.provide_with::<tags::Value<T>>(fulfil)
}
- /// Provide a reference, note that the referee type must be bounded by `'static`,
+ /// Provide a reference. The referee type must be bounded by `'static`,
/// but may be unsized.
///
/// # Examples
/// Provides a reference to a field as a `&str`.
///
/// ```rust
- /// # #![feature(provide_any)]
+ /// #![feature(provide_any)]
+ ///
/// use std::any::{Provider, Demand};
/// # struct SomeConcreteType { field: String }
///
self.provide::<tags::Ref<tags::MaybeSizedValue<T>>>(value)
}
+ /// Provide a reference computed using a closure. The referee type
+ /// must be bounded by `'static`, but may be unsized.
+ ///
+ /// # Examples
+ ///
+ /// Provides a reference to a field as a `&str`.
+ ///
+ /// ```rust
+ /// #![feature(provide_any)]
+ ///
+ /// use std::any::{Provider, Demand};
+ /// # struct SomeConcreteType { business: String, party: String }
+ /// # fn today_is_a_weekday() -> bool { true }
+ ///
+ /// impl Provider for SomeConcreteType {
+ /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
+ /// demand.provide_ref_with::<str>(|| {
+ /// if today_is_a_weekday() {
+ /// &self.business
+ /// } else {
+ /// &self.party
+ /// }
+ /// });
+ /// }
+ /// }
+ /// ```
+ #[unstable(feature = "provide_any", issue = "96024")]
+ pub fn provide_ref_with<T: ?Sized + 'static>(
+ &mut self,
+ fulfil: impl FnOnce() -> &'a T,
+ ) -> &mut Self {
+ self.provide_with::<tags::Ref<tags::MaybeSizedValue<T>>>(fulfil)
+ }
+
/// Provide a value with the given `Type` tag.
fn provide<I>(&mut self, value: I::Reified) -> &mut Self
where
}
self
}
+
+ /// Check if the `Demand` would be satisfied if provided with a
+ /// value of the specified type. If the type does not match or has
+ /// already been provided, returns false.
+ ///
+ /// # Examples
+ ///
+ /// Check if an `u8` still needs to be provided and then provides
+ /// it.
+ ///
+ /// ```rust
+ /// #![feature(provide_any)]
+ ///
+ /// use std::any::{Provider, Demand};
+ ///
+ /// struct Parent(Option<u8>);
+ ///
+ /// impl Provider for Parent {
+ /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
+ /// if let Some(v) = self.0 {
+ /// demand.provide_value::<u8>(v);
+ /// }
+ /// }
+ /// }
+ ///
+ /// struct Child {
+ /// parent: Parent,
+ /// }
+ ///
+ /// impl Child {
+ /// // Pretend that this takes a lot of resources to evaluate.
+ /// fn an_expensive_computation(&self) -> Option<u8> {
+ /// Some(99)
+ /// }
+ /// }
+ ///
+ /// impl Provider for Child {
+ /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
+ /// // In general, we don't know if this call will provide
+ /// // an `u8` value or not...
+ /// self.parent.provide(demand);
+ ///
+ /// // ...so we check to see if the `u8` is needed before
+ /// // we run our expensive computation.
+ /// if demand.would_be_satisfied_by_value_of::<u8>() {
+ /// if let Some(v) = self.an_expensive_computation() {
+ /// demand.provide_value::<u8>(v);
+ /// }
+ /// }
+ ///
+ /// // The demand will be satisfied now, regardless of if
+ /// // the parent provided the value or we did.
+ /// assert!(!demand.would_be_satisfied_by_value_of::<u8>());
+ /// }
+ /// }
+ ///
+ /// let parent = Parent(Some(42));
+ /// let child = Child { parent };
+ /// assert_eq!(Some(42), std::any::request_value::<u8>(&child));
+ ///
+ /// let parent = Parent(None);
+ /// let child = Child { parent };
+ /// assert_eq!(Some(99), std::any::request_value::<u8>(&child));
+ /// ```
+ #[unstable(feature = "provide_any", issue = "96024")]
+ pub fn would_be_satisfied_by_value_of<T>(&self) -> bool
+ where
+ T: 'static,
+ {
+ self.would_be_satisfied_by::<tags::Value<T>>()
+ }
+
+ /// Check if the `Demand` would be satisfied if provided with a
+ /// reference to a value of the specified type. If the type does
+ /// not match or has already been provided, returns false.
+ ///
+ /// # Examples
+ ///
+ /// Check if a `&str` still needs to be provided and then provides
+ /// it.
+ ///
+ /// ```rust
+ /// #![feature(provide_any)]
+ ///
+ /// use std::any::{Provider, Demand};
+ ///
+ /// struct Parent(Option<String>);
+ ///
+ /// impl Provider for Parent {
+ /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
+ /// if let Some(v) = &self.0 {
+ /// demand.provide_ref::<str>(v);
+ /// }
+ /// }
+ /// }
+ ///
+ /// struct Child {
+ /// parent: Parent,
+ /// name: String,
+ /// }
+ ///
+ /// impl Child {
+ /// // Pretend that this takes a lot of resources to evaluate.
+ /// fn an_expensive_computation(&self) -> Option<&str> {
+ /// Some(&self.name)
+ /// }
+ /// }
+ ///
+ /// impl Provider for Child {
+ /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
+ /// // In general, we don't know if this call will provide
+ /// // a `str` reference or not...
+ /// self.parent.provide(demand);
+ ///
+ /// // ...so we check to see if the `&str` is needed before
+ /// // we run our expensive computation.
+ /// if demand.would_be_satisfied_by_ref_of::<str>() {
+ /// if let Some(v) = self.an_expensive_computation() {
+ /// demand.provide_ref::<str>(v);
+ /// }
+ /// }
+ ///
+ /// // The demand will be satisfied now, regardless of if
+ /// // the parent provided the reference or we did.
+ /// assert!(!demand.would_be_satisfied_by_ref_of::<str>());
+ /// }
+ /// }
+ ///
+ /// let parent = Parent(Some("parent".into()));
+ /// let child = Child { parent, name: "child".into() };
+ /// assert_eq!(Some("parent"), std::any::request_ref::<str>(&child));
+ ///
+ /// let parent = Parent(None);
+ /// let child = Child { parent, name: "child".into() };
+ /// assert_eq!(Some("child"), std::any::request_ref::<str>(&child));
+ /// ```
+ #[unstable(feature = "provide_any", issue = "96024")]
+ pub fn would_be_satisfied_by_ref_of<T>(&self) -> bool
+ where
+ T: ?Sized + 'static,
+ {
+ self.would_be_satisfied_by::<tags::Ref<tags::MaybeSizedValue<T>>>()
+ }
+
+ fn would_be_satisfied_by<I>(&self) -> bool
+ where
+ I: tags::Type<'a>,
+ {
+ matches!(self.0.downcast::<I>(), Some(TaggedOption(None)))
+ }
}
#[unstable(feature = "provide_any", issue = "96024")]
/// Returns some reference to the dynamic value if it is tagged with `I`,
/// or `None` otherwise.
#[inline]
+ fn downcast<I>(&self) -> Option<&TaggedOption<'a, I>>
+ where
+ I: tags::Type<'a>,
+ {
+ if self.tag_id() == TypeId::of::<I>() {
+ // SAFETY: Just checked whether we're pointing to an I.
+ Some(unsafe { &*(self as *const Self).cast::<TaggedOption<'a, I>>() })
+ } else {
+ None
+ }
+ }
+
+ /// Returns some mutable reference to the dynamic value if it is tagged with `I`,
+ /// or `None` otherwise.
+ #[inline]
fn downcast_mut<I>(&mut self) -> Option<&mut TaggedOption<'a, I>>
where
I: tags::Type<'a>,
}
impl fmt::Write for PadAdapter<'_, '_> {
- fn write_str(&mut self, mut s: &str) -> fmt::Result {
- while !s.is_empty() {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ for s in s.split_inclusive('\n') {
if self.state.on_newline {
self.buf.write_str(" ")?;
}
- let split = match s.find('\n') {
- Some(pos) => {
- self.state.on_newline = true;
- pos + 1
- }
- None => {
- self.state.on_newline = false;
- s.len()
- }
- };
- self.buf.write_str(&s[..split])?;
- s = &s[split..];
+ self.state.on_newline = s.ends_with('\n');
+ self.buf.write_str(s)?;
}
Ok(())
///
/// `unreachable_unchecked()` can be used in situations where the compiler
/// can't prove invariants that were previously established. Such situations
-/// have a higher chance of occuring if those invariants are upheld by
+/// have a higher chance of occurring if those invariants are upheld by
/// external code that the compiler can't analyze.
/// ```
/// fn prepare_inputs(divisors: &mut Vec<u32>) {
/// Note that using `transmute` to turn a pointer to a `usize` is (as noted above) [undefined
/// behavior][ub] in `const` contexts. Also outside of consts, this operation might not behave
/// as expected -- this is touching on many unspecified aspects of the Rust memory model.
- /// Depending on what the code is doing, the following alternatives are preferrable to
+ /// Depending on what the code is doing, the following alternatives are preferable to
/// pointer-to-integer transmutation:
/// - If the code just wants to store data of arbitrary type in some buffer and needs to pick a
/// type for that buffer, it can use [`MaybeUninit`][mem::MaybeUninit].
/// Unwraps a result or propagates its error.
///
-/// The `?` operator was added to replace `try!` and should be used instead.
-/// Furthermore, `try` is a reserved word in Rust 2018, so if you must use
-/// it, you will need to use the [raw-identifier syntax][ris]: `r#try`.
+/// The [`?` operator][propagating-errors] was added to replace `try!`
+/// and should be used instead. Furthermore, `try` is a reserved word
+/// in Rust 2018, so if you must use it, you will need to use the
+/// [raw-identifier syntax][ris]: `r#try`.
///
+/// [propagating-errors]: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator
/// [ris]: https://doc.rust-lang.org/nightly/rust-by-example/compatibility/raw_identifiers.html
///
/// `try!` matches the given [`Result`]. In case of the `Ok` variant, the
/// // The equivalent code with `MaybeUninit<i32>`:
/// let x: i32 = unsafe { MaybeUninit::uninit().assume_init() }; // undefined behavior! ⚠️
/// ```
-/// (Notice that the rules around uninitialized integers are not finalized yet, but
-/// until they are, it is advisable to avoid them.)
-///
/// On top of that, remember that most types have additional invariants beyond merely
/// being considered initialized at the type level. For example, a `1`-initialized [`Vec<T>`]
/// is considered initialized (under the current implementation; this does not constitute
/// correctly: it has the same effect as [`MaybeUninit::uninit().assume_init()`][uninit].
/// As the [`assume_init` documentation][assume_init] explains,
/// [the Rust compiler assumes][inv] that values are properly initialized.
-/// As a consequence, calling e.g. `mem::uninitialized::<bool>()` causes immediate
-/// undefined behavior for returning a `bool` that is not definitely either `true`
-/// or `false`. Worse, truly uninitialized memory like what gets returned here
+///
+/// Truly uninitialized memory like what gets returned here
/// is special in that the compiler knows that it does not have a fixed value.
/// This makes it undefined behavior to have uninitialized data in a variable even
/// if that variable has an integer type.
-/// (Notice that the rules around uninitialized integers are not finalized yet, but
-/// until they are, it is advisable to avoid them.)
+///
+/// Therefore, it is immediate undefined behavior to call this function on nearly all types,
+/// including integer types and arrays of integer types, and even if the result is unused.
///
/// [uninit]: MaybeUninit::uninit
/// [assume_init]: MaybeUninit::assume_init
( $( $Ty: ident($Int: ident); )+ ) => {
$(
impl $Ty {
- /// Add an unsigned integer to a non-zero value.
- /// Check for overflow and return [`None`] on overflow
+ /// Adds an unsigned integer to a non-zero value.
+ /// Checks for overflow and returns [`None`] on overflow.
/// As a consequence, the result cannot wrap to zero.
///
///
}
}
- /// Add an unsigned integer to a non-zero value.
+ /// Adds an unsigned integer to a non-zero value.
#[doc = concat!("Return [`", stringify!($Int), "::MAX`] on overflow.")]
///
/// # Examples
unsafe { $Ty::new_unchecked(self.get().saturating_add(other)) }
}
- /// Add an unsigned integer to a non-zero value,
+ /// Adds an unsigned integer to a non-zero value,
/// assuming overflow cannot occur.
/// Overflow is unchecked, and it is undefined behaviour to overflow
/// *even if the result would wrap to a non-zero value*.
}
/// Returns the smallest power of two greater than or equal to n.
- /// Check for overflow and return [`None`]
+ /// Checks for overflow and returns [`None`]
/// if the next power of two is greater than the type’s maximum value.
/// As a consequence, the result cannot wrap to zero.
///
}
/// Checked absolute value.
- /// Check for overflow and returns [`None`] if
+ /// Checks for overflow and returns [`None`] if
#[doc = concat!("`self == ", stringify!($Int), "::MIN`.")]
/// The result cannot be zero.
///
( $( $signedness:ident $Ty: ident($Int: ty); )+ ) => {
$(
impl $Ty {
- /// Multiply two non-zero integers together.
- /// Check for overflow and return [`None`] on overflow.
+ /// Multiplies two non-zero integers together.
+ /// Checks for overflow and returns [`None`] on overflow.
/// As a consequence, the result cannot wrap to zero.
///
/// # Examples
}
}
- /// Multiply two non-zero integers together.
+ /// Multiplies two non-zero integers together.
#[doc = concat!("Return [`", stringify!($Int), "::MAX`] on overflow.")]
///
/// # Examples
unsafe { $Ty::new_unchecked(self.get().saturating_mul(other.get())) }
}
- /// Multiply two non-zero integers together,
+ /// Multiplies two non-zero integers together,
/// assuming overflow cannot occur.
/// Overflow is unchecked, and it is undefined behaviour to overflow
/// *even if the result would wrap to a non-zero value*.
unsafe { $Ty::new_unchecked(self.get().unchecked_mul(other.get())) }
}
- /// Raise non-zero value to an integer power.
- /// Check for overflow and return [`None`] on overflow.
+ /// Raises non-zero value to an integer power.
+ /// Checks for overflow and returns [`None`] on overflow.
/// As a consequence, the result cannot wrap to zero.
///
/// # Examples
#[doc(alias = "&")]
#[doc(alias = "&mut")]
//
-/// References, both shared and mutable.
+/// References, `&T` and `&mut T`.
///
/// A reference represents a borrow of some owned value. You can get one by using the `&` or `&mut`
/// operators on a value, or by using a [`ref`](../std/keyword.ref.html) or
/// This is similar to `self as usize`, which semantically discards *provenance* and
/// *address-space* information. However, unlike `self as usize`, casting the returned address
/// back to a pointer yields [`invalid`][], which is undefined behavior to dereference. To
- /// properly restore the lost information and obtain a dereferencable pointer, use
+ /// properly restore the lost information and obtain a dereferenceable pointer, use
/// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr].
///
/// If using those APIs is not possible because there is no way to preserve a pointer with the
let offset = dest_addr.wrapping_sub(self_addr);
// This is the canonical desugarring of this operation
- self.cast::<u8>().wrapping_offset(offset).cast::<T>()
+ self.wrapping_byte_offset(offset)
}
/// Creates a new pointer by mapping `self`'s address to a new one.
//! isn't *pointer*-sized but address-space/offset/allocation-sized (we'll probably continue
//! to conflate these notions). This would potentially make it possible to more efficiently
//! target platforms where pointers are larger than offsets, such as CHERI and maybe some
-//! segmented architecures.
+//! segmented architectures.
//!
//! ## Provenance
//!
//! a pointer to a usize is generally an operation which *only* extracts the address. It is
//! therefore *impossible* to construct a valid pointer from a usize because there is no way
//! to restore the address-space and provenance. In other words, pointer-integer-pointer
-//! roundtrips are not possible (in the sense that the resulting pointer is not dereferencable).
+//! roundtrips are not possible (in the sense that the resulting pointer is not dereferenceable).
//!
//! The key insight to making this model *at all* viable is the [`with_addr`][] method:
//!
//!
//! * Create an invalid pointer from just an address (see [`ptr::invalid`][]). This can
//! be used for sentinel values like `null` *or* to represent a tagged pointer that will
-//! never be dereferencable. In general, it is always sound for an integer to pretend
+//! never be dereferenceable. In general, it is always sound for an integer to pretend
//! to be a pointer "for fun" as long as you don't use operations on it which require
//! it to be valid (offset, read, write, etc).
//!
/// This is similar to `self as usize`, which semantically discards *provenance* and
/// *address-space* information. However, unlike `self as usize`, casting the returned address
/// back to a pointer yields [`invalid`][], which is undefined behavior to dereference. To
- /// properly restore the lost information and obtain a dereferencable pointer, use
+ /// properly restore the lost information and obtain a dereferenceable pointer, use
/// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr].
///
/// If using those APIs is not possible because there is no way to preserve a pointer with the
let offset = dest_addr.wrapping_sub(self_addr);
// This is the canonical desugarring of this operation
- self.cast::<u8>().wrapping_offset(offset).cast::<T>()
+ self.wrapping_byte_offset(offset)
}
/// Creates a new pointer by mapping `self`'s address to a new one.
None => 0,
};
// SAFETY: This type ensures that self.v is a valid pointer with a correct len.
- // Therefore the bounds check in split_at_mut guarantess the split point is inbounds.
+ // Therefore the bounds check in split_at_mut guarantees the split point is inbounds.
let (head, tail) = unsafe { self.v.split_at_mut(start) };
// SAFETY: This type ensures that self.v is a valid pointer with a correct len.
- // Therefore the bounds check in split_at_mut guarantess the split point is inbounds.
+ // Therefore the bounds check in split_at_mut guarantees the split point is inbounds.
let (nth, _) = unsafe { tail.split_at_mut(end - start) };
self.v = head;
// SAFETY: Nothing else points to or will point to the contents of this slice.
// backwards by `n`. `n` must not exceed `self.len()`.
macro_rules! zst_shrink {
($self: ident, $n: ident) => {
- $self.end = ($self.end as * $raw_mut u8).wrapping_offset(-$n) as * $raw_mut T;
+ $self.end = $self.end.wrapping_byte_offset(-$n);
}
}
/// }
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
- #[rustc_const_unstable(feature = "const_slice_split_at_not_mut", issue = "none")]
+ #[rustc_const_unstable(feature = "const_slice_split_at_not_mut", issue = "101158")]
#[inline]
#[track_caller]
#[must_use]
}
/// Binary searches this slice for a given element.
- /// This behaves similary to [`contains`] if this slice is sorted.
+ /// This behaves similarly to [`contains`] if this slice is sorted.
///
/// If the value is found then [`Result::Ok`] is returned, containing the
/// index of the matching element. If there are multiple matches, then any
// alignment targeted for U.
// `crate::ptr::align_offset` is called with a correctly aligned and
// valid pointer `ptr` (it comes from a reference to `self`) and with
- // a size that is a power of two (since it comes from the alignement for U),
+ // a size that is a power of two (since it comes from the alignment for U),
// satisfying its safety constraints.
let offset = unsafe { crate::ptr::align_offset(ptr, mem::align_of::<U>()) };
if offset > self.len() {
let rem_msb = nanos_tmp & rem_msb_mask == 0;
let add_ns = !(rem_msb || (is_even && is_tie));
- // f32 does not have enough presicion to trigger the second branch
+ // f32 does not have enough precision to trigger the second branch
// since it can not represent numbers between 0.999_999_940_395 and 1.0.
let nanos = nanos + add_ns as u32;
if ($mant_bits == 23) || (nanos != NANOS_PER_SEC) { (0, nanos) } else { (1, 0) }
let rem_msb = nanos_tmp & rem_msb_mask == 0;
let add_ns = !(rem_msb || (is_even && is_tie));
- // f32 does not have enough presicion to trigger the second branch.
+ // f32 does not have enough precision to trigger the second branch.
// For example, it can not represent numbers between 1.999_999_880...
- // and 2.0. Bigger values result in even smaller presicion of the
+ // and 2.0. Bigger values result in even smaller precision of the
// fractional part.
let nanos = nanos + add_ns as u32;
if ($mant_bits == 23) || (nanos != NANOS_PER_SEC) {
demand
.provide_ref::<String>(&self.some_string)
.provide_ref::<str>(&self.some_string)
- .provide_value::<String>(|| "bye".to_owned());
+ .provide_value_with::<String>(|| "bye".to_owned());
}
}
assert_eq!(atom.fetch_ptr_sub(1, SeqCst), n.wrapping_add(1));
assert_eq!(atom.load(SeqCst), n);
- let bytes_from_n = |b| n.cast::<u8>().wrapping_add(b).cast::<i64>();
+ let bytes_from_n = |b| n.wrapping_byte_add(b);
assert_eq!(atom.fetch_byte_add(1, SeqCst), n);
assert_eq!(atom.load(SeqCst), bytes_from_n(1));
.unwrap_or_else(|| handle_alloc_error(layout))
.cast::<DynMetadata<T>>();
ptr.as_ptr().write(meta);
- ptr.cast::<u8>().as_ptr().add(offset).cast::<Value>().write(value);
+ ptr.as_ptr().byte_add(offset).cast::<Value>().write(value);
Self { ptr, phantom: PhantomData }
}
}
impl ToBitMask<BitMask=u64> for Mask<_, 64>
}
-/// Returns the minimum numnber of bytes in a bitmask with `lanes` lanes.
+/// Returns the minimum number of bytes in a bitmask with `lanes` lanes.
#[cfg(feature = "generic_const_exprs")]
pub const fn bitmask_len(lanes: usize) -> usize {
(lanes + 7) / 8
FreeFunctions,
TokenStream,
SourceFile,
- MultiSpan,
- Diagnostic,
'interned:
Span,
fn track_env_var(var: &str, value: Option<&str>);
fn track_path(path: &str);
fn literal_from_str(s: &str) -> Result<Literal<$S::Span, $S::Symbol>, ()>;
+ fn emit_diagnostic(diagnostic: Diagnostic<$S::Span>);
},
TokenStream {
fn drop($self: $S::TokenStream);
fn path($self: &$S::SourceFile) -> String;
fn is_real($self: &$S::SourceFile) -> bool;
},
- MultiSpan {
- fn drop($self: $S::MultiSpan);
- fn new() -> $S::MultiSpan;
- fn push($self: &mut $S::MultiSpan, span: $S::Span);
- },
- Diagnostic {
- fn drop($self: $S::Diagnostic);
- fn new(level: Level, msg: &str, span: $S::MultiSpan) -> $S::Diagnostic;
- fn sub(
- $self: &mut $S::Diagnostic,
- level: Level,
- msg: &str,
- span: $S::MultiSpan,
- );
- fn emit($self: $S::Diagnostic);
- },
Span {
fn debug($self: $S::Span) -> String;
fn source_file($self: $S::Span) -> $S::SourceFile;
}
);
+#[derive(Clone, Debug)]
+pub struct Diagnostic<Span> {
+ pub level: Level,
+ pub message: String,
+ pub spans: Vec<Span>,
+ pub children: Vec<Diagnostic<Span>>,
+}
+
+compound_traits!(
+ struct Diagnostic<Span> { level, message, spans, children }
+);
+
/// Globals provided alongside the initial inputs for a macro expansion.
/// Provides values such as spans which are used frequently to avoid RPC.
#[derive(Clone)]
type FreeFunctions: 'static;
type TokenStream: 'static + Clone;
type SourceFile: 'static + Clone;
- type MultiSpan: 'static;
- type Diagnostic: 'static;
type Span: 'static + Copy + Eq + Hash;
type Symbol: 'static;
}
/// Emit the diagnostic.
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
pub fn emit(self) {
- fn to_internal(spans: Vec<Span>) -> crate::bridge::client::MultiSpan {
- let mut multi_span = crate::bridge::client::MultiSpan::new();
- for span in spans {
- multi_span.push(span.0);
+ fn to_internal(diag: Diagnostic) -> crate::bridge::Diagnostic<crate::bridge::client::Span> {
+ crate::bridge::Diagnostic {
+ level: diag.level,
+ message: diag.message,
+ spans: diag.spans.into_iter().map(|s| s.0).collect(),
+ children: diag.children.into_iter().map(to_internal).collect(),
}
- multi_span
}
- let mut diag = crate::bridge::client::Diagnostic::new(
- self.level,
- &self.message[..],
- to_internal(self.spans),
- );
- for c in self.children {
- diag.sub(c.level, &c.message[..], to_internal(c.spans));
- }
- diag.emit();
+ crate::bridge::client::FreeFunctions::emit_diagnostic(to_internal(self));
}
}
OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref())
}
+ /// Creates a new file in read-write mode; error if the file exists.
+ ///
+ /// This function will create a file if it does not exist, or return an error if it does. This
+ /// way, if the call succeeds, the file returned is guaranteed to be new.
+ ///
+ /// This option is useful because it is atomic. Otherwise between checking whether a file
+ /// exists and creating a new one, the file may have been created by another process (a TOCTOU
+ /// race condition / attack).
+ ///
+ /// This can also be written using
+ /// `File::options().read(true).write(true).create_new(true).open(...)`.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// #![feature(file_create_new)]
+ ///
+ /// use std::fs::File;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let mut f = File::create_new("foo.txt")?;
+ /// Ok(())
+ /// }
+ /// ```
+ #[unstable(feature = "file_create_new", issue = "none")]
+ pub fn create_new<P: AsRef<Path>>(path: P) -> io::Result<File> {
+ OpenOptions::new().read(true).write(true).create_new(true).open(path.as_ref())
+ }
+
/// Returns a new OpenOptions object.
///
/// This function returns a new OpenOptions object that you can use to
}
TAG_SIMPLE_MESSAGE => ErrorData::SimpleMessage(&*ptr.cast::<SimpleMessage>().as_ptr()),
TAG_CUSTOM => {
- // It would be correct for us to use `ptr::sub` here (see the
+ // It would be correct for us to use `ptr::byte_sub` here (see the
// comment above the `wrapping_add` call in `new_custom` for why),
// but it isn't clear that it makes a difference, so we don't.
- let custom = ptr.as_ptr().cast::<u8>().wrapping_sub(TAG_CUSTOM).cast::<Custom>();
+ let custom = ptr.as_ptr().wrapping_byte_sub(TAG_CUSTOM).cast::<Custom>();
ErrorData::Custom(make_custom(custom))
}
_ => {
/// # Examples
///
/// ```no_run
-/// #![feature(io_read_to_string)]
-///
/// # use std::io;
/// fn main() -> io::Result<()> {
/// let stdin = io::read_to_string(io::stdin())?;
/// Ok(())
/// }
/// ```
-#[unstable(feature = "io_read_to_string", issue = "80218")]
+#[stable(feature = "io_read_to_string", since = "CURRENT_RUSTC_VERSION")]
pub fn read_to_string<R: Read>(mut reader: R) -> Result<String> {
let mut buf = String::new();
reader.read_to_string(&mut buf)?;
/// otherwise. `label` identifies the stream in a panic message.
///
/// This function is used to print error messages, so it takes extra
-/// care to avoid causing a panic when `local_s` is unusable.
-/// For instance, if the TLS key for the local stream is
-/// already destroyed, or if the local stream is locked by another
-/// thread, it will just fall back to the global stream.
+/// care to avoid causing a panic when `OUTPUT_CAPTURE` is unusable.
+/// For instance, if the TLS key for output capturing is already destroyed, or
+/// if the local stream is in use by another thread, it will just fall back to
+/// the global stream.
///
/// However, if the actual I/O causes an error, this function does panic.
fn print_to<T>(args: fmt::Arguments<'_>, global_s: fn() -> T, label: &str)
/// and [proposal]s exist to use `unsafe {}` blocks inside such functions when
/// making `unsafe` operations.
///
-/// See the [Rustnomicon] and the [Reference] for more informations.
+/// See the [Rustnomicon] and the [Reference] for more information.
///
/// # Examples
///
/// Add constraints that must be upheld to use an item.
///
/// `where` allows specifying constraints on lifetime and generic parameters.
-/// The [RFC] introducing `where` contains detailed informations about the
+/// The [RFC] introducing `where` contains detailed information about the
/// keyword.
///
/// # Examples
/// println!("f = {f} and i = {i}");
/// ```
///
-/// See the [Reference][union] for more informations on `union`s.
+/// See the [Reference][union] for more information on `union`s.
///
/// [`struct`]: keyword.struct.html
/// [union]: ../reference/items/unions.html
#![feature(intra_doc_pointers)]
#![cfg_attr(bootstrap, feature(label_break_value))]
#![feature(lang_items)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(linkage)]
#![feature(link_cfg)]
#![feature(panic_can_unwind)]
#![feature(panic_info_message)]
#![feature(panic_internals)]
+#![feature(pointer_byte_offsets)]
#![feature(pointer_is_aligned)]
#![feature(portable_simd)]
#![feature(prelude_2024)]
#[doc(alias = "&")]
#[doc(alias = "&mut")]
//
-/// References, both shared and mutable.
+/// References, `&T` and `&mut T`.
///
/// A reference represents a borrow of some owned value. You can get one by using the `&` or `&mut`
/// operators on a value, or by using a [`ref`](../std/keyword.ref.html) or
and cause Futures to not implement `Send`"]
#[stable(feature = "rust1", since = "1.0.0")]
#[clippy::has_significant_drop]
+#[cfg_attr(not(test), rustc_diagnostic_item = "MutexGuard")]
pub struct MutexGuard<'a, T: ?Sized + 'a> {
lock: &'a Mutex<T>,
poison: poison::Guard,
and cause Futures to not implement `Send`"]
#[stable(feature = "rust1", since = "1.0.0")]
#[clippy::has_significant_drop]
+#[cfg_attr(not(test), rustc_diagnostic_item = "RwLockReadGuard")]
pub struct RwLockReadGuard<'a, T: ?Sized + 'a> {
// NB: we use a pointer instead of `&'a T` to avoid `noalias` violations, because a
// `Ref` argument doesn't hold immutability for its whole scope, only until it drops.
and cause Future's to not implement `Send`"]
#[stable(feature = "rust1", since = "1.0.0")]
#[clippy::has_significant_drop]
+#[cfg_attr(not(test), rustc_diagnostic_item = "RwLockWriteGuard")]
pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> {
lock: &'a RwLock<T>,
poison: poison::Guard,
}
}
- // The state has changed or a wakeup occured, try to lock the mutex.
+ // The state has changed or a wakeup occurred, try to lock the mutex.
match self.futex.compare_exchange(UNLOCKED, owned_state, Acquire, Relaxed) {
Ok(_) => return,
Err(updated) => state = updated,
// We avoid an unnecessary write if it as already set to 2,
// to be friendlier for the caches.
if state != 2 && self.futex.swap(2, Acquire) == 0 {
- // We changed it from 0 to 2, so we just succesfully locked it.
+ // We changed it from 0 to 2, so we just successfully locked it.
return;
}
// We don't allow read-locking if there's readers waiting, even if the lock is unlocked
// and there's no writers waiting. The only situation when this happens is after unlocking,
// at which point the unlocking thread might be waking up writers, which have priority over readers.
- // The unlocking thread will clear the readers waiting bit and wake up readers, if necssary.
+ // The unlocking thread will clear the readers waiting bit and wake up readers, if necessary.
state & MASK < MAX_READERS && !has_readers_waiting(state) && !has_writers_waiting(state)
}
}
}
-#[cfg(target_os = "macos")]
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
mod imp {
- use crate::fs::File;
- use crate::io::Read;
- use crate::sys::os::errno;
+ use crate::io;
use crate::sys::weak::weak;
use libc::{c_int, c_void, size_t};
for s in v.chunks_mut(256) {
let ret = unsafe { f(s.as_mut_ptr() as *mut c_void, s.len()) };
if ret == -1 {
- panic!("unexpected getentropy error: {}", errno());
+ panic!("unexpected getentropy error: {}", io::Error::last_os_error());
}
}
true
.unwrap_or(false)
}
+ #[cfg(target_os = "macos")]
+ fn fallback_fill_bytes(v: &mut [u8]) {
+ use crate::fs::File;
+ use crate::io::Read;
+
+ let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
+ file.read_exact(v).expect("failed to read /dev/urandom")
+ }
+
+ // On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with
+ // `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded
+ // from `/dev/random` and which runs on its own thread accessed via GCD.
+ //
+ // This is very heavyweight compared to the alternatives, but they may not be usable:
+ // - `getentropy` was added in iOS 10, but we support a minimum of iOS 7
+ // - `/dev/urandom` is not accessible inside the iOS app sandbox.
+ //
+ // Therefore `SecRandomCopyBytes` is only used on older iOS versions where no
+ // better options are present.
+ #[cfg(target_os = "ios")]
+ fn fallback_fill_bytes(v: &mut [u8]) {
+ use crate::ptr;
+
+ enum SecRandom {}
+
+ #[allow(non_upper_case_globals)]
+ const kSecRandomDefault: *const SecRandom = ptr::null();
+
+ extern "C" {
+ fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int;
+ }
+
+ let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) };
+ if ret == -1 {
+ panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
+ }
+ }
+
+ // All supported versions of watchOS (>= 5) have support for `getentropy`.
+ #[cfg(target_os = "watchos")]
+ #[cold]
+ fn fallback_fill_bytes(_: &mut [u8]) {
+ unreachable!()
+ }
+
pub fn fill_bytes(v: &mut [u8]) {
if getentropy_fill_bytes(v) {
return;
}
- // for older macos which doesn't support getentropy
- let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
- file.read_exact(v).expect("failed to read /dev/urandom")
+ // Older macOS versions (< 10.12) don't support `getentropy`. Fallback to
+ // reading from `/dev/urandom` on these systems.
+ //
+ // Older iOS versions (< 10) don't support it either. Fallback to
+ // `SecRandomCopyBytes` on these systems. On watchOS, this is unreachable
+ // because the minimum supported version is 5 while `getentropy` became accessible
+ // in 3.
+ fallback_fill_bytes(v)
}
}
}
}
-// On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with
-// `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded
-// from `/dev/random` and which runs on its own thread accessed via GCD.
-// This seems needlessly heavyweight for the purposes of generating two u64s
-// once per thread in `hashmap_random_keys`. Therefore `SecRandomCopyBytes` is
-// only used on iOS where direct access to `/dev/urandom` is blocked by the
-// sandbox.
-#[cfg(any(target_os = "ios", target_os = "watchos"))]
-mod imp {
- use crate::io;
- use crate::ptr;
- use libc::{c_int, size_t};
-
- enum SecRandom {}
-
- #[allow(non_upper_case_globals)]
- const kSecRandomDefault: *const SecRandom = ptr::null();
-
- extern "C" {
- fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int;
- }
-
- pub fn fill_bytes(v: &mut [u8]) {
- let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) };
- if ret == -1 {
- panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
- }
- }
-}
-
#[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
mod imp {
use crate::ptr;
use crate::io::{self, IoSlice, IoSliceMut};
use crate::mem::ManuallyDrop;
use crate::os::raw;
-use crate::os::wasi::io::{AsRawFd, FromRawFd};
+use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd};
pub struct Stdin;
pub struct Stdout;
}
}
+impl AsFd for Stdin {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw(0) }
+ }
+}
+
impl io::Read for Stdin {
fn read(&mut self, data: &mut [u8]) -> io::Result<usize> {
self.read_vectored(&mut [IoSliceMut::new(data)])
}
}
+impl AsFd for Stdout {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw(1) }
+ }
+}
+
impl io::Write for Stdout {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
self.write_vectored(&[IoSlice::new(data)])
}
}
+impl AsFd for Stderr {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw(2) }
+ }
+}
+
impl io::Write for Stderr {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
self.write_vectored(&[IoSlice::new(data)])
pub const IPV6_DROP_MEMBERSHIP: c_int = 13;
pub const MSG_PEEK: c_int = 0x2;
-pub const LOAD_LIBRARY_SEARCH_SYSTEM32: u32 = 0x800;
-
#[repr(C)]
#[derive(Copy, Clone)]
pub struct linger {
pub EndOfFile: LARGE_INTEGER,
}
+/// NB: Use carefully! In general using this as a reference is likely to get the
+/// provenance wrong for the `rest` field!
#[repr(C)]
pub struct REPARSE_DATA_BUFFER {
pub ReparseTag: c_uint,
pub rest: (),
}
+/// NB: Use carefully! In general using this as a reference is likely to get the
+/// provenance wrong for the `PathBuffer` field!
#[repr(C)]
pub struct SYMBOLIC_LINK_REPARSE_BUFFER {
pub SubstituteNameOffset: c_ushort,
pub PathBuffer: WCHAR,
}
+/// NB: Use carefully! In general using this as a reference is likely to get the
+/// provenance wrong for the `PathBuffer` field!
#[repr(C)]
pub struct MOUNT_POINT_REPARSE_BUFFER {
pub SubstituteNameOffset: c_ushort,
pub fn GetProcAddress(handle: HMODULE, name: LPCSTR) -> *mut c_void;
pub fn GetModuleHandleA(lpModuleName: LPCSTR) -> HMODULE;
pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
- pub fn LoadLibraryExA(lplibfilename: *const i8, hfile: HANDLE, dwflags: u32) -> HINSTANCE;
pub fn GetSystemTimeAsFileTime(lpSystemTimeAsFileTime: LPFILETIME);
pub fn GetSystemInfo(lpSystemInfo: LPSYSTEM_INFO);
}
// On 32-bit x86 MSVC these functions aren't defined, so we just define shims
-// which promote everything fo f64, perform the calculation, and then demote
+// which promote everything to f64, perform the calculation, and then demote
// back to f32. While not precisely correct should be "correct enough" for now.
#[cfg(all(target_env = "msvc", target_arch = "x86"))]
mod shims {
use crate::ffi::{c_void, CStr};
use crate::ptr::NonNull;
-use crate::sync::atomic::{AtomicBool, Ordering};
+use crate::sync::atomic::Ordering;
use crate::sys::c;
+// This uses a static initializer to preload some imported functions.
+// The CRT (C runtime) executes static initializers before `main`
+// is called (for binaries) and before `DllMain` is called (for DLLs).
+//
+// It works by contributing a global symbol to the `.CRT$XCT` section.
+// The linker builds a table of all static initializer functions.
+// The CRT startup code then iterates that table, calling each
+// initializer function.
+//
+// NOTE: User code should instead use .CRT$XCU to reliably run after std's initializer.
+// If you're reading this and would like a guarantee here, please
+// file an issue for discussion; currently we don't guarantee any functionality
+// before main.
+// See https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170
+#[used]
+#[link_section = ".CRT$XCT"]
+static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init;
+
+/// Preload some imported functions.
+///
+/// Note that any functions included here will be unconditionally loaded in
+/// the final binary, regardless of whether or not they're actually used.
+///
+/// Therefore, this should be limited to `compat_fn_optional` functions which
+/// must be preloaded or any functions where lazier loading demonstrates a
+/// negative performance impact in practical situations.
+///
+/// Currently we only preload `WaitOnAddress` and `WakeByAddressSingle`.
+unsafe extern "C" fn init() {
+ // In an exe this code is executed before main() so is single threaded.
+ // In a DLL the system's loader lock will be held thereby synchronizing
+ // access. So the same best practices apply here as they do to running in DllMain:
+ // https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices
+ //
+ // DO NOT do anything interesting or complicated in this function! DO NOT call
+ // any Rust functions or CRT functions if those functions touch any global state,
+ // because this function runs during global initialization. For example, DO NOT
+ // do any dynamic allocation, don't call LoadLibrary, etc.
+
+ // Attempt to preload the synch functions.
+ load_synch_functions();
+}
+
/// Helper macro for creating CStrs from literals and symbol names.
macro_rules! ansi_str {
(sym $ident:ident) => {{
NonNull::new(module).map(Self)
}
- /// Load the library (if not already loaded)
- ///
- /// # Safety
- ///
- /// The module must not be unloaded.
- pub unsafe fn load_system_library(name: &CStr) -> Option<Self> {
- let module = c::LoadLibraryExA(
- name.as_ptr(),
- crate::ptr::null_mut(),
- c::LOAD_LIBRARY_SEARCH_SYSTEM32,
- );
- NonNull::new(module).map(Self)
- }
-
// Try to get the address of a function.
pub fn proc_address(self, name: &CStr) -> Option<NonNull<c_void>> {
// SAFETY:
#[inline(always)]
pub fn option() -> Option<F> {
- let f = PTR.load(Ordering::Acquire);
- if !f.is_null() { Some(unsafe { mem::transmute(f) }) } else { try_load() }
- }
-
- #[cold]
- fn try_load() -> Option<F> {
- $load_functions;
- NonNull::new(PTR.load(Ordering::Acquire)).map(|f| unsafe { mem::transmute(f) })
+ // Miri does not understand the way we do preloading
+ // therefore load the function here instead.
+ #[cfg(miri)] $load_functions;
+ NonNull::new(PTR.load(Ordering::Relaxed)).map(|f| unsafe { mem::transmute(f) })
}
}
)+
// Try loading the library and all the required functions.
// If any step fails, then they all fail.
- let library = unsafe { Module::load_system_library(MODULE_NAME) }?;
+ let library = unsafe { Module::new(MODULE_NAME) }?;
let wait_on_address = library.proc_address(WAIT_ON_ADDRESS)?;
let wake_by_address_single = library.proc_address(WAKE_BY_ADDRESS_SINGLE)?;
- c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Release);
- c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Release);
+ c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Relaxed);
+ c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Relaxed);
Some(())
}
- // Try to load the module but skip loading if a previous attempt failed.
- static LOAD_MODULE: AtomicBool = AtomicBool::new(true);
- let module_loaded = LOAD_MODULE.load(Ordering::Acquire) && try_load().is_some();
- LOAD_MODULE.store(module_loaded, Ordering::Release)
+ try_load();
}
use crate::ffi::OsString;
use crate::fmt;
use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
-use crate::mem;
+use crate::mem::{self, MaybeUninit};
use crate::os::windows::io::{AsHandle, BorrowedHandle};
use crate::path::{Path, PathBuf};
use crate::ptr;
use crate::sync::Arc;
use crate::sys::handle::Handle;
use crate::sys::time::SystemTime;
-use crate::sys::{c, cvt};
+use crate::sys::{c, cvt, Align8};
use crate::sys_common::{AsInner, FromInner, IntoInner};
use crate::thread;
cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?;
let mut reparse_tag = 0;
if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
- let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ let mut b =
+ Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
if let Ok((_, buf)) = self.reparse_point(&mut b) {
- reparse_tag = buf.ReparseTag;
+ reparse_tag = (*buf).ReparseTag;
}
}
Ok(FileAttr {
attr.file_size = info.AllocationSize as u64;
attr.number_of_links = Some(info.NumberOfLinks);
if attr.file_type().is_reparse_point() {
- let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ let mut b =
+ Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
if let Ok((_, buf)) = self.reparse_point(&mut b) {
- attr.reparse_tag = buf.ReparseTag;
+ attr.reparse_tag = (*buf).ReparseTag;
}
}
Ok(attr)
Ok(Self { handle: self.handle.try_clone()? })
}
- fn reparse_point<'a>(
+ // NB: returned pointer is derived from `space`, and has provenance to
+ // match. A raw pointer is returned rather than a reference in order to
+ // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`.
+ fn reparse_point(
&self,
- space: &'a mut [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE],
- ) -> io::Result<(c::DWORD, &'a c::REPARSE_DATA_BUFFER)> {
+ space: &mut Align8<[MaybeUninit<u8>]>,
+ ) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> {
unsafe {
let mut bytes = 0;
cvt({
+ // Grab this in advance to avoid it invalidating the pointer
+ // we get from `space.0.as_mut_ptr()`.
+ let len = space.0.len();
c::DeviceIoControl(
self.handle.as_raw_handle(),
c::FSCTL_GET_REPARSE_POINT,
ptr::null_mut(),
0,
- space.as_mut_ptr() as *mut _,
- space.len() as c::DWORD,
+ space.0.as_mut_ptr().cast(),
+ len as c::DWORD,
&mut bytes,
ptr::null_mut(),
)
})?;
- Ok((bytes, &*(space.as_ptr() as *const c::REPARSE_DATA_BUFFER)))
+ const _: () = assert!(core::mem::align_of::<c::REPARSE_DATA_BUFFER>() <= 8);
+ Ok((bytes, space.0.as_ptr().cast::<c::REPARSE_DATA_BUFFER>()))
}
}
fn readlink(&self) -> io::Result<PathBuf> {
- let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ let mut space = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
let (_bytes, buf) = self.reparse_point(&mut space)?;
unsafe {
- let (path_buffer, subst_off, subst_len, relative) = match buf.ReparseTag {
+ let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
c::IO_REPARSE_TAG_SYMLINK => {
let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER =
- &buf.rest as *const _ as *const _;
+ ptr::addr_of!((*buf).rest).cast();
+ assert!(info.is_aligned());
(
- &(*info).PathBuffer as *const _ as *const u16,
+ ptr::addr_of!((*info).PathBuffer).cast::<u16>(),
(*info).SubstituteNameOffset / 2,
(*info).SubstituteNameLength / 2,
(*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0,
}
c::IO_REPARSE_TAG_MOUNT_POINT => {
let info: *const c::MOUNT_POINT_REPARSE_BUFFER =
- &buf.rest as *const _ as *const _;
+ ptr::addr_of!((*buf).rest).cast();
+ assert!(info.is_aligned());
(
- &(*info).PathBuffer as *const _ as *const u16,
+ ptr::addr_of!((*info).PathBuffer).cast::<u16>(),
(*info).SubstituteNameOffset / 2,
(*info).SubstituteNameLength / 2,
false,
/// A buffer for holding directory entries.
struct DirBuff {
- buffer: Vec<u8>,
+ buffer: Box<Align8<[MaybeUninit<u8>; Self::BUFFER_SIZE]>>,
}
impl DirBuff {
+ const BUFFER_SIZE: usize = 1024;
fn new() -> Self {
- const BUFFER_SIZE: usize = 1024;
- Self { buffer: vec![0_u8; BUFFER_SIZE] }
+ Self {
+ // Safety: `Align8<[MaybeUninit<u8>; N]>` does not need
+ // initialization.
+ buffer: unsafe { Box::new_uninit().assume_init() },
+ }
}
fn capacity(&self) -> usize {
- self.buffer.len()
+ self.buffer.0.len()
}
fn as_mut_ptr(&mut self) -> *mut u8 {
- self.buffer.as_mut_ptr().cast()
+ self.buffer.0.as_mut_ptr().cast()
}
/// Returns a `DirBuffIter`.
fn iter(&self) -> DirBuffIter<'_> {
DirBuffIter::new(self)
}
}
-impl AsRef<[u8]> for DirBuff {
- fn as_ref(&self) -> &[u8] {
- &self.buffer
+impl AsRef<[MaybeUninit<u8>]> for DirBuff {
+ fn as_ref(&self) -> &[MaybeUninit<u8>] {
+ &self.buffer.0
}
}
///
/// Currently only returns file names (UTF-16 encoded).
struct DirBuffIter<'a> {
- buffer: Option<&'a [u8]>,
+ buffer: Option<&'a [MaybeUninit<u8>]>,
cursor: usize,
}
impl<'a> DirBuffIter<'a> {
let buffer = &self.buffer?[self.cursor..];
// Get the name and next entry from the buffer.
- // SAFETY: The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the
- // last field (the file name) is unsized. So an offset has to be
- // used to get the file name slice.
+ // SAFETY:
+ // - The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the last
+ // field (the file name) is unsized. So an offset has to be used to
+ // get the file name slice.
+ // - The OS has guaranteed initialization of the fields of
+ // `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least
+ // `FileNameLength` bytes)
let (name, is_directory, next_entry) = unsafe {
let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();
+ // Guaranteed to be aligned in documentation for
+ // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info
+ assert!(info.is_aligned());
let next_entry = (*info).NextEntryOffset as usize;
let name = crate::slice::from_raw_parts(
- (*info).FileName.as_ptr().cast::<u16>(),
+ ptr::addr_of!((*info).FileName).cast::<u16>(),
(*info).FileNameLength as usize / size_of::<u16>(),
);
let is_directory = ((*info).FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) != 0;
let h = f.as_inner().as_raw_handle();
unsafe {
- let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
- let db = data.as_mut_ptr() as *mut c::REPARSE_MOUNTPOINT_DATA_BUFFER;
- let buf = &mut (*db).ReparseTarget as *mut c::WCHAR;
+ let mut data = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
+ let data_ptr = data.0.as_mut_ptr();
+ let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>();
+ let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>();
let mut i = 0;
// FIXME: this conversion is very hacky
let v = br"\??\";
cvt(c::DeviceIoControl(
h as *mut _,
c::FSCTL_SET_REPARSE_POINT,
- data.as_ptr() as *mut _,
+ data_ptr.cast(),
(*db).ReparseDataLength + 8,
ptr::null_mut(),
0,
}
crate::intrinsics::abort();
}
+
+/// Align the inner value to 8 bytes.
+///
+/// This is enough for almost all of the buffers we're likely to work with in
+/// the Windows APIs we use.
+#[repr(C, align(8))]
+#[derive(Copy, Clone)]
+pub(crate) struct Align8<T: ?Sized>(pub T);
assert_eq!(prefix, parse_prefix(r"\\?/C:\windows\system32\notepad.exe"));
}
-// See #93586 for more infomation.
+// See #93586 for more information.
#[test]
fn test_windows_prefix_components() {
use crate::path::Path;
use crate::char::decode_utf16;
use crate::cmp;
use crate::io;
+use crate::mem::MaybeUninit;
use crate::os::windows::io::{FromRawHandle, IntoRawHandle};
use crate::ptr;
use crate::str;
}
fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result<usize> {
- let mut utf16 = [0u16; MAX_BUFFER_SIZE / 2];
+ let mut utf16 = [MaybeUninit::<u16>::uninit(); MAX_BUFFER_SIZE / 2];
let mut len_utf16 = 0;
for (chr, dest) in utf8.encode_utf16().zip(utf16.iter_mut()) {
- *dest = chr;
+ *dest = MaybeUninit::new(chr);
len_utf16 += 1;
}
- let utf16 = &utf16[..len_utf16];
+ // Safety: We've initialized `len_utf16` values.
+ let utf16: &[u16] = unsafe { MaybeUninit::slice_assume_init_ref(&utf16[..len_utf16]) };
let mut written = write_u16s(handle, &utf16)?;
return Ok(bytes_copied);
} else if buf.len() - bytes_copied < 4 {
// Not enough space to get a UTF-8 byte. We will use the incomplete UTF8.
- let mut utf16_buf = [0u16; 1];
+ let mut utf16_buf = [MaybeUninit::new(0); 1];
// Read one u16 character.
let read = read_u16s_fixup_surrogates(handle, &mut utf16_buf, 1, &mut self.surrogate)?;
// Read bytes, using the (now-empty) self.incomplete_utf8 as extra space.
- let read_bytes = utf16_to_utf8(&utf16_buf[..read], &mut self.incomplete_utf8.bytes)?;
+ let read_bytes = utf16_to_utf8(
+ unsafe { MaybeUninit::slice_assume_init_ref(&utf16_buf[..read]) },
+ &mut self.incomplete_utf8.bytes,
+ )?;
// Read in the bytes from incomplete_utf8 until the buffer is full.
self.incomplete_utf8.len = read_bytes as u8;
bytes_copied += self.incomplete_utf8.read(&mut buf[bytes_copied..]);
Ok(bytes_copied)
} else {
- let mut utf16_buf = [0u16; MAX_BUFFER_SIZE / 2];
+ let mut utf16_buf = [MaybeUninit::<u16>::uninit(); MAX_BUFFER_SIZE / 2];
+
// In the worst case, a UTF-8 string can take 3 bytes for every `u16` of a UTF-16. So
// we can read at most a third of `buf.len()` chars and uphold the guarantee no data gets
// lost.
let amount = cmp::min(buf.len() / 3, utf16_buf.len());
let read =
read_u16s_fixup_surrogates(handle, &mut utf16_buf, amount, &mut self.surrogate)?;
-
- match utf16_to_utf8(&utf16_buf[..read], buf) {
+ // Safety `read_u16s_fixup_surrogates` returns the number of items
+ // initialized.
+ let utf16s = unsafe { MaybeUninit::slice_assume_init_ref(&utf16_buf[..read]) };
+ match utf16_to_utf8(utf16s, buf) {
Ok(value) => return Ok(bytes_copied + value),
Err(e) => return Err(e),
}
// This is a best effort, and might not work if we are not the only reader on Stdin.
fn read_u16s_fixup_surrogates(
handle: c::HANDLE,
- buf: &mut [u16],
+ buf: &mut [MaybeUninit<u16>],
mut amount: usize,
surrogate: &mut u16,
) -> io::Result<usize> {
// Insert possibly remaining unpaired surrogate from last read.
let mut start = 0;
if *surrogate != 0 {
- buf[0] = *surrogate;
+ buf[0] = MaybeUninit::new(*surrogate);
*surrogate = 0;
start = 1;
if amount == 1 {
let mut amount = read_u16s(handle, &mut buf[start..amount])? + start;
if amount > 0 {
- let last_char = buf[amount - 1];
+ // Safety: The returned `amount` is the number of values initialized,
+ // and it is not 0, so we know that `buf[amount - 1]` have been
+ // initialized.
+ let last_char = unsafe { buf[amount - 1].assume_init() };
if last_char >= 0xD800 && last_char <= 0xDBFF {
// high surrogate
*surrogate = last_char;
Ok(amount)
}
-fn read_u16s(handle: c::HANDLE, buf: &mut [u16]) -> io::Result<usize> {
+// Returns `Ok(n)` if it initialized `n` values in `buf`.
+fn read_u16s(handle: c::HANDLE, buf: &mut [MaybeUninit<u16>]) -> io::Result<usize> {
// Configure the `pInputControl` parameter to not only return on `\r\n` but also Ctrl-Z, the
// traditional DOS method to indicate end of character stream / user input (SUB).
// See #38274 and https://stackoverflow.com/questions/43836040/win-api-readconsole.
}
break;
}
-
- if amount > 0 && buf[amount as usize - 1] == CTRL_Z {
+ // Safety: if `amount > 0`, then that many bytes were written, so
+ // `buf[amount as usize - 1]` has been initialized.
+ if amount > 0 && unsafe { buf[amount as usize - 1].assume_init() } == CTRL_Z {
amount -= 1;
}
Ok(amount as usize)
option = "-#"
else:
option = "-s"
- # If curl is not present on Win32, we shoud not sys.exit
+ # If curl is not present on Win32, we should not sys.exit
# but raise `CalledProcessError` or `OSError` instead
require(["curl", "--version"], exception=platform_is_win32)
run(["curl", option,
def check_vendored_status(self):
"""Check that vendoring is configured properly"""
+ # keep this consistent with the equivalent check in rustbuild:
+ # https://github.com/rust-lang/rust/blob/a8a33cf27166d3eabaffc58ed3799e054af3b0c6/src/bootstrap/lib.rs#L399-L405
if 'SUDO_USER' in os.environ and not self.use_vendored_sources:
if os.getuid() == 0:
self.use_vendored_sources = True
};
patchelf.args(&[OsString::from("--set-rpath"), rpath_entries]);
if !fname.extension().map_or(false, |ext| ext == "so") {
- // Finally, set the corret .interp for binaries
+ // Finally, set the correct .interp for binaries
let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker");
// FIXME: can we support utf8 here? `args` doesn't accept Vec<u8>, only OsString ...
let dynamic_linker = t!(String::from_utf8(t!(fs::read(dynamic_linker_path))));
let tempfile = self.tempdir().join(dest_path.file_name().unwrap());
// While bootstrap itself only supports http and https downloads, downstream forks might
// need to download components from other protocols. The match allows them adding more
- // protocols without worrying about merge conficts if we change the HTTP implementation.
+ // protocols without worrying about merge conflicts if we change the HTTP implementation.
match url.split_once("://").map(|(proto, _)| proto) {
Some("http") | Some("https") => {
self.download_http_with_retries(&tempfile, url, help_on_error)
pub struct Target {
/// Some(path to llvm-config) if using an external LLVM.
pub llvm_config: Option<PathBuf>,
+ pub llvm_has_rust_patches: Option<bool>,
/// Some(path to FileCheck) if one was specified.
pub llvm_filecheck: Option<PathBuf>,
pub llvm_libunwind: Option<LlvmLibunwind>,
default_linker: Option<PathBuf> = "default-linker",
linker: Option<String> = "linker",
llvm_config: Option<String> = "llvm-config",
+ llvm_has_rust_patches: Option<bool> = "llvm-has-rust-patches",
llvm_filecheck: Option<String> = "llvm-filecheck",
llvm_libunwind: Option<String> = "llvm-libunwind",
android_ndk: Option<String> = "android-ndk",
if let Some(ref s) = cfg.llvm_config {
target.llvm_config = Some(config.src.join(s));
}
+ target.llvm_has_rust_patches = cfg.llvm_has_rust_patches;
if let Some(ref s) = cfg.llvm_filecheck {
target.llvm_filecheck = Some(config.src.join(s));
}
use std::process::Command;
use std::str;
+use config::Target;
use filetime::FileTime;
use once_cell::sync::OnceCell;
let src = config.src.clone();
let out = config.out.clone();
+ #[cfg(unix)]
+ // keep this consistent with the equivalent check in x.py:
+ // https://github.com/rust-lang/rust/blob/a8a33cf27166d3eabaffc58ed3799e054af3b0c6/src/bootstrap/bootstrap.py#L796-L797
let is_sudo = match env::var_os("SUDO_USER") {
- Some(sudo_user) => match env::var_os("USER") {
- Some(user) => user != sudo_user,
- None => false,
- },
+ Some(_sudo_user) => {
+ let uid = unsafe { libc::getuid() };
+ uid == 0
+ }
None => false,
};
+ #[cfg(not(unix))]
+ let is_sudo = false;
let ignore_git = config.ignore_git;
let rust_info = channel::GitInfo::new(ignore_git, &src);
let rust_submodules = [
"src/tools/rust-installer",
"src/tools/cargo",
- "src/tools/rls",
"src/tools/miri",
"library/backtrace",
"library/stdarch",
///
/// If no custom `llvm-config` was specified then Rust's llvm will be used.
fn is_rust_llvm(&self, target: TargetSelection) -> bool {
- if self.config.llvm_from_ci && target == self.config.build {
- return true;
- }
-
match self.config.target_config.get(&target) {
- Some(ref c) => c.llvm_config.is_none(),
+ Some(Target { llvm_has_rust_patches: Some(patched), .. }) => *patched,
+ Some(Target { llvm_config, .. }) => {
+ // If the user set llvm-config we assume Rust is not patched,
+ // but first check to see if it was configured by llvm-from-ci.
+ (self.config.llvm_from_ci && target == self.config.build) || llvm_config.is_none()
+ }
None => true,
}
}
/// If code is not 0 (successful exit status), exit status is 101 (rust's default error code.)
/// If the test is running and code is an error code, it will cause a panic.
fn detail_exit(code: i32) -> ! {
- // if in test and code is an error code, panic with staus code provided
+ // if in test and code is an error code, panic with status code provided
if cfg!(test) && code != 0 {
panic!("status code: {}", code);
} else {
cfg.define("LLVM_LINK_LLVM_DYLIB", "ON");
}
- if target.starts_with("riscv") && !target.contains("freebsd") {
+ if target.starts_with("riscv") && !target.contains("freebsd") && !target.contains("openbsd")
+ {
// RISC-V GCC erroneously requires linking against
// `libatomic` when using 1-byte and 2-byte C++
// atomics but the LLVM build system check cannot
// detect this. Therefore it is set manually here.
- // FreeBSD uses Clang as its system compiler and
+ // Some BSD uses Clang as its system compiler and
// provides no libatomic in its base system so does
// not want this.
ldflags.exe.push(" -latomic");
if target.contains("darwin") {
// Make sure that CMake does not build universal binaries on macOS.
- // Explicitly specifiy the one single target architecture.
+ // Explicitly specify the one single target architecture.
if target.starts_with("aarch64") {
// macOS uses a different name for building arm64
cfg.define("CMAKE_OSX_ARCHITECTURES", "arm64");
.output()
.map(|output| String::from_utf8_lossy(&output.stdout).into_owned())
.unwrap_or(String::new());
- lines.lines().find_map(|l| l.split(":browser-ui-test@").skip(1).next()).map(|v| v.to_owned())
+ lines
+ .lines()
+ .find_map(|l| l.split(':').nth(1)?.strip_prefix("browser-ui-test@"))
+ .map(|v| v.to_owned())
}
fn get_browser_ui_test_version(npm: &Path) -> Option<String> {
-FROM ubuntu:16.04
+FROM ubuntu:22.04
+ARG DEBIAN_FRONTEND=noninteractive
COPY scripts/android-base-apt-get.sh /scripts/
RUN sh /scripts/android-base-apt-get.sh
libgl1-mesa-glx \
libpulse0 \
libstdc++6:i386 \
- openjdk-9-jre-headless \
+ openjdk-8-jre-headless \
tzdata \
wget \
python3
ENV TARGETS=arm-linux-androideabi
-# We are intentionally allowing an old toolchain on this builder (and that's
-# incompatible with LLVM downloads today).
-ENV NO_DOWNLOAD_CI_LLVM 1
-
-ENV RUST_CONFIGURE_ARGS --arm-linux-androideabi-ndk=/android/ndk/arm-14 \
- --set llvm.allow-old-toolchain
+ENV RUST_CONFIGURE_ARGS --arm-linux-androideabi-ndk=/android/ndk/arm-14
ENV SCRIPT python3 ../x.py --stage 2 test --host='' --target $TARGETS
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
-COPY scripts/cmake.sh /scripts/
-RUN /scripts/cmake.sh
-
COPY scripts/android-start-emulator.sh /scripts/
ENTRYPOINT ["/scripts/android-start-emulator.sh"]
-FROM ubuntu:16.04
+FROM ubuntu:22.04
COPY scripts/android-base-apt-get.sh /scripts/
RUN sh /scripts/android-base-apt-get.sh
--i686-linux-android-ndk=/android/ndk/x86-14 \
--aarch64-linux-android-ndk=/android/ndk/arm64-21 \
--x86_64-linux-android-ndk=/android/ndk/x86_64-21 \
- --disable-docs \
- --set llvm.allow-old-toolchain
+ --disable-docs
ENV SCRIPT python3 ../x.py dist --host='' --target $TARGETS
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
-
-COPY scripts/cmake.sh /scripts/
-RUN /scripts/cmake.sh
-FROM ubuntu:16.04
+FROM ubuntu:22.04
+ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
g++-multilib \
make \
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
-COPY scripts/cmake.sh /scripts/
-RUN /scripts/cmake.sh
-
RUN mkdir -p /config
RUN echo "[rust]" > /config/nopt-std-config.toml
RUN echo "optimize = false" >> /config/nopt-std-config.toml
-# We are intentionally allowing an old toolchain on this builder (and that's
-# incompatible with LLVM downloads today).
-ENV NO_DOWNLOAD_CI_LLVM 1
-
-ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu --disable-optimize-tests \
- --set llvm.allow-old-toolchain
+ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu --disable-optimize-tests
ENV SCRIPT python3 ../x.py test --stage 0 --config /config/nopt-std-config.toml library/std \
&& python3 ../x.py --stage 2 test
-FROM ubuntu:16.04
+FROM ubuntu:22.04
+ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
g++-multilib \
make \
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
-COPY scripts/cmake.sh /scripts/
-RUN /scripts/cmake.sh
-
-# We are intentionally allowing an old toolchain on this builder (and that's
-# incompatible with LLVM downloads today).
-ENV NO_DOWNLOAD_CI_LLVM 1
-ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu \
- --set llvm.allow-old-toolchain
+ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu
# Exclude some tests that are unlikely to be platform specific, to speed up
# this slow job.
ENV SCRIPT python3 ../x.py --stage 2 test \
-FROM ubuntu:16.04
+FROM ubuntu:22.04
+ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
make \
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
-COPY scripts/cmake.sh /scripts/
-RUN /scripts/cmake.sh
-
-# We are intentionally allowing an old toolchain on this builder (and that's
-# incompatible with LLVM downloads today).
-ENV NO_DOWNLOAD_CI_LLVM 1
-
-ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu \
- --set llvm.allow-old-toolchain
+ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu
ENV RUST_CHECK_TARGET check-aux
-FROM ubuntu:16.04
+FROM ubuntu:22.04
+ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
make \
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
-COPY scripts/cmake.sh /scripts/
-RUN /scripts/cmake.sh
-
-# We are intentionally allowing an old toolchain on this builder (and that's
-# incompatible with LLVM downloads today).
+# We are disabling CI LLVM since distcheck is an offline build.
ENV NO_DOWNLOAD_CI_LLVM 1
-ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --set rust.ignore-git=false \
- --set llvm.allow-old-toolchain
+ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --set rust.ignore-git=false
ENV SCRIPT python3 ../x.py --stage 2 test distcheck
ENV DIST_SRC 1
-FROM ubuntu:16.04
+FROM ubuntu:22.04
+ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
make \
libdbus-1-3 \
libexpat1 \
libfontconfig1 \
+ libgbm1 \
libgcc1 \
libgconf-2-4 \
libgdk-pixbuf2.0-0 \
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
-COPY scripts/cmake.sh /scripts/
-RUN /scripts/cmake.sh
-
COPY host-x86_64/x86_64-gnu-tools/checktools.sh /tmp/
-RUN curl -sL https://nodejs.org/dist/v14.4.0/node-v14.4.0-linux-x64.tar.xz | tar -xJ
-ENV NODE_FOLDER=/node-v14.4.0-linux-x64/bin
+RUN curl -sL https://nodejs.org/dist/v14.20.0/node-v14.20.0-linux-x64.tar.xz | tar -xJ
+ENV NODE_FOLDER=/node-v14.20.0-linux-x64/bin
ENV PATH="$NODE_FOLDER:${PATH}"
COPY host-x86_64/x86_64-gnu-tools/browser-ui-test.version /tmp/
# the local version of the package is different than the one used by the CI.
RUN npm install -g browser-ui-test@$(head -n 1 /tmp/browser-ui-test.version) --unsafe-perm=true
-# We are intentionally allowing an old toolchain on this builder (and that's
-# incompatible with LLVM downloads today).
-ENV NO_DOWNLOAD_CI_LLVM 1
-
ENV RUST_CONFIGURE_ARGS \
- --set llvm.allow-old-toolchain \
--build=x86_64-unknown-linux-gnu \
--save-toolstates=/tmp/toolstate/toolstates.json
ENV SCRIPT /tmp/checktools.sh ../x.py && \
- NODE_PATH=`npm root -g` python3 ../x.py test src/test/rustdoc-gui --stage 2
+ NODE_PATH=`npm root -g` python3 ../x.py test src/test/rustdoc-gui --stage 2 \
+ --test-args "'--no-sandbox --jobs 1'"
g++ \
git \
libssl-dev \
+ libncurses5 \
make \
ninja-build \
pkg-config \
-Subproject commit 42ca0ef484fcc8437a0682cee23abe4b7c407d52
+Subproject commit 0a5421ceb238357b3634fb75234eba4d1dad643c
-Subproject commit 8e6aa3448515a0654e347b5e2510f1d4bc4d5a64
+Subproject commit d880e6ac2acf133dce640da24b9fb692844f02d4
-Subproject commit e647eb102890e8927f488bea12672b079eff8d9d
+Subproject commit f62e93c28323ed9637d0a205a0c256498674a509
-Subproject commit d3daa1f28e169087becbc5e2b49ac91ca0405a44
+Subproject commit 04892c1a6fc145602ac7367945fda9d4ee83c9fb
`powerpc64-unknown-linux-musl` | ? | |
`powerpc64-wrs-vxworks` | ? | |
`powerpc64le-unknown-linux-musl` | ? | |
+[`powerpc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/powerpc64
`riscv32gc-unknown-linux-gnu` | | | RISC-V Linux (kernel 5.4, glibc 2.33)
`riscv32gc-unknown-linux-musl` | | | RISC-V Linux (kernel 5.4, musl + RISCV32 support patches)
`riscv32im-unknown-none-elf` | * | | Bare RISC-V (RV32IM ISA)
`riscv32imc-esp-espidf` | ✓ | | RISC-V ESP-IDF
`riscv64gc-unknown-freebsd` | | | RISC-V FreeBSD
`riscv64gc-unknown-linux-musl` | | | RISC-V Linux (kernel 4.20, musl 1.2.0)
+[`riscv64gc-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/riscv64
`s390x-unknown-linux-musl` | | | S390x Linux (kernel 3.2, MUSL)
`sparc-unknown-linux-gnu` | ✓ | | 32-bit SPARC Linux
`sparc64-unknown-netbsd` | ✓ | ✓ | NetBSD/sparc64
--- /dev/null
+# armv4t-none-eabi
+
+Tier 3
+
+Bare-metal target for any cpu in the ARMv4T architecture family, supporting
+ARM/Thumb code interworking (aka `a32`/`t32`), with ARM code as the default code
+generation.
+
+In particular this supports the Gameboy Advance (GBA), but there's nothing GBA
+specific with this target, so any ARMv4T device should work fine.
+
+## Target Maintainers
+
+* [@Lokathor](https://github.com/lokathor)
+
+## Requirements
+
+The target is cross-compiled, and uses static linking.
+
+The linker that comes with rustc cannot link for this platform (the platform is
+too old). You will need the `arm-none-eabi-ld` linker from a GNU Binutils
+targeting ARM. This can be obtained for Windows/Mac/Linux from the [ARM
+Developer Website][arm-dev], or possibly from your OS's package manager.
+
+[arm-dev]: https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain
+
+This target doesn't provide a linker script, you'll need to bring your own
+according to the specific device you want to target. Pass
+`-Clink-arg=-Tyour_script.ld` as a rustc argument to make the linker use
+`your_script.ld` during linking.
+
+## Building Rust Programs
+
+Because it is Tier 3, rust does not yet ship pre-compiled artifacts for this target.
+
+Just use the `build-std` nightly cargo feature to build the `core` library. You
+can pass this as a command line argument to cargo, or your `.cargo/config.toml`
+file might include the following lines:
+
+```toml
+[unstable]
+build-std = ["core"]
+```
+
+Most of `core` should work as expected, with the following notes:
+* the target is "soft float", so `f32` and `f64` operations are emulated in
+ software.
+* integer division is also emulated in software.
+* the target is old enough that it doesn't have atomic instructions.
+
+Rust programs are output as ELF files.
+
+For running on hardware, you'll generally need to extract the "raw" program code
+out of the ELF and into a file of its own. The `objcopy` program provided as
+part of the GNU Binutils can do this:
+
+```shell
+arm-none-eabi-objcopy --output-target binary [in_file] [out_file]
+```
+
+## Testing
+
+This is a cross-compiled target that you will need to emulate during testing.
+
+Because this is a device-agnostic target, and the exact emulator that you'll
+need depends on the specific device you want to run your code on.
+
+For example, when programming for the Gameboy Advance, the
+[mgba-test-runner](https://github.com/agbrs/agb) program could be used to make a
+normal set of rust tests be run within the `mgba` emulator.
+++ /dev/null
-# armv4t-none-eabi
-
-Tier 3
-
-Bare-metal target for any cpu in the ARMv4T architecture family, supporting
-ARM/Thumb code interworking (aka `a32`/`t32`), with ARM code as the default code
-generation.
-
-In particular this supports the Gameboy Advance (GBA), but there's nothing GBA
-specific with this target, so any ARMv4T device should work fine.
-
-## Target Maintainers
-
-* [@Lokathor](https://github.com/lokathor)
-
-## Requirements
-
-The target is cross-compiled, and uses static linking.
-
-The linker that comes with rustc cannot link for this platform (the platform is
-too old). You will need the `arm-none-eabi-ld` linker from a GNU Binutils
-targeting ARM. This can be obtained for Windows/Mac/Linux from the [ARM
-Developer Website][arm-dev], or possibly from your OS's package manager.
-
-[arm-dev]: https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain
-
-This target doesn't provide a linker script, you'll need to bring your own
-according to the specific device you want to target. Pass
-`-Clink-arg=-Tyour_script.ld` as a rustc argument to make the linker use
-`your_script.ld` during linking.
-
-## Building Rust Programs
-
-Because it is Tier 3, rust does not yet ship pre-compiled artifacts for this target.
-
-Just use the `build-std` nightly cargo feature to build the `core` library. You
-can pass this as a command line argument to cargo, or your `.cargo/config.toml`
-file might include the following lines:
-
-```toml
-[unstable]
-build-std = ["core"]
-```
-
-Most of `core` should work as expected, with the following notes:
-* the target is "soft float", so `f32` and `f64` operations are emulated in
- software.
-* integer division is also emulated in software.
-* the target is old enough that it doesn't have atomic instructions.
-
-Rust programs are output as ELF files.
-
-For running on hardware, you'll generally need to extract the "raw" program code
-out of the ELF and into a file of its own. The `objcopy` program provided as
-part of the GNU Binutils can do this:
-
-```shell
-arm-none-eabi-objcopy --output-target binary [in_file] [out_file]
-```
-
-## Testing
-
-This is a cross-compiled target that you will need to emulate during testing.
-
-Because this is a device-agnostic target, and the exact emulator that you'll
-need depends on the specific device you want to run your code on.
-
-For example, when programming for the Gameboy Advance, the
-[mgba-test-runner](https://github.com/agbrs/agb) program could be used to make a
-normal set of rust tests be run within the `mgba` emulator.
[Fuchsia] is a modern open source operating system that's simple, secure,
updatable, and performant.
-[Fuchsia]: https://fuchsia.dev/
-
## Target maintainers
The [Fuchsia team]:
-[Fuchsia team]: https://team-api.infra.rust-lang.org/v1/teams/fuchsia.json
-
- Tyler Mandry ([@tmandry](https://github.com/tmandry))
- Dan Johnson ([@computerdruid](https://github.com/computerdruid))
- David Koloski ([@djkoloski](https://github.com/djkoloski))
authoritative if this occurs. Instead of pinging individual members, use
`@rustbot ping fuchsia` to contact the team on GitHub.
+## Table of contents
+
+1. [Requirements](#requirements)
+1. [Walkthrough structure](#walkthrough-structure)
+1. [Compiling a Rust binary targeting Fuchsia](#compiling-a-rust-binary-targeting-fuchsia)
+ 1. [Targeting Fuchsia with rustup and cargo](#targeting-fuchsia-with-rustup-and-cargo)
+ 1. [Targeting Fuchsia with a compiler built from source](#targeting-fuchsia-with-a-compiler-built-from-source)
+1. [Creating a Fuchsia package](#creating-a-fuchsia-package)
+ 1. [Creating a Fuchsia component](#creating-a-fuchsia-component)
+ 1. [Building a Fuchsia package](#building-a-fuchsia-package)
+1. [Publishing a Fuchsia package](#publishing-a-fuchsia-package)
+ 1. [Creating a Fuchsia package repository](#creating-a-fuchsia-package-repository)
+ 1. [Publishing Fuchsia package to repository](#publishing-fuchsia-package-to-repository)
+1. [Running a Fuchsia component on an emulator](#running-a-fuchsia-component-on-an-emulator)
+ 1. [Starting the Fuchsia emulator](#starting-the-fuchsia-emulator)
+ 1. [Watching emulator logs](#watching-emulator-logs)
+ 1. [Serving a Fuchsia package](#serving-a-fuchsia-package)
+ 1. [Running a Fuchsia component](#running-a-fuchsia-component)
+1. [`.gitignore` extensions](#gitignore-extensions)
+1. [Testing](#testing)
+ 1. [Running unit tests](#running-unit-tests)
+ 1. [Running the compiler test suite](#running-the-compiler-test-suite)
+
## Requirements
-This target is cross-compiled from a host environment. Development may be done
-from the [source tree] or using the Fuchsia SDK.
+This target is cross-compiled from a host environment. You will need a recent
+copy of the [Fuchsia SDK], which provides the tools, libraries, and binaries
+required to build and link programs for Fuchsia.
-[source tree]: https://fuchsia.dev/fuchsia-src/get-started/learn/build
+Development may also be done from the [source tree].
-Fuchsia targets support std and follow the `sysv64` calling convention on
+Fuchsia targets support `std` and follow the `sysv64` calling convention on
x86_64. Fuchsia binaries use the ELF file format.
-## Building the target
+## Walkthrough structure
+
+This walkthrough will cover:
+
+1. Compiling a Rust binary targeting Fuchsia.
+1. Building a Fuchsia package.
+1. Publishing and running a Fuchsia package to a Fuchsia emulator.
+
+For the purposes of this walkthrough, we will only target `x86_64-fuchsia`.
+
+## Compiling a Rust binary targeting Fuchsia
+
+Today, there are two main ways to build a Rust binary targeting Fuchsia
+using the Fuchsia SDK:
+1. Allow [rustup] to handle the installation of Fuchsia targets for you.
+1. Build a toolchain locally that can target Fuchsia.
+
+### Targeting Fuchsia with rustup and cargo
+
+The easiest way to build a Rust binary targeting Fuchsia is by allowing [rustup]
+to handle the installation of Fuchsia targets for you. This can be done by issuing
+the following commands:
+
+```sh
+rustup target add x86_64-fuchsia
+rustup target add aarch64-fuchsia
+```
+
+After installing our Fuchsia targets, we can now compile a Rust binary that targets
+Fuchsia.
+
+To create our Rust project, we can issue a standard `cargo` command as follows:
+
+**From base working directory**
+```sh
+cargo new hello_fuchsia
+```
+
+The rest of this walkthrough will take place from `hello_fuchsia`, so we can
+change into that directory now:
+
+```sh
+cd hello_fuchsia
+```
+
+*Note: From this point onwards, all commands will be issued from the `hello_fuchsia/`
+directory, and all `hello_fuchsia/` prefixes will be removed from references for sake of brevity.*
+
+We can edit our `src/main.rs` to include a test as follows:
+
+**`src/main.rs`**
+```rust
+fn main() {
+ println!("Hello Fuchsia!");
+}
+
+#[test]
+fn it_works() {
+ assert_eq!(2 + 2, 4);
+}
+```
+
+In addition to the standard workspace created, we will want to create a
+`.cargo/config.toml` file to link necessary libraries
+during compilation:
+
+**`.cargo/config.toml`**
+```txt
+[target.x86_64-fuchsia]
+
+rustflags = [
+ "-Lnative=<SDK_PATH>/arch/x64/lib",
+ "-Lnative=<SDK_PATH>/arch/x64/sysroot/lib"
+]
+```
+
+*Note: Make sure to fill out `<SDK_PATH>` with the path to the downloaded [Fuchsia SDK].*
+
+These options configure the following:
+
+* `-Lnative=${SDK_PATH}/arch/${ARCH}/lib`: Link against Fuchsia libraries from
+ the SDK
+* `-Lnative=${SDK_PATH}/arch/${ARCH}/sysroot/lib`: Link against Fuchsia kernel
+ libraries from the SDK
+
+In total, our new project will look like:
+
+**Current directory structure**
+```txt
+hello_fuchsia/
+┣━ src/
+┃ ┗━ main.rs
+┣━ Cargo.toml
+┗━ .cargo/
+ ┗━ config.toml
+```
+
+Finally, we can build our rust binary as:
+
+```sh
+cargo build --target x86_64-fuchsia
+```
+
+Now we have a Rust binary at `target/x86_64-fuchsia/debug/hello_fuchsia`,
+targeting our desired Fuchsia target.
+
+**Current directory structure**
+```txt
+hello_fuchsia/
+┣━ src/
+┃ ┗━ main.rs
+┣━ target/
+┃ ┗━ x86_64-fuchsia/
+┃ ┗━ debug/
+┃ ┗━ hello_fuchsia
+┣━ Cargo.toml
+┗━ .cargo/
+ ┗━ config.toml
+```
+
+### Targeting Fuchsia with a compiler built from source
+
+An alternative to the first workflow is to target Fuchsia by using
+`rustc` built from source.
Before building Rust for Fuchsia, you'll need a clang toolchain that supports
Fuchsia as well. A recent version (14+) of clang should be sufficient to compile
Rust for Fuchsia.
-You'll also need a recent copy of the [Fuchsia SDK], which provides the tools
-and binaries required to build and link programs for Fuchsia.
-
-[Fuchsia SDK]: https://chrome-infra-packages.appspot.com/p/fuchsia/sdk/core
-
x86-64 and AArch64 Fuchsia targets can be enabled using the following
configuration.
These can be run together in a shell environment by executing
`(source config-env.sh && ./x.py install)`.
-## Building Rust programs
+Once `rustc` is installed, we can create a new working directory to work from,
+`hello_fuchsia` along with `hello_fuchsia/src`:
+
+```sh
+mkdir hello_fuchsia
+cd hello_fuchsia
+mkdir src
+```
-After compiling Rust binaries, you'll need to build a component, package it, and
-serve it to a Fuchsia device or emulator. All of this can be done using the
-Fuchsia SDK.
+*Note: From this point onwards, all commands will be issued from the `hello_fuchsia/`
+directory, and all `hello_fuchsia/` prefixes will be removed from references for sake of brevity.*
-As an example, we'll compile and run this simple program on a Fuchsia emulator:
+There, we can create a new file named `src/hello_fuchsia.rs`:
-**`hello_fuchsia.rs`**
+**`src/hello_fuchsia.rs`**
```rust
fn main() {
println!("Hello Fuchsia!");
}
```
-Create a new file named `hello_fuchsia.rs` and fill out its contents with that
-code.
+**Current directory structure**
+```txt
+hello_fuchsia/
+┗━ src/
+ ┗━ hello_fuchsia.rs
+```
+
+Using your freshly installed `rustc`, you can compile a binary for Fuchsia using
+the following options:
+
+* `--target x86_64-fuchsia`/`--target aarch64-fuchsia`: Targets the Fuchsia
+ platform of your choice
+* `-Lnative ${SDK_PATH}/arch/${ARCH}/lib`: Link against Fuchsia libraries from
+ the SDK
+* `-Lnative ${SDK_PATH}/arch/${ARCH}/sysroot/lib`: Link against Fuchsia kernel
+ libraries from the SDK
+
+Putting it all together:
+
+```sh
+# Configure these for the Fuchsia target of your choice
+TARGET_ARCH="<x86_64-fuchsia|aarch64-fuchsia>"
+ARCH="<x64|aarch64>"
+
+rustc \
+ --target ${TARGET_ARCH} \
+ -Lnative=${SDK_PATH}/arch/${ARCH}/lib \
+ -Lnative=${SDK_PATH}/arch/${ARCH}/sysroot/lib \
+ --out-dir bin src/hello_fuchsia.rs
+```
+
+**Current directory structure**
+```txt
+hello_fuchsia/
+┣━ src/
+┃ ┗━ hello_fuchsia.rs
+┗━ bin/
+ ┗━ hello_fuchsia
+```
-### Create a package
+## Creating a Fuchsia package
+Before moving on, double check your directory structure:
+
+**Current directory structure**
+```txt
+hello_fuchsia/
+┣━ src/ (if using rustc)
+┃ ┗━ hello_fuchsia.rs ...
+┣━ bin/ ...
+┃ ┗━ hello_fuchsia ...
+┣━ src/ (if using cargo)
+┃ ┗━ main.rs ...
+┗━ target/ ...
+ ┗━ x86_64-fuchsia/ ...
+ ┗━ debug/ ...
+ ┗━ hello_fuchsia ...
+```
+
+With our Rust binary built, we can move to creating a Fuchsia package.
On Fuchsia, a package is the unit of distribution for software. We'll need to
create a new package directory where we will place files like our finished
-binary and any data it may need. The working directory will have this layout:
+binary and any data it may need.
+
+To start, make the `pkg`, and `pkg/meta` directories:
+
+```sh
+mkdir pkg
+mkdir pkg/meta
+```
+**Current directory structure**
```txt
-hello_fuchsia.rs
-hello_fuchsia.cml
-package
-┣━ bin
-┃ ┗━ hello_fuchsia
-┣━ meta
-┃ ┣━ package
-┃ ┗━ hello_fuchsia.cm
-┗━ hello_fuchsia.manifest
+hello_fuchsia/
+┗━ pkg/
+ ┗━ meta/
```
-Make the `package`, `package/bin`, and `package/meta` directories and create the
-following files inside:
+Now, create the following files inside:
-**`package/meta/package`**
+**`pkg/meta/package`**
```json
{
"name": "hello_fuchsia",
The `package` file describes our package's name and version number. Every
package must contain one.
-**`package/hello_fuchsia.manifest`**
+**`pkg/hello_fuchsia.manifest`**
```txt
-bin/hello_fuchsia=package/bin/hello_fuchsia
+bin/hello_fuchsia=target/x86_64-fuchsia/debug/hello_fuchsia # If using cargo...
+bin/hello_fuchsia=bin/hello_fuchsia # If using rustc...
lib/ld.so.1=<SDK_PATH>/arch/x64/sysroot/dist/lib/ld.so.1
lib/libfdio.so=<SDK_PATH>/arch/x64/dist/libfdio.so
-meta/package=package/meta/package
-meta/hello_fuchsia.cm=package/meta/hello_fuchsia.cm
+meta/package=pkg/meta/package
+meta/hello_fuchsia.cm=pkg/meta/hello_fuchsia.cm
```
*Note: Relative manifest paths are resolved starting from the working directory
SDK.*
The `.manifest` file will be used to describe the contents of the package by
-relating their location when installed to their location on the file system. You
-can use this to make a package pull files from other places, but for this
-example we'll just be placing everything in the `package` directory.
-
-### Compiling a binary
+relating their location when installed to their location on the file system. The
+`bin/hello_fuchsia=` entry will be different depending on how your Rust binary
+was built, so choose accordingly.
-Using your freshly compiled `rustc`, you can compile a binary for Fuchsia using
-the following options:
-
-* `--target x86_64-fuchsia`/`--target aarch64-fuchsia`: Targets the Fuchsia
- platform of your choice
-* `-Lnative ${SDK_PATH}/arch/${ARCH}/lib`: Link against Fuchsia libraries from
- the SDK
-* `-Lnative ${SDK_PATH}/arch/${ARCH}/sysroot/lib`: Link against Fuchsia kernel
- libraries from the SDK
-
-Putting it all together:
-
-```sh
-# Configure these for the Fuchsia target of your choice
-TARGET_ARCH="<x86_64-fuchsia|aarch64-fuchsia>"
-ARCH="<x64|aarch64>"
-
-rustc --target ${TARGET_ARCH} -Lnative=${SDK_PATH}/arch/${ARCH}/lib -Lnative=${SDK_PATH}/arch/${ARCH}/sysroot/lib -o package/bin/hello_fuchsia hello_fuchsia.rs
+**Current directory structure**
+```txt
+hello_fuchsia/
+┗━ pkg/
+ ┣━ meta/
+ ┃ ┗━ package
+ ┗━ hello_fuchsia.manifest
```
-### Bulding a component
+### Creating a Fuchsia component
-On Fuchsia, components require a component manifest written in Fuchia's markup
+On Fuchsia, components require a component manifest written in Fuchsia's markup
language called CML. The Fuchsia devsite contains an [overview of CML] and a
[reference for the file format]. Here's a basic one that can run our single binary:
-[overview of CML]: https://fuchsia.dev/fuchsia-src/concepts/components/v2/component_manifests
-[reference for the file format]: https://fuchsia.dev/reference/cml
-
-**`hello_fuchsia.cml`**
+**`pkg/hello_fuchsia.cml`**
```txt
{
include: [ "syslog/client.shard.cml" ],
}
```
+**Current directory structure**
+```txt
+hello_fuchsia/
+┗━ pkg/
+ ┣━ meta/
+ ┃ ┗━ package
+ ┣━ hello_fuchsia.manifest
+ ┗━ hello_fuchsia.cml
+```
+
Now we can compile that CML into a component manifest:
```sh
-${SDK_PATH}/tools/${ARCH}/cmc compile hello_fuchsia.cml --includepath ${SDK_PATH}/pkg -o package/meta/hello_fuchsia.cm
+${SDK_PATH}/tools/${ARCH}/cmc compile \
+ pkg/hello_fuchsia.cml \
+ --includepath ${SDK_PATH}/pkg \
+ -o pkg/meta/hello_fuchsia.cm
```
-`--includepath` tells the compiler where to look for `include`s from our CML.
-In our case, we're only using `syslog/client.shard.cml`.
+*Note: `--includepath` tells the compiler where to look for `include`s from our CML.
+In our case, we're only using `syslog/client.shard.cml`.*
-### Building and publishing a package
+**Current directory structure**
+```txt
+hello_fuchsia/
+┗━ pkg/
+ ┣━ meta/
+ ┃ ┣━ package
+ ┃ ┗━ hello_fuchsia.cm
+ ┣━ hello_fuchsia.manifest
+ ┗━ hello_fuchsia.cml
+```
+
+### Building a Fuchsia package
-Next, we'll build our package as defined by our manifest:
+Next, we'll build a package manifest as defined by our manifest:
```sh
-${SDK_PATH}/tools/${ARCH}/pm -o hello_fuchsia -m package/hello_fuchsia.manifest build -output-package-manifest hello_fuchsia_manifest
+${SDK_PATH}/tools/${ARCH}/pm \
+ -o pkg/hello_fuchsia_manifest \
+ -m pkg/hello_fuchsia.manifest \
+ build \
+ -output-package-manifest pkg/hello_fuchsia_package_manifest
```
-This will produce `hello_fuchsia_manifest` which is a package manifest we can
-publish directly to a repository. We can set up that repository with:
+This will produce `pkg/hello_fuchsia_manifest/` which is a package manifest we can
+publish directly to a repository.
+
+**Current directory structure**
+```txt
+hello_fuchsia/
+┗━ pkg/
+ ┣━ meta/
+ ┃ ┣━ package
+ ┃ ┗━ hello_fuchsia.cm
+ ┣━ hello_fuchsia_manifest/
+ ┃ ┗━ ...
+ ┣━ hello_fuchsia.manifest
+ ┣━ hello_fuchsia.cml
+ ┗━ hello_fuchsia_package_manifest
+```
+
+We are now ready to publish the package.
+
+## Publishing a Fuchsia package
+
+With our package and component manifests setup,
+we can now publish our package. The first step will
+be to create a Fuchsia package repository to publish
+to.
+
+### Creating a Fuchsia package repository
+
+We can set up our repository with:
```sh
-${SDK_PATH}/tools/${ARCH}/pm newrepo -repo repo
+${SDK_PATH}/tools/${ARCH}/pm newrepo \
+ -repo pkg/repo
```
-And then publish our new package to that repository with:
+**Current directory structure**
+```txt
+hello_fuchsia/
+┗━ pkg/
+ ┣━ meta/
+ ┃ ┣━ package
+ ┃ ┗━ hello_fuchsia.cm
+ ┣━ hello_fuchsia_manifest/
+ ┃ ┗━ ...
+ ┣━ repo/
+ ┃ ┗━ ...
+ ┣━ hello_fuchsia.manifest
+ ┣━ hello_fuchsia.cml
+ ┗━ hello_fuchsia_package_manifest
+```
+
+## Publishing Fuchsia package to repository
+
+We can publish our new package to that repository with:
```sh
-${SDK_PATH}/tools/${ARCH}/pm publish -repo repo -lp -f <(echo "hello_fuchsia_manifest")
+${SDK_PATH}/tools/${ARCH}/pm publish \
+ -repo pkg/repo \
+ -lp -f <(echo "pkg/hello_fuchsia_package_manifest")
```
-Then we can add it to `ffx`'s package server as `hello-fuchsia` using:
+Then we can add the repository to `ffx`'s package server as `hello-fuchsia` using:
```sh
-${SDK_PATH}/tools/${ARCH}/ffx repository add-from-pm repo -r hello-fuchsia
+${SDK_PATH}/tools/${ARCH}/ffx repository add-from-pm \
+ pkg/repo \
+ -r hello-fuchsia
+```
+
+## Running a Fuchsia component on an emulator
+
+At this point, we are ready to run our Fuchsia
+component. For reference, our final directory
+structure will look like:
+
+**Final directory structure**
+```txt
+hello_fuchsia/
+┣━ src/ (if using rustc)
+┃ ┗━ hello_fuchsia.rs ...
+┣━ bin/ ...
+┃ ┗━ hello_fuchsia ...
+┣━ src/ (if using cargo)
+┃ ┗━ main.rs ...
+┣━ target/ ...
+┃ ┗━ x86_64-fuchsia/ ...
+┃ ┗━ debug/ ...
+┃ ┗━ hello_fuchsia ...
+┗━ pkg/
+ ┣━ meta/
+ ┃ ┣━ package
+ ┃ ┗━ hello_fuchsia.cm
+ ┣━ hello_fuchsia_manifest/
+ ┃ ┗━ ...
+ ┣━ repo/
+ ┃ ┗━ ...
+ ┣━ hello_fuchsia.manifest
+ ┣━ hello_fuchsia.cml
+ ┗━ hello_fuchsia_package_manifest
```
-### Starting the emulator
+### Starting the Fuchsia emulator
Start a Fuchsia emulator in a new terminal using:
${SDK_PATH}/tools/${ARCH}/ffx emu start workstation_eng.qemu-${ARCH} --headless
```
-Once the emulator is running, start a package repository server to serve our
-package to the emulator:
+### Watching emulator logs
+Once the emulator is running, open a separate terminal to watch the emulator logs:
+
+**In separate terminal**
```sh
-${SDK_PATH}/tools/${ARCH}/ffx repository server start
+${SDK_PATH}/tools/${ARCH}/ffx log \
+ --since now
```
-Once the repository server is up and running, register our repository:
+### Serving a Fuchsia package
+
+Now, start a package repository server to serve our
+package to the emulator:
```sh
-${SDK_PATH}/tools/${ARCH}/ffx target repository register --repository hello-fuchsia
+${SDK_PATH}/tools/${ARCH}/ffx repository server start
```
-And watch the logs from the emulator in a separate terminal:
+Once the repository server is up and running, register it with the target Fuchsia system running in the emulator:
```sh
-${SDK_PATH}/tools/${ARCH}/ffx log --since now
+${SDK_PATH}/tools/${ARCH}/ffx target repository register \
+ --repository hello-fuchsia
```
+### Running a Fuchsia component
+
Finally, run the component:
```sh
-${SDK_PATH}/tools/${ARCH}/ffx component run fuchsia-pkg://hello-fuchsia/hello_fuchsia#meta/hello_fuchsia.cm
+${SDK_PATH}/tools/${ARCH}/ffx component run \
+ fuchsia-pkg://hello-fuchsia/hello_fuchsia_manifest#meta/hello_fuchsia.cm
```
On reruns of the component, the `--recreate` argument may also need to be
passed.
```sh
-${SDK_PATH}/tools/${ARCH}/ffx component run --recreate fuchsia-pkg://hello-fuchsia/hello_fuchsia#meta/hello_fuchsia.cm
+${SDK_PATH}/tools/${ARCH}/ffx component run \
+ --recreate \
+ fuchsia-pkg://hello-fuchsia/hello_fuchsia_manifest#meta/hello_fuchsia.cm
+```
+
+## `.gitignore` extensions
+
+Optionally, we can create/extend our `.gitignore` file to ignore files and
+directories that are not helpful to track:
+
+```txt
+pkg/repo
+pkg/meta/hello_fuchsia.cm
+pkg/hello_fuchsia_manifest
+pkg/hello_fuchsia_package_manifest
```
## Testing
### Running unit tests
-Tests can be run in the same way as a regular binary, simply by passing `--test`
-to the `rustc` invocation and then repackaging and rerunning. The test harness
-will run the applicable unit tests.
+Tests can be run in the same way as a regular binary.
+
+* If using `cargo`, you can simply pass `test --no-run`
+to the `cargo` invocation and then repackage and rerun the Fuchsia package. From our previous example,
+this would look like `cargo test --target x86_64-fuchsia --no-run`, and moving the executable
+binary path found from the line `Executable unittests src/main.rs (target/x86_64-fuchsia/debug/deps/hello_fuchsia-<HASH>)`
+into `pkg/hello_fuchsia.manifest`.
+
+* If using the compiled `rustc`, you can simply pass `--test`
+to the `rustc` invocation and then repackage and rerun the Fuchsia package.
+
+The test harness will run the applicable unit tests.
Often when testing, you may want to pass additional command line arguments to
your binary. Additional arguments can be set in the component manifest:
-**`hello_fuchsia.cml`**
+**`pkg/hello_fuchsia.cml`**
```txt
{
include: [ "syslog/client.shard.cml" ],
This will pass the argument `it_works` to the binary, filtering the tests to
only those tests that match the pattern. There are many more configuration
options available in CML including environment variables. More documentation is
-available on the [Fuchsia devsite](https://fuchsia.dev/reference/cml).
+available on the [Fuchsia devsite].
### Running the compiler test suite
Running the Rust test suite on Fuchsia is [not currently supported], but work is
underway to enable it.
+[Fuchsia team]: https://team-api.infra.rust-lang.org/v1/teams/fuchsia.json
+[Fuchsia]: https://fuchsia.dev/
+[source tree]: https://fuchsia.dev/fuchsia-src/get-started/learn/build
+[rustup]: https://rustup.rs/
+[cargo]: https://doc.rust-lang.org/cargo/
+[Fuchsia SDK]: https://chrome-infra-packages.appspot.com/p/fuchsia/sdk/core
+[overview of CML]: https://fuchsia.dev/fuchsia-src/concepts/components/v2/component_manifests
+[reference for the file format]: https://fuchsia.dev/reference/cml
+[Fuchsia devsite]: https://fuchsia.dev/reference/cml
[not currently supported]: https://fxbug.dev/105393
rustc --target m68k-unknown-linux-gnu your-code.rs
```
-Very simple progams can be run using the `qemu-m68k-static` program:
+Very simple programs can be run using the `qemu-m68k-static` program:
```text
$ qemu-m68k-static your-code
|--------------------------------|-------------|------------------|
| `aarch64-unknown-openbsd` | libc++ | [64-bit ARM systems](https://www.openbsd.org/arm64.html) |
| `i686-unknown-openbsd` | libc++ | [Standard PC and clones based on the Intel i386 architecture and compatible processors](https://www.openbsd.org/i386.html) |
+| `powerpc64-unknown-openbsd` | libc++ | [IBM POWER-based PowerNV systems](https://www.openbsd.org/powerpc64.html) |
+| `riscv64gc-unknown-openbsd` | libc++ | [64-bit RISC-V systems](https://www.openbsd.org/riscv64.html) |
| `sparc64-unknown-openbsd` | estdc++ | [Sun UltraSPARC and Fujitsu SPARC64 systems](https://www.openbsd.org/sparc64.html) |
| `x86_64-unknown-openbsd` | libc++ | [AMD64-based systems](https://www.openbsd.org/amd64.html) |
## Building the target
-For cross-compilation I recommend using [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) toolchain, one change that seems necessary beside configuring corss compilers is disabling experimental `m86k` target. Otherwise LLVM build fails with `multiple definition ...` errors.
+For cross-compilation I recommend using [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) toolchain, one change that seems necessary beside configuring cross compilers is disabling experimental `m86k` target. Otherwise LLVM build fails with `multiple definition ...` errors.
Native bootstrapping builds require rather fragile hacks until host artifacts are available so I won't describe them here.
## Building Rust programs
- Use native Windows targets. This means compiling your C code for the Windows
platform as if it was the UEFI platform. This works for static libraries, but
needs adjustments when linking into an UEFI executable. You can, however,
- link such static libraries seemlessly into rust code compiled for UEFI
+ link such static libraries seamlessly into rust code compiled for UEFI
targets. Be wary of any includes that are not specifically suitable for UEFI
targets (especially the C standard library includes are not always
compatible). Freestanding compilations are recommended to avoid
memory size is now the full 64-bit address space instead of the 4GB as limited
by the 32-bit address space for `wasm32-unknown-unknown`.
-This target is not a stable target. The [memory64] WebAssembly proposal is stil
+This target is not a stable target. The [memory64] WebAssembly proposal is still
in-progress and not standardized. This means that there are not many engines
which implement the `memory64` feature and if they do they're likely behind a
flag, for example:
#[cfg(has_feathers = "zapping")] // This is expected as "has_feathers" was provided in names()
// and because no value checking was enable for "has_feathers"
- // no warning is emited for the value "zapping"
+ // no warning is emitted for the value "zapping"
fn do_zapping() {}
#[cfg(has_mumble_frotz)] // This is UNEXPECTED because names checking is enable and
replacing the current working directory prefix with a specified value.
The given value may be absolute or relative, or empty. This switch takes
-precidence over `--remap-path-prefix` in case they would both match a given
+precedence over `--remap-path-prefix` in case they would both match a given
path.
This flag helps to produce deterministic output, by removing the current working
This feature allows for use of one of following sanitizers:
-* [AddressSanitizer][clang-asan] a fast memory error detector.
-* [ControlFlowIntegrity][clang-cfi] LLVM Control Flow Integrity (CFI) provides
+* [AddressSanitizer](#addresssanitizer) a fast memory error detector.
+* [ControlFlowIntegrity](#controlflowintegrity) LLVM Control Flow Integrity (CFI) provides
forward-edge control flow protection.
-* [HWAddressSanitizer][clang-hwasan] a memory error detector similar to
+* [HWAddressSanitizer](#hwaddresssanitizer) a memory error detector similar to
AddressSanitizer, but based on partial hardware assistance.
-* [LeakSanitizer][clang-lsan] a run-time memory leak detector.
-* [MemorySanitizer][clang-msan] a detector of uninitialized reads.
-* [MemTagSanitizer][clang-memtag] fast memory error detector based on
+* [LeakSanitizer](#leaksanitizer) a run-time memory leak detector.
+* [MemorySanitizer](#memorysanitizer) a detector of uninitialized reads.
+* [MemTagSanitizer](#memtagsanitizer) fast memory error detector based on
Armv8.5-A Memory Tagging Extension.
-* [ShadowCallStack][clang-scs] provides backward-edge control flow protection.
-* [ThreadSanitizer][clang-tsan] a fast data race detector.
+* [ShadowCallStack](#shadowcallstack) provides backward-edge control flow protection.
+* [ThreadSanitizer](#threadsanitizer) a fast data race detector.
To enable a sanitizer compile with `-Zsanitizer=address`,`-Zsanitizer=cfi`,
`-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, `-Zsanitizer=memory`,
ability to detect some bugs. It is not expected to produce false positive
reports.
+See the [Clang AddressSanitizer documentation][clang-asan] for more details.
+
## Examples
Stack buffer overflow:
LLVM CFI can be enabled with -Zsanitizer=cfi and requires LTO (i.e., -Clto).
+See the [Clang ControlFlowIntegrity documentation][clang-cfi] for more details.
+
## Example
```text
globals. To enable this target feature compile with `-C
target-feature=+tagged-globals`
+See the [Clang HWAddressSanitizer documentation][clang-hwasan] for more details.
+
## Example
Heap buffer overflow:
* `x86_64-apple-darwin`
* `x86_64-unknown-linux-gnu`
+See the [Clang LeakSanitizer documentation][clang-lsan] for more details.
+
# MemorySanitizer
MemorySanitizer is detector of uninitialized reads.
need to be recompiled using Clang with `-fsanitize=memory` option. Failing to
achieve that will result in false positive reports.
+See the [Clang MemorySanitizer documentation][clang-msan] for more details.
+
## Example
Detecting the use of uninitialized memory. The `-Zbuild-std` flag rebuilds and
MemTagSanitizer requires hardware support and the `mte` target feature.
To enable this target feature compile with `-C target-feature="+mte"`.
-More information can be found in the associated [LLVM documentation](https://llvm.org/docs/MemTagSanitizer.html).
+See the [LLVM MemTagSanitizer documentation][llvm-memtag] for more details.
# ShadowCallStack
* `aarch64-linux-android`
-A runtime must be provided by the application or operating system. See the [LLVM documentation][clang-scs] for further details.
+A runtime must be provided by the application or operating system.
+
+See the [Clang ShadowCallStack documentation][clang-scs] for more details.
# ThreadSanitizer
ThreadSanitizer does not support atomic fences `std::sync::atomic::fence`,
nor synchronization performed using inline assembly code.
+See the [Clang ThreadSanitizer documentation][clang-tsan] for more details.
+
## Example
```rust
* [HWAddressSanitizer in Clang][clang-hwasan]
* [LeakSanitizer in Clang][clang-lsan]
* [MemorySanitizer in Clang][clang-msan]
+* [MemTagSanitizer in LLVM][llvm-memtag]
* [ThreadSanitizer in Clang][clang-tsan]
[clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html
[clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html
[clang-scs]: https://clang.llvm.org/docs/ShadowCallStack.html
[clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html
+[llvm-memtag]: https://llvm.org/docs/MemTagSanitizer.html
check_generic_bound(bound)
if item["inner"]["default"]:
check_type(item["inner"]["default"])
+ elif item["kind"] == "import":
+ if item["inner"]["id"]:
+ inner_id = item["inner"]["id"]
+ assert valid_id(inner_id)
+ if inner_id in crate["index"] and inner_id not in visited:
+ work_list.add(inner_id)
# Improvements to this script are greatly appreciated!
if [[ $# != 2 ]]; then
- echo "expected 2 arguments, recieved $#"
+ echo "expected 2 arguments, received $#"
echo "example usage: './src/etc/cpu-usage-over-time-plot.sh \
7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c \
x86_64-gnu'"
return ret
-# Returns the number of occurences matching the regex (`regexp`) and the text (`pat`).
+# Returns the number of occurrences matching the regex (`regexp`) and the text (`pat`).
def check_tree_text(tree, path, pat, regexp, stop_at_first):
path = normalize_xpath(path)
match_count = 0
# https://github.com/rust-lang/rust/issues/77620#issuecomment-705144570
unset GIT_DIR
ROOT_DIR="$(git rev-parse --show-toplevel)"
-COMMAND="$ROOT_DIR/x.py test tidy --bless"
+COMMAND="$ROOT_DIR/x.py test tidy"
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" ]]; then
COMMAND="python $COMMAND"
arrayvec = { version = "0.7", default-features = false }
askama = { version = "0.11", default-features = false, features = ["config"] }
atty = "0.2"
-pulldown-cmark = { version = "0.9.2", default-features = false }
+itertools = "0.10.1"
minifier = "0.2.2"
-serde = { version = "1.0", features = ["derive"] }
+once_cell = "1.10.0"
+pulldown-cmark = { version = "0.9.2", default-features = false }
+regex = "1"
+rustdoc-json-types = { path = "../rustdoc-json-types" }
serde_json = "1.0"
+serde = { version = "1.0", features = ["derive"] }
smallvec = "1.8.1"
tempfile = "3"
-itertools = "0.10.1"
-regex = "1"
-rustdoc-json-types = { path = "../rustdoc-json-types" }
+thin-vec = "0.2.8"
tracing = "0.1"
tracing-tree = "0.2.0"
-once_cell = "1.10.0"
[dependencies.tracing-subscriber]
version = "0.3.3"
kind: Box::new(ImplItem(Box::new(Impl {
unsafety: hir::Unsafety::Normal,
generics: new_generics,
- trait_: Some(clean_trait_ref_with_bindings(self.cx, trait_ref, &[])),
+ trait_: Some(clean_trait_ref_with_bindings(self.cx, trait_ref, ThinVec::new())),
for_: clean_middle_ty(ty, self.cx, None),
items: Vec::new(),
polarity,
),
// FIXME(eddyb) compute both `trait_` and `for_` from
// the post-inference `trait_ref`, as it's more accurate.
- trait_: Some(clean_trait_ref_with_bindings(cx, trait_ref.0, &[])),
+ trait_: Some(clean_trait_ref_with_bindings(cx, trait_ref.0, ThinVec::new())),
for_: clean_middle_ty(ty.0, cx, None),
items: cx.tcx
.associated_items(impl_def_id)
use std::iter::once;
use std::sync::Arc;
+use thin_vec::ThinVec;
+
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::thin_vec::ThinVec;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
),
};
let polarity = tcx.impl_polarity(did);
- let trait_ = associated_trait.map(|t| clean_trait_ref_with_bindings(cx, t, &[]));
+ let trait_ = associated_trait.map(|t| clean_trait_ref_with_bindings(cx, t, ThinVec::new()));
if trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() {
super::build_deref_target_impls(cx, &trait_items, ret);
}
use std::collections::BTreeMap;
use std::default::Default;
use std::hash::Hash;
-use std::{mem, vec};
+use std::mem;
+use thin_vec::ThinVec;
use crate::core::{self, DocContext, ImplTraitParam};
use crate::formats::item_type::ItemType;
bug!("clean: parenthesized `GenericBound::LangItemTrait`");
};
- let trait_ = clean_trait_ref_with_bindings(cx, trait_ref, &bindings);
+ let trait_ = clean_trait_ref_with_bindings(cx, trait_ref, bindings);
GenericBound::TraitBound(
PolyTrait { trait_, generic_params: vec![] },
hir::TraitBoundModifier::None,
pub(crate) fn clean_trait_ref_with_bindings<'tcx>(
cx: &mut DocContext<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
- bindings: &[TypeBinding],
+ bindings: ThinVec<TypeBinding>,
) -> Path {
let kind = cx.tcx.def_kind(trait_ref.def_id).into();
if !matches!(kind, ItemType::Trait | ItemType::TraitAlias) {
span_bug!(cx.tcx.def_span(trait_ref.def_id), "`TraitRef` had unexpected kind {:?}", kind);
}
inline::record_extern_fqn(cx, trait_ref.def_id, kind);
- let path = external_path(cx, trait_ref.def_id, true, bindings.to_vec(), trait_ref.substs);
+ let path = external_path(cx, trait_ref.def_id, true, bindings, trait_ref.substs);
debug!("ty::TraitRef\n subst: {:?}\n", trait_ref.substs);
fn clean_poly_trait_ref_with_bindings<'tcx>(
cx: &mut DocContext<'tcx>,
poly_trait_ref: ty::PolyTraitRef<'tcx>,
- bindings: &[TypeBinding],
+ bindings: ThinVec<TypeBinding>,
) -> GenericBound {
let poly_trait_ref = poly_trait_ref.lift_to_tcx(cx.tcx).unwrap();
fn clean_lifetime<'tcx>(lifetime: hir::Lifetime, cx: &mut DocContext<'tcx>) -> Lifetime {
let def = cx.tcx.named_region(lifetime.hir_id);
if let Some(
- rl::Region::EarlyBound(_, node_id)
+ rl::Region::EarlyBound(node_id)
| rl::Region::LateBound(_, _, node_id)
| rl::Region::Free(_, node_id),
) = def
let poly_trait_ref = pred.map_bound(|pred| pred.trait_ref);
Some(WherePredicate::BoundPredicate {
ty: clean_middle_ty(poly_trait_ref.skip_binder().self_ty(), cx, None),
- bounds: vec![clean_poly_trait_ref_with_bindings(cx, poly_trait_ref, &[])],
+ bounds: vec![clean_poly_trait_ref_with_bindings(cx, poly_trait_ref, ThinVec::new())],
bound_params: Vec::new(),
})
}
def_id: Option<DefId>,
) -> Type {
let lifted = ty.lift_to_tcx(cx.tcx).unwrap();
- let trait_ = clean_trait_ref_with_bindings(cx, lifted.trait_ref(cx.tcx), &[]);
+ let trait_ = clean_trait_ref_with_bindings(cx, lifted.trait_ref(cx.tcx), ThinVec::new());
let self_type = clean_middle_ty(ty.self_ty(), cx, None);
let self_def_id = if let Some(def_id) = def_id {
cx.tcx.opt_parent(def_id).or(Some(def_id))
// NOTE: generics must be cleaned before args
let generics = clean_generics(generics, cx);
let args = clean_args_from_types_and_body_id(cx, sig.decl.inputs, body_id);
- let decl = clean_fn_decl_with_args(cx, sig.decl, args);
+ let mut decl = clean_fn_decl_with_args(cx, sig.decl, args);
+ if sig.header.is_async() {
+ decl.output = decl.sugared_async_return_type();
+ }
(generics, decl)
});
Box::new(Function { decl, generics })
AdtKind::Enum => ItemType::Enum,
};
inline::record_extern_fqn(cx, did, kind);
- let path = external_path(cx, did, false, vec![], substs);
+ let path = external_path(cx, did, false, ThinVec::new(), substs);
Type::Path { path }
}
ty::Foreign(did) => {
inline::record_extern_fqn(cx, did, ItemType::ForeignType);
- let path = external_path(cx, did, false, vec![], InternalSubsts::empty());
+ let path = external_path(cx, did, false, ThinVec::new(), InternalSubsts::empty());
Type::Path { path }
}
ty::Dynamic(obj, ref reg) => {
let mut bounds = dids
.map(|did| {
let empty = cx.tcx.intern_substs(&[]);
- let path = external_path(cx, did, false, vec![], empty);
+ let path = external_path(cx, did, false, ThinVec::new(), empty);
inline::record_extern_fqn(cx, did, ItemType::Trait);
PolyTrait { trait_: path, generic_params: Vec::new() }
})
}
}
- let bindings: Vec<_> = bounds
+ let bindings: ThinVec<_> = bounds
.iter()
.filter_map(|bound| {
if let ty::PredicateKind::Projection(proj) = bound.kind().skip_binder()
})
.collect();
- Some(clean_poly_trait_ref_with_bindings(cx, trait_ref, &bindings))
+ Some(clean_poly_trait_ref_with_bindings(cx, trait_ref, bindings))
})
.collect::<Vec<_>>();
bounds.extend(regions);
})
.collect::<Vec<_>>()
.into();
- let bindings = generic_args
- .bindings
- .iter()
- .map(|x| clean_type_binding(x, cx))
- .collect::<Vec<_>>()
- .into();
+ let bindings =
+ generic_args.bindings.iter().map(|x| clean_type_binding(x, cx)).collect::<ThinVec<_>>();
GenericArgs::AngleBracketed { args, bindings }
}
}
use std::{cmp, fmt, iter};
use arrayvec::ArrayVec;
+use thin_vec::ThinVec;
use rustc_ast::attr;
use rustc_ast::util::comments::beautify_doc_string;
use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel};
use rustc_const_eval::const_eval::is_unstable_const_fn;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::thin_vec::ThinVec;
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
pub(crate) fn maybe_sized(cx: &mut DocContext<'_>) -> GenericBound {
let did = cx.tcx.require_lang_item(LangItem::Sized, None);
let empty = cx.tcx.intern_substs(&[]);
- let path = external_path(cx, did, false, vec![], empty);
+ let path = external_path(cx, did, false, ThinVec::new(), empty);
inline::record_extern_fqn(cx, did, ItemType::Trait);
GenericBound::TraitBound(
PolyTrait { trait_: path, generic_params: Vec::new() },
use rustc_ast as ast;
use rustc_ast::tokenstream::TokenTree;
-use rustc_data_structures::thin_vec::ThinVec;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_span::symbol::{kw, sym, Symbol};
use std::fmt::Write as _;
use std::mem;
+use thin_vec::ThinVec;
#[cfg(test)]
mod tests;
cx: &mut DocContext<'tcx>,
did: DefId,
has_self: bool,
- bindings: Vec<TypeBinding>,
+ bindings: ThinVec<TypeBinding>,
substs: SubstsRef<'tcx>,
) -> GenericArgs {
let args = substs_to_args(cx, substs, has_self);
// The trait's first substitution is the one after self, if there is one.
match substs.iter().nth(if has_self { 1 } else { 0 }).unwrap().expect_ty().kind() {
ty::Tuple(tys) => tys.iter().map(|t| clean_middle_ty(t, cx, None)).collect::<Vec<_>>().into(),
- _ => return GenericArgs::AngleBracketed { args: args.into(), bindings: bindings.into() },
+ _ => return GenericArgs::AngleBracketed { args: args.into(), bindings },
};
let output = None;
// FIXME(#20299) return type comes from a projection now
cx: &mut DocContext<'tcx>,
did: DefId,
has_self: bool,
- bindings: Vec<TypeBinding>,
+ bindings: ThinVec<TypeBinding>,
substs: SubstsRef<'tcx>,
) -> Path {
let def_kind = cx.tcx.def_kind(did);
let where_preds = comma_sep(where_predicates, false);
let clause = if f.alternate() {
if ending == Ending::Newline {
- // add a space so stripping <br> tags and breaking spaces still renders properly
- format!(" where{where_preds}, ")
+ format!(" where{where_preds},")
} else {
format!(" where{where_preds}")
}
if ending == Ending::Newline {
let mut clause = " ".repeat(indent.saturating_sub(1));
- // add a space so stripping <br> tags and breaking spaces still renders properly
- write!(
- clause,
- " <span class=\"where fmt-newline\">where{where_preds}, </span>"
- )?;
+ write!(clause, "<span class=\"where fmt-newline\">where{where_preds},</span>")?;
clause
} else {
// insert a <br> tag after a single space but before multiple spaces at the start
if indent == 0 {
- format!(" <br><span class=\"where\">where{where_preds}</span>")
+ format!("<br><span class=\"where\">where{where_preds}</span>")
} else {
let mut clause = br_with_padding;
clause.truncate(clause.len() - 5 * " ".len());
- write!(clause, " <span class=\"where\">where{where_preds}</span>")?;
+ write!(clause, "<span class=\"where\">where{where_preds}</span>")?;
clause
}
}
/// <br>Used to determine line-wrapping.
/// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is
/// necessary.
- /// * `asyncness`: Whether the function is async or not.
pub(crate) fn full_print<'a, 'tcx: 'a>(
&'a self,
header_len: usize,
indent: usize,
- asyncness: hir::IsAsync,
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
- display_fn(move |f| self.inner_full_print(header_len, indent, asyncness, f, cx))
+ display_fn(move |f| self.inner_full_print(header_len, indent, f, cx))
}
fn inner_full_print(
&self,
header_len: usize,
indent: usize,
- asyncness: hir::IsAsync,
f: &mut fmt::Formatter<'_>,
cx: &Context<'_>,
) -> fmt::Result {
args_plain.push_str(", ...");
}
- let arrow_plain;
- let arrow = if let hir::IsAsync::Async = asyncness {
- let output = self.sugared_async_return_type();
- arrow_plain = format!("{:#}", output.print(cx));
- if f.alternate() { arrow_plain.clone() } else { format!("{}", output.print(cx)) }
- } else {
- arrow_plain = format!("{:#}", self.output.print(cx));
- if f.alternate() { arrow_plain.clone() } else { format!("{}", self.output.print(cx)) }
- };
+ let arrow_plain = format!("{:#}", self.output.print(cx));
+ let arrow =
+ if f.alternate() { arrow_plain.clone() } else { format!("{}", self.output.print(cx)) };
let declaration_len = header_len + args_plain.len() + arrow_plain.len();
let output = if declaration_len > 80 {
fn new(iter: TokenIter<'a>) -> Self {
Self { stored: VecDeque::new(), peek_pos: 0, iter }
}
- /// Returns the next item after the current one. It doesn't interfer with `peek_next` output.
+ /// Returns the next item after the current one. It doesn't interfere with `peek_next` output.
fn peek(&mut self) -> Option<&(TokenKind, &'a str)> {
if self.stored.is_empty() {
if let Some(next) = self.iter.next() {
}
self.stored.front()
}
- /// Returns the next item after the last one peeked. It doesn't interfer with `peek` output.
+ /// Returns the next item after the last one peeked. It doesn't interfere with `peek` output.
fn peek_next(&mut self) -> Option<&(TokenKind, &'a str)> {
self.peek_pos += 1;
if self.peek_pos - 1 < self.stored.len() {
// Render the list of items inside one of the sections "Trait Implementations",
// "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages).
-fn render_impls(
+pub(crate) fn render_impls(
cx: &mut Context<'_>,
w: &mut Buffer,
- impls: &[&&Impl],
+ impls: &[&Impl],
containing_item: &clean::Item,
toggle_open_by_default: bool,
) {
href = href,
name = name,
generics = g.print(cx),
- decl = d.full_print(header_len, indent, header.asyncness, cx),
+ decl = d.full_print(header_len, indent, cx),
notable_traits = notable_traits_decl(d, cx),
where_clause = print_where_clause(g, cx, indent, end_newline),
)
}
}
+fn write_impl_section_heading(w: &mut Buffer, title: &str, id: &str) {
+ write!(
+ w,
+ "<h2 id=\"{id}\" class=\"small-section-header\">\
+ {title}\
+ <a href=\"#{id}\" class=\"anchor\"></a>\
+ </h2>"
+ );
+}
+
+pub(crate) fn render_all_impls(
+ w: &mut Buffer,
+ cx: &mut Context<'_>,
+ containing_item: &clean::Item,
+ concrete: &[&Impl],
+ synthetic: &[&Impl],
+ blanket_impl: &[&Impl],
+) {
+ let mut impls = Buffer::empty_from(w);
+ render_impls(cx, &mut impls, concrete, containing_item, true);
+ let impls = impls.into_inner();
+ if !impls.is_empty() {
+ write_impl_section_heading(w, "Trait Implementations", "trait-implementations");
+ write!(w, "<div id=\"trait-implementations-list\">{}</div>", impls);
+ }
+
+ if !synthetic.is_empty() {
+ write_impl_section_heading(w, "Auto Trait Implementations", "synthetic-implementations");
+ w.write_str("<div id=\"synthetic-implementations-list\">");
+ render_impls(cx, w, synthetic, containing_item, false);
+ w.write_str("</div>");
+ }
+
+ if !blanket_impl.is_empty() {
+ write_impl_section_heading(w, "Blanket Implementations", "blanket-implementations");
+ w.write_str("<div id=\"blanket-implementations-list\">");
+ render_impls(cx, w, blanket_impl, containing_item, false);
+ w.write_str("</div>");
+ }
+}
+
fn render_assoc_items(
w: &mut Buffer,
cx: &mut Context<'_>,
let mut tmp_buf = Buffer::empty_from(w);
let (render_mode, id) = match what {
AssocItemRender::All => {
- tmp_buf.write_str(
- "<h2 id=\"implementations\" class=\"small-section-header\">\
- Implementations\
- <a href=\"#implementations\" class=\"anchor\"></a>\
- </h2>",
- );
+ write_impl_section_heading(&mut tmp_buf, "Implementations", "implementations");
(RenderMode::Normal, "implementations-list".to_owned())
}
AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
if let Some(def_id) = type_.def_id(cx.cache()) {
cx.deref_id_map.insert(def_id, id.clone());
}
- write!(
- tmp_buf,
- "<h2 id=\"{id}\" class=\"small-section-header\">\
- <span>Methods from {trait_}<Target = {type_}></span>\
- <a href=\"#{id}\" class=\"anchor\"></a>\
- </h2>",
- id = id,
- trait_ = trait_.print(cx),
- type_ = type_.print(cx),
+ write_impl_section_heading(
+ &mut tmp_buf,
+ &format!(
+ "<span>Methods from {trait_}<Target = {type_}></span>",
+ trait_ = trait_.print(cx),
+ type_ = type_.print(cx),
+ ),
+ &id,
);
(RenderMode::ForDeref { mut_: deref_mut_ }, cx.derive_id(id))
}
return;
}
- let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) =
- traits.iter().partition(|t| t.inner_impl().kind.is_auto());
- let (blanket_impl, concrete): (Vec<&&Impl>, _) =
+ let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
+ traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
+ let (blanket_impl, concrete): (Vec<&Impl>, _) =
concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
- let mut impls = Buffer::empty_from(w);
- render_impls(cx, &mut impls, &concrete, containing_item, true);
- let impls = impls.into_inner();
- if !impls.is_empty() {
- write!(
- w,
- "<h2 id=\"trait-implementations\" class=\"small-section-header\">\
- Trait Implementations\
- <a href=\"#trait-implementations\" class=\"anchor\"></a>\
- </h2>\
- <div id=\"trait-implementations-list\">{}</div>",
- impls
- );
- }
-
- if !synthetic.is_empty() {
- w.write_str(
- "<h2 id=\"synthetic-implementations\" class=\"small-section-header\">\
- Auto Trait Implementations\
- <a href=\"#synthetic-implementations\" class=\"anchor\"></a>\
- </h2>\
- <div id=\"synthetic-implementations-list\">",
- );
- render_impls(cx, w, &synthetic, containing_item, false);
- w.write_str("</div>");
- }
-
- if !blanket_impl.is_empty() {
- w.write_str(
- "<h2 id=\"blanket-implementations\" class=\"small-section-header\">\
- Blanket Implementations\
- <a href=\"#blanket-implementations\" class=\"anchor\"></a>\
- </h2>\
- <div id=\"blanket-implementations-list\">",
- );
- render_impls(cx, w, &blanket_impl, containing_item, false);
- w.write_str("</div>");
- }
+ render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
}
}
// in documentation pages for trait with automatic implementations like "Send" and "Sync".
aliases: &[String],
) {
- let id =
- cx.derive_id(get_id_for_impl(&i.inner_impl().for_, i.inner_impl().trait_.as_ref(), cx));
+ let inner_impl = i.inner_impl();
+ let id = cx.derive_id(get_id_for_impl(&inner_impl.for_, inner_impl.trait_.as_ref(), cx));
let aliases = if aliases.is_empty() {
String::new()
} else {
write!(w, "<h3 class=\"code-header in-band\">");
if let Some(use_absolute) = use_absolute {
- write!(w, "{}", i.inner_impl().print(use_absolute, cx));
+ write!(w, "{}", inner_impl.print(use_absolute, cx));
if show_def_docs {
- for it in &i.inner_impl().items {
+ for it in &inner_impl.items {
if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind {
w.write_str("<span class=\"where fmt-newline\"> ");
assoc_type(
}
}
} else {
- write!(w, "{}", i.inner_impl().print(false, cx));
+ write!(w, "{}", inner_impl.print(false, cx));
}
write!(w, "</h3>");
- let is_trait = i.inner_impl().trait_.is_some();
+ let is_trait = inner_impl.trait_.is_some();
if is_trait {
if let Some(portability) = portability(&i.impl_item, Some(parent)) {
write!(w, "<span class=\"item-info\">{}</span>", portability);
}
}
+pub(crate) fn sidebar_render_assoc_items(
+ cx: &Context<'_>,
+ out: &mut Buffer,
+ id_map: &mut IdMap,
+ concrete: Vec<&Impl>,
+ synthetic: Vec<&Impl>,
+ blanket_impl: Vec<&Impl>,
+) {
+ let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
+ let mut links = FxHashSet::default();
+
+ let mut ret = impls
+ .iter()
+ .filter_map(|it| {
+ let trait_ = it.inner_impl().trait_.as_ref()?;
+ let encoded =
+ id_map.derive(get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
+
+ let i_display = format!("{:#}", trait_.print(cx));
+ let out = Escape(&i_display);
+ let prefix = match it.inner_impl().polarity {
+ ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
+ ty::ImplPolarity::Negative => "!",
+ };
+ let generated = format!("<a href=\"#{}\">{}{}</a>", encoded, prefix, out);
+ if links.insert(generated.clone()) { Some(generated) } else { None }
+ })
+ .collect::<Vec<String>>();
+ ret.sort();
+ ret
+ };
+
+ let concrete_format = format_impls(concrete, id_map);
+ let synthetic_format = format_impls(synthetic, id_map);
+ let blanket_format = format_impls(blanket_impl, id_map);
+
+ if !concrete_format.is_empty() {
+ print_sidebar_block(
+ out,
+ "trait-implementations",
+ "Trait Implementations",
+ concrete_format.iter(),
+ );
+ }
+
+ if !synthetic_format.is_empty() {
+ print_sidebar_block(
+ out,
+ "synthetic-implementations",
+ "Auto Trait Implementations",
+ synthetic_format.iter(),
+ );
+ }
+
+ if !blanket_format.is_empty() {
+ print_sidebar_block(
+ out,
+ "blanket-implementations",
+ "Blanket Implementations",
+ blanket_format.iter(),
+ );
+ }
+}
+
fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
let did = it.item_id.expect_def_id();
let cache = cx.cache();
sidebar_deref_methods(cx, out, impl_, v, &mut derefs, &mut used_links);
}
- let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
- let mut links = FxHashSet::default();
-
- let mut ret = impls
- .iter()
- .filter_map(|it| {
- let trait_ = it.inner_impl().trait_.as_ref()?;
- let encoded =
- id_map.derive(get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
-
- let i_display = format!("{:#}", trait_.print(cx));
- let out = Escape(&i_display);
- let prefix = match it.inner_impl().polarity {
- ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
- ty::ImplPolarity::Negative => "!",
- };
- let generated = format!("<a href=\"#{}\">{}{}</a>", encoded, prefix, out);
- if links.insert(generated.clone()) { Some(generated) } else { None }
- })
- .collect::<Vec<String>>();
- ret.sort();
- ret
- };
-
let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto());
let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket());
- let concrete_format = format_impls(concrete, &mut id_map);
- let synthetic_format = format_impls(synthetic, &mut id_map);
- let blanket_format = format_impls(blanket_impl, &mut id_map);
-
- if !concrete_format.is_empty() {
- print_sidebar_block(
- out,
- "trait-implementations",
- "Trait Implementations",
- concrete_format.iter(),
- );
- }
-
- if !synthetic_format.is_empty() {
- print_sidebar_block(
- out,
- "synthetic-implementations",
- "Auto Trait Implementations",
- synthetic_format.iter(),
- );
- }
-
- if !blanket_format.is_empty() {
- print_sidebar_block(
- out,
- "blanket-implementations",
- "Blanket Implementations",
- blanket_format.iter(),
- );
- }
+ sidebar_render_assoc_items(cx, out, &mut id_map, concrete, synthetic, blanket_impl);
}
}
}
buf.push_str("</section>")
}
+/// Returns the list of implementations for the primitive reference type, filtering out any
+/// implementations that are on concrete or partially generic types, only keeping implementations
+/// of the form `impl<T> Trait for &T`.
+pub(crate) fn get_filtered_impls_for_reference<'a>(
+ shared: &'a Rc<SharedContext<'_>>,
+ it: &clean::Item,
+) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) {
+ let def_id = it.item_id.expect_def_id();
+ // If the reference primitive is somehow not defined, exit early.
+ let Some(v) = shared.cache.impls.get(&def_id) else { return (Vec::new(), Vec::new(), Vec::new()) };
+ // Since there is no "direct implementation" on the reference primitive type, we filter out
+ // every implementation which isn't a trait implementation.
+ let traits: Vec<_> = v.iter().filter(|i| i.inner_impl().trait_.is_some()).collect();
+ let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
+ traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
+
+ let (blanket_impl, concrete): (Vec<&Impl>, _) =
+ concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
+ // Now we keep only references over full generic types.
+ let concrete: Vec<_> = concrete
+ .into_iter()
+ .filter(|t| match t.inner_impl().for_ {
+ clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(),
+ _ => false,
+ })
+ .collect();
+
+ (concrete, synthetic, blanket_impl)
+}
+
fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
let mut sidebar = Buffer::new();
- sidebar_assoc_items(cx, &mut sidebar, it);
+
+ if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
+ sidebar_assoc_items(cx, &mut sidebar, it);
+ } else {
+ let shared = Rc::clone(&cx.shared);
+ let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it);
+
+ sidebar_render_assoc_items(
+ cx,
+ &mut sidebar,
+ &mut IdMap::new(),
+ concrete,
+ synthetic,
+ blanket_impl,
+ );
+ }
if !sidebar.is_empty() {
write!(buf, "<section>{}</section>", sidebar.into_inner());
use std::rc::Rc;
use super::{
- collect_paths_for_type, document, ensure_trailing_slash, item_ty_to_section,
- notable_traits_decl, render_assoc_item, render_assoc_items, render_attributes_in_code,
- render_attributes_in_pre, render_impl, render_rightside, render_stability_since_raw,
- AssocItemLink, Context, ImplRenderingParameters,
+ collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference,
+ item_ty_to_section, notable_traits_decl, render_all_impls, render_assoc_item,
+ render_assoc_items, render_attributes_in_code, render_attributes_in_pre, render_impl,
+ render_rightside, render_stability_since_raw, AssocItemLink, Context, ImplRenderingParameters,
};
use crate::clean;
use crate::config::ModuleSorting;
name = name,
generics = f.generics.print(cx),
where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline),
- decl = f.decl.full_print(header_len, 0, header.asyncness, cx),
+ decl = f.decl.full_print(header_len, 0, cx),
notable_traits = notable_traits_decl(&f.decl, cx),
);
});
}
fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
+ let def_id = it.item_id.expect_def_id();
document(w, cx, it, None, HeadingOffset::H2);
- render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
+ if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
+ render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
+ } else {
+ // We handle the "reference" primitive type on its own because we only want to list
+ // implementations on generic types.
+ let shared = Rc::clone(&cx.shared);
+ let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it);
+
+ render_all_impls(w, cx, it, &concrete, &synthetic, &blanket_impl);
+ }
}
fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) {
a.source,
.search-input,
.search-results .result-name,
-.content table td:first-child > a,
.item-left > a,
.out-of-band,
span.since,
top: inherit;
}
-.content table:not(.table-display) {
+.content table {
border-spacing: 0 5px;
}
.content td { vertical-align: top; }
margin-bottom: 15px;
}
-.content .docblock > .impl-items {
- margin-left: 20px;
- margin-top: -34px;
-}
-.content .docblock >.impl-items .table-display {
- margin: 0;
-}
-.content .docblock >.impl-items table td {
- padding: 0;
-}
-.content .docblock > .impl-items .table-display {
- border: none;
-}
-
.item-info {
display: block;
}
width: 100%;
}
-.table-display {
- width: 100%;
- border: 0;
- border-collapse: collapse;
- border-spacing: 0;
- font-size: 1rem;
-}
-
-.table-display tr td:first-child {
- padding-right: 0;
-}
-
-.table-display tr td:last-child {
- float: right;
-}
-.table-display .out-of-band {
- position: relative;
- font-size: 1.125rem;
- display: block;
-}
-
-.table-display td:hover .anchor {
- display: block;
- top: 2px;
- left: -5px;
-}
-
#main-content > ul {
padding-left: 10px;
}
height: 73px;
}
- #main-content > table:not(.table-display) td {
+ #main-content > table td {
word-break: break-word;
width: 50%;
}
}
.search-results a:hover {
- background-color: #777;
+ color: #fff !important;
+ background-color: #3c3c3c;
}
.search-results a:focus {
- color: #000 !important;
- background-color: #c6afb3;
+ color: #fff !important;
+ background-color: #3c3c3c;
}
.search-results a {
color: #0096cf;
clean::KeywordItem => return None,
clean::StrippedItem(ref inner) => {
match &**inner {
- // We document non-empty stripped modules as with `Module::is_stripped` set to
+ // We document stripped modules as with `Module::is_stripped` set to
// `true`, to prevent contained items from being orphaned for downstream users,
// as JSON does no inlining.
- clean::ModuleItem(m) if !m.items.is_empty() => from_clean_item(item, self.tcx),
+ clean::ModuleItem(_)
+ if self.imported_items.contains(&item_id.expect_def_id()) =>
+ {
+ from_clean_item(item, self.tcx)
+ }
_ => return None,
}
}
--- /dev/null
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::def_id::DefId;
+
+use crate::{
+ clean::{self, Import, ImportSource, Item},
+ fold::DocFolder,
+};
+
+/// Get the id's of all items that are `pub use`d in the crate.
+///
+/// We need this to know if a stripped module is `pub use mod::*`, to decide
+/// if it needs to be kept in the index, despite being stripped.
+///
+/// See [#100973](https://github.com/rust-lang/rust/issues/100973) and
+/// [#101103](https://github.com/rust-lang/rust/issues/101103) for times when
+/// this information is needed.
+pub(crate) fn get_imports(krate: clean::Crate) -> (clean::Crate, FxHashSet<DefId>) {
+ let mut finder = ImportFinder { imported: FxHashSet::default() };
+ let krate = finder.fold_crate(krate);
+ (krate, finder.imported)
+}
+
+struct ImportFinder {
+ imported: FxHashSet<DefId>,
+}
+
+impl DocFolder for ImportFinder {
+ fn fold_item(&mut self, i: Item) -> Option<Item> {
+ match *i.kind {
+ clean::ImportItem(Import { source: ImportSource { did: Some(did), .. }, .. }) => {
+ self.imported.insert(did);
+ Some(i)
+ }
+
+ _ => Some(self.fold_item_recur(i)),
+ }
+ }
+}
//! docs for usage and details.
mod conversions;
+mod import_finder;
use std::cell::RefCell;
use std::fs::{create_dir_all, File};
use std::path::PathBuf;
use std::rc::Rc;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::DefId;
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
/// The directory where the blob will be written to.
out_path: PathBuf,
cache: Rc<Cache>,
+ imported_items: FxHashSet<DefId>,
}
impl<'tcx> JsonRenderer<'tcx> {
tcx: TyCtxt<'tcx>,
) -> Result<(Self, clean::Crate), Error> {
debug!("Initializing json renderer");
+
+ let (krate, imported_items) = import_finder::get_imports(krate);
+
Ok((
JsonRenderer {
tcx,
index: Rc::new(RefCell::new(FxHashMap::default())),
out_path: options.output,
cache: Rc::new(cache),
+ imported_items,
},
krate,
))
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(drain_filter)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(test)]
#![feature(never_type)]
// If there's no `::`, it's not an associated item.
// So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
.ok_or_else(|| {
- debug!("found no `::`, assumming {} was correctly not in scope", item_name);
+ debug!("found no `::`, assuming {} was correctly not in scope", item_name);
UnresolvedPath {
item_id,
module_id,
///
/// This is just a wrapper around [`TyCtxt::impl_item_implementor_ids()`] and
/// [`TyCtxt::associated_item()`] (with some helpful logging added).
-#[instrument(level = "debug", skip(tcx))]
+#[instrument(level = "debug", skip(tcx), ret)]
fn trait_assoc_to_impl_assoc_item<'tcx>(
tcx: TyCtxt<'tcx>,
impl_id: DefId,
debug!(?trait_to_impl_assoc_map);
let impl_assoc_id = *trait_to_impl_assoc_map.get(&trait_assoc_id)?;
debug!(?impl_assoc_id);
- let impl_assoc = tcx.associated_item(impl_assoc_id);
- debug!(?impl_assoc);
- Some(impl_assoc)
+ Some(tcx.associated_item(impl_assoc_id))
}
/// Given a type, return all trait impls in scope in `module` for that type.
&mut self,
key: ResolutionInfo,
diag: DiagnosticInfo<'_>,
- // If errors are cached then they are only reported on first ocurrence
+ // If errors are cached then they are only reported on first occurrence
// which we want in some cases but not in others.
cache_errors: bool,
) -> Option<(Res, Option<UrlFragment>)> {
use crate::clean::cfg::Cfg;
use crate::clean::inline::{load_attrs, merge_attrs};
-use crate::clean::{Crate, Item};
+use crate::clean::{Crate, Item, ItemKind};
use crate::core::DocContext;
use crate::fold::DocFolder;
use crate::passes::Pass;
cx: &'a mut DocContext<'tcx>,
}
-impl<'a, 'tcx> DocFolder for CfgPropagator<'a, 'tcx> {
- fn fold_item(&mut self, mut item: Item) -> Option<Item> {
- let old_parent_cfg = self.parent_cfg.clone();
+impl<'a, 'tcx> CfgPropagator<'a, 'tcx> {
+ // Some items need to merge their attributes with their parents' otherwise a few of them
+ // (mostly `cfg` ones) will be missing.
+ fn merge_with_parent_attributes(&mut self, item: &mut Item) {
+ let check_parent = match &*item.kind {
+ // impl blocks can be in different modules with different cfg and we need to get them
+ // as well.
+ ItemKind::ImplItem(_) => false,
+ kind if kind.is_non_assoc() => true,
+ _ => return,
+ };
- if item.kind.is_non_assoc() &&
- let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local()) {
- let hir = self.cx.tcx.hir();
- let hir_id = hir.local_def_id_to_hir_id(def_id);
+ let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local())
+ else { return };
+
+ let hir = self.cx.tcx.hir();
+ let hir_id = hir.local_def_id_to_hir_id(def_id);
+
+ if check_parent {
let expected_parent = hir.get_parent_item(hir_id);
+ // If parents are different, it means that `item` is a reexport and we need
+ // to compute the actual `cfg` by iterating through its "real" parents.
+ if self.parent == Some(expected_parent) {
+ return;
+ }
+ }
- // If parents are different, it means that `item` is a reexport and we need to compute
- // the actual `cfg` by iterating through its "real" parents.
- if self.parent != Some(expected_parent) {
- let mut attrs = Vec::new();
- for (parent_hir_id, _) in hir.parent_iter(hir_id) {
- if let Some(def_id) = hir.opt_local_def_id(parent_hir_id) {
- attrs.extend_from_slice(load_attrs(self.cx, def_id.to_def_id()));
- }
- }
- let (_, cfg) =
- merge_attrs(self.cx, None, item.attrs.other_attrs.as_slice(), Some(&attrs));
- item.cfg = cfg;
+ let mut attrs = Vec::new();
+ for (parent_hir_id, _) in hir.parent_iter(hir_id) {
+ if let Some(def_id) = hir.opt_local_def_id(parent_hir_id) {
+ attrs.extend_from_slice(load_attrs(self.cx, def_id.to_def_id()));
}
}
+ let (_, cfg) = merge_attrs(self.cx, None, item.attrs.other_attrs.as_slice(), Some(&attrs));
+ item.cfg = cfg;
+ }
+}
+
+impl<'a, 'tcx> DocFolder for CfgPropagator<'a, 'tcx> {
+ fn fold_item(&mut self, mut item: Item) -> Option<Item> {
+ let old_parent_cfg = self.parent_cfg.clone();
+
+ self.merge_with_parent_attributes(&mut item);
+
let new_cfg = match (self.parent_cfg.take(), item.cfg.take()) {
(None, None) => None,
(Some(rc), None) | (None, Some(rc)) => Some(rc),
clean::ExternCrateItem { .. } => {}
clean::ImportItem(ref imp) => {
// Because json doesn't inline imports from private modules, we need to mark
- // the imported item as retained so it's impls won't be stripped.i
+ // the imported item as retained so it's impls won't be stripped.
//
// FIXME: Is it necessary to check for json output here: See
// https://github.com/rust-lang/rust/pull/100325#discussion_r941495215
"tool is executed."
],
"compiler": {
- "date": "2022-08-09",
+ "date": "2022-08-19",
"version": "beta"
},
"rustfmt": {
- "date": "2022-08-09",
+ "date": "2022-08-20",
"version": "nightly"
},
"checksums_sha256": {
- "dist/2022-08-09/cargo-beta-aarch64-apple-darwin.tar.gz": "5d9f0ee2de50a18124fd506f493cbdf6cb6385944d32aca1aca9dcdf15bdef4d",
- "dist/2022-08-09/cargo-beta-aarch64-apple-darwin.tar.xz": "683e8546ab86d98ec84204cdcdd2192d19a98c42c13bf0c69697865641238001",
- "dist/2022-08-09/cargo-beta-aarch64-pc-windows-msvc.tar.gz": "fb2a95793d79e04201dcb38fb5ccdb86f1c74503b181a0b3fce0f332e3380bec",
- "dist/2022-08-09/cargo-beta-aarch64-pc-windows-msvc.tar.xz": "5b1b826315990487dc0afe561fecd7f937d82bc8d2898742592349c9993cb3c8",
- "dist/2022-08-09/cargo-beta-aarch64-unknown-linux-gnu.tar.gz": "23d20d9078154f6f03b3b056607b804ce3e049dfe4d949ccf430556c2f8e79b1",
- "dist/2022-08-09/cargo-beta-aarch64-unknown-linux-gnu.tar.xz": "d7a9283bf838c1182e6cc8904b6b464cd9c1dd342078b2447309797868aec7ff",
- "dist/2022-08-09/cargo-beta-aarch64-unknown-linux-musl.tar.gz": "fb82a87e759d5ab3536a8edba682793653f51c15d8f27a874ecbff11c991b1fc",
- "dist/2022-08-09/cargo-beta-aarch64-unknown-linux-musl.tar.xz": "21de23c600646a3d5a9421761c0411d7e173876278548c42d6a8794abfc5d3d2",
- "dist/2022-08-09/cargo-beta-arm-unknown-linux-gnueabi.tar.gz": "d931162577c04807b093d6cedcca989315b15b48f153c790f8ee2ce3edf479dd",
- "dist/2022-08-09/cargo-beta-arm-unknown-linux-gnueabi.tar.xz": "797752acf6d0b5814ff1c6e7dd5e54fec0716677c4a11f03143c6e769ed34b1f",
- "dist/2022-08-09/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz": "f080f5f486cbfbd09206cca7b70cb051da5e5a68914227a58ee81ecb5ed19c48",
- "dist/2022-08-09/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz": "79964e35d780df520ace77ec8035652571709620ba49f14ac7bc59008fd712f8",
- "dist/2022-08-09/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz": "339b969dcf283d0cedd0ffea09d534994c5bf588977ecfde61ce87e64090f70d",
- "dist/2022-08-09/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz": "601b6b0f58652434d888320851867b9188cb5a99b5f18358b0d7376dd98d4a0a",
- "dist/2022-08-09/cargo-beta-i686-pc-windows-gnu.tar.gz": "1c9944d71ff7ed9cd40be1df8fc828e7faa4f5496d917dc6ccc94a1b31839a12",
- "dist/2022-08-09/cargo-beta-i686-pc-windows-gnu.tar.xz": "4e3fc72b845b821e3aa1193250a10d494592f3e0917c7716b5bd963d0ad08b73",
- "dist/2022-08-09/cargo-beta-i686-pc-windows-msvc.tar.gz": "80559b267ab936cb08a7ef51087e63b61a215df64fa9f5254809705fa31ea3a6",
- "dist/2022-08-09/cargo-beta-i686-pc-windows-msvc.tar.xz": "aee6122d25a0bf7e083b45e622858acf34be3e17a1f9964a2f0d9fc7dfdbe164",
- "dist/2022-08-09/cargo-beta-i686-unknown-linux-gnu.tar.gz": "5e1f2c32adbdde68b7135d1e4c358076f5dd872c935ac1bbdcbfda0ff43e061f",
- "dist/2022-08-09/cargo-beta-i686-unknown-linux-gnu.tar.xz": "f0e6acd5158430866ad0bed435a7e0dbd9f35130b4805b990050b3d9751b4416",
- "dist/2022-08-09/cargo-beta-mips-unknown-linux-gnu.tar.gz": "99b71c9001b65408f42e65cf481a905bc30f77edc96401691fb9b122a20009fb",
- "dist/2022-08-09/cargo-beta-mips-unknown-linux-gnu.tar.xz": "08d1f3824354af35fb90d6563a7a51775ff9574a9ffe0c87e897e377be778598",
- "dist/2022-08-09/cargo-beta-mips64-unknown-linux-gnuabi64.tar.gz": "7dd3dc44fd37af40e587dba68d973b9d2cf40a939a97605a6adee5ba0f589215",
- "dist/2022-08-09/cargo-beta-mips64-unknown-linux-gnuabi64.tar.xz": "2f638fc21bb6e11fd59ff07d66a1012d3336d6dbd5b73b2c42461f84415e333d",
- "dist/2022-08-09/cargo-beta-mips64el-unknown-linux-gnuabi64.tar.gz": "ade7f1ba6f19382980b0a6cbe3e8035932a60c898f7141782921d22398b1cf03",
- "dist/2022-08-09/cargo-beta-mips64el-unknown-linux-gnuabi64.tar.xz": "2fe5d3b4102320fb1e36198e8d6e43d201986a2c58e4ad45fc2acb23865cf09f",
- "dist/2022-08-09/cargo-beta-mipsel-unknown-linux-gnu.tar.gz": "c99aa42005094a65bbeb41deb3499a2fd46032296f6509fd1bf108f7d4cbdd04",
- "dist/2022-08-09/cargo-beta-mipsel-unknown-linux-gnu.tar.xz": "7c65e8e6d76cab786f628e4ca02b6af47f6c2c4a36123ee7b8fc059309ce6039",
- "dist/2022-08-09/cargo-beta-powerpc-unknown-linux-gnu.tar.gz": "b7b98621ef835f6108c0d1ec60c88d66b462a3daf62ddfc796d31f4e09f7baca",
- "dist/2022-08-09/cargo-beta-powerpc-unknown-linux-gnu.tar.xz": "f85473a1d6b33d9b865a6532ea444415c7cfaa3f76af39d637619ffdddd0c00b",
- "dist/2022-08-09/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz": "e783ea66457a0aff31489cd42f549a2c9c33cbb2ca0aa253575a59c9de7f73f4",
- "dist/2022-08-09/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz": "d90a02cb425320fb5a5b163d87ec03726159994af31b32954c91b1cb9eb3ebf4",
- "dist/2022-08-09/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz": "ff1fda58f7bdb9df563172388f66f787a60f75bdd169ef8d2c791b27bbd8c6ce",
- "dist/2022-08-09/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz": "df740d61415bf15c62f9656932370e08fea5a289ac25bec78a626e771d24ab7f",
- "dist/2022-08-09/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz": "019dfff6fef8c7714dd19c64a5bb408b2b5063367ec1aa8a72960316aa880834",
- "dist/2022-08-09/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz": "f8d025d2943b76d665500c9c72aab18a63ecd137e07c10d1463f1905b4274573",
- "dist/2022-08-09/cargo-beta-s390x-unknown-linux-gnu.tar.gz": "c379f3c122a9df41b66f13ca3dcb79feb9ae68798c9704dac3018d85ec96d895",
- "dist/2022-08-09/cargo-beta-s390x-unknown-linux-gnu.tar.xz": "69bd9bd12f4386c831aaa8e1fac8edf1a0e618d1f6e0e4bcb7be16f1eeb08d81",
- "dist/2022-08-09/cargo-beta-x86_64-apple-darwin.tar.gz": "d74acf4f57681527af3a2c469e916bd9dc1ecdb591e16657757db493984de07a",
- "dist/2022-08-09/cargo-beta-x86_64-apple-darwin.tar.xz": "00201568ce4b95c6618389ec86e00677a27435276dd1bec16fea44b87a2dce4b",
- "dist/2022-08-09/cargo-beta-x86_64-pc-windows-gnu.tar.gz": "3d049963cdda9755e524443a04b0b9c5566cb0e58dc61e13779484d92c3e0f69",
- "dist/2022-08-09/cargo-beta-x86_64-pc-windows-gnu.tar.xz": "c8f0c13771e010ed85002ee10e44ad7c0b21ce2ddd105c9b90fbe1896d0687f0",
- "dist/2022-08-09/cargo-beta-x86_64-pc-windows-msvc.tar.gz": "796d9da5a59c984a5a78afcfce762134e3a72e52525d0150ebe4bb29d207bd00",
- "dist/2022-08-09/cargo-beta-x86_64-pc-windows-msvc.tar.xz": "1ec2764428d4df9355380444158aaa2947c4fe00708e7a30bc6978433621d120",
- "dist/2022-08-09/cargo-beta-x86_64-unknown-freebsd.tar.gz": "14643490b1259120b14a7be84942bfd324fdf03dcf473f33f7ae2466fb99f60b",
- "dist/2022-08-09/cargo-beta-x86_64-unknown-freebsd.tar.xz": "3af267ab65b3c61b0184f8fb5b5cc5fb61688f9b668654986f30d3a794d9a1a6",
- "dist/2022-08-09/cargo-beta-x86_64-unknown-illumos.tar.gz": "5b84826e0e68b6f9769ef7d00b4b1225cb1a4dde1b41f5b6021feb34e7376c63",
- "dist/2022-08-09/cargo-beta-x86_64-unknown-illumos.tar.xz": "a1dde17fd13bfb626acd0ae65d88c440a3172e1fdb20aeefda3a205bad71d703",
- "dist/2022-08-09/cargo-beta-x86_64-unknown-linux-gnu.tar.gz": "fedfd6a7b8156de19ed2d6b8f354affc4120fe311874e6563b9620634a5d962b",
- "dist/2022-08-09/cargo-beta-x86_64-unknown-linux-gnu.tar.xz": "8701ba8c8fcf21623a690d3b7c00d50fc9949035af5aee092ff1239bed4a9784",
- "dist/2022-08-09/cargo-beta-x86_64-unknown-linux-musl.tar.gz": "beac64aa92687f975b3a0d5265d7c79ef3962c1d0f0e196296f67bf8e2b803d0",
- "dist/2022-08-09/cargo-beta-x86_64-unknown-linux-musl.tar.xz": "8b50c03102cfdf5ffff5fa7b51b0a4d159e24671be5430c77c07256e54a4fd12",
- "dist/2022-08-09/cargo-beta-x86_64-unknown-netbsd.tar.gz": "dde29de6d56f02f5cdf35b96287e404ceed85372c3c671ee54972255369470fe",
- "dist/2022-08-09/cargo-beta-x86_64-unknown-netbsd.tar.xz": "7e4d07676c4b33af0303436442dc763623784ed20293ed262780267b01843949",
- "dist/2022-08-09/rust-std-beta-aarch64-apple-darwin.tar.gz": "63526962ddd66cd6b60f5bb348861caab7a78c7d80515baf4397cab857258d71",
- "dist/2022-08-09/rust-std-beta-aarch64-apple-darwin.tar.xz": "64ba52408086b6c7e3578e2ded98528fd63466ec8d7f86045b55a575e0ca780b",
- "dist/2022-08-09/rust-std-beta-aarch64-apple-ios-sim.tar.gz": "261109cb02193cfa113697c9d769fcd4d4e265b01dbb4ca0bfa0e0d62f64c71e",
- "dist/2022-08-09/rust-std-beta-aarch64-apple-ios-sim.tar.xz": "7f2182b749a8980c2e7b4f31e5a8c72b8a9df8dd2552cda29d061e2236acc589",
- "dist/2022-08-09/rust-std-beta-aarch64-apple-ios.tar.gz": "5abe9df533e7bfd62586483f20a1911be5eede5da6114b53b2a000c110e35768",
- "dist/2022-08-09/rust-std-beta-aarch64-apple-ios.tar.xz": "2898b8ad95c6a67c2c296c4698acd043cadcb3a2b0449fb7515bfeb48db69a70",
- "dist/2022-08-09/rust-std-beta-aarch64-fuchsia.tar.gz": "9f86fcd871ddef0fb86cdfb0084d1bf6fa150bb7cb4dfe3fcade13b75beea5a6",
- "dist/2022-08-09/rust-std-beta-aarch64-fuchsia.tar.xz": "4aa91e4f7f3589085c2da45de78a16c48cfe9dd47541928db87ee964c3406b53",
- "dist/2022-08-09/rust-std-beta-aarch64-linux-android.tar.gz": "910f4c1ad41d74306e7fdae43c8d04e7fc47724ae84b9276f2d264fbdf2b70f1",
- "dist/2022-08-09/rust-std-beta-aarch64-linux-android.tar.xz": "cd60ac333fd684cb730c134cf5a037718254afb9aa65528b2463d76a138f07f7",
- "dist/2022-08-09/rust-std-beta-aarch64-pc-windows-msvc.tar.gz": "39740739479c12203c9b6ae7c72f0e542c92750e08b2ca66c5e96355f1eea61f",
- "dist/2022-08-09/rust-std-beta-aarch64-pc-windows-msvc.tar.xz": "1c7fb416b404c401f34d4461c61c95ffbaa5b50d8c95d090485aabe12cc78a0c",
- "dist/2022-08-09/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz": "8cd2b395607ba16cac4a4fb85f1c5ccf9626849a8e11dc66e911053a6cd1df1b",
- "dist/2022-08-09/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz": "c7ef35d4bb2ab6b8ad762349821b6e076851bd5b51dddb7a3b1678b78e21cb42",
- "dist/2022-08-09/rust-std-beta-aarch64-unknown-linux-musl.tar.gz": "6410dc79e6e00eca4e483b438ef8a314a55ea6857c618efd50a32a2f95195df8",
- "dist/2022-08-09/rust-std-beta-aarch64-unknown-linux-musl.tar.xz": "4d674cbb7d417944a1aaaa23e8c654ac9276529fed3639c75421e1de1e7bfcc2",
- "dist/2022-08-09/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz": "1719104655a62e8300df06fb4636543532821c0ac43dcf90435d5fbbf39c12c1",
- "dist/2022-08-09/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz": "bf3c12dae8fee831402f9700295d9a6718d6a6046a7e5a0d0748c3f2e3d82224",
- "dist/2022-08-09/rust-std-beta-aarch64-unknown-none.tar.gz": "727621bd08ad0e879a79635fb00c187de6d23e0291143b494d3965cacb35d358",
- "dist/2022-08-09/rust-std-beta-aarch64-unknown-none.tar.xz": "6b152098c76b2a3367dad676e4c3d328e5d5c89c95504b041e1997e5d141404a",
- "dist/2022-08-09/rust-std-beta-arm-linux-androideabi.tar.gz": "c78d9c97e742ef1777679743765de5d475dffe62ea9b60efe2025eb91ba7ba5c",
- "dist/2022-08-09/rust-std-beta-arm-linux-androideabi.tar.xz": "1029382048bce0b8e556b87008821f8b4c5c92e30b340c9a8086e32a8ca7f105",
- "dist/2022-08-09/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz": "d4b8bb607968ca49d840c1835fc56e92006dd186c7ed71663a23b7495fa392e9",
- "dist/2022-08-09/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz": "562dcbfb8ddd31e64939844055c3dbd87a4c04065ad34dbe3a2d87136c0bb4b4",
- "dist/2022-08-09/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz": "1d8e8d87ce8f1ee5e8cb946c8b7a92e6f4a56ddd6b3afd1c646e4d5cdb703608",
- "dist/2022-08-09/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz": "ce8edce3a2976694a8ca0cff0b296b331f64e0afa6b5cb107bf03e2aff5949c1",
- "dist/2022-08-09/rust-std-beta-arm-unknown-linux-musleabi.tar.gz": "d57aff420435c76ff8bfaaaa8d94bce62d96299a2a0158b67b9fe914695cc188",
- "dist/2022-08-09/rust-std-beta-arm-unknown-linux-musleabi.tar.xz": "7eecf0facd3f2dd236e5f731eaf3fbc51b46d1a039f762f5bb42aabd0ab21c00",
- "dist/2022-08-09/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz": "6b6341eeca9a48e1c15b03501428ebdd2f591f4196c50d988408a1c591152fb0",
- "dist/2022-08-09/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz": "019e36e7c8855ef9d1277d4f5388862412c6d49e7f16d6808d3a6f69c60e5030",
- "dist/2022-08-09/rust-std-beta-armebv7r-none-eabi.tar.gz": "41b1cfbcfd16061d52413e0bf2503756ffbe819b4ef2d81f8570e477e0b5893b",
- "dist/2022-08-09/rust-std-beta-armebv7r-none-eabi.tar.xz": "209828e95d236ee218a10f468b12d64e4f4bb773a0cbfc247e6f54c2aacdc8a5",
- "dist/2022-08-09/rust-std-beta-armebv7r-none-eabihf.tar.gz": "762a5764e926cb081062040cc4f43f8653eab007ac9cb8600751c0f364f17639",
- "dist/2022-08-09/rust-std-beta-armebv7r-none-eabihf.tar.xz": "eb9b66ad16ff5c7427112ed5cf73498a02694b7b3af37220e3160bb6a16eca1c",
- "dist/2022-08-09/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz": "369b3afe600692f552f7d0bdd310c7b254f8745948e5a4ffff7c5fccf0873ce4",
- "dist/2022-08-09/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz": "2cdeb7b58e979bec71aa0a67a95ede5b55ab6ac295b24b08c67e651c4a76b13a",
- "dist/2022-08-09/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz": "1a31c291792ba44053c9701d8e665c62155cb319b371359c7f0f9b5b81269f6b",
- "dist/2022-08-09/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz": "54b17f4959cff54daf926f33dad8141663366856f691f4ca1015fedf36720022",
- "dist/2022-08-09/rust-std-beta-armv7-linux-androideabi.tar.gz": "77ca2251056b76db72b9e8795dfc1b9583544f35dd067435128b2336b33aa892",
- "dist/2022-08-09/rust-std-beta-armv7-linux-androideabi.tar.xz": "888b80410a8f9f1f85ec379c4e5490fc18bfb3f7e4954fa66f9b9c27ea92d121",
- "dist/2022-08-09/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz": "3b8132040574da39cafbf48cc1e2535ab1c52c51ac2968aaf167911bb4bab648",
- "dist/2022-08-09/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz": "915acd9a715cb25db4e899315afbb2b97ad34aee52a7f4536b4e684295e93e8b",
- "dist/2022-08-09/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz": "9a1e59513f6812f631fa4926ea794b05524e58cd4d36a55b7a4e286b2e7bd506",
- "dist/2022-08-09/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz": "017892f81ce10323b76dd32347d5ba921003177fd95e5d4b4972ad4e84e699c8",
- "dist/2022-08-09/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz": "3a7b2f2c4d06135a9ff3931a1a9c0f4431bb3a31763501bbad1dbfb85849e681",
- "dist/2022-08-09/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz": "6226c7228337739435cd7194fe3c233250fb2eb8df4dd16c9ef637b2d91c1807",
- "dist/2022-08-09/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz": "48b626650f72a7e3885ea2aabbb8c520b079ebda0f798f685392501f1ef7fd79",
- "dist/2022-08-09/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz": "627a0be5e0059cc07a4bfd99a0f70e009367c7be6ea45eb4ac0f8554680309e4",
- "dist/2022-08-09/rust-std-beta-armv7a-none-eabi.tar.gz": "2489ef3102a8b0ba9885c092309a2e7deaf65721c934a83259fa7f7698440638",
- "dist/2022-08-09/rust-std-beta-armv7a-none-eabi.tar.xz": "0e43df943f9550e374ba9334a0bd68f3bd107dc68ee8012f92357aaa0bf3851e",
- "dist/2022-08-09/rust-std-beta-armv7r-none-eabi.tar.gz": "edb09fe24634386fe335685bbced045cd7448d1aad520f1cbaa4f0d38f3d38a1",
- "dist/2022-08-09/rust-std-beta-armv7r-none-eabi.tar.xz": "2b170a322d07da39e949047871dd28444feaf49d09c901fbdd4015f00e456415",
- "dist/2022-08-09/rust-std-beta-armv7r-none-eabihf.tar.gz": "82a69f097820c70c0fdc9b5f22b5378ab1d70d3d35bfec0ebab8b30ad7903178",
- "dist/2022-08-09/rust-std-beta-armv7r-none-eabihf.tar.xz": "0301f9984effc3b186531aacb182e65fb57519c5ebd80f445c7346db27dfcc39",
- "dist/2022-08-09/rust-std-beta-asmjs-unknown-emscripten.tar.gz": "47bc8975498b6f6afdce3ef7d2d8d4b239b6219f5e4bae9841a82587e1279415",
- "dist/2022-08-09/rust-std-beta-asmjs-unknown-emscripten.tar.xz": "0806ef9782068d292106f3fce8b7bf879931fb89efc5e832d85f82bbffa99489",
- "dist/2022-08-09/rust-std-beta-i586-pc-windows-msvc.tar.gz": "fb11022f5410c508936617b337d8940d82431544f77366ba6dbc3da81172bd21",
- "dist/2022-08-09/rust-std-beta-i586-pc-windows-msvc.tar.xz": "326bc6a7dfe216b03222b187ed424ca6b83325f9a91e0b873d9294c713040a37",
- "dist/2022-08-09/rust-std-beta-i586-unknown-linux-gnu.tar.gz": "0a608449de9eca7fbb421263fee19d22a3950f68dee80ca343c8704b2a9fcefb",
- "dist/2022-08-09/rust-std-beta-i586-unknown-linux-gnu.tar.xz": "ca980e9d255d4444a329983565eb15807a26c0cdb48bfe763f9fdee061d8a9e4",
- "dist/2022-08-09/rust-std-beta-i586-unknown-linux-musl.tar.gz": "db8c6517bb71bf49470b87cc1c9bd180c0421475bad04917be988c0933620db6",
- "dist/2022-08-09/rust-std-beta-i586-unknown-linux-musl.tar.xz": "2feccd2d2ec54e25ac36ec2b9ff823887d353c9ca1106a29f8ca0f33f2e5d08a",
- "dist/2022-08-09/rust-std-beta-i686-linux-android.tar.gz": "0ce00226f5aa2165d043108650e7444e165ae80c985c62f953b46fd680946b5a",
- "dist/2022-08-09/rust-std-beta-i686-linux-android.tar.xz": "e591b8ae11fa7b6b8907d598e159c8795e4c3ff9c2c55e65773c29e488f64550",
- "dist/2022-08-09/rust-std-beta-i686-pc-windows-gnu.tar.gz": "51fa05e5fb2d0d310b97c9e255ea3bc5ecb185093013406588cc96fddba18788",
- "dist/2022-08-09/rust-std-beta-i686-pc-windows-gnu.tar.xz": "64d87c682eb1a2e0b8abd393358e1880016803bb7da699f2d239ad2d59e0eb0c",
- "dist/2022-08-09/rust-std-beta-i686-pc-windows-msvc.tar.gz": "e3c2df5572996ade55a9af5b5b54a84fc0ff2edfcad164f57852ab0d3695d938",
- "dist/2022-08-09/rust-std-beta-i686-pc-windows-msvc.tar.xz": "2991215756373d253ffd27a8f650613a5f2bb636adf263b664741772c594241a",
- "dist/2022-08-09/rust-std-beta-i686-unknown-freebsd.tar.gz": "bf82e2e7734304f12d3ed5db591ece38a1078b575d92aab8f45328d087cf56ba",
- "dist/2022-08-09/rust-std-beta-i686-unknown-freebsd.tar.xz": "73a3b79fe72e835a8b7dfdaba6a1b9c5ee8dde25534fcc520b0ee80a7853b31a",
- "dist/2022-08-09/rust-std-beta-i686-unknown-linux-gnu.tar.gz": "74e23ff14e240e935f630e19467bbc9e1b02e9962313dc74f8c444c96bcbfb2e",
- "dist/2022-08-09/rust-std-beta-i686-unknown-linux-gnu.tar.xz": "987003e5b23308f31f613a5a133466cd045c05a5998ab29e4d3d8ef15a8342d9",
- "dist/2022-08-09/rust-std-beta-i686-unknown-linux-musl.tar.gz": "1c76bac0701c3f9352f4439d2daecb219c08964c6a48ceb212520b4a8e532e02",
- "dist/2022-08-09/rust-std-beta-i686-unknown-linux-musl.tar.xz": "7007e60d8ba5e9e40f6aefa248b4881057a9b9a73aa9e7ff0c67a36de7748be3",
- "dist/2022-08-09/rust-std-beta-mips-unknown-linux-gnu.tar.gz": "0f6ca4637da137235a3e9315691f1b5c0afc2f853a855458ad6d10ea59ef8579",
- "dist/2022-08-09/rust-std-beta-mips-unknown-linux-gnu.tar.xz": "778557cf1c57ada5c1a64ecd58db3a49e8f874217664d53a8be0cfb89b031980",
- "dist/2022-08-09/rust-std-beta-mips-unknown-linux-musl.tar.gz": "cbe799d70ca9a24645187d61198c40d495aeed8ef037570ed3cad95b898f6c82",
- "dist/2022-08-09/rust-std-beta-mips-unknown-linux-musl.tar.xz": "ab37bff0f1c90a6adb9d603041d5fbe7c94dccfa05a6171145c41335350d02a9",
- "dist/2022-08-09/rust-std-beta-mips64-unknown-linux-gnuabi64.tar.gz": "3e5aa74f970a70614751d258b7958696457b03c3f8c6eb2b8e61f3395d9f9169",
- "dist/2022-08-09/rust-std-beta-mips64-unknown-linux-gnuabi64.tar.xz": "e32f82fa27968b9f9cc0bad083daae95cc21bc7f56b6fed8e57de39200398248",
- "dist/2022-08-09/rust-std-beta-mips64-unknown-linux-muslabi64.tar.gz": "29247f72e5d5563dbfaff65876cd1181943c57121e27a979c11005734825de4f",
- "dist/2022-08-09/rust-std-beta-mips64-unknown-linux-muslabi64.tar.xz": "1835664e58cd35261f7387be0746bfd821a8d8108ef8f26dc68db1b4886cc6b6",
- "dist/2022-08-09/rust-std-beta-mips64el-unknown-linux-gnuabi64.tar.gz": "eb6826841be8177bead8edf3d06892219fe7e898e541d2a76539e49c7cf2caa7",
- "dist/2022-08-09/rust-std-beta-mips64el-unknown-linux-gnuabi64.tar.xz": "25c2801f324985094cf3aa9aa56d88e2c73bcd1f7849fdbfd11032fb9d853bc9",
- "dist/2022-08-09/rust-std-beta-mips64el-unknown-linux-muslabi64.tar.gz": "573f981b5e197ef337812fb3bcc4ee513121067968d3d33e369426ec51cfdb8e",
- "dist/2022-08-09/rust-std-beta-mips64el-unknown-linux-muslabi64.tar.xz": "da6d57cff4999bec0e4249c0b1f1048eae925e793149c60f044b257f306d26bf",
- "dist/2022-08-09/rust-std-beta-mipsel-unknown-linux-gnu.tar.gz": "8637387cb7f722457e7cdbb6c75eb70fdb913a5543adb1e4dd81d7316f0d2520",
- "dist/2022-08-09/rust-std-beta-mipsel-unknown-linux-gnu.tar.xz": "539a0a66e06fe7b49e40b2615c3d04611236746419bf03f5439b14cc37f3344e",
- "dist/2022-08-09/rust-std-beta-mipsel-unknown-linux-musl.tar.gz": "458734ed82bb7e3feb1d831ea6224cd4eeabfbcb06599181d025660298ef7bbe",
- "dist/2022-08-09/rust-std-beta-mipsel-unknown-linux-musl.tar.xz": "64f0ce96ae9a99daa300d49d087837c7c619e4bdb38f41664d79844c84254c32",
- "dist/2022-08-09/rust-std-beta-nvptx64-nvidia-cuda.tar.gz": "937c3b1481aeab47b45dcb8dc45c12f856a771615c3b6e56b04e10b4f8075d1d",
- "dist/2022-08-09/rust-std-beta-nvptx64-nvidia-cuda.tar.xz": "05094bfba7b7fe1fe66f0643ed60079c675d739613e27cb64ca8a61b5faadcd0",
- "dist/2022-08-09/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz": "d15c897c52bc02a241e9756c62b48c126d651d498a72a3cc70521e6a395bce7c",
- "dist/2022-08-09/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz": "2182c3300e6cf5f7fba5a687235c34fa9e799beef2edbb07206205ede50964ad",
- "dist/2022-08-09/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz": "cba214c9827f639f6c88de0bb28f81c58e81f1263b10ccb4fda5c84ad23ea036",
- "dist/2022-08-09/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz": "f7f9bb0d56ebd4b80db0560d6f71b25028c97b99f79eb7ef89255d46ca4df22e",
- "dist/2022-08-09/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz": "b190a078b408d184766831664df1a2d472a134909ad00f195fb84d522d360a56",
- "dist/2022-08-09/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz": "31bcf275272e3a46618110219f61eefd5fdd2c11a75f7ff612fea012dbc3c4b5",
- "dist/2022-08-09/rust-std-beta-riscv32i-unknown-none-elf.tar.gz": "eadde81672eda5af55c8f2eff6729ad134dd484ac16aef4fa0e402e0164f30df",
- "dist/2022-08-09/rust-std-beta-riscv32i-unknown-none-elf.tar.xz": "2a5d69f4ec463377e2f9eca3a70647680d1ed09bef4f9a7b839d1d08eed989ca",
- "dist/2022-08-09/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz": "89b9b9149461362d54b64b2cbdec317674266a9061fc0d46b438f664e003ac83",
- "dist/2022-08-09/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz": "3c56a23f2fda148e442318bb5a21d895a5b61cc698db5ea9a1ff3c2740887fc4",
- "dist/2022-08-09/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz": "94f9f155ce07f09ca12814b745cce5d6dfc731cc59c95fcd5b1831f60d9c04fb",
- "dist/2022-08-09/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz": "b9f76d28c4e057d9b7eada2076c4358d6876aed1c32f65dfa50dc3e3c1970d79",
- "dist/2022-08-09/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz": "e664018f445157229a0c07406d3fc4e7a19cb53722fc3838fcee1bbd3a3a00cf",
- "dist/2022-08-09/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz": "8d48a304ba7ca9470ed5f88076f6c50477f6b3a1afe303aab772638df8978c32",
- "dist/2022-08-09/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz": "347072268911f3a03223bd494cd2d14cfe4c553fc35e9d36571b54995e04d9aa",
- "dist/2022-08-09/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz": "100d5a1e75b7c302f03eba8eb26c5c023e1b396cd44d7798f2902bf8f40ec48b",
- "dist/2022-08-09/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz": "f5d91e04ac7980aa5253e0c90e7e312e42b5fa7de03f35f61fe11ab207894f50",
- "dist/2022-08-09/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz": "0d83e81a8c48f9cc3e62442e51aee769cef4cc618a2661506a5fb9120163be25",
- "dist/2022-08-09/rust-std-beta-s390x-unknown-linux-gnu.tar.gz": "96d983517839cbd1d64adfff4b3b0241ceed99573579f041d136be39aa2b7995",
- "dist/2022-08-09/rust-std-beta-s390x-unknown-linux-gnu.tar.xz": "0eb2c70b7eeb3f754ffea3716e41dc3efae49e98d77ca1843541587dddd6b880",
- "dist/2022-08-09/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz": "91fa2a025c04e41a7b9a7eb6b4cda351b93fc53801f97042ae0e76bd871d92e9",
- "dist/2022-08-09/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz": "8ad9da4410688824d5adc6cb8ed42f76bb8a35d41bee62b5f7b44e0d6bd346bb",
- "dist/2022-08-09/rust-std-beta-sparcv9-sun-solaris.tar.gz": "cb3a28d68f432a3357b6a971f046b5ea37fdad5d4aaa4aea7cc997356d487b75",
- "dist/2022-08-09/rust-std-beta-sparcv9-sun-solaris.tar.xz": "746fb859655ed6b4379fce8937fff998952cb81297412091844b0e277ec65a23",
- "dist/2022-08-09/rust-std-beta-thumbv6m-none-eabi.tar.gz": "8bab8c46c7e873d0fa0a7078675fa7a54a90e5342f8b58419e0dd434e9942b67",
- "dist/2022-08-09/rust-std-beta-thumbv6m-none-eabi.tar.xz": "954b5c32e5515e09b77bcaa3a7c4e139905a4f3d1801b492069c027a8311bc6b",
- "dist/2022-08-09/rust-std-beta-thumbv7em-none-eabi.tar.gz": "9eb56471c38528c2b80dea81d94e5368f68a977745a1e02449f3eae353ca405f",
- "dist/2022-08-09/rust-std-beta-thumbv7em-none-eabi.tar.xz": "0334379d0e8c3ef9680837b1dfc96788da4836a91e42d71446461ece7217ab78",
- "dist/2022-08-09/rust-std-beta-thumbv7em-none-eabihf.tar.gz": "0c65cc553ca2730d020fd8a97b90ebfe2b3c0fc2755b137df55955a3d1c7908f",
- "dist/2022-08-09/rust-std-beta-thumbv7em-none-eabihf.tar.xz": "625a109a589fae6e2fb64376e3fec4bb39ca19217ad6d312df524129868f343b",
- "dist/2022-08-09/rust-std-beta-thumbv7m-none-eabi.tar.gz": "e0002c917055c85b613297da21427fc3b6c5f55ee120921ee8bae9b99afec2b6",
- "dist/2022-08-09/rust-std-beta-thumbv7m-none-eabi.tar.xz": "74cc0f5b245b483b62c9a9e8bd642069af5205ecc59fd0b7464ab1fd6f4f3f7e",
- "dist/2022-08-09/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz": "98c09985ea2d56fcc8c219c46d355310b65fd4f7c6920df2e7ec5355848ba83d",
- "dist/2022-08-09/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz": "5122e60b031458c4606905a3df5d64b7bac54bcf883c18ba53443aa76fc5c1cf",
- "dist/2022-08-09/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz": "6ab96971e69a073a5789c3eaaa460a7f5d6a3a5df8ac5125344744dde8476706",
- "dist/2022-08-09/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz": "be874a256076dd2869d6d5780c5b4e8c850e52a62b5d10087d7c1aca32b0b831",
- "dist/2022-08-09/rust-std-beta-thumbv8m.base-none-eabi.tar.gz": "c37ab11b56d3ac41b8eeadbed09ae5e78467c8945cecb49c218fe91a284e43c0",
- "dist/2022-08-09/rust-std-beta-thumbv8m.base-none-eabi.tar.xz": "dfa4d1dcfb32d4001b2b9f00a829afd699065dcc6538598eaa27d684a01facac",
- "dist/2022-08-09/rust-std-beta-thumbv8m.main-none-eabi.tar.gz": "31931cdb16a9a81bf7fc779c8aef6890c1f35130ba1c323d8b548a5dd5494346",
- "dist/2022-08-09/rust-std-beta-thumbv8m.main-none-eabi.tar.xz": "0aeb4ff0e9b139e3b5550103c6429cb3cb4b455335514b07491e22fa63719169",
- "dist/2022-08-09/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz": "ba7077816ba6f1b6b386b0245249e10a1482b82eaec711561f912a1975482817",
- "dist/2022-08-09/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz": "6a49c1907e0ce91a3650aeeb61f6674c39936b67366fa4bb197fc612e9ae5a49",
- "dist/2022-08-09/rust-std-beta-wasm32-unknown-emscripten.tar.gz": "0e268083c1935be97e3a96ca94b824c84877970a65c2e581f3e71d7364264dff",
- "dist/2022-08-09/rust-std-beta-wasm32-unknown-emscripten.tar.xz": "27510dbb922d9cc0ee669f3854ffeb1ca28c105b3284e3ba523abcb9cfc10055",
- "dist/2022-08-09/rust-std-beta-wasm32-unknown-unknown.tar.gz": "6d2f49e7e75ca2e3873dd1530d4bc3eedcebf3b2db6b1e193c0d57d7daa681a3",
- "dist/2022-08-09/rust-std-beta-wasm32-unknown-unknown.tar.xz": "5508d36c7ae20176af2c10a0c974d5162202e3914e79468b9541c3e1b585612a",
- "dist/2022-08-09/rust-std-beta-wasm32-wasi.tar.gz": "721726c4b073b27df9abc9e47a2b82eb48ca12a44c87363ae57a632d26619fc9",
- "dist/2022-08-09/rust-std-beta-wasm32-wasi.tar.xz": "320ae842da96d4ba3147ca338e91306ed602250320df5163a01748491599ade3",
- "dist/2022-08-09/rust-std-beta-x86_64-apple-darwin.tar.gz": "2dd9b9defe95357d7af5ae81428be814efe9923c308a4c2f3a370b16d912707f",
- "dist/2022-08-09/rust-std-beta-x86_64-apple-darwin.tar.xz": "17ea6b22feb7fef1211c319a3d9d0f7bcdd8cd215d3ddffc523a83cfeb8331a6",
- "dist/2022-08-09/rust-std-beta-x86_64-apple-ios.tar.gz": "df3618fb0b30d89d567d238d4e744d8cff81ca07cef701876b4471a98768c810",
- "dist/2022-08-09/rust-std-beta-x86_64-apple-ios.tar.xz": "60932e2848af4a25699e7d300833f0b8e10bc8dc201d4ac9dcda417f86413cb8",
- "dist/2022-08-09/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz": "57db93d53efad36639645baf0cdfa47b4b70664e22a1eac3f08a5abedc0834ac",
- "dist/2022-08-09/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz": "cea91317cf86ec301730a2ef8a1c68213b6a4921c105bab228e9bd8620641f2b",
- "dist/2022-08-09/rust-std-beta-x86_64-fuchsia.tar.gz": "7b961a55c6616b02aa91a25eb9e2521bd50ac881120975ea3af92543adfdd8b5",
- "dist/2022-08-09/rust-std-beta-x86_64-fuchsia.tar.xz": "3ce7314f2c8f3a00430a6686fa267a5b4a8ef20b28023b765aa326f0151d2521",
- "dist/2022-08-09/rust-std-beta-x86_64-linux-android.tar.gz": "37d538db9ab3cad73c4cdfb0b3836f124a6a2e47dbd1f6aa54167451057ced2f",
- "dist/2022-08-09/rust-std-beta-x86_64-linux-android.tar.xz": "600ea7bc318580195d3b4e681c5f374075ec40272641050f0fd6296da4a9d383",
- "dist/2022-08-09/rust-std-beta-x86_64-pc-solaris.tar.gz": "7d389f2d2632068cde7e8f72e685a888462897f4d5aa98034060472443cb89ff",
- "dist/2022-08-09/rust-std-beta-x86_64-pc-solaris.tar.xz": "60a6532403d902d71883169c2001d6b279f882bee4444bb8107e742e4cfa3ecc",
- "dist/2022-08-09/rust-std-beta-x86_64-pc-windows-gnu.tar.gz": "76c93994bc6eaa7596ee91dd24f51685ab659533835d441352f88b89a0991236",
- "dist/2022-08-09/rust-std-beta-x86_64-pc-windows-gnu.tar.xz": "fdba628daf4b04afc1ec1d8ac4b9460ffe0a230feaf61dd7b621e2ec02b09a2b",
- "dist/2022-08-09/rust-std-beta-x86_64-pc-windows-msvc.tar.gz": "4ee478c238ec38ec4977fa8a1689aa28b7a2b0173549da92f1b2e1a2f6772aee",
- "dist/2022-08-09/rust-std-beta-x86_64-pc-windows-msvc.tar.xz": "c732f3054a4a67f25242ba9acb5bfbc945e3ac62a2b0de5b7ede0990313984b5",
- "dist/2022-08-09/rust-std-beta-x86_64-sun-solaris.tar.gz": "47a4e8d8cc03e402e480ce5f24326de7094622fe339e4441df7f6a6271a8775c",
- "dist/2022-08-09/rust-std-beta-x86_64-sun-solaris.tar.xz": "260c93c4dafb20d476a965e2681751e38be15bfb96d32b10730e44a1d4da6079",
- "dist/2022-08-09/rust-std-beta-x86_64-unknown-freebsd.tar.gz": "9d7c63a2ced8041b21459192a0176665ad93324c3774f3028b411eacafd9bfe8",
- "dist/2022-08-09/rust-std-beta-x86_64-unknown-freebsd.tar.xz": "276235e7efd773273ffe281cfe5119a9fd361f04ddb1f3ed060d0abb983ff55d",
- "dist/2022-08-09/rust-std-beta-x86_64-unknown-illumos.tar.gz": "00ee12a1d47b1d00403e8f2d083a0dbfc8bbbf86ba973b08fbf4c59333f70052",
- "dist/2022-08-09/rust-std-beta-x86_64-unknown-illumos.tar.xz": "e11ffdd664228acb94e8e92ae919f7c3bf354083a1ca586be4fa65b7abac17eb",
- "dist/2022-08-09/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz": "d58aedd100cf952c52b8364731b1ecc05567c4d6be4899d36f210ff619b934dd",
- "dist/2022-08-09/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz": "28021e5a4ff36813f76f6aeb063fefaf58310d65993557df2128ff5b9eeb4f75",
- "dist/2022-08-09/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz": "ef5ed09b2bd6b9c1772aeb66444dd821176b4aefb27e657db5bbd24f09d9c104",
- "dist/2022-08-09/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz": "c41edbd8ec4266555b7824594eef470ca6d7695675cf13303dcca6b248d0b692",
- "dist/2022-08-09/rust-std-beta-x86_64-unknown-linux-musl.tar.gz": "575e5b74248f2503de16294d10e6fec2f6bacac9082ffd73e0395ac2669aefda",
- "dist/2022-08-09/rust-std-beta-x86_64-unknown-linux-musl.tar.xz": "4b0f56d6a004c6ebdf325ece1d8006795ffb404ea4f1e958a1425ba9edb28b18",
- "dist/2022-08-09/rust-std-beta-x86_64-unknown-netbsd.tar.gz": "9e39545494934be7f91675de58907b7b71fe383c535b55799add8c5706371dcb",
- "dist/2022-08-09/rust-std-beta-x86_64-unknown-netbsd.tar.xz": "3807db4641082a329edbd08654f40f769e44d139edc99be0b559c8c00c27adbf",
- "dist/2022-08-09/rust-std-beta-x86_64-unknown-none.tar.gz": "4eeddd2654e6a84ef5ad5a6f90d88dab043ca087f884e640c945a4565c6b20e2",
- "dist/2022-08-09/rust-std-beta-x86_64-unknown-none.tar.xz": "656e1ff5941fe4699ac16d6c1cdd715fca3a7980b4159644b856af08d8b47918",
- "dist/2022-08-09/rust-std-beta-x86_64-unknown-redox.tar.gz": "0e0d5de37c86d6a0684b627f16dd6adebbd2ab4776f4a3fb6a386b3ebe9cdbb2",
- "dist/2022-08-09/rust-std-beta-x86_64-unknown-redox.tar.xz": "dc79147efac5730201980035d1ae377cda5a18bb446647e1bc8375c5d645b1c9",
- "dist/2022-08-09/rustc-beta-aarch64-apple-darwin.tar.gz": "a3916d7a71d422a0118363228bfcca4a72abd08a6fe6c6d3b4888b87846a1bbf",
- "dist/2022-08-09/rustc-beta-aarch64-apple-darwin.tar.xz": "f18d8918c9c1ff55cc513fffa309d72b9aeb853f92dfa66e34fb151a2c374269",
- "dist/2022-08-09/rustc-beta-aarch64-pc-windows-msvc.tar.gz": "ba008d21693e01291e3016773f728c98040903eaa6a71abda23f8043eb9348b6",
- "dist/2022-08-09/rustc-beta-aarch64-pc-windows-msvc.tar.xz": "1333977a0a147bd5f05eef63ca859b8e78ddc51bed2202616ce5fda5a9203080",
- "dist/2022-08-09/rustc-beta-aarch64-unknown-linux-gnu.tar.gz": "78e40c65e40a287e9ee2d9178f84c8ff7277c2a5beff67555185d79fee1af44b",
- "dist/2022-08-09/rustc-beta-aarch64-unknown-linux-gnu.tar.xz": "ce24270b141952b3fb67c0f43896416dd77be66ed3b311857bff8b07121e1de9",
- "dist/2022-08-09/rustc-beta-aarch64-unknown-linux-musl.tar.gz": "6557c13f5014acda10349a99e24749224b4f86e55b6499e35f05b5e583a1fed3",
- "dist/2022-08-09/rustc-beta-aarch64-unknown-linux-musl.tar.xz": "d4b1b814bfa277f895ef4a6c8963ce482965fbdf83ba0661b11db8df39ef301c",
- "dist/2022-08-09/rustc-beta-arm-unknown-linux-gnueabi.tar.gz": "3fd9fdfe30333a0a99cf8e9fe754cb1b8f8734b5d1ab47aaf9490d8eaf3b29b8",
- "dist/2022-08-09/rustc-beta-arm-unknown-linux-gnueabi.tar.xz": "a736efa35fab54ebf36aebf5c7f5b6d4f0ec1f7b0e5b8b3acf2720d97b0c7d93",
- "dist/2022-08-09/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz": "5b824f9aa8a77f8a21d8cf68751924739e65d599c0fe561ea9383b8de4132315",
- "dist/2022-08-09/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz": "9996ad84db143044b454483cc24c6ad5c333edd7988ab4ef221237d37891e26d",
- "dist/2022-08-09/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz": "6fcbf77721d4d5c51fe5c580f0ff8ac7347d6c49895da33a3eca19e505cc752d",
- "dist/2022-08-09/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz": "3b4e566667ee1b2e9fdb5ac6cde4aa3ddf9c9739e7e64e519f2204db2048bd20",
- "dist/2022-08-09/rustc-beta-i686-pc-windows-gnu.tar.gz": "cc4f775b79c3ad51bf6ff558267100e05c6fa12ac378d69cc0b0affc37b541a9",
- "dist/2022-08-09/rustc-beta-i686-pc-windows-gnu.tar.xz": "b646befe7d3ad1b0a30c9eb4e14a6e401f1346e11f88145ee17043de2b6f4df0",
- "dist/2022-08-09/rustc-beta-i686-pc-windows-msvc.tar.gz": "9b4847beb5faf417bc24b4b963f40c8c8dce55b73658eacb10d2d1c968c676b5",
- "dist/2022-08-09/rustc-beta-i686-pc-windows-msvc.tar.xz": "6b57826d56686f481d2a20e90994a2af6cb9886c063b1dc08c3bb181ce248a69",
- "dist/2022-08-09/rustc-beta-i686-unknown-linux-gnu.tar.gz": "ea34b82db9cc6d079a67df542036c83e8661d435d15802976676671cb2b27c24",
- "dist/2022-08-09/rustc-beta-i686-unknown-linux-gnu.tar.xz": "d679a401e807b12fa8fd3686d3ac09374d3c8d660c6f0a571efc7c9217a05f09",
- "dist/2022-08-09/rustc-beta-mips-unknown-linux-gnu.tar.gz": "cb3c3638460f3d71c94ca10ab9684ebdbb25f886b8604e62e8412361be56e579",
- "dist/2022-08-09/rustc-beta-mips-unknown-linux-gnu.tar.xz": "0665c0e3b2ac28648c2a0ac49670181a737ae30fc74add44a2ee612b1d74dd24",
- "dist/2022-08-09/rustc-beta-mips64-unknown-linux-gnuabi64.tar.gz": "0acc722d22b787adbe16549edbf28debdd4d84633701f8bb7514d5911d6ff124",
- "dist/2022-08-09/rustc-beta-mips64-unknown-linux-gnuabi64.tar.xz": "441d5ca203afbd5bc4af16431c31d3c76e05edbe45ad37aea4d1f90ca4afecee",
- "dist/2022-08-09/rustc-beta-mips64el-unknown-linux-gnuabi64.tar.gz": "437c52c97b0a8edd1165b957b768e7c9f7c3d79023626fdcb97538899f424592",
- "dist/2022-08-09/rustc-beta-mips64el-unknown-linux-gnuabi64.tar.xz": "9812034d2fe8bb583dfe57a792639a6ac7ab912682ad7383db975efd5f0cd281",
- "dist/2022-08-09/rustc-beta-mipsel-unknown-linux-gnu.tar.gz": "5d39fe2005a50e40505b18210af700ed8fd439d018fe8723c13221c7683defad",
- "dist/2022-08-09/rustc-beta-mipsel-unknown-linux-gnu.tar.xz": "da77cb500618d57682ef4d601e1c8492d9d5412a1833757ffa95d00b0aab790f",
- "dist/2022-08-09/rustc-beta-powerpc-unknown-linux-gnu.tar.gz": "5e4559e41c12f1248cec5f4cb40c196e5d6520bffcc043a40759e7a7668ee0b7",
- "dist/2022-08-09/rustc-beta-powerpc-unknown-linux-gnu.tar.xz": "41c527f4dbf76275f26f7486b48613fba57171b82275ae29ad90bbe684497ce8",
- "dist/2022-08-09/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz": "410f2768e5b69f03ebed37923e0c0bb54d4b297bcbef98d82529216a1dfa4c6f",
- "dist/2022-08-09/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz": "7f0d0ff4a797120c1a31d33cecd4abd4bd69ec6ff97351291267423f27f7f3f1",
- "dist/2022-08-09/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz": "2fa446a7f79d254350b7e9828a36fd415f6931494bdec8bf8f26e0c13636849f",
- "dist/2022-08-09/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz": "8572718b7ca5aef0de8227550816dbbd7b41fcc2ab6b0624a50096fa387503ce",
- "dist/2022-08-09/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz": "7d82792f2526c6e6522c5255dd350e09f838551f743f760634b875eb22b48b0e",
- "dist/2022-08-09/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz": "99f4ff1b50ce3b644b12bc0da84910f376c9187adede0222dc8a1e242e347263",
- "dist/2022-08-09/rustc-beta-s390x-unknown-linux-gnu.tar.gz": "81f32e172bc635028fe048d2194f1d49c4f31b4daadf8690931cd3a15a9e06d1",
- "dist/2022-08-09/rustc-beta-s390x-unknown-linux-gnu.tar.xz": "c50258bda8d09646bef493463d66be02d8e7155122e2b7eafe9bc5bd11cbe6b9",
- "dist/2022-08-09/rustc-beta-x86_64-apple-darwin.tar.gz": "fa2d6a74109c087f13fbbf00171252fcd9079b9978028ee6df4fb2d18c27a39e",
- "dist/2022-08-09/rustc-beta-x86_64-apple-darwin.tar.xz": "630b218549ee04a8bc5a8a29616d5d8c1d866804cedb2fc3861cc2e354382c5e",
- "dist/2022-08-09/rustc-beta-x86_64-pc-windows-gnu.tar.gz": "633b22f9c9a93f6cbed7a53fb466ea0bf5895f6fa9c3a22a5883951cedbd606b",
- "dist/2022-08-09/rustc-beta-x86_64-pc-windows-gnu.tar.xz": "d109d67480ab01ab1e176b5c904df5053fbb54baaf422d4f5019a28dd122e149",
- "dist/2022-08-09/rustc-beta-x86_64-pc-windows-msvc.tar.gz": "627635dcd8515ed029515e1621b8411e1a3a6ede4058d2cf581c8d669b96dd5f",
- "dist/2022-08-09/rustc-beta-x86_64-pc-windows-msvc.tar.xz": "6f98febabe7229c566c86728dcf773849c97f29bc0f43ea4f5ffb4e81f163b18",
- "dist/2022-08-09/rustc-beta-x86_64-unknown-freebsd.tar.gz": "a4b4dcd9dd1128df37d761efaa669ebb35841af0847b5bb5bcf38295ca47db6d",
- "dist/2022-08-09/rustc-beta-x86_64-unknown-freebsd.tar.xz": "cd28418166eaa754102b469414d0fcfe6785096855e50a2b895c5903e528d8c2",
- "dist/2022-08-09/rustc-beta-x86_64-unknown-illumos.tar.gz": "168cf42f579132b44ac35a214328f518ab201d6fb9d88d645899fdd88e1d5e34",
- "dist/2022-08-09/rustc-beta-x86_64-unknown-illumos.tar.xz": "7792763eda89f90a878b09dd99ea9af94d6db0c36bb9570e9b1a05d8dc2f1e47",
- "dist/2022-08-09/rustc-beta-x86_64-unknown-linux-gnu.tar.gz": "ec79355625df63a29487253925f5213442f8606f2fd6fca7a94f8db57220f802",
- "dist/2022-08-09/rustc-beta-x86_64-unknown-linux-gnu.tar.xz": "b7df0b00cfc3bee9a1a906441726ae009aa7dfbfe22e16bc9127ec4df11cd1a7",
- "dist/2022-08-09/rustc-beta-x86_64-unknown-linux-musl.tar.gz": "372a7c389366e543f3477f2854271c7a74042abf51c37f2d5ce1450f4cd3bfd3",
- "dist/2022-08-09/rustc-beta-x86_64-unknown-linux-musl.tar.xz": "11243da1c7642ee7ece1b43de1e59b4c45fa8ecf0c9d632d6f1db4772e4479a6",
- "dist/2022-08-09/rustc-beta-x86_64-unknown-netbsd.tar.gz": "808a451394f4a023ef28420d7dc29e2de92618e5a563055798ec2307e0ca062f",
- "dist/2022-08-09/rustc-beta-x86_64-unknown-netbsd.tar.xz": "55b447374eb361b2ea1de87080bac3a0cc4fbd233e916e53e5424d6b9ed0dc48",
- "dist/2022-08-09/rustfmt-nightly-aarch64-apple-darwin.tar.gz": "bbbfd82986d5fafa76f06b87b08cfda800dbc87a1adaed3587f570336a648c3f",
- "dist/2022-08-09/rustfmt-nightly-aarch64-apple-darwin.tar.xz": "69a99ec973a3603c3d84361d407fbe9c79af0a284a48f5578fb6f46affe751b1",
- "dist/2022-08-09/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz": "4a886c744cd6b5f4fe0ec2a3b87730e9a4f7a66c9d4fb0c6411be32014e9e5f0",
- "dist/2022-08-09/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz": "095ff2687c8dd48decf47c2b65ce84579bc753de0732877cb092b4ccb9df4f92",
- "dist/2022-08-09/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz": "3533a40f72ee318f04448f005e0a811add2e97285e158f63758f76b4424ebe46",
- "dist/2022-08-09/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz": "85a4f34d889bf52b65b93066bd1bd6a91092ed49cf6443f476bd60fb36b1cb6d",
- "dist/2022-08-09/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz": "11c4bb5a071d555cda8b685e340727eb588f7a830cd4fc22937e44e68b3d7ee4",
- "dist/2022-08-09/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz": "b7c9628299f155530d5a2831a4ffe8d398d2cd222952815006eb19d6ff92a846",
- "dist/2022-08-09/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz": "04ce1aec5cd9c7ba644954d8ec6ba4c851de1c964b64b0d63965cd2043e7d590",
- "dist/2022-08-09/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz": "08635b48b6a7585dbeb7e53f5aac172404e8dfbdf8c165e281064737f073a013",
- "dist/2022-08-09/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz": "852ea71bee89cbf9c9fd1d9abb02a88695732d51b106d3de78c36f371663341e",
- "dist/2022-08-09/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz": "499ded5cd2689626186c3a51af671991ec67ea0eb16bfa30097a764c6718405a",
- "dist/2022-08-09/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz": "eff28bbdd1d3dc44fb4504b24856dc8c550f1311ce56a1614acf2b7409a1c5e2",
- "dist/2022-08-09/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz": "b621a87a5257c280b0610fa90abd288264ee01238669d61030ae057e87eb8e47",
- "dist/2022-08-09/rustfmt-nightly-i686-pc-windows-gnu.tar.gz": "9307f1feb9c06f57930cd0009aa7b740f1c9e1515212bd7e2ae81149ea0f571d",
- "dist/2022-08-09/rustfmt-nightly-i686-pc-windows-gnu.tar.xz": "fea653c9e14d305ed217f9c984742c066ea7f7d2fb6d316a13586afc820fca8a",
- "dist/2022-08-09/rustfmt-nightly-i686-pc-windows-msvc.tar.gz": "8e600a6ad04dff36fe9b1846592faa693b1a036f58d136a522d6ca86f9d76be1",
- "dist/2022-08-09/rustfmt-nightly-i686-pc-windows-msvc.tar.xz": "52b45791540bc754a82a7349ca4c5ce194afd28e6c857b61f0ff7e87f10f1253",
- "dist/2022-08-09/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz": "964847cfa824c66db554ec20a7e623f4ea50e4cef2439b6813592bcdc31a94ce",
- "dist/2022-08-09/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz": "71b89b63be5a44e12d0303e71e3e14b0b770aaac5bcf2879e17526b27e568c6d",
- "dist/2022-08-09/rustfmt-nightly-mips-unknown-linux-gnu.tar.gz": "aac0b10cfcb07c64de04fea33d9cf8d691f307e3aefaa9582a8ba3e1fd54aa35",
- "dist/2022-08-09/rustfmt-nightly-mips-unknown-linux-gnu.tar.xz": "e3688bc8ef82fff7685cc95fc1ec708a5d76d288cccbd42c37e5c83184f6a52e",
- "dist/2022-08-09/rustfmt-nightly-mips64-unknown-linux-gnuabi64.tar.gz": "aa89a2dbfe0c6020139d8be52be126a01af9691eaf73fa94c94f9ae75d09d619",
- "dist/2022-08-09/rustfmt-nightly-mips64-unknown-linux-gnuabi64.tar.xz": "1e7e5f11f829b06f4d8ae5b6d38f13e055ff1fda5b1481e33e40e2a336c7852a",
- "dist/2022-08-09/rustfmt-nightly-mips64el-unknown-linux-gnuabi64.tar.gz": "e43b8009179139c1bd15f3e947a8bc7357e57833959f9fd18fde1d75a742bede",
- "dist/2022-08-09/rustfmt-nightly-mips64el-unknown-linux-gnuabi64.tar.xz": "5bf84aa3ad0badbf0c60d31f686498801d9a23dc0324062128cd7690f9c69234",
- "dist/2022-08-09/rustfmt-nightly-mipsel-unknown-linux-gnu.tar.gz": "b3816072c441ee9e2b8da5cc6a8ed022795b7176d4ac604d97b0d213f89522ac",
- "dist/2022-08-09/rustfmt-nightly-mipsel-unknown-linux-gnu.tar.xz": "d7c6b668f4a2c128ab845e010deb7c9c3e374aaf167244395cdad6c746f73420",
- "dist/2022-08-09/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz": "91c36676f8b9093976d7e30ac39f2c477f8442771a3e9c49f41610a0b8688cbe",
- "dist/2022-08-09/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz": "86bc5289beee0abf87bfc57a09d6504bfe58675871f4e7559b7f9e1b748e8dc6",
- "dist/2022-08-09/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz": "12a1716934b48c2cf716a56c66f95852a70bc74b65c2e44a542b9c2ebf1be5ec",
- "dist/2022-08-09/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz": "591ac7d4acd0036d532c91fa3f64a7789881224dd5496b1fe3564c79fd5f0254",
- "dist/2022-08-09/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz": "b558c9463e5b8380e133a7a64bc95994d8625fa0097f8b41fb39c93367704698",
- "dist/2022-08-09/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz": "d28bbc411779d19279fec251e9378b3923c4b3a8c651d375e139732dd6daaaad",
- "dist/2022-08-09/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz": "af9644ab1e369495a4e35fbd111946c679e597c8c1f8342d3a92bad0cad30ffc",
- "dist/2022-08-09/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz": "c2acdb98f2c2565cbaf57557cbd99038fb066a09b5602ff3d3bbcb58388b4501",
- "dist/2022-08-09/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz": "eddd4a32740ba700c095568bff791e959d6b8ab0a47b5e98a317ea56cc754751",
- "dist/2022-08-09/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz": "021bba582076650b577d78fda0b6d9374b17e623d2f3ddb0322f9960874ac269",
- "dist/2022-08-09/rustfmt-nightly-x86_64-apple-darwin.tar.gz": "355c877020b386f0297765f8ae1b1e3fa126c9e9b537f8923d275bd31cbdba33",
- "dist/2022-08-09/rustfmt-nightly-x86_64-apple-darwin.tar.xz": "809d3314e1b69efb5544fa6daa6719907e09edfd7e0c2cf483ad89a3d508b98d",
- "dist/2022-08-09/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz": "def326ede8106aa440b57698a027fffe024158351879d17f9bba0e58c3788662",
- "dist/2022-08-09/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz": "86684f3c6ba66216c5c9cb203eb9fce96e0239a345df912a2071bc9aa83cc9da",
- "dist/2022-08-09/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz": "2d658db4849089e86af76eb4d7986d605da644af6f6ad43429931f775b5bb094",
- "dist/2022-08-09/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz": "ec14c63680836e5303450d3bfda7b6ca5b163ec01cdaf1e3f2d7b6c584c4c6a1",
- "dist/2022-08-09/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz": "39542f4fd6261b864c59494958071dacd32703fd870286ec5fceaf3664d3e5a4",
- "dist/2022-08-09/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz": "a02061c8bcdd23a8b3f21c4f776e900536fcd44019a495fdec159fd9b4282700",
- "dist/2022-08-09/rustfmt-nightly-x86_64-unknown-illumos.tar.gz": "15fe16aca96080380c6ebf109522f60134dbaad610d3841e07ac5059ee920edc",
- "dist/2022-08-09/rustfmt-nightly-x86_64-unknown-illumos.tar.xz": "43d562bce508ccc40afdc3f1ec85b6f0e29b1bad1661a758d07e57cb97d63407",
- "dist/2022-08-09/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz": "80659ef19b274724eb3becb90ca03ea6cb6eb1ff9ff6989073df6e1e52c0d406",
- "dist/2022-08-09/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz": "392e98288ea2ef6602dff922c9bef4fcb17acdcefbb4bca050ede73aeee669e8",
- "dist/2022-08-09/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz": "1eccdcb7cdb96ffb5a5261d4190e4bd1f92e5cf61e92e556ed6baf42e9c316ab",
- "dist/2022-08-09/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz": "9526d26c9fef619cf429437d45c5a79e89a894b22f88635d7995639f1f9696b6",
- "dist/2022-08-09/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz": "486b79d19fff869275f973b274b3cddf02693ba444a79a2e100c6ba79be5d493",
- "dist/2022-08-09/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz": "1378376c8b16aa269034cd99b2efb8d8a0fe77e9526338d652b6d6e9aff2ac0c"
+ "dist/2022-08-19/cargo-beta-aarch64-apple-darwin.tar.gz": "79196736248e8e0f76a5d365a45fda39d5bdd815f0f2a8e4341acba31b183a22",
+ "dist/2022-08-19/cargo-beta-aarch64-apple-darwin.tar.xz": "2cad3b28e9ee50c01d031b9c0086f40ccc16845ff2496e91fb1adebc8560b1ec",
+ "dist/2022-08-19/cargo-beta-aarch64-pc-windows-msvc.tar.gz": "c725c27d06a4201df716f50509651cf071708fc0860e2d482a8dcc7dc69011bc",
+ "dist/2022-08-19/cargo-beta-aarch64-pc-windows-msvc.tar.xz": "f5e1c07a8043d5e94c0450e896069262f825fa534c7688465fb0e50424b052ab",
+ "dist/2022-08-19/cargo-beta-aarch64-unknown-linux-gnu.tar.gz": "4f07b76c98206ade5b18a89af56004adae0abd51be1c295f6725b6b24bd2dfa1",
+ "dist/2022-08-19/cargo-beta-aarch64-unknown-linux-gnu.tar.xz": "9ef70808f78032f2d2f60c529dd43354752df510e7eb9d7db09abc34898a0243",
+ "dist/2022-08-19/cargo-beta-aarch64-unknown-linux-musl.tar.gz": "8c3fa219aad16a2c7a2858844bc3e9b39d2382907b5a145a21d88001d082b510",
+ "dist/2022-08-19/cargo-beta-aarch64-unknown-linux-musl.tar.xz": "60f18159e805894e5504d84999a33396cb27d32d2827ee6b903ed9efab5ae3e7",
+ "dist/2022-08-19/cargo-beta-arm-unknown-linux-gnueabi.tar.gz": "66ae9ea36d30e5e027e452b5e4e76e7deaa0d5f7c601b781c02559a99e5a888b",
+ "dist/2022-08-19/cargo-beta-arm-unknown-linux-gnueabi.tar.xz": "61c00674f1a33234f0622e7f2b770d641b8202d1e79ce231ad372c0c5e8a0cb2",
+ "dist/2022-08-19/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz": "298e096659bf1a3903073f3c59bec22f763a2a8f8a9ee91f94589b8e9361bbc1",
+ "dist/2022-08-19/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz": "dc57c0aea0ae45d781d9a000a36b9df66532b5434024cc6c4d99d8007a0bef8f",
+ "dist/2022-08-19/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz": "cd21f821f8e6ff9e0b0c3d3e841192fb81a6fecb6ebffd09fa880ac07c9fe677",
+ "dist/2022-08-19/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz": "8757bc726950c5e8507ad0d9ae518504a9467e8cfabe590b458714be3b6fc1bf",
+ "dist/2022-08-19/cargo-beta-i686-pc-windows-gnu.tar.gz": "4c3c891c19ab8990a892fc98ee5e1546b5a8626ebd769b1b6c26cfcc636f12b4",
+ "dist/2022-08-19/cargo-beta-i686-pc-windows-gnu.tar.xz": "2da6fa16707edccedc3fb37782ae2cbee1330cc89e646a16fbb288c07fe2502d",
+ "dist/2022-08-19/cargo-beta-i686-pc-windows-msvc.tar.gz": "76c4f8be3abfff215a9a565e378fe8e5853b164e2c1ea5f91c4791851457840b",
+ "dist/2022-08-19/cargo-beta-i686-pc-windows-msvc.tar.xz": "8f49f0ca8542ca72bfc05f4928a97ccc9e825a3b7f00dc7fadc58ba184169d07",
+ "dist/2022-08-19/cargo-beta-i686-unknown-linux-gnu.tar.gz": "51b48101522af0cae055e01d025ca657fd7284fb4e3cf81839774fdef97d6c21",
+ "dist/2022-08-19/cargo-beta-i686-unknown-linux-gnu.tar.xz": "63e34d3e51a99eac167f7567002026f63b830defcff364b3029d8819ccd4abb5",
+ "dist/2022-08-19/cargo-beta-mips-unknown-linux-gnu.tar.gz": "2888d73ea48e9ca5ffb6f2db7d1ce665413229d38454e625cad4d4d9e2111feb",
+ "dist/2022-08-19/cargo-beta-mips-unknown-linux-gnu.tar.xz": "cc361abd32f693e2b0d44d718c9d14cf4f4e9333eb965a888013abc1abf2e81d",
+ "dist/2022-08-19/cargo-beta-mips64-unknown-linux-gnuabi64.tar.gz": "7c180166f2ab5c91ed218d2f9fa2ad552e47818a268347bf5be48cad36f14858",
+ "dist/2022-08-19/cargo-beta-mips64-unknown-linux-gnuabi64.tar.xz": "1e5895d5ccd86c5fe13362d8763e9a75ec46909e7c4840dcc84286aa7bf4b367",
+ "dist/2022-08-19/cargo-beta-mips64el-unknown-linux-gnuabi64.tar.gz": "6890940f4e02979b6bb8496faf7e71d75c317be563b35c44fdaebdfe11b62cd1",
+ "dist/2022-08-19/cargo-beta-mips64el-unknown-linux-gnuabi64.tar.xz": "37bf3bed41ed98d1517d5e925ca22ca39c6d51ab1e65b2e7fd9e8f082364d258",
+ "dist/2022-08-19/cargo-beta-mipsel-unknown-linux-gnu.tar.gz": "aaaef89dd7ef0cf07a400cecdd70851be23d066b0522d2e89f94358ea196ebeb",
+ "dist/2022-08-19/cargo-beta-mipsel-unknown-linux-gnu.tar.xz": "17c883e6c59a892a4eceee482f27eef60a020f417e06e9c1282992fc085b6106",
+ "dist/2022-08-19/cargo-beta-powerpc-unknown-linux-gnu.tar.gz": "722efeb58b8d6802ecc52bfc686b4a1a24fd7b9494e93ed7cb8e4ac974e5c161",
+ "dist/2022-08-19/cargo-beta-powerpc-unknown-linux-gnu.tar.xz": "26b3572b77ff7faaf40c06204852683408fda2aa4256167611ac1d0c177789de",
+ "dist/2022-08-19/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz": "93c504cbade48dc07b4e839d85fa6f477f28ab140c894e8f9acec6477f21e7db",
+ "dist/2022-08-19/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz": "72354c21c154868a9b1c420047cf2a9eb5aaae20e93dde41f8283ad6e441a744",
+ "dist/2022-08-19/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz": "ba4c589975b4ca7ae10c98d07c02bae50b7c5d0bcaded77f1ccfefbf555d4116",
+ "dist/2022-08-19/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz": "fe9e8e7cbb9a1b00871e11b54ae3f64bc35dc4cc80ca49e59b4d8daf5fbdeea4",
+ "dist/2022-08-19/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz": "287e7a014a6d8e650124c71c87bbd9655785564f4ee1f179a9226ce371e8d78c",
+ "dist/2022-08-19/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz": "4e35f5a198bbcc06842569e2fda1985e77794425f2e85f1948d7a7664125099b",
+ "dist/2022-08-19/cargo-beta-s390x-unknown-linux-gnu.tar.gz": "05e091a433009843f0dcc295b5b53b46ba2f55a37f7057c777a9cd92c094cba3",
+ "dist/2022-08-19/cargo-beta-s390x-unknown-linux-gnu.tar.xz": "54202c092c265bff6be7f598ce1f6e96a84a5ba4c354d9bbd4986475cf0b809d",
+ "dist/2022-08-19/cargo-beta-x86_64-apple-darwin.tar.gz": "0ae9a6dde1693d33ff774f435bba3e18b67e2d8ce757df5e26ac3b757733cb29",
+ "dist/2022-08-19/cargo-beta-x86_64-apple-darwin.tar.xz": "d67c4c748443be773016d27a74e859428b404a7fb06e651b1fdbc205ea08c03b",
+ "dist/2022-08-19/cargo-beta-x86_64-pc-windows-gnu.tar.gz": "fd617b76eb4d9d951ca9757a31fe92c8cfeb76b093e09262728c7737aa9d2970",
+ "dist/2022-08-19/cargo-beta-x86_64-pc-windows-gnu.tar.xz": "14f263241b90e91fc40a75361e4b8de23165cf8b7c973bc7016a9bf337381ec0",
+ "dist/2022-08-19/cargo-beta-x86_64-pc-windows-msvc.tar.gz": "c5440958cc976526109ef17b4883e27394d6ce087d90653956371837de5a7f8f",
+ "dist/2022-08-19/cargo-beta-x86_64-pc-windows-msvc.tar.xz": "8e3c28a0e593651b530bed0fb5216ca8f04ebc54e98b8cb83cfae3ea13c1c8a1",
+ "dist/2022-08-19/cargo-beta-x86_64-unknown-freebsd.tar.gz": "c80991b59c39129fce5f775c5f1b530592d04060bfee7f7a7a443da698efc50f",
+ "dist/2022-08-19/cargo-beta-x86_64-unknown-freebsd.tar.xz": "81fe7a26762f503c04000f954986c25be1acbaa6687389b70a48e4230901fdfe",
+ "dist/2022-08-19/cargo-beta-x86_64-unknown-illumos.tar.gz": "8b08d6c4c41c3ab05ea3310d44943a3453254c96df92b5f25cc888c4fb7d31fe",
+ "dist/2022-08-19/cargo-beta-x86_64-unknown-illumos.tar.xz": "eb4705ba79bc9b7d2a3969748d966c7faf305ab196bd002856fe5a0499c11989",
+ "dist/2022-08-19/cargo-beta-x86_64-unknown-linux-gnu.tar.gz": "b0f14a0bfa064b4511dc34a71039af3c69b006b51906f9087bc7929014b9db76",
+ "dist/2022-08-19/cargo-beta-x86_64-unknown-linux-gnu.tar.xz": "3d8cdbc3679550a374e29b4a363b30048e2d65d3d4b9c9dbaeb0a907606afee3",
+ "dist/2022-08-19/cargo-beta-x86_64-unknown-linux-musl.tar.gz": "2f564886b29fece08e8ccf4fdaf6035b5819d03fc963ce46cd6225a79e2d4da0",
+ "dist/2022-08-19/cargo-beta-x86_64-unknown-linux-musl.tar.xz": "78f7ed8eb7e8d147113472f6402688e0127a41a1a26679c19c9e558bf39e8df7",
+ "dist/2022-08-19/cargo-beta-x86_64-unknown-netbsd.tar.gz": "3edff35d00c5af406b72db5e2ee33340c05d24a4bdf4658f172b7d74994825ff",
+ "dist/2022-08-19/cargo-beta-x86_64-unknown-netbsd.tar.xz": "96b290796ea1781fb5a69838f969591b70e5e193efeab85f4e7c45dfb2c698f2",
+ "dist/2022-08-19/rust-std-beta-aarch64-apple-darwin.tar.gz": "c7b09f59bdd2cf91a9aaf771257a474655ed5905f5455f9a3b47bb832d1f124c",
+ "dist/2022-08-19/rust-std-beta-aarch64-apple-darwin.tar.xz": "b82c8c4a454f59218b22eb04a346b78215c10269c001e28395080abefaa9e6f4",
+ "dist/2022-08-19/rust-std-beta-aarch64-apple-ios-sim.tar.gz": "cbccc251b16f41383845d5d1d6196f33d2769df7455f308c096e0af46b43d14c",
+ "dist/2022-08-19/rust-std-beta-aarch64-apple-ios-sim.tar.xz": "99be53158e3ee2b467d75d26d461e7aa4330301c6dba74c048331b16e09188c5",
+ "dist/2022-08-19/rust-std-beta-aarch64-apple-ios.tar.gz": "bf94ccc4bc6a4c171cd60d7a62c34d32e09b242a7029b69feef6159cf4c22fee",
+ "dist/2022-08-19/rust-std-beta-aarch64-apple-ios.tar.xz": "6a9a910573ea80de9618c7bf46e21e93341db652e4d82bee06e73544b92805f4",
+ "dist/2022-08-19/rust-std-beta-aarch64-fuchsia.tar.gz": "27693e376c29e561844e43ef21d006942e44263076ab4144643827b671ea3339",
+ "dist/2022-08-19/rust-std-beta-aarch64-fuchsia.tar.xz": "81d00752188815a7747f89ba76610d91d276459543e6c4362f1f86ecacc28f2d",
+ "dist/2022-08-19/rust-std-beta-aarch64-linux-android.tar.gz": "6f076bd4565d9af83003e1165e24fb912eca0adaf40a40f1d87956ff8dff535a",
+ "dist/2022-08-19/rust-std-beta-aarch64-linux-android.tar.xz": "a0f528910137d9b7e0e6dd371dad181f7d48d270e8ae289d8b7996440bef101b",
+ "dist/2022-08-19/rust-std-beta-aarch64-pc-windows-msvc.tar.gz": "6ac6afb78d0c3e55cffc6cc52bba51259fe83aac35075cebdb6940ad3d147b60",
+ "dist/2022-08-19/rust-std-beta-aarch64-pc-windows-msvc.tar.xz": "f1043c1fa0d0d75f8001d854967f2ec8be6def5845662505048742e6472ebb87",
+ "dist/2022-08-19/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz": "d3390a8cb0617551cdce7bb9348fc39a304c877bf5c5ce02ec9cdf636a69571d",
+ "dist/2022-08-19/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz": "81da17399275e43ec9b15983faa9828df2a8dbc72ea15d16671d0c31d4d0795c",
+ "dist/2022-08-19/rust-std-beta-aarch64-unknown-linux-musl.tar.gz": "3deca6def0ebb3476898e1ee7368592a034867d73948e51f24ad92445c6275b6",
+ "dist/2022-08-19/rust-std-beta-aarch64-unknown-linux-musl.tar.xz": "23bc4165841199f110293a627735624a2a37bc6d92019e9cb1533e006933f9d5",
+ "dist/2022-08-19/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz": "3360ea38f8d8bc84f2419b4ca90bfca7a9709a6fbec2ba209fe335090a534c0e",
+ "dist/2022-08-19/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz": "ce7643548eb6f797b7823795e27b748aaa09cd02baffaeee44bdc3c00ed796e3",
+ "dist/2022-08-19/rust-std-beta-aarch64-unknown-none.tar.gz": "5f6cd59b283ac76709e7c76c89f352e92b6818b1606b585332f36ba3c4b6a87b",
+ "dist/2022-08-19/rust-std-beta-aarch64-unknown-none.tar.xz": "60d00cacbd24576b0b247f2b12d6a63e9451912d874385b50165a312da080762",
+ "dist/2022-08-19/rust-std-beta-arm-linux-androideabi.tar.gz": "15494aade74e5e29bb0d881d243da37d3b929bd98baab7c6b0345e0a6a0ed803",
+ "dist/2022-08-19/rust-std-beta-arm-linux-androideabi.tar.xz": "d62cdfc5d0a41749ea90d6cabaa77f4f45630061a04dd3be9611cd5f0ea868d5",
+ "dist/2022-08-19/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz": "40e532cd37262e7280a7c0bdd54808815adba533a911bd46a1d9b76d0446b958",
+ "dist/2022-08-19/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz": "bb08d0ca767f257fb13f6a946e7c725dc877055f94d324128eab521d43cc552b",
+ "dist/2022-08-19/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz": "2f22a08cee234574c48e5022e0c5b1641c2ad01fad69537b03eb9f247e7877dd",
+ "dist/2022-08-19/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz": "8cb10237d87a4fcf2100b5aec0623ab04c2f1d976a0f30a1a89357cb41ce6d4e",
+ "dist/2022-08-19/rust-std-beta-arm-unknown-linux-musleabi.tar.gz": "811c691d678f08627758bba341e3ac06a2d465da78d819c49de494e0602073e6",
+ "dist/2022-08-19/rust-std-beta-arm-unknown-linux-musleabi.tar.xz": "50063b02901983e3ce37e98e57e664521cb5c99ab9a1015bb949a53dfda9b49c",
+ "dist/2022-08-19/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz": "2ca7a740767463e432b22b846f81ab9983751279b65b4488d07202b77e378afc",
+ "dist/2022-08-19/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz": "8b56365bd261fb6a4c2984b51dce14d8e2c996667ba4af866153042855043916",
+ "dist/2022-08-19/rust-std-beta-armebv7r-none-eabi.tar.gz": "83ece7e0db0e1ddd44b104d52def00ce06fea37dd8c90b1c0a840d0561a76d54",
+ "dist/2022-08-19/rust-std-beta-armebv7r-none-eabi.tar.xz": "e3a343fa5f35372ace7f23e6775c8c01b9803fca9e1b686d3db695c2f754db5b",
+ "dist/2022-08-19/rust-std-beta-armebv7r-none-eabihf.tar.gz": "874dec1039af060488f34be26ba54615a12ee4cb64f3a871932936e4e736fd76",
+ "dist/2022-08-19/rust-std-beta-armebv7r-none-eabihf.tar.xz": "cdc6b9a375a40847ee4938f3609f8c49f3368394b46fa9dccf1b5b16468852f7",
+ "dist/2022-08-19/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz": "28e72dd4c24716224b663d341d6f784f2b0deebbf52721709fb396240e327854",
+ "dist/2022-08-19/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz": "5c8cf5b47b567b8bc41c3ce831f34d076396179d050bf982e1949bef74a2b2fb",
+ "dist/2022-08-19/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz": "dcfd76d646b17521f5a7daf94a0617a731d914612368ff112345a03a2a5b5939",
+ "dist/2022-08-19/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz": "7d43dfef571dc4c2e883bdf92e05dd3465b9233c30f34bf282b57826339e9e96",
+ "dist/2022-08-19/rust-std-beta-armv7-linux-androideabi.tar.gz": "4bb1f0110629b7af1fbe09a6eb8387de9aa800a95053ef7177bba30a2d1b430c",
+ "dist/2022-08-19/rust-std-beta-armv7-linux-androideabi.tar.xz": "55f97c5a16e8d65dcfa13adf989039ffcafc5a4a09aec92083d00d7e2786def9",
+ "dist/2022-08-19/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz": "e7fb4da36908d6cc0c13a93ec3f4b01ea1b2c515213005479da45712614c0a69",
+ "dist/2022-08-19/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz": "c727568116de82658335c3be93d51a7e2850cc2824b9818a166594649dd5b3ce",
+ "dist/2022-08-19/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz": "1a186dab35dba7fcf4982e717a1ac6d1d846b87e50e6815ac0f9b2c40e44f4d2",
+ "dist/2022-08-19/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz": "7f5d3c66c57ba75d3c91328f3ceccddeeebdc08f015e762e26af770a968ab14d",
+ "dist/2022-08-19/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz": "c0108882d6f80cb82a9fa016adfdca2478b4966c72c6d6d240c94c56fa668166",
+ "dist/2022-08-19/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz": "4f30e1114c7c4ca187f9a2e4b2ffdd4fa66cd2346534c80ff5a7278a846f2c8c",
+ "dist/2022-08-19/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz": "a69c55803776f8ccfc1685181209463ebbbd82252ba2cd47387d6ce8f8f09afb",
+ "dist/2022-08-19/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz": "c12d6e9f9cec1aee0dcad3ac95a33dea0522a0e00c88e1d1e5ea4a0d98119128",
+ "dist/2022-08-19/rust-std-beta-armv7a-none-eabi.tar.gz": "91cb7d15d30bea14c3f7b9ad19ccc3cf58b69abf8ff7ec2ebaf1ed60cab4940d",
+ "dist/2022-08-19/rust-std-beta-armv7a-none-eabi.tar.xz": "887aad7c712055aca460a840bb1f5b69caf4fe11a458032df73038af94285abe",
+ "dist/2022-08-19/rust-std-beta-armv7r-none-eabi.tar.gz": "54a4f38e30039211e0199bc25733572d24dd5ad9eb6fec524146c1dc08b745d4",
+ "dist/2022-08-19/rust-std-beta-armv7r-none-eabi.tar.xz": "0b6b366e0380df3513f36eac4341d5562520eff0fc995280179c6424fcc4f5db",
+ "dist/2022-08-19/rust-std-beta-armv7r-none-eabihf.tar.gz": "76f33e20c3800f2a7cc53db4552a44b5269aa8678c7f3de970071ed0f3c66dfe",
+ "dist/2022-08-19/rust-std-beta-armv7r-none-eabihf.tar.xz": "e71d8e8a2144cd4ca220cb25218aaeca7d0ba776ef10d2adebd8745bc1fdf988",
+ "dist/2022-08-19/rust-std-beta-asmjs-unknown-emscripten.tar.gz": "50ba543bcfe3d1ca5d5606e7ea656968f70930ce074fb0110e65658e9cab9810",
+ "dist/2022-08-19/rust-std-beta-asmjs-unknown-emscripten.tar.xz": "b2498657da8c27464213afbbad1ff95db637236a9e0f88d2c9bf0c6940a0c583",
+ "dist/2022-08-19/rust-std-beta-i586-pc-windows-msvc.tar.gz": "8275136f11ddad506a9569d3e6d1ad4b48688a1e7e498e22566ff3453675ff4b",
+ "dist/2022-08-19/rust-std-beta-i586-pc-windows-msvc.tar.xz": "8c5fae3775152a3dd3d1bc47e1ab1861efa60b51f8087fb2a63da0eb45185a79",
+ "dist/2022-08-19/rust-std-beta-i586-unknown-linux-gnu.tar.gz": "628a29ee30489dc9bb6877a2aaccb4f5de05a5a58ef154be875643ce393776d4",
+ "dist/2022-08-19/rust-std-beta-i586-unknown-linux-gnu.tar.xz": "ff4f7ade08cb2337d20fbaa8a6e74f662b805cb2bf142a584e10b4aae66139a9",
+ "dist/2022-08-19/rust-std-beta-i586-unknown-linux-musl.tar.gz": "3f7859c372943096d3f6a258561584cee28adb9cfeff5b98036dd7cab74dab7d",
+ "dist/2022-08-19/rust-std-beta-i586-unknown-linux-musl.tar.xz": "40323fdb61ae453d42525ea13c41b5dedd322d859f9993315a798a87634ee386",
+ "dist/2022-08-19/rust-std-beta-i686-linux-android.tar.gz": "b103802c800524bc1dc6f3d996ad47d2c03fe03b236588f8a9de3fc763ba2eff",
+ "dist/2022-08-19/rust-std-beta-i686-linux-android.tar.xz": "ecb631362408d708e63093a0bec0b735b1cdc6bb7c0509ab5852e572f796812e",
+ "dist/2022-08-19/rust-std-beta-i686-pc-windows-gnu.tar.gz": "3b7af769cb25b75fc5a657e4f3dd489e6745df2d383c5e78cc033fb46345d72e",
+ "dist/2022-08-19/rust-std-beta-i686-pc-windows-gnu.tar.xz": "f1a65b9607cd153e421f27891b5ebcdc1e4a1571613ecead7402d8794eff3330",
+ "dist/2022-08-19/rust-std-beta-i686-pc-windows-msvc.tar.gz": "e5214f670f981c7b95e690c8487323dc2a564d34f5ad4f589d2862ee11225b83",
+ "dist/2022-08-19/rust-std-beta-i686-pc-windows-msvc.tar.xz": "efe17356f42fa47949151ebfdb75ee10d14cc5a279eb4361c79f36c5c322d9cb",
+ "dist/2022-08-19/rust-std-beta-i686-unknown-freebsd.tar.gz": "35a3dcfd6df77ac71cd01bec57814d9153d1358409cab11e206b98e86e14c701",
+ "dist/2022-08-19/rust-std-beta-i686-unknown-freebsd.tar.xz": "4dbeea93b2aa61076de0c84c2d5833474eec7adb66a96e90372b074e6c7790c7",
+ "dist/2022-08-19/rust-std-beta-i686-unknown-linux-gnu.tar.gz": "e07c47100a8719e54176bb2465ae62d1156a4264695739039b901c6ad16e5bd9",
+ "dist/2022-08-19/rust-std-beta-i686-unknown-linux-gnu.tar.xz": "2dab5f2fbc8c5900b0b4f35ffde00d0e6777c823b67a4632fddd19a3c3b5e20f",
+ "dist/2022-08-19/rust-std-beta-i686-unknown-linux-musl.tar.gz": "8b8131f8644c4cbf13003266dcfd5ff82f7b72aa8fdd14a27ff8281821ea6a5f",
+ "dist/2022-08-19/rust-std-beta-i686-unknown-linux-musl.tar.xz": "b47f1cd70533ab2a59a8ee8c218e7d94406a552df5d23061f9cea714301d973b",
+ "dist/2022-08-19/rust-std-beta-mips-unknown-linux-gnu.tar.gz": "415d22301e698a355fbc1a32181b02a72b7ff36fe1779460d6f380fde1608500",
+ "dist/2022-08-19/rust-std-beta-mips-unknown-linux-gnu.tar.xz": "2f3524f5908f0570b843304dc11eb8d1f7285eab7cda18581043321f65e2a1dd",
+ "dist/2022-08-19/rust-std-beta-mips-unknown-linux-musl.tar.gz": "6f25a53edc32d423752557cb65daf64f9e3a622ba5a6fdf3f07f95024b2348b6",
+ "dist/2022-08-19/rust-std-beta-mips-unknown-linux-musl.tar.xz": "a6223971aa5679e3db6df2afbdd31db05b58563347d01ae8682f37236ef2c402",
+ "dist/2022-08-19/rust-std-beta-mips64-unknown-linux-gnuabi64.tar.gz": "756c7cd706d57fb766c0e255c9a10146f4d8c2bef0118196706f268a5b1ac65e",
+ "dist/2022-08-19/rust-std-beta-mips64-unknown-linux-gnuabi64.tar.xz": "b54706a7faa146e4144e7c66d0f19289bebb155bc0adfaba3c4356866514e830",
+ "dist/2022-08-19/rust-std-beta-mips64-unknown-linux-muslabi64.tar.gz": "23fc1e3e7fd1b0f1ad210273090494246e45cf43d4c30076afd28925575a9447",
+ "dist/2022-08-19/rust-std-beta-mips64-unknown-linux-muslabi64.tar.xz": "ada4af591dbac60a137d00c4030e240eca53874604b2a306db1fe8af10bf0332",
+ "dist/2022-08-19/rust-std-beta-mips64el-unknown-linux-gnuabi64.tar.gz": "b9180c5bf936713c89f6f0f5f6a637810f72194a7742fdcbbf8fe6e4a6bfd358",
+ "dist/2022-08-19/rust-std-beta-mips64el-unknown-linux-gnuabi64.tar.xz": "1c2940139107d3aa8f6616e90f68fa42e62fd965f9e22cc264655ad5935971b6",
+ "dist/2022-08-19/rust-std-beta-mips64el-unknown-linux-muslabi64.tar.gz": "3dee776bf2aa9bdaaa38235d88a1ca98f0418adc10ceb3d0e3d8999339925bb2",
+ "dist/2022-08-19/rust-std-beta-mips64el-unknown-linux-muslabi64.tar.xz": "85f16418e4edc3b2d087104c51e4de61d7d9cf5631ee48065eaef9d796f662f7",
+ "dist/2022-08-19/rust-std-beta-mipsel-unknown-linux-gnu.tar.gz": "ba374a6acaa1eee5beb9c234d2aa6e46b8b1fc7be65e0ffe8d55d9ff85cc4ff1",
+ "dist/2022-08-19/rust-std-beta-mipsel-unknown-linux-gnu.tar.xz": "70621189ef43a8034d9f96765e35c43b17b16e077f143d3994e5e6cfc089e14e",
+ "dist/2022-08-19/rust-std-beta-mipsel-unknown-linux-musl.tar.gz": "143ab6fc7b3e57d551ada1d8335fdd77c4577d303d37663c4e7ce4b24557a752",
+ "dist/2022-08-19/rust-std-beta-mipsel-unknown-linux-musl.tar.xz": "a81db30558c46f43ef4c8cbf3480407af54bb6ad1813f0e912bab6ab0baef8b6",
+ "dist/2022-08-19/rust-std-beta-nvptx64-nvidia-cuda.tar.gz": "7def94fc92ad2ac6921d90efc821f37aa69bf59b6b6c8cddf958a7bc6e90ab51",
+ "dist/2022-08-19/rust-std-beta-nvptx64-nvidia-cuda.tar.xz": "c39b30a1dbb8c6a828b860f4b0685a3c0498a65dfde880df21e505c7b4ab545d",
+ "dist/2022-08-19/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz": "184441ee82c9578b62f905e0e1fae2e27d63cc0729ddc4a58797905341a18c30",
+ "dist/2022-08-19/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz": "b7677f219cb8158ce4a9dc64dafffb63f5bda0752d14390ba52c609bac629fd7",
+ "dist/2022-08-19/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz": "e295a74c453bf003d02d0aab07aa4b8c609878975cea9795ef467150b01f3b62",
+ "dist/2022-08-19/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz": "5c45723e1047a502718365252f66aa376e1ba7702fbf1453dee3a288367b3208",
+ "dist/2022-08-19/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz": "c348be9b8846b158b21cd73542dcfb891401421ad5fd4e0a8eacdb49e56cdb4c",
+ "dist/2022-08-19/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz": "f6036cb2c1fdb232016eb99f908a198c06bae44ac4d6d7536d3ebf42ba0b6db3",
+ "dist/2022-08-19/rust-std-beta-riscv32i-unknown-none-elf.tar.gz": "766ebdbc735cca0906a23683c5da5ada8a6e455dc12e49c7b34f71e66922f60a",
+ "dist/2022-08-19/rust-std-beta-riscv32i-unknown-none-elf.tar.xz": "83dc6727a20ac37273ad8b3ce043f005d5f2be7dab1d5d0c9658fe5897ee9ce2",
+ "dist/2022-08-19/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz": "5f059405fde4d25ff250c7219a5b7d8c009a69672f4255867d23ebd50539515e",
+ "dist/2022-08-19/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz": "2aad029aa8eaed74e95ac82d471bc750955323b2ef12066b5257e9779cdb9f79",
+ "dist/2022-08-19/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz": "734e92db50a3793eefba0378dfe2daceb37b6c06f5a6ed1692b83553406060ef",
+ "dist/2022-08-19/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz": "f2278a4aa83e2cba6ccf32744c3ae8b55fd44f77bdc31f08bff36105b945d2b8",
+ "dist/2022-08-19/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz": "4ce4d1659cd7ae70705c8f4808226814df18bfdedc079c44484fe1ce0dc06f00",
+ "dist/2022-08-19/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz": "77e5962551433593e6ee3a2b1ab6766706acdb9c364f5adc8c5b99e84338d3a0",
+ "dist/2022-08-19/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz": "6a85f89c5211fc18f86cdea8874ce4ee4d5cf15299b3884dca207003715bb54d",
+ "dist/2022-08-19/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz": "cfe8149d98fa1e6d1b406a5f4133ff6bcc9fe3cc5cdb006eebd354537efa55db",
+ "dist/2022-08-19/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz": "45c5918df046c2ac5fdce3025eba539eac759d09fd1a134d51272647b829b3ba",
+ "dist/2022-08-19/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz": "42d01adaa0a222270a303c10fe5b44e61817f317494595f09ab3101ab859e764",
+ "dist/2022-08-19/rust-std-beta-s390x-unknown-linux-gnu.tar.gz": "09ca832cd73a5f995a76ce9e6c330095417d8802d2b3afdcf3c42f646dc9346b",
+ "dist/2022-08-19/rust-std-beta-s390x-unknown-linux-gnu.tar.xz": "293fdda30521323f1cafe3cc0db92d80bca7fb25ec73a2b2e0c2cdcb48cc9564",
+ "dist/2022-08-19/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz": "f74c4dd7526251aeab43172f5bf043bdd70b8819c899866086a98c45f7895583",
+ "dist/2022-08-19/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz": "0de08e4670a7b5c3a4efd4b00ab6da6d2b3ceb37664d3adcc1050274b66f6a00",
+ "dist/2022-08-19/rust-std-beta-sparcv9-sun-solaris.tar.gz": "4b88e96c15cbcdc66f200fd216715c73ad26e0cdd5920105a15ba44cc02d9a50",
+ "dist/2022-08-19/rust-std-beta-sparcv9-sun-solaris.tar.xz": "819275f203ecff1bb37628dc0589b28a0667085f3e5115c3fcd26c4536b235aa",
+ "dist/2022-08-19/rust-std-beta-thumbv6m-none-eabi.tar.gz": "579d425d60bcb229cb61291caa8fcc99ea8a759b66c61b4f1c796d207ec08274",
+ "dist/2022-08-19/rust-std-beta-thumbv6m-none-eabi.tar.xz": "d2ebf00fda10f4f2e8476b5bcfce2be358f046f60a413b6e71aef4e76c6b55da",
+ "dist/2022-08-19/rust-std-beta-thumbv7em-none-eabi.tar.gz": "c898ca80eca184ed57125d8b463ca7bc60293bdedb6a3980a2c8da0d222f1869",
+ "dist/2022-08-19/rust-std-beta-thumbv7em-none-eabi.tar.xz": "01a73cb52d6fadb848eb672c567eedac339a6ebd432756c2a9e42c7273c4e9f4",
+ "dist/2022-08-19/rust-std-beta-thumbv7em-none-eabihf.tar.gz": "cdb64f9439964310c7a35edcb36596fb68b5925b7bd2c89a048dbbd5cbbdd434",
+ "dist/2022-08-19/rust-std-beta-thumbv7em-none-eabihf.tar.xz": "3f90031df2e3de1d423c327764cf7c83ff9b422414bceaeb8f4ca7786a17b6cd",
+ "dist/2022-08-19/rust-std-beta-thumbv7m-none-eabi.tar.gz": "626d6e503c6f72766d211faebb88cb8d8bba95775e8536eb181bd76e9a26434a",
+ "dist/2022-08-19/rust-std-beta-thumbv7m-none-eabi.tar.xz": "5bcd4b052b2699798c14bc2b50b40c9e538c24ed00bc7fc1f9c812816f47a965",
+ "dist/2022-08-19/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz": "90196536e30d2ae5e59ab0bceac283a1934170484c98c4672a86f8a3198307e5",
+ "dist/2022-08-19/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz": "8dbf82b8f8c9f5b054c5281bcb5429503d7e7f916aec7fbdd4e1422db6ccbee1",
+ "dist/2022-08-19/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz": "45010646984aa7d942288d74e60052a4da6a57312e56ec812ba34ee66ee32709",
+ "dist/2022-08-19/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz": "c2ced97a849a6600163ef798adcb8aa9f9b0800bc8554c02b635ce0c26b9dc16",
+ "dist/2022-08-19/rust-std-beta-thumbv8m.base-none-eabi.tar.gz": "3d4f2fc427971a1bfb7de12c876a03e553e8741887568fa8895996da84d96fd3",
+ "dist/2022-08-19/rust-std-beta-thumbv8m.base-none-eabi.tar.xz": "2f81f4c2168dae4a76f2705b88eead61dcdf805d6f3796c9f8fb4a0f8ace21c3",
+ "dist/2022-08-19/rust-std-beta-thumbv8m.main-none-eabi.tar.gz": "f045bb0963aeb3e4d4b08da616c4ef298f1916d7491ee624f334c21427e26e9e",
+ "dist/2022-08-19/rust-std-beta-thumbv8m.main-none-eabi.tar.xz": "b963e0af4532cba9611c9cfa62d816c45b9cac75e3d390772dcee1bbc0111408",
+ "dist/2022-08-19/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz": "133a35f0ff672356dab8413aa7958fe70e1987eb95c7adfd2362e1f77fa1a17f",
+ "dist/2022-08-19/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz": "70ef699fe52f695bedce35910eb85527d492c8f663ce926e111522d3c70c62d2",
+ "dist/2022-08-19/rust-std-beta-wasm32-unknown-emscripten.tar.gz": "386900d6fe52f807c20aa607cc76864406e096bc6282185bce4e1fe6c2e95350",
+ "dist/2022-08-19/rust-std-beta-wasm32-unknown-emscripten.tar.xz": "7c40bb5d55bd01c865de037695ed088671318b2a1a5d7c1aeeb9fccdfce666bd",
+ "dist/2022-08-19/rust-std-beta-wasm32-unknown-unknown.tar.gz": "41a606a2e9148e288baa8548312c993bd82f8b288d558472ba6cc84938a2f61a",
+ "dist/2022-08-19/rust-std-beta-wasm32-unknown-unknown.tar.xz": "bceb44ce91d28547ac123d17f10788268c27c9af8c8da463260dd7c574a30de7",
+ "dist/2022-08-19/rust-std-beta-wasm32-wasi.tar.gz": "1bd60b74b2e1555c669e273687b84db1da3818ef6c9b0dbe8168852c3e8810db",
+ "dist/2022-08-19/rust-std-beta-wasm32-wasi.tar.xz": "d98e83775d0340d27f2deb0cb17a05250bca53f00c2c1039ae34f412dc1249c7",
+ "dist/2022-08-19/rust-std-beta-x86_64-apple-darwin.tar.gz": "476760a9a396adbcf5312afc135f2eeb97c04da8654188726a63734ad8f88725",
+ "dist/2022-08-19/rust-std-beta-x86_64-apple-darwin.tar.xz": "caccc07d4fff2d394f5aefbea589ade137a3154eba0a2562f7a1a036928fc8c4",
+ "dist/2022-08-19/rust-std-beta-x86_64-apple-ios.tar.gz": "3fb5384aa267a2f4495483cf2857f7b33f7d1df0409539bc0735f78f5e8fa1a4",
+ "dist/2022-08-19/rust-std-beta-x86_64-apple-ios.tar.xz": "e2383658b0dfc4bbdcea69eb42a9c75686d578c0f15adce039f46aa0f25297f4",
+ "dist/2022-08-19/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz": "6cc9433de637d2156ede6bd9620ab7a6895894d8ec848563620df131e30e79c5",
+ "dist/2022-08-19/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz": "408d1d1ceccc78df145f804bdd848d4b595274ff2e5a32c76e979134aadba2f8",
+ "dist/2022-08-19/rust-std-beta-x86_64-fuchsia.tar.gz": "6ee5d79ba87acfa1f2093c1e9cfa26eb2851db48bec86d4f25654c40f9980900",
+ "dist/2022-08-19/rust-std-beta-x86_64-fuchsia.tar.xz": "eb3e2e365edc084594666862345d903165630034e4c6fe97e206be68920d1657",
+ "dist/2022-08-19/rust-std-beta-x86_64-linux-android.tar.gz": "1713831fd1675c317d7d6059a27181591b51d094b6c39978e1d52cc92fff4ba9",
+ "dist/2022-08-19/rust-std-beta-x86_64-linux-android.tar.xz": "864c4f5edf3602ae629fe77f6d08d8ed49d354ce88bc5ea034858b77aebb2c1d",
+ "dist/2022-08-19/rust-std-beta-x86_64-pc-solaris.tar.gz": "683bc54f134507fef80665397c3e2aff1b854d08cc8130b8cef2aa6e5b573561",
+ "dist/2022-08-19/rust-std-beta-x86_64-pc-solaris.tar.xz": "86f037d022446e277dc92c007ed531da9e0ec5013b7c757f0810f1abaa7376be",
+ "dist/2022-08-19/rust-std-beta-x86_64-pc-windows-gnu.tar.gz": "312db623e7d4711eb965d4826c19986019194fdd8528086aed6a08d3f6457b5c",
+ "dist/2022-08-19/rust-std-beta-x86_64-pc-windows-gnu.tar.xz": "1581eaf570f86f49189b4f8f51cf84e535a2bc3cb41fbd8043719d0dcf5185eb",
+ "dist/2022-08-19/rust-std-beta-x86_64-pc-windows-msvc.tar.gz": "0cd8dc6a60623bc64410cac94e89abbddcc7b3cba1b89f8fb2fa56ccea4493c1",
+ "dist/2022-08-19/rust-std-beta-x86_64-pc-windows-msvc.tar.xz": "1831ca7a6ed59064a201cae8f2fe919e74c05e825ff67c671d57393506e95edc",
+ "dist/2022-08-19/rust-std-beta-x86_64-sun-solaris.tar.gz": "9845a9ac85f716ab46d186696fc44993e701de7d89c2c5aad188d7b0fe1761a1",
+ "dist/2022-08-19/rust-std-beta-x86_64-sun-solaris.tar.xz": "16fa497ccd7fd94a6f7ab03ba975d00c63b60f1786fe2da1a7eedca3da62642c",
+ "dist/2022-08-19/rust-std-beta-x86_64-unknown-freebsd.tar.gz": "3c3e24897193e3feb947a387d09059d2ee495b642dd5a160cfd1ae61eb1ae155",
+ "dist/2022-08-19/rust-std-beta-x86_64-unknown-freebsd.tar.xz": "2ffd051693a8e831ede8e93285da11b0fef127473d95fcdcc40b4e6ef811ae5b",
+ "dist/2022-08-19/rust-std-beta-x86_64-unknown-illumos.tar.gz": "4d32d311dc0c3350c329d2aff147529db13e537f8405ae0bf317b8a85e9242a4",
+ "dist/2022-08-19/rust-std-beta-x86_64-unknown-illumos.tar.xz": "fb9d10ef315f2555a0374903d66695078d5f483167c4df2c680d48af4312d109",
+ "dist/2022-08-19/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz": "67fa67de6bb7d34ca870a52346dc410084702705b1f9f9adf92d9b71afe11d1b",
+ "dist/2022-08-19/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz": "b8df9d518bf30a87633745430f41172d10fd833ee94d6331a73de3649866b62d",
+ "dist/2022-08-19/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz": "42f03a9aeff106f40f3d2ada7a02b1e91135adfc98be1acda0f9ebd63b388097",
+ "dist/2022-08-19/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz": "da4f0915a71736b3d3c2479440f8a9728250eb50234b39aedbc17d9d1b390651",
+ "dist/2022-08-19/rust-std-beta-x86_64-unknown-linux-musl.tar.gz": "06d00f30c2332d68a924b50e42e813f2f8479d568b74c5edf51b7fbdf57b66d5",
+ "dist/2022-08-19/rust-std-beta-x86_64-unknown-linux-musl.tar.xz": "1e3b5a879ddd2e4fdc31c3853783cbe8a5a4183250b909198553a4d95f1a7cd5",
+ "dist/2022-08-19/rust-std-beta-x86_64-unknown-netbsd.tar.gz": "cc4e9cb8e856ee173eb028746487604f0436347223f163294aa1ad34c0d740a3",
+ "dist/2022-08-19/rust-std-beta-x86_64-unknown-netbsd.tar.xz": "02a2ef7754af534e1fe6be3a04ef4446dc6abb6e6c57326ca460a5ba6c96f9d2",
+ "dist/2022-08-19/rust-std-beta-x86_64-unknown-none.tar.gz": "62d55c2a36afbf210d0b9947ef2f05104e7da71bb7958509119b0336874f0f92",
+ "dist/2022-08-19/rust-std-beta-x86_64-unknown-none.tar.xz": "86270ba78b8d74d0f3ac6c2f9a38906d25ec888a8ab604b0c593e99ba5128eed",
+ "dist/2022-08-19/rust-std-beta-x86_64-unknown-redox.tar.gz": "194a933992ab2596af7966e1b10900b1a4627bfef99b0ff1fc87bdf0a4e2ca17",
+ "dist/2022-08-19/rust-std-beta-x86_64-unknown-redox.tar.xz": "1a2137c628946d1cb1a84e24abe3a44758425a7470ed4a48555f9c6dca8e7013",
+ "dist/2022-08-19/rustc-beta-aarch64-apple-darwin.tar.gz": "b4a4752c96d439c2977a148a40b63864041f903e5733419faacd96c5839bc8e5",
+ "dist/2022-08-19/rustc-beta-aarch64-apple-darwin.tar.xz": "f3f83489cd6890aed11a75f4b60d3edf2b32dfac023c93faa8cf1604a9e8fad3",
+ "dist/2022-08-19/rustc-beta-aarch64-pc-windows-msvc.tar.gz": "5d012c23083429116ffb171923343169380559f3ee90fbaee0989f77da878487",
+ "dist/2022-08-19/rustc-beta-aarch64-pc-windows-msvc.tar.xz": "15f3534ff3b881709eebaf55e55359d700686429bf55eb4b1c7292ad1a13ff54",
+ "dist/2022-08-19/rustc-beta-aarch64-unknown-linux-gnu.tar.gz": "d3aa46b9f2f052b080222a13e598ffa96227ec271c9db2efd2ddbeb0b1fef2ed",
+ "dist/2022-08-19/rustc-beta-aarch64-unknown-linux-gnu.tar.xz": "932d89e503d2b4fad90241c014c4b4f892ab8054d9555590819422abb51a999e",
+ "dist/2022-08-19/rustc-beta-aarch64-unknown-linux-musl.tar.gz": "5c006e8324bc8a5b9039da7b61d1147575d0383a18deca3b75d01ebe684c7850",
+ "dist/2022-08-19/rustc-beta-aarch64-unknown-linux-musl.tar.xz": "0d03091ccefff702877e2c38d131932125f0b2d25c8a94e260601c277a3e494f",
+ "dist/2022-08-19/rustc-beta-arm-unknown-linux-gnueabi.tar.gz": "373650c44db5568d8d2889b6002d3b5948acd0079773f257d9dc0e380de8c026",
+ "dist/2022-08-19/rustc-beta-arm-unknown-linux-gnueabi.tar.xz": "0cbd356980688e816baf4c88b7c8963e4c4b009cdf3e9bac057a3fedccf0c552",
+ "dist/2022-08-19/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz": "de83386be1885fad366b0b3379f41a8c9e30ca6cfcc5752a0bd917ec635954ba",
+ "dist/2022-08-19/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz": "a605234a11f44a65b80554bbfd10b6fe83c20a2a62102aaa46f0febc5ad846b9",
+ "dist/2022-08-19/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz": "22c323b69a4786bee149402c7c64d85795540e2143e0da1e48739b2dbb1a869a",
+ "dist/2022-08-19/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz": "f9daad394d53aa405f783d9013916610b014d930a355fdb88757b47469619cf3",
+ "dist/2022-08-19/rustc-beta-i686-pc-windows-gnu.tar.gz": "275d21aafcc272b18bdefdd6adf28116a493beb053eb214b6ab842ccd31e9cbb",
+ "dist/2022-08-19/rustc-beta-i686-pc-windows-gnu.tar.xz": "5a032ea14e3845de2f39305a4fb5b9a4415939f6bd0ff79a0e527c4ebd9af669",
+ "dist/2022-08-19/rustc-beta-i686-pc-windows-msvc.tar.gz": "d892ad811e831b6f1cedf8d352dc71f9f86a26089fe4fce4d22b4f77482094eb",
+ "dist/2022-08-19/rustc-beta-i686-pc-windows-msvc.tar.xz": "ca16e42ea493c869b53031ea80b52c66c5771fc25531fb13529931d0740ec92a",
+ "dist/2022-08-19/rustc-beta-i686-unknown-linux-gnu.tar.gz": "d3aa43cb3bef265d6d2fdb17c6962e906ff021c4ca0d638eba6a98f09324d942",
+ "dist/2022-08-19/rustc-beta-i686-unknown-linux-gnu.tar.xz": "9dbd9e4a20535c8e19d9cee1218f2b3e0d3289d118b840b27e12aab3e48c80d8",
+ "dist/2022-08-19/rustc-beta-mips-unknown-linux-gnu.tar.gz": "5c17afb542d3661dc31cd3d334de66706de956f45959404494f90cf73e6f6d21",
+ "dist/2022-08-19/rustc-beta-mips-unknown-linux-gnu.tar.xz": "68fc729c47c290c09a14fe450ab0b82eba568a4e687554e0f87d095fe64d056f",
+ "dist/2022-08-19/rustc-beta-mips64-unknown-linux-gnuabi64.tar.gz": "64e77868b050b0d237eae46f33a723daa2cac8c8736e3787664315e13d879629",
+ "dist/2022-08-19/rustc-beta-mips64-unknown-linux-gnuabi64.tar.xz": "c3c339f17752f334dacf18c75837578d8bcd1020a59a8a21ad0b3cca0e58e49b",
+ "dist/2022-08-19/rustc-beta-mips64el-unknown-linux-gnuabi64.tar.gz": "45e586abdfb8a46f126b2327e64e115f8acfa861d183a276ef3688c148e99d46",
+ "dist/2022-08-19/rustc-beta-mips64el-unknown-linux-gnuabi64.tar.xz": "7be6e24c013bc03ae28d494d78e767488c38f7487557d5debd5a42eceaa49e34",
+ "dist/2022-08-19/rustc-beta-mipsel-unknown-linux-gnu.tar.gz": "a3af91bc8d8179bd06d51615ee2ccbda0c838f99c2b30fb91c52c76304ce46e0",
+ "dist/2022-08-19/rustc-beta-mipsel-unknown-linux-gnu.tar.xz": "948b7598e5c7648b02c39757e50ad9550f78fda4943b578b4d19831c2496e86c",
+ "dist/2022-08-19/rustc-beta-powerpc-unknown-linux-gnu.tar.gz": "1f01de1fb0274b98424867fba7eef93e00485624a5fb6e21657d6591607d9f50",
+ "dist/2022-08-19/rustc-beta-powerpc-unknown-linux-gnu.tar.xz": "879bf1abc548a1dee7fc6c26c0b0881fab064be58a1540b67730b8d3643f8ddd",
+ "dist/2022-08-19/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz": "3303c7dcbdd1a38a8e32e4b96071be4afd191bb6403d6fbf17fbc65087b1f15b",
+ "dist/2022-08-19/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz": "cf8693715e3d58e016c2dbe0bf77a3933765ea5fe0a65091cc427b694a490d60",
+ "dist/2022-08-19/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz": "4ad0221a3df7e223d3bdf271f28f0a694aef8184cd377cae530fbca5c12d82d6",
+ "dist/2022-08-19/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz": "ad1390820e4fcc426a27d18f97fc8f952c9e949327d8d6b60e6853e4dc9e2c04",
+ "dist/2022-08-19/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz": "c9232ad8c52060c40ef35eae32e79585d550e2abc011d2b6562edb1b02244966",
+ "dist/2022-08-19/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz": "3e937985bbf7ef5af05e96e82114fec6ee17bc04f784c3e7472224dae1ebb148",
+ "dist/2022-08-19/rustc-beta-s390x-unknown-linux-gnu.tar.gz": "f4dfa226259ce0d9f8fb9295f82a18a130470f11479811717ddb0f5064a9eb81",
+ "dist/2022-08-19/rustc-beta-s390x-unknown-linux-gnu.tar.xz": "919dd5fbfc80c77f270e1eefee8dbe1c76ed7cc94cc11dff34f54ee05fd415de",
+ "dist/2022-08-19/rustc-beta-x86_64-apple-darwin.tar.gz": "482c51eef00cc238872b5002f12c351259a666e8b1380b08f5536fb87b046f4a",
+ "dist/2022-08-19/rustc-beta-x86_64-apple-darwin.tar.xz": "3b78a63fb9d27e7960ea35d8bef3734789bd48d80ed1605ddb9f547859977260",
+ "dist/2022-08-19/rustc-beta-x86_64-pc-windows-gnu.tar.gz": "4c7ec47ced7a7cbd9e6b038ac0f57d0e705801f42375011eac42a708808d75c5",
+ "dist/2022-08-19/rustc-beta-x86_64-pc-windows-gnu.tar.xz": "6935fbe4de2b5712fe15c865ef2eb4c41959267b03afe4f276f8fcc6eee992bd",
+ "dist/2022-08-19/rustc-beta-x86_64-pc-windows-msvc.tar.gz": "30b78a0ac0852bbb3aecea5d4f61f4e1737b5d4910e07a59c64e7cdfeb3da215",
+ "dist/2022-08-19/rustc-beta-x86_64-pc-windows-msvc.tar.xz": "73d15af622a7accb4a04bd596acc95cd0856545fe6a386df18905cd221107fee",
+ "dist/2022-08-19/rustc-beta-x86_64-unknown-freebsd.tar.gz": "dc00c8c6f6121ac1ba112120b82215ce811a4c518025cdc4d1202c53db88f65e",
+ "dist/2022-08-19/rustc-beta-x86_64-unknown-freebsd.tar.xz": "e827aa07bff90fb978b1055826d9e672e1e40056caab3d8a182afa3c371f0f4f",
+ "dist/2022-08-19/rustc-beta-x86_64-unknown-illumos.tar.gz": "05f6a99d63847d910e05a93d78944014c775c5b6e8b8bd742f2c74c39f7cd883",
+ "dist/2022-08-19/rustc-beta-x86_64-unknown-illumos.tar.xz": "86cde423efde88155ce35514399897fcb2248ddac9f77244827ba4e10189038a",
+ "dist/2022-08-19/rustc-beta-x86_64-unknown-linux-gnu.tar.gz": "e2dea522d946db96791f651fdbb098bcf7285bdb0182e14c573f87826f6d99b2",
+ "dist/2022-08-19/rustc-beta-x86_64-unknown-linux-gnu.tar.xz": "399847ea81239d666acdd890b4407bf2f62f3a527a60515fa38fdfd3ff61d0d0",
+ "dist/2022-08-19/rustc-beta-x86_64-unknown-linux-musl.tar.gz": "9e1511eb0704962ad3932578821bcfca877826cb18f14e4b7defbc08aaa9f0d8",
+ "dist/2022-08-19/rustc-beta-x86_64-unknown-linux-musl.tar.xz": "1e0eb9eb917a1db99764b374844ab3ed2cbc83495c50b28f3dcf9512edcb035f",
+ "dist/2022-08-19/rustc-beta-x86_64-unknown-netbsd.tar.gz": "28651213923b8009c6317b59a2a15f6f202108efa0b604a590b735b0cd031202",
+ "dist/2022-08-19/rustc-beta-x86_64-unknown-netbsd.tar.xz": "36591aefa17e1a4cb55625632bc97931db6f3c4252d457963353bbfa5e997a32",
+ "dist/2022-08-20/rustfmt-nightly-aarch64-apple-darwin.tar.gz": "3f90dbf1c64d8b4cddd1fcb589b85e4b76fae6c3fed88483210bf5bddecd2bf6",
+ "dist/2022-08-20/rustfmt-nightly-aarch64-apple-darwin.tar.xz": "1f850ef1e1a6ee89cbb862ec986cc3b5de2b4654db625a5da0906f3eff3e834c",
+ "dist/2022-08-20/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz": "3b1a7f2e5d380b58bd02fd1f28212869f9feeb2fbb971694425cb837f99f8f0f",
+ "dist/2022-08-20/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz": "f24153076383ad22da9cefd9073c1e6dc227c582ef7fb92c73cdfbd4cef6c295",
+ "dist/2022-08-20/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz": "e409988fc3bd50f2120d3f5828e8f5a5494fa0bc5296a97f1c26294fb2e1ade7",
+ "dist/2022-08-20/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz": "854e275ecd011e12606203f4b9737c373a678821b7f11df6bc08b774d670ada7",
+ "dist/2022-08-20/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz": "31bb41173b63d3aaae977b0ab48b0302d1ba2b7f6470fbd7b0206557e2dca2f3",
+ "dist/2022-08-20/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz": "7f4e356dca11bfc4bcfc9ffa588640cd9321cd73bc70c16d55901f04a119ca2d",
+ "dist/2022-08-20/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz": "1a8a63027aeaca5c9981567c862222b9c2d0de0456c4534213f5686862507890",
+ "dist/2022-08-20/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz": "2d217c695886cd59bf7bf40091813d427eb302323650d3641439f050308b2660",
+ "dist/2022-08-20/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz": "bc257cce6466ef7bfd30eb9790db3e72cc425ce78f763954861b28232dfe1d55",
+ "dist/2022-08-20/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz": "3326acc5adcf63dde12937d1ad9be4eb8c01ed3f24b07e1df28d194754506a77",
+ "dist/2022-08-20/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz": "14dc0a6e21cd6e7294995bf7c14238c97cccaf37910765602ca37292267060b0",
+ "dist/2022-08-20/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz": "93628b8ea58d6484423ff58666520701702f36eab86846a19b205a9c8d737313",
+ "dist/2022-08-20/rustfmt-nightly-i686-pc-windows-gnu.tar.gz": "199dfdf436b9ae97aa7a4a081d6a88b29e825e0e3e24409a008531dc3ea3ae5b",
+ "dist/2022-08-20/rustfmt-nightly-i686-pc-windows-gnu.tar.xz": "cbce925e5bc323d970d7df1c714b31b693208eae0a00eb43a616da3ba499cca2",
+ "dist/2022-08-20/rustfmt-nightly-i686-pc-windows-msvc.tar.gz": "0c899e9273fb04c73c3abbd9483e74cf0312aa61d0818acbd2737a515432cb1c",
+ "dist/2022-08-20/rustfmt-nightly-i686-pc-windows-msvc.tar.xz": "bc3d2e850d77f13b563ce3b286fd781f49abc6ac17f046aea7397655c3955194",
+ "dist/2022-08-20/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz": "8940d2f6219a956521ef3c3c43001843cc5bd65706853e7b506848bf11c785d1",
+ "dist/2022-08-20/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz": "bee2d0136087693ed3328b8264f4c652f843a18128bfbf3d679b6ce426b44d07",
+ "dist/2022-08-20/rustfmt-nightly-mips-unknown-linux-gnu.tar.gz": "37f383823d76b2f88fe7c7950b96aad73b94ffe5bf74f7af05d0bbe18c492f66",
+ "dist/2022-08-20/rustfmt-nightly-mips-unknown-linux-gnu.tar.xz": "832d60d4dad18f729638c5cea3b5a3a8577b1875977b8bf2110537a4b17a71e2",
+ "dist/2022-08-20/rustfmt-nightly-mips64-unknown-linux-gnuabi64.tar.gz": "e060d1a8925f9b7f670244718fc0d84519a590cc630e832b27a0bd8149c009af",
+ "dist/2022-08-20/rustfmt-nightly-mips64-unknown-linux-gnuabi64.tar.xz": "3743c798e26306d3f55afcf989221ade32e5174132e939bd4d3f0a2f1fd7523e",
+ "dist/2022-08-20/rustfmt-nightly-mips64el-unknown-linux-gnuabi64.tar.gz": "723509d619bb3f2c5423267afdbe082b426678d5d1df850b7e0d51cbcad99491",
+ "dist/2022-08-20/rustfmt-nightly-mips64el-unknown-linux-gnuabi64.tar.xz": "da258b293fa651db38348eee14239171010b230e390074a65f5975bfd9a990f2",
+ "dist/2022-08-20/rustfmt-nightly-mipsel-unknown-linux-gnu.tar.gz": "6ad271d28309c92a76bdef40052c624b8d9e62e389ab14c8df372a6d0cfb8883",
+ "dist/2022-08-20/rustfmt-nightly-mipsel-unknown-linux-gnu.tar.xz": "b3e71cedf9df9ce72bdd9b559197fd3989e4d108cd71ff30806b75541c072c34",
+ "dist/2022-08-20/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz": "82e7f9cd8409e9eeb89166c8ff047910e24a45d65712cc4bef883bb5ccbeb51d",
+ "dist/2022-08-20/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz": "9e0da808f7a3c03b10b309b2a1d934e447bef877adbe78b8d5f9485b2464bb32",
+ "dist/2022-08-20/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz": "31841de97f5f7fef456205ab14b7cb568b7edd202f31fc8a4465105ab59ed413",
+ "dist/2022-08-20/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz": "bc55f15780a850f2fa19fa26d39edce194745235d6f209d21b86a2ab7924c4dd",
+ "dist/2022-08-20/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz": "bdb86ba6b1dfe3359823aa2be3ff1b945d7a34c21383fb2b2ec6f06122ef5587",
+ "dist/2022-08-20/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz": "1df7664bd1f32e1a9b5d37a4eaaaac54ca7b71853edfed4b0ece2e256f17290b",
+ "dist/2022-08-20/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz": "67727084e37de55a4cfa4ad2c1f3a834ef49379f949b50440aeb53d65c6dfc36",
+ "dist/2022-08-20/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz": "820fdce7d561b7e0118b25093cf639eab304ed29852921286f0cbda62e3e8752",
+ "dist/2022-08-20/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz": "8cf01738cb6e7450753000737c6b33acf7d433d32b28f6b278d6efb9513c4464",
+ "dist/2022-08-20/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz": "3bb194f2d770934580d063b7f0939cea231dbd280a4df062f0309c3dcbcd1d32",
+ "dist/2022-08-20/rustfmt-nightly-x86_64-apple-darwin.tar.gz": "95a42b3a88d411d7c2567f8532bf86d3bd8ee57ff2a5254c147f08502f5488a9",
+ "dist/2022-08-20/rustfmt-nightly-x86_64-apple-darwin.tar.xz": "ed885440a7a03d81dfc6ba206c37afdcd4480368a11143805ca12d8df583172d",
+ "dist/2022-08-20/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz": "2d6aa369242b820621cddc095f833eb5bf9dda9127dbcea179d730b2e1fc0d09",
+ "dist/2022-08-20/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz": "49291adffb00f8f25e9dadfb8e8f530d6a151479e8024de4b0b42c52c7a21f7e",
+ "dist/2022-08-20/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz": "a61ebb7b049b8a8c8440acc7e8698896a3143c0e1f8b1c29084f680e12087d9b",
+ "dist/2022-08-20/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz": "8bde3268496729d1f5178863859fdad77fa94f85887b0c129e0517226e3bdfb4",
+ "dist/2022-08-20/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz": "08f4bcd90bc01fea5e0c6238d40fb59bd1160e6635b401f908c046e6446d2183",
+ "dist/2022-08-20/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz": "a13ad331d827666d8631b4b30a51b1b69de0a90e9cfecc1e8bfb559d52144353",
+ "dist/2022-08-20/rustfmt-nightly-x86_64-unknown-illumos.tar.gz": "2c7c0607392a0d2cf517cc4fcb726f443c85c26b7deca0f10cbc555de56d20d3",
+ "dist/2022-08-20/rustfmt-nightly-x86_64-unknown-illumos.tar.xz": "bbd31f2bd64b4cc2e11c0936787bb44d792d20ec303e3b55efd53e0bd8efe88c",
+ "dist/2022-08-20/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz": "27703699164e10526a32a6920eb8d0b71e3572d423defecd81cd6b24c2c5134a",
+ "dist/2022-08-20/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz": "e4d388af9bdfee7bb6470a4467e7743a9b7827cd9d4219174fd424c18204a6b6",
+ "dist/2022-08-20/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz": "e090c6410f3ded22065097b5839a80d1ca6c80a7657ca28b88244fe119977371",
+ "dist/2022-08-20/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz": "a00a785d87e64660165b56489f6709c0d0280ae728782356a3274e8c0be48c36",
+ "dist/2022-08-20/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz": "ab9736c80331f0febe42a0e1f29d758327c4c8035954111370ff2c568c784fe4",
+ "dist/2022-08-20/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz": "ca8127aeae4e84a208ce9a5cdd04c497894e62c226c8bccd00713c1a41f99ddd"
}
}
#[lang="copy"]
trait Copy { }
-//x86_64: define win64cc void @has_efiapi
+//x86_64: define dso_local win64cc void @has_efiapi
//i686: define void @has_efiapi
//aarch64: define dso_local void @has_efiapi
//arm: define dso_local void @has_efiapi
}
}
-// CHECK: define void @test(){{.+}}addrspace(1)
+// CHECK: define dso_local void @test(){{.+}}addrspace(1)
#[no_mangle]
pub extern "C" fn test() {
let mut buf = 7;
// Test that `wrapping_div` only checks divisor once.
-// This test checks that there is only a single compare agains -1 and -1 is not present as a
+// This test checks that there is only a single compare against -1 and -1 is not present as a
// switch case (the second check present until rustc 1.12).
// This test also verifies that a single panic call is generated (for the division by zero case).
--- /dev/null
+// min-llvm-version: 15.0.0
+// compile-flags: -O
+
+#![crate_type = "lib"]
+
+#[no_mangle]
+pub fn u16_be_to_arch(mut data: [u8; 2]) -> [u8; 2] {
+ // CHECK-LABEL: @u16_be_to_arch
+ // CHECK: @llvm.bswap.i16
+ data.reverse();
+ data
+}
+
+#[no_mangle]
+pub fn u32_be_to_arch(mut data: [u8; 4]) -> [u8; 4] {
+ // CHECK-LABEL: @u32_be_to_arch
+ // CHECK: @llvm.bswap.i32
+ data.reverse();
+ data
+}
--- /dev/null
+// min-llvm-version: 15.0
+// compile-flags: -O
+
+#![crate_type = "lib"]
+#![feature(inline_const)]
+
+use std::mem::MaybeUninit;
+
+pub fn maybe_uninit() -> [MaybeUninit<u8>; 3000] {
+ // CHECK-NOT: memset
+ [MaybeUninit::uninit(); 3000]
+}
+
+pub fn maybe_uninit_const<T>() -> [MaybeUninit<T>; 8192] {
+ // CHECK-NOT: memset
+ [const { MaybeUninit::uninit() }; 8192]
+}
#[lang="sized"]
trait Sized { }
-// Test that `nounwind` atributes are correctly applied to exported `aapcs` and
+// Test that `nounwind` attributes are correctly applied to exported `aapcs` and
// `aapcs-unwind` extern functions. `aapcs-unwind` functions MUST NOT have this attribute. We
// disable optimizations above to prevent LLVM from inferring the attribute.
// compile-flags: -C panic=abort
-// Test that `nounwind` atributes are also applied to extern `C-unwind` Rust functions
+// Test that `nounwind` attributes are also applied to extern `C-unwind` Rust functions
// when the code is compiled with `panic=abort`.
#![crate_type = "lib"]
// compile-flags: -C opt-level=0
-// Test that `nounwind` atributes are correctly applied to exported `C` and `C-unwind` extern
+// Test that `nounwind` attributes are correctly applied to exported `C` and `C-unwind` extern
// functions. `C-unwind` functions MUST NOT have this attribute. We disable optimizations above
// to prevent LLVM from inferring the attribute.
// compile-flags: -C opt-level=0
-// Test that `nounwind` atributes are correctly applied to exported `cdecl` and
+// Test that `nounwind` attributes are correctly applied to exported `cdecl` and
// `cdecl-unwind` extern functions. `cdecl-unwind` functions MUST NOT have this attribute. We
// disable optimizations above to prevent LLVM from inferring the attribute.
#[lang="sized"]
trait Sized { }
-// Test that `nounwind` atributes are correctly applied to exported `fastcall` and
+// Test that `nounwind` attributes are correctly applied to exported `fastcall` and
// `fastcall-unwind` extern functions. `fastcall-unwind` functions MUST NOT have this attribute. We
// disable optimizations above to prevent LLVM from inferring the attribute.
#![crate_type = "lib"]
-// We disable optimizations to prevent LLVM from infering the attribute.
+// We disable optimizations to prevent LLVM from inferring the attribute.
// CHECK: Function Attrs:{{.*}}nounwind
// CHECK-NEXT: @foo
#![crate_type = "lib"]
-// We disable optimizations to prevent LLVM from infering the attribute.
+// We disable optimizations to prevent LLVM from inferring the attribute.
extern "C" {
fn bar();
#![crate_type = "lib"]
#![feature(c_unwind)]
-// We disable optimizations to prevent LLVM from infering the attribute.
+// We disable optimizations to prevent LLVM from inferring the attribute.
// CHECK: Function Attrs:{{.*}}nounwind
// CHECK-NEXT: @foo
#[lang="sized"]
trait Sized { }
-// Test that `nounwind` atributes are correctly applied to exported `stdcall` and `stdcall-unwind`
+// Test that `nounwind` attributes are correctly applied to exported `stdcall` and `stdcall-unwind`
// extern functions. `stdcall-unwind` functions MUST NOT have this attribute. We disable
// optimizations above to prevent LLVM from inferring the attribute.
// compile-flags: -C opt-level=0
-// Test that `nounwind` atributes are correctly applied to exported `system` and `system-unwind`
+// Test that `nounwind` attributes are correctly applied to exported `system` and `system-unwind`
// extern functions. `system-unwind` functions MUST NOT have this attribute. We disable
// optimizations above to prevent LLVM from inferring the attribute.
#[lang="sized"]
trait Sized { }
-// Test that `nounwind` atributes are correctly applied to exported `sysv64` and
+// Test that `nounwind` attributes are correctly applied to exported `sysv64` and
// `sysv64-unwind` extern functions. `sysv64-unwind` functions MUST NOT have this attribute. We
// disable optimizations above to prevent LLVM from inferring the attribute.
#[lang="sized"]
trait Sized { }
-// Test that `nounwind` atributes are correctly applied to exported `thiscall` and
+// Test that `nounwind` attributes are correctly applied to exported `thiscall` and
// `thiscall-unwind` extern functions. `thiscall-unwind` functions MUST NOT have this attribute. We
// disable optimizations above to prevent LLVM from inferring the attribute.
#[lang="sized"]
trait Sized { }
-// Test that `nounwind` atributes are correctly applied to exported `vectorcall` and
+// Test that `nounwind` attributes are correctly applied to exported `vectorcall` and
// `vectorcall-unwind` extern functions. `vectorcall-unwind` functions MUST NOT have this attribute.
// We disable optimizations above to prevent LLVM from inferring the attribute.
#[lang="sized"]
trait Sized { }
-// Test that `nounwind` atributes are correctly applied to exported `win64` and
+// Test that `nounwind` attributes are correctly applied to exported `win64` and
// `win64-unwind` extern functions. `win64-unwind` functions MUST NOT have this attribute. We
// disable optimizations above to prevent LLVM from inferring the attribute.
#![feature(c_unwind)]
// Make sure these all do *not* get the attribute.
-// We disable optimizations to prevent LLVM from infering the attribute.
+// We disable optimizations to prevent LLVM from inferring the attribute.
// CHECK-NOT: nounwind
// "C" ABI
}
#[cfg(not(any(cfail1,cfail4)))]
-#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,generics_of,predicates_of")]
+#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,predicates_of")]
#[rustc_clean(cfg="cfail3")]
-#[rustc_clean(cfg="cfail5", except="hir_owner,hir_owner_nodes,generics_of,predicates_of")]
+#[rustc_clean(cfg="cfail5", except="hir_owner,hir_owner_nodes,predicates_of")]
#[rustc_clean(cfg="cfail6")]
enum EnumAddLifetimeBoundToParameter<'a, T: 'a> {
Variant1(T),
}
#[cfg(not(any(cfail1,cfail4)))]
-#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,generics_of,predicates_of")]
+#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,predicates_of")]
#[rustc_clean(cfg="cfail3")]
-#[rustc_clean(cfg="cfail5", except="hir_owner,hir_owner_nodes,generics_of,predicates_of")]
+#[rustc_clean(cfg="cfail5", except="hir_owner,hir_owner_nodes,predicates_of")]
#[rustc_clean(cfg="cfail6")]
enum EnumAddLifetimeBoundToParameterWhere<'a, T> where T: 'a {
Variant1(T),
trait TraitAddLifetimeBoundToTypeParameterOfTrait<'a, T> { }
#[cfg(not(any(cfail1,cfail4)))]
-#[rustc_clean(except="hir_owner,hir_owner_nodes,generics_of,predicates_of", cfg="cfail2")]
+#[rustc_clean(except="hir_owner,hir_owner_nodes,predicates_of", cfg="cfail2")]
#[rustc_clean(cfg="cfail3")]
-#[rustc_clean(except="hir_owner,hir_owner_nodes,generics_of,predicates_of", cfg="cfail5")]
+#[rustc_clean(except="hir_owner,hir_owner_nodes,predicates_of", cfg="cfail5")]
#[rustc_clean(cfg="cfail6")]
trait TraitAddLifetimeBoundToTypeParameterOfTrait<'a, T: 'a> { }
trait TraitAddSecondLifetimeBoundToTypeParameterOfTrait<'a, 'b, T: 'a> { }
#[cfg(not(any(cfail1,cfail4)))]
-#[rustc_clean(except="hir_owner,hir_owner_nodes,generics_of,predicates_of", cfg="cfail2")]
+#[rustc_clean(except="hir_owner,hir_owner_nodes,predicates_of", cfg="cfail2")]
#[rustc_clean(cfg="cfail3")]
-#[rustc_clean(except="hir_owner,hir_owner_nodes,generics_of,predicates_of", cfg="cfail5")]
+#[rustc_clean(except="hir_owner,hir_owner_nodes,predicates_of", cfg="cfail5")]
#[rustc_clean(cfg="cfail6")]
trait TraitAddSecondLifetimeBoundToTypeParameterOfTrait<'a, 'b, T: 'a + 'b> { }
trait TraitAddLifetimeBoundToTypeParameterOfTraitWhere<'a, T> { }
#[cfg(not(any(cfail1,cfail4)))]
-#[rustc_clean(except="hir_owner,hir_owner_nodes,generics_of,predicates_of", cfg="cfail2")]
+#[rustc_clean(except="hir_owner,hir_owner_nodes,predicates_of", cfg="cfail2")]
#[rustc_clean(cfg="cfail3")]
-#[rustc_clean(except="hir_owner,hir_owner_nodes,generics_of,predicates_of", cfg="cfail5")]
+#[rustc_clean(except="hir_owner,hir_owner_nodes,predicates_of", cfg="cfail5")]
#[rustc_clean(cfg="cfail6")]
trait TraitAddLifetimeBoundToTypeParameterOfTraitWhere<'a, T> where T: 'a { }
trait TraitAddSecondLifetimeBoundToTypeParameterOfTraitWhere<'a, 'b, T> where T: 'a { }
#[cfg(not(any(cfail1,cfail4)))]
-#[rustc_clean(except="hir_owner,hir_owner_nodes,generics_of,predicates_of", cfg="cfail2")]
+#[rustc_clean(except="hir_owner,hir_owner_nodes,predicates_of", cfg="cfail2")]
#[rustc_clean(cfg="cfail3")]
-#[rustc_clean(except="hir_owner,hir_owner_nodes,generics_of,predicates_of", cfg="cfail5")]
+#[rustc_clean(except="hir_owner,hir_owner_nodes,predicates_of", cfg="cfail5")]
#[rustc_clean(cfg="cfail6")]
trait TraitAddSecondLifetimeBoundToTypeParameterOfTraitWhere<'a, 'b, T> where T: 'a + 'b { }
// compile-flags: -Z query-dep-graph
// aux-build:cached_hygiene.rs
-// This tests the folllowing scenario
+// This tests the following scenario
// 1. A foreign crate is compiled with incremental compilation.
// This causes hygiene information to be saved to the incr cache.
// 2. One function is the foreign crate is modified. This causes the
// Regression test for hashing involving canonical variables. In this
-// test -- which has an intensional error -- the type of the value
+// test -- which has an intentional error -- the type of the value
// being dropped winds up including a type variable. Canonicalization
// would then produce a `?0` which -- in turn -- triggered an ICE in
// hashing.
// rust-lang/rust#69798:
//
-// This is analgous to cgu_invalidated_when_import_added, but it covers a
+// This is analogous to cgu_invalidated_when_import_added, but it covers a
// problem uncovered where a change to the *export* set caused a link failure
// when reusing post-LTO optimized object code.
// rust-lang/rust#69798:
//
-// This is analgous to cgu_invalidated_when_export_added, but it covers the
+// This is analogous to cgu_invalidated_when_export_added, but it covers the
// other direction. This is analogous to cgu_invalidated_when_import_added: we
// include it, because it may uncover bugs in variant implementation strategies.
StorageLive(_4); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+4:9: +4:10
_4 = (_2.1: i32); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+4:13: +4:16
StorageLive(_5); // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:9: +5:10
- _5 = (_2.0: i32); // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:13: +5:16
+- _5 = (_2.0: i32); // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:13: +5:16
++ _5 = const 1_i32; // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:13: +5:16
nop; // scope 0 at $DIR/mutable_variable_unprop_assign.rs:+0:11: +6:2
StorageDead(_5); // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
StorageDead(_4); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
-//! Tests that we can propogate into places that are projections into unions
+//! Tests that we can propagate into places that are projections into unions
// compile-flags: -Zunsound-mir-opts
fn val() -> u32 {
1
-// compile-flags: -Z mir-opt-level=4 -Zunsound-mir-opts
+// unit-test: EarlyOtherwiseBranch
+
+// FIXME: This test was broken by the derefer change.
// example from #68867
type CSSFloat = f32;
}
// EMIT_MIR early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.diff
-// EMIT_MIR early_otherwise_branch_68867.try_sum EarlyOtherwiseBranch.before SimplifyConstCondition-final.after
#[no_mangle]
pub extern "C" fn try_sum(
x: &ViewportPercentageLength,
+++ /dev/null
-- // MIR for `try_sum` before EarlyOtherwiseBranch
-+ // MIR for `try_sum` after SimplifyConstCondition-final
-
- fn try_sum(_1: &ViewportPercentageLength, _2: &ViewportPercentageLength) -> Result<ViewportPercentageLength, ()> {
- debug x => _1; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+1:5: +1:6
- debug other => _2; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+2:5: +2:10
- let mut _0: std::result::Result<ViewportPercentageLength, ()>; // return place in scope 0 at $DIR/early_otherwise_branch_68867.rs:+3:6: +3:42
- let mut _3: ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +11:6
- let mut _4: (&ViewportPercentageLength, &ViewportPercentageLength); // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- let mut _5: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:15: +5:16
- let mut _6: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:18: +5:23
- let mut _7: isize; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:21: +6:30
- let mut _8: isize; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:21: +7:30
- let mut _9: isize; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:23: +8:34
- let mut _10: isize; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:23: +9:34
- let mut _11: isize; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:11: +6:18
- let _12: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:14: +6:17
- let _13: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:24: +6:29
- let mut _14: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:38: +6:49
- let mut _15: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:38: +6:41
- let mut _16: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:44: +6:49
- let _17: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:14: +7:17
- let _18: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:24: +7:29
- let mut _19: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:38: +7:49
- let mut _20: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:38: +7:41
- let mut _21: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:44: +7:49
- let _22: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:16: +8:19
- let _23: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:28: +8:33
- let mut _24: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:44: +8:55
- let mut _25: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:44: +8:47
- let mut _26: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:50: +8:55
- let _27: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:16: +9:19
- let _28: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:28: +9:33
- let mut _29: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:44: +9:55
- let mut _30: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:44: +9:47
- let mut _31: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:50: +9:55
- let mut _32: !; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:14: +10:28
- let mut _33: (); // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:25: +10:27
- let mut _34: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- let mut _35: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- let mut _36: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- let mut _37: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- let mut _38: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- let mut _39: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- let mut _40: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- let mut _41: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- let mut _42: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- let mut _43: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- let mut _44: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- let mut _45: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- let mut _46: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- scope 1 {
-- debug one => _12; // in scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:14: +6:17
-- debug other => _13; // in scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:24: +6:29
-+ debug one => _15; // in scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:14: +6:17
-+ debug other => _16; // in scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:24: +6:29
- }
- scope 2 {
-- debug one => _17; // in scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:14: +7:17
-- debug other => _18; // in scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:24: +7:29
-+ debug one => _20; // in scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:14: +7:17
-+ debug other => _21; // in scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:24: +7:29
- }
- scope 3 {
-- debug one => _22; // in scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:16: +8:19
-- debug other => _23; // in scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:28: +8:33
-+ debug one => _25; // in scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:16: +8:19
-+ debug other => _26; // in scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:28: +8:33
- }
- scope 4 {
-- debug one => _27; // in scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:16: +9:19
-- debug other => _28; // in scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:28: +9:33
-+ debug one => _30; // in scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:16: +9:19
-+ debug other => _31; // in scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:28: +9:33
- }
-
- bb0: {
-- StorageLive(_3); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +11:6
-- StorageLive(_4); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
-- StorageLive(_5); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:15: +5:16
-- _5 = _1; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:15: +5:16
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +11:6
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:15: +5:16
-+ (_4.0: &ViewportPercentageLength) = _1; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:15: +5:16
- StorageLive(_6); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:18: +5:23
- _6 = _2; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:18: +5:23
- Deinit(_4); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
-- (_4.0: &ViewportPercentageLength) = move _5; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- (_4.1: &ViewportPercentageLength) = move _6; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- StorageDead(_6); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:23: +5:24
-- StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:23: +5:24
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:23: +5:24
- _34 = deref_copy (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- _11 = discriminant((*_34)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- switchInt(move _11) -> [0_isize: bb1, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb11]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +5:24
- }
-
- bb1: {
- _35 = deref_copy (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- _7 = discriminant((*_35)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- switchInt(move _7) -> [0_isize: bb6, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +5:24
- }
-
- bb2: {
- StorageLive(_33); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:25: +10:27
- nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:25: +10:27
- Deinit(_0); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:21: +10:28
- nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:21: +10:28
- discriminant(_0) = 1; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:21: +10:28
- StorageDead(_33); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:27: +10:28
-- StorageDead(_3); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+11:6: +11:7
-- StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+12:1: +12:2
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+11:6: +11:7
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+12:1: +12:2
- return; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+12:2: +12:2
- }
-
- bb3: {
- _36 = deref_copy (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- _8 = discriminant((*_36)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- switchInt(move _8) -> [1_isize: bb7, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +5:24
- }
-
- bb4: {
- _37 = deref_copy (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- _9 = discriminant((*_37)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- switchInt(move _9) -> [2_isize: bb8, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +5:24
- }
-
- bb5: {
- _38 = deref_copy (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- _10 = discriminant((*_38)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- switchInt(move _10) -> [3_isize: bb9, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +5:24
- }
-
- bb6: {
-- StorageLive(_12); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:14: +6:17
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:14: +6:17
- _39 = deref_copy (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:14: +6:17
-- _12 = (((*_39) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:14: +6:17
-- StorageLive(_13); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:24: +6:29
-+ _15 = (((*_39) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:14: +6:17
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:24: +6:29
- _40 = deref_copy (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:24: +6:29
-- _13 = (((*_40) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:24: +6:29
-- StorageLive(_14); // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:38: +6:49
-- StorageLive(_15); // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:38: +6:41
-- _15 = _12; // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:38: +6:41
-- StorageLive(_16); // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:44: +6:49
-- _16 = _13; // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:44: +6:49
-- _14 = Add(move _15, move _16); // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:38: +6:49
-- StorageDead(_16); // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:48: +6:49
-- StorageDead(_15); // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:48: +6:49
-- Deinit(_3); // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:35: +6:50
-- ((_3 as Vw).0: f32) = move _14; // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:35: +6:50
-- discriminant(_3) = 0; // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:35: +6:50
-- StorageDead(_14); // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:49: +6:50
-- StorageDead(_13); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:49: +6:50
-- StorageDead(_12); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:49: +6:50
-+ _16 = (((*_40) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:24: +6:29
-+ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:38: +6:49
-+ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:38: +6:41
-+ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:38: +6:41
-+ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:44: +6:49
-+ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:44: +6:49
-+ ((((_0 as Ok).0: ViewportPercentageLength) as Vw).0: f32) = Add(move _15, move _16); // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:38: +6:49
-+ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:48: +6:49
-+ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:48: +6:49
-+ Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:35: +6:50
-+ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:35: +6:50
-+ discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 0; // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:35: +6:50
-+ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:49: +6:50
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:49: +6:50
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:49: +6:50
- goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:49: +6:50
- }
-
- bb7: {
-- StorageLive(_17); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:14: +7:17
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:14: +7:17
- _41 = deref_copy (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:14: +7:17
-- _17 = (((*_41) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:14: +7:17
-- StorageLive(_18); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:24: +7:29
-+ _20 = (((*_41) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:14: +7:17
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:24: +7:29
- _42 = deref_copy (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:24: +7:29
-- _18 = (((*_42) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:24: +7:29
-- StorageLive(_19); // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:38: +7:49
-- StorageLive(_20); // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:38: +7:41
-- _20 = _17; // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:38: +7:41
-- StorageLive(_21); // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:44: +7:49
-- _21 = _18; // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:44: +7:49
-- _19 = Add(move _20, move _21); // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:38: +7:49
-- StorageDead(_21); // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:48: +7:49
-- StorageDead(_20); // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:48: +7:49
-- Deinit(_3); // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:35: +7:50
-- ((_3 as Vh).0: f32) = move _19; // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:35: +7:50
-- discriminant(_3) = 1; // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:35: +7:50
-- StorageDead(_19); // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:49: +7:50
-- StorageDead(_18); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:49: +7:50
-- StorageDead(_17); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:49: +7:50
-+ _21 = (((*_42) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:24: +7:29
-+ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:38: +7:49
-+ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:38: +7:41
-+ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:38: +7:41
-+ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:44: +7:49
-+ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:44: +7:49
-+ ((((_0 as Ok).0: ViewportPercentageLength) as Vh).0: f32) = Add(move _20, move _21); // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:38: +7:49
-+ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:48: +7:49
-+ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:48: +7:49
-+ Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:35: +7:50
-+ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:35: +7:50
-+ discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 1; // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:35: +7:50
-+ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:49: +7:50
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:49: +7:50
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:49: +7:50
- goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:49: +7:50
- }
-
- bb8: {
-- StorageLive(_22); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:16: +8:19
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:16: +8:19
- _43 = deref_copy (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:16: +8:19
-- _22 = (((*_43) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:16: +8:19
-- StorageLive(_23); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:28: +8:33
-+ _25 = (((*_43) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:16: +8:19
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:28: +8:33
- _44 = deref_copy (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:28: +8:33
-- _23 = (((*_44) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:28: +8:33
-- StorageLive(_24); // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:44: +8:55
-- StorageLive(_25); // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:44: +8:47
-- _25 = _22; // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:44: +8:47
-- StorageLive(_26); // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:50: +8:55
-- _26 = _23; // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:50: +8:55
-- _24 = Add(move _25, move _26); // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:44: +8:55
-- StorageDead(_26); // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:54: +8:55
-- StorageDead(_25); // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:54: +8:55
-- Deinit(_3); // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:39: +8:56
-- ((_3 as Vmin).0: f32) = move _24; // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:39: +8:56
-- discriminant(_3) = 2; // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:39: +8:56
-- StorageDead(_24); // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:55: +8:56
-- StorageDead(_23); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:55: +8:56
-- StorageDead(_22); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:55: +8:56
-+ _26 = (((*_44) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:28: +8:33
-+ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:44: +8:55
-+ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:44: +8:47
-+ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:44: +8:47
-+ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:50: +8:55
-+ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:50: +8:55
-+ ((((_0 as Ok).0: ViewportPercentageLength) as Vmin).0: f32) = Add(move _25, move _26); // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:44: +8:55
-+ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:54: +8:55
-+ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:54: +8:55
-+ Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:39: +8:56
-+ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:39: +8:56
-+ discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 2; // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:39: +8:56
-+ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:55: +8:56
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:55: +8:56
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:55: +8:56
- goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:55: +8:56
- }
-
- bb9: {
-- StorageLive(_27); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:16: +9:19
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:16: +9:19
- _45 = deref_copy (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:16: +9:19
-- _27 = (((*_45) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:16: +9:19
-- StorageLive(_28); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:28: +9:33
-+ _30 = (((*_45) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:16: +9:19
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:28: +9:33
- _46 = deref_copy (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:28: +9:33
-- _28 = (((*_46) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:28: +9:33
-- StorageLive(_29); // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:44: +9:55
-- StorageLive(_30); // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:44: +9:47
-- _30 = _27; // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:44: +9:47
-- StorageLive(_31); // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:50: +9:55
-- _31 = _28; // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:50: +9:55
-- _29 = Add(move _30, move _31); // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:44: +9:55
-- StorageDead(_31); // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:54: +9:55
-- StorageDead(_30); // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:54: +9:55
-- Deinit(_3); // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:39: +9:56
-- ((_3 as Vmax).0: f32) = move _29; // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:39: +9:56
-- discriminant(_3) = 3; // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:39: +9:56
-- StorageDead(_29); // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:55: +9:56
-- StorageDead(_28); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:55: +9:56
-- StorageDead(_27); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:55: +9:56
-+ _31 = (((*_46) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:28: +9:33
-+ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:44: +9:55
-+ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:44: +9:47
-+ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:44: +9:47
-+ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:50: +9:55
-+ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:50: +9:55
-+ ((((_0 as Ok).0: ViewportPercentageLength) as Vmax).0: f32) = Add(move _30, move _31); // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:44: +9:55
-+ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:54: +9:55
-+ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:54: +9:55
-+ Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:39: +9:56
-+ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:39: +9:56
-+ discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 3; // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:39: +9:56
-+ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:55: +9:56
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:55: +9:56
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:55: +9:56
- goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:55: +9:56
- }
-
- bb10: {
- Deinit(_0); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:5: +11:7
-- ((_0 as Ok).0: ViewportPercentageLength) = move _3; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:5: +11:7
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:5: +11:7
- discriminant(_0) = 0; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:5: +11:7
-- StorageDead(_3); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+11:6: +11:7
-- StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+12:1: +12:2
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+11:6: +11:7
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+12:1: +12:2
- return; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+12:2: +12:2
- }
-
- bb11: {
- unreachable; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+12:2: +12:2
- }
- }
-
StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:23: +5:24
_34 = deref_copy (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
_11 = discriminant((*_34)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- switchInt(move _11) -> [0_isize: bb1, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb11]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +5:24
+ switchInt(move _11) -> [0_isize: bb1, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +5:24
}
bb1: {
bb2: {
StorageLive(_33); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:25: +10:27
- nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:25: +10:27
+ Deinit(_33); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:25: +10:27
Deinit(_0); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:21: +10:28
- nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:21: +10:28
+ ((_0 as Err).0: ()) = move _33; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:21: +10:28
discriminant(_0) = 1; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:21: +10:28
StorageDead(_33); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:27: +10:28
StorageDead(_3); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+11:6: +11:7
StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+12:1: +12:2
- return; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+12:2: +12:2
+ goto -> bb11; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+12:2: +12:2
}
bb3: {
discriminant(_0) = 0; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:5: +11:7
StorageDead(_3); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+11:6: +11:7
StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+12:1: +12:2
- return; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+12:2: +12:2
+ goto -> bb11; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+12:2: +12:2
}
bb11: {
- unreachable; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+12:2: +12:2
+ return; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+12:2: +12:2
}
}
bb0: {
StorageLive(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:23:13: 23:14
- Deinit(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:23:17: 23:23
- (_3.0: i32) = const 5_i32; // scope 0 at $DIR/generator-storage-dead-unwind.rs:23:17: 23:23
+ _3 = Foo(const 5_i32); // scope 0 at $DIR/generator-storage-dead-unwind.rs:23:17: 23:23
StorageLive(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:24:13: 24:14
- Deinit(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:24:17: 24:23
- (_4.0: i32) = const 6_i32; // scope 1 at $DIR/generator-storage-dead-unwind.rs:24:17: 24:23
+ _4 = Bar(const 6_i32); // scope 1 at $DIR/generator-storage-dead-unwind.rs:24:17: 24:23
StorageLive(_5); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14
StorageLive(_6); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14
- Deinit(_6); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14
- _5 = yield(move _6) -> [resume: bb1, drop: bb5]; // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14
+ _6 = (); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14
+ _5 = yield(move _6) -> [resume: bb1, drop: bb6]; // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14
}
bb1: {
StorageLive(_7); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:9: 26:16
StorageLive(_8); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:14: 26:15
_8 = move _3; // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:14: 26:15
- _7 = take::<Foo>(move _8) -> [return: bb2, unwind: bb9]; // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:9: 26:16
+ _7 = take::<Foo>(move _8) -> [return: bb2, unwind: bb10]; // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:9: 26:16
// mir::Constant
// + span: $DIR/generator-storage-dead-unwind.rs:26:9: 26:13
// + literal: Const { ty: fn(Foo) {take::<Foo>}, val: Value(<ZST>) }
StorageLive(_9); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:9: 27:16
StorageLive(_10); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:14: 27:15
_10 = move _4; // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:14: 27:15
- _9 = take::<Bar>(move _10) -> [return: bb3, unwind: bb8]; // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:9: 27:16
+ _9 = take::<Bar>(move _10) -> [return: bb3, unwind: bb9]; // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:9: 27:16
// mir::Constant
// + span: $DIR/generator-storage-dead-unwind.rs:27:9: 27:13
// + literal: Const { ty: fn(Bar) {take::<Bar>}, val: Value(<ZST>) }
StorageDead(_9); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:16: 27:17
_0 = const (); // scope 0 at $DIR/generator-storage-dead-unwind.rs:22:19: 28:6
StorageDead(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
- StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
- drop(_1) -> [return: bb4, unwind: bb11]; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
+ goto -> bb4; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
}
bb4: {
- return; // scope 0 at $DIR/generator-storage-dead-unwind.rs:+0:18: +0:18
+ StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
+ drop(_1) -> [return: bb5, unwind: bb14]; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
}
bb5: {
+ return; // scope 0 at $DIR/generator-storage-dead-unwind.rs:+0:18: +0:18
+ }
+
+ bb6: {
StorageDead(_6); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:13: 25:14
StorageDead(_5); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:14: 25:15
StorageDead(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
- drop(_3) -> [return: bb6, unwind: bb12]; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
+ drop(_3) -> [return: bb7, unwind: bb15]; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
}
- bb6: {
+ bb7: {
StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
- drop(_1) -> [return: bb7, unwind: bb11]; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
+ drop(_1) -> [return: bb8, unwind: bb14]; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
}
- bb7: {
+ bb8: {
generator_drop; // scope 0 at $DIR/generator-storage-dead-unwind.rs:+0:16: +0:18
}
- bb8 (cleanup): {
+ bb9 (cleanup): {
StorageDead(_10); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:15: 27:16
StorageDead(_9); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:16: 27:17
- goto -> bb10; // scope 2 at no-location
+ goto -> bb12; // scope 2 at no-location
}
- bb9 (cleanup): {
+ bb10 (cleanup): {
+ goto -> bb11; // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:15: 26:16
+ }
+
+ bb11 (cleanup): {
StorageDead(_8); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:15: 26:16
StorageDead(_7); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:16: 26:17
- goto -> bb10; // scope 2 at no-location
+ goto -> bb12; // scope 2 at no-location
}
- bb10 (cleanup): {
+ bb12 (cleanup): {
StorageDead(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
+ goto -> bb13; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
+ }
+
+ bb13 (cleanup): {
StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
- drop(_1) -> bb11; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
+ drop(_1) -> bb14; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
}
- bb11 (cleanup): {
+ bb14 (cleanup): {
resume; // scope 0 at $DIR/generator-storage-dead-unwind.rs:+0:16: +0:18
}
- bb12 (cleanup): {
+ bb15 (cleanup): {
StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
- drop(_1) -> bb11; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
+ drop(_1) -> bb14; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6
}
}
bb1: {
_10 = move _2; // scope 0 at $DIR/generator-tiny.rs:+0:16: +0:24
nop; // scope 0 at $DIR/generator-tiny.rs:20:13: 20:15
- Deinit((((*(_1.0: &mut [generator@$DIR/generator-tiny.rs:19:16: 19:24])) as variant#3).0: HasDrop)); // scope 0 at $DIR/generator-tiny.rs:20:18: 20:25
+ (((*(_1.0: &mut [generator@$DIR/generator-tiny.rs:19:16: 19:24])) as variant#3).0: HasDrop) = HasDrop; // scope 0 at $DIR/generator-tiny.rs:20:18: 20:25
StorageLive(_4); // scope 1 at $DIR/generator-tiny.rs:21:9: 24:10
goto -> bb2; // scope 1 at $DIR/generator-tiny.rs:21:9: 24:10
}
bb2: {
StorageLive(_6); // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18
StorageLive(_7); // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18
- Deinit(_7); // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18
+ _7 = (); // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18
Deinit(_0); // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18
((_0 as Yielded).0: ()) = move _7; // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18
discriminant(_0) = 0; // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18
// EMIT_MIR_FOR_EACH_BIT_WIDTH
// EMIT_MIR issue_73223.main.SimplifyArmIdentity.diff
-// EMIT_MIR issue_73223.main.PreCodegen.diff
+++ /dev/null
-- // MIR for `main` before PreCodegen
-+ // MIR for `main` after PreCodegen
-
- fn main() -> () {
- let mut _0: (); // return place in scope 0 at $DIR/issue-73223.rs:+0:11: +0:11
- let _1: i32; // in scope 0 at $DIR/issue-73223.rs:+1:9: +1:14
- let mut _2: std::option::Option<i32>; // in scope 0 at $DIR/issue-73223.rs:+1:23: +1:30
- let _3: i32; // in scope 0 at $DIR/issue-73223.rs:+2:14: +2:15
- let mut _5: (&i32, &i32); // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _6: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _7: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _10: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _11: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _12: i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let _14: !; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _15: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let _16: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _17: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let _18: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _19: std::option::Option<std::fmt::Arguments>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- scope 1 {
- debug split => _1; // in scope 1 at $DIR/issue-73223.rs:+1:9: +1:14
- let _4: std::option::Option<i32>; // in scope 1 at $DIR/issue-73223.rs:+6:9: +6:14
- scope 3 {
- debug _prev => _4; // in scope 3 at $DIR/issue-73223.rs:+6:9: +6:14
- let _8: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let _9: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _20: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- scope 4 {
- debug left_val => _8; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- debug right_val => _9; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let _13: core::panicking::AssertKind; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- scope 5 {
- debug kind => _13; // in scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- }
- }
- }
- }
- scope 2 {
- debug v => _3; // in scope 2 at $DIR/issue-73223.rs:+2:14: +2:15
- }
-
- bb0: {
- StorageLive(_1); // scope 0 at $DIR/issue-73223.rs:+1:9: +1:14
- StorageLive(_2); // scope 0 at $DIR/issue-73223.rs:+1:23: +1:30
- Deinit(_2); // scope 0 at $DIR/issue-73223.rs:+1:23: +1:30
- ((_2 as Some).0: i32) = const 1_i32; // scope 0 at $DIR/issue-73223.rs:+1:23: +1:30
- discriminant(_2) = 1; // scope 0 at $DIR/issue-73223.rs:+1:23: +1:30
- StorageLive(_3); // scope 0 at $DIR/issue-73223.rs:+2:14: +2:15
- _3 = ((_2 as Some).0: i32); // scope 0 at $DIR/issue-73223.rs:+2:14: +2:15
- _1 = _3; // scope 2 at $DIR/issue-73223.rs:+2:20: +2:21
- StorageDead(_3); // scope 0 at $DIR/issue-73223.rs:+2:20: +2:21
- StorageDead(_2); // scope 0 at $DIR/issue-73223.rs:+4:6: +4:7
- StorageLive(_4); // scope 1 at $DIR/issue-73223.rs:+6:9: +6:14
- StorageLive(_5); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_6); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _6 = &_1; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_7); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _20 = const main::promoted[0]; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- // mir::Constant
- // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
- // + literal: Const { ty: &i32, val: Unevaluated(main, [], Some(promoted[0])) }
- _7 = _20; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- Deinit(_5); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- (_5.0: &i32) = move _6; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- (_5.1: &i32) = move _7; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_7); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_6); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_8); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _8 = (_5.0: &i32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_9); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _9 = (_5.1: &i32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_10); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_11); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_12); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _12 = (*_8); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _11 = Eq(move _12, const 1_i32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_12); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _10 = Not(move _11); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_11); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- switchInt(move _10) -> [false: bb2, otherwise: bb1]; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- }
-
- bb1: {
- StorageLive(_13); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_14); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_15); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_16); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _16 = _8; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _15 = _16; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_17); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_18); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _18 = _9; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _17 = _18; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_19); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- Deinit(_19); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- discriminant(_19) = 0; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _14 = core::panicking::assert_failed::<i32, i32>(const core::panicking::AssertKind::Eq, move _15, move _17, move _19); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- // mir::Constant
- // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
- // + literal: Const { ty: for<'r, 's, 't0> fn(core::panicking::AssertKind, &'r i32, &'s i32, Option<Arguments<'t0>>) -> ! {core::panicking::assert_failed::<i32, i32>}, val: Value(<ZST>) }
- // mir::Constant
- // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
- // + literal: Const { ty: core::panicking::AssertKind, val: Value(Scalar(0x00)) }
- }
-
- bb2: {
- StorageDead(_10); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_9); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_8); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_5); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_4); // scope 1 at $DIR/issue-73223.rs:+8:1: +8:2
- StorageDead(_1); // scope 0 at $DIR/issue-73223.rs:+8:1: +8:2
- return; // scope 0 at $DIR/issue-73223.rs:+8:2: +8:2
- }
- }
-
+++ /dev/null
-- // MIR for `main` before PreCodegen
-+ // MIR for `main` after PreCodegen
-
- fn main() -> () {
- let mut _0: (); // return place in scope 0 at $DIR/issue-73223.rs:+0:11: +0:11
- let _1: i32; // in scope 0 at $DIR/issue-73223.rs:+1:9: +1:14
- let mut _2: std::option::Option<i32>; // in scope 0 at $DIR/issue-73223.rs:+1:23: +1:30
- let _3: i32; // in scope 0 at $DIR/issue-73223.rs:+2:14: +2:15
- let mut _5: (&i32, &i32); // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _6: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _7: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _10: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _11: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _12: i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let _14: !; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _15: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let _16: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _17: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let _18: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _19: std::option::Option<std::fmt::Arguments>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- scope 1 {
- debug split => _1; // in scope 1 at $DIR/issue-73223.rs:+1:9: +1:14
- let _4: std::option::Option<i32>; // in scope 1 at $DIR/issue-73223.rs:+6:9: +6:14
- scope 3 {
- debug _prev => _4; // in scope 3 at $DIR/issue-73223.rs:+6:9: +6:14
- let _8: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let _9: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _20: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- scope 4 {
- debug left_val => _8; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- debug right_val => _9; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let _13: core::panicking::AssertKind; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- scope 5 {
- debug kind => _13; // in scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- }
- }
- }
- }
- scope 2 {
- debug v => _3; // in scope 2 at $DIR/issue-73223.rs:+2:14: +2:15
- }
-
- bb0: {
- StorageLive(_1); // scope 0 at $DIR/issue-73223.rs:+1:9: +1:14
- StorageLive(_2); // scope 0 at $DIR/issue-73223.rs:+1:23: +1:30
- Deinit(_2); // scope 0 at $DIR/issue-73223.rs:+1:23: +1:30
- ((_2 as Some).0: i32) = const 1_i32; // scope 0 at $DIR/issue-73223.rs:+1:23: +1:30
- discriminant(_2) = 1; // scope 0 at $DIR/issue-73223.rs:+1:23: +1:30
- StorageLive(_3); // scope 0 at $DIR/issue-73223.rs:+2:14: +2:15
- _3 = ((_2 as Some).0: i32); // scope 0 at $DIR/issue-73223.rs:+2:14: +2:15
- _1 = _3; // scope 2 at $DIR/issue-73223.rs:+2:20: +2:21
- StorageDead(_3); // scope 0 at $DIR/issue-73223.rs:+2:20: +2:21
- StorageDead(_2); // scope 0 at $DIR/issue-73223.rs:+4:6: +4:7
- StorageLive(_4); // scope 1 at $DIR/issue-73223.rs:+6:9: +6:14
- StorageLive(_5); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_6); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _6 = &_1; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_7); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _20 = const main::promoted[0]; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- // mir::Constant
- // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
- // + literal: Const { ty: &i32, val: Unevaluated(main, [], Some(promoted[0])) }
- _7 = _20; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- Deinit(_5); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- (_5.0: &i32) = move _6; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- (_5.1: &i32) = move _7; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_7); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_6); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_8); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _8 = (_5.0: &i32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_9); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _9 = (_5.1: &i32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_10); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_11); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_12); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _12 = (*_8); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _11 = Eq(move _12, const 1_i32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_12); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _10 = Not(move _11); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_11); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- switchInt(move _10) -> [false: bb2, otherwise: bb1]; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- }
-
- bb1: {
- StorageLive(_13); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_14); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_15); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_16); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _16 = _8; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _15 = _16; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_17); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_18); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _18 = _9; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _17 = _18; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_19); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- Deinit(_19); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- discriminant(_19) = 0; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _14 = core::panicking::assert_failed::<i32, i32>(const core::panicking::AssertKind::Eq, move _15, move _17, move _19); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- // mir::Constant
- // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
- // + literal: Const { ty: for<'r, 's, 't0> fn(core::panicking::AssertKind, &'r i32, &'s i32, Option<Arguments<'t0>>) -> ! {core::panicking::assert_failed::<i32, i32>}, val: Value(<ZST>) }
- // mir::Constant
- // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
- // + literal: Const { ty: core::panicking::AssertKind, val: Value(Scalar(0x00)) }
- }
-
- bb2: {
- StorageDead(_10); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_9); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_8); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_5); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_4); // scope 1 at $DIR/issue-73223.rs:+8:1: +8:2
- StorageDead(_1); // scope 0 at $DIR/issue-73223.rs:+8:1: +8:2
- return; // scope 0 at $DIR/issue-73223.rs:+8:2: +8:2
- }
- }
-
+++ /dev/null
-- // MIR for `array_bound` before InstCombine
-+ // MIR for `array_bound` after InstCombine
-
- fn array_bound(_1: usize, _2: &[u8; N]) -> u8 {
- debug index => _1; // in scope 0 at $DIR/lower_array_len.rs:+0:36: +0:41
- debug slice => _2; // in scope 0 at $DIR/lower_array_len.rs:+0:50: +0:55
- let mut _0: u8; // return place in scope 0 at $DIR/lower_array_len.rs:+0:70: +0:72
- let mut _3: bool; // in scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
- let mut _4: usize; // in scope 0 at $DIR/lower_array_len.rs:+1:8: +1:13
- let mut _5: usize; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- let mut _6: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- let mut _7: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- let _8: usize; // in scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20
- let mut _9: usize; // in scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
- let mut _10: bool; // in scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
- let mut _11: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-
- bb0: {
- StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
- StorageLive(_4); // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:13
- _4 = _1; // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:13
- StorageLive(_5); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- StorageLive(_6); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- StorageLive(_7); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-- _7 = &(*_2); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-+ _7 = _2; // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- StorageLive(_11); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- _11 = _7; // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- _6 = move _7 as &[u8] (Pointer(Unsize)); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- StorageDead(_7); // scope 0 at $DIR/lower_array_len.rs:+1:20: +1:21
-- _5 = Len((*_11)); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-+ _5 = const N; // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- StorageDead(_11); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- StorageDead(_6); // scope 0 at $DIR/lower_array_len.rs:+1:26: +1:27
- _3 = Lt(move _4, move _5); // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
- StorageDead(_5); // scope 0 at $DIR/lower_array_len.rs:+1:26: +1:27
- StorageDead(_4); // scope 0 at $DIR/lower_array_len.rs:+1:26: +1:27
- switchInt(move _3) -> [false: bb3, otherwise: bb1]; // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
- }
-
- bb1: {
- StorageLive(_8); // scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20
- _8 = _1; // scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20
-- _9 = Len((*_2)); // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-+ _9 = const N; // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
- _10 = Lt(_8, _9); // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
- assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> bb2; // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
- }
-
- bb2: {
- _0 = (*_2)[_8]; // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
- StorageDead(_8); // scope 0 at $DIR/lower_array_len.rs:+3:5: +3:6
- goto -> bb4; // scope 0 at $DIR/lower_array_len.rs:+1:5: +5:6
- }
-
- bb3: {
- _0 = const 42_u8; // scope 0 at $DIR/lower_array_len.rs:+4:9: +4:11
- goto -> bb4; // scope 0 at $DIR/lower_array_len.rs:+1:5: +5:6
- }
-
- bb4: {
- StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:+5:5: +5:6
- return; // scope 0 at $DIR/lower_array_len.rs:+6:2: +6:2
- }
- }
-
+++ /dev/null
-- // MIR for `array_bound` before SimplifyLocals
-+ // MIR for `array_bound` after SimplifyLocals
-
- fn array_bound(_1: usize, _2: &[u8; N]) -> u8 {
- debug index => _1; // in scope 0 at $DIR/lower_array_len.rs:+0:36: +0:41
- debug slice => _2; // in scope 0 at $DIR/lower_array_len.rs:+0:50: +0:55
- let mut _0: u8; // return place in scope 0 at $DIR/lower_array_len.rs:+0:70: +0:72
- let mut _3: bool; // in scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
- let mut _4: usize; // in scope 0 at $DIR/lower_array_len.rs:+1:8: +1:13
- let mut _5: usize; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-- let mut _6: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-- let mut _7: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-- let _8: usize; // in scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20
-- let mut _9: usize; // in scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-- let mut _10: bool; // in scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-- let mut _11: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-+ let _6: usize; // in scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20
-+ let mut _7: usize; // in scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-+ let mut _8: bool; // in scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-
- bb0: {
- StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
- StorageLive(_4); // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:13
- _4 = _1; // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:13
- StorageLive(_5); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-- StorageLive(_6); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-- StorageLive(_7); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-- StorageLive(_11); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-- StorageDead(_7); // scope 0 at $DIR/lower_array_len.rs:+1:20: +1:21
- _5 = const N; // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-- StorageDead(_11); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-- StorageDead(_6); // scope 0 at $DIR/lower_array_len.rs:+1:26: +1:27
- _3 = Lt(move _4, move _5); // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
- StorageDead(_5); // scope 0 at $DIR/lower_array_len.rs:+1:26: +1:27
- StorageDead(_4); // scope 0 at $DIR/lower_array_len.rs:+1:26: +1:27
- switchInt(move _3) -> [false: bb3, otherwise: bb1]; // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
- }
-
- bb1: {
-- StorageLive(_8); // scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20
-- _8 = _1; // scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20
-- _9 = const N; // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-- _10 = Lt(_8, _9); // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-- assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> bb2; // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-+ StorageLive(_6); // scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20
-+ _6 = _1; // scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20
-+ _7 = const N; // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-+ _8 = Lt(_6, _7); // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> bb2; // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
- }
-
- bb2: {
-- _0 = (*_2)[_8]; // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-- StorageDead(_8); // scope 0 at $DIR/lower_array_len.rs:+3:5: +3:6
-+ _0 = (*_2)[_6]; // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-+ StorageDead(_6); // scope 0 at $DIR/lower_array_len.rs:+3:5: +3:6
- goto -> bb4; // scope 0 at $DIR/lower_array_len.rs:+1:5: +5:6
- }
-
- bb3: {
- _0 = const 42_u8; // scope 0 at $DIR/lower_array_len.rs:+4:9: +4:11
- goto -> bb4; // scope 0 at $DIR/lower_array_len.rs:+1:5: +5:6
- }
-
- bb4: {
- StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:+5:5: +5:6
- return; // scope 0 at $DIR/lower_array_len.rs:+6:2: +6:2
- }
- }
-
+++ /dev/null
-- // MIR for `array_bound_mut` before InstCombine
-+ // MIR for `array_bound_mut` after InstCombine
-
- fn array_bound_mut(_1: usize, _2: &mut [u8; N]) -> u8 {
- debug index => _1; // in scope 0 at $DIR/lower_array_len.rs:+0:40: +0:45
- debug slice => _2; // in scope 0 at $DIR/lower_array_len.rs:+0:54: +0:59
- let mut _0: u8; // return place in scope 0 at $DIR/lower_array_len.rs:+0:78: +0:80
- let mut _3: bool; // in scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
- let mut _4: usize; // in scope 0 at $DIR/lower_array_len.rs:+1:8: +1:13
- let mut _5: usize; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- let mut _6: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- let mut _7: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- let _8: usize; // in scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20
- let mut _9: usize; // in scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
- let mut _10: bool; // in scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
- let _11: usize; // in scope 0 at $DIR/lower_array_len.rs:+4:15: +4:16
- let mut _12: usize; // in scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17
- let mut _13: bool; // in scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17
- let mut _14: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-
- bb0: {
- StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
- StorageLive(_4); // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:13
- _4 = _1; // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:13
- StorageLive(_5); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- StorageLive(_6); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- StorageLive(_7); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- _7 = &(*_2); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- StorageLive(_14); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- _14 = _7; // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- _6 = move _7 as &[u8] (Pointer(Unsize)); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- StorageDead(_7); // scope 0 at $DIR/lower_array_len.rs:+1:20: +1:21
-- _5 = Len((*_14)); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-+ _5 = const N; // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- StorageDead(_14); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
- StorageDead(_6); // scope 0 at $DIR/lower_array_len.rs:+1:26: +1:27
- _3 = Lt(move _4, move _5); // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
- StorageDead(_5); // scope 0 at $DIR/lower_array_len.rs:+1:26: +1:27
- StorageDead(_4); // scope 0 at $DIR/lower_array_len.rs:+1:26: +1:27
- switchInt(move _3) -> [false: bb3, otherwise: bb1]; // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
- }
-
- bb1: {
- StorageLive(_8); // scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20
- _8 = _1; // scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20
-- _9 = Len((*_2)); // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-+ _9 = const N; // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
- _10 = Lt(_8, _9); // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
- assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> bb2; // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
- }
-
- bb2: {
- _0 = (*_2)[_8]; // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
- StorageDead(_8); // scope 0 at $DIR/lower_array_len.rs:+3:5: +3:6
- goto -> bb5; // scope 0 at $DIR/lower_array_len.rs:+1:5: +7:6
- }
-
- bb3: {
- StorageLive(_11); // scope 0 at $DIR/lower_array_len.rs:+4:15: +4:16
- _11 = const 0_usize; // scope 0 at $DIR/lower_array_len.rs:+4:15: +4:16
-- _12 = Len((*_2)); // scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17
-+ _12 = const N; // scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17
- _13 = Lt(_11, _12); // scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17
- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> bb4; // scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17
- }
-
- bb4: {
- (*_2)[_11] = const 42_u8; // scope 0 at $DIR/lower_array_len.rs:+4:9: +4:22
- StorageDead(_11); // scope 0 at $DIR/lower_array_len.rs:+4:22: +4:23
- _0 = const 42_u8; // scope 0 at $DIR/lower_array_len.rs:+6:9: +6:11
- goto -> bb5; // scope 0 at $DIR/lower_array_len.rs:+1:5: +7:6
- }
-
- bb5: {
- StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:+7:5: +7:6
- return; // scope 0 at $DIR/lower_array_len.rs:+8:2: +8:2
- }
- }
-
+++ /dev/null
-- // MIR for `array_bound_mut` before SimplifyLocals
-+ // MIR for `array_bound_mut` after SimplifyLocals
-
- fn array_bound_mut(_1: usize, _2: &mut [u8; N]) -> u8 {
- debug index => _1; // in scope 0 at $DIR/lower_array_len.rs:+0:40: +0:45
- debug slice => _2; // in scope 0 at $DIR/lower_array_len.rs:+0:54: +0:59
- let mut _0: u8; // return place in scope 0 at $DIR/lower_array_len.rs:+0:78: +0:80
- let mut _3: bool; // in scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
- let mut _4: usize; // in scope 0 at $DIR/lower_array_len.rs:+1:8: +1:13
- let mut _5: usize; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-- let mut _6: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-- let mut _7: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-- let _8: usize; // in scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20
-- let mut _9: usize; // in scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-- let mut _10: bool; // in scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-- let _11: usize; // in scope 0 at $DIR/lower_array_len.rs:+4:15: +4:16
-- let mut _12: usize; // in scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17
-- let mut _13: bool; // in scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17
-- let mut _14: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-+ let _6: usize; // in scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20
-+ let mut _7: usize; // in scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-+ let mut _8: bool; // in scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-+ let _9: usize; // in scope 0 at $DIR/lower_array_len.rs:+4:15: +4:16
-+ let mut _10: usize; // in scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17
-+ let mut _11: bool; // in scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17
-
- bb0: {
- StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
- StorageLive(_4); // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:13
- _4 = _1; // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:13
- StorageLive(_5); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-- StorageLive(_6); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-- StorageLive(_7); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-- StorageLive(_14); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-- StorageDead(_7); // scope 0 at $DIR/lower_array_len.rs:+1:20: +1:21
- _5 = const N; // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-- StorageDead(_14); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
-- StorageDead(_6); // scope 0 at $DIR/lower_array_len.rs:+1:26: +1:27
- _3 = Lt(move _4, move _5); // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
- StorageDead(_5); // scope 0 at $DIR/lower_array_len.rs:+1:26: +1:27
- StorageDead(_4); // scope 0 at $DIR/lower_array_len.rs:+1:26: +1:27
- switchInt(move _3) -> [false: bb3, otherwise: bb1]; // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
- }
-
- bb1: {
-- StorageLive(_8); // scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20
-- _8 = _1; // scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20
-- _9 = const N; // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-- _10 = Lt(_8, _9); // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-- assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> bb2; // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-+ StorageLive(_6); // scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20
-+ _6 = _1; // scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20
-+ _7 = const N; // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-+ _8 = Lt(_6, _7); // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> bb2; // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
- }
-
- bb2: {
-- _0 = (*_2)[_8]; // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-- StorageDead(_8); // scope 0 at $DIR/lower_array_len.rs:+3:5: +3:6
-+ _0 = (*_2)[_6]; // scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
-+ StorageDead(_6); // scope 0 at $DIR/lower_array_len.rs:+3:5: +3:6
- goto -> bb5; // scope 0 at $DIR/lower_array_len.rs:+1:5: +7:6
- }
-
- bb3: {
-- StorageLive(_11); // scope 0 at $DIR/lower_array_len.rs:+4:15: +4:16
-- _11 = const 0_usize; // scope 0 at $DIR/lower_array_len.rs:+4:15: +4:16
-- _12 = const N; // scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17
-- _13 = Lt(const 0_usize, _12); // scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17
-- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, const 0_usize) -> bb4; // scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17
-+ StorageLive(_9); // scope 0 at $DIR/lower_array_len.rs:+4:15: +4:16
-+ _9 = const 0_usize; // scope 0 at $DIR/lower_array_len.rs:+4:15: +4:16
-+ _10 = const N; // scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17
-+ _11 = Lt(const 0_usize, _10); // scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17
-+ assert(move _11, "index out of bounds: the length is {} but the index is {}", move _10, const 0_usize) -> bb4; // scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17
- }
-
- bb4: {
-- (*_2)[_11] = const 42_u8; // scope 0 at $DIR/lower_array_len.rs:+4:9: +4:22
-- StorageDead(_11); // scope 0 at $DIR/lower_array_len.rs:+4:22: +4:23
-+ (*_2)[_9] = const 42_u8; // scope 0 at $DIR/lower_array_len.rs:+4:9: +4:22
-+ StorageDead(_9); // scope 0 at $DIR/lower_array_len.rs:+4:22: +4:23
- _0 = const 42_u8; // scope 0 at $DIR/lower_array_len.rs:+6:9: +6:11
- goto -> bb5; // scope 0 at $DIR/lower_array_len.rs:+1:5: +7:6
- }
-
- bb5: {
- StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:+7:5: +7:6
- return; // scope 0 at $DIR/lower_array_len.rs:+8:2: +8:2
- }
- }
-
+++ /dev/null
-- // MIR for `array_len` before InstCombine
-+ // MIR for `array_len` after InstCombine
-
- fn array_len(_1: &[u8; N]) -> usize {
- debug arr => _1; // in scope 0 at $DIR/lower_array_len.rs:+0:34: +0:37
- let mut _0: usize; // return place in scope 0 at $DIR/lower_array_len.rs:+0:52: +0:57
- let mut _2: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
- let mut _3: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
- let mut _4: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-
- bb0: {
- StorageLive(_2); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
- StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-- _3 = &(*_1); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-+ _3 = _1; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
- StorageLive(_4); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
- _4 = _3; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
- _2 = move _3 as &[u8] (Pointer(Unsize)); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
- StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:+1:7: +1:8
-- _0 = Len((*_4)); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-+ _0 = const N; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
- StorageDead(_4); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
- StorageDead(_2); // scope 0 at $DIR/lower_array_len.rs:+1:13: +1:14
- return; // scope 0 at $DIR/lower_array_len.rs:+2:2: +2:2
- }
- }
-
+++ /dev/null
-- // MIR for `array_len` before SimplifyLocals
-+ // MIR for `array_len` after SimplifyLocals
-
- fn array_len(_1: &[u8; N]) -> usize {
- debug arr => _1; // in scope 0 at $DIR/lower_array_len.rs:+0:34: +0:37
- let mut _0: usize; // return place in scope 0 at $DIR/lower_array_len.rs:+0:52: +0:57
-- let mut _2: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-- let mut _3: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-- let mut _4: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-
- bb0: {
-- StorageLive(_2); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-- StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-- StorageLive(_4); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-- StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:+1:7: +1:8
- _0 = const N; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-- StorageDead(_4); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-- StorageDead(_2); // scope 0 at $DIR/lower_array_len.rs:+1:13: +1:14
- return; // scope 0 at $DIR/lower_array_len.rs:+2:2: +2:2
- }
- }
-
+++ /dev/null
-- // MIR for `array_len_by_value` before InstCombine
-+ // MIR for `array_len_by_value` after InstCombine
-
- fn array_len_by_value(_1: [u8; N]) -> usize {
- debug arr => _1; // in scope 0 at $DIR/lower_array_len.rs:+0:43: +0:46
- let mut _0: usize; // return place in scope 0 at $DIR/lower_array_len.rs:+0:60: +0:65
- let mut _2: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
- let mut _3: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
- let mut _4: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-
- bb0: {
- StorageLive(_2); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
- StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
- _3 = &_1; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
- StorageLive(_4); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
- _4 = _3; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
- _2 = move _3 as &[u8] (Pointer(Unsize)); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
- StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:+1:7: +1:8
-- _0 = Len((*_4)); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-+ _0 = const N; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
- StorageDead(_4); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
- StorageDead(_2); // scope 0 at $DIR/lower_array_len.rs:+1:13: +1:14
- return; // scope 0 at $DIR/lower_array_len.rs:+2:2: +2:2
- }
- }
-
+++ /dev/null
-- // MIR for `array_len_by_value` before SimplifyLocals
-+ // MIR for `array_len_by_value` after SimplifyLocals
-
- fn array_len_by_value(_1: [u8; N]) -> usize {
- debug arr => _1; // in scope 0 at $DIR/lower_array_len.rs:+0:43: +0:46
- let mut _0: usize; // return place in scope 0 at $DIR/lower_array_len.rs:+0:60: +0:65
-- let mut _2: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-- let mut _3: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-- let mut _4: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-
- bb0: {
-- StorageLive(_2); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-- StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-- StorageLive(_4); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-- StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:+1:7: +1:8
- _0 = const N; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-- StorageDead(_4); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
-- StorageDead(_2); // scope 0 at $DIR/lower_array_len.rs:+1:13: +1:14
- return; // scope 0 at $DIR/lower_array_len.rs:+2:2: +2:2
- }
- }
-
-// compile-flags: -Z mir-opt-level=4
+// unit-test: NormalizeArrayLen
+// compile-flags: -Zmir-enable-passes=+LowerSliceLenCalls
// EMIT_MIR lower_array_len.array_bound.NormalizeArrayLen.diff
-// EMIT_MIR lower_array_len.array_bound.SimplifyLocals.diff
-// EMIT_MIR lower_array_len.array_bound.InstCombine.diff
pub fn array_bound<const N: usize>(index: usize, slice: &[u8; N]) -> u8 {
if index < slice.len() {
slice[index]
}
// EMIT_MIR lower_array_len.array_bound_mut.NormalizeArrayLen.diff
-// EMIT_MIR lower_array_len.array_bound_mut.SimplifyLocals.diff
-// EMIT_MIR lower_array_len.array_bound_mut.InstCombine.diff
pub fn array_bound_mut<const N: usize>(index: usize, slice: &mut [u8; N]) -> u8 {
if index < slice.len() {
slice[index]
}
// EMIT_MIR lower_array_len.array_len.NormalizeArrayLen.diff
-// EMIT_MIR lower_array_len.array_len.SimplifyLocals.diff
-// EMIT_MIR lower_array_len.array_len.InstCombine.diff
pub fn array_len<const N: usize>(arr: &[u8; N]) -> usize {
arr.len()
}
// EMIT_MIR lower_array_len.array_len_by_value.NormalizeArrayLen.diff
-// EMIT_MIR lower_array_len.array_len_by_value.SimplifyLocals.diff
-// EMIT_MIR lower_array_len.array_len_by_value.InstCombine.diff
pub fn array_len_by_value<const N: usize>(arr: [u8; N]) -> usize {
arr.len()
}
--- /dev/null
+// MIR for `array_bound` after PreCodegen
+
+fn array_bound(_1: usize, _2: &[u8; N]) -> u8 {
+ debug index => _1; // in scope 0 at $DIR/lower_array_len_e2e.rs:+0:36: +0:41
+ debug slice => _2; // in scope 0 at $DIR/lower_array_len_e2e.rs:+0:50: +0:55
+ let mut _0: u8; // return place in scope 0 at $DIR/lower_array_len_e2e.rs:+0:70: +0:72
+ let mut _3: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
+ let mut _4: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:13
+ let mut _5: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27
+ let _6: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:15: +2:20
+ let mut _7: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ let mut _8: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+
+ bb0: {
+ StorageLive(_3); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
+ StorageLive(_4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:13
+ _4 = _1; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:13
+ StorageLive(_5); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27
+ _5 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27
+ _3 = Lt(move _4, move _5); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
+ StorageDead(_5); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:26: +1:27
+ StorageDead(_4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:26: +1:27
+ switchInt(move _3) -> [false: bb3, otherwise: bb1]; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
+ }
+
+ bb1: {
+ StorageLive(_6); // scope 0 at $DIR/lower_array_len_e2e.rs:+2:15: +2:20
+ _6 = _1; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:15: +2:20
+ _7 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ _8 = Lt(_6, _7); // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> bb2; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ }
+
+ bb2: {
+ _0 = (*_2)[_6]; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ StorageDead(_6); // scope 0 at $DIR/lower_array_len_e2e.rs:+3:5: +3:6
+ goto -> bb4; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:5: +5:6
+ }
+
+ bb3: {
+ _0 = const 42_u8; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:11
+ goto -> bb4; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:5: +5:6
+ }
+
+ bb4: {
+ StorageDead(_3); // scope 0 at $DIR/lower_array_len_e2e.rs:+5:5: +5:6
+ return; // scope 0 at $DIR/lower_array_len_e2e.rs:+6:2: +6:2
+ }
+}
--- /dev/null
+// MIR for `array_bound_mut` after PreCodegen
+
+fn array_bound_mut(_1: usize, _2: &mut [u8; N]) -> u8 {
+ debug index => _1; // in scope 0 at $DIR/lower_array_len_e2e.rs:+0:40: +0:45
+ debug slice => _2; // in scope 0 at $DIR/lower_array_len_e2e.rs:+0:54: +0:59
+ let mut _0: u8; // return place in scope 0 at $DIR/lower_array_len_e2e.rs:+0:78: +0:80
+ let mut _3: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
+ let mut _4: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:13
+ let mut _5: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27
+ let _6: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:15: +2:20
+ let mut _7: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ let mut _8: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ let _9: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+4:15: +4:16
+ let mut _10: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17
+ let mut _11: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17
+
+ bb0: {
+ StorageLive(_3); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
+ StorageLive(_4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:13
+ _4 = _1; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:13
+ StorageLive(_5); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27
+ _5 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27
+ _3 = Lt(move _4, move _5); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
+ StorageDead(_5); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:26: +1:27
+ StorageDead(_4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:26: +1:27
+ switchInt(move _3) -> [false: bb3, otherwise: bb1]; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
+ }
+
+ bb1: {
+ StorageLive(_6); // scope 0 at $DIR/lower_array_len_e2e.rs:+2:15: +2:20
+ _6 = _1; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:15: +2:20
+ _7 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ _8 = Lt(_6, _7); // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> bb2; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ }
+
+ bb2: {
+ _0 = (*_2)[_6]; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ StorageDead(_6); // scope 0 at $DIR/lower_array_len_e2e.rs:+3:5: +3:6
+ goto -> bb5; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:5: +7:6
+ }
+
+ bb3: {
+ StorageLive(_9); // scope 0 at $DIR/lower_array_len_e2e.rs:+4:15: +4:16
+ _9 = const 0_usize; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:15: +4:16
+ _10 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17
+ _11 = Lt(const 0_usize, _10); // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17
+ assert(move _11, "index out of bounds: the length is {} but the index is {}", move _10, const 0_usize) -> bb4; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17
+ }
+
+ bb4: {
+ (*_2)[_9] = const 42_u8; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:22
+ StorageDead(_9); // scope 0 at $DIR/lower_array_len_e2e.rs:+4:22: +4:23
+ _0 = const 42_u8; // scope 0 at $DIR/lower_array_len_e2e.rs:+6:9: +6:11
+ goto -> bb5; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:5: +7:6
+ }
+
+ bb5: {
+ StorageDead(_3); // scope 0 at $DIR/lower_array_len_e2e.rs:+7:5: +7:6
+ return; // scope 0 at $DIR/lower_array_len_e2e.rs:+8:2: +8:2
+ }
+}
--- /dev/null
+// MIR for `array_len` after PreCodegen
+
+fn array_len(_1: &[u8; N]) -> usize {
+ debug arr => _1; // in scope 0 at $DIR/lower_array_len_e2e.rs:+0:34: +0:37
+ let mut _0: usize; // return place in scope 0 at $DIR/lower_array_len_e2e.rs:+0:52: +0:57
+
+ bb0: {
+ _0 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:5: +1:14
+ return; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:2: +2:2
+ }
+}
--- /dev/null
+// MIR for `array_len_by_value` after PreCodegen
+
+fn array_len_by_value(_1: [u8; N]) -> usize {
+ debug arr => _1; // in scope 0 at $DIR/lower_array_len_e2e.rs:+0:43: +0:46
+ let mut _0: usize; // return place in scope 0 at $DIR/lower_array_len_e2e.rs:+0:60: +0:65
+
+ bb0: {
+ _0 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:5: +1:14
+ return; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:2: +2:2
+ }
+}
--- /dev/null
+// compile-flags: -Z mir-opt-level=4
+
+// EMIT_MIR lower_array_len_e2e.array_bound.PreCodegen.after.mir
+pub fn array_bound<const N: usize>(index: usize, slice: &[u8; N]) -> u8 {
+ if index < slice.len() {
+ slice[index]
+ } else {
+ 42
+ }
+}
+
+// EMIT_MIR lower_array_len_e2e.array_bound_mut.PreCodegen.after.mir
+pub fn array_bound_mut<const N: usize>(index: usize, slice: &mut [u8; N]) -> u8 {
+ if index < slice.len() {
+ slice[index]
+ } else {
+ slice[0] = 42;
+
+ 42
+ }
+}
+
+// EMIT_MIR lower_array_len_e2e.array_len.PreCodegen.after.mir
+pub fn array_len<const N: usize>(arr: &[u8; N]) -> usize {
+ arr.len()
+}
+
+// EMIT_MIR lower_array_len_e2e.array_len_by_value.PreCodegen.after.mir
+pub fn array_len_by_value<const N: usize>(arr: [u8; N]) -> usize {
+ arr.len()
+}
+
+fn main() {
+ let _ = array_bound(3, &[0, 1, 2, 3]);
+ let mut tmp = [0, 1, 2, 3, 4];
+ let _ = array_bound_mut(3, &mut [0, 1, 2, 3]);
+ let _ = array_len(&[0]);
+ let _ = array_len_by_value([0, 2]);
+}
bb0: {
- _0 = std::intrinsics::min_align_of::<T>() -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:42
- // mir::Constant
-- // + span: $DIR/lower_intrinsics.rs:19:5: 19:40
+- // + span: $DIR/lower_intrinsics.rs:21:5: 21:40
- // + literal: Const { ty: extern "rust-intrinsic" fn() -> usize {std::intrinsics::min_align_of::<T>}, val: Value(<ZST>) }
+ _0 = AlignOf(T); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:42
+ goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:42
_3 = &(*_4); // scope 0 at $DIR/lower_intrinsics.rs:+1:42: +1:44
- _2 = discriminant_value::<T>(move _3) -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:45
- // mir::Constant
-- // + span: $DIR/lower_intrinsics.rs:74:5: 74:41
+- // + span: $DIR/lower_intrinsics.rs:49:5: 49:41
- // + literal: Const { ty: for<'r> extern "rust-intrinsic" fn(&'r T) -> <T as DiscriminantKind>::Discriminant {discriminant_value::<T>}, val: Value(<ZST>) }
+ _2 = discriminant((*_3)); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:45
+ goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:45
StorageLive(_7); // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44
_19 = const discriminant::<T>::promoted[2]; // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44
// mir::Constant
- // + span: $DIR/lower_intrinsics.rs:75:42: 75:44
+ // + span: $DIR/lower_intrinsics.rs:50:42: 50:44
// + literal: Const { ty: &i32, val: Unevaluated(discriminant, [T], Some(promoted[2])) }
_7 = &(*_19); // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44
_6 = &(*_7); // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44
- _5 = discriminant_value::<i32>(move _6) -> bb2; // scope 0 at $DIR/lower_intrinsics.rs:+2:5: +2:45
- // mir::Constant
-- // + span: $DIR/lower_intrinsics.rs:75:5: 75:41
+- // + span: $DIR/lower_intrinsics.rs:50:5: 50:41
- // + literal: Const { ty: for<'r> extern "rust-intrinsic" fn(&'r i32) -> <i32 as DiscriminantKind>::Discriminant {discriminant_value::<i32>}, val: Value(<ZST>) }
+ _5 = discriminant((*_6)); // scope 0 at $DIR/lower_intrinsics.rs:+2:5: +2:45
+ goto -> bb2; // scope 0 at $DIR/lower_intrinsics.rs:+2:5: +2:45
StorageLive(_11); // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45
_18 = const discriminant::<T>::promoted[1]; // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45
// mir::Constant
- // + span: $DIR/lower_intrinsics.rs:76:42: 76:45
+ // + span: $DIR/lower_intrinsics.rs:51:42: 51:45
// + literal: Const { ty: &(), val: Unevaluated(discriminant, [T], Some(promoted[1])) }
_11 = &(*_18); // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45
_10 = &(*_11); // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45
- _9 = discriminant_value::<()>(move _10) -> bb3; // scope 0 at $DIR/lower_intrinsics.rs:+3:5: +3:46
- // mir::Constant
-- // + span: $DIR/lower_intrinsics.rs:76:5: 76:41
+- // + span: $DIR/lower_intrinsics.rs:51:5: 51:41
- // + literal: Const { ty: for<'r> extern "rust-intrinsic" fn(&'r ()) -> <() as DiscriminantKind>::Discriminant {discriminant_value::<()>}, val: Value(<ZST>) }
+ _9 = discriminant((*_10)); // scope 0 at $DIR/lower_intrinsics.rs:+3:5: +3:46
+ goto -> bb3; // scope 0 at $DIR/lower_intrinsics.rs:+3:5: +3:46
StorageLive(_15); // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47
_17 = const discriminant::<T>::promoted[0]; // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47
// mir::Constant
- // + span: $DIR/lower_intrinsics.rs:77:42: 77:47
+ // + span: $DIR/lower_intrinsics.rs:52:42: 52:47
// + literal: Const { ty: &E, val: Unevaluated(discriminant, [T], Some(promoted[0])) }
_15 = &(*_17); // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47
_14 = &(*_15); // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47
- _13 = discriminant_value::<E>(move _14) -> bb4; // scope 0 at $DIR/lower_intrinsics.rs:+4:5: +4:48
- // mir::Constant
-- // + span: $DIR/lower_intrinsics.rs:77:5: 77:41
+- // + span: $DIR/lower_intrinsics.rs:52:5: 52:41
- // + literal: Const { ty: for<'r> extern "rust-intrinsic" fn(&'r E) -> <E as DiscriminantKind>::Discriminant {discriminant_value::<E>}, val: Value(<ZST>) }
+ _13 = discriminant((*_14)); // scope 0 at $DIR/lower_intrinsics.rs:+4:5: +4:48
+ goto -> bb4; // scope 0 at $DIR/lower_intrinsics.rs:+4:5: +4:48
StorageDead(_15); // scope 0 at $DIR/lower_intrinsics.rs:+4:48: +4:49
StorageDead(_13); // scope 0 at $DIR/lower_intrinsics.rs:+4:48: +4:49
_0 = const (); // scope 0 at $DIR/lower_intrinsics.rs:+0:30: +5:2
- drop(_1) -> bb5; // scope 0 at $DIR/lower_intrinsics.rs:+5:1: +5:2
+ drop(_1) -> [return: bb5, unwind: bb6]; // scope 0 at $DIR/lower_intrinsics.rs:+5:1: +5:2
}
bb5: {
return; // scope 0 at $DIR/lower_intrinsics.rs:+5:2: +5:2
}
+
+ bb6 (cleanup): {
+ resume; // scope 0 at $DIR/lower_intrinsics.rs:+0:1: +5:2
+ }
}
+++ /dev/null
-// MIR for `f_u64` before PreCodegen
-
-fn f_u64() -> () {
- let mut _0: (); // return place in scope 0 at $DIR/lower_intrinsics.rs:+0:16: +0:16
- let mut _1: u64; // in scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:21
- scope 1 (inlined f_dispatch::<u64>) { // at $DIR/lower_intrinsics.rs:40:5: 40:21
- debug t => _1; // in scope 1 at $DIR/lower_intrinsics.rs:44:22: 44:23
- let _2: (); // in scope 1 at $DIR/lower_intrinsics.rs:48:9: 48:21
- let mut _3: u64; // in scope 1 at $DIR/lower_intrinsics.rs:48:19: 48:20
- scope 2 (inlined std::mem::size_of::<u64>) { // at $DIR/lower_intrinsics.rs:45:8: 45:32
- }
- }
-
- bb0: {
- StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:21
- _1 = const 0_u64; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:21
- StorageLive(_2); // scope 1 at $DIR/lower_intrinsics.rs:48:9: 48:21
- StorageLive(_3); // scope 1 at $DIR/lower_intrinsics.rs:48:19: 48:20
- _3 = move _1; // scope 1 at $DIR/lower_intrinsics.rs:48:19: 48:20
- _2 = f_non_zst::<u64>(move _3) -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:48:9: 48:21
- // mir::Constant
- // + span: $DIR/lower_intrinsics.rs:48:9: 48:18
- // + literal: Const { ty: fn(u64) {f_non_zst::<u64>}, val: Value(<ZST>) }
- }
-
- bb1: {
- StorageDead(_3); // scope 1 at $DIR/lower_intrinsics.rs:48:20: 48:21
- StorageDead(_2); // scope 1 at $DIR/lower_intrinsics.rs:48:21: 48:22
- StorageDead(_1); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:21
- return; // scope 0 at $DIR/lower_intrinsics.rs:+2:2: +2:2
- }
-}
+++ /dev/null
-// MIR for `f_unit` before PreCodegen
-
-fn f_unit() -> () {
- let mut _0: (); // return place in scope 0 at $DIR/lower_intrinsics.rs:+0:17: +0:17
- let mut _1: (); // in scope 0 at $DIR/lower_intrinsics.rs:+1:16: +1:18
- scope 1 (inlined f_dispatch::<()>) { // at $DIR/lower_intrinsics.rs:34:5: 34:19
- debug t => _1; // in scope 1 at $DIR/lower_intrinsics.rs:44:22: 44:23
- let _2: (); // in scope 1 at $DIR/lower_intrinsics.rs:46:9: 46:17
- let mut _3: (); // in scope 1 at $DIR/lower_intrinsics.rs:46:15: 46:16
- scope 2 (inlined std::mem::size_of::<()>) { // at $DIR/lower_intrinsics.rs:45:8: 45:32
- }
- }
-
- bb0: {
- StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+1:16: +1:18
- StorageLive(_2); // scope 1 at $DIR/lower_intrinsics.rs:46:9: 46:17
- StorageLive(_3); // scope 1 at $DIR/lower_intrinsics.rs:46:15: 46:16
- _2 = f_zst::<()>(move _3) -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:46:9: 46:17
- // mir::Constant
- // + span: $DIR/lower_intrinsics.rs:46:9: 46:14
- // + literal: Const { ty: fn(()) {f_zst::<()>}, val: Value(<ZST>) }
- }
-
- bb1: {
- StorageDead(_3); // scope 1 at $DIR/lower_intrinsics.rs:46:16: 46:17
- StorageDead(_2); // scope 1 at $DIR/lower_intrinsics.rs:46:17: 46:18
- StorageDead(_1); // scope 0 at $DIR/lower_intrinsics.rs:+1:18: +1:19
- return; // scope 0 at $DIR/lower_intrinsics.rs:+2:2: +2:2
- }
-}
_2 = move _1; // scope 0 at $DIR/lower_intrinsics.rs:+1:30: +1:31
- _0 = std::intrinsics::forget::<T>(move _2) -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:32
- // mir::Constant
-- // + span: $DIR/lower_intrinsics.rs:24:5: 24:29
+- // + span: $DIR/lower_intrinsics.rs:26:5: 26:29
- // + literal: Const { ty: extern "rust-intrinsic" fn(T) {std::intrinsics::forget::<T>}, val: Value(<ZST>) }
+ _0 = const (); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:32
+ goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:32
StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+2:9: +2:18
_1 = std::intrinsics::size_of::<T>; // scope 0 at $DIR/lower_intrinsics.rs:+2:21: +2:51
// mir::Constant
- // + span: $DIR/lower_intrinsics.rs:62:21: 62:51
+ // + span: $DIR/lower_intrinsics.rs:37:21: 37:51
// + literal: Const { ty: extern "rust-intrinsic" fn() -> usize {std::intrinsics::size_of::<T>}, val: Value(<ZST>) }
StorageLive(_2); // scope 1 at $DIR/lower_intrinsics.rs:+3:5: +3:14
_2 = _1; // scope 1 at $DIR/lower_intrinsics.rs:+3:5: +3:14
-// compile-flags: -Cpanic=abort
+// unit-test: LowerIntrinsics
+// ignore-wasm32 compiled with panic=abort by default
+
#![feature(core_intrinsics)]
#![crate_type = "lib"]
unsafe { core::intrinsics::unreachable() };
}
-// EMIT_MIR lower_intrinsics.f_unit.PreCodegen.before.mir
-pub fn f_unit() {
- f_dispatch(());
-}
-
-
-// EMIT_MIR lower_intrinsics.f_u64.PreCodegen.before.mir
-pub fn f_u64() {
- f_dispatch(0u64);
-}
-
-#[inline(always)]
-pub fn f_dispatch<T>(t: T) {
- if std::mem::size_of::<T>() == 0 {
- f_zst(t);
- } else {
- f_non_zst(t);
- }
-}
-
-#[inline(never)]
-pub fn f_zst<T>(_t: T) {
-}
-
-#[inline(never)]
-pub fn f_non_zst<T>(_t: T) {}
-
// EMIT_MIR lower_intrinsics.non_const.LowerIntrinsics.diff
pub fn non_const<T>() -> usize {
// Check that lowering works with non-const operand as a func.
bb0: {
- _0 = std::intrinsics::size_of::<T>() -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:37
- // mir::Constant
-- // + span: $DIR/lower_intrinsics.rs:14:5: 14:35
+- // + span: $DIR/lower_intrinsics.rs:16:5: 16:35
- // + literal: Const { ty: extern "rust-intrinsic" fn() -> usize {std::intrinsics::size_of::<T>}, val: Value(<ZST>) }
+ _0 = SizeOf(T); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:37
+ goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:37
StorageLive(_3); // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:45
- _3 = std::intrinsics::unreachable(); // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:45
- // mir::Constant
-- // + span: $DIR/lower_intrinsics.rs:29:14: 29:43
+- // + span: $DIR/lower_intrinsics.rs:31:14: 31:43
- // + literal: Const { ty: unsafe extern "rust-intrinsic" fn() -> ! {std::intrinsics::unreachable}, val: Value(<ZST>) }
+ unreachable; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:45
}
_5 = _2; // scope 0 at $DIR/lower_intrinsics.rs:+1:48: +1:49
- _3 = wrapping_add::<i32>(move _4, move _5) -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:50
- // mir::Constant
-- // + span: $DIR/lower_intrinsics.rs:7:14: 7:44
+- // + span: $DIR/lower_intrinsics.rs:9:14: 9:44
- // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> i32 {wrapping_add::<i32>}, val: Value(<ZST>) }
+ _3 = Add(move _4, move _5); // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:50
+ goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:50
_8 = _2; // scope 1 at $DIR/lower_intrinsics.rs:+2:48: +2:49
- _6 = wrapping_sub::<i32>(move _7, move _8) -> bb2; // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:50
- // mir::Constant
-- // + span: $DIR/lower_intrinsics.rs:8:14: 8:44
+- // + span: $DIR/lower_intrinsics.rs:10:14: 10:44
- // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> i32 {wrapping_sub::<i32>}, val: Value(<ZST>) }
+ _6 = Sub(move _7, move _8); // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:50
+ goto -> bb2; // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:50
_11 = _2; // scope 2 at $DIR/lower_intrinsics.rs:+3:48: +3:49
- _9 = wrapping_mul::<i32>(move _10, move _11) -> bb3; // scope 2 at $DIR/lower_intrinsics.rs:+3:14: +3:50
- // mir::Constant
-- // + span: $DIR/lower_intrinsics.rs:9:14: 9:44
+- // + span: $DIR/lower_intrinsics.rs:11:14: 11:44
- // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> i32 {wrapping_mul::<i32>}, val: Value(<ZST>) }
+ _9 = Mul(move _10, move _11); // scope 2 at $DIR/lower_intrinsics.rs:+3:14: +3:50
+ goto -> bb3; // scope 2 at $DIR/lower_intrinsics.rs:+3:14: +3:50
--- /dev/null
+// MIR for `f_u64` after PreCodegen
+
+fn f_u64() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/lower_intrinsics_e2e.rs:+0:16: +0:16
+ let mut _1: u64; // in scope 0 at $DIR/lower_intrinsics_e2e.rs:+1:5: +1:21
+ scope 1 (inlined f_dispatch::<u64>) { // at $DIR/lower_intrinsics_e2e.rs:15:5: 15:21
+ debug t => _1; // in scope 1 at $DIR/lower_intrinsics_e2e.rs:19:22: 19:23
+ let _2: (); // in scope 1 at $DIR/lower_intrinsics_e2e.rs:23:9: 23:21
+ let mut _3: u64; // in scope 1 at $DIR/lower_intrinsics_e2e.rs:23:19: 23:20
+ scope 2 (inlined std::mem::size_of::<u64>) { // at $DIR/lower_intrinsics_e2e.rs:20:8: 20:32
+ }
+ }
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/lower_intrinsics_e2e.rs:+1:5: +1:21
+ _1 = const 0_u64; // scope 0 at $DIR/lower_intrinsics_e2e.rs:+1:5: +1:21
+ StorageLive(_2); // scope 1 at $DIR/lower_intrinsics_e2e.rs:23:9: 23:21
+ StorageLive(_3); // scope 1 at $DIR/lower_intrinsics_e2e.rs:23:19: 23:20
+ _3 = move _1; // scope 1 at $DIR/lower_intrinsics_e2e.rs:23:19: 23:20
+ _2 = f_non_zst::<u64>(move _3) -> bb1; // scope 1 at $DIR/lower_intrinsics_e2e.rs:23:9: 23:21
+ // mir::Constant
+ // + span: $DIR/lower_intrinsics_e2e.rs:23:9: 23:18
+ // + literal: Const { ty: fn(u64) {f_non_zst::<u64>}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageDead(_3); // scope 1 at $DIR/lower_intrinsics_e2e.rs:23:20: 23:21
+ StorageDead(_2); // scope 1 at $DIR/lower_intrinsics_e2e.rs:23:21: 23:22
+ StorageDead(_1); // scope 0 at $DIR/lower_intrinsics_e2e.rs:+1:5: +1:21
+ return; // scope 0 at $DIR/lower_intrinsics_e2e.rs:+2:2: +2:2
+ }
+}
--- /dev/null
+// MIR for `f_unit` after PreCodegen
+
+fn f_unit() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/lower_intrinsics_e2e.rs:+0:17: +0:17
+ let mut _1: (); // in scope 0 at $DIR/lower_intrinsics_e2e.rs:+1:16: +1:18
+ scope 1 (inlined f_dispatch::<()>) { // at $DIR/lower_intrinsics_e2e.rs:9:5: 9:19
+ debug t => _1; // in scope 1 at $DIR/lower_intrinsics_e2e.rs:19:22: 19:23
+ let _2: (); // in scope 1 at $DIR/lower_intrinsics_e2e.rs:21:9: 21:17
+ let mut _3: (); // in scope 1 at $DIR/lower_intrinsics_e2e.rs:21:15: 21:16
+ scope 2 (inlined std::mem::size_of::<()>) { // at $DIR/lower_intrinsics_e2e.rs:20:8: 20:32
+ }
+ }
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/lower_intrinsics_e2e.rs:+1:16: +1:18
+ StorageLive(_2); // scope 1 at $DIR/lower_intrinsics_e2e.rs:21:9: 21:17
+ StorageLive(_3); // scope 1 at $DIR/lower_intrinsics_e2e.rs:21:15: 21:16
+ _2 = f_zst::<()>(move _3) -> bb1; // scope 1 at $DIR/lower_intrinsics_e2e.rs:21:9: 21:17
+ // mir::Constant
+ // + span: $DIR/lower_intrinsics_e2e.rs:21:9: 21:14
+ // + literal: Const { ty: fn(()) {f_zst::<()>}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageDead(_3); // scope 1 at $DIR/lower_intrinsics_e2e.rs:21:16: 21:17
+ StorageDead(_2); // scope 1 at $DIR/lower_intrinsics_e2e.rs:21:17: 21:18
+ StorageDead(_1); // scope 0 at $DIR/lower_intrinsics_e2e.rs:+1:18: +1:19
+ return; // scope 0 at $DIR/lower_intrinsics_e2e.rs:+2:2: +2:2
+ }
+}
--- /dev/null
+// Checks that we do not have any branches in the MIR for the two tested functions.
+
+// compile-flags: -Cpanic=abort
+#![feature(core_intrinsics)]
+#![crate_type = "lib"]
+
+// EMIT_MIR lower_intrinsics_e2e.f_unit.PreCodegen.after.mir
+pub fn f_unit() {
+ f_dispatch(());
+}
+
+
+// EMIT_MIR lower_intrinsics_e2e.f_u64.PreCodegen.after.mir
+pub fn f_u64() {
+ f_dispatch(0u64);
+}
+
+#[inline(always)]
+pub fn f_dispatch<T>(t: T) {
+ if std::mem::size_of::<T>() == 0 {
+ f_zst(t);
+ } else {
+ f_non_zst(t);
+ }
+}
+
+#[inline(never)]
+pub fn f_zst<T>(_t: T) {
+}
+
+#[inline(never)]
+pub fn f_non_zst<T>(_t: T) {}
- _3 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:+16:13: +16:22
- _4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:+17:13: +17:22
- _5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:+18:13: +18:21
-- nop; // scope 4 at $DIR/matches_reduce_branches.rs:+19:13: +19:15
+- Deinit(_6); // scope 4 at $DIR/matches_reduce_branches.rs:+19:13: +19:15
- goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:+19:13: +19:15
- }
-
+ _3 = Eq(_11, const 7_i32); // scope 4 at $DIR/matches_reduce_branches.rs:+9:13: +9:21
_4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:+10:13: +10:22
_5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:+11:13: +11:21
-- nop; // scope 4 at $DIR/matches_reduce_branches.rs:+12:13: +12:15
+ Deinit(_6); // scope 4 at $DIR/matches_reduce_branches.rs:+12:13: +12:15
- goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:+12:13: +12:15
- }
-
- _3 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:+16:13: +16:22
- _4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:+17:13: +17:22
- _5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:+18:13: +18:21
-- nop; // scope 4 at $DIR/matches_reduce_branches.rs:+19:13: +19:15
+- Deinit(_6); // scope 4 at $DIR/matches_reduce_branches.rs:+19:13: +19:15
- goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:+19:13: +19:15
- }
-
+ _3 = Eq(_11, const 7_i32); // scope 4 at $DIR/matches_reduce_branches.rs:+9:13: +9:21
_4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:+10:13: +10:22
_5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:+11:13: +11:21
-- nop; // scope 4 at $DIR/matches_reduce_branches.rs:+12:13: +12:15
+ Deinit(_6); // scope 4 at $DIR/matches_reduce_branches.rs:+12:13: +12:15
- goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:+12:13: +12:15
- }
-
fn foo(_1: Option<()>) -> () {
debug bar => _1; // in scope 0 at $DIR/matches_reduce_branches.rs:+0:8: +0:11
let mut _0: (); // return place in scope 0 at $DIR/matches_reduce_branches.rs:+0:25: +0:25
- let mut _2: isize; // in scope 0 at $DIR/matches_reduce_branches.rs:+1:22: +1:26
-+ let mut _3: isize; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ let mut _2: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ let mut _3: isize; // in scope 0 at $DIR/matches_reduce_branches.rs:+1:22: +1:26
++ let mut _4: isize; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
bb0: {
- _2 = discriminant(_1); // scope 0 at $DIR/matches_reduce_branches.rs:+1:17: +1:20
-- switchInt(move _2) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-- }
--
-- bb1: {
+ StorageLive(_2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ _3 = discriminant(_1); // scope 0 at $DIR/matches_reduce_branches.rs:+1:17: +1:20
+- switchInt(move _3) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
++ StorageLive(_4); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
++ _4 = move _3; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
++ _2 = Eq(_4, const 0_isize); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
++ StorageDead(_4); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
++ switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ }
+
+ bb1: {
+- _2 = const false; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- goto -> bb3; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- }
-
- bb2: {
+- _2 = const true; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- goto -> bb3; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- }
-
- bb3: {
-+ StorageLive(_3); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-+ _3 = move _2; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-+ StorageDead(_3); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+- switchInt(move _2) -> [false: bb5, otherwise: bb4]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+- }
+-
+- bb4: {
+ Deinit(_0); // scope 0 at $DIR/matches_reduce_branches.rs:+2:9: +2:11
+- goto -> bb6; // scope 0 at $DIR/matches_reduce_branches.rs:+1:5: +3:6
++ goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:+1:5: +3:6
+ }
+
+- bb5: {
++ bb2: {
+ _0 = const (); // scope 0 at $DIR/matches_reduce_branches.rs:+3:6: +3:6
+- goto -> bb6; // scope 0 at $DIR/matches_reduce_branches.rs:+1:5: +3:6
++ goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:+1:5: +3:6
+ }
+
+- bb6: {
++ bb3: {
+ StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:+3:5: +3:6
return; // scope 0 at $DIR/matches_reduce_branches.rs:+4:2: +4:2
}
}
fn foo(_1: Option<()>) -> () {
debug bar => _1; // in scope 0 at $DIR/matches_reduce_branches.rs:+0:8: +0:11
let mut _0: (); // return place in scope 0 at $DIR/matches_reduce_branches.rs:+0:25: +0:25
- let mut _2: isize; // in scope 0 at $DIR/matches_reduce_branches.rs:+1:22: +1:26
-+ let mut _3: isize; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ let mut _2: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ let mut _3: isize; // in scope 0 at $DIR/matches_reduce_branches.rs:+1:22: +1:26
++ let mut _4: isize; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
bb0: {
- _2 = discriminant(_1); // scope 0 at $DIR/matches_reduce_branches.rs:+1:17: +1:20
-- switchInt(move _2) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-- }
--
-- bb1: {
+ StorageLive(_2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ _3 = discriminant(_1); // scope 0 at $DIR/matches_reduce_branches.rs:+1:17: +1:20
+- switchInt(move _3) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
++ StorageLive(_4); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
++ _4 = move _3; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
++ _2 = Eq(_4, const 0_isize); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
++ StorageDead(_4); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
++ switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ }
+
+ bb1: {
+- _2 = const false; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- goto -> bb3; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- }
-
- bb2: {
+- _2 = const true; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- goto -> bb3; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- }
-
- bb3: {
-+ StorageLive(_3); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-+ _3 = move _2; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-+ StorageDead(_3); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+- switchInt(move _2) -> [false: bb5, otherwise: bb4]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+- }
+-
+- bb4: {
+ Deinit(_0); // scope 0 at $DIR/matches_reduce_branches.rs:+2:9: +2:11
+- goto -> bb6; // scope 0 at $DIR/matches_reduce_branches.rs:+1:5: +3:6
++ goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:+1:5: +3:6
+ }
+
+- bb5: {
++ bb2: {
+ _0 = const (); // scope 0 at $DIR/matches_reduce_branches.rs:+3:6: +3:6
+- goto -> bb6; // scope 0 at $DIR/matches_reduce_branches.rs:+1:5: +3:6
++ goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:+1:5: +3:6
+ }
+
+- bb6: {
++ bb3: {
+ StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:+3:5: +3:6
return; // scope 0 at $DIR/matches_reduce_branches.rs:+4:2: +4:2
}
}
+++ /dev/null
-// MIR for `foo` before PreCodegen
-
-fn foo(_1: Option<()>) -> () {
- debug bar => _1; // in scope 0 at $DIR/matches_reduce_branches.rs:+0:8: +0:11
- let mut _0: (); // return place in scope 0 at $DIR/matches_reduce_branches.rs:+0:25: +0:25
-
- bb0: {
- return; // scope 0 at $DIR/matches_reduce_branches.rs:+4:2: +4:2
- }
-}
+++ /dev/null
-// MIR for `foo` before PreCodegen
-
-fn foo(_1: Option<()>) -> () {
- debug bar => _1; // in scope 0 at $DIR/matches_reduce_branches.rs:+0:8: +0:11
- let mut _0: (); // return place in scope 0 at $DIR/matches_reduce_branches.rs:+0:25: +0:25
-
- bb0: {
- return; // scope 0 at $DIR/matches_reduce_branches.rs:+4:2: +4:2
- }
-}
fn match_nested_if() -> bool {
let mut _0: bool; // return place in scope 0 at $DIR/matches_reduce_branches.rs:+0:25: +0:29
let _1: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+1:9: +1:12
- let mut _2: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
-+ let mut _3: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
+ let mut _2: (); // in scope 0 at $DIR/matches_reduce_branches.rs:+1:21: +1:23
+ let mut _3: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
+ let mut _4: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
+ let mut _5: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
+ let mut _6: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
++ let mut _7: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
++ let mut _8: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
++ let mut _9: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
++ let mut _10: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
scope 1 {
debug val => _1; // in scope 1 at $DIR/matches_reduce_branches.rs:+1:9: +1:12
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/matches_reduce_branches.rs:+1:9: +1:12
- StorageLive(_2); // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
- _2 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
-- switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
+ StorageLive(_2); // scope 0 at $DIR/matches_reduce_branches.rs:+1:21: +1:23
+ Deinit(_2); // scope 0 at $DIR/matches_reduce_branches.rs:+1:21: +1:23
+ StorageLive(_3); // scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
+ StorageLive(_4); // scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
+ StorageLive(_5); // scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
+ StorageLive(_6); // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
+ _6 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
+- switchInt(move _6) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
- }
-
- bb1: {
-+ StorageLive(_3); // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
-+ _3 = move _2; // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
- StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:+2:51: +2:52
-- _1 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:+8:13: +8:17
-- goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:+8:13: +8:17
+- _5 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:+2:31: +2:35
+- goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
- }
-
- bb2: {
-- StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:+2:51: +2:52
-- _1 = const false; // scope 0 at $DIR/matches_reduce_branches.rs:+10:14: +10:19
-- goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:+10:14: +10:19
+- _5 = const false; // scope 0 at $DIR/matches_reduce_branches.rs:+2:45: +2:50
+- goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
- }
-
- bb3: {
-+ _1 = Ne(_3, const false); // scope 0 at $DIR/matches_reduce_branches.rs:+10:14: +10:19
-+ StorageDead(_3); // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
++ StorageLive(_7); // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
++ _7 = move _6; // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
++ _5 = Ne(_7, const false); // scope 0 at $DIR/matches_reduce_branches.rs:+2:45: +2:50
++ StorageDead(_7); // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
+ StorageDead(_6); // scope 0 at $DIR/matches_reduce_branches.rs:+2:51: +2:52
+- switchInt(move _5) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
+- }
+-
+- bb4: {
+- _4 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:+2:55: +2:59
+- goto -> bb6; // scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
+- }
+-
+- bb5: {
+- _4 = const false; // scope 0 at $DIR/matches_reduce_branches.rs:+2:69: +2:74
+- goto -> bb6; // scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
+- }
+-
+- bb6: {
++ StorageLive(_8); // scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
++ _8 = move _5; // scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
++ _4 = Ne(_8, const false); // scope 0 at $DIR/matches_reduce_branches.rs:+2:69: +2:74
++ StorageDead(_8); // scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
+ StorageDead(_5); // scope 0 at $DIR/matches_reduce_branches.rs:+2:75: +2:76
+- switchInt(move _4) -> [false: bb8, otherwise: bb7]; // scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
+- }
+-
+- bb7: {
+- _3 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:+3:13: +3:17
+- goto -> bb9; // scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
+- }
+-
+- bb8: {
+- _3 = const false; // scope 0 at $DIR/matches_reduce_branches.rs:+5:13: +5:18
+- goto -> bb9; // scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
+- }
+-
+- bb9: {
+- switchInt(move _3) -> [false: bb11, otherwise: bb10]; // scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
+- }
+-
+- bb10: {
++ StorageLive(_9); // scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
++ _9 = move _4; // scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
++ _3 = Ne(_9, const false); // scope 0 at $DIR/matches_reduce_branches.rs:+5:13: +5:18
++ StorageDead(_9); // scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
++ StorageLive(_10); // scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
++ _10 = move _3; // scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
+ StorageDead(_4); // scope 0 at $DIR/matches_reduce_branches.rs:+6:9: +6:10
+ StorageDead(_3); // scope 0 at $DIR/matches_reduce_branches.rs:+6:9: +6:10
+- _1 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:+8:13: +8:17
+- goto -> bb12; // scope 0 at $DIR/matches_reduce_branches.rs:+8:13: +8:17
+- }
+-
+- bb11: {
+- StorageDead(_4); // scope 0 at $DIR/matches_reduce_branches.rs:+6:9: +6:10
+- StorageDead(_3); // scope 0 at $DIR/matches_reduce_branches.rs:+6:9: +6:10
+- _1 = const false; // scope 0 at $DIR/matches_reduce_branches.rs:+10:14: +10:19
+- goto -> bb12; // scope 0 at $DIR/matches_reduce_branches.rs:+10:14: +10:19
+- }
+-
+- bb12: {
++ _1 = Ne(_10, const false); // scope 0 at $DIR/matches_reduce_branches.rs:+10:14: +10:19
++ StorageDead(_10); // scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
+ StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:+11:6: +11:7
_0 = _1; // scope 1 at $DIR/matches_reduce_branches.rs:+12:5: +12:8
StorageDead(_1); // scope 0 at $DIR/matches_reduce_branches.rs:+13:1: +13:2
return; // scope 0 at $DIR/matches_reduce_branches.rs:+13:2: +13:2
fn match_nested_if() -> bool {
let mut _0: bool; // return place in scope 0 at $DIR/matches_reduce_branches.rs:+0:25: +0:29
let _1: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+1:9: +1:12
- let mut _2: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
-+ let mut _3: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
+ let mut _2: (); // in scope 0 at $DIR/matches_reduce_branches.rs:+1:21: +1:23
+ let mut _3: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
+ let mut _4: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
+ let mut _5: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
+ let mut _6: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
++ let mut _7: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
++ let mut _8: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
++ let mut _9: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
++ let mut _10: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
scope 1 {
debug val => _1; // in scope 1 at $DIR/matches_reduce_branches.rs:+1:9: +1:12
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/matches_reduce_branches.rs:+1:9: +1:12
- StorageLive(_2); // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
- _2 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
-- switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
+ StorageLive(_2); // scope 0 at $DIR/matches_reduce_branches.rs:+1:21: +1:23
+ Deinit(_2); // scope 0 at $DIR/matches_reduce_branches.rs:+1:21: +1:23
+ StorageLive(_3); // scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
+ StorageLive(_4); // scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
+ StorageLive(_5); // scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
+ StorageLive(_6); // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
+ _6 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
+- switchInt(move _6) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
- }
-
- bb1: {
-+ StorageLive(_3); // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
-+ _3 = move _2; // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
- StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:+2:51: +2:52
-- _1 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:+8:13: +8:17
-- goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:+8:13: +8:17
+- _5 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:+2:31: +2:35
+- goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
- }
-
- bb2: {
-- StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:+2:51: +2:52
-- _1 = const false; // scope 0 at $DIR/matches_reduce_branches.rs:+10:14: +10:19
-- goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:+10:14: +10:19
+- _5 = const false; // scope 0 at $DIR/matches_reduce_branches.rs:+2:45: +2:50
+- goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
- }
-
- bb3: {
-+ _1 = Ne(_3, const false); // scope 0 at $DIR/matches_reduce_branches.rs:+10:14: +10:19
-+ StorageDead(_3); // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
++ StorageLive(_7); // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
++ _7 = move _6; // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
++ _5 = Ne(_7, const false); // scope 0 at $DIR/matches_reduce_branches.rs:+2:45: +2:50
++ StorageDead(_7); // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
+ StorageDead(_6); // scope 0 at $DIR/matches_reduce_branches.rs:+2:51: +2:52
+- switchInt(move _5) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
+- }
+-
+- bb4: {
+- _4 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:+2:55: +2:59
+- goto -> bb6; // scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
+- }
+-
+- bb5: {
+- _4 = const false; // scope 0 at $DIR/matches_reduce_branches.rs:+2:69: +2:74
+- goto -> bb6; // scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
+- }
+-
+- bb6: {
++ StorageLive(_8); // scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
++ _8 = move _5; // scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
++ _4 = Ne(_8, const false); // scope 0 at $DIR/matches_reduce_branches.rs:+2:69: +2:74
++ StorageDead(_8); // scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
+ StorageDead(_5); // scope 0 at $DIR/matches_reduce_branches.rs:+2:75: +2:76
+- switchInt(move _4) -> [false: bb8, otherwise: bb7]; // scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
+- }
+-
+- bb7: {
+- _3 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:+3:13: +3:17
+- goto -> bb9; // scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
+- }
+-
+- bb8: {
+- _3 = const false; // scope 0 at $DIR/matches_reduce_branches.rs:+5:13: +5:18
+- goto -> bb9; // scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
+- }
+-
+- bb9: {
+- switchInt(move _3) -> [false: bb11, otherwise: bb10]; // scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
+- }
+-
+- bb10: {
++ StorageLive(_9); // scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
++ _9 = move _4; // scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
++ _3 = Ne(_9, const false); // scope 0 at $DIR/matches_reduce_branches.rs:+5:13: +5:18
++ StorageDead(_9); // scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
++ StorageLive(_10); // scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
++ _10 = move _3; // scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
+ StorageDead(_4); // scope 0 at $DIR/matches_reduce_branches.rs:+6:9: +6:10
+ StorageDead(_3); // scope 0 at $DIR/matches_reduce_branches.rs:+6:9: +6:10
+- _1 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:+8:13: +8:17
+- goto -> bb12; // scope 0 at $DIR/matches_reduce_branches.rs:+8:13: +8:17
+- }
+-
+- bb11: {
+- StorageDead(_4); // scope 0 at $DIR/matches_reduce_branches.rs:+6:9: +6:10
+- StorageDead(_3); // scope 0 at $DIR/matches_reduce_branches.rs:+6:9: +6:10
+- _1 = const false; // scope 0 at $DIR/matches_reduce_branches.rs:+10:14: +10:19
+- goto -> bb12; // scope 0 at $DIR/matches_reduce_branches.rs:+10:14: +10:19
+- }
+-
+- bb12: {
++ _1 = Ne(_10, const false); // scope 0 at $DIR/matches_reduce_branches.rs:+10:14: +10:19
++ StorageDead(_10); // scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
+ StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:+11:6: +11:7
_0 = _1; // scope 1 at $DIR/matches_reduce_branches.rs:+12:5: +12:8
StorageDead(_1); // scope 0 at $DIR/matches_reduce_branches.rs:+13:1: +13:2
return; // scope 0 at $DIR/matches_reduce_branches.rs:+13:2: +13:2
+// unit-test: MatchBranchSimplification
+
// EMIT_MIR_FOR_EACH_BIT_WIDTH
// EMIT_MIR matches_reduce_branches.foo.MatchBranchSimplification.diff
-// EMIT_MIR matches_reduce_branches.foo.PreCodegen.before.mir
// EMIT_MIR matches_reduce_branches.bar.MatchBranchSimplification.diff
// EMIT_MIR matches_reduce_branches.match_nested_if.MatchBranchSimplification.diff
+// unit-test: MatchBranchSimplification
+
// EMIT_MIR_FOR_EACH_BIT_WIDTH
// EMIT_MIR matches_u8.exhaustive_match.MatchBranchSimplification.diff
// EMIT_MIR matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff
StorageLive(_1); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:9: +1:14
StorageLive(_2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:24: +1:42
StorageLive(_3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:32: +1:41
- _3 = Droppy(const 0_usize); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:32: +1:41
- _2 = Aligned(move _3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:24: +1:42
+ Deinit(_3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:32: +1:41
+ (_3.0: usize) = const 0_usize; // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:32: +1:41
+ Deinit(_2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:24: +1:42
+ (_2.0: Droppy) = move _3; // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:24: +1:42
StorageDead(_3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:41: +1:42
- _1 = Packed(move _2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:17: +1:43
+ Deinit(_1); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:17: +1:43
+ (_1.0: Aligned) = move _2; // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:17: +1:43
StorageDead(_2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:42: +1:43
StorageLive(_4); // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:11: +2:29
StorageLive(_5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:19: +2:28
- _5 = Droppy(const 0_usize); // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:19: +2:28
- _4 = Aligned(move _5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:11: +2:29
+ Deinit(_5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:19: +2:28
+ (_5.0: usize) = const 0_usize; // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:19: +2:28
+ Deinit(_4); // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:11: +2:29
+ (_4.0: Droppy) = move _5; // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:11: +2:29
StorageDead(_5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:28: +2:29
StorageLive(_6); // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:5: +2:8
_6 = move (_1.0: Aligned); // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:5: +2:8
StorageLive(_1); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:9: +1:14
StorageLive(_2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:24: +1:42
StorageLive(_3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:32: +1:41
- _3 = Droppy(const 0_usize); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:32: +1:41
- _2 = Aligned(move _3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:24: +1:42
+ Deinit(_3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:32: +1:41
+ (_3.0: usize) = const 0_usize; // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:32: +1:41
+ Deinit(_2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:24: +1:42
+ (_2.0: Droppy) = move _3; // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:24: +1:42
StorageDead(_3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:41: +1:42
- _1 = Packed(move _2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:17: +1:43
+ Deinit(_1); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:17: +1:43
+ (_1.0: Aligned) = move _2; // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:17: +1:43
StorageDead(_2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:+1:42: +1:43
StorageLive(_4); // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:11: +2:29
StorageLive(_5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:19: +2:28
- _5 = Droppy(const 0_usize); // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:19: +2:28
- _4 = Aligned(move _5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:11: +2:29
+ Deinit(_5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:19: +2:28
+ (_5.0: usize) = const 0_usize; // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:19: +2:28
+ Deinit(_4); // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:11: +2:29
+ (_4.0: Droppy) = move _5; // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:11: +2:29
StorageDead(_5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:28: +2:29
StorageLive(_6); // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:5: +2:8
_6 = move (_1.0: Aligned); // scope 1 at $DIR/packed-struct-drop-aligned.rs:+2:5: +2:8
Retag(_35); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_18 = &(*_35); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
Retag(_18); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _13 = (move _14, move _18); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ Deinit(_13); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ (_13.0: &usize) = move _14; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ (_13.1: &usize) = move _18; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
Retag(_13); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageDead(_18); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageDead(_14); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
bb3: {
StorageLive(_27); // scope 7 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _27 = core::panicking::AssertKind::Eq; // scope 7 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ Deinit(_27); // scope 7 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ discriminant(_27) = 0; // scope 7 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageLive(_28); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageLive(_29); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_29 = move _27; // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_32 = &(*_33); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
Retag(_32); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageLive(_34); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _34 = Option::<Arguments>::None; // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ Deinit(_34); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ discriminant(_34) = 0; // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
Retag(_34); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_28 = core::panicking::assert_failed::<usize, usize>(move _29, move _30, move _32, move _34); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
// mir::Constant
StorageLive(_3); // scope 1 at $DIR/retag.rs:+3:13: +3:14
StorageLive(_4); // scope 1 at $DIR/retag.rs:+3:17: +3:36
StorageLive(_5); // scope 1 at $DIR/retag.rs:+3:17: +3:24
- _5 = Test(const 0_i32); // scope 1 at $DIR/retag.rs:+3:17: +3:24
+ Deinit(_5); // scope 1 at $DIR/retag.rs:+3:17: +3:24
+ (_5.0: i32) = const 0_i32; // scope 1 at $DIR/retag.rs:+3:17: +3:24
_4 = &_5; // scope 1 at $DIR/retag.rs:+3:17: +3:36
Retag(_4); // scope 1 at $DIR/retag.rs:+3:17: +3:36
StorageLive(_6); // scope 1 at $DIR/retag.rs:+3:29: +3:35
StorageDead(_2); // scope 1 at $DIR/retag.rs:+8:5: +8:6
StorageLive(_13); // scope 1 at $DIR/retag.rs:+11:9: +11:10
StorageLive(_14); // scope 1 at $DIR/retag.rs:+11:31: +14:6
- _14 = [closure@main::{closure#0}]; // scope 1 at $DIR/retag.rs:+11:31: +14:6
- // closure
- // + def_id: DefId(0:14 ~ retag[4622]::main::{closure#0})
- // + substs: [
- // i8,
- // for<'r> extern "rust-call" fn((&'r i32,)) -> &'r i32,
- // (),
- // ]
+ Deinit(_14); // scope 1 at $DIR/retag.rs:+11:31: +14:6
Retag(_14); // scope 1 at $DIR/retag.rs:+11:31: +14:6
_13 = move _14 as for<'r> fn(&'r i32) -> &'r i32 (Pointer(ClosureFnPointer(Normal))); // scope 1 at $DIR/retag.rs:+11:31: +14:6
StorageDead(_14); // scope 1 at $DIR/retag.rs:+11:47: +11:48
StorageLive(_19); // scope 7 at $DIR/retag.rs:+18:5: +18:24
StorageLive(_20); // scope 7 at $DIR/retag.rs:+18:5: +18:24
StorageLive(_21); // scope 7 at $DIR/retag.rs:+18:5: +18:12
- _21 = Test(const 0_i32); // scope 7 at $DIR/retag.rs:+18:5: +18:12
+ Deinit(_21); // scope 7 at $DIR/retag.rs:+18:5: +18:12
+ (_21.0: i32) = const 0_i32; // scope 7 at $DIR/retag.rs:+18:5: +18:12
_20 = &_21; // scope 7 at $DIR/retag.rs:+18:5: +18:24
Retag(_20); // scope 7 at $DIR/retag.rs:+18:5: +18:24
StorageLive(_22); // scope 7 at $DIR/retag.rs:+18:21: +18:23
scope 1 {
debug residual => _6; // in scope 1 at $DIR/separate_const_switch.rs:+1:9: +1:10
scope 2 {
- scope 8 (inlined #[track_caller] <Result<i32, i32> as FromResidual<Result<Infallible, i32>>>::from_residual) { // at $DIR/separate_const_switch.rs:29:8: 29:10
+ scope 8 (inlined #[track_caller] <Result<i32, i32> as FromResidual<Result<Infallible, i32>>>::from_residual) { // at $DIR/separate_const_switch.rs:25:8: 25:10
debug residual => _8; // in scope 8 at $SRC_DIR/core/src/result.rs:LL:COL
let _16: i32; // in scope 8 at $SRC_DIR/core/src/result.rs:LL:COL
let mut _17: i32; // in scope 8 at $SRC_DIR/core/src/result.rs:LL:COL
scope 4 {
}
}
- scope 5 (inlined <Result<i32, i32> as Try>::branch) { // at $DIR/separate_const_switch.rs:29:8: 29:10
+ scope 5 (inlined <Result<i32, i32> as Try>::branch) { // at $DIR/separate_const_switch.rs:25:8: 25:10
debug self => _4; // in scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
let mut _10: isize; // in scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
let _11: i32; // in scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
use std::ops::ControlFlow;
// EMIT_MIR separate_const_switch.too_complex.SeparateConstSwitch.diff
-// EMIT_MIR separate_const_switch.too_complex.ConstProp.diff
-// EMIT_MIR separate_const_switch.too_complex.PreCodegen.after.mir
fn too_complex(x: Result<i32, usize>) -> Option<i32> {
// The pass should break the outer match into
// two blocks that only have one parent each.
}
// EMIT_MIR separate_const_switch.identity.SeparateConstSwitch.diff
-// EMIT_MIR separate_const_switch.identity.ConstProp.diff
-// EMIT_MIR separate_const_switch.identity.PreCodegen.after.mir
fn identity(x: Result<i32, i32>) -> Result<i32, i32> {
Ok(x?)
}
// compile-flags: -Zmir-opt-level=3
// EMIT_MIR_FOR_EACH_BIT_WIDTH
+// This pass is broken since deaggregation changed
+// ignore-test
+
enum Src {
Foo(u8),
Bar,
// EMIT_MIR simplify_arm.id_try.SimplifyArmIdentity.diff
// EMIT_MIR simplify_arm.id_try.SimplifyBranchSame.diff
+// This pass is broken since deaggregation changed
+// ignore-test
+
fn id(o: Option<u8>) -> Option<u8> {
match o {
Some(v) => Some(v),
-// compile-flags: -Zunsound-mir-opts
+// unit-test: SimplifyLocals
fn map(x: Option<Box<()>>) -> Option<Box<()>> {
match x {
+++ /dev/null
-- // MIR for `id` before SimplifyArmIdentity
-+ // MIR for `id` after SimplifyArmIdentity
-
- fn id(_1: Option<u8>) -> Option<u8> {
- debug o => _1; // in scope 0 at $DIR/simplify-arm.rs:+0:7: +0:8
- let mut _0: std::option::Option<u8>; // return place in scope 0 at $DIR/simplify-arm.rs:+0:25: +0:35
- let mut _2: isize; // in scope 0 at $DIR/simplify-arm.rs:+2:9: +2:16
- let _3: u8; // in scope 0 at $DIR/simplify-arm.rs:+2:14: +2:15
- let mut _4: u8; // in scope 0 at $DIR/simplify-arm.rs:+2:25: +2:26
- scope 1 {
- debug v => _3; // in scope 1 at $DIR/simplify-arm.rs:+2:14: +2:15
- }
-
- bb0: {
- _2 = discriminant(_1); // scope 0 at $DIR/simplify-arm.rs:+1:11: +1:12
- switchInt(move _2) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:+1:5: +1:12
- }
-
- bb1: {
- Deinit(_0); // scope 0 at $DIR/simplify-arm.rs:+3:17: +3:21
- discriminant(_0) = 0; // scope 0 at $DIR/simplify-arm.rs:+3:17: +3:21
- goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:+3:17: +3:21
- }
-
- bb2: {
- unreachable; // scope 0 at $DIR/simplify-arm.rs:+1:11: +1:12
- }
-
- bb3: {
- StorageLive(_3); // scope 0 at $DIR/simplify-arm.rs:+2:14: +2:15
- _3 = ((_1 as Some).0: u8); // scope 0 at $DIR/simplify-arm.rs:+2:14: +2:15
- StorageLive(_4); // scope 1 at $DIR/simplify-arm.rs:+2:25: +2:26
- _4 = _3; // scope 1 at $DIR/simplify-arm.rs:+2:25: +2:26
- Deinit(_0); // scope 1 at $DIR/simplify-arm.rs:+2:20: +2:27
- ((_0 as Some).0: u8) = move _4; // scope 1 at $DIR/simplify-arm.rs:+2:20: +2:27
- discriminant(_0) = 1; // scope 1 at $DIR/simplify-arm.rs:+2:20: +2:27
- StorageDead(_4); // scope 1 at $DIR/simplify-arm.rs:+2:26: +2:27
- StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:+2:26: +2:27
- goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:+2:26: +2:27
- }
-
- bb4: {
- return; // scope 0 at $DIR/simplify-arm.rs:+5:2: +5:2
- }
- }
-
+++ /dev/null
-- // MIR for `id` before SimplifyBranchSame
-+ // MIR for `id` after SimplifyBranchSame
-
- fn id(_1: Option<u8>) -> Option<u8> {
- debug o => _1; // in scope 0 at $DIR/simplify-arm.rs:+0:7: +0:8
- let mut _0: std::option::Option<u8>; // return place in scope 0 at $DIR/simplify-arm.rs:+0:25: +0:35
- let mut _2: isize; // in scope 0 at $DIR/simplify-arm.rs:+2:9: +2:16
- let _3: u8; // in scope 0 at $DIR/simplify-arm.rs:+2:14: +2:15
- let mut _4: u8; // in scope 0 at $DIR/simplify-arm.rs:+2:25: +2:26
- scope 1 {
- debug v => _3; // in scope 1 at $DIR/simplify-arm.rs:+2:14: +2:15
- }
-
- bb0: {
- _2 = discriminant(_1); // scope 0 at $DIR/simplify-arm.rs:+1:11: +1:12
- switchInt(move _2) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:+1:5: +1:12
- }
-
- bb1: {
- Deinit(_0); // scope 0 at $DIR/simplify-arm.rs:+3:17: +3:21
- discriminant(_0) = 0; // scope 0 at $DIR/simplify-arm.rs:+3:17: +3:21
- goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:+3:17: +3:21
- }
-
- bb2: {
- unreachable; // scope 0 at $DIR/simplify-arm.rs:+1:11: +1:12
- }
-
- bb3: {
- StorageLive(_3); // scope 0 at $DIR/simplify-arm.rs:+2:14: +2:15
- _3 = ((_1 as Some).0: u8); // scope 0 at $DIR/simplify-arm.rs:+2:14: +2:15
- StorageLive(_4); // scope 1 at $DIR/simplify-arm.rs:+2:25: +2:26
- _4 = _3; // scope 1 at $DIR/simplify-arm.rs:+2:25: +2:26
- Deinit(_0); // scope 1 at $DIR/simplify-arm.rs:+2:20: +2:27
- ((_0 as Some).0: u8) = move _4; // scope 1 at $DIR/simplify-arm.rs:+2:20: +2:27
- discriminant(_0) = 1; // scope 1 at $DIR/simplify-arm.rs:+2:20: +2:27
- StorageDead(_4); // scope 1 at $DIR/simplify-arm.rs:+2:26: +2:27
- StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:+2:26: +2:27
- goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:+2:26: +2:27
- }
-
- bb4: {
- return; // scope 0 at $DIR/simplify-arm.rs:+5:2: +5:2
- }
- }
-
+++ /dev/null
-- // MIR for `id_result` before SimplifyArmIdentity
-+ // MIR for `id_result` after SimplifyArmIdentity
-
- fn id_result(_1: Result<u8, i32>) -> Result<u8, i32> {
- debug r => _1; // in scope 0 at $DIR/simplify-arm.rs:+0:14: +0:15
- let mut _0: std::result::Result<u8, i32>; // return place in scope 0 at $DIR/simplify-arm.rs:+0:37: +0:52
- let mut _2: isize; // in scope 0 at $DIR/simplify-arm.rs:+2:9: +2:14
- let _3: u8; // in scope 0 at $DIR/simplify-arm.rs:+2:12: +2:13
- let mut _4: u8; // in scope 0 at $DIR/simplify-arm.rs:+2:21: +2:22
- let _5: i32; // in scope 0 at $DIR/simplify-arm.rs:+3:13: +3:14
- let mut _6: i32; // in scope 0 at $DIR/simplify-arm.rs:+3:23: +3:24
- scope 1 {
- debug x => _3; // in scope 1 at $DIR/simplify-arm.rs:+2:12: +2:13
- }
- scope 2 {
- debug y => _5; // in scope 2 at $DIR/simplify-arm.rs:+3:13: +3:14
- }
-
- bb0: {
- _2 = discriminant(_1); // scope 0 at $DIR/simplify-arm.rs:+1:11: +1:12
- switchInt(move _2) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:+1:5: +1:12
- }
-
- bb1: {
- StorageLive(_5); // scope 0 at $DIR/simplify-arm.rs:+3:13: +3:14
- _5 = ((_1 as Err).0: i32); // scope 0 at $DIR/simplify-arm.rs:+3:13: +3:14
- StorageLive(_6); // scope 2 at $DIR/simplify-arm.rs:+3:23: +3:24
- _6 = _5; // scope 2 at $DIR/simplify-arm.rs:+3:23: +3:24
- Deinit(_0); // scope 2 at $DIR/simplify-arm.rs:+3:19: +3:25
- ((_0 as Err).0: i32) = move _6; // scope 2 at $DIR/simplify-arm.rs:+3:19: +3:25
- discriminant(_0) = 1; // scope 2 at $DIR/simplify-arm.rs:+3:19: +3:25
- StorageDead(_6); // scope 2 at $DIR/simplify-arm.rs:+3:24: +3:25
- StorageDead(_5); // scope 0 at $DIR/simplify-arm.rs:+3:24: +3:25
- goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:+3:24: +3:25
- }
-
- bb2: {
- unreachable; // scope 0 at $DIR/simplify-arm.rs:+1:11: +1:12
- }
-
- bb3: {
- StorageLive(_3); // scope 0 at $DIR/simplify-arm.rs:+2:12: +2:13
- _3 = ((_1 as Ok).0: u8); // scope 0 at $DIR/simplify-arm.rs:+2:12: +2:13
- StorageLive(_4); // scope 1 at $DIR/simplify-arm.rs:+2:21: +2:22
- _4 = _3; // scope 1 at $DIR/simplify-arm.rs:+2:21: +2:22
- Deinit(_0); // scope 1 at $DIR/simplify-arm.rs:+2:18: +2:23
- ((_0 as Ok).0: u8) = move _4; // scope 1 at $DIR/simplify-arm.rs:+2:18: +2:23
- discriminant(_0) = 0; // scope 1 at $DIR/simplify-arm.rs:+2:18: +2:23
- StorageDead(_4); // scope 1 at $DIR/simplify-arm.rs:+2:22: +2:23
- StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:+2:22: +2:23
- goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:+2:22: +2:23
- }
-
- bb4: {
- return; // scope 0 at $DIR/simplify-arm.rs:+5:2: +5:2
- }
- }
-
+++ /dev/null
-- // MIR for `id_result` before SimplifyBranchSame
-+ // MIR for `id_result` after SimplifyBranchSame
-
- fn id_result(_1: Result<u8, i32>) -> Result<u8, i32> {
- debug r => _1; // in scope 0 at $DIR/simplify-arm.rs:+0:14: +0:15
- let mut _0: std::result::Result<u8, i32>; // return place in scope 0 at $DIR/simplify-arm.rs:+0:37: +0:52
- let mut _2: isize; // in scope 0 at $DIR/simplify-arm.rs:+2:9: +2:14
- let _3: u8; // in scope 0 at $DIR/simplify-arm.rs:+2:12: +2:13
- let mut _4: u8; // in scope 0 at $DIR/simplify-arm.rs:+2:21: +2:22
- let _5: i32; // in scope 0 at $DIR/simplify-arm.rs:+3:13: +3:14
- let mut _6: i32; // in scope 0 at $DIR/simplify-arm.rs:+3:23: +3:24
- scope 1 {
- debug x => _3; // in scope 1 at $DIR/simplify-arm.rs:+2:12: +2:13
- }
- scope 2 {
- debug y => _5; // in scope 2 at $DIR/simplify-arm.rs:+3:13: +3:14
- }
-
- bb0: {
- _2 = discriminant(_1); // scope 0 at $DIR/simplify-arm.rs:+1:11: +1:12
- switchInt(move _2) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:+1:5: +1:12
- }
-
- bb1: {
- StorageLive(_5); // scope 0 at $DIR/simplify-arm.rs:+3:13: +3:14
- _5 = ((_1 as Err).0: i32); // scope 0 at $DIR/simplify-arm.rs:+3:13: +3:14
- StorageLive(_6); // scope 2 at $DIR/simplify-arm.rs:+3:23: +3:24
- _6 = _5; // scope 2 at $DIR/simplify-arm.rs:+3:23: +3:24
- Deinit(_0); // scope 2 at $DIR/simplify-arm.rs:+3:19: +3:25
- ((_0 as Err).0: i32) = move _6; // scope 2 at $DIR/simplify-arm.rs:+3:19: +3:25
- discriminant(_0) = 1; // scope 2 at $DIR/simplify-arm.rs:+3:19: +3:25
- StorageDead(_6); // scope 2 at $DIR/simplify-arm.rs:+3:24: +3:25
- StorageDead(_5); // scope 0 at $DIR/simplify-arm.rs:+3:24: +3:25
- goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:+3:24: +3:25
- }
-
- bb2: {
- unreachable; // scope 0 at $DIR/simplify-arm.rs:+1:11: +1:12
- }
-
- bb3: {
- StorageLive(_3); // scope 0 at $DIR/simplify-arm.rs:+2:12: +2:13
- _3 = ((_1 as Ok).0: u8); // scope 0 at $DIR/simplify-arm.rs:+2:12: +2:13
- StorageLive(_4); // scope 1 at $DIR/simplify-arm.rs:+2:21: +2:22
- _4 = _3; // scope 1 at $DIR/simplify-arm.rs:+2:21: +2:22
- Deinit(_0); // scope 1 at $DIR/simplify-arm.rs:+2:18: +2:23
- ((_0 as Ok).0: u8) = move _4; // scope 1 at $DIR/simplify-arm.rs:+2:18: +2:23
- discriminant(_0) = 0; // scope 1 at $DIR/simplify-arm.rs:+2:18: +2:23
- StorageDead(_4); // scope 1 at $DIR/simplify-arm.rs:+2:22: +2:23
- StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:+2:22: +2:23
- goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:+2:22: +2:23
- }
-
- bb4: {
- return; // scope 0 at $DIR/simplify-arm.rs:+5:2: +5:2
- }
- }
-
+++ /dev/null
-- // MIR for `main` before SimplifyArmIdentity
-+ // MIR for `main` after SimplifyArmIdentity
-
- fn main() -> () {
- let mut _0: (); // return place in scope 0 at $DIR/simplify-arm-identity.rs:+0:11: +0:11
- let _1: Src; // in scope 0 at $DIR/simplify-arm-identity.rs:+1:9: +1:10
- let mut _2: Dst; // in scope 0 at $DIR/simplify-arm-identity.rs:+2:18: +5:6
- let mut _3: isize; // in scope 0 at $DIR/simplify-arm-identity.rs:+3:9: +3:20
- let mut _5: u8; // in scope 0 at $DIR/simplify-arm-identity.rs:+3:33: +3:34
- scope 1 {
- debug e => _1; // in scope 1 at $DIR/simplify-arm-identity.rs:+1:9: +1:10
- let _4: u8; // in scope 1 at $DIR/simplify-arm-identity.rs:+3:18: +3:19
- scope 2 {
- }
- scope 3 {
- debug x => _4; // in scope 3 at $DIR/simplify-arm-identity.rs:+3:18: +3:19
- }
- }
-
- bb0: {
- StorageLive(_1); // scope 0 at $DIR/simplify-arm-identity.rs:+1:9: +1:10
- Deinit(_1); // scope 0 at $DIR/simplify-arm-identity.rs:+1:18: +1:29
- ((_1 as Foo).0: u8) = const 0_u8; // scope 0 at $DIR/simplify-arm-identity.rs:+1:18: +1:29
- discriminant(_1) = 0; // scope 0 at $DIR/simplify-arm-identity.rs:+1:18: +1:29
- StorageLive(_2); // scope 1 at $DIR/simplify-arm-identity.rs:+2:18: +5:6
- _3 = const 0_isize; // scope 1 at $DIR/simplify-arm-identity.rs:+2:24: +2:25
- goto -> bb3; // scope 1 at $DIR/simplify-arm-identity.rs:+2:18: +2:25
- }
-
- bb1: {
- Deinit(_2); // scope 1 at $DIR/simplify-arm-identity.rs:+4:21: +4:32
- ((_2 as Foo).0: u8) = const 0_u8; // scope 1 at $DIR/simplify-arm-identity.rs:+4:21: +4:32
- discriminant(_2) = 0; // scope 1 at $DIR/simplify-arm-identity.rs:+4:21: +4:32
- goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:+4:21: +4:32
- }
-
- bb2: {
- unreachable; // scope 1 at $DIR/simplify-arm-identity.rs:+2:24: +2:25
- }
-
- bb3: {
- StorageLive(_4); // scope 1 at $DIR/simplify-arm-identity.rs:+3:18: +3:19
- _4 = ((_1 as Foo).0: u8); // scope 1 at $DIR/simplify-arm-identity.rs:+3:18: +3:19
- StorageLive(_5); // scope 3 at $DIR/simplify-arm-identity.rs:+3:33: +3:34
- _5 = _4; // scope 3 at $DIR/simplify-arm-identity.rs:+3:33: +3:34
- Deinit(_2); // scope 3 at $DIR/simplify-arm-identity.rs:+3:24: +3:35
- ((_2 as Foo).0: u8) = move _5; // scope 3 at $DIR/simplify-arm-identity.rs:+3:24: +3:35
- discriminant(_2) = 0; // scope 3 at $DIR/simplify-arm-identity.rs:+3:24: +3:35
- StorageDead(_5); // scope 3 at $DIR/simplify-arm-identity.rs:+3:34: +3:35
- StorageDead(_4); // scope 1 at $DIR/simplify-arm-identity.rs:+3:34: +3:35
- goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:+3:34: +3:35
- }
-
- bb4: {
- StorageDead(_2); // scope 1 at $DIR/simplify-arm-identity.rs:+5:6: +5:7
- nop; // scope 0 at $DIR/simplify-arm-identity.rs:+0:11: +6:2
- StorageDead(_1); // scope 0 at $DIR/simplify-arm-identity.rs:+6:1: +6:2
- return; // scope 0 at $DIR/simplify-arm-identity.rs:+6:2: +6:2
- }
- }
-
+++ /dev/null
-- // MIR for `main` before SimplifyArmIdentity
-+ // MIR for `main` after SimplifyArmIdentity
-
- fn main() -> () {
- let mut _0: (); // return place in scope 0 at $DIR/simplify-arm-identity.rs:+0:11: +0:11
- let _1: Src; // in scope 0 at $DIR/simplify-arm-identity.rs:+1:9: +1:10
- let mut _2: Dst; // in scope 0 at $DIR/simplify-arm-identity.rs:+2:18: +5:6
- let mut _3: isize; // in scope 0 at $DIR/simplify-arm-identity.rs:+3:9: +3:20
- let mut _5: u8; // in scope 0 at $DIR/simplify-arm-identity.rs:+3:33: +3:34
- scope 1 {
- debug e => _1; // in scope 1 at $DIR/simplify-arm-identity.rs:+1:9: +1:10
- let _4: u8; // in scope 1 at $DIR/simplify-arm-identity.rs:+3:18: +3:19
- scope 2 {
- }
- scope 3 {
- debug x => _4; // in scope 3 at $DIR/simplify-arm-identity.rs:+3:18: +3:19
- }
- }
-
- bb0: {
- StorageLive(_1); // scope 0 at $DIR/simplify-arm-identity.rs:+1:9: +1:10
- Deinit(_1); // scope 0 at $DIR/simplify-arm-identity.rs:+1:18: +1:29
- ((_1 as Foo).0: u8) = const 0_u8; // scope 0 at $DIR/simplify-arm-identity.rs:+1:18: +1:29
- discriminant(_1) = 0; // scope 0 at $DIR/simplify-arm-identity.rs:+1:18: +1:29
- StorageLive(_2); // scope 1 at $DIR/simplify-arm-identity.rs:+2:18: +5:6
- _3 = const 0_isize; // scope 1 at $DIR/simplify-arm-identity.rs:+2:24: +2:25
- goto -> bb3; // scope 1 at $DIR/simplify-arm-identity.rs:+2:18: +2:25
- }
-
- bb1: {
- Deinit(_2); // scope 1 at $DIR/simplify-arm-identity.rs:+4:21: +4:32
- ((_2 as Foo).0: u8) = const 0_u8; // scope 1 at $DIR/simplify-arm-identity.rs:+4:21: +4:32
- discriminant(_2) = 0; // scope 1 at $DIR/simplify-arm-identity.rs:+4:21: +4:32
- goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:+4:21: +4:32
- }
-
- bb2: {
- unreachable; // scope 1 at $DIR/simplify-arm-identity.rs:+2:24: +2:25
- }
-
- bb3: {
- StorageLive(_4); // scope 1 at $DIR/simplify-arm-identity.rs:+3:18: +3:19
- _4 = ((_1 as Foo).0: u8); // scope 1 at $DIR/simplify-arm-identity.rs:+3:18: +3:19
- StorageLive(_5); // scope 3 at $DIR/simplify-arm-identity.rs:+3:33: +3:34
- _5 = _4; // scope 3 at $DIR/simplify-arm-identity.rs:+3:33: +3:34
- Deinit(_2); // scope 3 at $DIR/simplify-arm-identity.rs:+3:24: +3:35
- ((_2 as Foo).0: u8) = move _5; // scope 3 at $DIR/simplify-arm-identity.rs:+3:24: +3:35
- discriminant(_2) = 0; // scope 3 at $DIR/simplify-arm-identity.rs:+3:24: +3:35
- StorageDead(_5); // scope 3 at $DIR/simplify-arm-identity.rs:+3:34: +3:35
- StorageDead(_4); // scope 1 at $DIR/simplify-arm-identity.rs:+3:34: +3:35
- goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:+3:34: +3:35
- }
-
- bb4: {
- StorageDead(_2); // scope 1 at $DIR/simplify-arm-identity.rs:+5:6: +5:7
- nop; // scope 0 at $DIR/simplify-arm-identity.rs:+0:11: +6:2
- StorageDead(_1); // scope 0 at $DIR/simplify-arm-identity.rs:+6:1: +6:2
- return; // scope 0 at $DIR/simplify-arm-identity.rs:+6:2: +6:2
- }
- }
-
debug x => _1; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+0:8: +0:9
let mut _0: std::option::Option<std::boxed::Box<()>>; // return place in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+0:31: +0:46
let mut _2: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+2:9: +2:13
-- let _3: std::boxed::Box<()>; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:14: +3:15
-- let mut _4: std::boxed::Box<()>; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:25: +3:26
+ let _3: std::boxed::Box<()>; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:14: +3:15
+ let mut _4: std::boxed::Box<()>; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:25: +3:26
- let mut _5: bool; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+5:1: +5:2
- let mut _6: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+5:1: +5:2
- let mut _7: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+5:1: +5:2
scope 1 {
- debug x => ((_0 as Some).0: std::boxed::Box<()>); // in scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:14: +3:15
+ debug x => _3; // in scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:14: +3:15
}
bb0: {
+- _5 = const false; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+1:11: +1:12
+- _5 = const true; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+1:11: +1:12
_2 = discriminant(_1); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+1:11: +1:12
switchInt(move _2) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+1:5: +1:12
}
bb1: {
- ((_0 as Some).0: std::boxed::Box<()>) = move ((_1 as Some).0: std::boxed::Box<()>); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:14: +3:15
+ StorageLive(_3); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:14: +3:15
+ _3 = move ((_1 as Some).0: std::boxed::Box<()>); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:14: +3:15
+ StorageLive(_4); // scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:25: +3:26
+ _4 = move _3; // scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:25: +3:26
Deinit(_0); // scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:20: +3:27
+ ((_0 as Some).0: std::boxed::Box<()>) = move _4; // scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:20: +3:27
discriminant(_0) = 1; // scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:20: +3:27
+ StorageDead(_4); // scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:26: +3:27
+ StorageDead(_3); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:26: +3:27
goto -> bb4; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:26: +3:27
}
}
bb4: {
+- _6 = discriminant(_1); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+5:1: +5:2
return; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+5:2: +5:2
}
}
debug x => _1; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+0:8: +0:9
let mut _0: std::option::Option<std::boxed::Box<()>>; // return place in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+0:31: +0:46
let mut _2: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+2:9: +2:13
-- let _3: std::boxed::Box<()>; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:14: +3:15
-- let mut _4: std::boxed::Box<()>; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:25: +3:26
+ let _3: std::boxed::Box<()>; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:14: +3:15
+ let mut _4: std::boxed::Box<()>; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:25: +3:26
- let mut _5: bool; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+5:1: +5:2
- let mut _6: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+5:1: +5:2
- let mut _7: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+5:1: +5:2
scope 1 {
- debug x => ((_0 as Some).0: std::boxed::Box<()>); // in scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:14: +3:15
+ debug x => _3; // in scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:14: +3:15
}
bb0: {
+- _5 = const false; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+1:11: +1:12
+- _5 = const true; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+1:11: +1:12
_2 = discriminant(_1); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+1:11: +1:12
switchInt(move _2) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+1:5: +1:12
}
bb1: {
- ((_0 as Some).0: std::boxed::Box<()>) = move ((_1 as Some).0: std::boxed::Box<()>); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:14: +3:15
+ StorageLive(_3); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:14: +3:15
+ _3 = move ((_1 as Some).0: std::boxed::Box<()>); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:14: +3:15
+ StorageLive(_4); // scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:25: +3:26
+ _4 = move _3; // scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:25: +3:26
Deinit(_0); // scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:20: +3:27
+ ((_0 as Some).0: std::boxed::Box<()>) = move _4; // scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:20: +3:27
discriminant(_0) = 1; // scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:20: +3:27
+ StorageDead(_4); // scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:26: +3:27
+ StorageDead(_3); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:26: +3:27
goto -> bb4; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+3:26: +3:27
}
}
bb4: {
+- _6 = discriminant(_1); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+5:1: +5:2
return; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:+5:2: +5:2
}
}
+++ /dev/null
-// compile-flags: -Zunsound-mir-opts
-// EMIT_MIR simplify_try.try_identity.SimplifyArmIdentity.diff
-// EMIT_MIR simplify_try.try_identity.SimplifyBranchSame.after.mir
-// EMIT_MIR simplify_try.try_identity.SimplifyLocals.after.mir
-// EMIT_MIR simplify_try.try_identity.DestinationPropagation.diff
-
-
-fn into_result<T, E>(r: Result<T, E>) -> Result<T, E> {
- r
-}
-
-fn from_error<T, E>(e: E) -> Result<T, E> {
- Err(e)
-}
-
-// This was written to the `?` from `try_trait`, but `try_trait_v2` uses a different structure,
-// so the relevant desugar is copied inline in order to keep the test testing the same thing.
-// FIXME(#85133): while this might be useful for `r#try!`, it would be nice to have a MIR
-// optimization that picks up the `?` desugaring, as `SimplifyArmIdentity` does not.
-fn try_identity(x: Result<u32, i32>) -> Result<u32, i32> {
- let y = match into_result(x) {
- Err(e) => return from_error(From::from(e)),
- Ok(v) => v,
- };
- Ok(y)
-}
-
-fn main() {
- let _ = try_identity(Ok(0));
-}
--- /dev/null
+// MIR for `new` after PreCodegen
+
+fn new(_1: Result<T, E>) -> Result<T, E> {
+ debug x => _1; // in scope 0 at $DIR/try_identity_e2e.rs:+0:14: +0:15
+ let mut _0: std::result::Result<T, E>; // return place in scope 0 at $DIR/try_identity_e2e.rs:+0:34: +0:46
+ let mut _2: T; // in scope 0 at $DIR/try_identity_e2e.rs:+2:9: +10:10
+ let mut _3: std::ops::ControlFlow<E, T>; // in scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10
+ let mut _4: isize; // in scope 0 at $DIR/try_identity_e2e.rs:+4:17: +4:22
+ let _5: T; // in scope 0 at $DIR/try_identity_e2e.rs:+4:20: +4:21
+ let mut _6: T; // in scope 0 at $DIR/try_identity_e2e.rs:+4:48: +4:49
+ let _7: E; // in scope 0 at $DIR/try_identity_e2e.rs:+5:21: +5:22
+ let mut _8: E; // in scope 0 at $DIR/try_identity_e2e.rs:+5:46: +5:47
+ let mut _9: isize; // in scope 0 at $DIR/try_identity_e2e.rs:+8:13: +8:37
+ let _10: T; // in scope 0 at $DIR/try_identity_e2e.rs:+8:35: +8:36
+ let _11: E; // in scope 0 at $DIR/try_identity_e2e.rs:+9:32: +9:33
+ let mut _12: E; // in scope 0 at $DIR/try_identity_e2e.rs:+9:49: +9:50
+ scope 1 {
+ debug v => _5; // in scope 1 at $DIR/try_identity_e2e.rs:+4:20: +4:21
+ }
+ scope 2 {
+ debug e => _7; // in scope 2 at $DIR/try_identity_e2e.rs:+5:21: +5:22
+ }
+ scope 3 {
+ debug v => _10; // in scope 3 at $DIR/try_identity_e2e.rs:+8:35: +8:36
+ }
+ scope 4 {
+ debug e => _11; // in scope 4 at $DIR/try_identity_e2e.rs:+9:32: +9:33
+ }
+
+ bb0: {
+ StorageLive(_2); // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +10:10
+ StorageLive(_3); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10
+ _4 = discriminant(_1); // scope 0 at $DIR/try_identity_e2e.rs:+3:19: +3:20
+ switchInt(move _4) -> [0_isize: bb2, 1_isize: bb1, otherwise: bb4]; // scope 0 at $DIR/try_identity_e2e.rs:+3:13: +3:20
+ }
+
+ bb1: {
+ StorageLive(_7); // scope 0 at $DIR/try_identity_e2e.rs:+5:21: +5:22
+ _7 = move ((_1 as Err).0: E); // scope 0 at $DIR/try_identity_e2e.rs:+5:21: +5:22
+ StorageLive(_8); // scope 2 at $DIR/try_identity_e2e.rs:+5:46: +5:47
+ _8 = move _7; // scope 2 at $DIR/try_identity_e2e.rs:+5:46: +5:47
+ Deinit(_3); // scope 2 at $DIR/try_identity_e2e.rs:+5:27: +5:48
+ ((_3 as Break).0: E) = move _8; // scope 2 at $DIR/try_identity_e2e.rs:+5:27: +5:48
+ discriminant(_3) = 1; // scope 2 at $DIR/try_identity_e2e.rs:+5:27: +5:48
+ StorageDead(_8); // scope 2 at $DIR/try_identity_e2e.rs:+5:47: +5:48
+ StorageDead(_7); // scope 0 at $DIR/try_identity_e2e.rs:+5:47: +5:48
+ _9 = discriminant(_3); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10
+ switchInt(move _9) -> [0_isize: bb5, 1_isize: bb3, otherwise: bb4]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +7:10
+ }
+
+ bb2: {
+ StorageLive(_5); // scope 0 at $DIR/try_identity_e2e.rs:+4:20: +4:21
+ _5 = move ((_1 as Ok).0: T); // scope 0 at $DIR/try_identity_e2e.rs:+4:20: +4:21
+ StorageLive(_6); // scope 1 at $DIR/try_identity_e2e.rs:+4:48: +4:49
+ _6 = move _5; // scope 1 at $DIR/try_identity_e2e.rs:+4:48: +4:49
+ Deinit(_3); // scope 1 at $DIR/try_identity_e2e.rs:+4:26: +4:50
+ ((_3 as Continue).0: T) = move _6; // scope 1 at $DIR/try_identity_e2e.rs:+4:26: +4:50
+ discriminant(_3) = 0; // scope 1 at $DIR/try_identity_e2e.rs:+4:26: +4:50
+ StorageDead(_6); // scope 1 at $DIR/try_identity_e2e.rs:+4:49: +4:50
+ StorageDead(_5); // scope 0 at $DIR/try_identity_e2e.rs:+4:49: +4:50
+ _9 = discriminant(_3); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10
+ switchInt(move _9) -> [0_isize: bb5, 1_isize: bb3, otherwise: bb4]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +7:10
+ }
+
+ bb3: {
+ StorageLive(_11); // scope 0 at $DIR/try_identity_e2e.rs:+9:32: +9:33
+ _11 = move ((_3 as Break).0: E); // scope 0 at $DIR/try_identity_e2e.rs:+9:32: +9:33
+ StorageLive(_12); // scope 4 at $DIR/try_identity_e2e.rs:+9:49: +9:50
+ _12 = move _11; // scope 4 at $DIR/try_identity_e2e.rs:+9:49: +9:50
+ Deinit(_0); // scope 4 at $DIR/try_identity_e2e.rs:+9:45: +9:51
+ ((_0 as Err).0: E) = move _12; // scope 4 at $DIR/try_identity_e2e.rs:+9:45: +9:51
+ discriminant(_0) = 1; // scope 4 at $DIR/try_identity_e2e.rs:+9:45: +9:51
+ StorageDead(_12); // scope 4 at $DIR/try_identity_e2e.rs:+9:50: +9:51
+ StorageDead(_11); // scope 0 at $DIR/try_identity_e2e.rs:+9:50: +9:51
+ StorageDead(_2); // scope 0 at $DIR/try_identity_e2e.rs:+11:5: +11:6
+ StorageDead(_3); // scope 0 at $DIR/try_identity_e2e.rs:+12:1: +12:2
+ return; // scope 0 at $DIR/try_identity_e2e.rs:+12:1: +12:2
+ }
+
+ bb4: {
+ unreachable; // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10
+ }
+
+ bb5: {
+ StorageLive(_10); // scope 0 at $DIR/try_identity_e2e.rs:+8:35: +8:36
+ _10 = move ((_3 as Continue).0: T); // scope 0 at $DIR/try_identity_e2e.rs:+8:35: +8:36
+ _2 = move _10; // scope 3 at $DIR/try_identity_e2e.rs:+8:41: +8:42
+ StorageDead(_10); // scope 0 at $DIR/try_identity_e2e.rs:+8:41: +8:42
+ Deinit(_0); // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +11:6
+ ((_0 as Ok).0: T) = move _2; // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +11:6
+ discriminant(_0) = 0; // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +11:6
+ StorageDead(_2); // scope 0 at $DIR/try_identity_e2e.rs:+11:5: +11:6
+ StorageDead(_3); // scope 0 at $DIR/try_identity_e2e.rs:+12:1: +12:2
+ return; // scope 0 at $DIR/try_identity_e2e.rs:+12:1: +12:2
+ }
+}
--- /dev/null
+// MIR for `old` after PreCodegen
+
+fn old(_1: Result<T, E>) -> Result<T, E> {
+ debug x => _1; // in scope 0 at $DIR/try_identity_e2e.rs:+0:14: +0:15
+ let mut _0: std::result::Result<T, E>; // return place in scope 0 at $DIR/try_identity_e2e.rs:+0:34: +0:46
+ let mut _2: T; // in scope 0 at $DIR/try_identity_e2e.rs:+2:9: +5:10
+ let mut _3: isize; // in scope 0 at $DIR/try_identity_e2e.rs:+3:13: +3:18
+ let _4: T; // in scope 0 at $DIR/try_identity_e2e.rs:+3:16: +3:17
+ let _5: E; // in scope 0 at $DIR/try_identity_e2e.rs:+4:17: +4:18
+ let mut _6: E; // in scope 0 at $DIR/try_identity_e2e.rs:+4:34: +4:35
+ scope 1 {
+ debug v => _4; // in scope 1 at $DIR/try_identity_e2e.rs:+3:16: +3:17
+ }
+ scope 2 {
+ debug e => _5; // in scope 2 at $DIR/try_identity_e2e.rs:+4:17: +4:18
+ }
+
+ bb0: {
+ StorageLive(_2); // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +5:10
+ _3 = discriminant(_1); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +2:16
+ switchInt(move _3) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +2:16
+ }
+
+ bb1: {
+ StorageLive(_5); // scope 0 at $DIR/try_identity_e2e.rs:+4:17: +4:18
+ _5 = move ((_1 as Err).0: E); // scope 0 at $DIR/try_identity_e2e.rs:+4:17: +4:18
+ StorageLive(_6); // scope 2 at $DIR/try_identity_e2e.rs:+4:34: +4:35
+ _6 = move _5; // scope 2 at $DIR/try_identity_e2e.rs:+4:34: +4:35
+ Deinit(_0); // scope 2 at $DIR/try_identity_e2e.rs:+4:30: +4:36
+ ((_0 as Err).0: E) = move _6; // scope 2 at $DIR/try_identity_e2e.rs:+4:30: +4:36
+ discriminant(_0) = 1; // scope 2 at $DIR/try_identity_e2e.rs:+4:30: +4:36
+ StorageDead(_6); // scope 2 at $DIR/try_identity_e2e.rs:+4:35: +4:36
+ StorageDead(_5); // scope 0 at $DIR/try_identity_e2e.rs:+4:35: +4:36
+ StorageDead(_2); // scope 0 at $DIR/try_identity_e2e.rs:+6:5: +6:6
+ return; // scope 0 at $DIR/try_identity_e2e.rs:+7:1: +7:2
+ }
+
+ bb2: {
+ unreachable; // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +2:16
+ }
+
+ bb3: {
+ StorageLive(_4); // scope 0 at $DIR/try_identity_e2e.rs:+3:16: +3:17
+ _4 = move ((_1 as Ok).0: T); // scope 0 at $DIR/try_identity_e2e.rs:+3:16: +3:17
+ _2 = move _4; // scope 1 at $DIR/try_identity_e2e.rs:+3:22: +3:23
+ StorageDead(_4); // scope 0 at $DIR/try_identity_e2e.rs:+3:22: +3:23
+ Deinit(_0); // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +6:6
+ ((_0 as Ok).0: T) = move _2; // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +6:6
+ discriminant(_0) = 0; // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +6:6
+ StorageDead(_2); // scope 0 at $DIR/try_identity_e2e.rs:+6:5: +6:6
+ return; // scope 0 at $DIR/try_identity_e2e.rs:+7:1: +7:2
+ }
+}
--- /dev/null
+// Track the status of MIR optimizations simplifying `Ok(res?)` for both the old and new desugarings
+// of that syntax.
+
+use std::ops::ControlFlow;
+
+// EMIT_MIR try_identity_e2e.new.PreCodegen.after.mir
+fn new<T, E>(x: Result<T, E>) -> Result<T, E> {
+ Ok(
+ match {
+ match x {
+ Ok(v) => ControlFlow::Continue(v),
+ Err(e) => ControlFlow::Break(e),
+ }
+ } {
+ ControlFlow::Continue(v) => v,
+ ControlFlow::Break(e) => return Err(e),
+ }
+ )
+}
+
+// EMIT_MIR try_identity_e2e.old.PreCodegen.after.mir
+fn old<T, E>(x: Result<T, E>) -> Result<T, E> {
+ Ok(
+ match x {
+ Ok(v) => v,
+ Err(e) => return Err(e),
+ }
+ )
+}
+
+fn main() {
+ let _ = new::<(), ()>(Ok(()));
+ let _ = old::<(), ()>(Ok(()));
+}
# Dump all the symbols from the staticlib into `syms`
"$(LLVM_BIN_DIR)"/llvm-objdump -t $(TMPDIR)/libdownstream.a > $(TMPDIR)/syms
# Count the global instances of `issue64153_test_function`. There'll be 2
- # if the `upstream` object file got erronously included twice.
+ # if the `upstream` object file got erroneously included twice.
# The line we are testing for with the regex looks something like:
# 0000000000000000 g F .text.issue64153_test_function 00000023 issue64153_test_function
grep -c -e "[[:space:]]g[[:space:]]*F[[:space:]].*issue64153_test_function" $(TMPDIR)/syms > $(TMPDIR)/count
# for now, but it is effectively ignored for all tests that don't include this file anyway.
#
# (Note that it's also possible the `_counters.<test>.txt` and `<test>.json` files (if generated)
-# may order results from multiple files inconsistently, which might also have to be accomodated
+# may order results from multiple files inconsistently, which might also have to be accommodated
# if and when we allow `llvm-cov` to produce results for multiple files. Note, the path separators
# appear to be normalized to `/` in those files, thankfully.)
LLVM_COV_IGNORE_FILES=\
# `// ignore-llvm-cov-show-diffs` anymore. This directive exists to work around a limitation
# with `llvm-cov show`. When reporting coverage for multiple instantiations of a generic function,
# with different type substitutions, `llvm-cov show` prints these in a non-deterministic order,
- # breaking the `diff` comparision.
+ # breaking the `diff` comparison.
#
# A partial workaround is implemented below, with `diff --ignore-matching-lines=RE`
# to ignore each line prefixing each generic instantiation coverage code region.
53| 1| 1 // This line appears covered, but the 1-character expression span covering the `1`
^0
54| 1| // is not executed. (`llvm-cov show` displays a `^0` below the `1` ). This is because
- 55| 1| // `fn j()` executes the open brace for the funciton body, followed by the function's
+ 55| 1| // `fn j()` executes the open brace for the function body, followed by the function's
56| 1| // first executable statement, `match x`. Inner function declarations are not
57| 1| // "visible" to the MIR for `j()`, so the code region counts all lines between the
58| 1| // open brace and the first statement as executed, which is, in a sense, true.
if x == 8 {
1 // This line appears covered, but the 1-character expression span covering the `1`
// is not executed. (`llvm-cov show` displays a `^0` below the `1` ). This is because
- // `fn j()` executes the open brace for the funciton body, followed by the function's
+ // `fn j()` executes the open brace for the function body, followed by the function's
// first executable statement, `match x`. Inner function declarations are not
// "visible" to the MIR for `j()`, so the code region counts all lines between the
// open brace and the first statement as executed, which is, in a sense, true.
check cc_plus_one_cxx_asm cc_plus_one_cxx_asm.checks
check cc_plus_one_asm cc_plus_one_asm.checks \
|| echo "warning: the cc crate forwards assembly files to the CC compiler." \
- "Clang uses its own intergrated assembler, which does not include the LVI passes."
+ "Clang uses its own integrated assembler, which does not include the LVI passes."
check cmake_plus_one_c cmake_plus_one_c.checks
check cmake_plus_one_c_asm cmake_plus_one_c_asm.checks
{"color": "rgb(120, 135, 151)"},
)
+// Checking the `<a>` container.
+assert-css: (
+ "//*[@class='result-name']/*[text()='test_docs::']/ancestor::a",
+ {"color": "rgb(0, 150, 207)", "background-color": "rgba(0, 0, 0, 0)"},
+)
+
+// Checking color and background on hover.
+move-cursor-to: "//*[@class='desc']//*[text()='Just a normal struct.']"
+assert-css: (
+ "//*[@class='result-name']/*[text()='test_docs::']",
+ {"color": "rgb(255, 255, 255)"},
+)
+assert-css: (
+ "//*[@class='result-name']/*[text()='test_docs::']/ancestor::a",
+ {"color": "rgb(255, 255, 255)", "background-color": "rgb(60, 60, 60)"},
+)
+
// Dark theme
local-storage: {
"rustdoc-theme": "dark",
{"color": "rgb(221, 221, 221)"},
)
+// Checking the `<a>` container.
+assert-css: (
+ "//*[@class='result-name']/*[text()='test_docs::']/ancestor::a",
+ {"color": "rgb(221, 221, 221)", "background-color": "rgba(0, 0, 0, 0)"},
+)
+
+// Checking color and background on hover.
+move-cursor-to: "//*[@class='desc']//*[text()='Just a normal struct.']"
+assert-css: (
+ "//*[@class='result-name']/*[text()='test_docs::']",
+ {"color": "rgb(221, 221, 221)"},
+)
+assert-css: (
+ "//*[@class='result-name']/*[text()='test_docs::']/ancestor::a",
+ {"color": "rgb(221, 221, 221)", "background-color": "rgb(119, 119, 119)"},
+)
+
// Light theme
local-storage: {"rustdoc-theme": "light", "rustdoc-use-system-theme": "false"}
reload:
{"color": "rgb(0, 0, 0)"},
)
+// Checking the `<a>` container.
+assert-css: (
+ "//*[@class='result-name']/*[text()='test_docs::']/ancestor::a",
+ {"color": "rgb(0, 0, 0)", "background-color": "rgba(0, 0, 0, 0)"},
+)
+
+// Checking color and background on hover.
+move-cursor-to: "//*[@class='desc']//*[text()='Just a normal struct.']"
+assert-css: (
+ "//*[@class='result-name']/*[text()='test_docs::']",
+ {"color": "rgb(0, 0, 0)"},
+)
+assert-css: (
+ "//*[@class='result-name']/*[text()='test_docs::']/ancestor::a",
+ {"color": "rgb(0, 0, 0)", "background-color": "rgb(221, 221, 221)"},
+)
+
// Check the alias more specifically in the dark theme.
goto: file://|DOC_PATH|/test_docs/index.html
// We set the theme so we're sure that the correct values will be used, whatever the computer
/// Some docs.
#[doc(cfg(any(target_os = "android", target_os = "linux", target_os = "emscripten", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))]
impl SimpleTrait for LongItemInfo2 {}
+
+pub struct WhereWhitespace<T>;
+
+impl<T> WhereWhitespace<T> {
+ pub fn new<F>(f: F) -> Self
+ where
+ F: FnMut() -> i32,
+ {}
+}
+
+impl<K, T> Whitespace<&K> for WhereWhitespace<T>
+where
+ K: std::fmt::Debug,
+{
+ type Output = WhereWhitespace<T>;
+ fn index(&self, _key: &K) -> &Self::Output {
+ self
+ }
+}
+
+pub trait Whitespace<Idx>
+where
+ Idx: ?Sized,
+{
+ type Output;
+ fn index(&self, index: Idx) -> &Self::Output;
+}
size: (600, 600)
goto: file://|DOC_PATH|/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html
// It shouldn't have an overflow in the topbar either.
-assert-property: (".mobile-topbar .location", {"scrollWidth": "492"})
-assert-property: (".mobile-topbar .location", {"clientWidth": "492"})
+assert-property: (".mobile-topbar .location", {"scrollWidth": "502"})
+assert-property: (".mobile-topbar .location", {"clientWidth": "502"})
assert-css: (".mobile-topbar .location", {"overflow-x": "hidden"})
--- /dev/null
+// This test ensures that the where conditions are correctly displayed.
+goto: file://|DOC_PATH|/lib2/trait.Whitespace.html
+show-text: true
+// First, we check in the trait definition if the where clause is "on its own" (not on the same
+// line than "pub trait Whitespace<Idx>").
+compare-elements-position-false: (".item-decl code", ".where.fmt-newline", ("y"))
+// And that the code following it isn't on the same line either.
+compare-elements-position-false: (".item-decl .fnname", ".where.fmt-newline", ("y"))
+
+goto: file://|DOC_PATH|/lib2/struct.WhereWhitespace.html
+// We make the screen a bit wider to ensure that the trait impl is on one line.
+size: (915, 915)
+
+compare-elements-position-false: ("#method\.new .fnname", "#method\.new .where.fmt-newline", ("y"))
+// We ensure that both the trait name and the struct name are on the same line in
+// "impl<K, T> Whitespace<&K> for WhereWhitespace<T>".
+compare-elements-position: (
+ "#trait-implementations-list .impl h3 .trait",
+ "#trait-implementations-list .impl h3 .struct",
+ ("y"),
+)
+// And we now check that the where condition isn't on the same line.
+compare-elements-position-false: (
+ "#trait-implementations-list .impl h3 .trait",
+ "#trait-implementations-list .impl h3 .where.fmt-newline",
+ ("y"),
+)
--- /dev/null
+// edition:2021
+// ignore-tidy-linelength
+
+// Regression test for <https://github.com/rust-lang/rust/issues/101199>
+
+use std::future::Future;
+
+// @is "$.index[*][?(@.name=='get_int')].inner.decl.output" '{"inner": "i32", "kind": "primitive"}'
+// @is "$.index[*][?(@.name=='get_int')].inner.header.async" false
+pub fn get_int() -> i32 {
+ 42
+}
+
+// @is "$.index[*][?(@.name=='get_int_async')].inner.decl.output" '{"inner": "i32", "kind": "primitive"}'
+// @is "$.index[*][?(@.name=='get_int_async')].inner.header.async" true
+pub async fn get_int_async() -> i32 {
+ 42
+}
+
+// @is "$.index[*][?(@.name=='get_int_future')].inner.decl.output.kind" '"impl_trait"'
+// @is "$.index[*][?(@.name=='get_int_future')].inner.decl.output.inner[0].trait_bound.trait.name" '"Future"'
+// @is "$.index[*][?(@.name=='get_int_future')].inner.decl.output.inner[0].trait_bound.trait.args.angle_bracketed.bindings[0].name" '"Output"'
+// @is "$.index[*][?(@.name=='get_int_future')].inner.decl.output.inner[0].trait_bound.trait.args.angle_bracketed.bindings[0].binding.equality.type" '{"inner": "i32", "kind": "primitive"}'
+// @is "$.index[*][?(@.name=='get_int_future')].inner.header.async" false
+pub fn get_int_future() -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+// @is "$.index[*][?(@.name=='get_int_future_async')].inner.decl.output.kind" '"impl_trait"'
+// @is "$.index[*][?(@.name=='get_int_future_async')].inner.decl.output.inner[0].trait_bound.trait.name" '"Future"'
+// @is "$.index[*][?(@.name=='get_int_future_async')].inner.decl.output.inner[0].trait_bound.trait.args.angle_bracketed.bindings[0].name" '"Output"'
+// @is "$.index[*][?(@.name=='get_int_future_async')].inner.decl.output.inner[0].trait_bound.trait.args.angle_bracketed.bindings[0].binding.equality.type" '{"inner": "i32", "kind": "primitive"}'
+// @is "$.index[*][?(@.name=='get_int_future_async')].inner.header.async" true
+pub async fn get_int_future_async() -> impl Future<Output = i32> {
+ async { 42 }
+}
--- /dev/null
+// Regression test for https://github.com/rust-lang/rust/issues/100973
+
+#![feature(no_core)]
+#![no_core]
+
+// @set m1 = "$.index[*][?(@.name == 'm1' && @.kind == 'module')].id"
+// @is "$.index[*][?(@.name == 'm1' && @.kind == 'module')].inner.items" []
+// @is "$.index[*][?(@.name == 'm1' && @.kind == 'module')].inner.is_stripped" true
+mod m1 {
+ pub fn f() {}
+}
+// @set m2 = "$.index[*][?(@.name == 'm2' && @.kind == 'module')].id"
+// @is "$.index[*][?(@.name == 'm2' && @.kind == 'module')].inner.items" []
+// @is "$.index[*][?(@.name == 'm2' && @.kind == 'module')].inner.is_stripped" true
+mod m2 {
+ pub fn f(_: u8) {}
+}
+
+// @set m1_use = "$.index[*][?(@.inner.name=='m1')].id"
+// @is "$.index[*][?(@.inner.name=='m1')].inner.id" $m1
+// @is "$.index[*][?(@.inner.name=='m1')].inner.glob" true
+pub use m1::*;
+// @set m2_use = "$.index[*][?(@.inner.name=='m2')].id"
+// @is "$.index[*][?(@.inner.name=='m2')].inner.id" $m2
+// @is "$.index[*][?(@.inner.name=='m2')].inner.glob" true
+pub use m2::*;
+
+// @ismany "$.index[*][?(@.inner.is_crate==true)].inner.items[*]" $m1_use $m2_use
--- /dev/null
+// Regression test for https://github.com/rust-lang/rust/issues/100973
+
+// @is "$.index[*][?(@.name=='m1' && @.kind == 'module')].inner.is_stripped" true
+// @set m1 = "$.index[*][?(@.name=='m1')].id"
+mod m1 {}
+
+// @is "$.index[*][?(@.inner.name=='m1' && @.kind=='import')].inner.id" $m1
+pub use m1::*;
#![feature(no_core)]
#![no_core]
-// @is "$.index[*][?(@.name=='foo')].kind" \"module\"
-// @is "$.index[*][?(@.name=='foo')].inner.is_stripped" "true"
+// @!has "$.index[*][?(@.name=='foo')]"
mod foo {
// @has "$.index[*][?(@.name=='Foo')]"
pub struct Foo;
--- /dev/null
+// Regression test for https://github.com/rust-lang/rust/issues/101103
+
+#![feature(no_core)]
+#![no_core]
+
+mod m1 {
+ pub fn x() {}
+}
+
+pub use m1::x;
+
+// @has "$.index[*][?(@.name=='x' && @.kind=='function')]"
+// @has "$.index[*][?(@.kind=='import' && @.inner.name=='x')].inner.source" '"m1::x"'
+// @!has "$.index[*][?(@.name=='m1')]"
#![no_core]
#![feature(no_core)]
-// @is "$.index[*][?(@.name=='style')].kind" \"module\"
-// @is "$.index[*][?(@.name=='style')].inner.is_stripped" "true"
+// @!has "$.index[*][?(@.name=='style')]"
mod style {
// @set color_struct_id = "$.index[*][?(@.kind=='struct' && @.name=='Color')].id"
pub struct Color;
#![no_core]
#![feature(no_core)]
-// @is "$.index[*][?(@.name=='inner')].kind" \"module\"
-// @is "$.index[*][?(@.name=='inner')].inner.is_stripped" "true"
+// @!has "$.index[*][?(@.kind=='inner')]"
mod inner {
// @has "$.index[*][?(@.name=='Public')]"
pub struct Public;
#![no_core]
#![feature(no_core)]
-// @is "$.index[*][?(@.name=='inner')].kind" \"module\"
-// @is "$.index[*][?(@.name=='inner')].inner.is_stripped" "true"
+// @!has "$.index[*][?(@.name=='inner')]"
mod inner {
// @set pub_id = "$.index[*][?(@.name=='Public')].id"
pub struct Public;
}
// @is "$.index[*][?(@.kind=='import')].inner.name" \"Public\"
+// @is "$.index[*][?(@.kind=='import')].inner.id" $pub_id
// @set use_id = "$.index[*][?(@.kind=='import')].id"
pub use inner::Public;
-// @ismany "$.index[*][?(@.name=='inner')].inner.items[*]" $pub_id
// @ismany "$.index[*][?(@.name=='simple_private')].inner.items[*]" $use_id
pub fn pub_inner_1() {}
}
-// @has "$.index[*][?(@.name=='pub_inner_reachable')]"
+// @!has "$.index[*][?(@.name=='pub_inner_reachable')]"
mod pub_inner_reachable {
// @has "$.index[*][?(@.name=='pub_inner_2')]"
pub fn pub_inner_2() {}
// check-pass
-// Regresion test for <https://github.com/rust-lang/rust/issues/79459>.
+// Regression test for <https://github.com/rust-lang/rust/issues/79459>.
pub trait Query {}
pub trait AsQuery {
impl<const N: usize> Trait<N> for [u8; N] {}
// @has foo/struct.Foo.html '//pre[@class="rust struct"]' \
-// 'pub struct Foo<const N: usize> where u8: Trait<N>'
+// 'pub struct Foo<const N: usize>where u8: Trait<N>'
pub struct Foo<const N: usize> where u8: Trait<N>;
// @has foo/struct.Bar.html '//pre[@class="rust struct"]' 'pub struct Bar<T, const N: usize>(_)'
pub struct Bar<T, const N: usize>([T; N]);
-// @has foo/struct.Foo.html '//*[@id="impl-Foo%3CM%3E"]/h3[@class="code-header in-band"]' 'impl<const M: usize> Foo<M> where u8: Trait<M>'
+// @has foo/struct.Foo.html '//*[@id="impl-Foo%3CM%3E"]/h3[@class="code-header in-band"]' 'impl<const M: usize> Foo<M>where u8: Trait<M>'
impl<const M: usize> Foo<M> where u8: Trait<M> {
// @has - '//*[@id="associatedconstant.FOO_ASSOC"]' 'pub const FOO_ASSOC: usize'
pub const FOO_ASSOC: usize = M + 13;
// @has foo/struct.Bar.html '//*[@id="impl-Bar%3Cu8%2C%20M%3E"]/h3[@class="code-header in-band"]' 'impl<const M: usize> Bar<u8, M>'
impl<const M: usize> Bar<u8, M> {
// @has - '//*[@id="method.hey"]' \
- // 'pub fn hey<const N: usize>(&self) -> Foo<N> where u8: Trait<N>'
+ // 'pub fn hey<const N: usize>(&self) -> Foo<N>where u8: Trait<N>'
pub fn hey<const N: usize>(&self) -> Foo<N> where u8: Trait<N> {
Foo
}
}
// @has foo/fn.test.html '//pre[@class="rust fn"]' \
-// 'pub fn test<const N: usize>() -> impl Trait<N> where u8: Trait<N>'
+// 'pub fn test<const N: usize>() -> impl Trait<N>where u8: Trait<N>'
pub fn test<const N: usize>() -> impl Trait<N> where u8: Trait<N> {
2u8
}
--- /dev/null
+// Regression test for <https://github.com/rust-lang/rust/issues/101129>.
+
+#![feature(doc_auto_cfg)]
+#![crate_type = "lib"]
+#![crate_name = "foo"]
+
+pub struct S;
+pub trait MyTrait1 {}
+pub trait MyTrait2 {}
+
+// @has foo/struct.S.html
+// @has - '//*[@id="impl-MyTrait1-for-S"]//*[@class="stab portability"]' \
+// 'Available on non-crate feature coolstuff only.'
+#[cfg(not(feature = "coolstuff"))]
+impl MyTrait1 for S {}
+
+#[cfg(not(feature = "coolstuff"))]
+mod submod {
+ use crate::{S, MyTrait2};
+ // This impl should also have the `not(feature = "coolstuff")`.
+ // @has - '//*[@id="impl-MyTrait2-for-S"]//*[@class="stab portability"]' \
+ // 'Available on non-crate feature coolstuff only.'
+ impl MyTrait2 for S {}
+}
// rust-lang/rust#75225
//
// Since Rust 2018 we encourage writing out <'_> explicitly to make it clear
-// that borrowing is occuring. Make sure rustdoc is following the same idiom.
+// that borrowing is occurring. Make sure rustdoc is following the same idiom.
#![crate_name = "foo"]
// @has foo/trait.LendingIterator.html
pub trait LendingIterator {
- // @has - '//*[@id="associatedtype.Item"]//h4[@class="code-header"]' "type Item<'a> where Self: 'a"
+ // @has - '//*[@id="associatedtype.Item"]//h4[@class="code-header"]' "type Item<'a>where Self: 'a"
type Item<'a> where Self: 'a;
// @has - '//*[@id="tymethod.next"]//h4[@class="code-header"]' \
pub struct Infinite<T>(T);
// @has foo/trait.LendingIterator.html
-// @has - '//*[@id="associatedtype.Item-2"]//h4[@class="code-header"]' "type Item<'a> where Self: 'a = &'a T"
+// @has - '//*[@id="associatedtype.Item-2"]//h4[@class="code-header"]' "type Item<'a>where Self: 'a = &'a T"
impl<T> LendingIterator for Infinite<T> {
type Item<'a> where Self: 'a = &'a T;
pub trait Trait<'x> {}
// @has foo/fn.test1.html
-// @has - '//pre' "pub fn test1<T>() where for<'a> &'a T: Iterator,"
+// @has - '//pre' "pub fn test1<T>()where for<'a> &'a T: Iterator,"
pub fn test1<T>()
where
for<'a> &'a T: Iterator,
}
// @has foo/fn.test2.html
-// @has - '//pre' "pub fn test2<T>() where for<'a, 'b> &'a T: Trait<'b>,"
+// @has - '//pre' "pub fn test2<T>()where for<'a, 'b> &'a T: Trait<'b>,"
pub fn test2<T>()
where
for<'a, 'b> &'a T: Trait<'b>,
}
// @has foo/fn.test3.html
-// @has - '//pre' "pub fn test3<F>() where F: for<'a, 'b> Fn(&'a u8, &'b u8),"
+// @has - '//pre' "pub fn test3<F>()where F: for<'a, 'b> Fn(&'a u8, &'b u8),"
pub fn test3<F>()
where
F: for<'a, 'b> Fn(&'a u8, &'b u8),
// @has - '//span[@id="structfield.some_trait"]' "some_trait: &'a dyn for<'b> Trait<'b>"
impl<'a> Foo<'a> {
- // @has - '//h4[@class="code-header"]' "pub fn bar<T>() where T: Trait<'a>,"
+ // @has - '//h4[@class="code-header"]' "pub fn bar<T>()where T: Trait<'a>,"
pub fn bar<T>()
where
T: Trait<'a>,
pub struct Foo<T> { field: T }
// @has impl_parts/struct.Foo.html '//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<T: Clone> !AnAutoTrait for Foo<T> where T: Sync,"
+// "impl<T: Clone> !AnAutoTrait for Foo<T>where T: Sync,"
// @has impl_parts/trait.AnAutoTrait.html '//*[@class="item-list"]//h3[@class="code-header in-band"]' \
-// "impl<T: Clone> !AnAutoTrait for Foo<T> where T: Sync,"
+// "impl<T: Clone> !AnAutoTrait for Foo<T>where T: Sync,"
impl<T: Clone> !AnAutoTrait for Foo<T> where T: Sync {}
// @has 'foo/builders/struct.ActionRowBuilder.html'
// @has - '//*[@id="synthetic-implementations"]' 'Auto Trait Implementations'
-// And that the link in the module is targetting it.
+// And that the link in the module is targeting it.
// @has 'foo/builders/index.html'
// @has - '//a[@href="struct.ActionRowBuilder.html"]' 'ActionRowBuilder'
pub mod reexport {
// @has issue_20727_4/reexport/trait.Index.html
- // @has - '//*[@class="rust trait"]' 'trait Index<Idx> where Idx: ?Sized, {'
+ // @has - '//*[@class="rust trait"]' 'trait Index<Idx>where Idx: ?Sized,{'
// @has - '//*[@class="rust trait"]' 'type Output: ?Sized'
// @has - '//*[@class="rust trait"]' \
// 'fn index(&self, index: Idx) -> &Self::Output'
// @has issue_20727_4/reexport/trait.IndexMut.html
// @has - '//*[@class="rust trait"]' \
- // 'trait IndexMut<Idx>: Index<Idx> where Idx: ?Sized, {'
+ // 'trait IndexMut<Idx>: Index<Idx>where Idx: ?Sized,{'
// @has - '//*[@class="rust trait"]' \
// 'fn index_mut(&mut self, index: Idx) -> &mut Self::Output;'
pub use issue_20727::IndexMut;
// @has issue_21801/struct.Foo.html
// @has - '//*[@id="method.new"]' \
-// 'fn new<F>(f: F) -> Foo where F: FnMut() -> i32'
+// 'fn new<F>(f: F) -> Foowhere F: FnMut() -> i32'
pub use issue_21801::Foo;
fn my_string(&self) -> String;
}
-// @has - "//div[@id='implementors-list']//*[@id='impl-MyTrait-for-T']//h3[@class='code-header in-band']" "impl<T> MyTrait for T where T: Debug"
+// @has - "//div[@id='implementors-list']//*[@id='impl-MyTrait-for-T']//h3[@class='code-header in-band']" "impl<T> MyTrait for Twhere T: Debug"
impl<T> MyTrait for T
where
T: fmt::Debug,
pub trait Bar {}
-// @has foo/struct.Foo.html '//pre' 'pub struct Foo<T>(pub T) where T: Bar;'
+// @has foo/struct.Foo.html '//pre' 'pub struct Foo<T>(pub T)where T: Bar;'
pub struct Foo<T>(pub T) where T: Bar;
}
// @has issue_50159/struct.Switch.html
-// @has - '//h3[@class="code-header in-band"]' 'impl<B> Send for Switch<B> where <B as Signal>::Item: Send'
-// @has - '//h3[@class="code-header in-band"]' 'impl<B> Sync for Switch<B> where <B as Signal>::Item: Sync'
+// @has - '//h3[@class="code-header in-band"]' 'impl<B> Send for Switch<B>where <B as Signal>::Item: Send'
+// @has - '//h3[@class="code-header in-band"]' '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 has-srclink"]' 5
pub struct Switch<B: Signal> {
// @has issue_51236/struct.Owned.html
// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<T> Send for Owned<T> where <T as Owned<'static>>::Reader: Send"
+// "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>,
}
pub trait ScopeHandle<'scope> {}
-
-
// @has issue_54705/struct.ScopeFutureContents.html
// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<'scope, S> Send for ScopeFutureContents<'scope, S> where S: Sync"
+// "impl<'scope, S> Send for ScopeFutureContents<'scope, S>where S: Sync"
//
// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<'scope, S> Sync for ScopeFutureContents<'scope, S> where S: Sync"
+// "impl<'scope, S> Sync for ScopeFutureContents<'scope, S>where S: Sync"
pub struct ScopeFutureContents<'scope, S>
where S: ScopeHandle<'scope>,
{
extern crate issue_98697_reexport_with_anonymous_lifetime;
-// @has issue_98697/fn.repro.html '//pre[@class="rust fn"]/code' 'fn repro<F>() where F: Fn(&str)'
+// @has issue_98697/fn.repro.html '//pre[@class="rust fn"]/code' 'fn repro<F>()where F: Fn(&str)'
// @!has issue_98697/fn.repro.html '//pre[@class="rust fn"]/code' 'for<'
pub use issue_98697_reexport_with_anonymous_lifetime::repro;
// (yes, that's a thing), rustdoc lists both of them on the index page,
// but only documents the first one on the page for the macro.
// Fortunately, this can only happen in document private items mode,
-// but it still isn't ideal beahvior.
+// but it still isn't ideal behavior.
//
// See https://github.com/rust-lang/rust/pull/88019#discussion_r693920453
//
--- /dev/null
+#![crate_name = "foo"]
+
+#![feature(rustdoc_internals)]
+
+// @has foo/index.html
+// @has - '//h2[@id="primitives"]' 'Primitive Types'
+// @has - '//a[@href="primitive.reference.html"]' 'reference'
+// @has - '//div[@class="sidebar-elems"]//li/a' 'Primitive Types'
+// @has - '//div[@class="sidebar-elems"]//li/a/@href' '#primitives'
+// @has foo/primitive.reference.html
+// @has - '//a[@class="primitive"]' 'reference'
+// @has - '//span[@class="in-band"]' 'Primitive Type reference'
+// @has - '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!'
+
+// There should be only one implementation listed.
+// @count - '//*[@class="impl has-srclink"]' 1
+// @has - '//*[@id="impl-Foo%3C%26A%3E-for-%26B"]/*[@class="code-header in-band"]' \
+// 'impl<A, B> Foo<&A> for &B'
+#[doc(primitive = "reference")]
+/// this is a test!
+mod reference {}
+
+pub struct Bar;
+
+// This implementation should **not** show up.
+impl<T> From<&T> for Bar {
+ fn from(s: &T) -> Self {
+ Bar
+ }
+}
+
+pub trait Foo<T> {
+ fn stuff(&self, other: &T) {}
+}
+
+// This implementation should show up.
+impl<A, B> Foo<&A> for &B {}
// @has - '//span[@class="in-band"]' 'Primitive Type slice'
// @has - '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!'
// @has - '//h2[@id="synthetic-implementations"]' 'Auto Trait Implementations'
-// @has - '//div[@id="synthetic-implementations-list"]//h3' 'impl<T> Send for [T] where T: Send'
-// @has - '//div[@id="synthetic-implementations-list"]//h3' 'impl<T> Sync for [T] where T: Sync'
+// @has - '//div[@id="synthetic-implementations-list"]//h3' 'impl<T> Send for [T]where T: Send'
+// @has - '//div[@id="synthetic-implementations-list"]//h3' 'impl<T> Sync for [T]where T: Sync'
#[doc(primitive = "slice")]
/// this is a test!
mod slice_prim {}
// @has basic/struct.Foo.html
-// @has - '//h3[@class="code-header in-band"]' 'impl<T> Send for Foo<T> where T: Send'
-// @has - '//h3[@class="code-header in-band"]' 'impl<T> Sync for Foo<T> where T: Sync'
+// @has - '//h3[@class="code-header in-band"]' 'impl<T> Send for Foo<T>where T: Send'
+// @has - '//h3[@class="code-header in-band"]' 'impl<T> Sync for Foo<T>where T: Sync'
// @count - '//*[@id="implementations-list"]//*[@class="impl has-srclink"]' 0
// @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]' 5
pub struct Foo<T> {
// @has complex/struct.NotOuter.html
// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<'a, T, K: ?Sized> Send for Outer<'a, T, K> where K: for<'b> Fn((&'b bool, &'a u8)) \
+// "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"
pub use foo::{Foo, Inner as NotInner, MyTrait as NotMyTrait, Outer as NotOuter};
// @has lifetimes/struct.Foo.html
// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<'c, K> Send for Foo<'c, K> where K: for<'b> Fn(&'b bool) -> &'c u8, 'c: 'static"
+// "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 has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<'c, K> Sync for Foo<'c, K> where K: Sync"
+// "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 has-srclink"]//h3[@class="code-header in-band"]' \
-// 'impl<T> Sync for Foo<T> where T: Sync'
+// 'impl<T> Sync for Foo<T>where T: Sync'
//
// @has - '//*[@id="trait-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
// 'impl<T> Send for Foo<T>'
// @has nested/struct.Foo.html
// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// 'impl<T> Send for Foo<T> where T: Copy'
+// 'impl<T> Send for Foo<T>where T: Copy'
//
// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// 'impl<T> Sync for Foo<T> where T: Sync'
+// '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 has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<T> Send for Outer<T> where T: Send + Copy"
+// "impl<T> Send for Outer<T>where T: Send + Copy"
pub struct Outer<T> {
inner_field: Inner<T>,
}
// @has project/struct.Foo.html
// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<'c, K> Send for Foo<'c, K> where K: MyTrait<MyItem = bool>, 'c: 'static"
+// "impl<'c, K> Send for Foo<'c, K>where K: MyTrait<MyItem = bool>, 'c: 'static"
//
// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<'c, K> Sync for Foo<'c, K> where K: MyTrait, <K as MyTrait>::MyItem: OtherTrait, \
+// "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 has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<P1> Send for WriteAndThen<P1> where <P1 as Pattern>::Value: Send"
+// "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 has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<T> Send for Owned<T> where <T as OwnedTrait<'static>>::Reader: Send"
+// "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,
}
}
// @has 'foo/trait.SomeTrait.html'
-// @has - "//*[@id='impl-SomeTrait%3C(A%2C%20B%2C%20C%2C%20D%2C%20E)%3E-for-(A%2C%20B%2C%20C%2C%20D%2C%20E)']/h3" "impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E) where A: PartialOrd<A> + PartialEq<A>, B: PartialOrd<B> + PartialEq<B>, C: PartialOrd<C> + PartialEq<C>, D: PartialOrd<D> + PartialEq<D>, E: PartialOrd<E> + PartialEq<E> + ?Sized, "
+// @has - "//*[@id='impl-SomeTrait%3C(A%2C%20B%2C%20C%2C%20D%2C%20E)%3E-for-(A%2C%20B%2C%20C%2C%20D%2C%20E)']/h3" "impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E)where A: PartialOrd<A> + PartialEq<A>, B: PartialOrd<B> + PartialEq<B>, C: PartialOrd<C> + PartialEq<C>, D: PartialOrd<D> + PartialEq<D>, E: PartialOrd<E> + PartialEq<E> + ?Sized, "
impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E)
where
A: PartialOrd<A> + PartialEq<A>,
pub trait MyTrait { fn dummy(&self) { } }
-// @has foo/struct.Alpha.html '//pre' "pub struct Alpha<A>(_) where A: MyTrait"
+// @has foo/struct.Alpha.html '//pre' "pub struct Alpha<A>(_)where A: MyTrait"
pub struct Alpha<A>(A) where A: MyTrait;
-// @has foo/trait.Bravo.html '//pre' "pub trait Bravo<B> where B: MyTrait"
+// @has foo/trait.Bravo.html '//pre' "pub trait Bravo<B>where B: MyTrait"
pub trait Bravo<B> where B: MyTrait { fn get(&self, B: B); }
-// @has foo/fn.charlie.html '//pre' "pub fn charlie<C>() where C: MyTrait"
+// @has foo/fn.charlie.html '//pre' "pub fn charlie<C>()where C: MyTrait"
pub fn charlie<C>() where C: MyTrait {}
pub struct Delta<D>(D);
// @has foo/struct.Delta.html '//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<D> Delta<D> where D: MyTrait"
+// "impl<D> Delta<D>where D: MyTrait"
impl<D> Delta<D> where D: MyTrait {
pub fn delta() {}
}
}
// @has foo/struct.Echo.html '//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<E> MyTrait for Echo<E> where E: MyTrait"
+// "impl<E> MyTrait for Echo<E>where E: MyTrait"
// @has foo/trait.MyTrait.html '//*[@id="implementors-list"]//h3[@class="code-header in-band"]' \
-// "impl<E> MyTrait for Echo<E> where E: MyTrait"
-impl<E> MyTrait for Echo<E> where E: MyTrait {}
+// "impl<E> MyTrait for Echo<E>where E: MyTrait"
+impl<E> MyTrait for Echo<E>where E: MyTrait {}
pub enum Foxtrot<F> { Foxtrot1(F) }
// @has foo/enum.Foxtrot.html '//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<F> MyTrait for Foxtrot<F> where F: MyTrait"
+// "impl<F> MyTrait for Foxtrot<F>where F: MyTrait"
// @has foo/trait.MyTrait.html '//*[@id="implementors-list"]//h3[@class="code-header in-band"]' \
-// "impl<F> MyTrait for Foxtrot<F> where F: MyTrait"
-impl<F> MyTrait for Foxtrot<F> where F: MyTrait {}
+// "impl<F> MyTrait for Foxtrot<F>where F: MyTrait"
+impl<F> MyTrait for Foxtrot<F>where F: MyTrait {}
// @has foo/type.Golf.html '//pre[@class="rust typedef"]' \
-// "type Golf<T> where T: Clone, = (T, T)"
+// "type Golf<T>where T: Clone, = (T, T)"
pub type Golf<T> where T: Clone = (T, T);
// compile-flags: -Z unstable-options
#![crate_type = "lib"]
+#![feature(rustc_attrs)]
#![feature(rustc_private)]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
//~^ ERROR diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
//~^^ ERROR diagnostics should be created using translatable messages
}
+
+// Check that `rustc_lint_diagnostics`-annotated functions aren't themselves linted.
+
+#[rustc_lint_diagnostics]
+pub fn skipped_because_of_annotation<'a>(sess: &'a ParseSess) {
+ let _diag = sess.struct_err("untranslatable diagnostic"); // okay!
+}
error: diagnostics should be created using translatable messages
- --> $DIR/diagnostics.rs:36:14
+ --> $DIR/diagnostics.rs:37:14
|
LL | sess.struct_err("untranslatable diagnostic")
| ^^^^^^^^^^
|
note: the lint level is defined here
- --> $DIR/diagnostics.rs:5:9
+ --> $DIR/diagnostics.rs:6:9
|
LL | #![deny(rustc::untranslatable_diagnostic)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: diagnostics should be created using translatable messages
- --> $DIR/diagnostics.rs:53:14
+ --> $DIR/diagnostics.rs:54:14
|
LL | diag.note("untranslatable diagnostic");
| ^^^^
error: diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
- --> $DIR/diagnostics.rs:67:22
+ --> $DIR/diagnostics.rs:68:22
|
LL | let _diag = sess.struct_err(fluent::parser::expect_path);
| ^^^^^^^^^^
|
note: the lint level is defined here
- --> $DIR/diagnostics.rs:6:9
+ --> $DIR/diagnostics.rs:7:9
|
LL | #![deny(rustc::diagnostic_outside_of_impl)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
- --> $DIR/diagnostics.rs:70:22
+ --> $DIR/diagnostics.rs:71:22
|
LL | let _diag = sess.struct_err("untranslatable diagnostic");
| ^^^^^^^^^^
error: diagnostics should be created using translatable messages
- --> $DIR/diagnostics.rs:70:22
+ --> $DIR/diagnostics.rs:71:22
|
LL | let _diag = sess.struct_err("untranslatable diagnostic");
| ^^^^^^^^^^
+++ /dev/null
-// check-pass
-// aux-build:lint-for-crate-rpass.rs
-// ignore-stage1
-// compile-flags: -D crate-not-okay
-
-#![feature(plugin, register_attr, custom_inner_attributes)]
-
-#![register_attr(
- crate_okay,
- crate_blue,
- crate_red,
- crate_grey,
- crate_green,
-)]
-
-#![plugin(lint_for_crate_rpass)] //~ WARNING compiler plugins are deprecated
-#![crate_okay]
-#![crate_blue]
-#![crate_red]
-#![crate_grey]
-#![crate_green]
-
-fn main() {}
+++ /dev/null
-warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
- --> $DIR/issue-15778-pass.rs:16:1
- |
-LL | #![plugin(lint_for_crate_rpass)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
- |
- = note: `#[warn(deprecated)]` on by default
-
-warning: 1 warning emitted
-
// Tests error conditions for specifying diagnostics using #[derive(SessionDiagnostic)]
// normalize-stderr-test "the following other types implement trait `IntoDiagnosticArg`:(?:.*\n){0,9}\s+and \d+ others" -> "normalized in stderr"
-
+// normalize-stderr-test "diagnostic_builder\.rs:[0-9]+:[0-9]+" -> "diagnostic_builder.rs:LL:CC"
// The proc_macro2 crate handles spans differently when on beta/stable release rather than nightly,
// changing the output of this test. Since SessionDiagnostic is strictly internal to the compiler
// the test is just ignored on stable and beta:
|
= help: normalized in stderr
note: required by a bound in `DiagnosticBuilder::<'a, G>::set_arg`
- --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:569:19
+ --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:LL:CC
|
LL | arg: impl IntoDiagnosticArg,
| ^^^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::set_arg`
b: u64
}
-#[derive(SessionSubdiagnostic)]
-#[label(parser::add_paren)]
-//~^ NOTE previously specified here
-//~^^ NOTE previously specified here
-#[label(parser::add_paren)]
-//~^ ERROR specified multiple times
-//~^^ ERROR specified multiple times
-struct AD {
- #[primary_span]
- span: Span,
-}
-
#[derive(SessionSubdiagnostic)]
#[label(parser::add_paren, parser::add_paren)]
//~^ ERROR `#[label(parser::add_paren)]` is not a valid attribute
LL | | }
| |_^
-error: specified multiple times
- --> $DIR/subdiagnostic-derive.rs:314:1
- |
-LL | #[label(parser::add_paren)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: previously specified here
- --> $DIR/subdiagnostic-derive.rs:311:1
- |
-LL | #[label(parser::add_paren)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: specified multiple times
- --> $DIR/subdiagnostic-derive.rs:314:1
- |
-LL | #[label(parser::add_paren)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: previously specified here
- --> $DIR/subdiagnostic-derive.rs:311:1
- |
-LL | #[label(parser::add_paren)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
error: `#[label(parser::add_paren)]` is not a valid attribute
- --> $DIR/subdiagnostic-derive.rs:323:28
+ --> $DIR/subdiagnostic-derive.rs:311:28
|
LL | #[label(parser::add_paren, parser::add_paren)]
| ^^^^^^^^^^^^^^^^^
= help: a diagnostic slug must be the first argument to the attribute
error: specified multiple times
- --> $DIR/subdiagnostic-derive.rs:336:5
+ --> $DIR/subdiagnostic-derive.rs:324:5
|
LL | #[primary_span]
| ^^^^^^^^^^^^^^^
|
note: previously specified here
- --> $DIR/subdiagnostic-derive.rs:333:5
+ --> $DIR/subdiagnostic-derive.rs:321:5
|
LL | #[primary_span]
| ^^^^^^^^^^^^^^^
error: subdiagnostic kind not specified
- --> $DIR/subdiagnostic-derive.rs:342:8
+ --> $DIR/subdiagnostic-derive.rs:330:8
|
LL | struct AG {
| ^^
error: specified multiple times
- --> $DIR/subdiagnostic-derive.rs:379:47
+ --> $DIR/subdiagnostic-derive.rs:367:47
|
LL | #[suggestion(parser::add_paren, code = "...", code = "...")]
| ^^^^^^^^^^^^
|
note: previously specified here
- --> $DIR/subdiagnostic-derive.rs:379:33
+ --> $DIR/subdiagnostic-derive.rs:367:33
|
LL | #[suggestion(parser::add_paren, code = "...", code = "...")]
| ^^^^^^^^^^^^
error: specified multiple times
- --> $DIR/subdiagnostic-derive.rs:397:5
+ --> $DIR/subdiagnostic-derive.rs:385:5
|
LL | #[applicability]
| ^^^^^^^^^^^^^^^^
|
note: previously specified here
- --> $DIR/subdiagnostic-derive.rs:394:5
+ --> $DIR/subdiagnostic-derive.rs:382:5
|
LL | #[applicability]
| ^^^^^^^^^^^^^^^^
error: the `#[applicability]` attribute can only be applied to fields of type `Applicability`
- --> $DIR/subdiagnostic-derive.rs:408:5
+ --> $DIR/subdiagnostic-derive.rs:396:5
|
LL | #[applicability]
| ^^^^^^^^^^^^^^^^
error: suggestion without `applicability`
- --> $DIR/subdiagnostic-derive.rs:403:1
+ --> $DIR/subdiagnostic-derive.rs:391:1
|
LL | / #[suggestion(parser::add_paren, code = "...")]
LL | |
| |_^
error: suggestion without `applicability`
- --> $DIR/subdiagnostic-derive.rs:414:1
+ --> $DIR/subdiagnostic-derive.rs:402:1
|
LL | / #[suggestion(parser::add_paren, code = "...")]
LL | |
| |_^
error: suggestion without `code = "..."`
- --> $DIR/subdiagnostic-derive.rs:422:1
+ --> $DIR/subdiagnostic-derive.rs:410:1
|
LL | / #[suggestion(parser::add_paren)]
LL | |
| |_^
error: invalid applicability
- --> $DIR/subdiagnostic-derive.rs:432:46
+ --> $DIR/subdiagnostic-derive.rs:420:46
|
LL | #[suggestion(parser::add_paren, code ="...", applicability = "foo")]
| ^^^^^^^^^^^^^^^^^^^^^
error: suggestion without `applicability`
- --> $DIR/subdiagnostic-derive.rs:450:1
+ --> $DIR/subdiagnostic-derive.rs:438:1
|
LL | / #[suggestion(parser::add_paren, code = "...")]
LL | |
| |_^
error: suggestion without `#[primary_span]` field
- --> $DIR/subdiagnostic-derive.rs:450:1
+ --> $DIR/subdiagnostic-derive.rs:438:1
|
LL | / #[suggestion(parser::add_paren, code = "...")]
LL | |
| |_^
error: unsupported type attribute for subdiagnostic enum
- --> $DIR/subdiagnostic-derive.rs:465:1
+ --> $DIR/subdiagnostic-derive.rs:453:1
|
LL | #[label]
| ^^^^^^^^
error: `var` doesn't refer to a field on this type
- --> $DIR/subdiagnostic-derive.rs:485:39
+ --> $DIR/subdiagnostic-derive.rs:473:39
|
LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")]
| ^^^^^^^
error: `var` doesn't refer to a field on this type
- --> $DIR/subdiagnostic-derive.rs:504:43
+ --> $DIR/subdiagnostic-derive.rs:492:43
|
LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")]
| ^^^^^^^
LL | #[label(slug)]
| ^^^^ not found in `rustc_errors::fluent`
-error: aborting due to 52 previous errors
+error: aborting due to 50 previous errors
For more information about this error, try `rustc --explain E0425`.
// Because we are compiling this code with `-C panic=abort`, this wouldn't normally be needed.
// However, `core` and `alloc` are both compiled with `-C panic=unwind`, which means that functions
-// in these libaries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't
+// in these libraries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't
// unwind. So, for this test case we will define the symbol.
#[lang = "eh_personality"]
extern fn rust_eh_personality() {}
// Because we are compiling this code with `-C panic=abort`, this wouldn't normally be needed.
// However, `core` and `alloc` are both compiled with `-C panic=unwind`, which means that functions
-// in these libaries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't
+// in these libraries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't
// unwind. So, for this test case we will define the symbol.
#[lang = "eh_personality"]
extern fn rust_eh_personality() {}
--> $DIR/type-check-1.rs:60:26
|
LL | asm!("{}", const 0 as *mut u8);
- | ^^^^^^^^^^^^ expected integer, found *-ptr
+ | ^^^^^^^^^^^^ expected integer, found `*mut u8`
|
= note: expected type `{integer}`
found raw pointer `*mut u8`
--> $DIR/type-check-1.rs:78:25
|
LL | global_asm!("{}", const 0 as *mut u8);
- | ^^^^^^^^^^^^ expected integer, found *-ptr
+ | ^^^^^^^^^^^^ expected integer, found `*mut u8`
|
= note: expected type `{integer}`
found raw pointer `*mut u8`
#![feature(associated_type_bounds)]
#![feature(anonymous_lifetime_in_impl_trait)]
-// The same thing should happen for constaints in dyn trait.
+// The same thing should happen for constraints in dyn trait.
fn f(x: &mut dyn Iterator<Item: Iterator<Item = &'_ ()>>) -> Option<&'_ ()> { x.next() }
//~^ ERROR missing lifetime specifier
//~| ERROR mismatched types
|
= note: expected unit type `()`
found fn item `fn() {<i8 as Foo<'static, 'static, u8>>::bar::<'static, char>}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
|
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>();
| ++
|
= note: expected unit type `()`
found fn item `fn() {<i8 as Foo<'static, 'static>>::bar::<'static, char>}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
|
LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>();
| ++
|
= note: expected unit type `()`
found fn item `fn() {<i8 as Foo<'static, 'static, u8>>::baz}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
|
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz();
| ++
|
= note: expected unit type `()`
found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::bar::<ReStatic, char>}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
|
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>();
| ++
|
= note: expected unit type `()`
found fn item `fn() {<i8 as Foo<ReStatic, ReStatic>>::bar::<ReStatic, char>}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
|
LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>();
| ++
|
= note: expected unit type `()`
found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::baz}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
|
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz();
| ++
+++ /dev/null
-#![feature(register_attr)]
-#![feature(register_tool)]
-
-#![register_attr] //~ ERROR malformed `register_attr` attribute input
-#![register_tool] //~ ERROR malformed `register_tool` attribute input
-
-#![register_attr(a::b)] //~ ERROR `register_attr` only accepts identifiers
-#![register_tool(a::b)] //~ ERROR `register_tool` only accepts identifiers
-
-#![register_attr(attr, attr)] //~ ERROR attribute `attr` was already registered
-#![register_tool(tool, tool)] //~ ERROR tool `tool` was already registered
-
-fn main() {}
+++ /dev/null
-error: `register_attr` only accepts identifiers
- --> $DIR/register-attr-tool-fail.rs:7:18
- |
-LL | #![register_attr(a::b)]
- | ^^^^ not an identifier
-
-error: attribute `attr` was already registered
- --> $DIR/register-attr-tool-fail.rs:10:24
- |
-LL | #![register_attr(attr, attr)]
- | ---- ^^^^
- | |
- | already registered here
-
-error: `register_tool` only accepts identifiers
- --> $DIR/register-attr-tool-fail.rs:8:18
- |
-LL | #![register_tool(a::b)]
- | ^^^^ not an identifier
-
-error: tool `tool` was already registered
- --> $DIR/register-attr-tool-fail.rs:11:24
- |
-LL | #![register_tool(tool, tool)]
- | ---- ^^^^
- | |
- | already registered here
-
-error: malformed `register_attr` attribute input
- --> $DIR/register-attr-tool-fail.rs:4:1
- |
-LL | #![register_attr]
- | ^^^^^^^^^^^^^^^^^ help: must be of the form: `#![register_attr(attr1, attr2, ...)]`
-
-error: malformed `register_tool` attribute input
- --> $DIR/register-attr-tool-fail.rs:5:1
- |
-LL | #![register_tool]
- | ^^^^^^^^^^^^^^^^^ help: must be of the form: `#![register_tool(tool1, tool2, ...)]`
-
-error: aborting due to 6 previous errors
-
+++ /dev/null
-// edition:2018
-// compile-flags: -Zsave-analysis
-// ~^ Also regression test for #69588
-
-#![feature(register_attr)]
-#![feature(register_tool)]
-
-#![register_attr(attr)]
-#![register_tool(tool)]
-
-use attr as renamed_attr; // OK
-use tool as renamed_tool; // OK
-
-#[renamed_attr] //~ ERROR cannot use an explicitly registered attribute through an import
-#[renamed_tool::attr] //~ ERROR cannot use a tool module through an import
- //~| ERROR cannot use a tool module through an import
-fn main() {}
+++ /dev/null
-error: cannot use an explicitly registered attribute through an import
- --> $DIR/register-attr-tool-import.rs:14:3
- |
-LL | #[renamed_attr]
- | ^^^^^^^^^^^^
- |
-note: the explicitly registered attribute imported here
- --> $DIR/register-attr-tool-import.rs:11:5
- |
-LL | use attr as renamed_attr; // OK
- | ^^^^^^^^^^^^^^^^^^^^
-
-error: cannot use a tool module through an import
- --> $DIR/register-attr-tool-import.rs:15:3
- |
-LL | #[renamed_tool::attr]
- | ^^^^^^^^^^^^
- |
-note: the tool module imported here
- --> $DIR/register-attr-tool-import.rs:12:5
- |
-LL | use tool as renamed_tool; // OK
- | ^^^^^^^^^^^^^^^^^^^^
-
-error: cannot use a tool module through an import
- --> $DIR/register-attr-tool-import.rs:15:3
- |
-LL | #[renamed_tool::attr]
- | ^^^^^^^^^^^^
- |
-note: the tool module imported here
- --> $DIR/register-attr-tool-import.rs:12:5
- |
-LL | use tool as renamed_tool; // OK
- | ^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 3 previous errors
-
+++ /dev/null
-#![feature(register_attr)]
-#![feature(register_tool)]
-
-#![register_attr(attr)]
-#![register_tool(tool)]
-
-#[no_implicit_prelude]
-mod m {
- #[attr] //~ ERROR cannot find attribute `attr` in this scope
- #[tool::attr] //~ ERROR failed to resolve: use of undeclared crate or module `tool`
- fn check() {}
-}
-
-fn main() {}
+++ /dev/null
-error[E0433]: failed to resolve: use of undeclared crate or module `tool`
- --> $DIR/register-attr-tool-prelude.rs:10:7
- |
-LL | #[tool::attr]
- | ^^^^ use of undeclared crate or module `tool`
-
-error: cannot find attribute `attr` in this scope
- --> $DIR/register-attr-tool-prelude.rs:9:7
- |
-LL | #[attr]
- | ^^^^
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0433`.
+++ /dev/null
-#![deny(unused)]
-
-#![feature(register_attr)]
-#![feature(register_tool)]
-
-#[register_attr(attr)] //~ ERROR crate-level attribute should be an inner attribute
-#[register_tool(tool)] //~ ERROR crate-level attribute should be an inner attribute
-fn main() {}
+++ /dev/null
-error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/register-attr-tool-unused.rs:6:1
- |
-LL | #[register_attr(attr)]
- | ^^^^^^^^^^^^^^^^^^^^^^
- |
-note: the lint level is defined here
- --> $DIR/register-attr-tool-unused.rs:1:9
- |
-LL | #![deny(unused)]
- | ^^^^^^
- = note: `#[deny(unused_attributes)]` implied by `#[deny(unused)]`
-
-error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
- --> $DIR/register-attr-tool-unused.rs:7:1
- |
-LL | #[register_tool(tool)]
- | ^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 2 previous errors
-
+++ /dev/null
-// check-pass
-// compile-flags: --cfg foo
-
-#![feature(register_attr)]
-#![feature(register_tool)]
-
-#![register_attr(attr)]
-#![register_tool(tool)]
-#![register_tool(rustfmt, clippy)] // OK
-#![cfg_attr(foo, register_attr(conditional_attr))]
-#![cfg_attr(foo, register_tool(conditional_tool))]
-
-#[attr]
-#[tool::attr]
-#[rustfmt::attr]
-#[clippy::attr]
-#[conditional_attr]
-#[conditional_tool::attr]
-fn main() {}
| --- ^^ - _
| |
| for<'r> fn(&'r i32) -> &'r i32 {foo}
+ |
+help: use parentheses to call this function
+ |
+LL | if foo(/* &i32 */) == y {}
+ | ++++++++++++
error: aborting due to previous error
--- /dev/null
+fn main() {
+ let mut vec: Vec<i32> = Vec::new();
+ let closure = move || {
+ vec.clear();
+ let mut iter = vec.iter();
+ move || { iter.next() } //~ ERROR captured variable cannot escape `FnMut` closure bod
+ };
+}
--- /dev/null
+error: captured variable cannot escape `FnMut` closure body
+ --> $DIR/do-not-suggest-adding-move-when-closure-is-already-marked-as-move.rs:6:9
+ |
+LL | let mut vec: Vec<i32> = Vec::new();
+ | ------- variable defined here
+LL | let closure = move || {
+ | - inferred to be a `FnMut` closure
+LL | vec.clear();
+ | --- variable captured here
+LL | let mut iter = vec.iter();
+LL | move || { iter.next() }
+ | ^^^^^^^^^^^^^^^^^^^^^^^ returns a closure that contains a reference to a captured variable, which then escapes the closure body
+ |
+ = note: `FnMut` closures only have access to their captured variables while they are executing...
+ = note: ...therefore, they cannot allow references to captured variables to escape
+
+error: aborting due to previous error
+
let mut i = I(10);
i[i[3]] = 4;
//~^ ERROR cannot borrow `i` as immutable because it is also borrowed as mutable [E0502]
- // Shoud be accepted with g2p
+ // Should be accepted with g2p
i[3] = i[4];
i[i[3]] = i[4];
//~^ ERROR cannot borrow `i` as immutable because it is also borrowed as mutable [E0502]
- // Shoud be accepted with g2p
+ // Should be accepted with g2p
}
fn main() {
//
// (At least in theory; part of the reason this test fails is that
// the constructed MIR throws in extra &mut reborrows which
- // flummoxes our attmpt to delay the activation point here.)
+ // flummoxes our attempt to delay the activation point here.)
delay.push(2);
}
-error: invalid `--check-cfg` argument: `names("NOT_IDENT")` (`names()` arguments must be simple identifers)
+error: invalid `--check-cfg` argument: `names("NOT_IDENT")` (`names()` arguments must be simple identifiers)
-error: invalid `--check-cfg` argument: `values("NOT_IDENT")` (`values()` first argument must be a simple identifer)
+error: invalid `--check-cfg` argument: `values("NOT_IDENT")` (`values()` first argument must be a simple identifier)
arr[1] += 10;
};
- // c will capture `arr` completely, therefore we cannot borrow other indecies
+ // c will capture `arr` completely, therefore we cannot borrow other indices
// into the array.
println!("{:#?}", &arr[3..2]);
//~^ ERROR: cannot borrow `arr` as immutable because it is also borrowed as mutable
c();
}
-// `String`, `u16` are not aligned at a one byte boundry and are thus affected by repr(packed).
+// `String`, `u16` are not aligned at a one byte boundary and are thus affected by repr(packed).
//
// Here we test that the closure doesn't capture a reference point to `foo.x` but
// rather capture `foo` entirely.
// edition:2021
// run-pass
-// Test that ByValue captures compile sucessefully especially when the captures are
-// derefenced within the closure.
+// Test that ByValue captures compile successfully especially when the captures are
+// dereferenced within the closure.
#[derive(Debug, Default)]
struct SomeLargeType;
// edition:2021
// run-pass
-// Tests that if a closure uses indivual fields of the same object
+// Tests that if a closure uses individual fields of the same object
// then that case is handled properly.
#![allow(unused)]
// edition:2021
// run-pass
-// Test that closures can catpure paths that are more precise than just one level
+// Test that closures can capture paths that are more precise than just one level
// from the root variable.
//
-// If the closures can handle such precison we should be able to mutate one path in the closure
+// If the closures can handle such precision we should be able to mutate one path in the closure
// while being able to mutate another path outside the closure, where the two paths are disjoint
// after applying two projections on the root variable.
#![allow(unused)]
-// If the closures can handle such precison we should be able to read one path in the closure
+// If the closures can handle such precision we should be able to read one path in the closure
// while being able mutate another path outside the closure, where the two paths are disjoint
// after applying two projections on the root variable.
// that is captured by the closure
// More specifically we test that the if the mutable reference isn't root variable of a capture
-// but rather accessed while acessing the precise capture.
+// but rather accessed while accessing the precise capture.
fn mut_tuple() {
let mut t = (10, 10);
// test for issue 84128
-// missing suggestion for similar ADT type with diffetent generic paramenter
+// missing suggestion for similar ADT type with diffetent generic parameter
// on closure ReturnNoExpression
struct Foo<T>(T);
let exe = me.file_name().unwrap();
let cwd = me.parent().unwrap();
eprintln!("cwd={:?}", cwd);
- // Change directory to where the exectuable is located, since this test
+ // Change directory to where the executable is located, since this test
// fundamentally needs to use relative paths. In some cases (like
// remote-test-server), the current_dir can be somewhere else, so make
// sure it is something we can use. We assume we can write to this
--> $DIR/invalid-const-arg-for-type-param.rs:6:23
|
LL | let _: u32 = 5i32.try_into::<32>().unwrap();
- | ^^^^^^^^------ help: remove these generics
- | |
- | expected 0 generic arguments
+ | ^^^^^^^^ expected 0 generic arguments
|
note: associated function defined here, with 0 generic parameters
--> $SRC_DIR/core/src/convert/mod.rs:LL:COL
|
LL | fn try_into(self) -> Result<T, Self::Error>;
| ^^^^^^^^
+help: consider moving this generic argument to the `TryInto` trait, which takes up to 1 argument
+ |
+LL | let _: u32 = TryInto::<32>::try_into(5i32).unwrap();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: remove these generics
+ |
+LL - let _: u32 = 5i32.try_into::<32>().unwrap();
+LL + let _: u32 = 5i32.try_into().unwrap();
+ |
error[E0599]: no method named `f` found for struct `S` in the current scope
--> $DIR/invalid-const-arg-for-type-param.rs:9:7
// regression test for #83466- tests that generic arg mismatch errors between
-// consts and types are not supressed when there are explicit late bound lifetimes
+// consts and types are not suppressed when there are explicit late bound lifetimes
struct S;
impl S {
--> $DIR/forbidden_slices.rs:27:1
|
LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, size_of::<&u32>()) };
- | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>: encountered a pointer, but expected plain (non-pointer) bytes
+ | ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
|
- = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: the raw bytes of the constant (size: 8, align: 4) {
╾─ALLOC_ID─╼ 04 00 00 00 │ ╾──╼....
}
--> $DIR/forbidden_slices.rs:57:1
|
LL | pub static R5: &[u8] = unsafe {
- | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>: encountered a pointer, but expected plain (non-pointer) bytes
+ | ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
|
- = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: the raw bytes of the constant (size: 8, align: 4) {
╾ALLOC_ID─╼ 04 00 00 00 │ ╾──╼....
}
--> $DIR/forbidden_slices.rs:27:1
|
LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, size_of::<&u32>()) };
- | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>: encountered a pointer, but expected plain (non-pointer) bytes
+ | ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
|
- = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: the raw bytes of the constant (size: 16, align: 8) {
╾───────ALLOC_ID───────╼ 08 00 00 00 00 00 00 00 │ ╾──────╼........
}
--> $DIR/forbidden_slices.rs:57:1
|
LL | pub static R5: &[u8] = unsafe {
- | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>: encountered a pointer, but expected plain (non-pointer) bytes
+ | ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
|
- = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: the raw bytes of the constant (size: 16, align: 8) {
╾──────ALLOC_ID───────╼ 08 00 00 00 00 00 00 00 │ ╾──────╼........
}
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:30:43
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:34:45
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:38:45
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:42:45
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: evaluation of constant value failed
--> $DIR/const-pointer-values-in-various-types.rs:46:47
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:54:45
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:58:45
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:62:45
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: evaluation of constant value failed
--> $DIR/const-pointer-values-in-various-types.rs:66:47
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:74:45
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:78:47
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:82:47
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:86:39
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:90:41
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:94:41
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:98:41
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:102:43
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:106:39
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:110:41
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:114:41
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:118:41
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:122:43
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:126:41
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:130:41
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:134:43
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/const-pointer-values-in-various-types.rs:138:43
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: aborting due to 29 previous errors
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: aborting due to previous error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: could not evaluate constant pattern
--> $DIR/ref_to_int_match.rs:7:14
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: could not evaluate constant pattern
--> $DIR/ref_to_int_match.rs:7:14
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/ub-enum.rs:30:1
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-enum.rs:43:1
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/ub-enum.rs:49:1
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: evaluation of constant value failed
--> $DIR/ub-enum.rs:59:42
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-enum.rs:82:1
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/ub-enum.rs:30:1
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-enum.rs:43:1
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/ub-enum.rs:49:1
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: evaluation of constant value failed
--> $DIR/ub-enum.rs:59:42
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-enum.rs:82:1
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
//~^ ERROR is undefined behavior
// All variants are uninhabited but also have data.
-// Use `0` as constant to make behavior endianess-independent.
+// Use `0` as constant to make behavior endianness-independent.
const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) };
//~^ ERROR evaluation of constant value failed
const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) };
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/ub-ref-ptr.rs:35:39
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/ub-ref-ptr.rs:35:38
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/ub-ref-ptr.rs:41:85
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/ub-ref-ptr.rs:35:39
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/ub-ref-ptr.rs:35:38
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/ub-ref-ptr.rs:41:85
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/ub-wide-ptr.rs:46:1
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:49:1
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:80:1
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:88:1
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/ub-wide-ptr.rs:46:1
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:49:1
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:80:1
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:88:1
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
const INVALID_PTR_IN_INT: () = unsafe {
let _x: usize = transmute(&3u8);
- //[with_flag]~^ ERROR: evaluation of constant value failed
- //[with_flag]~| invalid value
+ //[with_flag]~^ ERROR: any use of this value will cause an error
+ //[with_flag]~| previously accepted
};
const INVALID_SLICE_TO_USIZE_TRANSMUTE: () = unsafe {
let x: &[u8] = &[0; 32];
let _x: (usize, usize) = transmute(x);
- //[with_flag]~^ ERROR: evaluation of constant value failed
- //[with_flag]~| invalid value
+ //[with_flag]~^ ERROR: any use of this value will cause an error
+ //[with_flag]~| previously accepted
};
const UNALIGNED_PTR: () = unsafe {
LL | let _x: bool = transmute(3u8);
| ^^^^^^^^^^^^^^ constructing invalid value: encountered 0x03, but expected a boolean
-error[E0080]: evaluation of constant value failed
+error: any use of this value will cause an error
--> $DIR/detect-extra-ub.rs:15:21
|
+LL | const INVALID_PTR_IN_INT: () = unsafe {
+ | ----------------------------
LL | let _x: usize = transmute(&3u8);
- | ^^^^^^^^^^^^^^^ constructing invalid value: encountered (potentially part of) a pointer, but expected an integer
+ | ^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
+ |
+ = note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
-error[E0080]: evaluation of constant value failed
+error: any use of this value will cause an error
--> $DIR/detect-extra-ub.rs:22:30
|
+LL | const INVALID_SLICE_TO_USIZE_TRANSMUTE: () = unsafe {
+ | ------------------------------------------
+LL | let x: &[u8] = &[0; 32];
LL | let _x: (usize, usize) = transmute(x);
- | ^^^^^^^^^^^^ constructing invalid value at .0: encountered (potentially part of) a pointer, but expected an integer
+ | ^^^^^^^^^^^^ unable to turn pointer into raw bytes
+ |
+ = 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: evaluation of constant value failed
--> $DIR/detect-extra-ub.rs:28:20
LL | INNER;
| ^^^^^ referenced constant has errors
|
- = note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
For more information about this error, try `rustc --explain E0080`.
Future incompatibility report: Future breakage diagnostic:
+error: any use of this value will cause an error
+ --> $DIR/detect-extra-ub.rs:15:21
+ |
+LL | const INVALID_PTR_IN_INT: () = unsafe {
+ | ----------------------------
+LL | let _x: usize = transmute(&3u8);
+ | ^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
+ |
+ = note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
+
+Future breakage diagnostic:
+error: any use of this value will cause an error
+ --> $DIR/detect-extra-ub.rs:22:30
+ |
+LL | const INVALID_SLICE_TO_USIZE_TRANSMUTE: () = unsafe {
+ | ------------------------------------------
+LL | let x: &[u8] = &[0; 32];
+LL | let _x: (usize, usize) = transmute(x);
+ | ^^^^^^^^^^^^ unable to turn pointer into raw bytes
+ |
+ = note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
+
+Future breakage diagnostic:
error: any use of this value will cause an error
--> $DIR/detect-extra-ub.rs:34:5
|
--> $DIR/issue-83182.rs:5:1
|
LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[&()]) };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>.0: encountered a pointer in `str`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
|
- = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: the raw bytes of the constant (size: 8, align: 4) {
╾─alloc4──╼ 01 00 00 00 │ ╾──╼....
}
--> $DIR/issue-83182.rs:5:1
|
LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[&()]) };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>.0: encountered a pointer in `str`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
|
- = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: the raw bytes of the constant (size: 16, align: 8) {
╾───────alloc4────────╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........
}
struct MyStr(str);
const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[&()]) };
//~^ ERROR: it is undefined behavior to use this value
-//~| constructing invalid value at .<deref>.0: encountered a pointer in `str`
fn main() {}
// error-pattern unable to turn pointer into raw bytes
+// normalize-stderr-test: "alloc[0-9]+\+0x[a-z0-9]+" -> "ALLOC"
#![feature(const_ptr_read)]
const C: () = unsafe {
LL | copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
- | unable to turn pointer into raw bytes
+ | unable to copy parts of a pointer from memory at ALLOC
| inside `std::ptr::read::<u8>` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
| inside `ptr::const_ptr::<impl *const u8>::read` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
- | inside `C` at $DIR/issue-miri-1910.rs:7:5
+ | inside `C` at $DIR/issue-miri-1910.rs:8:5
|
- ::: $DIR/issue-miri-1910.rs:4:1
+ ::: $DIR/issue-miri-1910.rs:5:1
|
LL | const C: () = unsafe {
| -----------
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: aborting due to previous error
LL | copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
- | unable to turn pointer into raw bytes
+ | unable to copy parts of a pointer from memory at ALLOC
| inside `std::ptr::read::<u8>` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
| inside `ptr::const_ptr::<impl *const u8>::read` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
- | inside `C` at $DIR/issue-miri-1910.rs:7:5
+ | inside `C` at $DIR/issue-miri-1910.rs:8:5
|
- ::: $DIR/issue-miri-1910.rs:4:1
+ ::: $DIR/issue-miri-1910.rs:5:1
|
LL | const C: () = unsafe {
| -----------
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
static PTR_INT_CAST: () = {
let x = &0 as *const _ as usize;
//~^ ERROR could not evaluate static initializer
- //~| "exposing pointers" needs an rfc before being allowed inside constants
+ //~| exposing pointers
let _v = x == x;
};
//~| unable to turn pointer into raw bytes
};
+// I'd love to test pointer comparison, but that is not possible since
+// their `PartialEq` impl is non-`const`.
+
fn main() {}
--> $DIR/ptr_arith.rs:9:13
|
LL | let x = &0 as *const _ as usize;
- | ^^^^^^^^^^^^^^^^^^^^^^^ "exposing pointers" needs an rfc before being allowed inside constants
+ | ^^^^^^^^^^^^^^^^^^^^^^^ exposing pointers is not possible at compile-time
error[E0080]: could not evaluate static initializer
--> $DIR/ptr_arith.rs:17:14
|
LL | let _v = x + 0;
| ^ unable to turn pointer into raw bytes
+ |
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
warning: skipping const checks
|
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: any use of this value will cause an error
--> $DIR/ptr_comparisons.rs:70:27
|
= 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: aborting due to 4 previous errors
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
Future breakage diagnostic:
error: any use of this value will cause an error
= note: `#[deny(const_err)]` 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `Clone`
-help: one of the expressions' fields has a method of the same name
- |
-LL | let _y = x.i.clone();
- | ++
error: aborting due to previous error
--> $DIR/dst-bad-coercions.rs:14:17
|
LL | let y: &S = x;
- | -- ^ expected `&S`, found *-ptr
+ | -- ^ expected `&S`, found `*const S`
| |
| expected due to this
|
--> $DIR/dst-bad-coercions.rs:15:21
|
LL | let y: &dyn T = x;
- | ------ ^ expected `&dyn T`, found *-ptr
+ | ------ ^ expected `&dyn T`, found `*const S`
| |
| expected due to this
|
--> $DIR/dst-bad-coercions.rs:19:17
|
LL | let y: &S = x;
- | -- ^ expected `&S`, found *-ptr
+ | -- ^ expected `&S`, found `*mut S`
| |
| expected due to this
|
--> $DIR/dst-bad-coercions.rs:20:21
|
LL | let y: &dyn T = x;
- | ------ ^ expected `&dyn T`, found *-ptr
+ | ------ ^ expected `&dyn T`, found `*mut S`
| |
| expected due to this
|
// check-pass
+#![feature(let_chains)]
+
#[cfg(FALSE)]
fn foo() {
#[attr]
fn a() {
if let x = 1 && i = 2 {}
//~^ ERROR cannot find value `i` in this scope
+ //~| ERROR `let` expressions in this position are unstable
//~| ERROR mismatched types
//~| ERROR `let` expressions are not supported here
}
| ^ not found in this scope
error[E0425]: cannot find value `i` in this scope
- --> $DIR/bad-if-let-suggestion.rs:12:9
+ --> $DIR/bad-if-let-suggestion.rs:13:9
|
LL | fn a() {
| ------ similarly named function `a` defined here
| ^ help: a function with a similar name exists: `a`
error[E0425]: cannot find value `j` in this scope
- --> $DIR/bad-if-let-suggestion.rs:12:13
+ --> $DIR/bad-if-let-suggestion.rs:13:13
|
LL | fn a() {
| ------ similarly named function `a` defined here
| ^ help: a function with a similar name exists: `a`
error[E0425]: cannot find value `i` in this scope
- --> $DIR/bad-if-let-suggestion.rs:12:18
+ --> $DIR/bad-if-let-suggestion.rs:13:18
|
LL | fn a() {
| ------ similarly named function `a` defined here
| ^ help: a function with a similar name exists: `a`
error[E0425]: cannot find value `x` in this scope
- --> $DIR/bad-if-let-suggestion.rs:19:8
+ --> $DIR/bad-if-let-suggestion.rs:20:8
|
LL | fn a() {
| ------ similarly named function `a` defined here
LL | if x[0] = 1 {}
| ^ help: a function with a similar name exists: `a`
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/bad-if-let-suggestion.rs:5:8
+ |
+LL | if let x = 1 && i = 2 {}
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
error[E0308]: mismatched types
--> $DIR/bad-if-let-suggestion.rs:5:8
|
LL | if let x = 1 && i = 2 {}
| ^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
-error: aborting due to 7 previous errors
+error: aborting due to 8 previous errors
-Some errors have detailed explanations: E0308, E0425.
+Some errors have detailed explanations: E0308, E0425, E0658.
For more information about an error, try `rustc --explain E0308`.
| this trait cannot be made into an object...
error[E0038]: the trait `NonObjectSafe2` cannot be made into an object
- --> $DIR/feature-gate-object_safe_for_dispatch.rs:22:36
+ --> $DIR/feature-gate-object_safe_for_dispatch.rs:22:45
|
LL | fn return_non_object_safe_ref() -> &'static dyn NonObjectSafe2 {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NonObjectSafe2` cannot be made into an object
+ | ^^^^^^^^^^^^^^^^^^ `NonObjectSafe2` cannot be made into an object
|
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/feature-gate-object_safe_for_dispatch.rs:7:8
= help: consider moving `foo` to another trait
error[E0038]: the trait `NonObjectSafe4` cannot be made into an object
- --> $DIR/feature-gate-object_safe_for_dispatch.rs:31:35
+ --> $DIR/feature-gate-object_safe_for_dispatch.rs:31:47
|
LL | fn return_non_object_safe_rc() -> std::rc::Rc<dyn NonObjectSafe4> {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NonObjectSafe4` cannot be made into an object
+ | ^^^^^^^^^^^^^^^^^^ `NonObjectSafe4` cannot be made into an object
|
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/feature-gate-object_safe_for_dispatch.rs:15:22
+++ /dev/null
-#![register_attr(attr)] //~ ERROR the `#[register_attr]` attribute is an experimental feature
-
-fn main() {}
+++ /dev/null
-error[E0658]: the `#[register_attr]` attribute is an experimental feature
- --> $DIR/feature-gate-register_attr.rs:1:1
- |
-LL | #![register_attr(attr)]
- | ^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #66080 <https://github.com/rust-lang/rust/issues/66080> for more information
- = help: add `#![feature(register_attr)]` to the crate attributes to enable
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0658`.
// doesn't exist.
println!("{:.*}");
//~^ ERROR 2 positional arguments in format string, but no arguments were given
+ println!("{:.0$}");
+ //~^ ERROR 1 positional argument in format string, but no arguments were given
}
= note: positional arguments are zero-based
= note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html
+error: 1 positional argument in format string, but no arguments were given
+ --> $DIR/ifmt-bad-arg.rs:97:15
+ |
+LL | println!("{:.0$}");
+ | ^^---^
+ | |
+ | this precision flag expects an `usize` argument at position 0, but no arguments were given
+ |
+ = note: positional arguments are zero-based
+ = note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html
+
error[E0425]: cannot find value `foo` in this scope
--> $DIR/ifmt-bad-arg.rs:27:18
|
| ^^^^^^^^^^
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
-error: aborting due to 37 previous errors
+error: aborting due to 38 previous errors
Some errors have detailed explanations: E0308, E0425.
For more information about an error, try `rustc --explain E0308`.
| |
| fn() {f}
|
-help: you might have forgotten to call this function
+help: use parentheses to call these
|
-LL | let x = f() == g;
- | ++
-help: you might have forgotten to call this function
- |
-LL | let x = f == g();
- | ++
+LL | let x = f() == g();
+ | ++ ++
error[E0308]: mismatched types
--> $DIR/fn-compare-mismatch.rs:4:18
|
= note: expected unit type `()`
found struct `Box<dyn FnOnce(isize)>`
+help: use parentheses to call this trait object
+ |
+LL | let _: () = (Box::new(|_: isize| {}) as Box<dyn FnOnce(isize)>)(/* isize */);
+ | + ++++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-trait-formatting.rs:10:17
|
= note: expected unit type `()`
found struct `Box<dyn Fn(isize, isize)>`
+help: use parentheses to call this trait object
+ |
+LL | let _: () = (Box::new(|_: isize, isize| {}) as Box<dyn Fn(isize, isize)>)(/* isize */, /* isize */);
+ | + +++++++++++++++++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-trait-formatting.rs:14:17
// This test case checks the behavior of typeck::check::method::suggest::is_fn on Ty::Error.
+
+struct Foo;
+
+trait Bar {
+ //~^ NOTE `Bar` defines an item `bar`, perhaps you need to implement it
+ //~| NOTE `Bar` defines an item `bar`, perhaps you need to implement it
+ fn bar(&self) {}
+}
+
+impl Bar for Foo {}
+
fn main() {
let arc = std::sync::Arc::new(oops);
//~^ ERROR cannot find value `oops` in this scope
//~| NOTE not found
- // The error "note: this is a function, perhaps you wish to call it" MUST NOT appear.
- arc.blablabla();
- //~^ ERROR no method named `blablabla`
+ arc.bar();
+ //~^ ERROR no method named `bar`
//~| NOTE method not found
- let arc2 = std::sync::Arc::new(|| 1);
- // The error "note: this is a function, perhaps you wish to call it" SHOULD appear
- arc2.blablabla();
- //~^ ERROR no method named `blablabla`
+ //~| HELP items from traits can only be used if the trait is implemented and in scope
+
+ let arc2 = std::sync::Arc::new(|| Foo);
+ arc2.bar();
+ //~^ ERROR no method named `bar`
//~| NOTE method not found
- //~| NOTE this is a function, perhaps you wish to call it
+ //~| HELP items from traits can only be used if the trait is implemented and in scope
+ //~| HELP use parentheses to call this closure
}
error[E0425]: cannot find value `oops` in this scope
- --> $DIR/fn-help-with-err.rs:3:35
+ --> $DIR/fn-help-with-err.rs:14:35
|
LL | let arc = std::sync::Arc::new(oops);
| ^^^^ not found in this scope
-error[E0599]: no method named `blablabla` found for struct `Arc<_>` in the current scope
- --> $DIR/fn-help-with-err.rs:7:9
+error[E0599]: no method named `bar` found for struct `Arc<_>` in the current scope
+ --> $DIR/fn-help-with-err.rs:17:9
|
-LL | arc.blablabla();
- | ^^^^^^^^^ method not found in `Arc<_>`
+LL | arc.bar();
+ | ^^^ method not found in `Arc<_>`
+ |
+ = help: items from traits can only be used if the trait is implemented and in scope
+note: `Bar` defines an item `bar`, perhaps you need to implement it
+ --> $DIR/fn-help-with-err.rs:5:1
+ |
+LL | trait Bar {
+ | ^^^^^^^^^
-error[E0599]: no method named `blablabla` found for struct `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:38]>` in the current scope
- --> $DIR/fn-help-with-err.rs:12:10
+error[E0599]: no method named `bar` found for struct `Arc<[closure@$DIR/fn-help-with-err.rs:22:36: 22:38]>` in the current scope
+ --> $DIR/fn-help-with-err.rs:23:10
+ |
+LL | arc2.bar();
+ | ^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:22:36: 22:38]>`
+ |
+ = help: items from traits can only be used if the trait is implemented and in scope
+note: `Bar` defines an item `bar`, perhaps you need to implement it
+ --> $DIR/fn-help-with-err.rs:5:1
+ |
+LL | trait Bar {
+ | ^^^^^^^^^
+help: use parentheses to call this closure
|
-LL | arc2.blablabla();
- | ---- ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:38]>`
- | |
- | this is a function, perhaps you wish to call it
+LL | arc2().bar();
+ | ++
error: aborting due to 3 previous errors
--- /dev/null
+#![feature(generic_associated_types)]
+
+pub trait LendingIterator {
+ type Item<'a>
+ where
+ Self: 'a;
+
+ fn consume<F>(self, _f: F)
+ where
+ Self: Sized,
+ for<'a> Self::Item<'a>: FuncInput<'a, Self::Item<'a>>,
+ {
+ }
+}
+
+impl<I: LendingIterator + ?Sized> LendingIterator for &mut I {
+ type Item<'a> = I::Item<'a> where Self: 'a;
+}
+struct EmptyIter;
+impl LendingIterator for EmptyIter {
+ type Item<'a> = &'a mut () where Self:'a;
+}
+pub trait FuncInput<'a, F>
+where
+ F: Foo<Self>,
+ Self: Sized,
+{
+}
+impl<'a, T, F: 'a> FuncInput<'a, F> for T where F: Foo<T> {}
+trait Foo<T> {}
+
+fn map_test() {
+ (&mut EmptyIter).consume(());
+ //~^ ERROR the trait bound `for<'a> &'a mut (): Foo<&'a mut ()>` is not satisfied
+}
+
+fn main() {}
--- /dev/null
+error[E0277]: the trait bound `for<'a> &'a mut (): Foo<&'a mut ()>` is not satisfied
+ --> $DIR/issue-101020.rs:33:5
+ |
+LL | (&mut EmptyIter).consume(());
+ | ^^^^^^^^^^^^^^^^ ------- required by a bound introduced by this call
+ | |
+ | the trait `for<'a> Foo<&'a mut ()>` is not implemented for `&'a mut ()`
+ |
+note: required for `&'a mut ()` to implement `for<'a> FuncInput<'a, &'a mut ()>`
+ --> $DIR/issue-101020.rs:29:20
+ |
+LL | impl<'a, T, F: 'a> FuncInput<'a, F> for T where F: Foo<T> {}
+ | ^^^^^^^^^^^^^^^^ ^
+note: required by a bound in `LendingIterator::consume`
+ --> $DIR/issue-101020.rs:11:33
+ |
+LL | fn consume<F>(self, _f: F)
+ | ------- required by a bound in this
+...
+LL | for<'a> Self::Item<'a>: FuncInput<'a, Self::Item<'a>>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `LendingIterator::consume`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
-// Test for diagnostics when we have mismatched lifetime due to implict 'static lifetime in GATs
+// Test for diagnostics when we have mismatched lifetime due to implicit 'static lifetime in GATs
// check-fail
--- /dev/null
+fn opaque<T>(t: T) -> impl Sized {
+ //~^ ERROR cannot resolve opaque type
+ //~| WARNING function cannot return without recursing
+ opaque(Some(t))
+}
+
+#[allow(dead_code)]
+fn main() {}
--- /dev/null
+warning: function cannot return without recursing
+ --> $DIR/issue-100075-2.rs:1:1
+ |
+LL | fn opaque<T>(t: T) -> impl Sized {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
+...
+LL | opaque(Some(t))
+ | --------------- recursive call site
+ |
+ = note: `#[warn(unconditional_recursion)]` on by default
+ = help: a `loop` may express intention better if this is on purpose
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/issue-100075-2.rs:1:23
+ |
+LL | fn opaque<T>(t: T) -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+...
+LL | opaque(Some(t))
+ | --------------- returning here with type `impl Sized`
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0720`.
--- /dev/null
+trait Marker {}
+impl<T> Marker for T {}
+
+fn maybe<T>(
+ _t: T,
+) -> Option<
+ //removing the line below makes it compile
+ &'static T,
+> {
+ None
+}
+
+fn _g<T>(t: &'static T) -> &'static impl Marker {
+ //~^ ERROR cannot resolve opaque type
+ if let Some(t) = maybe(t) {
+ return _g(t);
+ }
+ todo!()
+}
+
+fn main() {}
--- /dev/null
+error[E0720]: cannot resolve opaque type
+ --> $DIR/issue-100075.rs:13:37
+ |
+LL | fn _g<T>(t: &'static T) -> &'static impl Marker {
+ | ^^^^^^^^^^^ recursive opaque type
+...
+LL | return _g(t);
+ | ----- returning here with type `&impl Marker`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0720`.
--- /dev/null
+// edition:2021
+
+fn main() {}
+
+struct Error;
+struct Okay;
+
+fn foo(t: Result<Okay, Error>) {
+ t.and_then(|t| -> _ { bar(t) });
+ //~^ ERROR mismatched types
+}
+
+async fn bar(t: Okay) {}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/issue-99914.rs:9:27
+ |
+LL | t.and_then(|t| -> _ { bar(t) });
+ | ^^^^^^ expected enum `Result`, found opaque type
+ |
+note: while checking the return type of the `async fn`
+ --> $DIR/issue-99914.rs:13:23
+ |
+LL | async fn bar(t: Okay) {}
+ | ^ checked the `Output` of this `async fn`, found opaque type
+ = note: expected enum `Result<_, Error>`
+ found opaque type `impl Future<Output = ()>`
+help: try wrapping the expression in `Ok`
+ |
+LL | t.and_then(|t| -> _ { Ok(bar(t)) });
+ | +++ +
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
| +++++++++++++++++
error[E0038]: the trait `NotObjectSafe` cannot be made into an object
- --> $DIR/object-unsafe-trait-in-return-position-dyn-trait.rs:28:13
+ --> $DIR/object-unsafe-trait-in-return-position-dyn-trait.rs:28:17
|
LL | fn cat() -> Box<dyn NotObjectSafe> {
- | ^^^^^^^^^^^^^^^^^^^^^^ `NotObjectSafe` cannot be made into an object
+ | ^^^^^^^^^^^^^^^^^ `NotObjectSafe` cannot be made into an object
|
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/object-unsafe-trait-in-return-position-dyn-trait.rs:3:8
|
= note: expected type `i32`
found opaque type `impl Fn() -> i32`
-help: use parentheses to call this closure
+help: use parentheses to call this opaque type
|
LL | opaque()()
| ++
// ever hands f_A off to instances of GaspA, and thus one should
// be able to prove the invariant that f_A is *only* invoked from
// from an instance of GaspA (either via the GaspA drop
- // implementation or the E drop implementaton). Yet the old (bad)
+ // implementation or the E drop implementation). Yet the old (bad)
// behavior allowed a call to f_A to leak in while we are tearing
// down a value of type GaspB.
}
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `Clone`
-help: one of the expressions' fields has a method of the same name
- |
-LL | let _d = c.x.clone();
- | ++
error: aborting due to previous error
found fn item `fn(u32) -> Foo {Foo}`
help: use parentheses to instantiate this tuple struct
|
-LL | fn test() -> Foo { Foo(_) }
- | +++
+LL | fn test() -> Foo { Foo(/* u32 */) }
+ | +++++++++++
error: aborting due to previous error
--> $DIR/issue-57362-1.rs:20:7
|
LL | a.f();
- | - ^ method not found in `fn(&u8)`
- | |
- | this is a function, perhaps you wish to call it
+ | ^ method not found in `fn(&u8)`
|
= help: items from traits can only be used if the trait is implemented and in scope
note: `Trait` defines an item `f`, perhaps you need to implement it
| |
| fn() -> i32 {foo}
|
-help: you might have forgotten to call this function
+help: use parentheses to call this function
|
LL | foo() > 12;
| ++
| |
| fn(i64) -> i64 {bar}
|
-help: you might have forgotten to call this function
+help: use parentheses to call this function
|
-LL | bar( /* arguments */ ) > 13;
- | +++++++++++++++++++
+LL | bar(/* i64 */) > 13;
+ | +++++++++++
error[E0308]: mismatched types
--> $DIR/issue-59488.rs:18:11
| |
| fn() -> i32 {foo}
|
-help: you might have forgotten to call this function
+help: use parentheses to call these
|
-LL | foo() > foo;
- | ++
-help: you might have forgotten to call this function
- |
-LL | foo > foo();
- | ++
+LL | foo() > foo();
+ | ++ ++
error[E0369]: binary operation `>` cannot be applied to type `fn() -> i32 {foo}`
--> $DIR/issue-59488.rs:25:9
| {integer}
|
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: you might have forgotten to call this function
- --> $SRC_DIR/core/src/macros/mod.rs:LL:COL
- |
-LL | if !(*left_val() == *right_val) {
- | ++
error[E0308]: mismatched types
--> $DIR/issue-70724-add_type_neq_err_label-unwrap.rs:6:5
| ^^^^^^^^^^^^^^^^ expected fn item, found integer
|
= note: expected fn item `fn() -> i32 {a}`
- found type `i32`
+ found type `{integer}`
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: `fn() -> i32 {a}` doesn't implement `Debug`
// run-pass
//! This snippet causes the type length to blowup exponentially,
-//! so check that we don't accidentially exceed the type length limit.
+//! so check that we don't accidentally exceed the type length limit.
// FIXME: Once the size of iterator adaptors is further reduced,
// increase the complexity of this test.
use std::collections::VecDeque;
// If you turn off deduplicate diagnostics (which rustc turns on by default but
// compiletest turns off when it runs ui tests), then the errors are
// (unfortunately) repeated here because the checking is done as we read in the
-// errors, and curretly that happens two or three different times, depending on
+// errors, and currently that happens two or three different times, depending on
// compiler flags.
//
// I decided avoiding the redundant output was not worth the time in engineering
--- /dev/null
+// check-pass
+#![warn(let_underscore_drop)]
+
+struct NontrivialDrop;
+
+impl Drop for NontrivialDrop {
+ fn drop(&mut self) {
+ println!("Dropping!");
+ }
+}
+
+fn main() {
+ let _ = NontrivialDrop; //~WARNING non-binding let on a type that implements `Drop`
+}
--- /dev/null
+warning: non-binding let on a type that implements `Drop`
+ --> $DIR/let_underscore_drop.rs:13:5
+ |
+LL | let _ = NontrivialDrop;
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/let_underscore_drop.rs:2:9
+ |
+LL | #![warn(let_underscore_drop)]
+ | ^^^^^^^^^^^^^^^^^^^
+help: consider binding to an unused variable to avoid immediately dropping the value
+ |
+LL | let _unused = NontrivialDrop;
+ | ~~~~~~~
+help: consider immediately dropping the value
+ |
+LL | drop(NontrivialDrop);
+ | ~~~~~ +
+
+warning: 1 warning emitted
+
--- /dev/null
+// check-fail
+use std::sync::{Arc, Mutex};
+
+fn main() {
+ let data = Arc::new(Mutex::new(0));
+ let _ = data.lock().unwrap(); //~ERROR non-binding let on a synchronization lock
+}
--- /dev/null
+error: non-binding let on a synchronization lock
+ --> $DIR/let_underscore_lock.rs:6:9
+ |
+LL | let _ = data.lock().unwrap();
+ | ^ ^^^^^^^^^^^^^^^^^^^^ this binding will immediately drop the value assigned to it
+ | |
+ | this lock is not assigned to a binding and is immediately dropped
+ |
+ = note: `#[deny(let_underscore_lock)]` on by default
+help: consider binding to an unused variable to avoid immediately dropping the value
+ |
+LL | let _unused = data.lock().unwrap();
+ | ~~~~~~~
+help: consider immediately dropping the value
+ |
+LL | drop(data.lock().unwrap());
+ | ~~~~~ +
+
+error: aborting due to previous error
+
let _val: [bool; 2] = mem::zeroed();
let _val: [bool; 2] = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+ let _val: i32 = mem::zeroed();
+ let _val: i32 = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+ let _val: f32 = mem::zeroed();
+ let _val: f32 = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+ let _val: *const () = mem::zeroed();
+ let _val: *const () = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+ let _val: *const [()] = mem::zeroed();
+ let _val: *const [()] = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
// Transmute-from-0
let _val: &'static i32 = mem::transmute(0usize); //~ ERROR: does not permit zero-initialization
let _val: &'static [i32] = mem::transmute((0usize, 0usize)); //~ ERROR: does not permit zero-initialization
let _val: Option<&'static i32> = mem::zeroed();
let _val: Option<fn()> = mem::zeroed();
let _val: MaybeUninit<&'static i32> = mem::zeroed();
- let _val: i32 = mem::zeroed();
let _val: bool = MaybeUninit::zeroed().assume_init();
let _val: [bool; 0] = MaybeUninit::uninit().assume_init();
let _val: [!; 0] = MaybeUninit::zeroed().assume_init();
+
// Some things that happen to work due to rustc implementation details,
// but are not guaranteed to keep working.
- let _val: i32 = mem::uninitialized();
let _val: OneFruit = mem::uninitialized();
}
}
| this code causes undefined behavior when executed
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
- = note: the `!` type has no valid value
+ = note: integers must not be uninitialized
error: the type `Void` does not permit zero-initialization
--> $DIR/uninitialized-zeroed.rs:57:26
|
= note: booleans must be either `true` or `false`
+error: the type `i32` does not permit being left uninitialized
+ --> $DIR/uninitialized-zeroed.rs:104:25
+ |
+LL | let _val: i32 = mem::uninitialized();
+ | ^^^^^^^^^^^^^^^^^^^^
+ | |
+ | this code causes undefined behavior when executed
+ | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
+ |
+ = note: integers must not be uninitialized
+
+error: the type `f32` does not permit being left uninitialized
+ --> $DIR/uninitialized-zeroed.rs:107:25
+ |
+LL | let _val: f32 = mem::uninitialized();
+ | ^^^^^^^^^^^^^^^^^^^^
+ | |
+ | this code causes undefined behavior when executed
+ | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
+ |
+ = note: floats must not be uninitialized
+
+error: the type `*const ()` does not permit being left uninitialized
+ --> $DIR/uninitialized-zeroed.rs:110:31
+ |
+LL | let _val: *const () = mem::uninitialized();
+ | ^^^^^^^^^^^^^^^^^^^^
+ | |
+ | this code causes undefined behavior when executed
+ | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
+ |
+ = note: raw pointers must not be uninitialized
+
+error: the type `*const [()]` does not permit being left uninitialized
+ --> $DIR/uninitialized-zeroed.rs:113:33
+ |
+LL | let _val: *const [()] = mem::uninitialized();
+ | ^^^^^^^^^^^^^^^^^^^^
+ | |
+ | this code causes undefined behavior when executed
+ | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
+ |
+ = note: raw pointers must not be uninitialized
+
error: the type `&i32` does not permit zero-initialization
- --> $DIR/uninitialized-zeroed.rs:104:34
+ --> $DIR/uninitialized-zeroed.rs:116:34
|
LL | let _val: &'static i32 = mem::transmute(0usize);
| ^^^^^^^^^^^^^^^^^^^^^^
= note: references must be non-null
error: the type `&[i32]` does not permit zero-initialization
- --> $DIR/uninitialized-zeroed.rs:105:36
+ --> $DIR/uninitialized-zeroed.rs:117:36
|
LL | let _val: &'static [i32] = mem::transmute((0usize, 0usize));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: references must be non-null
error: the type `NonZeroU32` does not permit zero-initialization
- --> $DIR/uninitialized-zeroed.rs:106:32
+ --> $DIR/uninitialized-zeroed.rs:118:32
|
LL | let _val: NonZeroU32 = mem::transmute(0);
| ^^^^^^^^^^^^^^^^^
= note: `std::num::NonZeroU32` must be non-null
error: the type `NonNull<i32>` does not permit zero-initialization
- --> $DIR/uninitialized-zeroed.rs:109:34
+ --> $DIR/uninitialized-zeroed.rs:121:34
|
LL | let _val: NonNull<i32> = MaybeUninit::zeroed().assume_init();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: `std::ptr::NonNull<i32>` must be non-null
error: the type `NonNull<i32>` does not permit being left uninitialized
- --> $DIR/uninitialized-zeroed.rs:110:34
+ --> $DIR/uninitialized-zeroed.rs:122:34
|
LL | let _val: NonNull<i32> = MaybeUninit::uninit().assume_init();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: `std::ptr::NonNull<i32>` must be non-null
error: the type `bool` does not permit being left uninitialized
- --> $DIR/uninitialized-zeroed.rs:111:26
+ --> $DIR/uninitialized-zeroed.rs:123:26
|
LL | let _val: bool = MaybeUninit::uninit().assume_init();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: booleans must be either `true` or `false`
-error: aborting due to 39 previous errors
+error: aborting due to 43 previous errors
struct Bug<A = [(); (let a = (), 1).1]> {
//~^ `let` expressions are not supported here
+ //~| `let` expressions in this position are unstable [E0658]
//~| expected expression, found `let` statement
a: A
}
|
= note: only supported directly in conditions of `if` and `while` expressions
-error: aborting due to 2 previous errors
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/issue-92893.rs:1:22
+ |
+LL | struct Bug<A = [(); (let a = (), 1).1]> {
+ | ^^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error: aborting due to 3 previous errors
+For more information about this error, try `rustc --explain E0658`.
// See `mir_drop_order.rs` for more information
+#![feature(let_chains)]
#![allow(irrefutable_let_patterns)]
use std::cell::RefCell;
LL | fn function(t: &mut dyn Trait) {
| - help: try adding a return type: `-> *mut dyn Trait`
LL | t as *mut dyn Trait
- | ^^^^^^^^^^^^^^^^^^^ expected `()`, found *-ptr
+ | ^^^^^^^^^^^^^^^^^^^ expected `()`, found `*mut dyn Trait`
|
= note: expected unit type `()`
found raw pointer `*mut dyn Trait`
--- /dev/null
+trait Foo {
+ type Bar;
+}
+
+impl<T> Foo for T {
+ type Bar = i32;
+}
+
+fn foo<T>(_: <T as Foo>::Bar, _: &'static <T as Foo>::Bar) {}
+
+fn needs_i32_ref_fn(_: fn(&'static i32, i32)) {}
+
+fn main() {
+ needs_i32_ref_fn(foo::<()>);
+ //~^ ERROR mismatched types
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/normalize-fn-sig.rs:14:22
+ |
+LL | needs_i32_ref_fn(foo::<()>);
+ | ---------------- ^^^^^^^^^ expected `&i32`, found `i32`
+ | |
+ | arguments to this function are incorrect
+ |
+ = note: expected fn pointer `fn(&'static i32, i32)`
+ found fn item `fn(i32, &'static i32) {foo::<()>}`
+note: function defined here
+ --> $DIR/normalize-fn-sig.rs:11:4
+ |
+LL | fn needs_i32_ref_fn(_: fn(&'static i32, i32)) {}
+ | ^^^^^^^^^^^^^^^^ ------------------------
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+#[allow(dead_code)]
+pub struct Dummy;
--- /dev/null
+mod lib;
+//~^ WARN found module declaration for lib.rs
+//~| ERROR file not found for module `lib`
+mod main;
+//~^ WARN found module declaration for main.rs
+//~| ERROR file not found for module `main`
+
+fn main() {}
--- /dev/null
+error[E0583]: file not found for module `lib`
+ --> $DIR/special_module_name.rs:1:1
+ |
+LL | mod lib;
+ | ^^^^^^^^
+ |
+ = help: to create the module `lib`, create file "$DIR/lib.rs" or "$DIR/lib/mod.rs"
+
+error[E0583]: file not found for module `main`
+ --> $DIR/special_module_name.rs:4:1
+ |
+LL | mod main;
+ | ^^^^^^^^^
+ |
+ = help: to create the module `main`, create file "$DIR/main.rs" or "$DIR/main/mod.rs"
+
+warning: found module declaration for lib.rs
+ --> $DIR/special_module_name.rs:1:1
+ |
+LL | mod lib;
+ | ^^^^^^^^
+ |
+ = note: `#[warn(special_module_name)]` on by default
+ = note: lib.rs is the root of this crate's library target
+ = help: to refer to it from other targets, use the library's name as the path
+
+warning: found module declaration for main.rs
+ --> $DIR/special_module_name.rs:4:1
+ |
+LL | mod main;
+ | ^^^^^^^^^
+ |
+ = note: a binary crate cannot be used as library
+
+error: aborting due to 2 previous errors; 2 warnings emitted
+
+For more information about this error, try `rustc --explain E0583`.
--- /dev/null
+// run-pass
+
+#[path = "auxiliary/dummy_lib.rs"]
+mod lib;
+
+#[path = "auxiliary/dummy_lib.rs"]
+mod main;
+
+fn main() {}
//
// This particular test case currently fails as the inference to `()` rather
// than `!` happens as a result of an `as` cast, which is not currently tracked.
-// Crater did not find many cases of this occuring, but it is included for
+// Crater did not find many cases of this occurring, but it is included for
// awareness.
//
// revisions: nofallback fallback
// * local/field: Is the structure in a local or a field
// * fully/partial/void: Are we fully initializing it before using any part?
// Is whole type empty due to a void component?
-// * init/reinit: First initialization, or did we previously inititalize and then move out?
+// * init/reinit: First initialization, or did we previously initialize and then move out?
// * struct/tuple: Is this a struct or a (X, Y).
//
// As a shorthand for the cases above, adding a numeric summary to
#![allow(dead_code)]
// This tests the various kinds of assignments there are. Polonius used to generate `killed`
-// facts only on simple assigments, but not projections, incorrectly causing errors to be emitted
+// facts only on simple assignments, but not projections, incorrectly causing errors to be emitted
// for code accepted by NLL. They are all variations from example code in the NLL RFC.
// check-pass
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `Clone`
-help: one of the expressions' fields has a method of the same name
- |
-LL | let _y = x.i.clone();
- | ++
-help: one of the expressions' fields has a method of the same name
- |
-LL | let _y = x.j.x.clone();
- | ++++
error: aborting due to previous error
error[E0038]: the trait `Bar` cannot be made into an object
- --> $DIR/object-safety-associated-consts.rs:12:30
+ --> $DIR/object-safety-associated-consts.rs:12:31
|
LL | fn make_bar<T:Bar>(t: &T) -> &dyn Bar {
- | ^^^^^^^^ `Bar` cannot be made into an object
+ | ^^^^^^^ `Bar` cannot be made into an object
|
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/object-safety-associated-consts.rs:9:11
error[E0038]: the trait `X` cannot be made into an object
- --> $DIR/object-safety-bounds.rs:7:11
+ --> $DIR/object-safety-bounds.rs:7:15
|
LL | fn f() -> Box<dyn X<U = u32>> {
- | ^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object
+ | ^^^^^^^^^^^^^^ `X` cannot be made into an object
|
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/object-safety-bounds.rs:4:13
error[E0038]: the trait `Bar` cannot be made into an object
- --> $DIR/object-safety-generics.rs:18:30
+ --> $DIR/object-safety-generics.rs:18:31
|
LL | fn make_bar<T:Bar>(t: &T) -> &dyn Bar {
- | ^^^^^^^^ `Bar` cannot be made into an object
+ | ^^^^^^^ `Bar` cannot be made into an object
|
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/object-safety-generics.rs:10:8
= help: consider moving `bar` to another trait
error[E0038]: the trait `Bar` cannot be made into an object
- --> $DIR/object-safety-generics.rs:24:39
+ --> $DIR/object-safety-generics.rs:24:40
|
LL | fn make_bar_explicit<T:Bar>(t: &T) -> &dyn Bar {
- | ^^^^^^^^ `Bar` cannot be made into an object
+ | ^^^^^^^ `Bar` cannot be made into an object
|
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/object-safety-generics.rs:10:8
error[E0038]: the trait `Bar` cannot be made into an object
- --> $DIR/object-safety-mentions-Self.rs:22:30
+ --> $DIR/object-safety-mentions-Self.rs:22:31
|
LL | fn make_bar<T:Bar>(t: &T) -> &dyn Bar {
- | ^^^^^^^^ `Bar` cannot be made into an object
+ | ^^^^^^^ `Bar` cannot be made into an object
|
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/object-safety-mentions-Self.rs:11:22
= help: consider moving `bar` to another trait
error[E0038]: the trait `Baz` cannot be made into an object
- --> $DIR/object-safety-mentions-Self.rs:28:30
+ --> $DIR/object-safety-mentions-Self.rs:28:31
|
LL | fn make_baz<T:Baz>(t: &T) -> &dyn Baz {
- | ^^^^^^^^ `Baz` cannot be made into an object
+ | ^^^^^^^ `Baz` cannot be made into an object
|
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/object-safety-mentions-Self.rs:15:22
error[E0038]: the trait `Foo` cannot be made into an object
- --> $DIR/object-safety-no-static.rs:12:18
+ --> $DIR/object-safety-no-static.rs:12:22
|
LL | fn diverges() -> Box<dyn Foo> {
- | ^^^^^^^^^^^^ `Foo` cannot be made into an object
+ | ^^^^^^^ `Foo` cannot be made into an object
|
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/object-safety-no-static.rs:9:8
error[E0038]: the trait `Bar` cannot be made into an object
- --> $DIR/object-safety-sized-2.rs:14:30
+ --> $DIR/object-safety-sized-2.rs:14:31
|
LL | fn make_bar<T:Bar>(t: &T) -> &dyn Bar {
- | ^^^^^^^^ `Bar` cannot be made into an object
+ | ^^^^^^^ `Bar` cannot be made into an object
|
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/object-safety-sized-2.rs:9:18
error[E0038]: the trait `Bar` cannot be made into an object
- --> $DIR/object-safety-sized.rs:12:30
+ --> $DIR/object-safety-sized.rs:12:31
|
LL | fn make_bar<T:Bar>(t: &T) -> &dyn Bar {
- | ^^^^^^^^ `Bar` cannot be made into an object
+ | ^^^^^^^ `Bar` cannot be made into an object
|
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/object-safety-sized.rs:8:13
// Here we test that rest patterns, i.e. `..`, are not allowed
-// outside of slice (+ ident patterns witin those), tuple,
+// outside of slice (+ ident patterns within those), tuple,
// and tuple struct patterns and that duplicates are caught in these contexts.
#![feature(box_patterns)]
--- /dev/null
+#![feature(rustc_attrs)]
+
+#[rustc_access_level] mod outer { //~ ERROR None
+ #[rustc_access_level] pub mod inner { //~ ERROR Some(Exported)
+ #[rustc_access_level]
+ extern "C" { //~ ERROR Some(Exported)
+ #[rustc_access_level] static a: u8; //~ ERROR None
+ #[rustc_access_level] pub fn b(); //~ ERROR Some(Exported)
+ }
+ #[rustc_access_level]
+ pub trait Trait { //~ ERROR Some(Exported)
+ #[rustc_access_level] const A: i32; //~ ERROR Some(Exported)
+ #[rustc_access_level] type B; //~ ERROR Some(Exported)
+ }
+
+ #[rustc_access_level]
+ pub struct Struct { //~ ERROR Some(Exported)
+ #[rustc_access_level] a: u8, //~ ERROR None
+ #[rustc_access_level] pub b: u8, //~ ERROR Some(Exported)
+ }
+
+ #[rustc_access_level]
+ pub union Union { //~ ERROR Some(Exported)
+ #[rustc_access_level] a: u8, //~ ERROR None
+ #[rustc_access_level] pub b: u8, //~ ERROR Some(Exported)
+ }
+
+ #[rustc_access_level]
+ pub enum Enum { //~ ERROR Some(Exported)
+ #[rustc_access_level] A( //~ ERROR Some(Exported)
+ #[rustc_access_level] Struct, //~ ERROR Some(Exported)
+ #[rustc_access_level] Union, //~ ERROR Some(Exported)
+ ),
+ }
+ }
+
+ #[rustc_access_level] macro_rules! none_macro { //~ ERROR None
+ () => {};
+ }
+
+ #[macro_export]
+ #[rustc_access_level] macro_rules! public_macro { //~ ERROR Some(Public)
+ () => {};
+ }
+}
+
+pub use outer::inner;
+
+fn main() {}
--- /dev/null
+error: None
+ --> $DIR/access_levels.rs:3:23
+ |
+LL | #[rustc_access_level] mod outer {
+ | ^^^^^^^^^
+
+error: Some(Exported)
+ --> $DIR/access_levels.rs:4:27
+ |
+LL | #[rustc_access_level] pub mod inner {
+ | ^^^^^^^^^^^^^
+
+error: Some(Exported)
+ --> $DIR/access_levels.rs:6:9
+ |
+LL | / extern "C" {
+LL | | #[rustc_access_level] static a: u8;
+LL | | #[rustc_access_level] pub fn b();
+LL | | }
+ | |_________^
+
+error: Some(Exported)
+ --> $DIR/access_levels.rs:11:9
+ |
+LL | pub trait Trait {
+ | ^^^^^^^^^^^^^^^
+
+error: Some(Exported)
+ --> $DIR/access_levels.rs:17:9
+ |
+LL | pub struct Struct {
+ | ^^^^^^^^^^^^^^^^^
+
+error: None
+ --> $DIR/access_levels.rs:18:35
+ |
+LL | #[rustc_access_level] a: u8,
+ | ^^^^^
+
+error: Some(Exported)
+ --> $DIR/access_levels.rs:19:35
+ |
+LL | #[rustc_access_level] pub b: u8,
+ | ^^^^^^^^^
+
+error: Some(Exported)
+ --> $DIR/access_levels.rs:23:9
+ |
+LL | pub union Union {
+ | ^^^^^^^^^^^^^^^
+
+error: None
+ --> $DIR/access_levels.rs:24:35
+ |
+LL | #[rustc_access_level] a: u8,
+ | ^^^^^
+
+error: Some(Exported)
+ --> $DIR/access_levels.rs:25:35
+ |
+LL | #[rustc_access_level] pub b: u8,
+ | ^^^^^^^^^
+
+error: Some(Exported)
+ --> $DIR/access_levels.rs:29:9
+ |
+LL | pub enum Enum {
+ | ^^^^^^^^^^^^^
+
+error: Some(Exported)
+ --> $DIR/access_levels.rs:30:35
+ |
+LL | #[rustc_access_level] A(
+ | ^
+
+error: Some(Exported)
+ --> $DIR/access_levels.rs:31:39
+ |
+LL | #[rustc_access_level] Struct,
+ | ^^^^^^
+
+error: Some(Exported)
+ --> $DIR/access_levels.rs:32:39
+ |
+LL | #[rustc_access_level] Union,
+ | ^^^^^
+
+error: None
+ --> $DIR/access_levels.rs:37:27
+ |
+LL | #[rustc_access_level] macro_rules! none_macro {
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: Some(Public)
+ --> $DIR/access_levels.rs:42:27
+ |
+LL | #[rustc_access_level] macro_rules! public_macro {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: Some(Exported)
+ --> $DIR/access_levels.rs:12:35
+ |
+LL | #[rustc_access_level] const A: i32;
+ | ^^^^^^^^^^^^
+
+error: Some(Exported)
+ --> $DIR/access_levels.rs:13:35
+ |
+LL | #[rustc_access_level] type B;
+ | ^^^^^^
+
+error: None
+ --> $DIR/access_levels.rs:7:35
+ |
+LL | #[rustc_access_level] static a: u8;
+ | ^^^^^^^^^^^^
+
+error: Some(Exported)
+ --> $DIR/access_levels.rs:8:35
+ |
+LL | #[rustc_access_level] pub fn b();
+ | ^^^^^^^^^^
+
+error: aborting due to 20 previous errors
+
// FIXME: This don't work when crate-type is specified by attribute
// `#![crate_type = "proc-macro"]`, not by `--crate-type=proc-macro`
-// command line flag. This is beacuse the list of `cfg` symbols is generated
+// command line flag. This is because the list of `cfg` symbols is generated
// before attributes are parsed. See rustc_interface::util::add_configuration
#[cfg(target_feature = "crt-static")]
compile_error!("crt-static is enabled");
+++ /dev/null
-// aux-build:derive-unstable-2.rs
-
-#![feature(register_attr)]
-
-#![register_attr(rustc_foo)]
-
-#[macro_use]
-extern crate derive_unstable_2;
-
-#[derive(Unstable)]
-//~^ ERROR attributes starting with `rustc` are reserved for use by the `rustc` compiler
-
-struct A;
-
-fn main() {
- foo();
-}
+++ /dev/null
-error: attributes starting with `rustc` are reserved for use by the `rustc` compiler
- --> $DIR/expand-to-unstable-2.rs:10:10
- |
-LL | #[derive(Unstable)]
- | ^^^^^^^^
- |
- = note: this error originates in the derive macro `Unstable` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to previous error
-
+// gate-test-custom_inner_attributes
// compile-flags: -Z span-debug --error-format human
// aux-build:test-macros.rs
// edition:2018
error: expected non-macro inner attribute, found attribute macro `print_attr`
- --> $DIR/inner-attrs.rs:63:12
+ --> $DIR/inner-attrs.rs:64:12
|
LL | #![print_attr]
| ^^^^^^^^^^ not a non-macro inner attribute
error: expected non-macro inner attribute, found attribute macro `print_attr`
- --> $DIR/inner-attrs.rs:67:12
+ --> $DIR/inner-attrs.rs:68:12
|
LL | #![print_attr]
| ^^^^^^^^^^ not a non-macro inner attribute
error: expected non-macro inner attribute, found attribute macro `print_attr`
- --> $DIR/inner-attrs.rs:71:12
+ --> $DIR/inner-attrs.rs:72:12
|
LL | #![print_attr]
| ^^^^^^^^^^ not a non-macro inner attribute
error: expected non-macro inner attribute, found attribute macro `print_attr`
- --> $DIR/inner-attrs.rs:75:12
+ --> $DIR/inner-attrs.rs:76:12
|
LL | #![print_attr]
| ^^^^^^^^^^ not a non-macro inner attribute
PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [
Ident {
ident: "first",
- span: $DIR/inner-attrs.rs:16:25: 16:30 (#0),
+ span: $DIR/inner-attrs.rs:17:25: 17:30 (#0),
},
]
PRINT-ATTR INPUT (DISPLAY): #[print_target_and_args(second)] fn foo()
Punct {
ch: '#',
spacing: Alone,
- span: $DIR/inner-attrs.rs:17:1: 17:2 (#0),
+ span: $DIR/inner-attrs.rs:18:1: 18:2 (#0),
},
Group {
delimiter: Bracket,
stream: TokenStream [
Ident {
ident: "print_target_and_args",
- span: $DIR/inner-attrs.rs:17:3: 17:24 (#0),
+ span: $DIR/inner-attrs.rs:18:3: 18:24 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "second",
- span: $DIR/inner-attrs.rs:17:25: 17:31 (#0),
+ span: $DIR/inner-attrs.rs:18:25: 18:31 (#0),
},
],
- span: $DIR/inner-attrs.rs:17:24: 17:32 (#0),
+ span: $DIR/inner-attrs.rs:18:24: 18:32 (#0),
},
],
- span: $DIR/inner-attrs.rs:17:2: 17:33 (#0),
+ span: $DIR/inner-attrs.rs:18:2: 18:33 (#0),
},
Ident {
ident: "fn",
- span: $DIR/inner-attrs.rs:18:1: 18:3 (#0),
+ span: $DIR/inner-attrs.rs:19:1: 19:3 (#0),
},
Ident {
ident: "foo",
- span: $DIR/inner-attrs.rs:18:4: 18:7 (#0),
+ span: $DIR/inner-attrs.rs:19:4: 19:7 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [],
- span: $DIR/inner-attrs.rs:18:7: 18:9 (#0),
+ span: $DIR/inner-attrs.rs:19:7: 19:9 (#0),
},
Group {
delimiter: Brace,
Punct {
ch: '#',
spacing: Joint,
- span: $DIR/inner-attrs.rs:19:5: 19:6 (#0),
+ span: $DIR/inner-attrs.rs:20:5: 20:6 (#0),
},
Punct {
ch: '!',
spacing: Alone,
- span: $DIR/inner-attrs.rs:19:6: 19:7 (#0),
+ span: $DIR/inner-attrs.rs:20:6: 20:7 (#0),
},
Group {
delimiter: Bracket,
stream: TokenStream [
Ident {
ident: "print_target_and_args",
- span: $DIR/inner-attrs.rs:19:8: 19:29 (#0),
+ span: $DIR/inner-attrs.rs:20:8: 20:29 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "third",
- span: $DIR/inner-attrs.rs:19:30: 19:35 (#0),
+ span: $DIR/inner-attrs.rs:20:30: 20:35 (#0),
},
],
- span: $DIR/inner-attrs.rs:19:29: 19:36 (#0),
+ span: $DIR/inner-attrs.rs:20:29: 20:36 (#0),
},
],
- span: $DIR/inner-attrs.rs:19:7: 19:37 (#0),
+ span: $DIR/inner-attrs.rs:20:7: 20:37 (#0),
},
Punct {
ch: '#',
spacing: Joint,
- span: $DIR/inner-attrs.rs:20:5: 20:6 (#0),
+ span: $DIR/inner-attrs.rs:21:5: 21:6 (#0),
},
Punct {
ch: '!',
spacing: Alone,
- span: $DIR/inner-attrs.rs:20:6: 20:7 (#0),
+ span: $DIR/inner-attrs.rs:21:6: 21:7 (#0),
},
Group {
delimiter: Bracket,
stream: TokenStream [
Ident {
ident: "print_target_and_args",
- span: $DIR/inner-attrs.rs:20:8: 20:29 (#0),
+ span: $DIR/inner-attrs.rs:21:8: 21:29 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "fourth",
- span: $DIR/inner-attrs.rs:20:30: 20:36 (#0),
+ span: $DIR/inner-attrs.rs:21:30: 21:36 (#0),
},
],
- span: $DIR/inner-attrs.rs:20:29: 20:37 (#0),
+ span: $DIR/inner-attrs.rs:21:29: 21:37 (#0),
},
],
- span: $DIR/inner-attrs.rs:20:7: 20:38 (#0),
+ span: $DIR/inner-attrs.rs:21:7: 21:38 (#0),
},
],
- span: $DIR/inner-attrs.rs:18:10: 21:2 (#0),
+ span: $DIR/inner-attrs.rs:19:10: 22:2 (#0),
},
]
PRINT-ATTR_ARGS INPUT (DISPLAY): second
PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [
Ident {
ident: "second",
- span: $DIR/inner-attrs.rs:17:25: 17:31 (#0),
+ span: $DIR/inner-attrs.rs:18:25: 18:31 (#0),
},
]
PRINT-ATTR INPUT (DISPLAY): fn foo()
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "fn",
- span: $DIR/inner-attrs.rs:18:1: 18:3 (#0),
+ span: $DIR/inner-attrs.rs:19:1: 19:3 (#0),
},
Ident {
ident: "foo",
- span: $DIR/inner-attrs.rs:18:4: 18:7 (#0),
+ span: $DIR/inner-attrs.rs:19:4: 19:7 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [],
- span: $DIR/inner-attrs.rs:18:7: 18:9 (#0),
+ span: $DIR/inner-attrs.rs:19:7: 19:9 (#0),
},
Group {
delimiter: Brace,
Punct {
ch: '#',
spacing: Joint,
- span: $DIR/inner-attrs.rs:19:5: 19:6 (#0),
+ span: $DIR/inner-attrs.rs:20:5: 20:6 (#0),
},
Punct {
ch: '!',
spacing: Alone,
- span: $DIR/inner-attrs.rs:19:6: 19:7 (#0),
+ span: $DIR/inner-attrs.rs:20:6: 20:7 (#0),
},
Group {
delimiter: Bracket,
stream: TokenStream [
Ident {
ident: "print_target_and_args",
- span: $DIR/inner-attrs.rs:19:8: 19:29 (#0),
+ span: $DIR/inner-attrs.rs:20:8: 20:29 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "third",
- span: $DIR/inner-attrs.rs:19:30: 19:35 (#0),
+ span: $DIR/inner-attrs.rs:20:30: 20:35 (#0),
},
],
- span: $DIR/inner-attrs.rs:19:29: 19:36 (#0),
+ span: $DIR/inner-attrs.rs:20:29: 20:36 (#0),
},
],
- span: $DIR/inner-attrs.rs:19:7: 19:37 (#0),
+ span: $DIR/inner-attrs.rs:20:7: 20:37 (#0),
},
Punct {
ch: '#',
spacing: Joint,
- span: $DIR/inner-attrs.rs:20:5: 20:6 (#0),
+ span: $DIR/inner-attrs.rs:21:5: 21:6 (#0),
},
Punct {
ch: '!',
spacing: Alone,
- span: $DIR/inner-attrs.rs:20:6: 20:7 (#0),
+ span: $DIR/inner-attrs.rs:21:6: 21:7 (#0),
},
Group {
delimiter: Bracket,
stream: TokenStream [
Ident {
ident: "print_target_and_args",
- span: $DIR/inner-attrs.rs:20:8: 20:29 (#0),
+ span: $DIR/inner-attrs.rs:21:8: 21:29 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "fourth",
- span: $DIR/inner-attrs.rs:20:30: 20:36 (#0),
+ span: $DIR/inner-attrs.rs:21:30: 21:36 (#0),
},
],
- span: $DIR/inner-attrs.rs:20:29: 20:37 (#0),
+ span: $DIR/inner-attrs.rs:21:29: 21:37 (#0),
},
],
- span: $DIR/inner-attrs.rs:20:7: 20:38 (#0),
+ span: $DIR/inner-attrs.rs:21:7: 21:38 (#0),
},
],
- span: $DIR/inner-attrs.rs:18:10: 21:2 (#0),
+ span: $DIR/inner-attrs.rs:19:10: 22:2 (#0),
},
]
PRINT-ATTR_ARGS INPUT (DISPLAY): third
PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [
Ident {
ident: "third",
- span: $DIR/inner-attrs.rs:19:30: 19:35 (#0),
+ span: $DIR/inner-attrs.rs:20:30: 20:35 (#0),
},
]
PRINT-ATTR INPUT (DISPLAY): fn foo() { #! [print_target_and_args(fourth)] }
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "fn",
- span: $DIR/inner-attrs.rs:18:1: 18:3 (#0),
+ span: $DIR/inner-attrs.rs:19:1: 19:3 (#0),
},
Ident {
ident: "foo",
- span: $DIR/inner-attrs.rs:18:4: 18:7 (#0),
+ span: $DIR/inner-attrs.rs:19:4: 19:7 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [],
- span: $DIR/inner-attrs.rs:18:7: 18:9 (#0),
+ span: $DIR/inner-attrs.rs:19:7: 19:9 (#0),
},
Group {
delimiter: Brace,
Punct {
ch: '#',
spacing: Joint,
- span: $DIR/inner-attrs.rs:20:5: 20:6 (#0),
+ span: $DIR/inner-attrs.rs:21:5: 21:6 (#0),
},
Punct {
ch: '!',
spacing: Alone,
- span: $DIR/inner-attrs.rs:20:6: 20:7 (#0),
+ span: $DIR/inner-attrs.rs:21:6: 21:7 (#0),
},
Group {
delimiter: Bracket,
stream: TokenStream [
Ident {
ident: "print_target_and_args",
- span: $DIR/inner-attrs.rs:20:8: 20:29 (#0),
+ span: $DIR/inner-attrs.rs:21:8: 21:29 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "fourth",
- span: $DIR/inner-attrs.rs:20:30: 20:36 (#0),
+ span: $DIR/inner-attrs.rs:21:30: 21:36 (#0),
},
],
- span: $DIR/inner-attrs.rs:20:29: 20:37 (#0),
+ span: $DIR/inner-attrs.rs:21:29: 21:37 (#0),
},
],
- span: $DIR/inner-attrs.rs:20:7: 20:38 (#0),
+ span: $DIR/inner-attrs.rs:21:7: 21:38 (#0),
},
],
- span: $DIR/inner-attrs.rs:18:10: 21:2 (#0),
+ span: $DIR/inner-attrs.rs:19:10: 22:2 (#0),
},
]
PRINT-ATTR_ARGS INPUT (DISPLAY): fourth
PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [
Ident {
ident: "fourth",
- span: $DIR/inner-attrs.rs:20:30: 20:36 (#0),
+ span: $DIR/inner-attrs.rs:21:30: 21:36 (#0),
},
]
PRINT-ATTR INPUT (DISPLAY): fn foo() {}
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "fn",
- span: $DIR/inner-attrs.rs:18:1: 18:3 (#0),
+ span: $DIR/inner-attrs.rs:19:1: 19:3 (#0),
},
Ident {
ident: "foo",
- span: $DIR/inner-attrs.rs:18:4: 18:7 (#0),
+ span: $DIR/inner-attrs.rs:19:4: 19:7 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [],
- span: $DIR/inner-attrs.rs:18:7: 18:9 (#0),
+ span: $DIR/inner-attrs.rs:19:7: 19:9 (#0),
},
Group {
delimiter: Brace,
stream: TokenStream [],
- span: $DIR/inner-attrs.rs:18:10: 21:2 (#0),
+ span: $DIR/inner-attrs.rs:19:10: 22:2 (#0),
},
]
PRINT-ATTR_ARGS INPUT (DISPLAY): mod_first
PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [
Ident {
ident: "mod_first",
- span: $DIR/inner-attrs.rs:23:25: 23:34 (#0),
+ span: $DIR/inner-attrs.rs:24:25: 24:34 (#0),
},
]
PRINT-ATTR INPUT (DISPLAY): #[print_target_and_args(mod_second)] mod inline_mod
Punct {
ch: '#',
spacing: Alone,
- span: $DIR/inner-attrs.rs:24:1: 24:2 (#0),
+ span: $DIR/inner-attrs.rs:25:1: 25:2 (#0),
},
Group {
delimiter: Bracket,
stream: TokenStream [
Ident {
ident: "print_target_and_args",
- span: $DIR/inner-attrs.rs:24:3: 24:24 (#0),
+ span: $DIR/inner-attrs.rs:25:3: 25:24 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "mod_second",
- span: $DIR/inner-attrs.rs:24:25: 24:35 (#0),
+ span: $DIR/inner-attrs.rs:25:25: 25:35 (#0),
},
],
- span: $DIR/inner-attrs.rs:24:24: 24:36 (#0),
+ span: $DIR/inner-attrs.rs:25:24: 25:36 (#0),
},
],
- span: $DIR/inner-attrs.rs:24:2: 24:37 (#0),
+ span: $DIR/inner-attrs.rs:25:2: 25:37 (#0),
},
Ident {
ident: "mod",
- span: $DIR/inner-attrs.rs:25:1: 25:4 (#0),
+ span: $DIR/inner-attrs.rs:26:1: 26:4 (#0),
},
Ident {
ident: "inline_mod",
- span: $DIR/inner-attrs.rs:25:5: 25:15 (#0),
+ span: $DIR/inner-attrs.rs:26:5: 26:15 (#0),
},
Group {
delimiter: Brace,
Punct {
ch: '#',
spacing: Joint,
- span: $DIR/inner-attrs.rs:26:5: 26:6 (#0),
+ span: $DIR/inner-attrs.rs:27:5: 27:6 (#0),
},
Punct {
ch: '!',
spacing: Alone,
- span: $DIR/inner-attrs.rs:26:6: 26:7 (#0),
+ span: $DIR/inner-attrs.rs:27:6: 27:7 (#0),
},
Group {
delimiter: Bracket,
stream: TokenStream [
Ident {
ident: "print_target_and_args",
- span: $DIR/inner-attrs.rs:26:8: 26:29 (#0),
+ span: $DIR/inner-attrs.rs:27:8: 27:29 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "mod_third",
- span: $DIR/inner-attrs.rs:26:30: 26:39 (#0),
+ span: $DIR/inner-attrs.rs:27:30: 27:39 (#0),
},
],
- span: $DIR/inner-attrs.rs:26:29: 26:40 (#0),
+ span: $DIR/inner-attrs.rs:27:29: 27:40 (#0),
},
],
- span: $DIR/inner-attrs.rs:26:7: 26:41 (#0),
+ span: $DIR/inner-attrs.rs:27:7: 27:41 (#0),
},
Punct {
ch: '#',
spacing: Joint,
- span: $DIR/inner-attrs.rs:27:5: 27:6 (#0),
+ span: $DIR/inner-attrs.rs:28:5: 28:6 (#0),
},
Punct {
ch: '!',
spacing: Alone,
- span: $DIR/inner-attrs.rs:27:6: 27:7 (#0),
+ span: $DIR/inner-attrs.rs:28:6: 28:7 (#0),
},
Group {
delimiter: Bracket,
stream: TokenStream [
Ident {
ident: "print_target_and_args",
- span: $DIR/inner-attrs.rs:27:8: 27:29 (#0),
+ span: $DIR/inner-attrs.rs:28:8: 28:29 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "mod_fourth",
- span: $DIR/inner-attrs.rs:27:30: 27:40 (#0),
+ span: $DIR/inner-attrs.rs:28:30: 28:40 (#0),
},
],
- span: $DIR/inner-attrs.rs:27:29: 27:41 (#0),
+ span: $DIR/inner-attrs.rs:28:29: 28:41 (#0),
},
],
- span: $DIR/inner-attrs.rs:27:7: 27:42 (#0),
+ span: $DIR/inner-attrs.rs:28:7: 28:42 (#0),
},
],
- span: $DIR/inner-attrs.rs:25:16: 28:2 (#0),
+ span: $DIR/inner-attrs.rs:26:16: 29:2 (#0),
},
]
PRINT-ATTR_ARGS INPUT (DISPLAY): mod_second
PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [
Ident {
ident: "mod_second",
- span: $DIR/inner-attrs.rs:24:25: 24:35 (#0),
+ span: $DIR/inner-attrs.rs:25:25: 25:35 (#0),
},
]
PRINT-ATTR INPUT (DISPLAY): mod inline_mod
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "mod",
- span: $DIR/inner-attrs.rs:25:1: 25:4 (#0),
+ span: $DIR/inner-attrs.rs:26:1: 26:4 (#0),
},
Ident {
ident: "inline_mod",
- span: $DIR/inner-attrs.rs:25:5: 25:15 (#0),
+ span: $DIR/inner-attrs.rs:26:5: 26:15 (#0),
},
Group {
delimiter: Brace,
Punct {
ch: '#',
spacing: Joint,
- span: $DIR/inner-attrs.rs:26:5: 26:6 (#0),
+ span: $DIR/inner-attrs.rs:27:5: 27:6 (#0),
},
Punct {
ch: '!',
spacing: Alone,
- span: $DIR/inner-attrs.rs:26:6: 26:7 (#0),
+ span: $DIR/inner-attrs.rs:27:6: 27:7 (#0),
},
Group {
delimiter: Bracket,
stream: TokenStream [
Ident {
ident: "print_target_and_args",
- span: $DIR/inner-attrs.rs:26:8: 26:29 (#0),
+ span: $DIR/inner-attrs.rs:27:8: 27:29 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "mod_third",
- span: $DIR/inner-attrs.rs:26:30: 26:39 (#0),
+ span: $DIR/inner-attrs.rs:27:30: 27:39 (#0),
},
],
- span: $DIR/inner-attrs.rs:26:29: 26:40 (#0),
+ span: $DIR/inner-attrs.rs:27:29: 27:40 (#0),
},
],
- span: $DIR/inner-attrs.rs:26:7: 26:41 (#0),
+ span: $DIR/inner-attrs.rs:27:7: 27:41 (#0),
},
Punct {
ch: '#',
spacing: Joint,
- span: $DIR/inner-attrs.rs:27:5: 27:6 (#0),
+ span: $DIR/inner-attrs.rs:28:5: 28:6 (#0),
},
Punct {
ch: '!',
spacing: Alone,
- span: $DIR/inner-attrs.rs:27:6: 27:7 (#0),
+ span: $DIR/inner-attrs.rs:28:6: 28:7 (#0),
},
Group {
delimiter: Bracket,
stream: TokenStream [
Ident {
ident: "print_target_and_args",
- span: $DIR/inner-attrs.rs:27:8: 27:29 (#0),
+ span: $DIR/inner-attrs.rs:28:8: 28:29 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "mod_fourth",
- span: $DIR/inner-attrs.rs:27:30: 27:40 (#0),
+ span: $DIR/inner-attrs.rs:28:30: 28:40 (#0),
},
],
- span: $DIR/inner-attrs.rs:27:29: 27:41 (#0),
+ span: $DIR/inner-attrs.rs:28:29: 28:41 (#0),
},
],
- span: $DIR/inner-attrs.rs:27:7: 27:42 (#0),
+ span: $DIR/inner-attrs.rs:28:7: 28:42 (#0),
},
],
- span: $DIR/inner-attrs.rs:25:16: 28:2 (#0),
+ span: $DIR/inner-attrs.rs:26:16: 29:2 (#0),
},
]
PRINT-ATTR_ARGS INPUT (DISPLAY): mod_third
PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [
Ident {
ident: "mod_third",
- span: $DIR/inner-attrs.rs:26:30: 26:39 (#0),
+ span: $DIR/inner-attrs.rs:27:30: 27:39 (#0),
},
]
PRINT-ATTR INPUT (DISPLAY): mod inline_mod { #! [print_target_and_args(mod_fourth)] }
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "mod",
- span: $DIR/inner-attrs.rs:25:1: 25:4 (#0),
+ span: $DIR/inner-attrs.rs:26:1: 26:4 (#0),
},
Ident {
ident: "inline_mod",
- span: $DIR/inner-attrs.rs:25:5: 25:15 (#0),
+ span: $DIR/inner-attrs.rs:26:5: 26:15 (#0),
},
Group {
delimiter: Brace,
Punct {
ch: '#',
spacing: Joint,
- span: $DIR/inner-attrs.rs:27:5: 27:6 (#0),
+ span: $DIR/inner-attrs.rs:28:5: 28:6 (#0),
},
Punct {
ch: '!',
spacing: Alone,
- span: $DIR/inner-attrs.rs:27:6: 27:7 (#0),
+ span: $DIR/inner-attrs.rs:28:6: 28:7 (#0),
},
Group {
delimiter: Bracket,
stream: TokenStream [
Ident {
ident: "print_target_and_args",
- span: $DIR/inner-attrs.rs:27:8: 27:29 (#0),
+ span: $DIR/inner-attrs.rs:28:8: 28:29 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "mod_fourth",
- span: $DIR/inner-attrs.rs:27:30: 27:40 (#0),
+ span: $DIR/inner-attrs.rs:28:30: 28:40 (#0),
},
],
- span: $DIR/inner-attrs.rs:27:29: 27:41 (#0),
+ span: $DIR/inner-attrs.rs:28:29: 28:41 (#0),
},
],
- span: $DIR/inner-attrs.rs:27:7: 27:42 (#0),
+ span: $DIR/inner-attrs.rs:28:7: 28:42 (#0),
},
],
- span: $DIR/inner-attrs.rs:25:16: 28:2 (#0),
+ span: $DIR/inner-attrs.rs:26:16: 29:2 (#0),
},
]
PRINT-ATTR_ARGS INPUT (DISPLAY): mod_fourth
PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [
Ident {
ident: "mod_fourth",
- span: $DIR/inner-attrs.rs:27:30: 27:40 (#0),
+ span: $DIR/inner-attrs.rs:28:30: 28:40 (#0),
},
]
PRINT-ATTR INPUT (DISPLAY): mod inline_mod {}
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "mod",
- span: $DIR/inner-attrs.rs:25:1: 25:4 (#0),
+ span: $DIR/inner-attrs.rs:26:1: 26:4 (#0),
},
Ident {
ident: "inline_mod",
- span: $DIR/inner-attrs.rs:25:5: 25:15 (#0),
+ span: $DIR/inner-attrs.rs:26:5: 26:15 (#0),
},
Group {
delimiter: Brace,
stream: TokenStream [],
- span: $DIR/inner-attrs.rs:25:16: 28:2 (#0),
+ span: $DIR/inner-attrs.rs:26:16: 29:2 (#0),
},
]
PRINT-DERIVE INPUT (DISPLAY): struct MyDerivePrint
PRINT-DERIVE INPUT (DEBUG): TokenStream [
Ident {
ident: "struct",
- span: $DIR/inner-attrs.rs:35:1: 35:7 (#0),
+ span: $DIR/inner-attrs.rs:36:1: 36:7 (#0),
},
Ident {
ident: "MyDerivePrint",
- span: $DIR/inner-attrs.rs:35:8: 35:21 (#0),
+ span: $DIR/inner-attrs.rs:36:8: 36:21 (#0),
},
Group {
delimiter: Brace,
stream: TokenStream [
Ident {
ident: "field",
- span: $DIR/inner-attrs.rs:36:5: 36:10 (#0),
+ span: $DIR/inner-attrs.rs:37:5: 37:10 (#0),
},
Punct {
ch: ':',
spacing: Alone,
- span: $DIR/inner-attrs.rs:36:10: 36:11 (#0),
+ span: $DIR/inner-attrs.rs:37:10: 37:11 (#0),
},
Group {
delimiter: Bracket,
stream: TokenStream [
Ident {
ident: "u8",
- span: $DIR/inner-attrs.rs:36:13: 36:15 (#0),
+ span: $DIR/inner-attrs.rs:37:13: 37:15 (#0),
},
Punct {
ch: ';',
spacing: Alone,
- span: $DIR/inner-attrs.rs:36:15: 36:16 (#0),
+ span: $DIR/inner-attrs.rs:37:15: 37:16 (#0),
},
Group {
delimiter: Brace,
stream: TokenStream [
Ident {
ident: "match",
- span: $DIR/inner-attrs.rs:37:9: 37:14 (#0),
+ span: $DIR/inner-attrs.rs:38:9: 38:14 (#0),
},
Ident {
ident: "true",
- span: $DIR/inner-attrs.rs:37:15: 37:19 (#0),
+ span: $DIR/inner-attrs.rs:38:15: 38:19 (#0),
},
Group {
delimiter: Brace,
stream: TokenStream [
Ident {
ident: "_",
- span: $DIR/inner-attrs.rs:38:13: 38:14 (#0),
+ span: $DIR/inner-attrs.rs:39:13: 39:14 (#0),
},
Punct {
ch: '=',
spacing: Joint,
- span: $DIR/inner-attrs.rs:38:15: 38:17 (#0),
+ span: $DIR/inner-attrs.rs:39:15: 39:17 (#0),
},
Punct {
ch: '>',
spacing: Alone,
- span: $DIR/inner-attrs.rs:38:15: 38:17 (#0),
+ span: $DIR/inner-attrs.rs:39:15: 39:17 (#0),
},
Group {
delimiter: Brace,
Punct {
ch: '#',
spacing: Alone,
- span: $DIR/inner-attrs.rs:39:17: 39:18 (#0),
+ span: $DIR/inner-attrs.rs:40:17: 40:18 (#0),
},
Punct {
ch: '!',
spacing: Alone,
- span: $DIR/inner-attrs.rs:39:18: 39:19 (#0),
+ span: $DIR/inner-attrs.rs:40:18: 40:19 (#0),
},
Group {
delimiter: Bracket,
stream: TokenStream [
Ident {
ident: "rustc_dummy",
- span: $DIR/inner-attrs.rs:39:41: 39:52 (#0),
+ span: $DIR/inner-attrs.rs:40:41: 40:52 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "third",
- span: $DIR/inner-attrs.rs:39:53: 39:58 (#0),
+ span: $DIR/inner-attrs.rs:40:53: 40:58 (#0),
},
],
- span: $DIR/inner-attrs.rs:39:52: 39:59 (#0),
+ span: $DIR/inner-attrs.rs:40:52: 40:59 (#0),
},
],
- span: $DIR/inner-attrs.rs:39:17: 39:18 (#0),
+ span: $DIR/inner-attrs.rs:40:17: 40:18 (#0),
},
Ident {
ident: "true",
- span: $DIR/inner-attrs.rs:40:17: 40:21 (#0),
+ span: $DIR/inner-attrs.rs:41:17: 41:21 (#0),
},
],
- span: $DIR/inner-attrs.rs:38:18: 41:14 (#0),
+ span: $DIR/inner-attrs.rs:39:18: 42:14 (#0),
},
],
- span: $DIR/inner-attrs.rs:37:20: 42:10 (#0),
+ span: $DIR/inner-attrs.rs:38:20: 43:10 (#0),
},
Punct {
ch: ';',
spacing: Alone,
- span: $DIR/inner-attrs.rs:42:10: 42:11 (#0),
+ span: $DIR/inner-attrs.rs:43:10: 43:11 (#0),
},
Literal {
kind: Integer,
symbol: "0",
suffix: None,
- span: $DIR/inner-attrs.rs:43:9: 43:10 (#0),
+ span: $DIR/inner-attrs.rs:44:9: 44:10 (#0),
},
],
- span: $DIR/inner-attrs.rs:36:17: 44:6 (#0),
+ span: $DIR/inner-attrs.rs:37:17: 45:6 (#0),
},
],
- span: $DIR/inner-attrs.rs:36:12: 44:7 (#0),
+ span: $DIR/inner-attrs.rs:37:12: 45:7 (#0),
},
],
- span: $DIR/inner-attrs.rs:35:22: 45:2 (#0),
+ span: $DIR/inner-attrs.rs:36:22: 46:2 (#0),
},
]
PRINT-ATTR_ARGS INPUT (DISPLAY): tuple_attrs
PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [
Ident {
ident: "tuple_attrs",
- span: $DIR/inner-attrs.rs:48:29: 48:40 (#0),
+ span: $DIR/inner-attrs.rs:49:29: 49:40 (#0),
},
]
PRINT-ATTR INPUT (DISPLAY): (3, 4, { #! [cfg_attr(not(FALSE), rustc_dummy(innermost))] 5 }) ;
kind: Integer,
symbol: "3",
suffix: None,
- span: $DIR/inner-attrs.rs:49:9: 49:10 (#0),
+ span: $DIR/inner-attrs.rs:50:9: 50:10 (#0),
},
Punct {
ch: ',',
spacing: Alone,
- span: $DIR/inner-attrs.rs:49:10: 49:11 (#0),
+ span: $DIR/inner-attrs.rs:50:10: 50:11 (#0),
},
Literal {
kind: Integer,
symbol: "4",
suffix: None,
- span: $DIR/inner-attrs.rs:49:12: 49:13 (#0),
+ span: $DIR/inner-attrs.rs:50:12: 50:13 (#0),
},
Punct {
ch: ',',
spacing: Alone,
- span: $DIR/inner-attrs.rs:49:13: 49:14 (#0),
+ span: $DIR/inner-attrs.rs:50:13: 50:14 (#0),
},
Group {
delimiter: Brace,
Punct {
ch: '#',
spacing: Joint,
- span: $DIR/inner-attrs.rs:50:13: 50:14 (#0),
+ span: $DIR/inner-attrs.rs:51:13: 51:14 (#0),
},
Punct {
ch: '!',
spacing: Alone,
- span: $DIR/inner-attrs.rs:50:14: 50:15 (#0),
+ span: $DIR/inner-attrs.rs:51:14: 51:15 (#0),
},
Group {
delimiter: Bracket,
stream: TokenStream [
Ident {
ident: "cfg_attr",
- span: $DIR/inner-attrs.rs:50:16: 50:24 (#0),
+ span: $DIR/inner-attrs.rs:51:16: 51:24 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "not",
- span: $DIR/inner-attrs.rs:50:25: 50:28 (#0),
+ span: $DIR/inner-attrs.rs:51:25: 51:28 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "FALSE",
- span: $DIR/inner-attrs.rs:50:29: 50:34 (#0),
+ span: $DIR/inner-attrs.rs:51:29: 51:34 (#0),
},
],
- span: $DIR/inner-attrs.rs:50:28: 50:35 (#0),
+ span: $DIR/inner-attrs.rs:51:28: 51:35 (#0),
},
Punct {
ch: ',',
spacing: Alone,
- span: $DIR/inner-attrs.rs:50:35: 50:36 (#0),
+ span: $DIR/inner-attrs.rs:51:35: 51:36 (#0),
},
Ident {
ident: "rustc_dummy",
- span: $DIR/inner-attrs.rs:50:37: 50:48 (#0),
+ span: $DIR/inner-attrs.rs:51:37: 51:48 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "innermost",
- span: $DIR/inner-attrs.rs:50:49: 50:58 (#0),
+ span: $DIR/inner-attrs.rs:51:49: 51:58 (#0),
},
],
- span: $DIR/inner-attrs.rs:50:48: 50:59 (#0),
+ span: $DIR/inner-attrs.rs:51:48: 51:59 (#0),
},
],
- span: $DIR/inner-attrs.rs:50:24: 50:60 (#0),
+ span: $DIR/inner-attrs.rs:51:24: 51:60 (#0),
},
],
- span: $DIR/inner-attrs.rs:50:15: 50:61 (#0),
+ span: $DIR/inner-attrs.rs:51:15: 51:61 (#0),
},
Literal {
kind: Integer,
symbol: "5",
suffix: None,
- span: $DIR/inner-attrs.rs:51:13: 51:14 (#0),
+ span: $DIR/inner-attrs.rs:52:13: 52:14 (#0),
},
],
- span: $DIR/inner-attrs.rs:49:15: 52:10 (#0),
+ span: $DIR/inner-attrs.rs:50:15: 53:10 (#0),
},
],
- span: $DIR/inner-attrs.rs:48:43: 53:6 (#0),
+ span: $DIR/inner-attrs.rs:49:43: 54:6 (#0),
},
Punct {
ch: ';',
spacing: Alone,
- span: $DIR/inner-attrs.rs:53:6: 53:7 (#0),
+ span: $DIR/inner-attrs.rs:54:6: 54:7 (#0),
},
]
PRINT-ATTR_ARGS INPUT (DISPLAY): tuple_attrs
PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [
Ident {
ident: "tuple_attrs",
- span: $DIR/inner-attrs.rs:55:29: 55:40 (#0),
+ span: $DIR/inner-attrs.rs:56:29: 56:40 (#0),
},
]
PRINT-ATTR INPUT (DISPLAY): (3, 4, { #! [cfg_attr(not(FALSE), rustc_dummy(innermost))] 5 }) ;
kind: Integer,
symbol: "3",
suffix: None,
- span: $DIR/inner-attrs.rs:56:9: 56:10 (#0),
+ span: $DIR/inner-attrs.rs:57:9: 57:10 (#0),
},
Punct {
ch: ',',
spacing: Alone,
- span: $DIR/inner-attrs.rs:56:10: 56:11 (#0),
+ span: $DIR/inner-attrs.rs:57:10: 57:11 (#0),
},
Literal {
kind: Integer,
symbol: "4",
suffix: None,
- span: $DIR/inner-attrs.rs:56:12: 56:13 (#0),
+ span: $DIR/inner-attrs.rs:57:12: 57:13 (#0),
},
Punct {
ch: ',',
spacing: Alone,
- span: $DIR/inner-attrs.rs:56:13: 56:14 (#0),
+ span: $DIR/inner-attrs.rs:57:13: 57:14 (#0),
},
Group {
delimiter: Brace,
Punct {
ch: '#',
spacing: Joint,
- span: $DIR/inner-attrs.rs:57:13: 57:14 (#0),
+ span: $DIR/inner-attrs.rs:58:13: 58:14 (#0),
},
Punct {
ch: '!',
spacing: Alone,
- span: $DIR/inner-attrs.rs:57:14: 57:15 (#0),
+ span: $DIR/inner-attrs.rs:58:14: 58:15 (#0),
},
Group {
delimiter: Bracket,
stream: TokenStream [
Ident {
ident: "cfg_attr",
- span: $DIR/inner-attrs.rs:57:16: 57:24 (#0),
+ span: $DIR/inner-attrs.rs:58:16: 58:24 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "not",
- span: $DIR/inner-attrs.rs:57:25: 57:28 (#0),
+ span: $DIR/inner-attrs.rs:58:25: 58:28 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "FALSE",
- span: $DIR/inner-attrs.rs:57:29: 57:34 (#0),
+ span: $DIR/inner-attrs.rs:58:29: 58:34 (#0),
},
],
- span: $DIR/inner-attrs.rs:57:28: 57:35 (#0),
+ span: $DIR/inner-attrs.rs:58:28: 58:35 (#0),
},
Punct {
ch: ',',
spacing: Alone,
- span: $DIR/inner-attrs.rs:57:35: 57:36 (#0),
+ span: $DIR/inner-attrs.rs:58:35: 58:36 (#0),
},
Ident {
ident: "rustc_dummy",
- span: $DIR/inner-attrs.rs:57:37: 57:48 (#0),
+ span: $DIR/inner-attrs.rs:58:37: 58:48 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "innermost",
- span: $DIR/inner-attrs.rs:57:49: 57:58 (#0),
+ span: $DIR/inner-attrs.rs:58:49: 58:58 (#0),
},
],
- span: $DIR/inner-attrs.rs:57:48: 57:59 (#0),
+ span: $DIR/inner-attrs.rs:58:48: 58:59 (#0),
},
],
- span: $DIR/inner-attrs.rs:57:24: 57:60 (#0),
+ span: $DIR/inner-attrs.rs:58:24: 58:60 (#0),
},
],
- span: $DIR/inner-attrs.rs:57:15: 57:61 (#0),
+ span: $DIR/inner-attrs.rs:58:15: 58:61 (#0),
},
Literal {
kind: Integer,
symbol: "5",
suffix: None,
- span: $DIR/inner-attrs.rs:58:13: 58:14 (#0),
+ span: $DIR/inner-attrs.rs:59:13: 59:14 (#0),
},
],
- span: $DIR/inner-attrs.rs:56:15: 59:10 (#0),
+ span: $DIR/inner-attrs.rs:57:15: 60:10 (#0),
},
],
- span: $DIR/inner-attrs.rs:55:43: 60:6 (#0),
+ span: $DIR/inner-attrs.rs:56:43: 61:6 (#0),
},
Punct {
ch: ';',
spacing: Alone,
- span: $DIR/inner-attrs.rs:60:6: 60:7 (#0),
+ span: $DIR/inner-attrs.rs:61:6: 61:7 (#0),
},
]
PRINT-ATTR_ARGS INPUT (DISPLAY): tenth
PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [
Ident {
ident: "tenth",
- span: $DIR/inner-attrs.rs:82:42: 82:47 (#0),
+ span: $DIR/inner-attrs.rs:83:42: 83:47 (#0),
},
]
PRINT-ATTR INPUT (DISPLAY): fn weird_extern() {}
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "fn",
- span: $DIR/inner-attrs.rs:81:5: 81:7 (#0),
+ span: $DIR/inner-attrs.rs:82:5: 82:7 (#0),
},
Ident {
ident: "weird_extern",
- span: $DIR/inner-attrs.rs:81:8: 81:20 (#0),
+ span: $DIR/inner-attrs.rs:82:8: 82:20 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [],
- span: $DIR/inner-attrs.rs:81:20: 81:22 (#0),
+ span: $DIR/inner-attrs.rs:82:20: 82:22 (#0),
},
Group {
delimiter: Brace,
stream: TokenStream [],
- span: $DIR/inner-attrs.rs:81:23: 83:6 (#0),
+ span: $DIR/inner-attrs.rs:82:23: 84:6 (#0),
},
]
+++ /dev/null
-// aux-build:test-macros.rs
-
-// FIXME: https://github.com/rust-lang/rust/issues/41430
-// This is a temporary regression test for the ICE reported in #41211
-
-#![feature(custom_inner_attributes)]
-#![feature(register_attr)]
-
-#![register_attr(identity_attr)]
-
-#![identity_attr]
-//~^ ERROR `identity_attr` is ambiguous
-extern crate test_macros;
-use test_macros::identity_attr;
-
-fn main() {}
+++ /dev/null
-error[E0659]: `identity_attr` is ambiguous
- --> $DIR/issue-41211.rs:11:4
- |
-LL | #![identity_attr]
- | ^^^^^^^^^^^^^ ambiguous name
- |
- = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution
-note: `identity_attr` could refer to the attribute macro imported here
- --> $DIR/issue-41211.rs:14:5
- |
-LL | use test_macros::identity_attr;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
- = help: use `crate::identity_attr` to refer to this attribute macro unambiguously
-note: `identity_attr` could also refer to the explicitly registered attribute defined here
- --> $DIR/issue-41211.rs:9:18
- |
-LL | #![register_attr(identity_attr)]
- | ^^^^^^^^^^^^^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0659`.
// by the function.
//
// This works today, which precludes changing things so that closures
-// follow the same lifetime-elision rules used elsehwere. See
+// follow the same lifetime-elision rules used elsewhere. See
// rust-lang/rust#56537
// check-pass
Unit,
}
+enum E {
+ TupleWithFields(()),
+}
+
fn main() {
// Only variants without fields are suggested (and others mentioned in a note) where an enum
// is used rather than a variant.
//~^ ERROR expected value, found enum `C`
D.foo();
//~^ ERROR expected value, found enum `D`
+ E.foo();
+ //~^ ERROR expected value, found enum `E`
// Only tuple variants are suggested in calls or tuple struct pattern matching.
error[E0423]: expected value, found enum `A`
- --> $DIR/issue-73427.rs:29:5
+ --> $DIR/issue-73427.rs:33:5
|
LL | A.foo();
| ^
| ~~~~~~~~~~~~
LL | A::Unit.foo();
| ~~~~~~~
-help: the following enum variants are available
+help: alternatively, the following enum variants are also available
|
LL | (A::StructWithFields { /* fields */ }).foo();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0423]: expected value, found enum `B`
- --> $DIR/issue-73427.rs:31:5
+ --> $DIR/issue-73427.rs:35:5
|
LL | B.foo();
| ^
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0423]: expected value, found enum `C`
- --> $DIR/issue-73427.rs:33:5
+ --> $DIR/issue-73427.rs:37:5
|
LL | C.foo();
| ^
|
LL | C::Unit.foo();
| ~~~~~~~
-help: the following enum variants are available
+help: alternatively, the following enum variants are also available
|
LL | (C::StructWithFields { /* fields */ }).foo();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0423]: expected value, found enum `D`
- --> $DIR/issue-73427.rs:35:5
+ --> $DIR/issue-73427.rs:39:5
|
LL | D.foo();
| ^
|
LL | D::Unit.foo();
| ~~~~~~~
-help: the following enum variant is available
+help: alternatively, the following enum variant is available
|
LL | (D::TupleWithFields(/* fields */)).foo();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+error[E0423]: expected value, found enum `E`
+ --> $DIR/issue-73427.rs:41:5
+ |
+LL | E.foo();
+ | ^
+ |
+note: the enum is defined here
+ --> $DIR/issue-73427.rs:25:1
+ |
+LL | / enum E {
+LL | | TupleWithFields(()),
+LL | | }
+ | |_^
+help: the following enum variant is available
+ |
+LL | (E::TupleWithFields(/* fields */)).foo();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: consider importing one of these items instead
+ |
+LL | use std::f32::consts::E;
+ |
+LL | use std::f64::consts::E;
+ |
+
error[E0423]: expected function, tuple struct or tuple variant, found enum `A`
- --> $DIR/issue-73427.rs:40:13
+ --> $DIR/issue-73427.rs:46:13
|
LL | let x = A(3);
| ^
| ~~~~~~~~~~~~~~~~~~
error[E0532]: expected tuple struct or tuple variant, found enum `A`
- --> $DIR/issue-73427.rs:42:12
+ --> $DIR/issue-73427.rs:48:12
|
LL | if let A(3) = x { }
| ^
LL | if let A::TupleWithFields(3) = x { }
| ~~~~~~~~~~~~~~~~~~
-error: aborting due to 6 previous errors
+error: aborting due to 7 previous errors
Some errors have detailed explanations: E0423, E0532.
For more information about an error, try `rustc --explain E0423`.
|
LL | m::Z::Unit;
| ~~~~~~~~~~
-help: the following enum variants are available
+help: alternatively, the following enum variants are also available
|
LL | (m::Z::Fn(/* fields */));
| ~~~~~~~~~~~~~~~~~~~~~~~~
|
LL | m::Z::Unit;
| ~~~~~~~~~~
-help: the following enum variants are available
+help: alternatively, the following enum variants are also available
|
LL | (m::Z::Fn(/* fields */));
| ~~~~~~~~~~~~~~~~~~~~~~~~
|
LL | let _: E = E::Unit;
| ~~~~~~~
-help: the following enum variants are available
+help: alternatively, the following enum variants are also available
|
LL | let _: E = (E::Fn(/* fields */));
| ~~~~~~~~~~~~~~~~~~~~~
|
LL | let _: E = E::Unit;
| ~~~~~~~
-help: the following enum variants are available
+help: alternatively, the following enum variants are also available
|
LL | let _: E = (E::Fn(/* fields */));
| ~~~~~~~~~~~~~~~~~~~~~
|
LL | let _: Z = m::Z::Unit;
| ~~~~~~~~~~
-help: the following enum variants are available
+help: alternatively, the following enum variants are also available
|
LL | let _: Z = (m::Z::Fn(/* fields */));
| ~~~~~~~~~~~~~~~~~~~~~~~~
found fn item `fn(u8) -> Z {Z::Fn}`
help: use parentheses to instantiate this tuple variant
|
-LL | let _: Z = Z::Fn(_);
- | +++
+LL | let _: Z = Z::Fn(/* u8 */);
+ | ++++++++++
error[E0618]: expected function, found enum variant `Z::Unit`
--> $DIR/privacy-enum-ctor.rs:31:17
found fn item `fn(u8) -> E {E::Fn}`
help: use parentheses to instantiate this tuple variant
|
-LL | let _: E = m::E::Fn(_);
- | +++
+LL | let _: E = m::E::Fn(/* u8 */);
+ | ++++++++++
error[E0618]: expected function, found enum variant `m::E::Unit`
--> $DIR/privacy-enum-ctor.rs:47:16
found fn item `fn(u8) -> E {E::Fn}`
help: use parentheses to instantiate this tuple variant
|
-LL | let _: E = E::Fn(_);
- | +++
+LL | let _: E = E::Fn(/* u8 */);
+ | ++++++++++
error[E0618]: expected function, found enum variant `E::Unit`
--> $DIR/privacy-enum-ctor.rs:55:16
//~^ ERROR `if let` guards are experimental
() if (let 0 = 1) => {}
- //~^ ERROR expected expression, found `let` statement
+ //~^ ERROR `let` expressions in this position are unstable
+ //~| ERROR expected expression, found `let` statement
() if (((let 0 = 1))) => {}
- //~^ ERROR expected expression, found `let` statement
+ //~^ ERROR `let` expressions in this position are unstable
+ //~| ERROR expected expression, found `let` statement
() if true && let 0 = 1 => {}
//~^ ERROR `if let` guards are experimental
+ //~| ERROR `let` expressions in this position are unstable
() if let 0 = 1 && true => {}
//~^ ERROR `if let` guards are experimental
+ //~| ERROR `let` expressions in this position are unstable
() if (let 0 = 1) && true => {}
- //~^ ERROR expected expression, found `let` statement
+ //~^ ERROR `let` expressions in this position are unstable
+ //~| ERROR expected expression, found `let` statement
() if true && (let 0 = 1) => {}
- //~^ ERROR expected expression, found `let` statement
+ //~^ ERROR `let` expressions in this position are unstable
+ //~| ERROR expected expression, found `let` statement
() if (let 0 = 1) && (let 0 = 1) => {}
- //~^ ERROR expected expression, found `let` statement
+ //~^ ERROR `let` expressions in this position are unstable
+ //~| ERROR `let` expressions in this position are unstable
+ //~| ERROR expected expression, found `let` statement
//~| ERROR expected expression, found `let` statement
() if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
//~^ ERROR `if let` guards are experimental
+ //~| ERROR `let` expressions in this position are unstable
+ //~| ERROR `let` expressions in this position are unstable
+ //~| ERROR `let` expressions in this position are unstable
+ //~| ERROR `let` expressions in this position are unstable
+ //~| ERROR `let` expressions in this position are unstable
//~| ERROR expected expression, found `let` statement
//~| ERROR expected expression, found `let` statement
//~| ERROR expected expression, found `let` statement
() if let Range { start: _, end: _ } = (true..true) && false => {}
//~^ ERROR `if let` guards are experimental
+ //~| ERROR `let` expressions in this position are unstable
_ => {}
}
}
}
use_expr!((let 0 = 1 && 0 == 0));
- //~^ ERROR expected expression, found `let` statement
+ //~^ ERROR `let` expressions in this position are unstable
+ //~| ERROR expected expression, found `let` statement
use_expr!((let 0 = 1));
- //~^ ERROR expected expression, found `let` statement
+ //~^ ERROR `let` expressions in this position are unstable
+ //~| ERROR expected expression, found `let` statement
match () {
#[cfg(FALSE)]
() if let 0 = 1 => {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/feature-gate.rs:13:18
+ --> $DIR/feature-gate.rs:14:18
|
LL | () if (((let 0 = 1))) => {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/feature-gate.rs:22:16
+ --> $DIR/feature-gate.rs:26:16
|
LL | () if (let 0 = 1) && true => {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/feature-gate.rs:25:24
+ --> $DIR/feature-gate.rs:30:24
|
LL | () if true && (let 0 = 1) => {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/feature-gate.rs:28:16
+ --> $DIR/feature-gate.rs:34:16
|
LL | () if (let 0 = 1) && (let 0 = 1) => {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/feature-gate.rs:28:31
+ --> $DIR/feature-gate.rs:34:31
|
LL | () if (let 0 = 1) && (let 0 = 1) => {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/feature-gate.rs:32:42
+ --> $DIR/feature-gate.rs:40:42
|
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/feature-gate.rs:32:55
+ --> $DIR/feature-gate.rs:40:55
|
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/feature-gate.rs:32:68
+ --> $DIR/feature-gate.rs:40:68
|
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/feature-gate.rs:54:16
+ --> $DIR/feature-gate.rs:68:16
|
LL | use_expr!((let 0 = 1 && 0 == 0));
| ^^^
error: expected expression, found `let` statement
- --> $DIR/feature-gate.rs:56:16
+ --> $DIR/feature-gate.rs:71:16
|
LL | use_expr!((let 0 = 1));
| ^^^
error: no rules expected the token `let`
- --> $DIR/feature-gate.rs:64:15
+ --> $DIR/feature-gate.rs:80:15
|
LL | macro_rules! use_expr {
| --------------------- when calling this macro
= help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
error[E0658]: `if let` guards are experimental
- --> $DIR/feature-gate.rs:16:12
+ --> $DIR/feature-gate.rs:18:12
|
LL | () if true && let 0 = 1 => {}
| ^^^^^^^^^^^^^^^^^^^^
= help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
error[E0658]: `if let` guards are experimental
- --> $DIR/feature-gate.rs:19:12
+ --> $DIR/feature-gate.rs:22:12
|
LL | () if let 0 = 1 && true => {}
| ^^^^^^^^^^^^^^^^^^^^
= help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
error[E0658]: `if let` guards are experimental
- --> $DIR/feature-gate.rs:32:12
+ --> $DIR/feature-gate.rs:40:12
|
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
error[E0658]: `if let` guards are experimental
- --> $DIR/feature-gate.rs:38:12
+ --> $DIR/feature-gate.rs:51:12
|
LL | () if let Range { start: _, end: _ } = (true..true) && false => {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
error[E0658]: `if let` guards are experimental
- --> $DIR/feature-gate.rs:60:12
+ --> $DIR/feature-gate.rs:76:12
|
LL | () if let 0 = 1 => {}
| ^^^^^^^^^^^^
= help: add `#![feature(if_let_guard)]` to the crate attributes to enable
= help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
-error: aborting due to 18 previous errors
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:10:16
+ |
+LL | () if (let 0 = 1) => {}
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:14:18
+ |
+LL | () if (((let 0 = 1))) => {}
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:18:23
+ |
+LL | () if true && let 0 = 1 => {}
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:22:15
+ |
+LL | () if let 0 = 1 && true => {}
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:26:16
+ |
+LL | () if (let 0 = 1) && true => {}
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:30:24
+ |
+LL | () if true && (let 0 = 1) => {}
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:34:16
+ |
+LL | () if (let 0 = 1) && (let 0 = 1) => {}
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:34:31
+ |
+LL | () if (let 0 = 1) && (let 0 = 1) => {}
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:40:15
+ |
+LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:40:28
+ |
+LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:40:42
+ |
+LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:40:55
+ |
+LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:40:68
+ |
+LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:51:15
+ |
+LL | () if let Range { start: _, end: _ } = (true..true) && false => {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:68:16
+ |
+LL | use_expr!((let 0 = 1 && 0 == 0));
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:71:16
+ |
+LL | use_expr!((let 0 = 1));
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error: aborting due to 34 previous errors
For more information about this error, try `rustc --explain E0658`.
+++ /dev/null
-// check-pass
-
-#![allow(irrefutable_let_patterns)]
-
-use std::ops::Range;
-
-fn _if() {
- if let 0 = 1 {}
-
- if true && let 0 = 1 {}
-
- if let 0 = 1 && true {}
-
- if let Range { start: _, end: _ } = (true..true) && false {}
-
- if let 1 = 1 && let true = { true } && false {
- }
-}
-
-fn _while() {
- while let 0 = 1 {}
-
- while true && let 0 = 1 {}
-
- while let 0 = 1 && true {}
-
- while let Range { start: _, end: _ } = (true..true) && false {}
-}
-
-fn main() {}
// run-pass
+#![feature(let_chains)]
#![allow(irrefutable_let_patterns)]
fn main() {
//
// To that end, we check some positions which is not part of the language above.
+#![feature(let_chains)] // Avoid inflating `.stderr` with overzealous gates in this test.
+
#![allow(irrefutable_let_patterns)]
use std::ops::Range;
//~^ ERROR `let` expressions are not supported here
//~| ERROR `let` expressions are not supported here
//~| ERROR expected expression, found `let` statement
- use_expr!(true && let 0 = 1);
- //~^ ERROR expected expression, found `let` statement
-
- macro_rules! noop_expr { ($e:expr) => {}; }
- noop_expr!((let 0 = 1));
- //~^ ERROR expected expression, found `let` statement
}
fn nested_within_if_expr() {
([1, 2, 3][let _ = ()])
//~^ ERROR expected expression, found `let` statement
}
-
- #[cfg(FALSE)] (let 0 = 1);
- //~^ ERROR expected expression, found `let` statement
}
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:27:9
+ --> $DIR/disallowed-positions.rs:29:9
|
LL | if (let 0 = 1) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:31:11
+ --> $DIR/disallowed-positions.rs:33:11
|
LL | if (((let 0 = 1))) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:35:9
+ --> $DIR/disallowed-positions.rs:37:9
|
LL | if (let 0 = 1) && true {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:39:17
+ --> $DIR/disallowed-positions.rs:41:17
|
LL | if true && (let 0 = 1) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:43:9
+ --> $DIR/disallowed-positions.rs:45:9
|
LL | if (let 0 = 1) && (let 0 = 1) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:43:24
+ --> $DIR/disallowed-positions.rs:45:24
|
LL | if (let 0 = 1) && (let 0 = 1) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:49:35
+ --> $DIR/disallowed-positions.rs:51:35
|
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:49:48
+ --> $DIR/disallowed-positions.rs:51:48
|
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:49:61
+ --> $DIR/disallowed-positions.rs:51:61
|
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:59:12
+ --> $DIR/disallowed-positions.rs:61:12
|
LL | while (let 0 = 1) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:63:14
+ --> $DIR/disallowed-positions.rs:65:14
|
LL | while (((let 0 = 1))) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:67:12
+ --> $DIR/disallowed-positions.rs:69:12
|
LL | while (let 0 = 1) && true {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:71:20
+ --> $DIR/disallowed-positions.rs:73:20
|
LL | while true && (let 0 = 1) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:75:12
+ --> $DIR/disallowed-positions.rs:77:12
|
LL | while (let 0 = 1) && (let 0 = 1) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:75:27
+ --> $DIR/disallowed-positions.rs:77:27
|
LL | while (let 0 = 1) && (let 0 = 1) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:81:38
+ --> $DIR/disallowed-positions.rs:83:38
|
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:81:51
+ --> $DIR/disallowed-positions.rs:83:51
|
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:81:64
+ --> $DIR/disallowed-positions.rs:83:64
|
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:114:9
+ --> $DIR/disallowed-positions.rs:110:9
|
LL | if &let 0 = 0 {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:119:9
+ --> $DIR/disallowed-positions.rs:115:9
|
LL | if !let 0 = 0 {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:122:9
+ --> $DIR/disallowed-positions.rs:118:9
|
LL | if *let 0 = 0 {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:126:9
+ --> $DIR/disallowed-positions.rs:122:9
|
LL | if -let 0 = 0 {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:136:9
+ --> $DIR/disallowed-positions.rs:132:9
|
LL | if (let 0 = 0)? {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:142:16
+ --> $DIR/disallowed-positions.rs:138:16
|
LL | if true || let 0 = 0 {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:145:17
+ --> $DIR/disallowed-positions.rs:141:17
|
LL | if (true || let 0 = 0) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:148:25
+ --> $DIR/disallowed-positions.rs:144:25
|
LL | if true && (true || let 0 = 0) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:151:25
+ --> $DIR/disallowed-positions.rs:147:25
|
LL | if true || (true && let 0 = 0) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:156:12
+ --> $DIR/disallowed-positions.rs:152:12
|
LL | if x = let 0 = 0 {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:161:15
+ --> $DIR/disallowed-positions.rs:157:15
|
LL | if true..(let 0 = 0) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:165:11
+ --> $DIR/disallowed-positions.rs:161:11
|
LL | if ..(let 0 = 0) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:169:9
+ --> $DIR/disallowed-positions.rs:165:9
|
LL | if (let 0 = 0).. {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:200:19
+ --> $DIR/disallowed-positions.rs:196:19
|
LL | if let true = let true = true {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:206:12
+ --> $DIR/disallowed-positions.rs:202:12
|
LL | while &let 0 = 0 {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:211:12
+ --> $DIR/disallowed-positions.rs:207:12
|
LL | while !let 0 = 0 {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:214:12
+ --> $DIR/disallowed-positions.rs:210:12
|
LL | while *let 0 = 0 {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:218:12
+ --> $DIR/disallowed-positions.rs:214:12
|
LL | while -let 0 = 0 {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:228:12
+ --> $DIR/disallowed-positions.rs:224:12
|
LL | while (let 0 = 0)? {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:234:19
+ --> $DIR/disallowed-positions.rs:230:19
|
LL | while true || let 0 = 0 {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:237:20
+ --> $DIR/disallowed-positions.rs:233:20
|
LL | while (true || let 0 = 0) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:240:28
+ --> $DIR/disallowed-positions.rs:236:28
|
LL | while true && (true || let 0 = 0) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:243:28
+ --> $DIR/disallowed-positions.rs:239:28
|
LL | while true || (true && let 0 = 0) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:248:15
+ --> $DIR/disallowed-positions.rs:244:15
|
LL | while x = let 0 = 0 {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:253:18
+ --> $DIR/disallowed-positions.rs:249:18
|
LL | while true..(let 0 = 0) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:257:14
+ --> $DIR/disallowed-positions.rs:253:14
|
LL | while ..(let 0 = 0) {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:261:12
+ --> $DIR/disallowed-positions.rs:257:12
|
LL | while (let 0 = 0).. {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:292:22
+ --> $DIR/disallowed-positions.rs:288:22
|
LL | while let true = let true = true {}
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:308:6
+ --> $DIR/disallowed-positions.rs:304:6
|
LL | &let 0 = 0;
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:312:6
+ --> $DIR/disallowed-positions.rs:308:6
|
LL | !let 0 = 0;
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:315:6
+ --> $DIR/disallowed-positions.rs:311:6
|
LL | *let 0 = 0;
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:319:6
+ --> $DIR/disallowed-positions.rs:315:6
|
LL | -let 0 = 0;
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:329:6
+ --> $DIR/disallowed-positions.rs:325:6
|
LL | (let 0 = 0)?;
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:335:13
+ --> $DIR/disallowed-positions.rs:331:13
|
LL | true || let 0 = 0;
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:338:14
+ --> $DIR/disallowed-positions.rs:334:14
|
LL | (true || let 0 = 0);
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:341:22
+ --> $DIR/disallowed-positions.rs:337:22
|
LL | true && (true || let 0 = 0);
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:346:9
+ --> $DIR/disallowed-positions.rs:342:9
|
LL | x = let 0 = 0;
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:350:12
+ --> $DIR/disallowed-positions.rs:346:12
|
LL | true..(let 0 = 0);
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:353:8
+ --> $DIR/disallowed-positions.rs:349:8
|
LL | ..(let 0 = 0);
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:356:6
+ --> $DIR/disallowed-positions.rs:352:6
|
LL | (let 0 = 0)..;
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:360:6
+ --> $DIR/disallowed-positions.rs:356:6
|
LL | (let Range { start: _, end: _ } = true..true || false);
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:365:6
+ --> $DIR/disallowed-positions.rs:361:6
|
LL | (let true = let true = true);
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:365:17
+ --> $DIR/disallowed-positions.rs:361:17
|
LL | (let true = let true = true);
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:372:25
+ --> $DIR/disallowed-positions.rs:368:25
|
LL | let x = true && let y = 1;
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:378:19
+ --> $DIR/disallowed-positions.rs:374:19
|
LL | [1, 2, 3][let _ = ()]
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:383:6
+ --> $DIR/disallowed-positions.rs:379:6
|
LL | &let 0 = 0
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:395:17
+ --> $DIR/disallowed-positions.rs:391:17
|
LL | true && let 1 = 1
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:401:17
+ --> $DIR/disallowed-positions.rs:397:17
|
LL | true && let 1 = 1
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:407:17
+ --> $DIR/disallowed-positions.rs:403:17
|
LL | true && let 1 = 1
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:419:17
+ --> $DIR/disallowed-positions.rs:415:17
|
LL | true && let 1 = 1
| ^^^
error: expressions must be enclosed in braces to be used as const generic arguments
- --> $DIR/disallowed-positions.rs:419:9
+ --> $DIR/disallowed-positions.rs:415:9
|
LL | true && let 1 = 1
| ^^^^^^^^^^^^^^^^^
| + +
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:429:9
+ --> $DIR/disallowed-positions.rs:425:9
|
LL | if (let Some(a) = opt && true) {
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:434:9
+ --> $DIR/disallowed-positions.rs:430:9
|
LL | if (let Some(a) = opt) && true {
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:438:9
+ --> $DIR/disallowed-positions.rs:434:9
|
LL | if (let Some(a) = opt) && (let Some(b) = a) {
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:438:32
+ --> $DIR/disallowed-positions.rs:434:32
|
LL | if (let Some(a) = opt) && (let Some(b) = a) {
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:447:9
+ --> $DIR/disallowed-positions.rs:443:9
|
LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:447:31
+ --> $DIR/disallowed-positions.rs:443:31
|
LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:453:9
+ --> $DIR/disallowed-positions.rs:449:9
|
LL | if (let Some(a) = opt && (let Some(b) = a)) && true {
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:453:31
+ --> $DIR/disallowed-positions.rs:449:31
|
LL | if (let Some(a) = opt && (let Some(b) = a)) && true {
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:459:9
+ --> $DIR/disallowed-positions.rs:455:9
|
LL | if (let Some(a) = opt && (true)) && true {
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:476:22
+ --> $DIR/disallowed-positions.rs:472:22
|
LL | let x = (true && let y = 1);
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:481:20
+ --> $DIR/disallowed-positions.rs:477:20
|
LL | ([1, 2, 3][let _ = ()])
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:485:20
- |
-LL | #[cfg(FALSE)] (let 0 = 1);
- | ^^^
-
-error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:97:16
+ --> $DIR/disallowed-positions.rs:99:16
|
LL | use_expr!((let 0 = 1 && 0 == 0));
| ^^^
error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:101:16
+ --> $DIR/disallowed-positions.rs:103:16
|
LL | use_expr!((let 0 = 1));
| ^^^
-error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:105:23
- |
-LL | use_expr!(true && let 0 = 1);
- | ^^^
-
-error: expected expression, found `let` statement
- --> $DIR/disallowed-positions.rs:109:17
- |
-LL | noop_expr!((let 0 = 1));
- | ^^^
-
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:27:9
+ --> $DIR/disallowed-positions.rs:29:9
|
LL | if (let 0 = 1) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:27:9
+ --> $DIR/disallowed-positions.rs:29:9
|
LL | if (let 0 = 1) {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:31:11
+ --> $DIR/disallowed-positions.rs:33:11
|
LL | if (((let 0 = 1))) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:31:11
+ --> $DIR/disallowed-positions.rs:33:11
|
LL | if (((let 0 = 1))) {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:35:9
+ --> $DIR/disallowed-positions.rs:37:9
|
LL | if (let 0 = 1) && true {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:35:9
+ --> $DIR/disallowed-positions.rs:37:9
|
LL | if (let 0 = 1) && true {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:39:17
+ --> $DIR/disallowed-positions.rs:41:17
|
LL | if true && (let 0 = 1) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:39:17
+ --> $DIR/disallowed-positions.rs:41:17
|
LL | if true && (let 0 = 1) {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:43:9
+ --> $DIR/disallowed-positions.rs:45:9
|
LL | if (let 0 = 1) && (let 0 = 1) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:43:9
+ --> $DIR/disallowed-positions.rs:45:9
|
LL | if (let 0 = 1) && (let 0 = 1) {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:43:24
+ --> $DIR/disallowed-positions.rs:45:24
|
LL | if (let 0 = 1) && (let 0 = 1) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:43:24
+ --> $DIR/disallowed-positions.rs:45:24
|
LL | if (let 0 = 1) && (let 0 = 1) {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:49:35
+ --> $DIR/disallowed-positions.rs:51:35
|
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:49:35
+ --> $DIR/disallowed-positions.rs:51:35
|
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:49:48
+ --> $DIR/disallowed-positions.rs:51:48
|
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:49:35
+ --> $DIR/disallowed-positions.rs:51:35
|
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:49:61
+ --> $DIR/disallowed-positions.rs:51:61
|
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:49:35
+ --> $DIR/disallowed-positions.rs:51:35
|
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:59:12
+ --> $DIR/disallowed-positions.rs:61:12
|
LL | while (let 0 = 1) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:59:12
+ --> $DIR/disallowed-positions.rs:61:12
|
LL | while (let 0 = 1) {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:63:14
+ --> $DIR/disallowed-positions.rs:65:14
|
LL | while (((let 0 = 1))) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:63:14
+ --> $DIR/disallowed-positions.rs:65:14
|
LL | while (((let 0 = 1))) {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:67:12
+ --> $DIR/disallowed-positions.rs:69:12
|
LL | while (let 0 = 1) && true {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:67:12
+ --> $DIR/disallowed-positions.rs:69:12
|
LL | while (let 0 = 1) && true {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:71:20
+ --> $DIR/disallowed-positions.rs:73:20
|
LL | while true && (let 0 = 1) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:71:20
+ --> $DIR/disallowed-positions.rs:73:20
|
LL | while true && (let 0 = 1) {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:75:12
+ --> $DIR/disallowed-positions.rs:77:12
|
LL | while (let 0 = 1) && (let 0 = 1) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:75:12
+ --> $DIR/disallowed-positions.rs:77:12
|
LL | while (let 0 = 1) && (let 0 = 1) {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:75:27
+ --> $DIR/disallowed-positions.rs:77:27
|
LL | while (let 0 = 1) && (let 0 = 1) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:75:27
+ --> $DIR/disallowed-positions.rs:77:27
|
LL | while (let 0 = 1) && (let 0 = 1) {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:81:38
+ --> $DIR/disallowed-positions.rs:83:38
|
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:81:38
+ --> $DIR/disallowed-positions.rs:83:38
|
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:81:51
+ --> $DIR/disallowed-positions.rs:83:51
|
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:81:38
+ --> $DIR/disallowed-positions.rs:83:38
|
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:81:64
+ --> $DIR/disallowed-positions.rs:83:64
|
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:81:38
+ --> $DIR/disallowed-positions.rs:83:38
|
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:97:16
+ --> $DIR/disallowed-positions.rs:99:16
|
LL | use_expr!((let 0 = 1 && 0 == 0));
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:97:16
+ --> $DIR/disallowed-positions.rs:99:16
|
LL | use_expr!((let 0 = 1 && 0 == 0));
| ^^^^^^^^^^^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:97:16
+ --> $DIR/disallowed-positions.rs:99:16
|
LL | use_expr!((let 0 = 1 && 0 == 0));
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:97:16
+ --> $DIR/disallowed-positions.rs:99:16
|
LL | use_expr!((let 0 = 1 && 0 == 0));
| ^^^^^^^^^^^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:101:16
+ --> $DIR/disallowed-positions.rs:103:16
|
LL | use_expr!((let 0 = 1));
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:101:16
+ --> $DIR/disallowed-positions.rs:103:16
|
LL | use_expr!((let 0 = 1));
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:101:16
+ --> $DIR/disallowed-positions.rs:103:16
|
LL | use_expr!((let 0 = 1));
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:101:16
+ --> $DIR/disallowed-positions.rs:103:16
|
LL | use_expr!((let 0 = 1));
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:114:9
+ --> $DIR/disallowed-positions.rs:110:9
|
LL | if &let 0 = 0 {}
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:119:9
+ --> $DIR/disallowed-positions.rs:115:9
|
LL | if !let 0 = 0 {}
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:122:9
+ --> $DIR/disallowed-positions.rs:118:9
|
LL | if *let 0 = 0 {}
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:126:9
+ --> $DIR/disallowed-positions.rs:122:9
|
LL | if -let 0 = 0 {}
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:136:9
+ --> $DIR/disallowed-positions.rs:132:9
|
LL | if (let 0 = 0)? {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:136:9
+ --> $DIR/disallowed-positions.rs:132:9
|
LL | if (let 0 = 0)? {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:142:16
+ --> $DIR/disallowed-positions.rs:138:16
|
LL | if true || let 0 = 0 {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `||` operators are not supported in let chain expressions
- --> $DIR/disallowed-positions.rs:142:13
+ --> $DIR/disallowed-positions.rs:138:13
|
LL | if true || let 0 = 0 {}
| ^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:145:17
+ --> $DIR/disallowed-positions.rs:141:17
|
LL | if (true || let 0 = 0) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `||` operators are not supported in let chain expressions
- --> $DIR/disallowed-positions.rs:145:14
+ --> $DIR/disallowed-positions.rs:141:14
|
LL | if (true || let 0 = 0) {}
| ^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:148:25
+ --> $DIR/disallowed-positions.rs:144:25
|
LL | if true && (true || let 0 = 0) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `||` operators are not supported in let chain expressions
- --> $DIR/disallowed-positions.rs:148:22
+ --> $DIR/disallowed-positions.rs:144:22
|
LL | if true && (true || let 0 = 0) {}
| ^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:151:25
+ --> $DIR/disallowed-positions.rs:147:25
|
LL | if true || (true && let 0 = 0) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:151:17
+ --> $DIR/disallowed-positions.rs:147:17
|
LL | if true || (true && let 0 = 0) {}
| ^^^^^^^^^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:156:12
+ --> $DIR/disallowed-positions.rs:152:12
|
LL | if x = let 0 = 0 {}
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:161:15
+ --> $DIR/disallowed-positions.rs:157:15
|
LL | if true..(let 0 = 0) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:161:15
+ --> $DIR/disallowed-positions.rs:157:15
|
LL | if true..(let 0 = 0) {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:165:11
+ --> $DIR/disallowed-positions.rs:161:11
|
LL | if ..(let 0 = 0) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:165:11
+ --> $DIR/disallowed-positions.rs:161:11
|
LL | if ..(let 0 = 0) {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:169:9
+ --> $DIR/disallowed-positions.rs:165:9
|
LL | if (let 0 = 0).. {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:169:9
+ --> $DIR/disallowed-positions.rs:165:9
|
LL | if (let 0 = 0).. {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:175:8
+ --> $DIR/disallowed-positions.rs:171:8
|
LL | if let Range { start: _, end: _ } = true..true && false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:179:8
+ --> $DIR/disallowed-positions.rs:175:8
|
LL | if let Range { start: _, end: _ } = true..true || false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:186:8
+ --> $DIR/disallowed-positions.rs:182:8
|
LL | if let Range { start: F, end } = F..|| true {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:194:8
+ --> $DIR/disallowed-positions.rs:190:8
|
LL | if let Range { start: true, end } = t..&&false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:200:19
+ --> $DIR/disallowed-positions.rs:196:19
|
LL | if let true = let true = true {}
| ^^^^^^^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:206:12
+ --> $DIR/disallowed-positions.rs:202:12
|
LL | while &let 0 = 0 {}
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:211:12
+ --> $DIR/disallowed-positions.rs:207:12
|
LL | while !let 0 = 0 {}
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:214:12
+ --> $DIR/disallowed-positions.rs:210:12
|
LL | while *let 0 = 0 {}
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:218:12
+ --> $DIR/disallowed-positions.rs:214:12
|
LL | while -let 0 = 0 {}
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:228:12
+ --> $DIR/disallowed-positions.rs:224:12
|
LL | while (let 0 = 0)? {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:228:12
+ --> $DIR/disallowed-positions.rs:224:12
|
LL | while (let 0 = 0)? {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:234:19
+ --> $DIR/disallowed-positions.rs:230:19
|
LL | while true || let 0 = 0 {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `||` operators are not supported in let chain expressions
- --> $DIR/disallowed-positions.rs:234:16
+ --> $DIR/disallowed-positions.rs:230:16
|
LL | while true || let 0 = 0 {}
| ^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:237:20
+ --> $DIR/disallowed-positions.rs:233:20
|
LL | while (true || let 0 = 0) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `||` operators are not supported in let chain expressions
- --> $DIR/disallowed-positions.rs:237:17
+ --> $DIR/disallowed-positions.rs:233:17
|
LL | while (true || let 0 = 0) {}
| ^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:240:28
+ --> $DIR/disallowed-positions.rs:236:28
|
LL | while true && (true || let 0 = 0) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `||` operators are not supported in let chain expressions
- --> $DIR/disallowed-positions.rs:240:25
+ --> $DIR/disallowed-positions.rs:236:25
|
LL | while true && (true || let 0 = 0) {}
| ^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:243:28
+ --> $DIR/disallowed-positions.rs:239:28
|
LL | while true || (true && let 0 = 0) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:243:20
+ --> $DIR/disallowed-positions.rs:239:20
|
LL | while true || (true && let 0 = 0) {}
| ^^^^^^^^^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:248:15
+ --> $DIR/disallowed-positions.rs:244:15
|
LL | while x = let 0 = 0 {}
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:253:18
+ --> $DIR/disallowed-positions.rs:249:18
|
LL | while true..(let 0 = 0) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:253:18
+ --> $DIR/disallowed-positions.rs:249:18
|
LL | while true..(let 0 = 0) {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:257:14
+ --> $DIR/disallowed-positions.rs:253:14
|
LL | while ..(let 0 = 0) {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:257:14
+ --> $DIR/disallowed-positions.rs:253:14
|
LL | while ..(let 0 = 0) {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:261:12
+ --> $DIR/disallowed-positions.rs:257:12
|
LL | while (let 0 = 0).. {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:261:12
+ --> $DIR/disallowed-positions.rs:257:12
|
LL | while (let 0 = 0).. {}
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:267:11
+ --> $DIR/disallowed-positions.rs:263:11
|
LL | while let Range { start: _, end: _ } = true..true && false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:271:11
+ --> $DIR/disallowed-positions.rs:267:11
|
LL | while let Range { start: _, end: _ } = true..true || false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:278:11
+ --> $DIR/disallowed-positions.rs:274:11
|
LL | while let Range { start: F, end } = F..|| true {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:286:11
+ --> $DIR/disallowed-positions.rs:282:11
|
LL | while let Range { start: true, end } = t..&&false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:292:22
+ --> $DIR/disallowed-positions.rs:288:22
|
LL | while let true = let true = true {}
| ^^^^^^^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:308:6
+ --> $DIR/disallowed-positions.rs:304:6
|
LL | &let 0 = 0;
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:312:6
+ --> $DIR/disallowed-positions.rs:308:6
|
LL | !let 0 = 0;
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:315:6
+ --> $DIR/disallowed-positions.rs:311:6
|
LL | *let 0 = 0;
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:319:6
+ --> $DIR/disallowed-positions.rs:315:6
|
LL | -let 0 = 0;
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:329:6
+ --> $DIR/disallowed-positions.rs:325:6
|
LL | (let 0 = 0)?;
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:329:6
+ --> $DIR/disallowed-positions.rs:325:6
|
LL | (let 0 = 0)?;
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:335:13
+ --> $DIR/disallowed-positions.rs:331:13
|
LL | true || let 0 = 0;
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `||` operators are not supported in let chain expressions
- --> $DIR/disallowed-positions.rs:335:10
+ --> $DIR/disallowed-positions.rs:331:10
|
LL | true || let 0 = 0;
| ^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:338:14
+ --> $DIR/disallowed-positions.rs:334:14
|
LL | (true || let 0 = 0);
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `||` operators are not supported in let chain expressions
- --> $DIR/disallowed-positions.rs:338:11
+ --> $DIR/disallowed-positions.rs:334:11
|
LL | (true || let 0 = 0);
| ^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:341:22
+ --> $DIR/disallowed-positions.rs:337:22
|
LL | true && (true || let 0 = 0);
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `||` operators are not supported in let chain expressions
- --> $DIR/disallowed-positions.rs:341:19
+ --> $DIR/disallowed-positions.rs:337:19
|
LL | true && (true || let 0 = 0);
| ^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:346:9
+ --> $DIR/disallowed-positions.rs:342:9
|
LL | x = let 0 = 0;
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:350:12
+ --> $DIR/disallowed-positions.rs:346:12
|
LL | true..(let 0 = 0);
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:350:12
+ --> $DIR/disallowed-positions.rs:346:12
|
LL | true..(let 0 = 0);
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:353:8
+ --> $DIR/disallowed-positions.rs:349:8
|
LL | ..(let 0 = 0);
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:353:8
+ --> $DIR/disallowed-positions.rs:349:8
|
LL | ..(let 0 = 0);
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:356:6
+ --> $DIR/disallowed-positions.rs:352:6
|
LL | (let 0 = 0)..;
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:356:6
+ --> $DIR/disallowed-positions.rs:352:6
|
LL | (let 0 = 0)..;
| ^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:360:6
+ --> $DIR/disallowed-positions.rs:356:6
|
LL | (let Range { start: _, end: _ } = true..true || false);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:365:6
+ --> $DIR/disallowed-positions.rs:361:6
|
LL | (let true = let true = true);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:365:6
+ --> $DIR/disallowed-positions.rs:361:6
|
LL | (let true = let true = true);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:383:6
+ --> $DIR/disallowed-positions.rs:379:6
|
LL | &let 0 = 0
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:395:17
+ --> $DIR/disallowed-positions.rs:391:17
|
LL | true && let 1 = 1
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:401:17
+ --> $DIR/disallowed-positions.rs:397:17
|
LL | true && let 1 = 1
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:407:17
+ --> $DIR/disallowed-positions.rs:403:17
|
LL | true && let 1 = 1
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:419:17
+ --> $DIR/disallowed-positions.rs:415:17
|
LL | true && let 1 = 1
| ^^^^^^^^^
= note: only supported directly in conditions of `if` and `while` expressions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:429:9
+ --> $DIR/disallowed-positions.rs:425:9
|
LL | if (let Some(a) = opt && true) {
| ^^^^^^^^^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:429:9
+ --> $DIR/disallowed-positions.rs:425:9
|
LL | if (let Some(a) = opt && true) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:434:9
+ --> $DIR/disallowed-positions.rs:430:9
|
LL | if (let Some(a) = opt) && true {
| ^^^^^^^^^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:434:9
+ --> $DIR/disallowed-positions.rs:430:9
|
LL | if (let Some(a) = opt) && true {
| ^^^^^^^^^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:438:9
+ --> $DIR/disallowed-positions.rs:434:9
|
LL | if (let Some(a) = opt) && (let Some(b) = a) {
| ^^^^^^^^^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:438:9
+ --> $DIR/disallowed-positions.rs:434:9
|
LL | if (let Some(a) = opt) && (let Some(b) = a) {
| ^^^^^^^^^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:438:32
+ --> $DIR/disallowed-positions.rs:434:32
|
LL | if (let Some(a) = opt) && (let Some(b) = a) {
| ^^^^^^^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:438:32
+ --> $DIR/disallowed-positions.rs:434:32
|
LL | if (let Some(a) = opt) && (let Some(b) = a) {
| ^^^^^^^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:447:9
+ --> $DIR/disallowed-positions.rs:443:9
|
LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
| ^^^^^^^^^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:447:9
+ --> $DIR/disallowed-positions.rs:443:9
|
LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:447:31
+ --> $DIR/disallowed-positions.rs:443:31
|
LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
| ^^^^^^^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:447:31
+ --> $DIR/disallowed-positions.rs:443:31
|
LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
| ^^^^^^^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:453:9
+ --> $DIR/disallowed-positions.rs:449:9
|
LL | if (let Some(a) = opt && (let Some(b) = a)) && true {
| ^^^^^^^^^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:453:9
+ --> $DIR/disallowed-positions.rs:449:9
|
LL | if (let Some(a) = opt && (let Some(b) = a)) && true {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:453:31
+ --> $DIR/disallowed-positions.rs:449:31
|
LL | if (let Some(a) = opt && (let Some(b) = a)) && true {
| ^^^^^^^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:453:31
+ --> $DIR/disallowed-positions.rs:449:31
|
LL | if (let Some(a) = opt && (let Some(b) = a)) && true {
| ^^^^^^^^^^^^^^^
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:459:9
+ --> $DIR/disallowed-positions.rs:455:9
|
LL | if (let Some(a) = opt && (true)) && true {
| ^^^^^^^^^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
- --> $DIR/disallowed-positions.rs:459:9
+ --> $DIR/disallowed-positions.rs:455:9
|
LL | if (let Some(a) = opt && (true)) && true {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:114:8
+ --> $DIR/disallowed-positions.rs:110:8
|
LL | if &let 0 = 0 {}
| ^^^^^^^^^^ expected `bool`, found `&bool`
|
error[E0614]: type `bool` cannot be dereferenced
- --> $DIR/disallowed-positions.rs:122:8
+ --> $DIR/disallowed-positions.rs:118:8
|
LL | if *let 0 = 0 {}
| ^^^^^^^^^^
error[E0600]: cannot apply unary operator `-` to type `bool`
- --> $DIR/disallowed-positions.rs:126:8
+ --> $DIR/disallowed-positions.rs:122:8
|
LL | if -let 0 = 0 {}
| ^^^^^^^^^^ cannot apply unary operator `-`
error[E0277]: the `?` operator can only be applied to values that implement `Try`
- --> $DIR/disallowed-positions.rs:136:8
+ --> $DIR/disallowed-positions.rs:132:8
|
LL | if (let 0 = 0)? {}
| ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool`
= help: the trait `Try` is not implemented for `bool`
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
- --> $DIR/disallowed-positions.rs:136:19
+ --> $DIR/disallowed-positions.rs:132:19
|
LL | / fn nested_within_if_expr() {
LL | | if &let 0 = 0 {}
= help: the trait `FromResidual<_>` is not implemented for `()`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:156:8
+ --> $DIR/disallowed-positions.rs:152:8
|
LL | if x = let 0 = 0 {}
| ^^^^^^^^^^^^^ expected `bool`, found `()`
| ~~
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:161:8
+ --> $DIR/disallowed-positions.rs:157:8
|
LL | if true..(let 0 = 0) {}
| ^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
found struct `std::ops::Range<bool>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:165:8
+ --> $DIR/disallowed-positions.rs:161:8
|
LL | if ..(let 0 = 0) {}
| ^^^^^^^^^^^^^ expected `bool`, found struct `RangeTo`
found struct `RangeTo<bool>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:169:8
+ --> $DIR/disallowed-positions.rs:165:8
|
LL | if (let 0 = 0).. {}
| ^^^^^^^^^^^^^ expected `bool`, found struct `RangeFrom`
found struct `RangeFrom<bool>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:175:12
+ --> $DIR/disallowed-positions.rs:171:12
|
LL | if let Range { start: _, end: _ } = true..true && false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool`
found struct `std::ops::Range<_>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:175:8
+ --> $DIR/disallowed-positions.rs:171:8
|
LL | if let Range { start: _, end: _ } = true..true && false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
found struct `std::ops::Range<bool>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:179:12
+ --> $DIR/disallowed-positions.rs:175:12
|
LL | if let Range { start: _, end: _ } = true..true || false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool`
found struct `std::ops::Range<_>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:179:8
+ --> $DIR/disallowed-positions.rs:175:8
|
LL | if let Range { start: _, end: _ } = true..true || false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
found struct `std::ops::Range<bool>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:186:12
+ --> $DIR/disallowed-positions.rs:182:12
|
LL | if let Range { start: F, end } = F..|| true {}
| ^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `fn() -> bool`
found struct `std::ops::Range<_>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:186:41
+ --> $DIR/disallowed-positions.rs:182:41
|
LL | if let Range { start: F, end } = F..|| true {}
| ^^^^^^^ expected `bool`, found closure
|
= note: expected type `bool`
- found closure `[closure@$DIR/disallowed-positions.rs:186:41: 186:43]`
+ found closure `[closure@$DIR/disallowed-positions.rs:182:41: 182:43]`
help: use parentheses to call this closure
|
LL | if let Range { start: F, end } = F..(|| true)() {}
| + +++
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:186:8
+ --> $DIR/disallowed-positions.rs:182:8
|
LL | if let Range { start: F, end } = F..|| true {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
found struct `std::ops::Range<bool>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:194:12
+ --> $DIR/disallowed-positions.rs:190:12
|
LL | if let Range { start: true, end } = t..&&false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `&&bool`
found struct `std::ops::Range<_>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:194:44
+ --> $DIR/disallowed-positions.rs:190:44
|
LL | if let Range { start: true, end } = t..&&false {}
| ^^^^^^^ expected `bool`, found `&&bool`
|
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:194:8
+ --> $DIR/disallowed-positions.rs:190:8
|
LL | if let Range { start: true, end } = t..&&false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
found struct `std::ops::Range<bool>`
error[E0277]: the `?` operator can only be applied to values that implement `Try`
- --> $DIR/disallowed-positions.rs:132:20
+ --> $DIR/disallowed-positions.rs:128:20
|
LL | if let 0 = 0? {}
| ^^ the `?` operator cannot be applied to type `{integer}`
= help: the trait `Try` is not implemented for `{integer}`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:206:11
+ --> $DIR/disallowed-positions.rs:202:11
|
LL | while &let 0 = 0 {}
| ^^^^^^^^^^ expected `bool`, found `&bool`
|
error[E0614]: type `bool` cannot be dereferenced
- --> $DIR/disallowed-positions.rs:214:11
+ --> $DIR/disallowed-positions.rs:210:11
|
LL | while *let 0 = 0 {}
| ^^^^^^^^^^
error[E0600]: cannot apply unary operator `-` to type `bool`
- --> $DIR/disallowed-positions.rs:218:11
+ --> $DIR/disallowed-positions.rs:214:11
|
LL | while -let 0 = 0 {}
| ^^^^^^^^^^ cannot apply unary operator `-`
error[E0277]: the `?` operator can only be applied to values that implement `Try`
- --> $DIR/disallowed-positions.rs:228:11
+ --> $DIR/disallowed-positions.rs:224:11
|
LL | while (let 0 = 0)? {}
| ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool`
= help: the trait `Try` is not implemented for `bool`
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
- --> $DIR/disallowed-positions.rs:228:22
+ --> $DIR/disallowed-positions.rs:224:22
|
LL | / fn nested_within_while_expr() {
LL | | while &let 0 = 0 {}
= help: the trait `FromResidual<_>` is not implemented for `()`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:248:11
+ --> $DIR/disallowed-positions.rs:244:11
|
LL | while x = let 0 = 0 {}
| ^^^^^^^^^^^^^ expected `bool`, found `()`
| ~~
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:253:11
+ --> $DIR/disallowed-positions.rs:249:11
|
LL | while true..(let 0 = 0) {}
| ^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
found struct `std::ops::Range<bool>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:257:11
+ --> $DIR/disallowed-positions.rs:253:11
|
LL | while ..(let 0 = 0) {}
| ^^^^^^^^^^^^^ expected `bool`, found struct `RangeTo`
found struct `RangeTo<bool>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:261:11
+ --> $DIR/disallowed-positions.rs:257:11
|
LL | while (let 0 = 0).. {}
| ^^^^^^^^^^^^^ expected `bool`, found struct `RangeFrom`
found struct `RangeFrom<bool>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:267:15
+ --> $DIR/disallowed-positions.rs:263:15
|
LL | while let Range { start: _, end: _ } = true..true && false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool`
found struct `std::ops::Range<_>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:267:11
+ --> $DIR/disallowed-positions.rs:263:11
|
LL | while let Range { start: _, end: _ } = true..true && false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
found struct `std::ops::Range<bool>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:271:15
+ --> $DIR/disallowed-positions.rs:267:15
|
LL | while let Range { start: _, end: _ } = true..true || false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool`
found struct `std::ops::Range<_>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:271:11
+ --> $DIR/disallowed-positions.rs:267:11
|
LL | while let Range { start: _, end: _ } = true..true || false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
found struct `std::ops::Range<bool>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:278:15
+ --> $DIR/disallowed-positions.rs:274:15
|
LL | while let Range { start: F, end } = F..|| true {}
| ^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `fn() -> bool`
found struct `std::ops::Range<_>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:278:44
+ --> $DIR/disallowed-positions.rs:274:44
|
LL | while let Range { start: F, end } = F..|| true {}
| ^^^^^^^ expected `bool`, found closure
|
= note: expected type `bool`
- found closure `[closure@$DIR/disallowed-positions.rs:278:44: 278:46]`
+ found closure `[closure@$DIR/disallowed-positions.rs:274:44: 274:46]`
help: use parentheses to call this closure
|
LL | while let Range { start: F, end } = F..(|| true)() {}
| + +++
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:278:11
+ --> $DIR/disallowed-positions.rs:274:11
|
LL | while let Range { start: F, end } = F..|| true {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
found struct `std::ops::Range<bool>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:286:15
+ --> $DIR/disallowed-positions.rs:282:15
|
LL | while let Range { start: true, end } = t..&&false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `&&bool`
found struct `std::ops::Range<_>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:286:47
+ --> $DIR/disallowed-positions.rs:282:47
|
LL | while let Range { start: true, end } = t..&&false {}
| ^^^^^^^ expected `bool`, found `&&bool`
|
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:286:11
+ --> $DIR/disallowed-positions.rs:282:11
|
LL | while let Range { start: true, end } = t..&&false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
found struct `std::ops::Range<bool>`
error[E0277]: the `?` operator can only be applied to values that implement `Try`
- --> $DIR/disallowed-positions.rs:224:23
+ --> $DIR/disallowed-positions.rs:220:23
|
LL | while let 0 = 0? {}
| ^^ the `?` operator cannot be applied to type `{integer}`
= help: the trait `Try` is not implemented for `{integer}`
error[E0614]: type `bool` cannot be dereferenced
- --> $DIR/disallowed-positions.rs:315:5
+ --> $DIR/disallowed-positions.rs:311:5
|
LL | *let 0 = 0;
| ^^^^^^^^^^
error[E0600]: cannot apply unary operator `-` to type `bool`
- --> $DIR/disallowed-positions.rs:319:5
+ --> $DIR/disallowed-positions.rs:315:5
|
LL | -let 0 = 0;
| ^^^^^^^^^^ cannot apply unary operator `-`
error[E0277]: the `?` operator can only be applied to values that implement `Try`
- --> $DIR/disallowed-positions.rs:329:5
+ --> $DIR/disallowed-positions.rs:325:5
|
LL | (let 0 = 0)?;
| ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool`
= help: the trait `Try` is not implemented for `bool`
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
- --> $DIR/disallowed-positions.rs:329:16
+ --> $DIR/disallowed-positions.rs:325:16
|
LL | / fn outside_if_and_while_expr() {
LL | | &let 0 = 0;
= help: the trait `FromResidual<_>` is not implemented for `()`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:360:10
+ --> $DIR/disallowed-positions.rs:356:10
|
LL | (let Range { start: _, end: _ } = true..true || false);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool`
found struct `std::ops::Range<_>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:383:5
+ --> $DIR/disallowed-positions.rs:379:5
|
LL | fn outside_if_and_while_expr() {
| - help: try adding a return type: `-> &bool`
| ^^^^^^^^^^ expected `()`, found `&bool`
error[E0277]: the `?` operator can only be applied to values that implement `Try`
- --> $DIR/disallowed-positions.rs:325:17
+ --> $DIR/disallowed-positions.rs:321:17
|
LL | let 0 = 0?;
| ^^ the `?` operator cannot be applied to type `{integer}`
|
= help: the trait `Try` is not implemented for `{integer}`
-error: aborting due to 218 previous errors
+error: aborting due to 215 previous errors
Some errors have detailed explanations: E0277, E0308, E0600, E0614.
For more information about an error, try `rustc --explain E0277`.
-#![feature(let_else)]
+#![feature(let_chains, let_else)]
fn main() {
let opt = Some(1i32);
--- /dev/null
+// gate-test-let_chains
+
+// Here we test feature gating for ´let_chains`.
+// See `disallowed-positions.rs` for the grammar
+// defining the language for gated allowed positions.
+
+#![allow(irrefutable_let_patterns)]
+
+use std::ops::Range;
+
+fn _if() {
+ if let 0 = 1 {} // Stable!
+
+ if true && let 0 = 1 {}
+ //~^ ERROR `let` expressions in this position are unstable [E0658]
+
+ if let 0 = 1 && true {}
+ //~^ ERROR `let` expressions in this position are unstable [E0658]
+
+ if let Range { start: _, end: _ } = (true..true) && false {}
+ //~^ ERROR `let` expressions in this position are unstable [E0658]
+
+ if let 1 = 1 && let true = { true } && false {
+ //~^ ERROR `let` expressions in this position are unstable [E0658]
+ //~| ERROR `let` expressions in this position are unstable [E0658]
+ }
+}
+
+fn _while() {
+ while let 0 = 1 {} // Stable!
+
+ while true && let 0 = 1 {}
+ //~^ ERROR `let` expressions in this position are unstable [E0658]
+
+ while let 0 = 1 && true {}
+ //~^ ERROR `let` expressions in this position are unstable [E0658]
+
+ while let Range { start: _, end: _ } = (true..true) && false {}
+ //~^ ERROR `let` expressions in this position are unstable [E0658]
+}
+
+fn _macros() {
+ macro_rules! noop_expr { ($e:expr) => {}; }
+
+ noop_expr!((let 0 = 1));
+ //~^ ERROR `let` expressions in this position are unstable [E0658]
+ //~| ERROR expected expression, found `let` statement
+
+ macro_rules! use_expr {
+ ($e:expr) => {
+ if $e {}
+ while $e {}
+ }
+ }
+ #[cfg(FALSE)] (let 0 = 1);
+ //~^ ERROR `let` expressions in this position are unstable [E0658]
+ //~| ERROR expected expression, found `let` statement
+ use_expr!(let 0 = 1);
+ //~^ ERROR no rules expected the token `let`
+}
+
+fn main() {}
--- /dev/null
+error: expected expression, found `let` statement
+ --> $DIR/feature-gate.rs:55:20
+ |
+LL | #[cfg(FALSE)] (let 0 = 1);
+ | ^^^
+
+error: expected expression, found `let` statement
+ --> $DIR/feature-gate.rs:45:17
+ |
+LL | noop_expr!((let 0 = 1));
+ | ^^^
+
+error: no rules expected the token `let`
+ --> $DIR/feature-gate.rs:58:15
+ |
+LL | macro_rules! use_expr {
+ | --------------------- when calling this macro
+...
+LL | use_expr!(let 0 = 1);
+ | ^^^ no rules expected this token in macro call
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:14:16
+ |
+LL | if true && let 0 = 1 {}
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:17:8
+ |
+LL | if let 0 = 1 && true {}
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:20:8
+ |
+LL | if let Range { start: _, end: _ } = (true..true) && false {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:23:8
+ |
+LL | if let 1 = 1 && let true = { true } && false {
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:23:21
+ |
+LL | if let 1 = 1 && let true = { true } && false {
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:32:19
+ |
+LL | while true && let 0 = 1 {}
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:35:11
+ |
+LL | while let 0 = 1 && true {}
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:38:11
+ |
+LL | while let Range { start: _, end: _ } = (true..true) && false {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:55:20
+ |
+LL | #[cfg(FALSE)] (let 0 = 1);
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/feature-gate.rs:45:17
+ |
+LL | noop_expr!((let 0 = 1));
+ | ^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error: aborting due to 13 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
+#![feature(let_chains)]
+
fn main() {
let _opt = Some(1i32);
error: expected expression, found `let` statement
- --> $DIR/invalid-let-in-a-valid-let-context.rs:6:19
+ --> $DIR/invalid-let-in-a-valid-let-context.rs:8:19
|
LL | let _ = &&let Some(x) = Some(42);
| ^^^
error: expected expression, found `let` statement
- --> $DIR/invalid-let-in-a-valid-let-context.rs:11:47
+ --> $DIR/invalid-let-in-a-valid-let-context.rs:13:47
|
LL | if let Some(elem) = _opt && [1, 2, 3][let _ = &&let Some(x) = Some(42)] = 1 {
| ^^^
error: expected expression, found `let` statement
- --> $DIR/invalid-let-in-a-valid-let-context.rs:11:57
+ --> $DIR/invalid-let-in-a-valid-let-context.rs:13:57
|
LL | if let Some(elem) = _opt && [1, 2, 3][let _ = &&let Some(x) = Some(42)] = 1 {
| ^^^
error: expected expression, found `let` statement
- --> $DIR/invalid-let-in-a-valid-let-context.rs:21:23
+ --> $DIR/invalid-let-in-a-valid-let-context.rs:23:23
|
LL | [1, 2, 3][let _ = ()];
| ^^^
error: expected expression, found `let` statement
- --> $DIR/invalid-let-in-a-valid-let-context.rs:30:47
+ --> $DIR/invalid-let-in-a-valid-let-context.rs:32:47
|
LL | if let Some(elem) = _opt && [1, 2, 3][let _ = ()] = 1 {
| ^^^
error: expected expression, found `let` statement
- --> $DIR/invalid-let-in-a-valid-let-context.rs:38:21
+ --> $DIR/invalid-let-in-a-valid-let-context.rs:40:21
|
LL | let x = let y = 1;
| ^^^
// revisions: allowed disallowed
//[allowed] check-pass
-#![feature(if_let_guard)]
+#![feature(if_let_guard, let_chains)]
#![cfg_attr(allowed, allow(irrefutable_let_patterns))]
#![cfg_attr(disallowed, deny(irrefutable_let_patterns))]
// check-pass
+#![feature(let_chains)]
+
fn main() {
let x = Some(vec!["test"]);
// check-pass
+#![feature(let_chains)]
+
fn main() {
let opt = Some("foo bar");
match true {
_ if let true = true && true => {}
//~^ ERROR `if let` guards are
+ //~| ERROR `let` expressions in this
_ => {}
}
}
= help: add `#![feature(if_let_guard)]` to the crate attributes to enable
= help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
-error: aborting due to previous error
+error[E0658]: `let` expressions in this position are unstable
+ --> $DIR/issue-93150.rs:3:14
+ |
+LL | _ if let true = true && true => {}
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+ = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0658`.
// run-pass
-#![feature(if_let_guard)]
+#![feature(if_let_guard, let_chains)]
fn check_if_let(opt: Option<Option<Option<i32>>>, value: i32) -> bool {
if let Some(first) = opt
#![feature(core_intrinsics)]
#![feature(start)]
#![feature(bench_black_box)]
+#![allow(invalid_value)]
use std::hint::black_box;
use std::mem::MaybeUninit;
+++ /dev/null
-// gate-test-custom_inner_attributes
-
-#![feature(register_attr)]
-
-#![register_attr(foo)]
-
-#[foo]
-mod foo {
- #![foo] //~ ERROR custom inner attributes are unstable
-}
-
-fn main() {}
+++ /dev/null
-error[E0658]: custom inner attributes are unstable
- --> $DIR/issue-36530.rs:9:8
- |
-LL | #![foo]
- | ^^^
- |
- = note: see issue #54726 <https://github.com/rust-lang/rust/issues/54726> for more information
- = help: add `#![feature(custom_inner_attributes)]` to the crate attributes to enable
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0658`.
-// Test to ensure that trait bounds are propertly
+// Test to ensure that trait bounds are properly
// checked on specializable associated types
#![allow(incomplete_features)]
--- /dev/null
+fn main() {
+ let mut x = 1i32;
+ let y = Box::new(|| 1);
+ x = y;
+ //~^ ERROR mismatched types
+ //~| HELP use parentheses to call this closure
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/call-boxed.rs:4:9
+ |
+LL | let mut x = 1i32;
+ | ---- expected due to this value
+LL | let y = Box::new(|| 1);
+ | -- the found closure
+LL | x = y;
+ | ^ expected `i32`, found struct `Box`
+ |
+ = note: expected type `i32`
+ found struct `Box<[closure@$DIR/call-boxed.rs:3:22: 3:24]>`
+help: use parentheses to call this closure
+ |
+LL | x = y();
+ | ++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+struct Foo { i: i32 }
+
+impl Foo {
+ fn bar(&self) {}
+}
+
+fn foo() -> Foo {
+ Foo { i: 1 }
+}
+
+fn main() {
+ foo.bar();
+ //~^ ERROR no method named `bar`
+ //~| HELP use parentheses to call this function
+
+ foo.i;
+ //~^ ERROR no field `i`
+ //~| HELP use parentheses to call this function
+
+ let callable = Box::new(|| Foo { i: 1 }) as Box<dyn Fn() -> Foo>;
+
+ callable.bar();
+ //~^ ERROR no method named `bar`
+ //~| HELP use parentheses to call this trait object
+
+ callable.i;
+ //~^ ERROR no field `i`
+ //~| HELP use parentheses to call this trait object
+}
+
+fn type_param<T: Fn() -> Foo>(t: T) {
+ t.bar();
+ //~^ ERROR no method named `bar`
+ //~| HELP use parentheses to call this type parameter
+
+ t.i;
+ //~^ ERROR no field `i`
+ //~| HELP use parentheses to call this type parameter
+}
--- /dev/null
+error[E0599]: no method named `bar` found for fn item `fn() -> Foo {foo}` in the current scope
+ --> $DIR/call-on-missing.rs:12:9
+ |
+LL | foo.bar();
+ | ^^^ method not found in `fn() -> Foo {foo}`
+ |
+help: use parentheses to call this function
+ |
+LL | foo().bar();
+ | ++
+
+error[E0609]: no field `i` on type `fn() -> Foo {foo}`
+ --> $DIR/call-on-missing.rs:16:9
+ |
+LL | foo.i;
+ | ^
+ |
+help: use parentheses to call this function
+ |
+LL | foo().i;
+ | ++
+
+error[E0599]: no method named `bar` found for struct `Box<dyn Fn() -> Foo>` in the current scope
+ --> $DIR/call-on-missing.rs:22:14
+ |
+LL | callable.bar();
+ | ^^^ method not found in `Box<dyn Fn() -> Foo>`
+ |
+help: use parentheses to call this trait object
+ |
+LL | callable().bar();
+ | ++
+
+error[E0609]: no field `i` on type `Box<dyn Fn() -> Foo>`
+ --> $DIR/call-on-missing.rs:26:14
+ |
+LL | callable.i;
+ | ^ unknown field
+ |
+help: use parentheses to call this trait object
+ |
+LL | callable().i;
+ | ++
+
+error[E0599]: no method named `bar` found for type parameter `T` in the current scope
+ --> $DIR/call-on-missing.rs:32:7
+ |
+LL | fn type_param<T: Fn() -> Foo>(t: T) {
+ | - method `bar` not found for this type parameter
+LL | t.bar();
+ | ^^^ method not found in `T`
+ |
+help: use parentheses to call this type parameter
+ |
+LL | t().bar();
+ | ++
+
+error[E0609]: no field `i` on type `T`
+ --> $DIR/call-on-missing.rs:36:7
+ |
+LL | fn type_param<T: Fn() -> Foo>(t: T) {
+ | - type parameter 'T' declared here
+...
+LL | t.i;
+ | ^
+ |
+help: use parentheses to call this type parameter
+ |
+LL | t().i;
+ | ++
+
+error: aborting due to 6 previous errors
+
+Some errors have detailed explanations: E0599, E0609.
+For more information about an error, try `rustc --explain E0599`.
found fn item `fn(usize, usize) -> usize {foo}`
help: use parentheses to call this function
|
-LL | let _: usize = foo(_, _);
- | ++++++
+LL | let _: usize = foo(/* usize */, /* usize */);
+ | ++++++++++++++++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:30:16
found fn item `fn(usize, usize) -> S {S}`
help: use parentheses to instantiate this tuple struct
|
-LL | let _: S = S(_, _);
- | ++++++
+LL | let _: S = S(/* usize */, /* usize */);
+ | ++++++++++++++++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:31:20
|
= note: expected type `usize`
found fn item `fn(usize, usize) -> usize {<_ as T>::baz}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
|
-LL | let _: usize = T::baz(_, _);
- | ++++++
+LL | let _: usize = T::baz(/* usize */, /* usize */);
+ | ++++++++++++++++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:34:20
|
= note: expected type `usize`
found fn item `fn(usize) -> usize {<_ as T>::bat}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
|
-LL | let _: usize = T::bat(_);
- | +++
+LL | let _: usize = T::bat(/* usize */);
+ | +++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:35:16
found fn item `fn(usize) -> E {E::A}`
help: use parentheses to instantiate this tuple variant
|
-LL | let _: E = E::A(_);
- | +++
+LL | let _: E = E::A(/* usize */);
+ | +++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:37:20
|
= note: expected type `usize`
found fn item `fn(usize, usize) -> usize {<X as T>::baz}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
|
-LL | let _: usize = X::baz(_, _);
- | ++++++
+LL | let _: usize = X::baz(/* usize */, /* usize */);
+ | ++++++++++++++++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:38:20
|
= note: expected type `usize`
found fn item `fn(usize) -> usize {<X as T>::bat}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
|
-LL | let _: usize = X::bat(_);
- | +++
+LL | let _: usize = X::bat(/* usize */);
+ | +++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:39:20
|
= note: expected type `usize`
found fn item `fn(usize) -> usize {<X as T>::bax}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
|
-LL | let _: usize = X::bax(_);
- | +++
+LL | let _: usize = X::bax(/* usize */);
+ | +++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:40:20
|
= note: expected type `usize`
found fn item `fn(usize) -> usize {<X as T>::bach}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
|
-LL | let _: usize = X::bach(_);
- | +++
+LL | let _: usize = X::bach(/* usize */);
+ | +++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:41:20
|
= note: expected type `usize`
found fn item `for<'r> fn(&'r X) -> usize {<X as T>::ban}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
|
-LL | let _: usize = X::ban(_);
- | +++
+LL | let _: usize = X::ban(/* &X */);
+ | ++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:42:20
|
= note: expected type `usize`
found fn item `for<'r> fn(&'r X) -> usize {<X as T>::bal}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
|
-LL | let _: usize = X::bal(_);
- | +++
+LL | let _: usize = X::bal(/* &X */);
+ | ++++++++++
error[E0615]: attempted to take value of method `ban` on type `X`
--> $DIR/fn-or-tuple-struct-without-args.rs:43:22
--- /dev/null
+use std::convert::TryInto;
+
+trait A<T> {
+ fn foo() {}
+}
+
+trait B<T, U> {
+ fn bar() {}
+}
+
+struct S;
+
+impl<T> A<T> for S {}
+impl<T, U> B<T, U> for S {}
+
+fn main() {
+ let _ = A::foo::<S>();
+ //~^ ERROR
+ //~| HELP remove these generics
+ //~| HELP consider moving this generic argument
+
+ let _ = B::bar::<S, S>();
+ //~^ ERROR
+ //~| HELP remove these generics
+ //~| HELP consider moving these generic arguments
+
+ let _ = A::<S>::foo::<S>();
+ //~^ ERROR
+ //~| HELP remove these generics
+
+ let _ = 42.into::<Option<_>>();
+ //~^ ERROR
+ //~| HELP remove these generics
+ //~| HELP consider moving this generic argument
+}
--- /dev/null
+error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied
+ --> $DIR/issue-89064.rs:17:16
+ |
+LL | let _ = A::foo::<S>();
+ | ^^^ expected 0 generic arguments
+ |
+note: associated function defined here, with 0 generic parameters
+ --> $DIR/issue-89064.rs:4:8
+ |
+LL | fn foo() {}
+ | ^^^
+help: consider moving this generic argument to the `A` trait, which takes up to 1 argument
+ |
+LL - let _ = A::foo::<S>();
+LL + let _ = A::<S>::foo();
+ |
+help: remove these generics
+ |
+LL - let _ = A::foo::<S>();
+LL + let _ = A::foo();
+ |
+
+error[E0107]: this associated function takes 0 generic arguments but 2 generic arguments were supplied
+ --> $DIR/issue-89064.rs:22:16
+ |
+LL | let _ = B::bar::<S, S>();
+ | ^^^ expected 0 generic arguments
+ |
+note: associated function defined here, with 0 generic parameters
+ --> $DIR/issue-89064.rs:8:8
+ |
+LL | fn bar() {}
+ | ^^^
+help: consider moving these generic arguments to the `B` trait, which takes up to 2 arguments
+ |
+LL - let _ = B::bar::<S, S>();
+LL + let _ = B::<S, S>::bar();
+ |
+help: remove these generics
+ |
+LL - let _ = B::bar::<S, S>();
+LL + let _ = B::bar();
+ |
+
+error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied
+ --> $DIR/issue-89064.rs:27:21
+ |
+LL | let _ = A::<S>::foo::<S>();
+ | ^^^----- help: remove these generics
+ | |
+ | expected 0 generic arguments
+ |
+note: associated function defined here, with 0 generic parameters
+ --> $DIR/issue-89064.rs:4:8
+ |
+LL | fn foo() {}
+ | ^^^
+
+error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied
+ --> $DIR/issue-89064.rs:31:16
+ |
+LL | let _ = 42.into::<Option<_>>();
+ | ^^^^ expected 0 generic arguments
+ |
+note: associated function defined here, with 0 generic parameters
+ --> $SRC_DIR/core/src/convert/mod.rs:LL:COL
+ |
+LL | fn into(self) -> T;
+ | ^^^^
+help: consider moving this generic argument to the `Into` trait, which takes up to 1 argument
+ |
+LL | let _ = Into::<Option<_>>::into(42);
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: remove these generics
+ |
+LL - let _ = 42.into::<Option<_>>();
+LL + let _ = 42.into();
+ |
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0107`.
--- /dev/null
+fn foo() {
+ //~^ HELP try adding a return type
+ |x: &i32| 1i32
+ //~^ ERROR mismatched types
+}
+
+fn bar(i: impl Sized) {
+ //~^ HELP a return type might be missing here
+ || i
+ //~^ ERROR mismatched types
+}
+
+fn main() {}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/return-closures.rs:3:5
+ |
+LL | fn foo() {
+ | - help: try adding a return type: `-> impl for<'r> Fn(&'r i32) -> i32`
+LL |
+LL | |x: &i32| 1i32
+ | ^^^^^^^^^^^^^^ expected `()`, found closure
+ |
+ = note: expected unit type `()`
+ found closure `[closure@$DIR/return-closures.rs:3:5: 3:14]`
+
+error[E0308]: mismatched types
+ --> $DIR/return-closures.rs:9:5
+ |
+LL | fn bar(i: impl Sized) {
+ | - help: a return type might be missing here: `-> _`
+LL |
+LL | || i
+ | ^^^^ expected `()`, found closure
+ |
+ = note: expected unit type `()`
+ found closure `[closure@$DIR/return-closures.rs:9:5: 9:7]`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
// Ensure that the compiler include the blanklet implementation suggestion
-// when inside a `impl` statment are used two local traits.
+// when inside a `impl` statement are used two local traits.
//
// edition:2021
use std::fmt;
--- /dev/null
+struct Thing {
+ a0: Foo,
+ a1: Foo,
+ a2: Foo,
+ a3: Foo,
+ a4: Foo,
+ a5: Foo,
+ a6: Foo,
+ a7: Foo,
+ a8: Foo,
+ a9: Foo,
+}
+
+struct Foo {
+ field: Field,
+}
+
+struct Field;
+
+impl Foo {
+ fn bar(&self) {}
+}
+
+fn bar(t: Thing) {
+ t.bar(); //~ ERROR no method named `bar` found for struct `Thing`
+ t.field; //~ ERROR no field `field` on type `Thing`
+}
+
+fn main() {}
--- /dev/null
+error[E0599]: no method named `bar` found for struct `Thing` in the current scope
+ --> $DIR/too-many-field-suggestions.rs:25:7
+ |
+LL | struct Thing {
+ | ------------ method `bar` not found for this struct
+...
+LL | t.bar();
+ | ^^^ method not found in `Thing`
+ |
+help: some of the expressions' fields have a method of the same name
+ |
+LL | t.a0.bar();
+ | +++
+LL | t.a1.bar();
+ | +++
+LL | t.a2.bar();
+ | +++
+LL | t.a3.bar();
+ | +++
+ and 6 other candidates
+
+error[E0609]: no field `field` on type `Thing`
+ --> $DIR/too-many-field-suggestions.rs:26:7
+ |
+LL | t.field;
+ | ^^^^^ unknown field
+ |
+ = note: available fields are: `a0`, `a1`, `a2`, `a3`, `a4` ... and 5 others
+help: some of the expressions' fields have a field of the same name
+ |
+LL | t.a0.field;
+ | +++
+LL | t.a1.field;
+ | +++
+LL | t.a2.field;
+ | +++
+LL | t.a3.field;
+ | +++
+ and 6 other candidates
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0599, E0609.
+For more information about an error, try `rustc --explain E0599`.
// This tests issue #79683: note in the error message that the trait is
-// explicitely unimplemented instead of suggesting to implement it.
+// explicitly unimplemented instead of suggesting to implement it.
#![feature(negative_impls)]
--- /dev/null
+#![deny(unused)]
+fn foo(xyza: &str) {
+//~^ ERROR unused variable: `xyza`
+ let _ = "{xyza}";
+}
+
+fn foo3(xyza: &str) {
+//~^ ERROR unused variable: `xyza`
+ let _ = "aaa{xyza}bbb";
+}
+
+fn main() {
+ foo("x");
+ foo3("xx");
+}
--- /dev/null
+error: unused variable: `xyza`
+ --> $DIR/issue-100584.rs:2:8
+ |
+LL | fn foo(xyza: &str) {
+ | ^^^^ unused variable
+LL |
+LL | let _ = "{xyza}";
+ | -------- you might have meant to use string interpolation in this string literal
+ |
+note: the lint level is defined here
+ --> $DIR/issue-100584.rs:1:9
+ |
+LL | #![deny(unused)]
+ | ^^^^^^
+ = note: `#[deny(unused_variables)]` implied by `#[deny(unused)]`
+help: string interpolation only works in `format!` invocations
+ |
+LL | let _ = format!("{xyza}");
+ | ++++++++ +
+help: if this is intentional, prefix it with an underscore
+ |
+LL | fn foo(_xyza: &str) {
+ | ~~~~~
+
+error: unused variable: `xyza`
+ --> $DIR/issue-100584.rs:7:9
+ |
+LL | fn foo3(xyza: &str) {
+ | ^^^^ unused variable
+LL |
+LL | let _ = "aaa{xyza}bbb";
+ | -------------- you might have meant to use string interpolation in this string literal
+ |
+help: string interpolation only works in `format!` invocations
+ |
+LL | let _ = format!("aaa{xyza}bbb");
+ | ++++++++ +
+help: if this is intentional, prefix it with an underscore
+ |
+LL | fn foo3(_xyza: &str) {
+ | ~~~~~
+
+error: aborting due to 2 previous errors
+
// Do this instead:
type T4<U> = <U as Bound>::Assoc;
-// Make sure the help about associatd types is not shown incorrectly
+// Make sure the help about associated types is not shown incorrectly
type T5<U: Bound> = <U as Bound>::Assoc; //~ WARN not enforced in type aliases
type T6<U: Bound> = ::std::vec::Vec<U>; //~ WARN not enforced in type aliases
error[E0277]: a value of type `i32` cannot be built from an iterator over elements of type `i32`
- --> $DIR/type-check-defaults.rs:6:19
+ --> $DIR/type-check-defaults.rs:6:23
|
LL | struct WellFormed<Z = Foo<i32, i32>>(Z);
- | ^^^^^^^^^^^^^^^^^ value of type `i32` cannot be built from `std::iter::Iterator<Item=i32>`
+ | ^^^^^^^^^^^^^ value of type `i32` cannot be built from `std::iter::Iterator<Item=i32>`
|
= help: the trait `FromIterator<i32>` is not implemented for `i32`
note: required by a bound in `Foo`
| ^^^^^^^^^^^^^^^ required by this bound in `Foo`
error[E0277]: a value of type `i32` cannot be built from an iterator over elements of type `i32`
- --> $DIR/type-check-defaults.rs:8:27
+ --> $DIR/type-check-defaults.rs:8:38
|
LL | struct WellFormedNoBounds<Z:?Sized = Foo<i32, i32>>(Z);
- | ^^^^^^^^^^^^^^^^^^^^^^^^ value of type `i32` cannot be built from `std::iter::Iterator<Item=i32>`
+ | ^^^^^^^^^^^^^ value of type `i32` cannot be built from `std::iter::Iterator<Item=i32>`
|
= help: the trait `FromIterator<i32>` is not implemented for `i32`
note: required by a bound in `Foo`
LL | pub fn drop<T>(_x: T) {}
| ^^^^^^^^^^^^^^^^^^^^^
|
- = note: the full type name has been written to '$TEST_BUILD_DIR/type_length_limit/type_length_limit.long-type.txt'
= help: consider adding a `#![type_length_limit="8"]` attribute to your crate
+ = note: the full type name has been written to '$TEST_BUILD_DIR/type_length_limit/type_length_limit.long-type.txt'
error: reached the type-length limit while instantiating `<[closure@std::rt::lang_start<()...e<()>>::call_once - shim(vtable)`
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: the full type name has been written to '$TEST_BUILD_DIR/type_length_limit/type_length_limit.long-type.txt'
= help: consider adding a `#![type_length_limit="8"]` attribute to your crate
+ = note: the full type name has been written to '$TEST_BUILD_DIR/type_length_limit/type_length_limit.long-type.txt'
error: aborting due to 2 previous errors
--> $DIR/issue-29124.rs:15:15
|
LL | Obj::func.x();
- | --------- ^ method not found in `fn() -> Ret {Obj::func}`
- | |
- | this is a function, perhaps you wish to call it
+ | ^ method not found in `fn() -> Ret {Obj::func}`
error[E0599]: no method named `x` found for fn item `fn() -> Ret {func}` in the current scope
--> $DIR/issue-29124.rs:17:10
|
LL | func.x();
- | ---- ^ method not found in `fn() -> Ret {func}`
- | |
- | this is a function, perhaps you wish to call it
+ | ^ method not found in `fn() -> Ret {func}`
error: aborting due to 2 previous errors
struct Foo();
impl Foo {
- fn foo() { }
+ fn foo(&self) { }
}
fn main() {
--> $DIR/empty-tuple-method.rs:12:15
|
LL | thing.bar.foo();
- | --------- ^^^ method not found in `fn() -> Foo {Foo}`
- | |
- | this is the constructor of a struct
+ | ^^^ method not found in `fn() -> Foo {Foo}`
|
-help: call the constructor
+help: use parentheses to instantiate this tuple struct
|
LL | (thing.bar)().foo();
| + +++
Tup()
}
impl Foo {
- fn foo() { }
+ fn foo(&self) { }
}
fn main() {
--> $DIR/enum-variant.rs:14:15
|
LL | thing.bar.foo();
- | --------- ^^^ method not found in `fn() -> Foo {Foo::Tup}`
- | |
- | this is the constructor of an enum variant
+ | ^^^ method not found in `fn() -> Foo {Foo::Tup}`
|
-help: call the constructor
+help: use parentheses to instantiate this tuple variant
|
LL | (thing.bar)().foo();
| + +++
--> $DIR/tuple-field.rs:12:15
|
LL | thing.bar.0;
- | --------- ^
- | |
- | this is the constructor of a struct
+ | ^
|
-help: call the constructor
+help: use parentheses to instantiate this tuple struct
|
-LL | (thing.bar)(_, _).0;
- | + +++++++
+LL | (thing.bar)(/* char */, /* u16 */).0;
+ | + ++++++++++++++++++++++++
error: aborting due to previous error
--> $DIR/tuple-method.rs:12:15
|
LL | thing.bar.foo();
- | --------- ^^^ method not found in `fn(u8, i32) -> Foo {Foo}`
- | |
- | this is the constructor of a struct
- |
-help: call the constructor
- |
-LL | (thing.bar)(_, _).foo();
- | + +++++++
+ | ^^^ method not found in `fn(u8, i32) -> Foo {Foo}`
error: aborting due to previous error
--> $DIR/issue-96738.rs:2:10
|
LL | Some.nonexistent_method();
- | ---- ^^^^^^^^^^^^^^^^^^ method not found in `fn(_) -> Option<_> {Option::<_>::Some}`
- | |
- | this is the constructor of an enum variant
- |
-help: call the constructor
- |
-LL | (Some)(_).nonexistent_method();
- | + ++++
+ | ^^^^^^^^^^^^^^^^^^ method not found in `fn(_) -> Option<_> {Option::<_>::Some}`
error[E0609]: no field `nonexistent_field` on type `fn(_) -> Option<_> {Option::<_>::Some}`
--> $DIR/issue-96738.rs:3:10
|
LL | Some.nonexistent_field;
- | ---- ^^^^^^^^^^^^^^^^^
- | |
- | this is the constructor of an enum variant
- |
-help: call the constructor
- |
-LL | (Some)(_).nonexistent_field;
- | + ++++
+ | ^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors
--> $DIR/unboxed-closures-static-call-wrong-trait.rs:7:10
|
LL | mut_.call((0, ));
- | ---- ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:29]`
- | |
- | this is a function, perhaps you wish to call it
+ | ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:29]`
error: aborting due to previous error
--- /dev/null
+// normalize-stderr-test "error `.*`" -> "$$ERROR_MESSAGE"
+// compile-flags: -o/tmp/ -Zunpretty=ast-tree
+
+fn main() {}
--- /dev/null
+error: pretty-print failed to write `/tmp/` due to $ERROR_MESSAGE
+
+error: aborting due to previous error
+
-// Test various uses of structs with distint variances to make sure
+// Test various uses of structs with distinct variances to make sure
// they permit lifetimes to be approximated as expected.
struct SomeStruct<T>(fn(T));
-// Test various uses of structs with distint variances to make sure
+// Test various uses of structs with distinct variances to make sure
// they permit lifetimes to be approximated as expected.
#![allow(dead_code)]
-// Test various uses of structs with distint variances to make sure
+// Test various uses of structs with distinct variances to make sure
// they permit lifetimes to be approximated as expected.
struct SomeStruct<T>(*mut T);
error[E0277]: the trait bound `Self: Eq` is not satisfied
- --> $DIR/wf-trait-fn-ret.rs:10:22
+ --> $DIR/wf-trait-fn-ret.rs:10:23
|
LL | fn bar(&self) -> &Bar<Self>;
- | ^^^^^^^^^^ the trait `Eq` is not implemented for `Self`
+ | ^^^^^^^^^ the trait `Eq` is not implemented for `Self`
|
note: required by a bound in `Bar`
--> $DIR/wf-trait-fn-ret.rs:7:14
-Subproject commit 6da726708a4406f31f996d813790818dce837161
+Subproject commit 4ed54cecce3ce9ab6ff058781f4c8a500ee6b8b5
RUST_BACKTRACE: 1
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
NO_FMT_TEST: 1
+ CARGO_INCREMENTAL: 0
jobs:
base:
RUST_BACKTRACE: 1
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
NO_FMT_TEST: 1
+ CARGO_INCREMENTAL: 0
defaults:
run:
## Unreleased / In Rust Nightly
-[7c21f91b...master](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...master)
+[d7b5cbf0...master](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...master)
+
+## Rust 1.63
+
+Current stable, released 2022-08-11
+
+[7c21f91b...d7b5cbf0](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...d7b5cbf0)
+
+### New Lints
+
+* [`borrow_deref_ref`]
+ [#7930](https://github.com/rust-lang/rust-clippy/pull/7930)
+* [`doc_link_with_quotes`]
+ [#8385](https://github.com/rust-lang/rust-clippy/pull/8385)
+* [`no_effect_replace`]
+ [#8754](https://github.com/rust-lang/rust-clippy/pull/8754)
+* [`rc_clone_in_vec_init`]
+ [#8769](https://github.com/rust-lang/rust-clippy/pull/8769)
+* [`derive_partial_eq_without_eq`]
+ [#8796](https://github.com/rust-lang/rust-clippy/pull/8796)
+* [`mismatching_type_param_order`]
+ [#8831](https://github.com/rust-lang/rust-clippy/pull/8831)
+* [`duplicate_mod`] [#8832](https://github.com/rust-lang/rust-clippy/pull/8832)
+* [`unused_rounding`]
+ [#8866](https://github.com/rust-lang/rust-clippy/pull/8866)
+* [`get_first`] [#8882](https://github.com/rust-lang/rust-clippy/pull/8882)
+* [`swap_ptr_to_ref`]
+ [#8916](https://github.com/rust-lang/rust-clippy/pull/8916)
+* [`almost_complete_letter_range`]
+ [#8918](https://github.com/rust-lang/rust-clippy/pull/8918)
+* [`needless_parens_on_range_literals`]
+ [#8933](https://github.com/rust-lang/rust-clippy/pull/8933)
+* [`as_underscore`] [#8934](https://github.com/rust-lang/rust-clippy/pull/8934)
+
+### Moves and Deprecations
+
+* Rename `eval_order_dependence` to [`mixed_read_write_in_expression`], move to
+ `nursery` [#8621](https://github.com/rust-lang/rust-clippy/pull/8621)
+
+### Enhancements
+
+* [`undocumented_unsafe_blocks`]: Now also lints on unsafe trait implementations
+ [#8761](https://github.com/rust-lang/rust-clippy/pull/8761)
+* [`empty_line_after_outer_attr`]: Now also lints on argumentless macros
+ [#8790](https://github.com/rust-lang/rust-clippy/pull/8790)
+* [`expect_used`]: Now can be disabled in tests with the `allow-expect-in-tests`
+ option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
+* [`unwrap_used`]: Now can be disabled in tests with the `allow-unwrap-in-tests`
+ option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
+* [`disallowed_methods`]: Now also lints indirect usages
+ [#8852](https://github.com/rust-lang/rust-clippy/pull/8852)
+* [`get_last_with_len`]: Now also lints `VecDeque` and any deref to slice
+ [#8862](https://github.com/rust-lang/rust-clippy/pull/8862)
+* [`manual_range_contains`]: Now also lints on chains of `&&` and `||`
+ [#8884](https://github.com/rust-lang/rust-clippy/pull/8884)
+* [`rc_clone_in_vec_init`]: Now also lints on `Weak`
+ [#8885](https://github.com/rust-lang/rust-clippy/pull/8885)
+* [`dbg_macro`]: Introduce `allow-dbg-in-tests` config option
+ [#8897](https://github.com/rust-lang/rust-clippy/pull/8897)
+* [`use_self`]: Now also lints on `TupleStruct` and `Struct` patterns
+ [#8899](https://github.com/rust-lang/rust-clippy/pull/8899)
+* [`manual_find_map`] and [`manual_filter_map`]: Now also lints on more complex
+ method chains inside `map`
+ [#8930](https://github.com/rust-lang/rust-clippy/pull/8930)
+* [`needless_return`]: Now also lints on macro expressions in return statements
+ [#8932](https://github.com/rust-lang/rust-clippy/pull/8932)
+* [`doc_markdown`]: Users can now indicate, that the `doc-valid-idents` config
+ should extend the default and not replace it
+ [#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
+* [`disallowed_names`]: Users can now indicate, that the `disallowed-names`
+ config should extend the default and not replace it
+ [#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
+* [`never_loop`]: Now checks for `continue` in struct expression
+ [#9002](https://github.com/rust-lang/rust-clippy/pull/9002)
+
+### False Positive Fixes
+
+* [`useless_transmute`]: No longer lints on types with erased regions
+ [#8564](https://github.com/rust-lang/rust-clippy/pull/8564)
+* [`vec_init_then_push`]: No longer lints when further extended
+ [#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
+* [`cmp_owned`]: No longer lints on `From::from` for `Copy` types
+ [#8807](https://github.com/rust-lang/rust-clippy/pull/8807)
+* [`redundant_allocation`]: No longer lints on fat pointers that would become
+ thin pointers [#8813](https://github.com/rust-lang/rust-clippy/pull/8813)
+* [`derive_partial_eq_without_eq`]:
+ * Handle differing predicates applied by `#[derive(PartialEq)]` and
+ `#[derive(Eq)]`
+ [#8869](https://github.com/rust-lang/rust-clippy/pull/8869)
+ * No longer lints on non-public types and better handles generics
+ [#8950](https://github.com/rust-lang/rust-clippy/pull/8950)
+* [`empty_line_after_outer_attr`]: No longer lints empty lines in inner
+ string values [#8892](https://github.com/rust-lang/rust-clippy/pull/8892)
+* [`branches_sharing_code`]: No longer lints when using different binding names
+ [#8901](https://github.com/rust-lang/rust-clippy/pull/8901)
+* [`significant_drop_in_scrutinee`]: No longer lints on Try `?` and `await`
+ desugared expressions [#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
+* [`checked_conversions`]: No longer lints in `const` contexts
+ [#8907](https://github.com/rust-lang/rust-clippy/pull/8907)
+* [`iter_overeager_cloned`]: No longer lints on `.cloned().flatten()` when
+ `T::Item` doesn't implement `IntoIterator`
+ [#8960](https://github.com/rust-lang/rust-clippy/pull/8960)
+
+### Suggestion Fixes/Improvements
+
+* [`vec_init_then_push`]: Suggest to remove `mut` binding when possible
+ [#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
+* [`manual_range_contains`]: Fix suggestion for integers with different signs
+ [#8763](https://github.com/rust-lang/rust-clippy/pull/8763)
+* [`identity_op`]: Add parenthesis to suggestions where required
+ [#8786](https://github.com/rust-lang/rust-clippy/pull/8786)
+* [`cast_lossless`]: No longer gives wrong suggestion on `usize`/`isize`->`f64`
+ [#8778](https://github.com/rust-lang/rust-clippy/pull/8778)
+* [`rc_clone_in_vec_init`]: Add suggestion
+ [#8814](https://github.com/rust-lang/rust-clippy/pull/8814)
+* The "unknown field" error messages for config files now wraps the field names
+ [#8823](https://github.com/rust-lang/rust-clippy/pull/8823)
+* [`cast_abs_to_unsigned`]: Do not remove cast if it's required
+ [#8876](https://github.com/rust-lang/rust-clippy/pull/8876)
+* [`significant_drop_in_scrutinee`]: Improve lint message for types that are not
+ references and not trivially clone-able
+ [#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
+* [`for_loops_over_fallibles`]: Now suggests the correct variant of `iter()`,
+ `iter_mut()` or `into_iter()`
+ [#8941](https://github.com/rust-lang/rust-clippy/pull/8941)
+
+### ICE Fixes
+
+* Fix ICE in [`let_unit_value`] when calling a `static`/`const` callable type
+ [#8835](https://github.com/rust-lang/rust-clippy/pull/8835)
+* Fix ICEs on callable `static`/`const`s
+ [#8896](https://github.com/rust-lang/rust-clippy/pull/8896)
+* [`needless_late_init`]
+ [#8912](https://github.com/rust-lang/rust-clippy/pull/8912)
+* Fix ICE in shadow lints
+ [#8913](https://github.com/rust-lang/rust-clippy/pull/8913)
+
+### Documentation Improvements
+
+* Clippy has a [Book](https://doc.rust-lang.org/nightly/clippy/) now!
+ [#7359](https://github.com/rust-lang/rust-clippy/pull/7359)
+* Add a *copy lint name*-button to Clippy's lint list
+ [#8839](https://github.com/rust-lang/rust-clippy/pull/8839)
+* Display past names of renamed lints on Clippy's lint list
+ [#8843](https://github.com/rust-lang/rust-clippy/pull/8843)
+* Add the ability to show the lint output in the lint list
+ [#8947](https://github.com/rust-lang/rust-clippy/pull/8947)
## Rust 1.62
-Current stable, released 2022-06-30
+Released 2022-06-30
[d0cf3481...7c21f91b](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...7c21f91b)
[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
[`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes
+[`cast_slice_from_raw_parts`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_from_raw_parts
[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
[`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
+[`collapsible_str_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace
[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
[`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
[`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator
[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
+[`iter_on_empty_collections`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_empty_collections
+[`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items
[`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
[`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
[`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
[`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
+[`manual_string_new`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_string_new
[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
[`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions
[`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic
[`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one
+[`multi_assignments`]: https://rust-lang.github.io/rust-clippy/master/index.html#multi_assignments
[`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions
[`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl
[`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate
[`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none
[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
+[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
[`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl
[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used
+[`result_large_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err
[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option
[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else
[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
[`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings
[`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn
+[`suspicious_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_to_owned
[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
[`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref
[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
[`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_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable
[`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding
[`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
return;
}
- let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file");
+ let test_output_file = fs::read(test_output_path).expect("Unable to read test output file");
let reference_file = fs::read(&reference_file_path).unwrap_or_default();
if test_output_file != reference_file {
// dependency
if fs::read_to_string(project_root.join("Cargo.toml"))
.expect("Failed to read clippy Cargo.toml")
- .contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
+ .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
{
return Err(CliError::IntellijSetupActive);
}
let args = &["--version"];
if context.verbose {
- println!("{}", format_command(&program, &dir, args));
+ println!("{}", format_command(program, &dir, args));
}
- let output = Command::new(&program).current_dir(&dir).args(args.iter()).output()?;
+ let output = Command::new(program).current_dir(&dir).args(args.iter()).output()?;
if output.status.success() {
Ok(())
Err(CliError::RustfmtNotInstalled)
} else {
Err(CliError::CommandFailed(
- format_command(&program, &dir, args),
+ format_command(program, &dir, args),
std::str::from_utf8(&output.stderr).unwrap_or("").to_string(),
))
}
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(once_cell)]
#![feature(rustc_private)]
name.split('_')
.map(|s| {
if s.is_empty() {
- String::from("")
+ String::new()
} else {
[&s[0..1].to_uppercase(), &s[1..]].concat()
}
.expect("failed to find `impl_lint_pass` terminator");
impl_lint_pass_end += impl_lint_pass_start;
- if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(&lint_name_upper) {
+ if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) {
let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len());
for c in content[lint_name_end..impl_lint_pass_end].chars() {
// Remove trailing whitespace
}
let mut content =
- fs::read_to_string(&path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
+ fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
eprintln!(
"warn: you will have to manually remove any code related to `{}` from `{}`",
+++ /dev/null
-use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, TyKind};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
- /// ### What it does
- /// Check for the usage of `as _` conversion using inferred type.
- ///
- /// ### Why is this bad?
- /// The conversion might include lossy conversion and dangerous cast that might go
- /// undetected du to the type being inferred.
- ///
- /// The lint is allowed by default as using `_` is less wordy than always specifying the type.
- ///
- /// ### Example
- /// ```rust
- /// fn foo(n: usize) {}
- /// let n: u16 = 256;
- /// foo(n as _);
- /// ```
- /// Use instead:
- /// ```rust
- /// fn foo(n: usize) {}
- /// let n: u16 = 256;
- /// foo(n as usize);
- /// ```
- #[clippy::version = "1.63.0"]
- pub AS_UNDERSCORE,
- restriction,
- "detects `as _` conversion"
-}
-declare_lint_pass!(AsUnderscore => [AS_UNDERSCORE]);
-
-impl<'tcx> LateLintPass<'tcx> for AsUnderscore {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
- if in_external_macro(cx.sess(), expr.span) {
- return;
- }
-
- if let ExprKind::Cast(_, ty) = expr.kind && let TyKind::Infer = ty.kind {
-
- let ty_resolved = cx.typeck_results().expr_ty(expr);
- if let ty::Error(_) = ty_resolved.kind() {
- span_lint_and_help(
- cx,
- AS_UNDERSCORE,
- expr.span,
- "using `as _` conversion",
- None,
- "consider giving the type explicitly",
- );
- } else {
- span_lint_and_then(
- cx,
- AS_UNDERSCORE,
- expr.span,
- "using `as _` conversion",
- |diag| {
- diag.span_suggestion(
- ty.span,
- "consider giving the type explicitly",
- ty_resolved,
- Applicability::MachineApplicable,
- );
- }
- );
- }
- }
- }
-}
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_no_std_crate;
-use clippy_utils::source::snippet_opt;
-use clippy_utils::{meets_msrv, msrvs};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, TyKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for the usage of `&expr as *const T` or
- /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
- /// `ptr::addr_of_mut` instead.
- ///
- /// ### Why is this bad?
- /// This would improve readability and avoid creating a reference
- /// that points to an uninitialized value or unaligned place.
- /// Read the `ptr::addr_of` docs for more information.
- ///
- /// ### Example
- /// ```rust
- /// let val = 1;
- /// let p = &val as *const i32;
- ///
- /// let mut val_mut = 1;
- /// let p_mut = &mut val_mut as *mut i32;
- /// ```
- /// Use instead:
- /// ```rust
- /// let val = 1;
- /// let p = std::ptr::addr_of!(val);
- ///
- /// let mut val_mut = 1;
- /// let p_mut = std::ptr::addr_of_mut!(val_mut);
- /// ```
- #[clippy::version = "1.60.0"]
- pub BORROW_AS_PTR,
- pedantic,
- "borrowing just to cast to a raw pointer"
-}
-
-impl_lint_pass!(BorrowAsPtr => [BORROW_AS_PTR]);
-
-pub struct BorrowAsPtr {
- msrv: Option<RustcVersion>,
-}
-
-impl BorrowAsPtr {
- #[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
- Self { msrv }
- }
-}
-
-impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if !meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
- return;
- }
-
- if expr.span.from_expansion() {
- return;
- }
-
- if_chain! {
- if let ExprKind::Cast(left_expr, ty) = &expr.kind;
- if let TyKind::Ptr(_) = ty.kind;
- if let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = &left_expr.kind;
-
- then {
- let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
- let macro_name = match mutability {
- Mutability::Not => "addr_of",
- Mutability::Mut => "addr_of_mut",
- };
-
- span_lint_and_sugg(
- cx,
- BORROW_AS_PTR,
- expr.span,
- "borrow as raw pointer",
- "try",
- format!(
- "{}::ptr::{}!({})",
- core_or_std,
- macro_name,
- snippet_opt(cx, e.span).unwrap()
- ),
- Applicability::MachineApplicable,
- );
- }
- }
- }
-
- extract_msrv_attr!(LateContext);
-}
///
/// ### Example
/// ```rust
- /// fn foo(_x: &str) {}
- ///
/// let s = &String::new();
///
/// let a: &String = &* s;
- /// foo(&*s);
/// ```
///
/// Use instead:
/// ```rust
- /// # fn foo(_x: &str) {}
/// # let s = &String::new();
/// let a: &String = s;
- /// foo(&**s);
/// ```
- #[clippy::version = "1.59.0"]
+ #[clippy::version = "1.63.0"]
pub BORROW_DEREF_REF,
complexity,
"deref on an immutable reference returns the same type as itself"
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::match_type;
-use clippy_utils::visitors::is_local_used;
-use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{self, UintTy};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for naive byte counts
- ///
- /// ### Why is this bad?
- /// The [`bytecount`](https://crates.io/crates/bytecount)
- /// crate has methods to count your bytes faster, especially for large slices.
- ///
- /// ### Known problems
- /// If you have predominantly small slices, the
- /// `bytecount::count(..)` method may actually be slower. However, if you can
- /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
- /// faster in those cases.
- ///
- /// ### Example
- /// ```rust
- /// # let vec = vec![1_u8];
- /// let count = vec.iter().filter(|x| **x == 0u8).count();
- /// ```
- ///
- /// Use instead:
- /// ```rust,ignore
- /// # let vec = vec![1_u8];
- /// let count = bytecount::count(&vec, 0u8);
- /// ```
- #[clippy::version = "pre 1.29.0"]
- pub NAIVE_BYTECOUNT,
- pedantic,
- "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
-}
-
-declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]);
-
-impl<'tcx> LateLintPass<'tcx> for ByteCount {
- fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
- if_chain! {
- if let ExprKind::MethodCall(count, [count_recv], _) = expr.kind;
- if count.ident.name == sym::count;
- if let ExprKind::MethodCall(filter, [filter_recv, filter_arg], _) = count_recv.kind;
- if filter.ident.name == sym!(filter);
- if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind;
- let body = cx.tcx.hir().body(body);
- if let [param] = body.params;
- if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
- if let ExprKind::Binary(ref op, l, r) = body.value.kind;
- if op.node == BinOpKind::Eq;
- if match_type(cx,
- cx.typeck_results().expr_ty(filter_recv).peel_refs(),
- &paths::SLICE_ITER);
- let operand_is_arg = |expr| {
- let expr = peel_ref_operators(cx, peel_blocks(expr));
- path_to_local_id(expr, arg_id)
- };
- let needle = if operand_is_arg(l) {
- r
- } else if operand_is_arg(r) {
- l
- } else {
- return;
- };
- if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
- if !is_local_used(cx, needle, arg_id);
- then {
- let haystack = if let ExprKind::MethodCall(path, args, _) =
- filter_recv.kind {
- let p = path.ident.name;
- if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
- &args[0]
- } else {
- filter_recv
- }
- } else {
- filter_recv
- };
- let mut applicability = Applicability::MaybeIncorrect;
- span_lint_and_sugg(
- cx,
- NAIVE_BYTECOUNT,
- expr.span,
- "you appear to be counting bytes the naive way",
- "consider using the bytecount crate",
- format!("bytecount::count({}, {})",
- snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
- snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
- applicability,
- );
- }
- };
- }
-}
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{match_def_path, paths};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir as hir;
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-
-declare_clippy_lint! {
- /// ### What it does
- /// It checks for `str::bytes().count()` and suggests replacing it with
- /// `str::len()`.
- ///
- /// ### Why is this bad?
- /// `str::bytes().count()` is longer and may not be as performant as using
- /// `str::len()`.
- ///
- /// ### Example
- /// ```rust
- /// "hello".bytes().count();
- /// String::from("hello").bytes().count();
- /// ```
- /// Use instead:
- /// ```rust
- /// "hello".len();
- /// String::from("hello").len();
- /// ```
- #[clippy::version = "1.62.0"]
- pub BYTES_COUNT_TO_LEN,
- complexity,
- "Using `bytes().count()` when `len()` performs the same functionality"
-}
-
-declare_lint_pass!(BytesCountToLen => [BYTES_COUNT_TO_LEN]);
-
-impl<'tcx> LateLintPass<'tcx> for BytesCountToLen {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
- if_chain! {
- if let hir::ExprKind::MethodCall(_, expr_args, _) = &expr.kind;
- if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
- if match_def_path(cx, expr_def_id, &paths::ITER_COUNT);
-
- if let [bytes_expr] = &**expr_args;
- if let hir::ExprKind::MethodCall(_, bytes_args, _) = &bytes_expr.kind;
- if let Some(bytes_def_id) = cx.typeck_results().type_dependent_def_id(bytes_expr.hir_id);
- if match_def_path(cx, bytes_def_id, &paths::STR_BYTES);
-
- if let [str_expr] = &**bytes_args;
- let ty = cx.typeck_results().expr_ty(str_expr).peel_refs();
-
- if is_type_diagnostic_item(cx, ty, sym::String) || ty.kind() == &ty::Str;
- then {
- let mut applicability = Applicability::MachineApplicable;
- span_lint_and_sugg(
- cx,
- BYTES_COUNT_TO_LEN,
- expr.span,
- "using long and hard to read `.bytes().count()`",
- "consider calling `.len()` instead",
- format!("{}.len()", snippet_with_applicability(cx, str_expr.span, "..", &mut applicability)),
- applicability
- );
- }
- };
- }
-}
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_help;
-use if_chain::if_chain;
-use rustc_ast::ast::LitKind;
-use rustc_hir::{Expr, ExprKind, PathSegment};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::{source_map::Spanned, symbol::sym, Span};
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for calls to `ends_with` with possible file extensions
- /// and suggests to use a case-insensitive approach instead.
- ///
- /// ### Why is this bad?
- /// `ends_with` is case-sensitive and may not detect files with a valid extension.
- ///
- /// ### Example
- /// ```rust
- /// fn is_rust_file(filename: &str) -> bool {
- /// filename.ends_with(".rs")
- /// }
- /// ```
- /// Use instead:
- /// ```rust
- /// fn is_rust_file(filename: &str) -> bool {
- /// let filename = std::path::Path::new(filename);
- /// filename.extension()
- /// .map(|ext| ext.eq_ignore_ascii_case("rs"))
- /// .unwrap_or(false)
- /// }
- /// ```
- #[clippy::version = "1.51.0"]
- pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
- pedantic,
- "Checks for calls to ends_with with case-sensitive file extensions"
-}
-
-declare_lint_pass!(CaseSensitiveFileExtensionComparisons => [CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS]);
-
-fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Span> {
- if_chain! {
- if let ExprKind::MethodCall(PathSegment { ident, .. }, [obj, extension, ..], span) = expr.kind;
- if ident.as_str() == "ends_with";
- if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind;
- if (2..=6).contains(&ext_literal.as_str().len());
- if ext_literal.as_str().starts_with('.');
- if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
- || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
- then {
- let mut ty = ctx.typeck_results().expr_ty(obj);
- ty = match ty.kind() {
- ty::Ref(_, ty, ..) => *ty,
- _ => ty
- };
-
- match ty.kind() {
- ty::Str => {
- return Some(span);
- },
- ty::Adt(def, _) => {
- if ctx.tcx.is_diagnostic_item(sym::String, def.did()) {
- return Some(span);
- }
- },
- _ => { return None; }
- }
- }
- }
- None
-}
-
-impl<'tcx> LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons {
- fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
- if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) {
- span_lint_and_help(
- ctx,
- CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
- span,
- "case-sensitive file extension comparison",
- None,
- "consider using a case-insensitive comparison instead",
- );
- }
- }
-}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, Ty, TyKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+
+use super::AS_UNDERSCORE;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ty: &'tcx Ty<'_>) {
+ if matches!(ty.kind, TyKind::Infer) {
+ span_lint_and_then(cx, AS_UNDERSCORE, expr.span, "using `as _` conversion", |diag| {
+ let ty_resolved = cx.typeck_results().expr_ty(expr);
+ if let ty::Error(_) = ty_resolved.kind() {
+ diag.help("consider giving the type explicitly");
+ } else {
+ diag.span_suggestion(
+ ty.span,
+ "consider giving the type explicitly",
+ ty_resolved,
+ Applicability::MachineApplicable,
+ );
+ }
+ });
+ }
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_no_std_crate;
+use clippy_utils::source::snippet_with_context;
+use rustc_errors::Applicability;
+use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind};
+use rustc_lint::LateContext;
+
+use super::BORROW_AS_PTR;
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ cast_expr: &'tcx Expr<'_>,
+ cast_to: &'tcx Ty<'_>,
+) {
+ if matches!(cast_to.kind, TyKind::Ptr(_))
+ && let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind
+ {
+ let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
+ let macro_name = match mutability {
+ Mutability::Not => "addr_of",
+ Mutability::Mut => "addr_of_mut",
+ };
+ let mut app = Applicability::MachineApplicable;
+ let snip = snippet_with_context(cx, e.span, cast_expr.span.ctxt(), "..", &mut app).0;
+
+ span_lint_and_sugg(
+ cx,
+ BORROW_AS_PTR,
+ expr.span,
+ "borrow as raw pointer",
+ "try",
+ format!("{}::ptr::{}!({})", core_or_std, macro_name, snip),
+ Applicability::MachineApplicable,
+ );
+ }
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{match_def_path, meets_msrv, msrvs, paths};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{def_id::DefId, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, Ty};
+use rustc_semver::RustcVersion;
+
+use super::CAST_SLICE_FROM_RAW_PARTS;
+
+enum RawPartsKind {
+ Immutable,
+ Mutable,
+}
+
+fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option<RawPartsKind> {
+ if match_def_path(cx, did, &paths::SLICE_FROM_RAW_PARTS) {
+ Some(RawPartsKind::Immutable)
+ } else if match_def_path(cx, did, &paths::SLICE_FROM_RAW_PARTS_MUT) {
+ Some(RawPartsKind::Mutable)
+ } else {
+ None
+ }
+}
+
+pub(super) fn check(
+ cx: &LateContext<'_>,
+ expr: &Expr<'_>,
+ cast_expr: &Expr<'_>,
+ cast_to: Ty<'_>,
+ msrv: Option<RustcVersion>,
+) {
+ if_chain! {
+ if meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS);
+ if let ty::RawPtr(ptrty) = cast_to.kind();
+ if let ty::Slice(_) = ptrty.ty.kind();
+ if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().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();
+ if let Some(rpk) = raw_parts_kind(cx, fun_def_id);
+ then {
+ let func = match rpk {
+ RawPartsKind::Immutable => "from_raw_parts",
+ RawPartsKind::Mutable => "from_raw_parts_mut"
+ };
+ let span = expr.span;
+ let mut applicability = Applicability::MachineApplicable;
+ let ptr = snippet_with_applicability(cx, ptr_arg.span, "ptr", &mut applicability);
+ let len = snippet_with_applicability(cx, len_arg.span, "len", &mut applicability);
+ span_lint_and_sugg(
+ cx,
+ CAST_SLICE_FROM_RAW_PARTS,
+ span,
+ &format!("casting the result of `{func}` to {cast_to}"),
+ "replace with",
+ format!("core::ptr::slice_{func}({ptr}, {len})"),
+ applicability
+ );
+ }
+ }
+}
+mod as_underscore;
+mod borrow_as_ptr;
mod cast_abs_to_unsigned;
mod cast_enum_constructor;
mod cast_lossless;
mod cast_ref_to_mut;
mod cast_sign_loss;
mod cast_slice_different_sizes;
+mod cast_slice_from_raw_parts;
mod char_lit_as_u8;
mod fn_to_numeric_cast;
mod fn_to_numeric_cast_any;
mod unnecessary_cast;
mod utils;
-use clippy_utils::is_hir_ty_cfg_dependant;
+use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
"casting the result of `abs()` to an unsigned integer can panic"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Check for the usage of `as _` conversion using inferred type.
+ ///
+ /// ### Why is this bad?
+ /// The conversion might include lossy conversion and dangerous cast that might go
+ /// undetected due to the type being inferred.
+ ///
+ /// The lint is allowed by default as using `_` is less wordy than always specifying the type.
+ ///
+ /// ### Example
+ /// ```rust
+ /// fn foo(n: usize) {}
+ /// let n: u16 = 256;
+ /// foo(n as _);
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// fn foo(n: usize) {}
+ /// let n: u16 = 256;
+ /// foo(n as usize);
+ /// ```
+ #[clippy::version = "1.63.0"]
+ pub AS_UNDERSCORE,
+ restriction,
+ "detects `as _` conversion"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the usage of `&expr as *const T` or
+ /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
+ /// `ptr::addr_of_mut` instead.
+ ///
+ /// ### Why is this bad?
+ /// This would improve readability and avoid creating a reference
+ /// that points to an uninitialized value or unaligned place.
+ /// Read the `ptr::addr_of` docs for more information.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let val = 1;
+ /// let p = &val as *const i32;
+ ///
+ /// let mut val_mut = 1;
+ /// let p_mut = &mut val_mut as *mut i32;
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let val = 1;
+ /// let p = std::ptr::addr_of!(val);
+ ///
+ /// let mut val_mut = 1;
+ /// let p_mut = std::ptr::addr_of_mut!(val_mut);
+ /// ```
+ #[clippy::version = "1.60.0"]
+ pub BORROW_AS_PTR,
+ pedantic,
+ "borrowing just to cast to a raw pointer"
+}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for a raw slice being cast to a slice pointer
+ ///
+ /// ### Why is this bad?
+ /// This can result in multiple `&mut` references to the same location when only a pointer is
+ /// required.
+ /// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require
+ /// the same [safety requirements] to be upheld.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _;
+ /// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _;
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
+ /// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len);
+ /// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len);
+ /// ```
+ /// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety
+ #[clippy::version = "1.64.0"]
+ pub CAST_SLICE_FROM_RAW_PARTS,
+ suspicious,
+ "casting a slice created from a pointer and length to a slice pointer"
+}
+
pub struct Casts {
msrv: Option<RustcVersion>,
}
PTR_AS_PTR,
CAST_ENUM_TRUNCATION,
CAST_ENUM_CONSTRUCTOR,
- CAST_ABS_TO_UNSIGNED
+ CAST_ABS_TO_UNSIGNED,
+ AS_UNDERSCORE,
+ BORROW_AS_PTR,
+ CAST_SLICE_FROM_RAW_PARTS
]);
impl<'tcx> LateLintPass<'tcx> for Casts {
return;
}
- if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
- if is_hir_ty_cfg_dependant(cx, cast_to) {
+ if let ExprKind::Cast(cast_expr, cast_to_hir) = expr.kind {
+ if is_hir_ty_cfg_dependant(cx, cast_to_hir) {
return;
}
let (cast_from, cast_to) = (
if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
return;
}
-
+ cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv);
fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
}
+
+ as_underscore::check(cx, expr, cast_to_hir);
+
+ if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
+ borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
+ }
}
cast_ref_to_mut::check(cx, expr);
fn lint_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) {
let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" };
+ let replaced_literal;
+ let matchless = if literal_str.contains(['(', ')']) {
+ replaced_literal = literal_str.replace(['(', ')'], "");
+ &replaced_literal
+ } else {
+ literal_str
+ };
span_lint_and_sugg(
cx,
UNNECESSARY_CAST,
expr.span,
&format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to),
"try",
- format!("{}_{}", literal_str.trim_end_matches('.'), cast_to),
+ format!("{}_{}", matchless.trim_end_matches('.'), cast_to),
Applicability::MachineApplicable,
);
}
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::has_enclosing_paren;
-use clippy_utils::ty::{expr_sig, peel_mid_ty_refs, ty_sig, variant_of_res};
-use clippy_utils::{get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage};
+use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
+use clippy_utils::{
+ fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local,
+ walk_to_expr_usage,
+};
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_ty, Visitor};
use rustc_hir::{
- self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId,
- ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
- TraitItemKind, TyKind, UnOp,
+ self as hir, def_id::DefId, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy,
+ GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind,
+ Path, QPath, TraitItem, TraitItemKind, TyKind, UnOp,
};
+use rustc_index::bit_set::BitSet;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
-use rustc_middle::ty::{self, Binder, BoundVariableKind, List, Ty, TyCtxt, TypeVisitable, TypeckResults};
+use rustc_middle::ty::{
+ self, subst::Subst, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
+ ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults,
+};
+use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
-use rustc_trait_selection::infer::InferCtxtExt;
+use rustc_trait_selection::infer::InferCtxtExt as _;
+use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
+use std::collections::VecDeque;
declare_clippy_lint! {
/// ### What it does
/// been finished. Note we can't lint at the end of every body as they can be nested within each
/// other.
current_body: Option<BodyId>,
+
/// The list of locals currently being checked by the lint.
/// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
/// This is needed for or patterns where one of the branches can be linted, but another can not
///
/// e.g. `m!(x) | Foo::Bar(ref x)`
ref_locals: FxIndexMap<HirId, Option<RefPat>>,
+
+ // `IntoIterator` for arrays requires Rust 1.53.
+ msrv: Option<RustcVersion>,
+}
+
+impl Dereferencing {
+ #[must_use]
+ pub fn new(msrv: Option<RustcVersion>) -> Self {
+ Self {
+ msrv,
+ ..Dereferencing::default()
+ }
+ }
}
struct StateData {
struct DerefedBorrow {
count: usize,
msg: &'static str,
+ snip_expr: Option<HirId>,
}
enum State {
match (self.state.take(), kind) {
(None, kind) => {
let expr_ty = typeck.expr_ty(expr);
- let (position, adjustments) = walk_parents(cx, expr);
+ let (position, adjustments) = walk_parents(cx, expr, self.msrv);
match kind {
RefOp::Deref => {
let deref_msg =
"this expression creates a reference which is immediately dereferenced by the compiler";
let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
+ let impl_msg = "the borrowed expression implements the required traits";
- let (required_refs, msg) = if position.can_auto_borrow() {
- (1, if deref_count == 1 { borrow_msg } else { deref_msg })
+ let (required_refs, msg, snip_expr) = if position.can_auto_borrow() {
+ (1, if deref_count == 1 { borrow_msg } else { deref_msg }, None)
+ } else if let Position::ImplArg(hir_id) = position {
+ (0, impl_msg, Some(hir_id))
} else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
next_adjust.map(|a| &a.kind)
{
if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable()
{
- (3, deref_msg)
+ (3, deref_msg, None)
} else {
- (2, deref_msg)
+ (2, deref_msg, None)
}
} else {
- (2, deref_msg)
+ (2, deref_msg, None)
};
if deref_count >= required_refs {
// can't be removed without breaking the code. See earlier comment.
count: deref_count - required_refs,
msg,
+ snip_expr,
}),
StateData { span: expr.span, hir_id: expr.hir_id, position },
));
spans: vec![pat.span],
app,
replacements: vec![(pat.span, snip.into())],
- hir_id: pat.hir_id
+ hir_id: pat.hir_id,
}),
);
}
self.current_body = None;
}
}
+
+ extract_msrv_attr!(LateContext);
}
fn try_parse_ref_op<'tcx>(
/// The method is defined on a reference type. e.g. `impl Foo for &T`
MethodReceiverRefImpl,
Callee,
+ ImplArg(HirId),
FieldAccess(Symbol),
Postfix,
Deref,
| Self::Callee
| Self::FieldAccess(_)
| Self::Postfix => PREC_POSTFIX,
- Self::Deref => PREC_PREFIX,
+ Self::ImplArg(_) | Self::Deref => PREC_PREFIX,
Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p,
}
}
/// Walks up the parent expressions attempting to determine both how stable the auto-deref result
/// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
/// locations as those follow different rules.
-#[allow(clippy::too_many_lines)]
-fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &'tcx [Adjustment<'tcx>]) {
+#[expect(clippy::too_many_lines)]
+fn walk_parents<'tcx>(
+ cx: &LateContext<'tcx>,
+ e: &'tcx Expr<'_>,
+ msrv: Option<RustcVersion>,
+) -> (Position, &'tcx [Adjustment<'tcx>]) {
let mut adjustments = [].as_slice();
let mut precedence = 0i8;
let ctxt = e.span.ctxt();
.iter()
.position(|arg| arg.hir_id == child_id)
.zip(expr_sig(cx, func))
- .and_then(|(i, sig)| sig.input_with_hir(i))
- .map(|(hir_ty, ty)| match hir_ty {
- // Type inference for closures can depend on how they're called. Only go by the explicit
- // types here.
- Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()),
- None => ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
- .position_for_arg(),
+ .and_then(|(i, sig)| {
+ sig.input_with_hir(i).map(|(hir_ty, ty)| match hir_ty {
+ // Type inference for closures can depend on how they're called. Only go by the explicit
+ // types here.
+ Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()),
+ None => {
+ if let ty::Param(param_ty) = ty.skip_binder().kind() {
+ needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv)
+ } else {
+ ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
+ .position_for_arg()
+ }
+ },
+ })
}),
ExprKind::MethodCall(_, args, _) => {
let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
.and_then(|subs| subs.get(1..))
{
Some(subs) => cx.tcx.mk_substs(subs.iter().copied()),
- None => cx.tcx.mk_substs([].iter()),
+ None => cx.tcx.mk_substs(std::iter::empty::<ty::subst::GenericArg<'_>>()),
} && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
// Trait methods taking `&self`
sub_ty
Position::MethodReceiver
}
} else {
- ty_auto_deref_stability(
- cx,
- cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)),
- precedence,
- )
- .position_for_arg()
+ let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
+ if let ty::Param(param_ty) = ty.kind() {
+ needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv)
+ } else {
+ ty_auto_deref_stability(
+ cx,
+ cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)),
+ precedence,
+ )
+ .position_for_arg()
+ }
}
})
},
v.0
}
+// Checks whether:
+// * child is an expression of the form `&e` in an argument position requiring an `impl Trait`
+// * `e`'s type implements `Trait` and is copyable
+// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`.
+// The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
+// be moved, but it cannot be.
+fn needless_borrow_impl_arg_position<'tcx>(
+ cx: &LateContext<'tcx>,
+ parent: &Expr<'tcx>,
+ arg_index: usize,
+ param_ty: ParamTy,
+ mut expr: &Expr<'tcx>,
+ precedence: i8,
+ msrv: Option<RustcVersion>,
+) -> Position {
+ let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
+ let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
+
+ let Some(callee_def_id) = fn_def_id(cx, parent) else { return Position::Other(precedence) };
+ let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
+ let substs_with_expr_ty = cx
+ .typeck_results()
+ .node_substs(if let ExprKind::Call(callee, _) = parent.kind {
+ callee.hir_id
+ } else {
+ parent.hir_id
+ });
+
+ let predicates = cx.tcx.param_env(callee_def_id).caller_bounds();
+ let projection_predicates = predicates
+ .iter()
+ .filter_map(|predicate| {
+ if let PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
+ Some(projection_predicate)
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<_>>();
+
+ let mut trait_with_ref_mut_self_method = false;
+
+ // If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return.
+ if predicates
+ .iter()
+ .filter_map(|predicate| {
+ if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
+ && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx)
+ {
+ Some(trait_predicate.trait_ref.def_id)
+ } else {
+ None
+ }
+ })
+ .inspect(|trait_def_id| {
+ trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id);
+ })
+ .all(|trait_def_id| {
+ Some(trait_def_id) == destruct_trait_def_id
+ || Some(trait_def_id) == sized_trait_def_id
+ || cx.tcx.is_diagnostic_item(sym::Any, trait_def_id)
+ })
+ {
+ return Position::Other(precedence);
+ }
+
+ // `substs_with_referent_ty` can be constructed outside of `check_referent` because the same
+ // elements are modified each time `check_referent` is called.
+ let mut substs_with_referent_ty = substs_with_expr_ty.to_vec();
+
+ let mut check_referent = |referent| {
+ let referent_ty = cx.typeck_results().expr_ty(referent);
+
+ if !is_copy(cx, referent_ty) {
+ return false;
+ }
+
+ // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
+ if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
+ return false;
+ }
+
+ if !replace_types(
+ cx,
+ param_ty,
+ referent_ty,
+ fn_sig,
+ arg_index,
+ &projection_predicates,
+ &mut substs_with_referent_ty,
+ ) {
+ return false;
+ }
+
+ predicates.iter().all(|predicate| {
+ if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
+ && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
+ && let ty::Param(param_ty) = trait_predicate.self_ty().kind()
+ && let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack()
+ && ty.is_array()
+ && !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR)
+ {
+ return false;
+ }
+
+ let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty);
+ let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate);
+ cx.tcx
+ .infer_ctxt()
+ .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
+ })
+ };
+
+ let mut needless_borrow = false;
+ while let ExprKind::AddrOf(_, _, referent) = expr.kind {
+ if !check_referent(referent) {
+ break;
+ }
+ expr = referent;
+ needless_borrow = true;
+ }
+
+ if needless_borrow {
+ Position::ImplArg(expr.hir_id)
+ } else {
+ Position::Other(precedence)
+ }
+}
+
+fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
+ cx.tcx
+ .associated_items(trait_def_id)
+ .in_definition_order()
+ .any(|assoc_item| {
+ if assoc_item.fn_has_self_parameter {
+ let self_ty = cx.tcx.fn_sig(assoc_item.def_id).skip_binder().inputs()[0];
+ matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut))
+ } else {
+ false
+ }
+ })
+}
+
+// Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting
+// projected type that is a type parameter. Returns `false` if replacing the types would have an
+// effect on the function signature beyond substituting `new_ty` for `param_ty`.
+// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
+fn replace_types<'tcx>(
+ cx: &LateContext<'tcx>,
+ param_ty: ParamTy,
+ new_ty: Ty<'tcx>,
+ fn_sig: FnSig<'tcx>,
+ arg_index: usize,
+ projection_predicates: &[ProjectionPredicate<'tcx>],
+ substs: &mut [ty::GenericArg<'tcx>],
+) -> bool {
+ let mut replaced = BitSet::new_empty(substs.len());
+
+ let mut deque = VecDeque::with_capacity(substs.len());
+ deque.push_back((param_ty, new_ty));
+
+ while let Some((param_ty, new_ty)) = deque.pop_front() {
+ // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in.
+ if !fn_sig
+ .inputs_and_output
+ .iter()
+ .enumerate()
+ .all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx)))
+ {
+ return false;
+ }
+
+ substs[param_ty.index as usize] = ty::GenericArg::from(new_ty);
+
+ // The `replaced.insert(...)` check provides some protection against infinite loops.
+ if replaced.insert(param_ty.index) {
+ for projection_predicate in projection_predicates {
+ if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx)
+ && let ty::Term::Ty(term_ty) = projection_predicate.term
+ && let ty::Param(term_param_ty) = term_ty.kind()
+ {
+ let item_def_id = projection_predicate.projection_ty.item_def_id;
+ let assoc_item = cx.tcx.associated_item(item_def_id);
+ let projection = cx.tcx
+ .mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, &[]));
+
+ if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
+ && substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
+ {
+ deque.push_back((*term_param_ty, projected_ty));
+ }
+ }
+ }
+ }
+ }
+
+ true
+}
+
struct TyPosition<'tcx> {
position: Position,
ty: Option<Ty<'tcx>>,
},
State::DerefedBorrow(state) => {
let mut app = Applicability::MachineApplicable;
- let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
+ let snip_expr = state.snip_expr.map_or(expr, |hir_id| cx.tcx.hir().expect_expr(hir_id));
+ let (snip, snip_is_macro) = snippet_with_context(cx, snip_expr.span, data.span.ctxt(), "..", &mut app);
span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
let calls_field = matches!(expr.kind, ExprKind::Field(..)) && matches!(data.position, Position::Callee);
let sugg = if !snip_is_macro
tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain(
params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| {
tcx.mk_predicate(Binder::dummy(PredicateKind::Trait(TraitPredicate {
- trait_ref: TraitRef::new(eq_trait_id, tcx.mk_substs([tcx.mk_param_from_def(param)].into_iter())),
+ trait_ref: TraitRef::new(
+ eq_trait_id,
+ tcx.mk_substs(std::iter::once(tcx.mk_param_from_def(param))),
+ ),
constness: BoundConstness::NotConst,
polarity: ImplPolarity::Positive,
})))
/// /// See also: [`foo`]
/// fn bar() {}
/// ```
- #[clippy::version = "1.60.0"]
+ #[clippy::version = "1.63.0"]
pub DOC_LINK_WITH_QUOTES,
pedantic,
"possible typo for an intra-doc link"
/// // a.rs
/// use crate::b;
/// ```
- #[clippy::version = "1.62.0"]
+ #[clippy::version = "1.63.0"]
pub DUPLICATE_MOD,
suspicious,
"file loaded as module multiple times"
use clippy_utils::diagnostics::span_lint_hir;
-use clippy_utils::ty::contains_ty;
use rustc_hir::intravisit;
use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind};
use rustc_infer::infer::TyCtxtInferExt;
///
/// ### Example
/// ```rust
- /// # fn foo(bar: usize) {}
- /// let x = Box::new(1);
- /// foo(*x);
- /// println!("{}", *x);
+ /// fn foo(x: Box<u32>) {}
/// ```
///
/// Use instead:
/// ```rust
- /// # fn foo(bar: usize) {}
- /// let x = 1;
- /// foo(x);
- /// println!("{}", x);
+ /// fn foo(x: u32) {}
/// ```
#[clippy::version = "pre 1.29.0"]
pub BOXED_LOCAL,
// skip if there is a `self` parameter binding to a type
// that contains `Self` (i.e.: `self: Box<Self>`), see #4804
if let Some(trait_self_ty) = self.trait_self_ty {
- if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(cmt.place.ty(), trait_self_ty) {
+ if map.name(cmt.hir_id) == kw::SelfLower && cmt.place.ty().contains(trait_self_ty) {
return;
}
}
expr.span,
"logarithm for bases 2, 10 and e can be computed more accurately",
"consider using",
- format!("{}.{}()", Sugg::hir(cx, &args[0], ".."), method),
+ format!("{}.{}()", Sugg::hir(cx, &args[0], "..").maybe_par(), method),
Applicability::MachineApplicable,
);
}
(
SUBOPTIMAL_FLOPS,
"square-root of a number can be computed more efficiently and accurately",
- format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..")),
+ format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..").maybe_par()),
)
} else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
(
IMPRECISE_FLOPS,
"cube-root of a number can be computed more accurately",
- format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..")),
+ format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..").maybe_par()),
)
} else if let Some(exponent) = get_integer_from_float_constant(&value) {
(
"exponentiation with integer powers can be computed more efficiently",
format!(
"{}.powi({})",
- Sugg::hir(cx, &args[0], ".."),
+ Sugg::hir(cx, &args[0], "..").maybe_par(),
numeric_literal::format(&exponent.to_string(), None, false)
),
)
"consider using",
format!(
"{}.mul_add({}, {})",
- Sugg::hir(cx, &args[0], ".."),
+ Sugg::hir(cx, &args[0], "..").maybe_par(),
Sugg::hir(cx, &args[0], ".."),
Sugg::hir(cx, other_addend, ".."),
),
"consider using",
format!(
"{}.exp_m1()",
- Sugg::hir(cx, self_arg, "..")
+ Sugg::hir(cx, self_arg, "..").maybe_par()
),
Applicability::MachineApplicable,
);
then {
let positive_abs_sugg = (
"manual implementation of `abs` method",
- format!("{}.abs()", Sugg::hir(cx, body, "..")),
+ format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
);
let negative_abs_sugg = (
"manual implementation of negation of `abs` method",
- format!("-{}.abs()", Sugg::hir(cx, body, "..")),
+ format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
);
let sugg = if is_testing_positive(cx, cond, body) {
if if_expr_positive {
expr.span,
"log base can be expressed more clearly",
"consider using",
- format!("{}.log({})", Sugg::hir(cx, largs_self, ".."), Sugg::hir(cx, rargs_self, ".."),),
+ format!("{}.log({})", Sugg::hir(cx, largs_self, "..").maybe_par(), Sugg::hir(cx, rargs_self, ".."),),
Applicability::MachineApplicable,
);
}
if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
(F32(180_f32) == lvalue || F64(180_f64) == lvalue)
{
- let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
+ let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
if_chain! {
if let ExprKind::Lit(ref literal) = mul_lhs.kind;
if let ast::LitKind::Float(ref value, float_type) = literal.node;
(F32(180_f32) == rvalue || F64(180_f64) == rvalue) &&
(F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
{
- let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
+ let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
if_chain! {
if let ExprKind::Lit(ref literal) = mul_lhs.kind;
if let ast::LitKind::Float(ref value, float_type) = literal.node;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
-use clippy_utils::source::{snippet_opt, snippet_with_applicability};
+use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use if_chain::if_chain;
use rustc_errors::Applicability;
};
let mut applicability = Applicability::MachineApplicable;
- if format_args.value_args.is_empty() {
- match *format_args.format_string_parts {
+ if format_args.args.is_empty() {
+ match *format_args.format_string.parts {
[] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
[_] => {
- if let Some(s_src) = snippet_opt(cx, format_args.format_string_span) {
- // Simulate macro expansion, converting {{ and }} to { and }.
- let s_expand = s_src.replace("{{", "{").replace("}}", "}");
- let sugg = format!("{}.to_string()", s_expand);
- span_useless_format(cx, call_site, sugg, applicability);
- }
+ // Simulate macro expansion, converting {{ and }} to { and }.
+ let s_expand = format_args.format_string.snippet.replace("{{", "{").replace("}}", "}");
+ let sugg = format!("{}.to_string()", s_expand);
+ span_useless_format(cx, call_site, sugg, applicability);
},
[..] => {},
}
- } else if let [value] = *format_args.value_args {
+ } else if let [arg] = &*format_args.args {
+ let value = arg.param.value;
if_chain! {
- if format_args.format_string_parts == [kw::Empty];
+ if format_args.format_string.parts == [kw::Empty];
if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()),
ty::Str => true,
_ => false,
};
- if let Some(args) = format_args.args();
- if args.iter().all(|arg| arg.format_trait == sym::Display && !arg.has_string_formatting());
+ if !arg.format.has_string_formatting();
then {
let is_new_string = match value.kind {
ExprKind::Binary(..) => true,
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::is_diag_trait_item;
-use clippy_utils::macros::{is_format_macro, FormatArgsArg, FormatArgsExpn};
+use clippy_utils::macros::{is_format_macro, FormatArgsExpn};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::implements_trait;
use if_chain::if_chain;
+use itertools::Itertools;
use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, HirId};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
use rustc_middle::ty::Ty;
if let Some(macro_def_id) = outermost_expn_data.macro_def_id;
if is_format_macro(cx, macro_def_id);
if let ExpnKind::Macro(_, name) = outermost_expn_data.kind;
- if let Some(args) = format_args.args();
then {
- for (i, arg) in args.iter().enumerate() {
- if arg.format_trait != sym::Display {
+ for arg in &format_args.args {
+ if arg.format.has_string_formatting() {
continue;
}
- if arg.has_string_formatting() {
+ if is_aliased(&format_args, arg.param.value.hir_id) {
continue;
}
- if is_aliased(&args, i) {
- continue;
- }
- check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.value);
- check_to_string_in_format_args(cx, name, arg.value);
+ check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value);
+ check_to_string_in_format_args(cx, name, arg.param.value);
}
}
}
if is_diag_trait_item(cx, method_def_id, sym::ToString);
let receiver_ty = cx.typeck_results().expr_ty(receiver);
if let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display);
+ let (n_needed_derefs, target) =
+ count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter());
+ if implements_trait(cx, target, display_trait_id, &[]);
+ if let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait();
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
then {
- let (n_needed_derefs, target) = count_needed_derefs(
- receiver_ty,
- cx.typeck_results().expr_adjustments(receiver).iter(),
- );
- if implements_trait(cx, target, display_trait_id, &[]) {
- if n_needed_derefs == 0 {
- span_lint_and_sugg(
- cx,
- TO_STRING_IN_FORMAT_ARGS,
- value.span.with_lo(receiver.span.hi()),
- &format!("`to_string` applied to a type that implements `Display` in `{}!` args", name),
- "remove this",
- String::new(),
- Applicability::MachineApplicable,
- );
- } else {
- span_lint_and_sugg(
- cx,
- TO_STRING_IN_FORMAT_ARGS,
- value.span,
- &format!("`to_string` applied to a type that implements `Display` in `{}!` args", name),
- "use this",
- format!("{:*>width$}{}", "", receiver_snippet, width = n_needed_derefs),
- Applicability::MachineApplicable,
- );
- }
+ let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
+ if n_needed_derefs == 0 && !needs_ref {
+ span_lint_and_sugg(
+ cx,
+ TO_STRING_IN_FORMAT_ARGS,
+ value.span.with_lo(receiver.span.hi()),
+ &format!(
+ "`to_string` applied to a type that implements `Display` in `{}!` args",
+ name
+ ),
+ "remove this",
+ String::new(),
+ Applicability::MachineApplicable,
+ );
+ } else {
+ span_lint_and_sugg(
+ cx,
+ TO_STRING_IN_FORMAT_ARGS,
+ value.span,
+ &format!(
+ "`to_string` applied to a type that implements `Display` in `{}!` args",
+ name
+ ),
+ "use this",
+ format!(
+ "{}{:*>width$}{}",
+ if needs_ref { "&" } else { "" },
+ "",
+ receiver_snippet,
+ width = n_needed_derefs
+ ),
+ Applicability::MachineApplicable,
+ );
}
}
}
}
-// Returns true if `args[i]` "refers to" or "is referred to by" another argument.
-fn is_aliased(args: &[FormatArgsArg<'_>], i: usize) -> bool {
- let value = args[i].value;
- args.iter()
- .enumerate()
- .any(|(j, arg)| i != j && std::ptr::eq(value, arg.value))
+// Returns true if `hir_id` is referred to by multiple format params
+fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
+ args.params()
+ .filter(|param| param.value.hir_id == hir_id)
+ .at_most_one()
+ .is_err()
}
fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArgsArg, FormatArgsExpn};
+use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArg, FormatArgsExpn};
use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
use if_chain::if_chain;
use rustc_errors::Applicability;
if let macro_def_id = outer_macro.def_id;
if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, outer_macro.expn);
if is_format_macro(cx, macro_def_id);
- if let Some(args) = format_args.args();
then {
- for arg in args {
- if arg.format_trait != impl_trait.name {
+ for arg in format_args.args {
+ if arg.format.r#trait != impl_trait.name {
continue;
}
check_format_arg_self(cx, expr, &arg, impl_trait);
}
}
-fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArgsArg<'_>, impl_trait: FormatTrait) {
+fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArg<'_>, impl_trait: FormatTrait) {
// Handle multiple dereferencing of references e.g. &&self
// Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl)
// Since the argument to fmt is itself a reference: &self
- let reference = peel_ref_operators(cx, arg.value);
+ let reference = peel_ref_operators(cx, arg.param.value);
let map = cx.tcx.hir();
// Is the reference self?
if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) {
mod must_use;
mod not_unsafe_ptr_arg_deref;
-mod result_unit_err;
+mod result;
mod too_many_arguments;
mod too_many_lines;
"public function returning `Result` with an `Err` type of `()`"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for functions that return `Result` with an unusually large
+ /// `Err`-variant.
+ ///
+ /// ### Why is this bad?
+ /// A `Result` is at least as large as the `Err`-variant. While we
+ /// expect that variant to be seldomly used, the compiler needs to reserve
+ /// and move that much memory every single time.
+ ///
+ /// ### Known problems
+ /// The size determined by Clippy is platform-dependent.
+ ///
+ /// ### Examples
+ /// ```rust
+ /// pub enum ParseError {
+ /// UnparsedBytes([u8; 512]),
+ /// UnexpectedEof,
+ /// }
+ ///
+ /// // The `Result` has at least 512 bytes, even in the `Ok`-case
+ /// pub fn parse() -> Result<(), ParseError> {
+ /// Ok(())
+ /// }
+ /// ```
+ /// should be
+ /// ```
+ /// pub enum ParseError {
+ /// UnparsedBytes(Box<[u8; 512]>),
+ /// UnexpectedEof,
+ /// }
+ ///
+ /// // The `Result` is slightly larger than a pointer
+ /// pub fn parse() -> Result<(), ParseError> {
+ /// Ok(())
+ /// }
+ /// ```
+ #[clippy::version = "1.64.0"]
+ pub RESULT_LARGE_ERR,
+ perf,
+ "function returning `Result` with large `Err` type"
+}
+
#[derive(Copy, Clone)]
pub struct Functions {
too_many_arguments_threshold: u64,
too_many_lines_threshold: u64,
+ large_error_threshold: u64,
}
impl Functions {
- pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64) -> Self {
+ pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64, large_error_threshold: u64) -> Self {
Self {
too_many_arguments_threshold,
too_many_lines_threshold,
+ large_error_threshold,
}
}
}
DOUBLE_MUST_USE,
MUST_USE_CANDIDATE,
RESULT_UNIT_ERR,
+ RESULT_LARGE_ERR,
]);
impl<'tcx> LateLintPass<'tcx> for Functions {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
must_use::check_item(cx, item);
- result_unit_err::check_item(cx, item);
+ result::check_item(cx, item, self.large_error_threshold);
}
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
must_use::check_impl_item(cx, item);
- result_unit_err::check_impl_item(cx, item);
+ result::check_impl_item(cx, item, self.large_error_threshold);
}
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold);
not_unsafe_ptr_arg_deref::check_trait_item(cx, item);
must_use::check_trait_item(cx, item);
- result_unit_err::check_trait_item(cx, item);
+ result::check_trait_item(cx, item, self.large_error_threshold);
}
}
--- /dev/null
+use rustc_errors::Diagnostic;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::{self, Ty};
+use rustc_span::{sym, Span};
+use rustc_typeck::hir_ty_to_ty;
+
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
+use clippy_utils::trait_ref_of_method;
+use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item};
+
+use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR};
+
+/// The type of the `Err`-variant in a `std::result::Result` returned by the
+/// given `FnDecl`
+fn result_err_ty<'tcx>(
+ cx: &LateContext<'tcx>,
+ decl: &hir::FnDecl<'tcx>,
+ item_span: Span,
+) -> Option<(&'tcx hir::Ty<'tcx>, Ty<'tcx>)> {
+ if !in_external_macro(cx.sess(), item_span)
+ && let hir::FnRetTy::Return(hir_ty) = decl.output
+ && let ty = hir_ty_to_ty(cx.tcx, hir_ty)
+ && is_type_diagnostic_item(cx, ty, sym::Result)
+ && let ty::Adt(_, substs) = ty.kind()
+ {
+ let err_ty = substs.type_at(1);
+ Some((hir_ty, err_ty))
+ } else {
+ None
+ }
+}
+
+pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, large_err_threshold: u64) {
+ if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind
+ && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span)
+ {
+ if cx.access_levels.is_exported(item.def_id) {
+ let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
+ check_result_unit_err(cx, err_ty, fn_header_span);
+ }
+ check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
+ }
+}
+
+pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::ImplItem<'tcx>, large_err_threshold: u64) {
+ // Don't lint if method is a trait's implementation, we can't do anything about those
+ if let hir::ImplItemKind::Fn(ref sig, _) = item.kind
+ && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span)
+ && trait_ref_of_method(cx, item.def_id).is_none()
+ {
+ if cx.access_levels.is_exported(item.def_id) {
+ let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
+ check_result_unit_err(cx, err_ty, fn_header_span);
+ }
+ check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
+ }
+}
+
+pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::TraitItem<'tcx>, large_err_threshold: u64) {
+ if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
+ let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
+ if let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span) {
+ if cx.access_levels.is_exported(item.def_id) {
+ check_result_unit_err(cx, err_ty, fn_header_span);
+ }
+ check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
+ }
+ }
+}
+
+fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: Span) {
+ if err_ty.is_unit() {
+ span_lint_and_help(
+ cx,
+ RESULT_UNIT_ERR,
+ fn_header_span,
+ "this returns a `Result<_, ()>`",
+ None,
+ "use a custom `Error` type instead",
+ );
+ }
+}
+
+fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) {
+ let ty_size = approx_ty_size(cx, err_ty);
+ if ty_size >= large_err_threshold {
+ span_lint_and_then(
+ cx,
+ RESULT_LARGE_ERR,
+ hir_ty_span,
+ "the `Err`-variant returned from this function is very large",
+ |diag: &mut Diagnostic| {
+ diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes"));
+ diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`"));
+ },
+ );
+ }
+}
+++ /dev/null
-use rustc_hir as hir;
-use rustc_lint::{LateContext, LintContext};
-use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty;
-use rustc_span::{sym, Span};
-use rustc_typeck::hir_ty_to_ty;
-
-use if_chain::if_chain;
-
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::trait_ref_of_method;
-use clippy_utils::ty::is_type_diagnostic_item;
-
-use super::RESULT_UNIT_ERR;
-
-pub(super) fn check_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
- if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind {
- let is_public = cx.access_levels.is_exported(item.def_id);
- let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
- if is_public {
- check_result_unit_err(cx, sig.decl, item.span, fn_header_span);
- }
- }
-}
-
-pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &hir::ImplItem<'_>) {
- if let hir::ImplItemKind::Fn(ref sig, _) = item.kind {
- let is_public = cx.access_levels.is_exported(item.def_id);
- let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
- if is_public && trait_ref_of_method(cx, item.def_id).is_none() {
- check_result_unit_err(cx, sig.decl, item.span, fn_header_span);
- }
- }
-}
-
-pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>) {
- if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
- let is_public = cx.access_levels.is_exported(item.def_id);
- let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
- if is_public {
- check_result_unit_err(cx, sig.decl, item.span, fn_header_span);
- }
- }
-}
-
-fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span: Span, fn_header_span: Span) {
- if_chain! {
- if !in_external_macro(cx.sess(), item_span);
- if let hir::FnRetTy::Return(ty) = decl.output;
- let ty = hir_ty_to_ty(cx.tcx, ty);
- if is_type_diagnostic_item(cx, ty, sym::Result);
- if let ty::Adt(_, substs) = ty.kind();
- let err_ty = substs.type_at(1);
- if err_ty.is_unit();
- then {
- span_lint_and_help(
- cx,
- RESULT_UNIT_ERR,
- fn_header_span,
- "this returns a `Result<_, ()>`",
- None,
- "use a custom `Error` type instead",
- );
- }
- }
-}
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{is_slice_of_primitives, match_def_path, paths};
-use if_chain::if_chain;
-use rustc_ast::LitKind;
-use rustc_errors::Applicability;
-use rustc_hir as hir;
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Spanned;
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for using `x.get(0)` instead of
- /// `x.first()`.
- ///
- /// ### Why is this bad?
- /// Using `x.first()` is easier to read and has the same
- /// result.
- ///
- /// ### Example
- /// ```rust
- /// let x = vec![2, 3, 5];
- /// let first_element = x.get(0);
- /// ```
- ///
- /// Use instead:
- /// ```rust
- /// let x = vec![2, 3, 5];
- /// let first_element = x.first();
- /// ```
- #[clippy::version = "1.63.0"]
- pub GET_FIRST,
- style,
- "Using `x.get(0)` when `x.first()` is simpler"
-}
-declare_lint_pass!(GetFirst => [GET_FIRST]);
-
-impl<'tcx> LateLintPass<'tcx> for GetFirst {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
- if_chain! {
- if let hir::ExprKind::MethodCall(_, [struct_calling_on, method_arg], _) = &expr.kind;
- if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
- if match_def_path(cx, expr_def_id, &paths::SLICE_GET);
-
- if let Some(_) = is_slice_of_primitives(cx, struct_calling_on);
- if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = method_arg.kind;
-
- then {
- let mut applicability = Applicability::MachineApplicable;
- let slice_name = snippet_with_applicability(
- cx,
- struct_calling_on.span, "..",
- &mut applicability,
- );
- span_lint_and_sugg(
- cx,
- GET_FIRST,
- expr.span,
- &format!("accessing first element with `{0}.get(0)`", slice_name),
- "try",
- format!("{}.first()", slice_name),
- applicability,
- );
- }
- }
- }
-}
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::SpanlessEq;
use if_chain::if_chain;
+use rustc_errors::Diagnostic;
use rustc_hir::intravisit::{self as visit, Visitor};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
- let mut arm_visit = ArmVisitor {
- mutex_lock_called: false,
- found_mutex: None,
- cx,
- };
- let mut op_visit = OppVisitor {
- mutex_lock_called: false,
- found_mutex: None,
- cx,
- };
+ let mut arm_visit = ArmVisitor { found_mutex: None, cx };
+ let mut op_visit = OppVisitor { found_mutex: None, cx };
if let Some(higher::IfLet {
let_expr,
if_then,
}) = higher::IfLet::hir(cx, expr)
{
op_visit.visit_expr(let_expr);
- if op_visit.mutex_lock_called {
+ if let Some(op_mutex) = op_visit.found_mutex {
arm_visit.visit_expr(if_then);
arm_visit.visit_expr(if_else);
- if arm_visit.mutex_lock_called && arm_visit.same_mutex(cx, op_visit.found_mutex.unwrap()) {
- span_lint_and_help(
+ if let Some(arm_mutex) = arm_visit.found_mutex_if_same_as(op_mutex) {
+ let diag = |diag: &mut Diagnostic| {
+ diag.span_label(
+ op_mutex.span,
+ "this Mutex will remain locked for the entire `if let`-block...",
+ );
+ diag.span_label(
+ arm_mutex.span,
+ "... and is tried to lock again here, which will always deadlock.",
+ );
+ diag.help("move the lock call outside of the `if let ...` expression");
+ };
+ span_lint_and_then(
cx,
IF_LET_MUTEX,
expr.span,
"calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
- None,
- "move the lock call outside of the `if let ...` expression",
+ diag,
);
}
}
/// Checks if `Mutex::lock` is called in the `if let` expr.
pub struct OppVisitor<'a, 'tcx> {
- mutex_lock_called: bool,
found_mutex: Option<&'tcx Expr<'tcx>>,
cx: &'a LateContext<'tcx>,
}
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
self.found_mutex = Some(mutex);
- self.mutex_lock_called = true;
return;
}
visit::walk_expr(self, expr);
/// Checks if `Mutex::lock` is called in any of the branches.
pub struct ArmVisitor<'a, 'tcx> {
- mutex_lock_called: bool,
found_mutex: Option<&'tcx Expr<'tcx>>,
cx: &'a LateContext<'tcx>,
}
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
self.found_mutex = Some(mutex);
- self.mutex_lock_called = true;
return;
}
visit::walk_expr(self, expr);
}
impl<'tcx, 'l> ArmVisitor<'tcx, 'l> {
- fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool {
- self.found_mutex
- .map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex))
+ fn found_mutex_if_same_as(&self, op_mutex: &Expr<'_>) -> Option<&Expr<'_>> {
+ self.found_mutex.and_then(|arm_mutex| {
+ SpanlessEq::new(self.cx)
+ .eq_expr(op_mutex, arm_mutex)
+ .then_some(arm_mutex)
+ })
}
}
if_chain! {
if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
if path.ident.as_str() == "lock";
- let ty = cx.typeck_results().expr_ty(self_arg);
+ let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
if is_type_diagnostic_item(cx, ty, sym::Mutex);
then {
Some(self_arg)
use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::eager_or_lazy::switch_to_eager_eval;
use clippy_utils::source::snippet_with_macro_callsite;
use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs, peel_blocks};
-use if_chain::if_chain;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
declare_clippy_lint! {
/// ### What it does
- /// Checks for if-else that could be written to `bool::then`.
+ /// Checks for if-else that could be written using either `bool::then` or `bool::then_some`.
///
/// ### Why is this bad?
- /// Looks a little redundant. Using `bool::then` helps it have less lines of code.
+ /// Looks a little redundant. Using `bool::then` is more concise and incurs no loss of clarity.
+ /// For simple calculations and known values, use `bool::then_some`, which is eagerly evaluated
+ /// in comparison to `bool::then`.
///
/// ### Example
/// ```rust
#[clippy::version = "1.53.0"]
pub IF_THEN_SOME_ELSE_NONE,
restriction,
- "Finds if-else that could be written using `bool::then`"
+ "Finds if-else that could be written using either `bool::then` or `bool::then_some`"
}
pub struct IfThenSomeElseNone {
impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
- fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if !meets_msrv(self.msrv, msrvs::BOOL_THEN) {
return;
}
return;
}
- if_chain! {
- if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr);
- if let ExprKind::Block(then_block, _) = then.kind;
- 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 is_lang_ctor(cx, then_call_qpath, OptionSome);
- if let ExprKind::Path(ref qpath) = peel_blocks(els).kind;
- if is_lang_ctor(cx, qpath, OptionNone);
- if !stmts_contains_early_return(then_block.stmts);
- then {
- let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
- let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
- format!("({})", cond_snip)
- } else {
- cond_snip.into_owned()
- };
- let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
- let closure_body = if then_block.stmts.is_empty() {
- arg_snip.into_owned()
- } else {
- format!("{{ /* snippet */ {} }}", arg_snip)
- };
- let help = format!(
- "consider using `bool::then` like: `{}.then(|| {})`",
- cond_snip,
- closure_body,
- );
- span_lint_and_help(
- cx,
- IF_THEN_SOME_ELSE_NONE,
- expr.span,
- "this could be simplified with `bool::then`",
- None,
- &help,
- );
- }
+ if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr)
+ && let ExprKind::Block(then_block, _) = then.kind
+ && let Some(then_expr) = then_block.expr
+ && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
+ && let ExprKind::Path(ref then_call_qpath) = then_call.kind
+ && is_lang_ctor(cx, then_call_qpath, OptionSome)
+ && let ExprKind::Path(ref qpath) = peel_blocks(els).kind
+ && is_lang_ctor(cx, qpath, OptionNone)
+ && !stmts_contains_early_return(then_block.stmts)
+ {
+ let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
+ let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
+ format!("({})", cond_snip)
+ } else {
+ cond_snip.into_owned()
+ };
+ let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
+ let mut method_body = if then_block.stmts.is_empty() {
+ arg_snip.into_owned()
+ } else {
+ format!("{{ /* snippet */ {} }}", arg_snip)
+ };
+ let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
+ "then_some"
+ } else {
+ method_body.insert_str(0, "|| ");
+ "then"
+ };
+
+ let help = format!(
+ "consider using `bool::{}` like: `{}.{}({})`",
+ method_name, cond_snip, method_name, method_body,
+ );
+ span_lint_and_help(
+ cx,
+ IF_THEN_SOME_ELSE_NONE,
+ expr.span,
+ &format!("this could be simplified with `bool::{}`", method_name),
+ None,
+ &help,
+ );
}
}
LintId::of(booleans::NONMINIMAL_BOOL),
LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR),
LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
- LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
LintId::of(casts::CAST_ABS_TO_UNSIGNED),
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
LintId::of(casts::CAST_ENUM_TRUNCATION),
LintId::of(casts::CAST_REF_TO_MUT),
LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
+ LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
LintId::of(casts::CHAR_LIT_AS_U8),
LintId::of(casts::FN_TO_NUMERIC_CAST),
LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
LintId::of(functions::DOUBLE_MUST_USE),
LintId::of(functions::MUST_USE_UNIT),
LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
+ LintId::of(functions::RESULT_LARGE_ERR),
LintId::of(functions::RESULT_UNIT_ERR),
LintId::of(functions::TOO_MANY_ARGUMENTS),
- LintId::of(get_first::GET_FIRST),
LintId::of(if_let_mutex::IF_LET_MUTEX),
LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
LintId::of(infinite_iter::INFINITE_ITER),
LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
LintId::of(manual_retain::MANUAL_RETAIN),
LintId::of(manual_strip::MANUAL_STRIP),
- LintId::of(map_clone::MAP_CLONE),
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
LintId::of(match_result_ok::MATCH_RESULT_OK),
LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
LintId::of(methods::BIND_INSTEAD_OF_MAP),
+ LintId::of(methods::BYTES_COUNT_TO_LEN),
LintId::of(methods::BYTES_NTH),
LintId::of(methods::CHARS_LAST_CMP),
LintId::of(methods::CHARS_NEXT_CMP),
LintId::of(methods::CLONE_DOUBLE_REF),
LintId::of(methods::CLONE_ON_COPY),
+ LintId::of(methods::COLLAPSIBLE_STR_REPLACE),
LintId::of(methods::ERR_EXPECT),
LintId::of(methods::EXPECT_FUN_CALL),
LintId::of(methods::EXTEND_WITH_DRAIN),
LintId::of(methods::FILTER_MAP_IDENTITY),
LintId::of(methods::FILTER_NEXT),
LintId::of(methods::FLAT_MAP_IDENTITY),
+ LintId::of(methods::GET_FIRST),
LintId::of(methods::GET_LAST_WITH_LEN),
LintId::of(methods::INSPECT_FOR_EACH),
LintId::of(methods::INTO_ITER_ON_REF),
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
LintId::of(methods::MANUAL_SPLIT_ONCE),
LintId::of(methods::MANUAL_STR_REPEAT),
+ LintId::of(methods::MAP_CLONE),
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
LintId::of(methods::MAP_FLATTEN),
LintId::of(methods::MAP_IDENTITY),
+ LintId::of(methods::MUT_MUTEX_LOCK),
LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
LintId::of(methods::NEEDLESS_OPTION_TAKE),
LintId::of(methods::NEEDLESS_SPLITN),
LintId::of(methods::NEW_RET_NO_SELF),
+ LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
LintId::of(methods::NO_EFFECT_REPLACE),
LintId::of(methods::OBFUSCATED_IF_ELSE),
LintId::of(methods::OK_EXPECT),
LintId::of(methods::OPTION_MAP_OR_NONE),
LintId::of(methods::OR_FUN_CALL),
LintId::of(methods::OR_THEN_UNWRAP),
+ LintId::of(methods::RANGE_ZIP_WITH_LEN),
+ LintId::of(methods::REPEAT_ONCE),
LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
LintId::of(methods::SEARCH_IS_SOME),
LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
LintId::of(methods::STRING_EXTEND_CHARS),
LintId::of(methods::SUSPICIOUS_MAP),
LintId::of(methods::SUSPICIOUS_SPLITN),
+ LintId::of(methods::SUSPICIOUS_TO_OWNED),
LintId::of(methods::UNINIT_ASSUMED_INIT),
+ LintId::of(methods::UNIT_HASH),
LintId::of(methods::UNNECESSARY_FILTER_MAP),
LintId::of(methods::UNNECESSARY_FIND_MAP),
LintId::of(methods::UNNECESSARY_FOLD),
LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
+ LintId::of(methods::UNNECESSARY_SORT_BY),
LintId::of(methods::UNNECESSARY_TO_OWNED),
LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
LintId::of(methods::USELESS_ASREF),
+ LintId::of(methods::VEC_RESIZE_TO_ZERO),
LintId::of(methods::WRONG_SELF_CONVENTION),
LintId::of(methods::ZST_OFFSET),
LintId::of(minmax::MIN_MAX),
LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION),
+ LintId::of(multi_assignments::MULTI_ASSIGNMENTS),
LintId::of(mut_key::MUTABLE_KEY_TYPE),
- LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
LintId::of(needless_bool::BOOL_COMPARISON),
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
LintId::of(octal_escapes::OCTAL_ESCAPES),
- LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
+ LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
LintId::of(operators::ASSIGN_OP_PATTERN),
LintId::of(operators::BAD_BIT_MASK),
LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
LintId::of(question_mark::QUESTION_MARK),
LintId::of(ranges::MANUAL_RANGE_CONTAINS),
- LintId::of(ranges::RANGE_ZIP_WITH_LEN),
LintId::of(ranges::REVERSED_EMPTY_RANGES),
LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
LintId::of(reference::DEREF_ADDROF),
LintId::of(regex::INVALID_REGEX),
- LintId::of(repeat_once::REPEAT_ONCE),
LintId::of(returns::LET_AND_RETURN),
LintId::of(returns::NEEDLESS_RETURN),
LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
+ LintId::of(transmute::TRANSMUTING_NULL),
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
LintId::of(transmute::USELESS_TRANSMUTE),
LintId::of(transmute::WRONG_TRANSMUTE),
- LintId::of(transmuting_null::TRANSMUTING_NULL),
LintId::of(types::BORROWED_BOX),
LintId::of(types::BOX_COLLECTION),
LintId::of(types::REDUNDANT_ALLOCATION),
LintId::of(types::VEC_BOX),
LintId::of(unicode::INVISIBLE_CHARACTERS),
LintId::of(uninit_vec::UNINIT_VEC),
- LintId::of(unit_hash::UNIT_HASH),
LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
LintId::of(unit_types::LET_UNIT_VALUE),
LintId::of(unit_types::UNIT_ARG),
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
- LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
+ LintId::of(unused_peekable::UNUSED_PEEKABLE),
LintId::of(unused_unit::UNUSED_UNIT),
LintId::of(unwrap::PANICKING_UNWRAP),
LintId::of(unwrap::UNNECESSARY_UNWRAP),
LintId::of(useless_conversion::USELESS_CONVERSION),
LintId::of(vec::USELESS_VEC),
LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
- LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
+ LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
LintId::of(write::PRINTLN_EMPTY_STRING),
LintId::of(write::PRINT_LITERAL),
LintId::of(write::PRINT_WITH_NEWLINE),
LintId::of(attrs::DEPRECATED_CFG_ATTR),
LintId::of(booleans::NONMINIMAL_BOOL),
LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
- LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
LintId::of(casts::CHAR_LIT_AS_U8),
LintId::of(casts::UNNECESSARY_CAST),
LintId::of(dereference::EXPLICIT_AUTO_DEREF),
LintId::of(matches::NEEDLESS_MATCH),
LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
LintId::of(methods::BIND_INSTEAD_OF_MAP),
+ LintId::of(methods::BYTES_COUNT_TO_LEN),
LintId::of(methods::CLONE_ON_COPY),
LintId::of(methods::FILTER_MAP_IDENTITY),
LintId::of(methods::FILTER_NEXT),
LintId::of(methods::OPTION_AS_REF_DEREF),
LintId::of(methods::OPTION_FILTER_MAP),
LintId::of(methods::OR_THEN_UNWRAP),
+ LintId::of(methods::RANGE_ZIP_WITH_LEN),
+ LintId::of(methods::REPEAT_ONCE),
LintId::of(methods::SEARCH_IS_SOME),
LintId::of(methods::SKIP_WHILE_NEXT),
LintId::of(methods::UNNECESSARY_FILTER_MAP),
LintId::of(methods::UNNECESSARY_FIND_MAP),
+ LintId::of(methods::UNNECESSARY_SORT_BY),
LintId::of(methods::USELESS_ASREF),
LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
LintId::of(no_effect::NO_EFFECT),
LintId::of(no_effect::UNNECESSARY_OPERATION),
+ LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
LintId::of(operators::DOUBLE_COMPARISONS),
LintId::of(operators::DURATION_SUBSEC),
LintId::of(operators::IDENTITY_OP),
LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
LintId::of(precedence::PRECEDENCE),
LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
- LintId::of(ranges::RANGE_ZIP_WITH_LEN),
LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
LintId::of(redundant_slicing::REDUNDANT_SLICING),
LintId::of(reference::DEREF_ADDROF),
- LintId::of(repeat_once::REPEAT_ONCE),
LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
LintId::of(swap::MANUAL_SWAP),
LintId::of(types::TYPE_COMPLEXITY),
LintId::of(types::VEC_BOX),
LintId::of(unit_types::UNIT_ARG),
- LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
LintId::of(unwrap::UNNECESSARY_UNWRAP),
LintId::of(useless_conversion::USELESS_CONVERSION),
LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
LintId::of(methods::CLONE_DOUBLE_REF),
LintId::of(methods::ITERATOR_STEP_BY_ZERO),
+ LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
LintId::of(methods::SUSPICIOUS_SPLITN),
LintId::of(methods::UNINIT_ASSUMED_INIT),
+ LintId::of(methods::UNIT_HASH),
+ LintId::of(methods::VEC_RESIZE_TO_ZERO),
LintId::of(methods::ZST_OFFSET),
LintId::of(minmax::MIN_MAX),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
- LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
LintId::of(operators::BAD_BIT_MASK),
LintId::of(operators::CMP_NAN),
LintId::of(serde_api::SERDE_API_MISUSE),
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
LintId::of(swap::ALMOST_SWAPPED),
+ LintId::of(transmute::TRANSMUTING_NULL),
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
LintId::of(transmute::WRONG_TRANSMUTE),
- LintId::of(transmuting_null::TRANSMUTING_NULL),
LintId::of(unicode::INVISIBLE_CHARACTERS),
LintId::of(uninit_vec::UNINIT_VEC),
- LintId::of(unit_hash::UNIT_HASH),
LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
LintId::of(unit_types::UNIT_CMP),
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
LintId::of(unwrap::PANICKING_UNWRAP),
- LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
])
almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
approx_const::APPROX_CONSTANT,
as_conversions::AS_CONVERSIONS,
- as_underscore::AS_UNDERSCORE,
asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
bool_assert_comparison::BOOL_ASSERT_COMPARISON,
booleans::NONMINIMAL_BOOL,
booleans::OVERLY_COMPLEX_BOOL_EXPR,
- borrow_as_ptr::BORROW_AS_PTR,
borrow_deref_ref::BORROW_DEREF_REF,
- bytecount::NAIVE_BYTECOUNT,
- bytes_count_to_len::BYTES_COUNT_TO_LEN,
cargo::CARGO_COMMON_METADATA,
cargo::MULTIPLE_CRATE_VERSIONS,
cargo::NEGATIVE_FEATURE_NAMES,
cargo::REDUNDANT_FEATURE_NAMES,
cargo::WILDCARD_DEPENDENCIES,
- case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
+ casts::AS_UNDERSCORE,
+ casts::BORROW_AS_PTR,
casts::CAST_ABS_TO_UNSIGNED,
casts::CAST_ENUM_CONSTRUCTOR,
casts::CAST_ENUM_TRUNCATION,
casts::CAST_REF_TO_MUT,
casts::CAST_SIGN_LOSS,
casts::CAST_SLICE_DIFFERENT_SIZES,
+ casts::CAST_SLICE_FROM_RAW_PARTS,
casts::CHAR_LIT_AS_U8,
casts::FN_TO_NUMERIC_CAST,
casts::FN_TO_NUMERIC_CAST_ANY,
functions::MUST_USE_CANDIDATE,
functions::MUST_USE_UNIT,
functions::NOT_UNSAFE_PTR_ARG_DEREF,
+ functions::RESULT_LARGE_ERR,
functions::RESULT_UNIT_ERR,
functions::TOO_MANY_ARGUMENTS,
functions::TOO_MANY_LINES,
future_not_send::FUTURE_NOT_SEND,
- get_first::GET_FIRST,
if_let_mutex::IF_LET_MUTEX,
if_not_else::IF_NOT_ELSE,
if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
manual_bits::MANUAL_BITS,
manual_instant_elapsed::MANUAL_INSTANT_ELAPSED,
manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
- manual_ok_or::MANUAL_OK_OR,
manual_rem_euclid::MANUAL_REM_EUCLID,
manual_retain::MANUAL_RETAIN,
+ manual_string_new::MANUAL_STRING_NEW,
manual_strip::MANUAL_STRIP,
- map_clone::MAP_CLONE,
- map_err_ignore::MAP_ERR_IGNORE,
map_unit_fn::OPTION_MAP_UNIT_FN,
map_unit_fn::RESULT_MAP_UNIT_FN,
match_result_ok::MATCH_RESULT_OK,
mem_replace::MEM_REPLACE_WITH_DEFAULT,
mem_replace::MEM_REPLACE_WITH_UNINIT,
methods::BIND_INSTEAD_OF_MAP,
+ methods::BYTES_COUNT_TO_LEN,
methods::BYTES_NTH,
+ methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
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::COLLAPSIBLE_STR_REPLACE,
methods::ERR_EXPECT,
methods::EXPECT_FUN_CALL,
methods::EXPECT_USED,
methods::FLAT_MAP_IDENTITY,
methods::FLAT_MAP_OPTION,
methods::FROM_ITER_INSTEAD_OF_COLLECT,
+ methods::GET_FIRST,
methods::GET_LAST_WITH_LEN,
methods::GET_UNWRAP,
methods::IMPLICIT_CLONE,
methods::ITER_NEXT_SLICE,
methods::ITER_NTH,
methods::ITER_NTH_ZERO,
+ methods::ITER_ON_EMPTY_COLLECTIONS,
+ methods::ITER_ON_SINGLE_ITEMS,
methods::ITER_OVEREAGER_CLONED,
methods::ITER_SKIP_NEXT,
methods::ITER_WITH_DRAIN,
methods::MANUAL_FILTER_MAP,
methods::MANUAL_FIND_MAP,
+ methods::MANUAL_OK_OR,
methods::MANUAL_SATURATING_ARITHMETIC,
methods::MANUAL_SPLIT_ONCE,
methods::MANUAL_STR_REPEAT,
+ methods::MAP_CLONE,
methods::MAP_COLLECT_RESULT_UNIT,
+ methods::MAP_ERR_IGNORE,
methods::MAP_FLATTEN,
methods::MAP_IDENTITY,
methods::MAP_UNWRAP_OR,
+ methods::MUT_MUTEX_LOCK,
+ methods::NAIVE_BYTECOUNT,
methods::NEEDLESS_OPTION_AS_DEREF,
methods::NEEDLESS_OPTION_TAKE,
methods::NEEDLESS_SPLITN,
methods::NEW_RET_NO_SELF,
+ methods::NONSENSICAL_OPEN_OPTIONS,
methods::NO_EFFECT_REPLACE,
methods::OBFUSCATED_IF_ELSE,
methods::OK_EXPECT,
methods::OPTION_MAP_OR_NONE,
methods::OR_FUN_CALL,
methods::OR_THEN_UNWRAP,
+ methods::PATH_BUF_PUSH_OVERWRITE,
+ methods::RANGE_ZIP_WITH_LEN,
+ methods::REPEAT_ONCE,
methods::RESULT_MAP_OR_INTO_OPTION,
methods::SEARCH_IS_SOME,
methods::SHOULD_IMPLEMENT_TRAIT,
methods::SINGLE_CHAR_ADD_STR,
methods::SINGLE_CHAR_PATTERN,
methods::SKIP_WHILE_NEXT,
+ methods::STABLE_SORT_PRIMITIVE,
methods::STRING_EXTEND_CHARS,
methods::SUSPICIOUS_MAP,
methods::SUSPICIOUS_SPLITN,
+ methods::SUSPICIOUS_TO_OWNED,
methods::UNINIT_ASSUMED_INIT,
+ methods::UNIT_HASH,
methods::UNNECESSARY_FILTER_MAP,
methods::UNNECESSARY_FIND_MAP,
methods::UNNECESSARY_FOLD,
methods::UNNECESSARY_JOIN,
methods::UNNECESSARY_LAZY_EVALUATIONS,
+ methods::UNNECESSARY_SORT_BY,
methods::UNNECESSARY_TO_OWNED,
methods::UNWRAP_OR_ELSE_DEFAULT,
methods::UNWRAP_USED,
methods::USELESS_ASREF,
+ methods::VEC_RESIZE_TO_ZERO,
+ methods::VERBOSE_FILE_READS,
methods::WRONG_SELF_CONVENTION,
methods::ZST_OFFSET,
minmax::MIN_MAX,
mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION,
module_style::MOD_MODULE_FILES,
module_style::SELF_NAMED_MODULE_FILES,
+ multi_assignments::MULTI_ASSIGNMENTS,
mut_key::MUTABLE_KEY_TYPE,
mut_mut::MUT_MUT,
- mut_mutex_lock::MUT_MUTEX_LOCK,
mut_reference::UNNECESSARY_MUT_PASSED,
mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
mutex_atomic::MUTEX_ATOMIC,
nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
octal_escapes::OCTAL_ESCAPES,
only_used_in_recursion::ONLY_USED_IN_RECURSION,
- open_options::NONSENSICAL_OPEN_OPTIONS,
operators::ABSURD_EXTREME_COMPARISONS,
operators::ARITHMETIC,
operators::ASSIGN_OP_PATTERN,
partialeq_to_none::PARTIALEQ_TO_NONE,
pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
- path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
precedence::PRECEDENCE,
ptr::CMP_NULL,
ranges::MANUAL_RANGE_CONTAINS,
ranges::RANGE_MINUS_ONE,
ranges::RANGE_PLUS_ONE,
- ranges::RANGE_ZIP_WITH_LEN,
ranges::REVERSED_EMPTY_RANGES,
rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT,
read_zero_byte_vec::READ_ZERO_BYTE_VEC,
reference::DEREF_ADDROF,
regex::INVALID_REGEX,
regex::TRIVIAL_REGEX,
- repeat_once::REPEAT_ONCE,
return_self_not_must_use::RETURN_SELF_NOT_MUST_USE,
returns::LET_AND_RETURN,
returns::NEEDLESS_RETURN,
single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
- stable_sort_primitive::STABLE_SORT_PRIMITIVE,
std_instead_of_core::ALLOC_INSTEAD_OF_CORE,
std_instead_of_core::STD_INSTEAD_OF_ALLOC,
std_instead_of_core::STD_INSTEAD_OF_CORE,
transmute::TRANSMUTE_PTR_TO_PTR,
transmute::TRANSMUTE_PTR_TO_REF,
transmute::TRANSMUTE_UNDEFINED_REPR,
+ transmute::TRANSMUTING_NULL,
transmute::UNSOUND_COLLECTION_TRANSMUTE,
transmute::USELESS_TRANSMUTE,
transmute::WRONG_TRANSMUTE,
- transmuting_null::TRANSMUTING_NULL,
types::BORROWED_BOX,
types::BOX_COLLECTION,
types::LINKEDLIST,
unicode::NON_ASCII_LITERAL,
unicode::UNICODE_NOT_NFC,
uninit_vec::UNINIT_VEC,
- unit_hash::UNIT_HASH,
unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
unit_types::LET_UNIT_VALUE,
unit_types::UNIT_ARG,
unnamed_address::VTABLE_ADDRESS_COMPARISONS,
unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS,
unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
- unnecessary_sort_by::UNNECESSARY_SORT_BY,
unnecessary_wraps::UNNECESSARY_WRAPS,
unnested_or_patterns::UNNESTED_OR_PATTERNS,
unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
unused_async::UNUSED_ASYNC,
unused_io_amount::UNUSED_IO_AMOUNT,
+ unused_peekable::UNUSED_PEEKABLE,
unused_rounding::UNUSED_ROUNDING,
unused_self::UNUSED_SELF,
unused_unit::UNUSED_UNIT,
useless_conversion::USELESS_CONVERSION,
vec::USELESS_VEC,
vec_init_then_push::VEC_INIT_THEN_PUSH,
- vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
- verbose_file_reads::VERBOSE_FILE_READS,
wildcard_imports::ENUM_GLOB_USE,
wildcard_imports::WILDCARD_IMPORTS,
+ write::POSITIONAL_NAMED_FORMAT_PARAMETERS,
write::PRINTLN_EMPTY_STRING,
write::PRINT_LITERAL,
write::PRINT_STDERR,
LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE),
LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE),
+ LintId::of(methods::ITER_ON_EMPTY_COLLECTIONS),
+ LintId::of(methods::ITER_ON_SINGLE_ITEMS),
LintId::of(methods::ITER_WITH_DRAIN),
+ LintId::of(methods::PATH_BUF_PUSH_OVERWRITE),
LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
LintId::of(mutex_atomic::MUTEX_ATOMIC),
LintId::of(mutex_atomic::MUTEX_INTEGER),
LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
- LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
- LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
LintId::of(regex::TRIVIAL_REGEX),
LintId::of(strings::STRING_LIT_AS_BYTES),
store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
LintId::of(attrs::INLINE_ALWAYS),
- LintId::of(borrow_as_ptr::BORROW_AS_PTR),
- LintId::of(bytecount::NAIVE_BYTECOUNT),
- LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
+ LintId::of(casts::BORROW_AS_PTR),
LintId::of(casts::CAST_LOSSLESS),
LintId::of(casts::CAST_POSSIBLE_TRUNCATION),
LintId::of(casts::CAST_POSSIBLE_WRAP),
LintId::of(macro_use::MACRO_USE_IMPORTS),
LintId::of(manual_assert::MANUAL_ASSERT),
LintId::of(manual_instant_elapsed::MANUAL_INSTANT_ELAPSED),
- LintId::of(manual_ok_or::MANUAL_OK_OR),
+ LintId::of(manual_string_new::MANUAL_STRING_NEW),
LintId::of(matches::MATCH_BOOL),
LintId::of(matches::MATCH_ON_VEC_ITEMS),
LintId::of(matches::MATCH_SAME_ARMS),
LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
LintId::of(matches::MATCH_WILD_ERR_ARM),
LintId::of(matches::SINGLE_MATCH_ELSE),
+ LintId::of(methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
LintId::of(methods::FILTER_MAP_NEXT),
LintId::of(methods::FLAT_MAP_OPTION),
LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
LintId::of(methods::IMPLICIT_CLONE),
LintId::of(methods::INEFFICIENT_TO_STRING),
+ LintId::of(methods::MANUAL_OK_OR),
LintId::of(methods::MAP_UNWRAP_OR),
+ LintId::of(methods::NAIVE_BYTECOUNT),
+ LintId::of(methods::STABLE_SORT_PRIMITIVE),
LintId::of(methods::UNNECESSARY_JOIN),
LintId::of(misc::USED_UNDERSCORE_BINDING),
LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER),
LintId::of(ref_option_ref::REF_OPTION_REF),
LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
- LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
LintId::of(strings::STRING_ADD_ASSIGN),
LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
LintId::of(escape::BOXED_LOCAL),
LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
+ LintId::of(functions::RESULT_LARGE_ERR),
LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
LintId::of(loops::MANUAL_MEMCPY),
LintId::of(loops::MISSING_SPIN_LOOP),
LintId::of(loops::NEEDLESS_COLLECT),
LintId::of(manual_retain::MANUAL_RETAIN),
+ LintId::of(methods::COLLAPSIBLE_STR_REPLACE),
LintId::of(methods::EXPECT_FUN_CALL),
LintId::of(methods::EXTEND_WITH_DRAIN),
LintId::of(methods::ITER_NTH),
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(as_conversions::AS_CONVERSIONS),
- LintId::of(as_underscore::AS_UNDERSCORE),
LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES),
LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON),
+ LintId::of(casts::AS_UNDERSCORE),
LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
LintId::of(create_dir::CREATE_DIR),
LintId::of(dbg_macro::DBG_MACRO),
LintId::of(large_include_file::LARGE_INCLUDE_FILE),
LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
- LintId::of(map_err_ignore::MAP_ERR_IGNORE),
LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
LintId::of(matches::TRY_ERR),
LintId::of(matches::WILDCARD_ENUM_MATCH_ARM),
LintId::of(methods::EXPECT_USED),
LintId::of(methods::FILETYPE_IS_FILE),
LintId::of(methods::GET_UNWRAP),
+ LintId::of(methods::MAP_ERR_IGNORE),
LintId::of(methods::UNWRAP_USED),
+ LintId::of(methods::VERBOSE_FILE_READS),
LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX),
LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
LintId::of(unicode::NON_ASCII_LITERAL),
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(write::PRINT_STDOUT),
LintId::of(write::USE_DEBUG),
LintId::of(functions::DOUBLE_MUST_USE),
LintId::of(functions::MUST_USE_UNIT),
LintId::of(functions::RESULT_UNIT_ERR),
- LintId::of(get_first::GET_FIRST),
LintId::of(inherent_to_string::INHERENT_TO_STRING),
LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
LintId::of(len_zero::COMPARISON_TO_EMPTY),
LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
LintId::of(manual_bits::MANUAL_BITS),
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
- LintId::of(map_clone::MAP_CLONE),
LintId::of(match_result_ok::MATCH_RESULT_OK),
LintId::of(matches::COLLAPSIBLE_MATCH),
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
LintId::of(methods::CHARS_LAST_CMP),
LintId::of(methods::CHARS_NEXT_CMP),
LintId::of(methods::ERR_EXPECT),
+ LintId::of(methods::GET_FIRST),
LintId::of(methods::INTO_ITER_ON_REF),
LintId::of(methods::IS_DIGIT_ASCII_RADIX),
LintId::of(methods::ITER_CLONED_COLLECT),
LintId::of(methods::ITER_NTH_ZERO),
LintId::of(methods::ITER_SKIP_NEXT),
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
+ LintId::of(methods::MAP_CLONE),
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
+ LintId::of(methods::MUT_MUTEX_LOCK),
LintId::of(methods::NEW_RET_NO_SELF),
LintId::of(methods::OBFUSCATED_IF_ELSE),
LintId::of(methods::OK_EXPECT),
LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
LintId::of(misc_early::REDUNDANT_PATTERN),
- LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
LintId::of(casts::CAST_ABS_TO_UNSIGNED),
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
LintId::of(casts::CAST_ENUM_TRUNCATION),
+ LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
LintId::of(drop_forget_ref::DROP_NON_DROP),
LintId::of(drop_forget_ref::FORGET_NON_DROP),
LintId::of(loops::MUT_RANGE_BOUND),
LintId::of(methods::NO_EFFECT_REPLACE),
LintId::of(methods::SUSPICIOUS_MAP),
+ LintId::of(methods::SUSPICIOUS_TO_OWNED),
+ LintId::of(multi_assignments::MULTI_ASSIGNMENTS),
LintId::of(mut_key::MUTABLE_KEY_TYPE),
LintId::of(octal_escapes::OCTAL_ESCAPES),
LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS),
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
+ LintId::of(unused_peekable::UNUSED_PEEKABLE),
+ LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
])
#![feature(control_flow_enum)]
#![feature(drain_filter)]
#![feature(iter_intersperse)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(lint_reasons)]
#![feature(never_type)]
mod almost_complete_letter_range;
mod approx_const;
mod as_conversions;
-mod as_underscore;
mod asm_syntax;
mod assertions_on_constants;
mod assertions_on_result_states;
mod blocks_in_if_conditions;
mod bool_assert_comparison;
mod booleans;
-mod borrow_as_ptr;
mod borrow_deref_ref;
-mod bytecount;
-mod bytes_count_to_len;
mod cargo;
-mod case_sensitive_file_extension_comparisons;
mod casts;
mod checked_conversions;
mod cognitive_complexity;
mod from_str_radix_10;
mod functions;
mod future_not_send;
-mod get_first;
mod if_let_mutex;
mod if_not_else;
mod if_then_some_else_none;
mod manual_bits;
mod manual_instant_elapsed;
mod manual_non_exhaustive;
-mod manual_ok_or;
mod manual_rem_euclid;
mod manual_retain;
+mod manual_string_new;
mod manual_strip;
-mod map_clone;
-mod map_err_ignore;
mod map_unit_fn;
mod match_result_ok;
mod matches;
mod missing_inline;
mod mixed_read_write_in_expression;
mod module_style;
+mod multi_assignments;
mod mut_key;
mod mut_mut;
-mod mut_mutex_lock;
mod mut_reference;
mod mutable_debug_assertion;
mod mutex_atomic;
mod nonstandard_macro_braces;
mod octal_escapes;
mod only_used_in_recursion;
-mod open_options;
mod operators;
mod option_env_unwrap;
mod option_if_let_else;
mod partialeq_ne_impl;
mod partialeq_to_none;
mod pass_by_ref_or_value;
-mod path_buf_push_overwrite;
mod pattern_type_mismatch;
mod precedence;
mod ptr;
mod ref_option_ref;
mod reference;
mod regex;
-mod repeat_once;
mod return_self_not_must_use;
mod returns;
mod same_name_method;
mod single_component_path_imports;
mod size_of_in_element_count;
mod slow_vector_initialization;
-mod stable_sort_primitive;
mod std_instead_of_core;
mod strings;
mod strlen_on_c_strings;
mod trailing_empty_array;
mod trait_bounds;
mod transmute;
-mod transmuting_null;
mod types;
mod undocumented_unsafe_blocks;
mod unicode;
mod uninit_vec;
-mod unit_hash;
mod unit_return_expecting_ord;
mod unit_types;
mod unnamed_address;
mod unnecessary_owned_empty_strings;
mod unnecessary_self_imports;
-mod unnecessary_sort_by;
mod unnecessary_wraps;
mod unnested_or_patterns;
mod unsafe_removed_from_name;
mod unused_async;
mod unused_io_amount;
+mod unused_peekable;
mod unused_rounding;
mod unused_self;
mod unused_unit;
mod useless_conversion;
mod vec;
mod vec_init_then_push;
-mod vec_resize_to_zero;
-mod verbose_file_reads;
mod wildcard_imports;
mod write;
mod zero_div_zero;
store.register_late_pass(|| Box::new(blocks_in_if_conditions::BlocksInIfConditions));
store.register_late_pass(|| Box::new(unicode::Unicode));
store.register_late_pass(|| Box::new(uninit_vec::UninitVec));
- store.register_late_pass(|| Box::new(unit_hash::UnitHash));
store.register_late_pass(|| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
store.register_late_pass(|| Box::new(strings::StringAdd));
store.register_late_pass(|| Box::new(implicit_return::ImplicitReturn));
store.register_late_pass(move || Box::new(needless_question_mark::NeedlessQuestionMark));
store.register_late_pass(move || Box::new(casts::Casts::new(msrv)));
store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
- store.register_late_pass(move || Box::new(map_clone::MapClone::new(msrv)));
-
store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
msrv,
))
});
- store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
store.register_late_pass(|| Box::new(shadow::Shadow::default()));
store.register_late_pass(|| Box::new(unit_types::UnitTypes));
store.register_late_pass(|| Box::new(loops::Loops));
store.register_late_pass(|| Box::new(lifetimes::Lifetimes));
store.register_late_pass(|| Box::new(entry::HashMapPass));
store.register_late_pass(|| Box::new(minmax::MinMaxPass));
- store.register_late_pass(|| Box::new(open_options::OpenOptions));
store.register_late_pass(|| Box::new(zero_div_zero::ZeroDiv));
store.register_late_pass(|| Box::new(mutex_atomic::Mutex));
store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate));
store.register_late_pass(move || Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone())));
let too_many_arguments_threshold = conf.too_many_arguments_threshold;
let too_many_lines_threshold = conf.too_many_lines_threshold;
+ let large_error_threshold = conf.large_error_threshold;
store.register_late_pass(move || {
Box::new(functions::Functions::new(
too_many_arguments_threshold,
too_many_lines_threshold,
+ large_error_threshold,
))
});
let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
);
store.register_late_pass(move || Box::new(pass_by_ref_or_value));
store.register_late_pass(|| Box::new(ref_option_ref::RefOptionRef));
- store.register_late_pass(|| Box::new(bytecount::ByteCount));
store.register_late_pass(|| Box::new(infinite_iter::InfiniteIter));
store.register_late_pass(|| Box::new(inline_fn_without_body::InlineFnWithoutBody));
store.register_late_pass(|| Box::new(useless_conversion::UselessConversion::default()));
store.register_late_pass(|| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
store.register_late_pass(|| Box::new(redundant_clone::RedundantClone));
store.register_late_pass(|| Box::new(slow_vector_initialization::SlowVectorInit));
- store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy));
store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
store.register_late_pass(|| Box::new(assertions_on_result_states::AssertionsOnResultStates));
- store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
- store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
let max_trait_bounds = conf.max_trait_bounds;
store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
store.register_late_pass(move || Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
- store.register_late_pass(|| Box::new(verbose_file_reads::VerboseFileReads));
store.register_late_pass(|| Box::new(redundant_pub_crate::RedundantPubCrate::default()));
store.register_late_pass(|| Box::new(unnamed_address::UnnamedAddress));
- store.register_late_pass(|| Box::new(dereference::Dereferencing::default()));
+ store.register_late_pass(move || Box::new(dereference::Dereferencing::new(msrv)));
store.register_late_pass(|| Box::new(option_if_let_else::OptionIfLetElse));
store.register_late_pass(|| Box::new(future_not_send::FutureNotSend));
store.register_late_pass(|| Box::new(if_let_mutex::IfLetMutex));
store.register_late_pass(|| Box::new(if_not_else::IfNotElse));
store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality));
- store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock));
store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn));
- store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero));
store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn));
let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
store.register_early_pass(move || {
store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(¯o_matcher)));
store.register_late_pass(|| Box::new(macro_use::MacroUseImports::default()));
store.register_late_pass(|| Box::new(pattern_type_mismatch::PatternTypeMismatch));
- store.register_late_pass(|| Box::new(stable_sort_primitive::StableSortPrimitive));
- store.register_late_pass(|| Box::new(repeat_once::RepeatOnce));
store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult));
- store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr));
store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
let disallowed_methods = conf.disallowed_methods.clone();
store.register_late_pass(|| Box::new(strings::StringToString));
store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
store.register_late_pass(|| Box::new(vec_init_then_push::VecInitThenPush::default()));
- store.register_late_pass(|| {
- Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons)
- });
store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing));
store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields));
store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
- store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv)));
store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation));
store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes));
- store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion));
+ store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion::default()));
let allow_dbg_in_tests = conf.allow_dbg_in_tests;
store.register_late_pass(move || Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
let cargo_ignore_publish = conf.cargo_ignore_publish;
store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
store.register_early_pass(|| Box::new(pub_use::PubUse));
store.register_late_pass(|| Box::new(format_push_string::FormatPushString));
- store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen));
let max_include_file_size = conf.max_include_file_size;
store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace));
store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default()));
- store.register_late_pass(|| Box::new(get_first::GetFirst));
store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
store.register_late_pass(|| Box::new(swap_ptr_to_ref::SwapPtrToRef));
store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch));
- store.register_late_pass(|| Box::new(as_underscore::AsUnderscore));
store.register_late_pass(|| Box::new(read_zero_byte_vec::ReadZeroByteVec));
store.register_late_pass(|| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
store.register_late_pass(move || Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports::default()));
store.register_late_pass(|| Box::new(manual_instant_elapsed::ManualInstantElapsed));
store.register_late_pass(|| Box::new(partialeq_to_none::PartialeqToNone));
+ store.register_late_pass(|| Box::new(manual_string_new::ManualStringNew));
+ store.register_late_pass(|| Box::new(unused_peekable::UnusedPeekable));
+ store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
// add lints here, do not remove this comment, it's used in `new_lint`
}
use super::NEEDLESS_COLLECT;
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
+use clippy_utils::higher;
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item;
impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
for (expr, hir_id) in block.stmts.iter().filter_map(get_expr_and_hir_id_from_stmt) {
+ if check_loop_kind(expr).is_some() {
+ continue;
+ }
self.visit_block_expr(expr, hir_id);
}
if let Some(expr) = block.expr {
- self.visit_block_expr(expr, None);
+ if let Some(loop_kind) = check_loop_kind(expr) {
+ if let LoopKind::Conditional(block_expr) = loop_kind {
+ self.visit_block_expr(block_expr, None);
+ }
+ } else {
+ self.visit_block_expr(expr, None);
+ }
}
}
}
}
+enum LoopKind<'tcx> {
+ Conditional(&'tcx Expr<'tcx>),
+ Loop,
+}
+
+fn check_loop_kind<'tcx>(expr: &Expr<'tcx>) -> Option<LoopKind<'tcx>> {
+ if let Some(higher::WhileLet { let_expr, .. }) = higher::WhileLet::hir(expr) {
+ return Some(LoopKind::Conditional(let_expr));
+ }
+ if let Some(higher::While { condition, .. }) = higher::While::hir(expr) {
+ return Some(LoopKind::Conditional(condition));
+ }
+ if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr) {
+ return Some(LoopKind::Conditional(arg));
+ }
+ if let ExprKind::Loop { .. } = expr.kind {
+ return Some(LoopKind::Loop);
+ }
+
+ None
+}
+
impl<'tcx> IterFunctionVisitor<'_, 'tcx> {
fn visit_block_expr(&mut self, expr: &'tcx Expr<'tcx>, hir_id: Option<HirId>) {
self.current_statement_hir_id = hir_id;
match output.kind {
TyKind::Tup(tys) if tys.is_empty() => {
let sugg = "remove the return type";
- Some((sugg, "".into()))
+ Some((sugg, String::new()))
},
_ => {
let sugg = "return the output of the future directly";
+++ /dev/null
-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::{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::{Closure, Expr, ExprKind, PatKind};
-use rustc_lint::LintContext;
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::lint::in_external_macro;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::sym;
-
-declare_clippy_lint! {
- /// ### What it does
- ///
- /// Finds patterns that reimplement `Option::ok_or`.
- ///
- /// ### Why is this bad?
- ///
- /// Concise code helps focusing on behavior instead of boilerplate.
- ///
- /// ### Examples
- /// ```rust
- /// let foo: Option<i32> = None;
- /// foo.map_or(Err("error"), |v| Ok(v));
- /// ```
- ///
- /// Use instead:
- /// ```rust
- /// let foo: Option<i32> = None;
- /// foo.ok_or("error");
- /// ```
- #[clippy::version = "1.49.0"]
- pub MANUAL_OK_OR,
- pedantic,
- "finds patterns that can be encoded more concisely with `Option::ok_or`"
-}
-
-declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]);
-
-impl<'tcx> LateLintPass<'tcx> for ManualOkOr {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) {
- if in_external_macro(cx.sess(), scrutinee.span) {
- return;
- }
-
- if_chain! {
- if let ExprKind::MethodCall(method_segment, [receiver, or_expr, map_expr], _) = scrutinee.kind;
- if method_segment.ident.name == sym!(map_or);
- let ty = cx.typeck_results().expr_ty(receiver);
- if is_type_diagnostic_item(cx, ty, sym::Option);
- if is_ok_wrapping(cx, map_expr);
- if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind;
- if is_lang_ctor(cx, err_path, ResultErr);
- if let Some(method_receiver_snippet) = snippet_opt(cx, receiver.span);
- if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
- if let Some(indent) = indent_of(cx, scrutinee.span);
- then {
- let reindented_err_arg_snippet =
- reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
- span_lint_and_sugg(
- cx,
- MANUAL_OK_OR,
- scrutinee.span,
- "this pattern reimplements `Option::ok_or`",
- "replace with",
- format!(
- "{}.ok_or({})",
- method_receiver_snippet,
- reindented_err_arg_snippet
- ),
- Applicability::MachineApplicable,
- );
- }
- }
- }
-}
-
-fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
- if let ExprKind::Path(ref qpath) = map_expr.kind {
- if is_lang_ctor(cx, qpath, ResultOk) {
- return true;
- }
- }
- if_chain! {
- if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind;
- let body = cx.tcx.hir().body(body);
- 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 is_lang_ctor(cx, ok_path, ResultOk);
- then { path_to_local_id(ok_arg, param_id) } else { false }
- }
-}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use rustc_ast::LitKind;
+use rustc_errors::Applicability::MachineApplicable;
+use rustc_hir::{Expr, ExprKind, PathSegment, QPath, TyKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{sym, symbol, Span};
+
+declare_clippy_lint! {
+ /// ### What it does
+ ///
+ /// Checks for usage of `""` to create a `String`, such as `"".to_string()`, `"".to_owned()`,
+ /// `String::from("")` and others.
+ ///
+ /// ### Why is this bad?
+ ///
+ /// Different ways of creating an empty string makes your code less standardized, which can
+ /// be confusing.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let a = "".to_string();
+ /// let b: String = "".into();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let a = String::new();
+ /// let b = String::new();
+ /// ```
+ #[clippy::version = "1.65.0"]
+ pub MANUAL_STRING_NEW,
+ pedantic,
+ "empty String is being created manually"
+}
+declare_lint_pass!(ManualStringNew => [MANUAL_STRING_NEW]);
+
+impl LateLintPass<'_> for ManualStringNew {
+ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+ if expr.span.from_expansion() {
+ return;
+ }
+
+ let ty = cx.typeck_results().expr_ty(expr);
+ match ty.kind() {
+ ty::Adt(adt_def, _) if adt_def.is_struct() => {
+ if !cx.tcx.is_diagnostic_item(sym::String, adt_def.did()) {
+ return;
+ }
+ },
+ _ => return,
+ }
+
+ match expr.kind {
+ ExprKind::Call(func, args) => {
+ parse_call(cx, expr.span, func, args);
+ },
+ ExprKind::MethodCall(path_segment, args, _) => {
+ parse_method_call(cx, expr.span, path_segment, args);
+ },
+ _ => (),
+ }
+ }
+}
+
+/// Checks if an expression's kind corresponds to an empty &str.
+fn is_expr_kind_empty_str(expr_kind: &ExprKind<'_>) -> bool {
+ if let ExprKind::Lit(lit) = expr_kind &&
+ let LitKind::Str(value, _) = lit.node &&
+ value == symbol::kw::Empty
+ {
+ return true;
+ }
+
+ false
+}
+
+fn warn_then_suggest(cx: &LateContext<'_>, span: Span) {
+ span_lint_and_sugg(
+ cx,
+ MANUAL_STRING_NEW,
+ span,
+ "empty String is being created manually",
+ "consider using",
+ "String::new()".into(),
+ MachineApplicable,
+ );
+}
+
+/// Tries to parse an expression as a method call, emitting the warning if necessary.
+fn parse_method_call(cx: &LateContext<'_>, span: Span, path_segment: &PathSegment<'_>, args: &[Expr<'_>]) {
+ if args.is_empty() {
+ // When parsing TryFrom::try_from(...).expect(...), we will have more than 1 arg.
+ return;
+ }
+
+ let ident = path_segment.ident.as_str();
+ let method_arg_kind = &args[0].kind;
+ if ["to_string", "to_owned", "into"].contains(&ident) && is_expr_kind_empty_str(method_arg_kind) {
+ warn_then_suggest(cx, span);
+ } else if let ExprKind::Call(func, args) = method_arg_kind {
+ // If our first argument is a function call itself, it could be an `unwrap`-like function.
+ // E.g. String::try_from("hello").unwrap(), TryFrom::try_from("").expect("hello"), etc.
+ parse_call(cx, span, func, args);
+ }
+}
+
+/// Tries to parse an expression as a function call, emitting the warning if necessary.
+fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, args: &[Expr<'_>]) {
+ if args.len() != 1 {
+ return;
+ }
+
+ let arg_kind = &args[0].kind;
+ if let ExprKind::Path(qpath) = &func.kind {
+ if let QPath::TypeRelative(_, _) = qpath {
+ // String::from(...) or String::try_from(...)
+ if let QPath::TypeRelative(ty, path_seg) = qpath &&
+ [sym::from, sym::try_from].contains(&path_seg.ident.name) &&
+ let TyKind::Path(qpath) = &ty.kind &&
+ let QPath::Resolved(_, path) = qpath &&
+ let [path_seg] = path.segments &&
+ path_seg.ident.name == sym::String &&
+ is_expr_kind_empty_str(arg_kind)
+ {
+ warn_then_suggest(cx, span);
+ }
+ } else if let QPath::Resolved(_, path) = qpath {
+ // From::from(...) or TryFrom::try_from(...)
+ if let [path_seg1, path_seg2] = path.segments &&
+ is_expr_kind_empty_str(arg_kind) && (
+ (path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from) ||
+ (path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from)
+ )
+ {
+ warn_then_suggest(cx, span);
+ }
+ }
+ }
+}
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
-use clippy_utils::{is_trait_method, meets_msrv, msrvs, peel_blocks};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir as hir;
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::mir::Mutability;
-use rustc_middle::ty;
-use rustc_middle::ty::adjustment::Adjust;
-use rustc_semver::RustcVersion;
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::symbol::Ident;
-use rustc_span::{sym, Span};
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for usage of `map(|x| x.clone())` or
- /// dereferencing closures for `Copy` types, on `Iterator` or `Option`,
- /// and suggests `cloned()` or `copied()` instead
- ///
- /// ### Why is this bad?
- /// Readability, this can be written more concisely
- ///
- /// ### Example
- /// ```rust
- /// let x = vec![42, 43];
- /// let y = x.iter();
- /// let z = y.map(|i| *i);
- /// ```
- ///
- /// The correct use would be:
- ///
- /// ```rust
- /// let x = vec![42, 43];
- /// let y = x.iter();
- /// let z = y.cloned();
- /// ```
- #[clippy::version = "pre 1.29.0"]
- pub MAP_CLONE,
- style,
- "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
-}
-
-pub struct MapClone {
- msrv: Option<RustcVersion>,
-}
-
-impl_lint_pass!(MapClone => [MAP_CLONE]);
-
-impl MapClone {
- pub fn new(msrv: Option<RustcVersion>) -> Self {
- Self { msrv }
- }
-}
-
-impl<'tcx> LateLintPass<'tcx> for MapClone {
- fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
- if e.span.from_expansion() {
- return;
- }
-
- if_chain! {
- if let hir::ExprKind::MethodCall(method, args, _) = e.kind;
- if args.len() == 2;
- if method.ident.name == sym::map;
- let ty = cx.typeck_results().expr_ty(&args[0]);
- if is_type_diagnostic_item(cx, ty, sym::Option) || is_trait_method(cx, e, sym::Iterator);
- if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = args[1].kind;
- then {
- let closure_body = cx.tcx.hir().body(body);
- let closure_expr = peel_blocks(&closure_body.value);
- match closure_body.params[0].pat.kind {
- hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
- hir::BindingAnnotation::Unannotated, .., name, None
- ) = inner.kind {
- if ident_eq(name, closure_expr) {
- self.lint_explicit_closure(cx, e.span, args[0].span, true);
- }
- },
- hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => {
- match closure_expr.kind {
- hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
- if ident_eq(name, inner) {
- if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
- self.lint_explicit_closure(cx, e.span, args[0].span, true);
- }
- }
- },
- hir::ExprKind::MethodCall(method, [obj], _) => if_chain! {
- if ident_eq(name, obj) && method.ident.name == sym::clone;
- if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id);
- if let Some(trait_id) = cx.tcx.trait_of_item(fn_id);
- if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id);
- // no autoderefs
- if !cx.typeck_results().expr_adjustments(obj).iter()
- .any(|a| matches!(a.kind, Adjust::Deref(Some(..))));
- then {
- let obj_ty = cx.typeck_results().expr_ty(obj);
- if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
- if matches!(mutability, Mutability::Not) {
- let copy = is_copy(cx, *ty);
- self.lint_explicit_closure(cx, e.span, args[0].span, copy);
- }
- } else {
- lint_needless_cloning(cx, e.span, args[0].span);
- }
- }
- },
- _ => {},
- }
- },
- _ => {},
- }
- }
- }
- }
-
- extract_msrv_attr!(LateContext);
-}
-
-fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
- if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind {
- path.segments.len() == 1 && path.segments[0].ident == name
- } else {
- false
- }
-}
-
-fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
- span_lint_and_sugg(
- cx,
- MAP_CLONE,
- root.trim_start(receiver).unwrap(),
- "you are needlessly cloning iterator elements",
- "remove the `map` call",
- String::new(),
- Applicability::MachineApplicable,
- );
-}
-
-impl MapClone {
- fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) {
- let mut applicability = Applicability::MachineApplicable;
-
- let (message, sugg_method) = if is_copy && meets_msrv(self.msrv, msrvs::ITERATOR_COPIED) {
- ("you are using an explicit closure for copying elements", "copied")
- } else {
- ("you are using an explicit closure for cloning elements", "cloned")
- };
-
- span_lint_and_sugg(
- cx,
- MAP_CLONE,
- replace,
- message,
- &format!("consider calling the dedicated `{}` method", sugg_method),
- format!(
- "{}.{}()",
- snippet_with_applicability(cx, root, "..", &mut applicability),
- sugg_method,
- ),
- applicability,
- );
- }
-}
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_help;
-use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for instances of `map_err(|_| Some::Enum)`
- ///
- /// ### Why is this bad?
- /// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
- ///
- /// ### Example
- /// Before:
- /// ```rust
- /// use std::fmt;
- ///
- /// #[derive(Debug)]
- /// enum Error {
- /// Indivisible,
- /// Remainder(u8),
- /// }
- ///
- /// impl fmt::Display for Error {
- /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- /// match self {
- /// Error::Indivisible => write!(f, "could not divide input by three"),
- /// Error::Remainder(remainder) => write!(
- /// f,
- /// "input is not divisible by three, remainder = {}",
- /// remainder
- /// ),
- /// }
- /// }
- /// }
- ///
- /// impl std::error::Error for Error {}
- ///
- /// fn divisible_by_3(input: &str) -> Result<(), Error> {
- /// input
- /// .parse::<i32>()
- /// .map_err(|_| Error::Indivisible)
- /// .map(|v| v % 3)
- /// .and_then(|remainder| {
- /// if remainder == 0 {
- /// Ok(())
- /// } else {
- /// Err(Error::Remainder(remainder as u8))
- /// }
- /// })
- /// }
- /// ```
- ///
- /// After:
- /// ```rust
- /// use std::{fmt, num::ParseIntError};
- ///
- /// #[derive(Debug)]
- /// enum Error {
- /// Indivisible(ParseIntError),
- /// Remainder(u8),
- /// }
- ///
- /// impl fmt::Display for Error {
- /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- /// match self {
- /// Error::Indivisible(_) => write!(f, "could not divide input by three"),
- /// Error::Remainder(remainder) => write!(
- /// f,
- /// "input is not divisible by three, remainder = {}",
- /// remainder
- /// ),
- /// }
- /// }
- /// }
- ///
- /// impl std::error::Error for Error {
- /// fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- /// match self {
- /// Error::Indivisible(source) => Some(source),
- /// _ => None,
- /// }
- /// }
- /// }
- ///
- /// fn divisible_by_3(input: &str) -> Result<(), Error> {
- /// input
- /// .parse::<i32>()
- /// .map_err(Error::Indivisible)
- /// .map(|v| v % 3)
- /// .and_then(|remainder| {
- /// if remainder == 0 {
- /// Ok(())
- /// } else {
- /// Err(Error::Remainder(remainder as u8))
- /// }
- /// })
- /// }
- /// ```
- #[clippy::version = "1.48.0"]
- pub MAP_ERR_IGNORE,
- restriction,
- "`map_err` should not ignore the original error"
-}
-
-declare_lint_pass!(MapErrIgnore => [MAP_ERR_IGNORE]);
-
-impl<'tcx> LateLintPass<'tcx> for MapErrIgnore {
- // do not try to lint if this is from a macro or desugaring
- fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
- if e.span.from_expansion() {
- return;
- }
-
- // check if this is a method call (e.g. x.foo())
- if let ExprKind::MethodCall(method, [_, arg], _) = e.kind {
- // only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1]
- // Enum::Variant[2]))
- if method.ident.name == sym!(map_err) {
- // make sure the first argument is a closure, and grab the CaptureRef, BodyId, and fn_decl_span
- // fields
- if let ExprKind::Closure(&Closure {
- capture_clause,
- body,
- fn_decl_span,
- ..
- }) = arg.kind
- {
- // check if this is by Reference (meaning there's no move statement)
- if capture_clause == CaptureBy::Ref {
- // Get the closure body to check the parameters and values
- let closure_body = cx.tcx.hir().body(body);
- // make sure there's only one parameter (`|_|`)
- if closure_body.params.len() == 1 {
- // make sure that parameter is the wild token (`_`)
- if let PatKind::Wild = closure_body.params[0].pat.kind {
- // span the area of the closure capture and warn that the
- // original error will be thrown away
- span_lint_and_help(
- cx,
- MAP_ERR_IGNORE,
- fn_decl_span,
- "`map_err(|_|...` wildcard pattern discards the original error",
- None,
- "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
- );
- }
- }
- }
- }
- }
- }
- }
-}
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_wild;
use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::span_contains_comment;
use rustc_ast::{Attribute, LitKind};
use rustc_errors::Applicability;
use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat};
-use rustc_lint::LateContext;
+use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty;
use rustc_span::source_map::Spanned;
>,
{
if_chain! {
+ if !span_contains_comment(cx.sess().source_map(), expr.span);
if iter.len() >= 2;
if cx.typeck_results().expr_ty(expr).is_bool();
if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();
};
use rustc_errors::Applicability;
use rustc_hir::LangItem::OptionNone;
-use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Node, Pat, PatKind, Path, QPath};
+use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Guard, Node, Pat, PatKind, Path, QPath};
use rustc_lint::LateContext;
use rustc_span::sym;
use rustc_typeck::hir_ty_to_ty;
fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
for arm in arms {
let arm_expr = peel_blocks_with_stmt(arm.body);
+
+ if let Some(guard_expr) = &arm.guard {
+ match guard_expr {
+ // gives up if `pat if expr` can have side effects
+ Guard::If(if_cond) => {
+ if if_cond.can_have_side_effects() {
+ return false;
+ }
+ },
+ // gives up `pat if let ...` arm
+ Guard::IfLet(_) => {
+ return false;
+ },
+ };
+ }
+
if let PatKind::Wild = arm.pat.kind {
- return eq_expr_value(cx, match_expr, strip_return(arm_expr));
+ if !eq_expr_value(cx, match_expr, strip_return(arm_expr)) {
+ return false;
+ }
} else if !pat_same_as_expr(arm.pat, arm_expr) {
return false;
}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::match_type;
+use clippy_utils::visitors::is_local_used;
+use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, UintTy};
+use rustc_span::sym;
+
+use super::NAIVE_BYTECOUNT;
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ filter_recv: &'tcx Expr<'_>,
+ filter_arg: &'tcx Expr<'_>,
+) {
+ if_chain! {
+ if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind;
+ let body = cx.tcx.hir().body(body);
+ if let [param] = body.params;
+ if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
+ if let ExprKind::Binary(ref op, l, r) = body.value.kind;
+ if op.node == BinOpKind::Eq;
+ if match_type(cx,
+ cx.typeck_results().expr_ty(filter_recv).peel_refs(),
+ &paths::SLICE_ITER);
+ let operand_is_arg = |expr| {
+ let expr = peel_ref_operators(cx, peel_blocks(expr));
+ path_to_local_id(expr, arg_id)
+ };
+ let needle = if operand_is_arg(l) {
+ r
+ } else if operand_is_arg(r) {
+ l
+ } else {
+ return;
+ };
+ if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
+ if !is_local_used(cx, needle, arg_id);
+ then {
+ let haystack = if let ExprKind::MethodCall(path, args, _) =
+ filter_recv.kind {
+ let p = path.ident.name;
+ if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
+ &args[0]
+ } else {
+ filter_recv
+ }
+ } else {
+ filter_recv
+ };
+ let mut applicability = Applicability::MaybeIncorrect;
+ span_lint_and_sugg(
+ cx,
+ NAIVE_BYTECOUNT,
+ expr.span,
+ "you appear to be counting bytes the naive way",
+ "consider using the bytecount crate",
+ format!("bytecount::count({}, {})",
+ snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
+ snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
+ applicability,
+ );
+ }
+ };
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::BYTES_COUNT_TO_LEN;
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx hir::Expr<'_>,
+ count_recv: &'tcx hir::Expr<'_>,
+ bytes_recv: &'tcx hir::Expr<'_>,
+) {
+ if_chain! {
+ if let Some(bytes_id) = cx.typeck_results().type_dependent_def_id(count_recv.hir_id);
+ if let Some(impl_id) = cx.tcx.impl_of_method(bytes_id);
+ if cx.tcx.type_of(impl_id).is_str();
+ let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs();
+ if ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String);
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ BYTES_COUNT_TO_LEN,
+ expr.span,
+ "using long and hard to read `.bytes().count()`",
+ "consider calling `.len()` instead",
+ format!("{}.len()", snippet_with_applicability(cx, bytes_recv.span, "..", &mut applicability)),
+ applicability
+ );
+ }
+ };
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_ast::ast::LitKind;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::{source_map::Spanned, symbol::sym, Span};
+
+use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS;
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ call_span: Span,
+ recv: &'tcx Expr<'_>,
+ arg: &'tcx Expr<'_>,
+) {
+ if_chain! {
+ if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+ if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+ if cx.tcx.type_of(impl_id).is_str();
+ if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = arg.kind;
+ if (2..=6).contains(&ext_literal.as_str().len());
+ let ext_str = ext_literal.as_str();
+ if ext_str.starts_with('.');
+ if ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
+ || ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
+ let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs();
+ if recv_ty.is_str() || is_type_diagnostic_item(cx, recv_ty, sym::String);
+ then {
+ span_lint_and_help(
+ cx,
+ CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
+ call_span,
+ "case-sensitive file extension comparison",
+ None,
+ "consider using a case-insensitive comparison instead",
+ );
+ }
+ }
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::visitors::for_each_expr;
+use clippy_utils::{eq_expr_value, get_parent_expr};
+use core::ops::ControlFlow;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use std::collections::VecDeque;
+
+use super::method_call;
+use super::COLLAPSIBLE_STR_REPLACE;
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx hir::Expr<'tcx>,
+ from: &'tcx hir::Expr<'tcx>,
+ to: &'tcx hir::Expr<'tcx>,
+) {
+ let replace_methods = collect_replace_calls(cx, expr, to);
+ if replace_methods.methods.len() > 1 {
+ let from_kind = cx.typeck_results().expr_ty(from).peel_refs().kind();
+ // If the parent node's `to` argument is the same as the `to` argument
+ // of the last replace call in the current chain, don't lint as it was already linted
+ if let Some(parent) = get_parent_expr(cx, expr)
+ && let Some(("replace", [_, current_from, current_to], _)) = method_call(parent)
+ && eq_expr_value(cx, to, current_to)
+ && from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind()
+ {
+ return;
+ }
+
+ check_consecutive_replace_calls(cx, expr, &replace_methods, to);
+ }
+}
+
+struct ReplaceMethods<'tcx> {
+ methods: VecDeque<&'tcx hir::Expr<'tcx>>,
+ from_args: VecDeque<&'tcx hir::Expr<'tcx>>,
+}
+
+fn collect_replace_calls<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx hir::Expr<'tcx>,
+ to_arg: &'tcx hir::Expr<'tcx>,
+) -> ReplaceMethods<'tcx> {
+ let mut methods = VecDeque::new();
+ let mut from_args = VecDeque::new();
+
+ let _: Option<()> = for_each_expr(expr, |e| {
+ if let Some(("replace", [_, from, to], _)) = method_call(e) {
+ if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() {
+ methods.push_front(e);
+ from_args.push_front(from);
+ ControlFlow::Continue(())
+ } else {
+ ControlFlow::BREAK
+ }
+ } else {
+ ControlFlow::Continue(())
+ }
+ });
+
+ ReplaceMethods { methods, from_args }
+}
+
+/// Check a chain of `str::replace` calls for `collapsible_str_replace` lint.
+fn check_consecutive_replace_calls<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx hir::Expr<'tcx>,
+ replace_methods: &ReplaceMethods<'tcx>,
+ to_arg: &'tcx hir::Expr<'tcx>,
+) {
+ let from_args = &replace_methods.from_args;
+ let from_arg_reprs: Vec<String> = from_args
+ .iter()
+ .map(|from_arg| snippet(cx, from_arg.span, "..").to_string())
+ .collect();
+ let app = Applicability::MachineApplicable;
+ let earliest_replace_call = replace_methods.methods.front().unwrap();
+ if let Some((_, [..], span_lo)) = method_call(earliest_replace_call) {
+ span_lint_and_sugg(
+ cx,
+ COLLAPSIBLE_STR_REPLACE,
+ expr.span.with_lo(span_lo.lo()),
+ "used consecutive `str::replace` call",
+ "replace with",
+ format!(
+ "replace([{}], {})",
+ from_arg_reprs.join(", "),
+ snippet(cx, to_arg.span, ".."),
+ ),
+ app,
+ );
+ }
+}
use super::EXPECT_USED;
-/// lint use of `expect()` for `Option`s and `Result`s
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_expect_in_tests: bool) {
+/// lint use of `expect()` or `expect_err` for `Result` and `expect()` for `Option`.
+pub(super) fn check(
+ cx: &LateContext<'_>,
+ expr: &hir::Expr<'_>,
+ recv: &hir::Expr<'_>,
+ is_err: bool,
+ allow_expect_in_tests: bool,
+) {
let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
- let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) {
+ let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
Some((EXPECT_USED, "an Option", "None", ""))
} else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
- Some((EXPECT_USED, "a Result", "Err", "an "))
+ Some((EXPECT_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an "))
} else {
None
};
+ let method = if is_err { "expect_err" } else { "expect" };
+
if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) {
return;
}
cx,
lint,
expr.span,
- &format!("used `expect()` on `{kind}` value"),
+ &format!("used `{method}()` on `{kind}` value"),
None,
&format!("if this value is {none_prefix}`{none_value}`, it will panic"),
);
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_slice_of_primitives;
+use clippy_utils::source::snippet_with_applicability;
+use if_chain::if_chain;
+use rustc_ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_span::source_map::Spanned;
+
+use super::GET_FIRST;
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx hir::Expr<'_>,
+ recv: &'tcx hir::Expr<'_>,
+ arg: &'tcx hir::Expr<'_>,
+) {
+ if_chain! {
+ if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+ if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+ if cx.tcx.type_of(impl_id).is_slice();
+ if let Some(_) = is_slice_of_primitives(cx, recv);
+ if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = arg.kind;
+ then {
+ let mut app = Applicability::MachineApplicable;
+ let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app);
+ span_lint_and_sugg(
+ cx,
+ GET_FIRST,
+ expr.span,
+ &format!("accessing first element with `{0}.get(0)`", slice_name),
+ "try",
+ format!("{}.first()", slice_name),
+ app,
+ );
+ }
+ }
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::{get_expr_use_or_unification_node, is_lang_ctor, is_no_std_crate};
+
+use rustc_errors::Applicability;
+use rustc_hir::LangItem::{OptionNone, OptionSome};
+use rustc_hir::{Expr, ExprKind, Node};
+use rustc_lint::LateContext;
+
+use super::{ITER_ON_EMPTY_COLLECTIONS, ITER_ON_SINGLE_ITEMS};
+
+enum IterType {
+ Iter,
+ IterMut,
+ IntoIter,
+}
+
+impl IterType {
+ fn ref_prefix(&self) -> &'static str {
+ match self {
+ Self::Iter => "&",
+ Self::IterMut => "&mut ",
+ Self::IntoIter => "",
+ }
+ }
+}
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) {
+ let item = match &recv.kind {
+ ExprKind::Array(v) if v.len() <= 1 => v.first(),
+ ExprKind::Path(p) => {
+ if is_lang_ctor(cx, p, OptionNone) {
+ None
+ } else {
+ return;
+ }
+ },
+ ExprKind::Call(f, some_args) if some_args.len() == 1 => {
+ if let ExprKind::Path(p) = &f.kind {
+ if is_lang_ctor(cx, p, OptionSome) {
+ Some(&some_args[0])
+ } else {
+ return;
+ }
+ } else {
+ return;
+ }
+ },
+ _ => return,
+ };
+ let iter_type = match method_name {
+ "iter" => IterType::Iter,
+ "iter_mut" => IterType::IterMut,
+ "into_iter" => IterType::IntoIter,
+ _ => return,
+ };
+
+ let is_unified = match get_expr_use_or_unification_node(cx.tcx, expr) {
+ Some((Node::Expr(parent), child_id)) => match parent.kind {
+ ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id == child_id => false,
+ ExprKind::If(_, _, _)
+ | ExprKind::Match(_, _, _)
+ | ExprKind::Closure(_)
+ | ExprKind::Ret(_)
+ | ExprKind::Break(_, _) => true,
+ _ => false,
+ },
+ Some((Node::Stmt(_) | Node::Local(_), _)) => false,
+ _ => true,
+ };
+
+ if is_unified {
+ return;
+ }
+
+ if let Some(i) = item {
+ let sugg = format!(
+ "{}::iter::once({}{})",
+ if is_no_std_crate(cx) { "core" } else { "std" },
+ iter_type.ref_prefix(),
+ snippet(cx, i.span, "...")
+ );
+ span_lint_and_sugg(
+ cx,
+ ITER_ON_SINGLE_ITEMS,
+ expr.span,
+ &format!("`{method_name}` call on a collection with only one item"),
+ "try",
+ sugg,
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ span_lint_and_sugg(
+ cx,
+ ITER_ON_EMPTY_COLLECTIONS,
+ expr.span,
+ &format!("`{method_name}` call on an empty collection"),
+ "try",
+ if is_no_std_crate(cx) {
+ "core::iter::empty()".to_string()
+ } else {
+ "std::iter::empty()".to_string()
+ },
+ Applicability::MaybeIncorrect,
+ );
+ }
+}
--- /dev/null
+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::{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::{Closure, Expr, ExprKind, PatKind};
+use rustc_lint::LateContext;
+use rustc_span::symbol::sym;
+
+use super::MANUAL_OK_OR;
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'tcx>,
+ recv: &'tcx Expr<'_>,
+ or_expr: &'tcx Expr<'_>,
+ map_expr: &'tcx Expr<'_>,
+) {
+ if_chain! {
+ if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+ if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+ if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Option);
+ if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, [err_arg]) = or_expr.kind;
+ if is_lang_ctor(cx, err_path, ResultErr);
+ if is_ok_wrapping(cx, map_expr);
+ if let Some(recv_snippet) = snippet_opt(cx, recv.span);
+ if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
+ if let Some(indent) = indent_of(cx, expr.span);
+ then {
+ let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
+ span_lint_and_sugg(
+ cx,
+ MANUAL_OK_OR,
+ expr.span,
+ "this pattern reimplements `Option::ok_or`",
+ "replace with",
+ format!(
+ "{}.ok_or({})",
+ recv_snippet,
+ reindented_err_arg_snippet
+ ),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+}
+
+fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
+ if let ExprKind::Path(ref qpath) = map_expr.kind {
+ if is_lang_ctor(cx, qpath, ResultOk) {
+ return true;
+ }
+ }
+ if_chain! {
+ if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind;
+ let body = cx.tcx.hir().body(body);
+ 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 is_lang_ctor(cx, ok_path, ResultOk);
+ then { path_to_local_id(ok_arg, param_id) } else { false }
+ }
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
+use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_middle::mir::Mutability;
+use rustc_middle::ty;
+use rustc_middle::ty::adjustment::Adjust;
+use rustc_semver::RustcVersion;
+use rustc_span::symbol::Ident;
+use rustc_span::{sym, Span};
+
+use super::MAP_CLONE;
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'_>,
+ e: &hir::Expr<'_>,
+ recv: &hir::Expr<'_>,
+ arg: &'tcx hir::Expr<'_>,
+ msrv: Option<RustcVersion>,
+) {
+ if_chain! {
+ if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id);
+ if cx.tcx.impl_of_method(method_id)
+ .map_or(false, |id| is_type_diagnostic_item(cx, cx.tcx.type_of(id), sym::Option))
+ || is_diag_trait_item(cx, method_id, sym::Iterator);
+ if let hir::ExprKind::Closure(&hir::Closure{ body, .. }) = arg.kind;
+ then {
+ let closure_body = cx.tcx.hir().body(body);
+ let closure_expr = peel_blocks(&closure_body.value);
+ match closure_body.params[0].pat.kind {
+ hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
+ hir::BindingAnnotation::Unannotated, .., name, None
+ ) = inner.kind {
+ if ident_eq(name, closure_expr) {
+ lint_explicit_closure(cx, e.span, recv.span, true, msrv);
+ }
+ },
+ hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => {
+ match closure_expr.kind {
+ hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
+ if ident_eq(name, inner) {
+ if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
+ lint_explicit_closure(cx, e.span, recv.span, true, msrv);
+ }
+ }
+ },
+ hir::ExprKind::MethodCall(method, [obj], _) => if_chain! {
+ if ident_eq(name, obj) && method.ident.name == sym::clone;
+ if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id);
+ if let Some(trait_id) = cx.tcx.trait_of_item(fn_id);
+ if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id);
+ // no autoderefs
+ if !cx.typeck_results().expr_adjustments(obj).iter()
+ .any(|a| matches!(a.kind, Adjust::Deref(Some(..))));
+ then {
+ let obj_ty = cx.typeck_results().expr_ty(obj);
+ if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
+ if matches!(mutability, Mutability::Not) {
+ let copy = is_copy(cx, *ty);
+ lint_explicit_closure(cx, e.span, recv.span, copy, msrv);
+ }
+ } else {
+ lint_needless_cloning(cx, e.span, recv.span);
+ }
+ }
+ },
+ _ => {},
+ }
+ },
+ _ => {},
+ }
+ }
+ }
+}
+
+fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
+ if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind {
+ path.segments.len() == 1 && path.segments[0].ident == name
+ } else {
+ false
+ }
+}
+
+fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
+ span_lint_and_sugg(
+ cx,
+ MAP_CLONE,
+ root.trim_start(receiver).unwrap(),
+ "you are needlessly cloning iterator elements",
+ "remove the `map` call",
+ String::new(),
+ Applicability::MachineApplicable,
+ );
+}
+
+fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: Option<RustcVersion>) {
+ let mut applicability = Applicability::MachineApplicable;
+
+ let (message, sugg_method) = if is_copy && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
+ ("you are using an explicit closure for copying elements", "copied")
+ } else {
+ ("you are using an explicit closure for cloning elements", "cloned")
+ };
+
+ span_lint_and_sugg(
+ cx,
+ MAP_CLONE,
+ replace,
+ message,
+ &format!("consider calling the dedicated `{}` method", sugg_method),
+ format!(
+ "{}.{}()",
+ snippet_with_applicability(cx, root, "..", &mut applicability),
+ sugg_method,
+ ),
+ applicability,
+ );
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_type_diagnostic_item;
+use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind};
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::MAP_ERR_IGNORE;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'_>, e: &Expr<'_>, arg: &'tcx Expr<'_>) {
+ if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
+ && let Some(impl_id) = cx.tcx.impl_of_method(method_id)
+ && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Result)
+ && let ExprKind::Closure(&Closure {
+ capture_clause: CaptureBy::Ref,
+ body,
+ fn_decl_span,
+ ..
+ }) = arg.kind
+ && let closure_body = cx.tcx.hir().body(body)
+ && let [param] = closure_body.params
+ && let PatKind::Wild = param.pat.kind
+ {
+ // span the area of the closure capture and warn that the
+ // original error will be thrown away
+ span_lint_and_help(
+ cx,
+ MAP_ERR_IGNORE,
+ fn_decl_span,
+ "`map_err(|_|...` wildcard pattern discards the original error",
+ None,
+ "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
+ );
+ }
+}
mod bind_instead_of_map;
+mod bytecount;
+mod bytes_count_to_len;
mod bytes_nth;
+mod case_sensitive_file_extension_comparisons;
mod chars_cmp;
mod chars_cmp_with_unwrap;
mod chars_last_cmp;
mod clone_on_copy;
mod clone_on_ref_ptr;
mod cloned_instead_of_copied;
+mod collapsible_str_replace;
mod err_expect;
mod expect_fun_call;
mod expect_used;
mod flat_map_identity;
mod flat_map_option;
mod from_iter_instead_of_collect;
+mod get_first;
mod get_last_with_len;
mod get_unwrap;
mod implicit_clone;
mod iter_next_slice;
mod iter_nth;
mod iter_nth_zero;
+mod iter_on_single_or_empty_collections;
mod iter_overeager_cloned;
mod iter_skip_next;
mod iter_with_drain;
mod iterator_step_by_zero;
+mod manual_ok_or;
mod manual_saturating_arithmetic;
mod manual_str_repeat;
+mod map_clone;
mod map_collect_result_unit;
+mod map_err_ignore;
mod map_flatten;
mod map_identity;
mod map_unwrap_or;
+mod mut_mutex_lock;
mod needless_option_as_deref;
mod needless_option_take;
mod no_effect_replace;
mod obfuscated_if_else;
mod ok_expect;
+mod open_options;
mod option_as_ref_deref;
mod option_map_or_none;
mod option_map_unwrap_or;
mod or_fun_call;
mod or_then_unwrap;
+mod path_buf_push_overwrite;
+mod range_zip_with_len;
+mod repeat_once;
mod search_is_some;
mod single_char_add_str;
mod single_char_insert_string;
mod single_char_pattern;
mod single_char_push_string;
mod skip_while_next;
+mod stable_sort_primitive;
mod str_splitn;
mod string_extend_chars;
mod suspicious_map;
mod suspicious_splitn;
+mod suspicious_to_owned;
mod uninit_assumed_init;
+mod unit_hash;
mod unnecessary_filter_map;
mod unnecessary_fold;
mod unnecessary_iter_cloned;
mod unnecessary_join;
mod unnecessary_lazy_eval;
+mod unnecessary_sort_by;
mod unnecessary_to_owned;
mod unwrap_or_else_default;
mod unwrap_used;
mod useless_asref;
mod utils;
+mod vec_resize_to_zero;
+mod verbose_file_reads;
mod wrong_self_convention;
mod zst_offset;
use bind_instead_of_map::BindInsteadOfMap;
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
-use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
-use clippy_utils::{contains_return, get_trait_def_id, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
+use clippy_utils::ty::{contains_adt_constructor, implements_trait, is_copy, is_type_diagnostic_item};
+use clippy_utils::{
+ contains_return, get_trait_def_id, is_trait_method, iter_input_pats, meets_msrv, msrvs, paths, return_ty,
+};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_hir::def::Res;
"used `cloned` where `copied` could be used instead"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for consecutive calls to `str::replace` (2 or more)
+ /// that can be collapsed into a single call.
+ ///
+ /// ### Why is this bad?
+ /// Consecutive `str::replace` calls scan the string multiple times
+ /// with repetitive code.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let hello = "hesuo worpd"
+ /// .replace('s', "l")
+ /// .replace("u", "l")
+ /// .replace('p', "l");
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let hello = "hesuo worpd".replace(&['s', 'u', 'p'], "l");
+ /// ```
+ #[clippy::version = "1.64.0"]
+ pub COLLAPSIBLE_STR_REPLACE,
+ perf,
+ "collapse consecutive calls to str::replace (2 or more) into a single call"
+}
+
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `_.cloned().<func>()` where call to `.cloned()` can be postponed.
declare_clippy_lint! {
/// ### What it does
- /// Checks for `.unwrap()` calls on `Option`s and on `Result`s.
+ /// Checks for `.unwrap()` or `.unwrap_err()` calls on `Result`s and `.unwrap()` call on `Option`s.
///
/// ### Why is this bad?
/// It is better to handle the `None` or `Err` case,
declare_clippy_lint! {
/// ### What it does
- /// Checks for `.expect()` calls on `Option`s and `Result`s.
+ /// Checks for `.expect()` or `.expect_err()` calls on `Result`s and `.expect()` call on `Option`s.
///
/// ### Why is this bad?
/// Usually it is better to handle the `None` or `Err` case.
"replace `.iter().count()` with `.len()`"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the usage of `_.to_owned()`, on a `Cow<'_, _>`.
+ ///
+ /// ### Why is this bad?
+ /// Calling `to_owned()` on a `Cow` creates a clone of the `Cow`
+ /// itself, without taking ownership of the `Cow` contents (i.e.
+ /// it's equivalent to calling `Cow::clone`).
+ /// The similarly named `into_owned` method, on the other hand,
+ /// clones the `Cow` contents, effectively turning any `Cow::Borrowed`
+ /// into a `Cow::Owned`.
+ ///
+ /// Given the potential ambiguity, consider replacing `to_owned`
+ /// with `clone` for better readability or, if getting a `Cow::Owned`
+ /// was the original intent, using `into_owned` instead.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # use std::borrow::Cow;
+ /// let s = "Hello world!";
+ /// let cow = Cow::Borrowed(s);
+ ///
+ /// let data = cow.to_owned();
+ /// assert!(matches!(data, Cow::Borrowed(_)))
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # use std::borrow::Cow;
+ /// let s = "Hello world!";
+ /// let cow = Cow::Borrowed(s);
+ ///
+ /// let data = cow.clone();
+ /// assert!(matches!(data, Cow::Borrowed(_)))
+ /// ```
+ /// or
+ /// ```rust
+ /// # use std::borrow::Cow;
+ /// let s = "Hello world!";
+ /// let cow = Cow::Borrowed(s);
+ ///
+ /// let data = cow.into_owned();
+ /// assert!(matches!(data, String))
+ /// ```
+ #[clippy::version = "1.65.0"]
+ pub SUSPICIOUS_TO_OWNED,
+ suspicious,
+ "calls to `to_owned` on a `Cow<'_, _>` might not do what they are expected"
+}
+
declare_clippy_lint! {
/// ### What it does
/// Checks for calls to [`splitn`]
/// "1234".replace("12", "12");
/// "1234".replacen("12", "12", 1);
/// ```
- #[clippy::version = "1.62.0"]
+ #[clippy::version = "1.63.0"]
pub NO_EFFECT_REPLACE,
suspicious,
"replace with no effect"
more clearly with `if .. else ..`"
}
+declare_clippy_lint! {
+ /// ### What it does
+ ///
+ /// Checks for calls to `iter`, `iter_mut` or `into_iter` on collections containing a single item
+ ///
+ /// ### Why is this bad?
+ ///
+ /// It is simpler to use the once function from the standard library:
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// let a = [123].iter();
+ /// let b = Some(123).into_iter();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// use std::iter;
+ /// let a = iter::once(&123);
+ /// let b = iter::once(123);
+ /// ```
+ ///
+ /// ### Known problems
+ ///
+ /// The type of the resulting iterator might become incompatible with its usage
+ #[clippy::version = "1.64.0"]
+ pub ITER_ON_SINGLE_ITEMS,
+ nursery,
+ "Iterator for array of length 1"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ ///
+ /// Checks for calls to `iter`, `iter_mut` or `into_iter` on empty collections
+ ///
+ /// ### Why is this bad?
+ ///
+ /// It is simpler to use the empty function from the standard library:
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// use std::{slice, option};
+ /// let a: slice::Iter<i32> = [].iter();
+ /// let f: option::IntoIter<i32> = None.into_iter();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// use std::iter;
+ /// let a: iter::Empty<i32> = iter::empty();
+ /// let b: iter::Empty<i32> = iter::empty();
+ /// ```
+ ///
+ /// ### Known problems
+ ///
+ /// The type of the resulting iterator might become incompatible with its usage
+ #[clippy::version = "1.64.0"]
+ pub ITER_ON_EMPTY_COLLECTIONS,
+ nursery,
+ "Iterator for empty array"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for naive byte counts
+ ///
+ /// ### Why is this bad?
+ /// The [`bytecount`](https://crates.io/crates/bytecount)
+ /// crate has methods to count your bytes faster, especially for large slices.
+ ///
+ /// ### Known problems
+ /// If you have predominantly small slices, the
+ /// `bytecount::count(..)` method may actually be slower. However, if you can
+ /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
+ /// faster in those cases.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let vec = vec![1_u8];
+ /// let count = vec.iter().filter(|x| **x == 0u8).count();
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust,ignore
+ /// # let vec = vec![1_u8];
+ /// let count = bytecount::count(&vec, 0u8);
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub NAIVE_BYTECOUNT,
+ pedantic,
+ "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// It checks for `str::bytes().count()` and suggests replacing it with
+ /// `str::len()`.
+ ///
+ /// ### Why is this bad?
+ /// `str::bytes().count()` is longer and may not be as performant as using
+ /// `str::len()`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// "hello".bytes().count();
+ /// String::from("hello").bytes().count();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// "hello".len();
+ /// String::from("hello").len();
+ /// ```
+ #[clippy::version = "1.62.0"]
+ pub BYTES_COUNT_TO_LEN,
+ complexity,
+ "Using `bytes().count()` when `len()` performs the same functionality"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for calls to `ends_with` with possible file extensions
+ /// and suggests to use a case-insensitive approach instead.
+ ///
+ /// ### Why is this bad?
+ /// `ends_with` is case-sensitive and may not detect files with a valid extension.
+ ///
+ /// ### Example
+ /// ```rust
+ /// fn is_rust_file(filename: &str) -> bool {
+ /// filename.ends_with(".rs")
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// fn is_rust_file(filename: &str) -> bool {
+ /// let filename = std::path::Path::new(filename);
+ /// filename.extension()
+ /// .map_or(false, |ext| ext.eq_ignore_ascii_case("rs"))
+ /// }
+ /// ```
+ #[clippy::version = "1.51.0"]
+ pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
+ pedantic,
+ "Checks for calls to ends_with with case-sensitive file extensions"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for using `x.get(0)` instead of
+ /// `x.first()`.
+ ///
+ /// ### Why is this bad?
+ /// Using `x.first()` is easier to read and has the same
+ /// result.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x = vec![2, 3, 5];
+ /// let first_element = x.get(0);
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// let x = vec![2, 3, 5];
+ /// let first_element = x.first();
+ /// ```
+ #[clippy::version = "1.63.0"]
+ pub GET_FIRST,
+ style,
+ "Using `x.get(0)` when `x.first()` is simpler"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ ///
+ /// Finds patterns that reimplement `Option::ok_or`.
+ ///
+ /// ### Why is this bad?
+ ///
+ /// Concise code helps focusing on behavior instead of boilerplate.
+ ///
+ /// ### Examples
+ /// ```rust
+ /// let foo: Option<i32> = None;
+ /// foo.map_or(Err("error"), |v| Ok(v));
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// let foo: Option<i32> = None;
+ /// foo.ok_or("error");
+ /// ```
+ #[clippy::version = "1.49.0"]
+ pub MANUAL_OK_OR,
+ pedantic,
+ "finds patterns that can be encoded more concisely with `Option::ok_or`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `map(|x| x.clone())` or
+ /// dereferencing closures for `Copy` types, on `Iterator` or `Option`,
+ /// and suggests `cloned()` or `copied()` instead
+ ///
+ /// ### Why is this bad?
+ /// Readability, this can be written more concisely
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x = vec![42, 43];
+ /// let y = x.iter();
+ /// let z = y.map(|i| *i);
+ /// ```
+ ///
+ /// The correct use would be:
+ ///
+ /// ```rust
+ /// let x = vec![42, 43];
+ /// let y = x.iter();
+ /// let z = y.cloned();
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub MAP_CLONE,
+ style,
+ "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for instances of `map_err(|_| Some::Enum)`
+ ///
+ /// ### Why is this bad?
+ /// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
+ ///
+ /// ### Example
+ /// Before:
+ /// ```rust
+ /// use std::fmt;
+ ///
+ /// #[derive(Debug)]
+ /// enum Error {
+ /// Indivisible,
+ /// Remainder(u8),
+ /// }
+ ///
+ /// impl fmt::Display for Error {
+ /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ /// match self {
+ /// Error::Indivisible => write!(f, "could not divide input by three"),
+ /// Error::Remainder(remainder) => write!(
+ /// f,
+ /// "input is not divisible by three, remainder = {}",
+ /// remainder
+ /// ),
+ /// }
+ /// }
+ /// }
+ ///
+ /// impl std::error::Error for Error {}
+ ///
+ /// fn divisible_by_3(input: &str) -> Result<(), Error> {
+ /// input
+ /// .parse::<i32>()
+ /// .map_err(|_| Error::Indivisible)
+ /// .map(|v| v % 3)
+ /// .and_then(|remainder| {
+ /// if remainder == 0 {
+ /// Ok(())
+ /// } else {
+ /// Err(Error::Remainder(remainder as u8))
+ /// }
+ /// })
+ /// }
+ /// ```
+ ///
+ /// After:
+ /// ```rust
+ /// use std::{fmt, num::ParseIntError};
+ ///
+ /// #[derive(Debug)]
+ /// enum Error {
+ /// Indivisible(ParseIntError),
+ /// Remainder(u8),
+ /// }
+ ///
+ /// impl fmt::Display for Error {
+ /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ /// match self {
+ /// Error::Indivisible(_) => write!(f, "could not divide input by three"),
+ /// Error::Remainder(remainder) => write!(
+ /// f,
+ /// "input is not divisible by three, remainder = {}",
+ /// remainder
+ /// ),
+ /// }
+ /// }
+ /// }
+ ///
+ /// impl std::error::Error for Error {
+ /// fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ /// match self {
+ /// Error::Indivisible(source) => Some(source),
+ /// _ => None,
+ /// }
+ /// }
+ /// }
+ ///
+ /// fn divisible_by_3(input: &str) -> Result<(), Error> {
+ /// input
+ /// .parse::<i32>()
+ /// .map_err(Error::Indivisible)
+ /// .map(|v| v % 3)
+ /// .and_then(|remainder| {
+ /// if remainder == 0 {
+ /// Ok(())
+ /// } else {
+ /// Err(Error::Remainder(remainder as u8))
+ /// }
+ /// })
+ /// }
+ /// ```
+ #[clippy::version = "1.48.0"]
+ pub MAP_ERR_IGNORE,
+ restriction,
+ "`map_err` should not ignore the original error"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `&mut Mutex::lock` calls
+ ///
+ /// ### Why is this bad?
+ /// `Mutex::lock` is less efficient than
+ /// calling `Mutex::get_mut`. In addition you also have a statically
+ /// guarantee that the mutex isn't locked, instead of just a runtime
+ /// guarantee.
+ ///
+ /// ### Example
+ /// ```rust
+ /// use std::sync::{Arc, Mutex};
+ ///
+ /// let mut value_rc = Arc::new(Mutex::new(42_u8));
+ /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
+ ///
+ /// let mut value = value_mutex.lock().unwrap();
+ /// *value += 1;
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// use std::sync::{Arc, Mutex};
+ ///
+ /// let mut value_rc = Arc::new(Mutex::new(42_u8));
+ /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
+ ///
+ /// let value = value_mutex.get_mut().unwrap();
+ /// *value += 1;
+ /// ```
+ #[clippy::version = "1.49.0"]
+ pub MUT_MUTEX_LOCK,
+ style,
+ "`&mut Mutex::lock` does unnecessary locking"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for duplicate open options as well as combinations
+ /// that make no sense.
+ ///
+ /// ### Why is this bad?
+ /// In the best case, the code will be harder to read than
+ /// necessary. I don't know the worst case.
+ ///
+ /// ### Example
+ /// ```rust
+ /// use std::fs::OpenOptions;
+ ///
+ /// OpenOptions::new().read(true).truncate(true);
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub NONSENSICAL_OPEN_OPTIONS,
+ correctness,
+ "nonsensical combination of options for opening a file"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ ///* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)
+ /// calls on `PathBuf` that can cause overwrites.
+ ///
+ /// ### Why is this bad?
+ /// Calling `push` with a root path at the start can overwrite the
+ /// previous defined path.
+ ///
+ /// ### Example
+ /// ```rust
+ /// use std::path::PathBuf;
+ ///
+ /// let mut x = PathBuf::from("/foo");
+ /// x.push("/bar");
+ /// assert_eq!(x, PathBuf::from("/bar"));
+ /// ```
+ /// Could be written:
+ ///
+ /// ```rust
+ /// use std::path::PathBuf;
+ ///
+ /// let mut x = PathBuf::from("/foo");
+ /// x.push("bar");
+ /// assert_eq!(x, PathBuf::from("/foo/bar"));
+ /// ```
+ #[clippy::version = "1.36.0"]
+ pub PATH_BUF_PUSH_OVERWRITE,
+ nursery,
+ "calling `push` with file system root on `PathBuf` can overwrite it"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for zipping a collection with the range of
+ /// `0.._.len()`.
+ ///
+ /// ### Why is this bad?
+ /// The code is better expressed with `.enumerate()`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let x = vec![1];
+ /// let _ = x.iter().zip(0..x.len());
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # let x = vec![1];
+ /// let _ = x.iter().enumerate();
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub RANGE_ZIP_WITH_LEN,
+ complexity,
+ "zipping iterator with a range when `enumerate()` would do"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `.repeat(1)` and suggest the following method for each types.
+ /// - `.to_string()` for `str`
+ /// - `.clone()` for `String`
+ /// - `.to_vec()` for `slice`
+ ///
+ /// The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if
+ /// they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306))
+ ///
+ /// ### Why is this bad?
+ /// For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning
+ /// the string is the intention behind this, `clone()` should be used.
+ ///
+ /// ### Example
+ /// ```rust
+ /// fn main() {
+ /// let x = String::from("hello world").repeat(1);
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// fn main() {
+ /// let x = String::from("hello world").clone();
+ /// }
+ /// ```
+ #[clippy::version = "1.47.0"]
+ pub REPEAT_ONCE,
+ complexity,
+ "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// When sorting primitive values (integers, bools, chars, as well
+ /// as arrays, slices, and tuples of such items), it is typically better to
+ /// use an unstable sort than a stable sort.
+ ///
+ /// ### Why is this bad?
+ /// Typically, using a stable sort consumes more memory and cpu cycles.
+ /// Because values which compare equal are identical, preserving their
+ /// relative order (the guarantee that a stable sort provides) means
+ /// nothing, while the extra costs still apply.
+ ///
+ /// ### Known problems
+ ///
+ /// As pointed out in
+ /// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241),
+ /// a stable sort can instead be significantly faster for certain scenarios
+ /// (eg. when a sorted vector is extended with new data and resorted).
+ ///
+ /// For more information and benchmarking results, please refer to the
+ /// issue linked above.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let mut vec = vec![2, 1, 3];
+ /// vec.sort();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let mut vec = vec![2, 1, 3];
+ /// vec.sort_unstable();
+ /// ```
+ #[clippy::version = "1.47.0"]
+ pub STABLE_SORT_PRIMITIVE,
+ pedantic,
+ "use of sort() when sort_unstable() is equivalent"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Detects `().hash(_)`.
+ ///
+ /// ### Why is this bad?
+ /// Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # use std::hash::Hash;
+ /// # use std::collections::hash_map::DefaultHasher;
+ /// # enum Foo { Empty, WithValue(u8) }
+ /// # use Foo::*;
+ /// # let mut state = DefaultHasher::new();
+ /// # let my_enum = Foo::Empty;
+ /// match my_enum {
+ /// Empty => ().hash(&mut state),
+ /// WithValue(x) => x.hash(&mut state),
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # use std::hash::Hash;
+ /// # use std::collections::hash_map::DefaultHasher;
+ /// # enum Foo { Empty, WithValue(u8) }
+ /// # use Foo::*;
+ /// # let mut state = DefaultHasher::new();
+ /// # let my_enum = Foo::Empty;
+ /// match my_enum {
+ /// Empty => 0_u8.hash(&mut state),
+ /// WithValue(x) => x.hash(&mut state),
+ /// }
+ /// ```
+ #[clippy::version = "1.58.0"]
+ pub UNIT_HASH,
+ correctness,
+ "hashing a unit value, which does nothing"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Detects uses of `Vec::sort_by` passing in a closure
+ /// which compares the two arguments, either directly or indirectly.
+ ///
+ /// ### Why is this bad?
+ /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
+ /// possible) than to use `Vec::sort_by` and a more complicated
+ /// closure.
+ ///
+ /// ### Known problems
+ /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
+ /// imported by a use statement, then it will need to be added manually.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # struct A;
+ /// # impl A { fn foo(&self) {} }
+ /// # let mut vec: Vec<A> = Vec::new();
+ /// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # struct A;
+ /// # impl A { fn foo(&self) {} }
+ /// # let mut vec: Vec<A> = Vec::new();
+ /// vec.sort_by_key(|a| a.foo());
+ /// ```
+ #[clippy::version = "1.46.0"]
+ pub UNNECESSARY_SORT_BY,
+ complexity,
+ "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Finds occurrences of `Vec::resize(0, an_int)`
+ ///
+ /// ### Why is this bad?
+ /// This is probably an argument inversion mistake.
+ ///
+ /// ### Example
+ /// ```rust
+ /// vec!(1, 2, 3, 4, 5).resize(0, 5)
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// vec!(1, 2, 3, 4, 5).clear()
+ /// ```
+ #[clippy::version = "1.46.0"]
+ pub VEC_RESIZE_TO_ZERO,
+ correctness,
+ "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for use of File::read_to_end and File::read_to_string.
+ ///
+ /// ### Why is this bad?
+ /// `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.
+ /// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
+ ///
+ /// ### Example
+ /// ```rust,no_run
+ /// # use std::io::Read;
+ /// # use std::fs::File;
+ /// let mut f = File::open("foo.txt").unwrap();
+ /// let mut bytes = Vec::new();
+ /// f.read_to_end(&mut bytes).unwrap();
+ /// ```
+ /// Can be written more concisely as
+ /// ```rust,no_run
+ /// # use std::fs;
+ /// let mut bytes = fs::read("foo.txt").unwrap();
+ /// ```
+ #[clippy::version = "1.44.0"]
+ pub VERBOSE_FILE_READS,
+ restriction,
+ "use of `File::read_to_end` or `File::read_to_string`"
+}
+
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Option<RustcVersion>,
CLONE_ON_COPY,
CLONE_ON_REF_PTR,
CLONE_DOUBLE_REF,
+ COLLAPSIBLE_STR_REPLACE,
ITER_OVEREAGER_CLONED,
CLONED_INSTEAD_OF_COPIED,
FLAT_MAP_OPTION,
FROM_ITER_INSTEAD_OF_COLLECT,
INSPECT_FOR_EACH,
IMPLICIT_CLONE,
+ SUSPICIOUS_TO_OWNED,
SUSPICIOUS_SPLITN,
MANUAL_STR_REPEAT,
EXTEND_WITH_DRAIN,
NEEDLESS_OPTION_TAKE,
NO_EFFECT_REPLACE,
OBFUSCATED_IF_ELSE,
+ ITER_ON_SINGLE_ITEMS,
+ ITER_ON_EMPTY_COLLECTIONS,
+ NAIVE_BYTECOUNT,
+ BYTES_COUNT_TO_LEN,
+ CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
+ GET_FIRST,
+ MANUAL_OK_OR,
+ MAP_CLONE,
+ MAP_ERR_IGNORE,
+ MUT_MUTEX_LOCK,
+ NONSENSICAL_OPEN_OPTIONS,
+ PATH_BUF_PUSH_OVERWRITE,
+ RANGE_ZIP_WITH_LEN,
+ REPEAT_ONCE,
+ STABLE_SORT_PRIMITIVE,
+ UNIT_HASH,
+ UNNECESSARY_SORT_BY,
+ VEC_RESIZE_TO_ZERO,
+ VERBOSE_FILE_READS,
]);
/// Extracts a method call name, args, and `Span` of the method name.
if contains_adt_constructor(ret_ty, self_adt) {
return;
}
- } else if contains_ty(ret_ty, self_ty) {
+ } else if ret_ty.contains(self_ty) {
return;
}
if contains_adt_constructor(assoc_ty, self_adt) {
return;
}
- } else if contains_ty(assoc_ty, self_ty) {
+ } else if assoc_ty.contains(self_ty) {
return;
}
}
if let TraitItemKind::Fn(_, _) = item.kind;
let ret_ty = return_ty(cx, item.hir_id());
let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
- if !contains_ty(ret_ty, self_ty);
+ if !ret_ty.contains(self_ty);
then {
span_lint(
},
_ => {},
},
- ("count", []) => match method_call(recv) {
+ ("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) {
Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false),
Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
iter_count::check(cx, expr, recv2, name2);
},
Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
+ Some(("filter", [recv2, arg], _)) => bytecount::check(cx, expr, recv2, arg),
+ Some(("bytes", [recv2], _)) => bytes_count_to_len::check(cx, expr, recv, recv2),
_ => {},
},
("drain", [arg]) => {
iter_with_drain::check(cx, expr, recv, span, arg);
},
+ ("ends_with", [arg]) => {
+ if let ExprKind::MethodCall(_, _, span) = expr.kind {
+ case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg);
+ }
+ },
("expect", [_]) => match method_call(recv) {
Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
- _ => expect_used::check(cx, expr, recv, self.allow_expect_in_tests),
+ _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
},
+ ("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests),
("extend", [arg]) => {
string_extend_chars::check(cx, expr, recv, arg);
extend_with_drain::check(cx, expr, recv, arg);
inspect_for_each::check(cx, expr, span2);
}
},
- ("get", [arg]) => get_last_with_len::check(cx, expr, recv, arg),
+ ("get", [arg]) => {
+ get_first::check(cx, expr, recv, arg);
+ get_last_with_len::check(cx, expr, recv, arg);
+ },
("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
+ ("hash", [arg]) => {
+ unit_hash::check(cx, expr, recv, arg);
+ },
("is_file", []) => filetype_is_file::check(cx, expr, recv),
("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv),
("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
+ ("iter" | "iter_mut" | "into_iter", []) => {
+ iter_on_single_or_empty_collections::check(cx, expr, name, recv);
+ },
("join", [join_arg]) => {
if let Some(("collect", _, span)) = method_call(recv) {
unnecessary_join::check(cx, expr, recv, join_arg, span);
}
}
},
+ ("lock", []) => {
+ mut_mutex_lock::check(cx, expr, recv, span);
+ },
(name @ ("map" | "map_err"), [m_arg]) => {
+ if name == "map" {
+ map_clone::check(cx, expr, recv, m_arg, self.msrv);
+ } else {
+ map_err_ignore::check(cx, expr, m_arg);
+ }
if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) {
match (name, args) {
("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv),
}
map_identity::check(cx, expr, recv, m_arg, name, span);
},
- ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
+ ("map_or", [def, map]) => {
+ option_map_or_none::check(cx, expr, recv, def, map);
+ manual_ok_or::check(cx, expr, recv, def, map);
+ },
("next", []) => {
if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) {
match (name2, args2) {
_ => iter_nth_zero::check(cx, expr, recv, n_arg),
},
("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
+ ("open", [_]) => {
+ open_options::check(cx, expr, recv);
+ },
("or_else", [arg]) => {
if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) {
unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
}
},
+ ("push", [arg]) => {
+ path_buf_push_overwrite::check(cx, expr, arg);
+ },
+ ("read_to_end", [_]) => {
+ verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_END_MSG);
+ },
+ ("read_to_string", [_]) => {
+ verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_STRING_MSG);
+ },
+ ("repeat", [arg]) => {
+ repeat_once::check(cx, expr, recv, arg);
+ },
+ (name @ ("replace" | "replacen"), [arg1, arg2] | [arg1, arg2, _]) => {
+ no_effect_replace::check(cx, expr, arg1, arg2);
+
+ // Check for repeated `str::replace` calls to perform `collapsible_str_replace` lint
+ if name == "replace" && let Some(("replace", ..)) = method_call(recv) {
+ collapsible_str_replace::check(cx, expr, arg1, arg2);
+ }
+ },
+ ("resize", [count_arg, default_arg]) => {
+ vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span);
+ },
+ ("sort", []) => {
+ stable_sort_primitive::check(cx, expr, recv);
+ },
+ ("sort_by", [arg]) => {
+ unnecessary_sort_by::check(cx, expr, recv, arg, false);
+ },
+ ("sort_unstable_by", [arg]) => {
+ unnecessary_sort_by::check(cx, expr, recv, arg, true);
+ },
("splitn" | "rsplitn", [count_arg, pat_arg]) => {
if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
suspicious_splitn::check(cx, name, expr, recv, count);
}
unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some");
},
- ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
+ ("to_owned", []) => {
+ if !suspicious_to_owned::check(cx, expr, recv) {
+ implicit_clone::check(cx, name, expr, recv);
+ }
+ },
+ ("to_os_string" | "to_path_buf" | "to_vec", []) => {
implicit_clone::check(cx, name, expr, recv);
},
("unwrap", []) => {
},
_ => {},
}
- unwrap_used::check(cx, expr, recv, self.allow_unwrap_in_tests);
+ unwrap_used::check(cx, expr, recv, false, self.allow_unwrap_in_tests);
},
+ ("unwrap_err", []) => unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests),
("unwrap_or", [u_arg]) => match method_call(recv) {
Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
},
},
- ("replace" | "replacen", [arg1, arg2] | [arg1, arg2, _]) => {
- no_effect_replace::check(cx, expr, arg1, arg2);
+ ("zip", [arg]) => {
+ if let ExprKind::MethodCall(name, [iter_recv], _) = recv.kind
+ && name.ident.name == sym::iter
+ {
+ range_zip_with_len::check(cx, expr, iter_recv, arg);
+ }
},
_ => {},
}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, Mutability};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::{sym, Span};
+
+use super::MUT_MUTEX_LOCK;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) {
+ if_chain! {
+ if let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind();
+ if let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id);
+ if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+ if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Mutex);
+ then {
+ span_lint_and_sugg(
+ cx,
+ MUT_MUTEX_LOCK,
+ name_span,
+ "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference",
+ "change this to",
+ "get_mut".to_owned(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::paths;
+use clippy_utils::ty::match_type;
+use rustc_ast::ast::LitKind;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::source_map::{Span, Spanned};
+
+use super::NONSENSICAL_OPEN_OPTIONS;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
+ if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
+ && let Some(impl_id) = cx.tcx.impl_of_method(method_id)
+ && match_type(cx, cx.tcx.type_of(impl_id), &paths::OPEN_OPTIONS)
+ {
+ let mut options = Vec::new();
+ get_open_options(cx, recv, &mut options);
+ check_open_options(cx, &options, e.span);
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+enum Argument {
+ True,
+ False,
+ Unknown,
+}
+
+#[derive(Debug)]
+enum OpenOption {
+ Write,
+ Read,
+ Truncate,
+ Create,
+ Append,
+}
+
+fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) {
+ if let ExprKind::MethodCall(path, arguments, _) = argument.kind {
+ let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs();
+
+ // Only proceed if this is a call on some object of type std::fs::OpenOptions
+ if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 {
+ let argument_option = match arguments[1].kind {
+ ExprKind::Lit(ref span) => {
+ if let Spanned {
+ node: LitKind::Bool(lit),
+ ..
+ } = *span
+ {
+ if lit { Argument::True } else { Argument::False }
+ } else {
+ // The function is called with a literal which is not a boolean literal.
+ // This is theoretically possible, but not very likely.
+ return;
+ }
+ },
+ _ => Argument::Unknown,
+ };
+
+ match path.ident.as_str() {
+ "create" => {
+ options.push((OpenOption::Create, argument_option));
+ },
+ "append" => {
+ options.push((OpenOption::Append, argument_option));
+ },
+ "truncate" => {
+ options.push((OpenOption::Truncate, argument_option));
+ },
+ "read" => {
+ options.push((OpenOption::Read, argument_option));
+ },
+ "write" => {
+ options.push((OpenOption::Write, argument_option));
+ },
+ _ => (),
+ }
+
+ get_open_options(cx, &arguments[0], options);
+ }
+ }
+}
+
+fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], span: Span) {
+ let (mut create, mut append, mut truncate, mut read, mut write) = (false, false, false, false, false);
+ let (mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) =
+ (false, false, false, false, false);
+ // This code is almost duplicated (oh, the irony), but I haven't found a way to
+ // unify it.
+
+ for option in options {
+ match *option {
+ (OpenOption::Create, arg) => {
+ if create {
+ span_lint(
+ cx,
+ NONSENSICAL_OPEN_OPTIONS,
+ span,
+ "the method `create` is called more than once",
+ );
+ } else {
+ create = true;
+ }
+ create_arg = create_arg || (arg == Argument::True);
+ },
+ (OpenOption::Append, arg) => {
+ if append {
+ span_lint(
+ cx,
+ NONSENSICAL_OPEN_OPTIONS,
+ span,
+ "the method `append` is called more than once",
+ );
+ } else {
+ append = true;
+ }
+ append_arg = append_arg || (arg == Argument::True);
+ },
+ (OpenOption::Truncate, arg) => {
+ if truncate {
+ span_lint(
+ cx,
+ NONSENSICAL_OPEN_OPTIONS,
+ span,
+ "the method `truncate` is called more than once",
+ );
+ } else {
+ truncate = true;
+ }
+ truncate_arg = truncate_arg || (arg == Argument::True);
+ },
+ (OpenOption::Read, arg) => {
+ if read {
+ span_lint(
+ cx,
+ NONSENSICAL_OPEN_OPTIONS,
+ span,
+ "the method `read` is called more than once",
+ );
+ } else {
+ read = true;
+ }
+ read_arg = read_arg || (arg == Argument::True);
+ },
+ (OpenOption::Write, arg) => {
+ if write {
+ span_lint(
+ cx,
+ NONSENSICAL_OPEN_OPTIONS,
+ span,
+ "the method `write` is called more than once",
+ );
+ } else {
+ write = true;
+ }
+ write_arg = write_arg || (arg == Argument::True);
+ },
+ }
+ }
+
+ if read && truncate && read_arg && truncate_arg && !(write && write_arg) {
+ span_lint(
+ cx,
+ NONSENSICAL_OPEN_OPTIONS,
+ span,
+ "file opened with `truncate` and `read`",
+ );
+ }
+ if append && truncate && append_arg && truncate_arg {
+ span_lint(
+ cx,
+ NONSENSICAL_OPEN_OPTIONS,
+ span,
+ "file opened with `append` and `truncate`",
+ );
+ }
+}
map_span,
String::from(if unwrap_snippet_none { "and_then" } else { "map_or" }),
),
- (expr.span.with_lo(unwrap_recv.span.hi()), String::from("")),
+ (expr.span.with_lo(unwrap_recv.span.hi()), String::new()),
];
if !unwrap_snippet_none {
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::symbol::sym;
+use std::path::{Component, Path};
+
+use super::PATH_BUF_PUSH_OVERWRITE;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
+ if_chain! {
+ if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+ if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+ if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::PathBuf);
+ if let ExprKind::Lit(ref lit) = arg.kind;
+ if let LitKind::Str(ref path_lit, _) = lit.node;
+ if let pushed_path = Path::new(path_lit.as_str());
+ if let Some(pushed_path_lit) = pushed_path.to_str();
+ if pushed_path.has_root();
+ if let Some(root) = pushed_path.components().next();
+ if root == Component::RootDir;
+ then {
+ span_lint_and_sugg(
+ cx,
+ PATH_BUF_PUSH_OVERWRITE,
+ lit.span,
+ "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
+ "try",
+ format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::source::snippet;
+use clippy_utils::{higher, SpanlessEq};
+use clippy_utils::{is_integer_const, is_trait_method};
+use if_chain::if_chain;
+use rustc_hir::{Expr, ExprKind, QPath};
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::RANGE_ZIP_WITH_LEN;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, zip_arg: &'tcx Expr<'_>) {
+ if_chain! {
+ if is_trait_method(cx, expr, sym::Iterator);
+ // range expression in `.zip()` call: `0..x.len()`
+ if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg);
+ if is_integer_const(cx, start, 0);
+ // `.len()` call
+ if let ExprKind::MethodCall(len_path, [len_recv], _) = end.kind;
+ if len_path.ident.name == sym::len;
+ // `.iter()` and `.len()` called on same `Path`
+ if let ExprKind::Path(QPath::Resolved(_, iter_path)) = recv.kind;
+ if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind;
+ if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
+ then {
+ span_lint(cx,
+ RANGE_ZIP_WITH_LEN,
+ expr.span,
+ &format!("it is more idiomatic to use `{}.iter().enumerate()`",
+ snippet(cx, recv.span, "_"))
+ );
+ }
+ }
+}
--- /dev/null
+use clippy_utils::consts::{constant_context, Constant};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::REPEAT_ONCE;
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ recv: &'tcx Expr<'_>,
+ repeat_arg: &'tcx Expr<'_>,
+) {
+ if constant_context(cx, cx.typeck_results()).expr(repeat_arg) == Some(Constant::Int(1)) {
+ let ty = cx.typeck_results().expr_ty(recv).peel_refs();
+ if ty.is_str() {
+ span_lint_and_sugg(
+ cx,
+ REPEAT_ONCE,
+ expr.span,
+ "calling `repeat(1)` on str",
+ "consider using `.to_string()` instead",
+ format!("{}.to_string()", snippet(cx, recv.span, r#""...""#)),
+ Applicability::MachineApplicable,
+ );
+ } else if ty.builtin_index().is_some() {
+ span_lint_and_sugg(
+ cx,
+ REPEAT_ONCE,
+ expr.span,
+ "calling `repeat(1)` on slice",
+ "consider using `.to_vec()` instead",
+ format!("{}.to_vec()", snippet(cx, recv.span, r#""...""#)),
+ Applicability::MachineApplicable,
+ );
+ } else if is_type_diagnostic_item(cx, ty, sym::String) {
+ span_lint_and_sugg(
+ cx,
+ REPEAT_ONCE,
+ expr.span,
+ "calling `repeat(1)` on a string literal",
+ "consider using `.clone()` instead",
+ format!("{}.clone()", snippet(cx, recv.span, r#""...""#)),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::is_slice_of_primitives;
+use clippy_utils::source::snippet_with_context;
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+
+use super::STABLE_SORT_PRIMITIVE;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
+ if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
+ && let Some(impl_id) = cx.tcx.impl_of_method(method_id)
+ && cx.tcx.type_of(impl_id).is_slice()
+ && let Some(slice_type) = is_slice_of_primitives(cx, recv)
+ {
+ span_lint_and_then(
+ cx,
+ STABLE_SORT_PRIMITIVE,
+ e.span,
+ &format!("used `sort` on primitive type `{}`", slice_type),
+ |diag| {
+ let mut app = Applicability::MachineApplicable;
+ let recv_snip = snippet_with_context(cx, recv.span, e.span.ctxt(), "..", &mut app).0;
+ diag.span_suggestion(e.span, "try", format!("{}.sort_unstable()", recv_snip), app);
+ diag.note(
+ "an unstable sort typically performs faster without any observable difference for this data type",
+ );
+ },
+ );
+ }
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_diag_trait_item;
+use clippy_utils::source::snippet_with_context;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::sym;
+
+use super::SUSPICIOUS_TO_OWNED;
+
+pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) -> bool {
+ if_chain! {
+ if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+ if is_diag_trait_item(cx, method_def_id, sym::ToOwned);
+ let input_type = cx.typeck_results().expr_ty(expr);
+ if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind();
+ if cx.tcx.is_diagnostic_item(sym::Cow, adt.did());
+ then {
+ let mut app = Applicability::MaybeIncorrect;
+ let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0;
+ span_lint_and_sugg(
+ cx,
+ SUSPICIOUS_TO_OWNED,
+ expr.span,
+ &format!("this `to_owned` call clones the {0} itself and does not cause the {0} contents to become owned", input_type),
+ "consider using, depending on intent",
+ format!("{0}.clone()` or `{0}.into_owned()", recv_snip),
+ app,
+ );
+ return true;
+ }
+ }
+ false
+}
use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{is_expr_diagnostic_item, ty::is_uninit_value_valid_for_ty};
+use clippy_utils::{is_path_diagnostic_item, ty::is_uninit_value_valid_for_ty};
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 is_expr_diagnostic_item(cx, callee, sym::maybe_uninit_uninit);
+ if is_path_diagnostic_item(cx, callee, sym::maybe_uninit_uninit);
if !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr));
then {
span_lint(
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::is_trait_method;
+use clippy_utils::source::snippet;
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::UNIT_HASH;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
+ if is_trait_method(cx, expr, sym::Hash) && cx.typeck_results().expr_ty(recv).is_unit() {
+ span_lint_and_then(
+ cx,
+ UNIT_HASH,
+ expr.span,
+ "this call to `hash` on the unit type will do nothing",
+ |diag| {
+ diag.span_suggestion(
+ expr.span,
+ "remove the call to `hash` or consider using",
+ format!("0_u8.hash({})", snippet(cx, arg.span, ".."),),
+ Applicability::MaybeIncorrect,
+ );
+ diag.note("the implementation of `Hash` for `()` is a no-op");
+ },
+ );
+ }
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::implements_trait;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, subst::GenericArgKind};
+use rustc_span::sym;
+use rustc_span::symbol::Ident;
+use std::iter;
+
+use super::UNNECESSARY_SORT_BY;
+
+enum LintTrigger {
+ Sort(SortDetection),
+ SortByKey(SortByKeyDetection),
+}
+
+struct SortDetection {
+ vec_name: String,
+}
+
+struct SortByKeyDetection {
+ vec_name: String,
+ closure_arg: String,
+ closure_body: String,
+ reverse: bool,
+}
+
+/// Detect if the two expressions are mirrored (identical, except one
+/// contains a and the other replaces it with b)
+fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident: &Ident) -> bool {
+ match (&a_expr.kind, &b_expr.kind) {
+ // Two boxes with mirrored contents
+ (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => {
+ mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
+ },
+ // Two arrays with mirrored contents
+ (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => {
+ iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
+ },
+ // The two exprs are function calls.
+ // Check to see that the function itself and its arguments are mirrored
+ (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => {
+ mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
+ && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
+ },
+ // The two exprs are method calls.
+ // Check to see that the function is the same and the arguments are mirrored
+ // This is enough because the receiver of the method is listed in the arguments
+ (ExprKind::MethodCall(left_segment, left_args, _), ExprKind::MethodCall(right_segment, right_args, _)) => {
+ left_segment.ident == right_segment.ident
+ && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
+ },
+ // Two tuples with mirrored contents
+ (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => {
+ iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
+ },
+ // Two binary ops, which are the same operation and which have mirrored arguments
+ (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => {
+ left_op.node == right_op.node
+ && mirrored_exprs(left_left, a_ident, right_left, b_ident)
+ && mirrored_exprs(left_right, a_ident, right_right, b_ident)
+ },
+ // Two unary ops, which are the same operation and which have the same argument
+ (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => {
+ left_op == right_op && mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
+ },
+ // The two exprs are literals of some kind
+ (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node,
+ (ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(left, a_ident, right, b_ident),
+ (ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => {
+ mirrored_exprs(left_block, a_ident, right_block, b_ident)
+ },
+ (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => {
+ left_ident.name == right_ident.name && mirrored_exprs(left_expr, a_ident, right_expr, right_ident)
+ },
+ // Two paths: either one is a and the other is b, or they're identical to each other
+ (
+ ExprKind::Path(QPath::Resolved(
+ _,
+ Path {
+ segments: left_segments,
+ ..
+ },
+ )),
+ ExprKind::Path(QPath::Resolved(
+ _,
+ Path {
+ segments: right_segments,
+ ..
+ },
+ )),
+ ) => {
+ (iter::zip(*left_segments, *right_segments).all(|(left, right)| left.ident == right.ident)
+ && left_segments
+ .iter()
+ .all(|seg| &seg.ident != a_ident && &seg.ident != b_ident))
+ || (left_segments.len() == 1
+ && &left_segments[0].ident == a_ident
+ && right_segments.len() == 1
+ && &right_segments[0].ident == b_ident)
+ },
+ // Matching expressions, but one or both is borrowed
+ (
+ ExprKind::AddrOf(left_kind, Mutability::Not, left_expr),
+ ExprKind::AddrOf(right_kind, Mutability::Not, right_expr),
+ ) => left_kind == right_kind && mirrored_exprs(left_expr, a_ident, right_expr, b_ident),
+ (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => mirrored_exprs(a_expr, a_ident, right_expr, b_ident),
+ (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(left_expr, a_ident, b_expr, b_ident),
+ _ => false,
+ }
+}
+
+fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) -> Option<LintTrigger> {
+ if_chain! {
+ if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+ if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+ if cx.tcx.type_of(impl_id).is_slice();
+ if let ExprKind::Closure(&Closure { body, .. }) = arg.kind;
+ if let closure_body = cx.tcx.hir().body(body);
+ if let &[
+ Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},
+ Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. }
+ ] = &closure_body.params;
+ if let ExprKind::MethodCall(method_path, [left_expr, right_expr], _) = closure_body.value.kind;
+ if method_path.ident.name == sym::cmp;
+ if is_trait_method(cx, &closure_body.value, sym::Ord);
+ then {
+ let (closure_body, closure_arg, reverse) = if mirrored_exprs(
+ left_expr,
+ left_ident,
+ right_expr,
+ right_ident
+ ) {
+ (Sugg::hir(cx, left_expr, "..").to_string(), left_ident.name.to_string(), false)
+ } else if mirrored_exprs(left_expr, right_ident, right_expr, left_ident) {
+ (Sugg::hir(cx, left_expr, "..").to_string(), right_ident.name.to_string(), true)
+ } else {
+ return None;
+ };
+ let vec_name = Sugg::hir(cx, recv, "..").to_string();
+
+ if_chain! {
+ if let ExprKind::Path(QPath::Resolved(_, Path {
+ segments: [PathSegment { ident: left_name, .. }], ..
+ })) = &left_expr.kind;
+ if left_name == left_ident;
+ if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| {
+ implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[])
+ });
+ then {
+ return Some(LintTrigger::Sort(SortDetection { vec_name }));
+ }
+ }
+
+ if !expr_borrows(cx, left_expr) {
+ return Some(LintTrigger::SortByKey(SortByKeyDetection {
+ vec_name,
+ closure_arg,
+ closure_body,
+ reverse,
+ }));
+ }
+ }
+ }
+
+ None
+}
+
+fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ let ty = cx.typeck_results().expr_ty(expr);
+ matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
+}
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ recv: &'tcx Expr<'_>,
+ arg: &'tcx Expr<'_>,
+ is_unstable: bool,
+) {
+ match detect_lint(cx, expr, recv, arg) {
+ Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg(
+ cx,
+ UNNECESSARY_SORT_BY,
+ expr.span,
+ "use Vec::sort_by_key here instead",
+ "try",
+ format!(
+ "{}.sort{}_by_key(|{}| {})",
+ trigger.vec_name,
+ if is_unstable { "_unstable" } else { "" },
+ trigger.closure_arg,
+ if trigger.reverse {
+ format!("std::cmp::Reverse({})", trigger.closure_body)
+ } else {
+ trigger.closure_body.to_string()
+ },
+ ),
+ if trigger.reverse {
+ Applicability::MaybeIncorrect
+ } else {
+ Applicability::MachineApplicable
+ },
+ ),
+ Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg(
+ cx,
+ UNNECESSARY_SORT_BY,
+ expr.span,
+ "use Vec::sort here instead",
+ "try",
+ format!(
+ "{}.sort{}()",
+ trigger.vec_name,
+ if is_unstable { "_unstable" } else { "" },
+ ),
+ Applicability::MachineApplicable,
+ ),
+ None => {},
+ }
+}
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{
- contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs,
+ get_associated_type, get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, peel_mid_ty_refs,
};
-use clippy_utils::{meets_msrv, msrvs};
-
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
+use clippy_utils::{meets_msrv, msrvs};
use rustc_errors::Applicability;
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
use rustc_lint::LateContext;
&trait_predicate.trait_ref.substs.iter().skip(1).collect::<Vec<_>>()[..],
call_substs,
);
- implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
+ // if `expr` is a `String` and generic target is [u8], skip
+ // (https://github.com/rust-lang/rust-clippy/issues/9317).
+ if let [subst] = composed_substs[..]
+ && let GenericArgKind::Type(arg_ty) = subst.unpack()
+ && arg_ty.is_slice()
+ && let inner_ty = arg_ty.builtin_index().unwrap()
+ && let ty::Uint(ty::UintTy::U8) = inner_ty.kind()
+ && let self_ty = cx.typeck_results().expr_ty(expr).peel_refs()
+ && is_type_diagnostic_item(cx, self_ty, sym::String) {
+ false
+ } else {
+ implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
+ }
} else {
false
};
// (https://github.com/rust-lang/rust-clippy/issues/8507).
if (n_refs == 0 && !receiver_ty.is_ref())
|| trait_predicate.def_id() != as_ref_trait_id
- || !contains_ty(fn_sig.output(), input);
+ || !fn_sig.output().contains(input);
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
then {
span_lint_and_sugg(
) -> (Vec<TraitPredicate<'tcx>>, Vec<ProjectionPredicate<'tcx>>) {
let mut trait_predicates = Vec::new();
let mut projection_predicates = Vec::new();
- for (predicate, _) in cx.tcx.predicates_of(callee_def_id).predicates.iter() {
- // `substs` should have 1 + n elements. The first is the type on the left hand side of an
- // `as`. The remaining n are trait parameters.
- let is_input_substs = |substs: SubstsRef<'tcx>| {
- if_chain! {
- if let Some(arg) = substs.iter().next();
- if let GenericArgKind::Type(arg_ty) = arg.unpack();
- if arg_ty == input;
- then { true } else { false }
- }
- };
+ for predicate in cx.tcx.param_env(callee_def_id).caller_bounds() {
match predicate.kind().skip_binder() {
PredicateKind::Trait(trait_predicate) => {
- if is_input_substs(trait_predicate.trait_ref.substs) {
+ if trait_predicate.trait_ref.self_ty() == input {
trait_predicates.push(trait_predicate);
}
},
PredicateKind::Projection(projection_predicate) => {
- if is_input_substs(projection_predicate.projection_ty.substs) {
+ if projection_predicate.projection_ty.self_ty() == input {
projection_predicates.push(projection_predicate);
}
},
use super::{EXPECT_USED, UNWRAP_USED};
-/// lint use of `unwrap()` for `Option`s and `Result`s
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_unwrap_in_tests: bool) {
+/// lint use of `unwrap()` or `unwrap_err` for `Result` and `unwrap()` for `Option`.
+pub(super) fn check(
+ cx: &LateContext<'_>,
+ expr: &hir::Expr<'_>,
+ recv: &hir::Expr<'_>,
+ is_err: bool,
+ allow_unwrap_in_tests: bool,
+) {
let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
- let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) {
+ let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
Some((UNWRAP_USED, "an Option", "None", ""))
} else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
- Some((UNWRAP_USED, "a Result", "Err", "an "))
+ Some((UNWRAP_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an "))
} else {
None
};
+ let method_suffix = if is_err { "_err" } else { "" };
+
if allow_unwrap_in_tests && is_in_test_function(cx.tcx, expr.hir_id) {
return;
}
let help = if is_lint_allowed(cx, EXPECT_USED, expr.hir_id) {
format!(
"if you don't want to handle the `{none_value}` case gracefully, consider \
- using `expect()` to provide a better panic message"
+ using `expect{method_suffix}()` to provide a better panic message"
)
} else {
format!("if this value is {none_prefix}`{none_value}`, it will panic")
cx,
lint,
expr.span,
- &format!("used `unwrap()` on `{kind}` value"),
+ &format!("used `unwrap{method_suffix}()` on `{kind}` value"),
None,
&help,
);
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::source_map::Spanned;
+use rustc_span::{sym, Span};
+
+use super::VEC_RESIZE_TO_ZERO;
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ count_arg: &'tcx Expr<'_>,
+ default_arg: &'tcx Expr<'_>,
+ name_span: Span,
+) {
+ if_chain! {
+ if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+ if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+ if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Vec);
+ if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = count_arg.kind;
+ if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = default_arg.kind;
+ then {
+ let method_call_span = expr.span.with_lo(name_span.lo());
+ span_lint_and_then(
+ cx,
+ VEC_RESIZE_TO_ZERO,
+ expr.span,
+ "emptying a vector with `resize`",
+ |db| {
+ db.help("the arguments may be inverted...");
+ db.span_suggestion(
+ method_call_span,
+ "...or you can empty the vector with",
+ "clear()".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ },
+ );
+ }
+ }
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_trait_method;
+use clippy_utils::ty::is_type_diagnostic_item;
+use rustc_hir::{Expr, ExprKind, QPath};
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::VERBOSE_FILE_READS;
+
+pub(super) const READ_TO_END_MSG: (&str, &str) = ("use of `File::read_to_end`", "consider using `fs::read` instead");
+pub(super) const READ_TO_STRING_MSG: (&str, &str) = (
+ "use of `File::read_to_string`",
+ "consider using `fs::read_to_string` instead",
+);
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ recv: &'tcx Expr<'_>,
+ (msg, help): (&str, &str),
+) {
+ if is_trait_method(cx, expr, sym::IoRead)
+ && matches!(recv.kind, ExprKind::Path(QPath::Resolved(None, _)))
+ && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(recv).peel_refs(), sym::File)
+ {
+ span_lint_and_help(cx, VERBOSE_FILE_READS, expr.span, msg, None, help);
+ }
+}
"these patterns are unneeded as the `..` pattern can match those elements"
},
if only_one { "remove it" } else { "remove them" },
- "".to_string(),
+ String::new(),
Applicability::MachineApplicable,
);
}
/// }
/// impl<A, B> Foo<A, B> {}
/// ```
- #[clippy::version = "1.62.0"]
+ #[clippy::version = "1.63.0"]
pub MISMATCHING_TYPE_PARAM_ORDER,
pedantic,
"type parameter positioned inconsistently between type def and impl block"
--- /dev/null
+use clippy_utils::diagnostics::span_lint;
+use rustc_ast::ast::{Expr, ExprKind, Stmt, StmtKind};
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for nested assignments.
+ ///
+ /// ### Why is this bad?
+ /// While this is in most cases already a type mismatch,
+ /// the result of an assignment being `()` can throw off people coming from languages like python or C,
+ /// where such assignments return a copy of the assigned value.
+ ///
+ /// ### Example
+ /// ```rust
+ ///# let (a, b);
+ /// a = b = 42;
+ /// ```
+ /// Use instead:
+ /// ```rust
+ ///# let (a, b);
+ /// b = 42;
+ /// a = b;
+ /// ```
+ #[clippy::version = "1.65.0"]
+ pub MULTI_ASSIGNMENTS,
+ suspicious,
+ "instead of using `a = b = c;` use `a = c; b = c;`"
+}
+
+declare_lint_pass!(MultiAssignments => [MULTI_ASSIGNMENTS]);
+
+fn strip_paren_blocks(expr: &Expr) -> &Expr {
+ match &expr.kind {
+ ExprKind::Paren(e) => strip_paren_blocks(e),
+ ExprKind::Block(b, _) => {
+ if let [
+ Stmt {
+ kind: StmtKind::Expr(e),
+ ..
+ },
+ ] = &b.stmts[..]
+ {
+ strip_paren_blocks(e)
+ } else {
+ expr
+ }
+ },
+ _ => expr,
+ }
+}
+
+impl EarlyLintPass for MultiAssignments {
+ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
+ if let ExprKind::Assign(target, source, _) = &expr.kind {
+ if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(target).kind {
+ span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
+ };
+ if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(source).kind {
+ span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
+ }
+ };
+ }
+}
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, Mutability};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for `&mut Mutex::lock` calls
- ///
- /// ### Why is this bad?
- /// `Mutex::lock` is less efficient than
- /// calling `Mutex::get_mut`. In addition you also have a statically
- /// guarantee that the mutex isn't locked, instead of just a runtime
- /// guarantee.
- ///
- /// ### Example
- /// ```rust
- /// use std::sync::{Arc, Mutex};
- ///
- /// let mut value_rc = Arc::new(Mutex::new(42_u8));
- /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
- ///
- /// let mut value = value_mutex.lock().unwrap();
- /// *value += 1;
- /// ```
- /// Use instead:
- /// ```rust
- /// use std::sync::{Arc, Mutex};
- ///
- /// let mut value_rc = Arc::new(Mutex::new(42_u8));
- /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
- ///
- /// let value = value_mutex.get_mut().unwrap();
- /// *value += 1;
- /// ```
- #[clippy::version = "1.49.0"]
- pub MUT_MUTEX_LOCK,
- style,
- "`&mut Mutex::lock` does unnecessary locking"
-}
-
-declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]);
-
-impl<'tcx> LateLintPass<'tcx> for MutMutexLock {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) {
- if_chain! {
- if let ExprKind::MethodCall(path, [self_arg, ..], _) = &ex.kind;
- if path.ident.name == sym!(lock);
- let ty = cx.typeck_results().expr_ty(self_arg);
- if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind();
- if is_type_diagnostic_item(cx, *inner_ty, sym::Mutex);
- then {
- span_lint_and_sugg(
- cx,
- MUT_MUTEX_LOCK,
- path.ident.span,
- "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference",
- "change this to",
- "get_mut".to_owned(),
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
-}
-use std::collections::VecDeque;
-
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_lint_allowed;
-use itertools::{izip, Itertools};
-use rustc_ast::{walk_list, Label, Mutability};
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{get_expr_use_or_unification_node, get_parent_node, path_def_id, path_to_local, path_to_local_id};
+use core::cell::Cell;
+use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
-use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
-use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
-use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor};
-use rustc_hir::{
- Arm, Block, Body, Closure, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path,
- PathSegment, QPath, Stmt, StmtKind, TyKind, UnOp,
-};
+use rustc_hir::hir_id::HirIdMap;
+use rustc_hir::{Body, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Node, PatKind, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_middle::ty::{Ty, TyCtxt, TypeckResults};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::kw;
-use rustc_span::symbol::Ident;
+use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
+use rustc_middle::ty::{self, ConstKind};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::symbol::{kw, Ident};
use rustc_span::Span;
declare_clippy_lint! {
/// ```
#[clippy::version = "1.61.0"]
pub ONLY_USED_IN_RECURSION,
- nursery,
+ complexity,
"arguments that is only used in recursion can be removed"
}
-declare_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
-
-impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
- fn check_fn(
- &mut self,
- cx: &LateContext<'tcx>,
- kind: FnKind<'tcx>,
- decl: &'tcx rustc_hir::FnDecl<'tcx>,
- body: &'tcx Body<'tcx>,
- _: Span,
- id: HirId,
- ) {
- if is_lint_allowed(cx, ONLY_USED_IN_RECURSION, id) {
- return;
- }
- if let FnKind::ItemFn(ident, ..) | FnKind::Method(ident, ..) = kind {
- let def_id = id.owner.to_def_id();
- let data = cx.tcx.def_path(def_id).data;
-
- if data.len() > 1 {
- match data.get(data.len() - 2) {
- Some(DisambiguatedDefPathData {
- data: DefPathData::Impl,
- disambiguator,
- }) if *disambiguator != 0 => return,
- _ => {},
- }
- }
-
- let has_self = !matches!(decl.implicit_self, ImplicitSelfKind::None);
-
- let ty_res = cx.typeck_results();
- let param_span = body
- .params
- .iter()
- .flat_map(|param| {
- let mut v = Vec::new();
- param.pat.each_binding(|_, hir_id, span, ident| {
- v.push((hir_id, span, ident));
- });
- v
- })
- .skip(if has_self { 1 } else { 0 })
- .filter(|(_, _, ident)| !ident.name.as_str().starts_with('_'))
- .collect_vec();
-
- let params = body.params.iter().map(|param| param.pat).collect();
-
- let mut visitor = SideEffectVisit {
- graph: FxHashMap::default(),
- has_side_effect: FxHashSet::default(),
- ret_vars: Vec::new(),
- contains_side_effect: false,
- break_vars: FxHashMap::default(),
- params,
- fn_ident: ident,
- fn_def_id: def_id,
- is_method: matches!(kind, FnKind::Method(..)),
- has_self,
- ty_res,
- tcx: cx.tcx,
- visited_exprs: FxHashSet::default(),
- };
-
- visitor.visit_expr(&body.value);
- let vars = std::mem::take(&mut visitor.ret_vars);
- // this would set the return variables to side effect
- visitor.add_side_effect(vars);
-
- let mut queue = visitor.has_side_effect.iter().copied().collect::<VecDeque<_>>();
-
- // a simple BFS to check all the variables that have side effect
- while let Some(id) = queue.pop_front() {
- if let Some(next) = visitor.graph.get(&id) {
- for i in next {
- if !visitor.has_side_effect.contains(i) {
- visitor.has_side_effect.insert(*i);
- queue.push_back(*i);
- }
- }
- }
- }
-
- for (id, span, ident) in param_span {
- // if the variable is not used in recursion, it would be marked as unused
- if !visitor.has_side_effect.contains(&id) {
- let mut queue = VecDeque::new();
- let mut visited = FxHashSet::default();
-
- queue.push_back(id);
-
- // a simple BFS to check the graph can reach to itself
- // if it can't, it means the variable is never used in recursion
- while let Some(id) = queue.pop_front() {
- if let Some(next) = visitor.graph.get(&id) {
- for i in next {
- if !visited.contains(i) {
- visited.insert(id);
- queue.push_back(*i);
- }
- }
- }
- }
+impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
+
+#[derive(Clone, Copy)]
+enum FnKind {
+ Fn,
+ TraitFn,
+ // This is a hack. Ideally we would store a `SubstsRef<'tcx>` type here, but a lint pass must be `'static`.
+ // Substitutions are, however, interned. This allows us to store the pointer as a `usize` when comparing for
+ // equality.
+ ImplTraitFn(usize),
+}
- if visited.contains(&id) {
- span_lint_and_sugg(
- cx,
- ONLY_USED_IN_RECURSION,
- span,
- "parameter is only used in recursion",
- "if this is intentional, prefix with an underscore",
- format!("_{}", ident.name.as_str()),
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
+struct Param {
+ /// The function this is a parameter for.
+ fn_id: DefId,
+ fn_kind: FnKind,
+ /// The index of this parameter.
+ idx: usize,
+ ident: Ident,
+ /// Whether this parameter should be linted. Set by `Params::flag_for_linting`.
+ apply_lint: Cell<bool>,
+ /// All the uses of this parameter.
+ uses: Vec<Usage>,
+}
+impl Param {
+ fn new(fn_id: DefId, fn_kind: FnKind, idx: usize, ident: Ident) -> Self {
+ Self {
+ fn_id,
+ fn_kind,
+ idx,
+ ident,
+ apply_lint: Cell::new(true),
+ uses: Vec::new(),
}
}
}
-pub fn is_primitive(ty: Ty<'_>) -> bool {
- let ty = ty.peel_refs();
- ty.is_primitive() || ty.is_str()
+#[derive(Debug)]
+struct Usage {
+ span: Span,
+ idx: usize,
}
-
-pub fn is_array(ty: Ty<'_>) -> bool {
- let ty = ty.peel_refs();
- ty.is_array() || ty.is_array_slice()
+impl Usage {
+ fn new(span: Span, idx: usize) -> Self {
+ Self { span, idx }
+ }
}
-/// This builds the graph of side effect.
-/// The edge `a -> b` means if `a` has side effect, `b` will have side effect.
-///
-/// There are some example in following code:
-/// ```rust, ignore
-/// let b = 1;
-/// let a = b; // a -> b
-/// let (c, d) = (a, b); // c -> b, d -> b
-///
-/// let e = if a == 0 { // e -> a
-/// c // e -> c
-/// } else {
-/// d // e -> d
-/// };
-/// ```
-pub struct SideEffectVisit<'tcx> {
- graph: FxHashMap<HirId, FxHashSet<HirId>>,
- has_side_effect: FxHashSet<HirId>,
- // bool for if the variable was dereferenced from mutable reference
- ret_vars: Vec<(HirId, bool)>,
- contains_side_effect: bool,
- // break label
- break_vars: FxHashMap<Ident, Vec<(HirId, bool)>>,
- params: Vec<&'tcx Pat<'tcx>>,
- fn_ident: Ident,
- fn_def_id: DefId,
- is_method: bool,
- has_self: bool,
- ty_res: &'tcx TypeckResults<'tcx>,
- tcx: TyCtxt<'tcx>,
- visited_exprs: FxHashSet<HirId>,
+/// The parameters being checked by the lint, indexed by both the parameter's `HirId` and the
+/// `DefId` of the function paired with the parameter's index.
+#[derive(Default)]
+struct Params {
+ params: Vec<Param>,
+ by_id: HirIdMap<usize>,
+ by_fn: FxHashMap<(DefId, usize), usize>,
}
-
-impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> {
- fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) {
- match s.kind {
- StmtKind::Local(Local {
- pat, init: Some(init), ..
- }) => {
- self.visit_pat_expr(pat, init, false);
- },
- StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => {
- walk_stmt(self, s);
- },
- StmtKind::Local(_) => {},
- }
- self.ret_vars.clear();
+impl Params {
+ fn insert(&mut self, param: Param, id: HirId) {
+ let idx = self.params.len();
+ self.by_id.insert(id, idx);
+ self.by_fn.insert((param.fn_id, param.idx), idx);
+ self.params.push(param);
}
- fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
- if !self.visited_exprs.insert(ex.hir_id) {
- return;
- }
- match ex.kind {
- ExprKind::Array(exprs) | ExprKind::Tup(exprs) => {
- self.ret_vars = exprs
- .iter()
- .flat_map(|expr| {
- self.visit_expr(expr);
- std::mem::take(&mut self.ret_vars)
- })
- .collect();
- },
- ExprKind::Call(callee, args) => self.visit_fn(callee, args),
- ExprKind::MethodCall(path, args, _) => self.visit_method_call(path, args),
- ExprKind::Binary(_, lhs, rhs) => {
- self.visit_bin_op(lhs, rhs);
- },
- ExprKind::Unary(op, expr) => self.visit_un_op(op, expr),
- ExprKind::Let(Let { pat, init, .. }) => self.visit_pat_expr(pat, init, false),
- ExprKind::If(bind, then_expr, else_expr) => {
- self.visit_if(bind, then_expr, else_expr);
- },
- ExprKind::Match(expr, arms, _) => self.visit_match(expr, arms),
- // since analysing the closure is not easy, just set all variables in it to side-effect
- ExprKind::Closure(&Closure { body, .. }) => {
- let body = self.tcx.hir().body(body);
- self.visit_body(body);
- let vars = std::mem::take(&mut self.ret_vars);
- self.add_side_effect(vars);
- },
- ExprKind::Loop(block, label, _, _) | ExprKind::Block(block, label) => {
- self.visit_block_label(block, label);
- },
- ExprKind::Assign(bind, expr, _) => {
- self.visit_assign(bind, expr);
- },
- ExprKind::AssignOp(_, bind, expr) => {
- self.visit_assign(bind, expr);
- self.visit_bin_op(bind, expr);
- },
- ExprKind::Field(expr, _) => {
- self.visit_expr(expr);
- if matches!(self.ty_res.expr_ty(expr).kind(), ty::Ref(_, _, Mutability::Mut)) {
- self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
- }
- },
- ExprKind::Index(expr, index) => {
- self.visit_expr(expr);
- let mut vars = std::mem::take(&mut self.ret_vars);
- self.visit_expr(index);
- self.ret_vars.append(&mut vars);
-
- if !is_array(self.ty_res.expr_ty(expr)) {
- self.add_side_effect(self.ret_vars.clone());
- } else if matches!(self.ty_res.expr_ty(expr).kind(), ty::Ref(_, _, Mutability::Mut)) {
- self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
- }
- },
- ExprKind::Break(dest, Some(expr)) => {
- self.visit_expr(expr);
- if let Some(label) = dest.label {
- self.break_vars
- .entry(label.ident)
- .or_insert(Vec::new())
- .append(&mut self.ret_vars);
- }
- self.contains_side_effect = true;
- },
- ExprKind::Ret(Some(expr)) => {
- self.visit_expr(expr);
- let vars = std::mem::take(&mut self.ret_vars);
- self.add_side_effect(vars);
- self.contains_side_effect = true;
- },
- ExprKind::Break(_, None) | ExprKind::Continue(_) | ExprKind::Ret(None) => {
- self.contains_side_effect = true;
- },
- ExprKind::Struct(_, exprs, expr) => {
- let mut ret_vars = exprs
- .iter()
- .flat_map(|field| {
- self.visit_expr(field.expr);
- std::mem::take(&mut self.ret_vars)
- })
- .collect();
-
- walk_list!(self, visit_expr, expr);
- self.ret_vars.append(&mut ret_vars);
- },
- _ => walk_expr(self, ex),
+ fn remove_by_id(&mut self, id: HirId) {
+ if let Some(param) = self.get_by_id_mut(id) {
+ param.uses = Vec::new();
+ let key = (param.fn_id, param.idx);
+ self.by_fn.remove(&key);
+ self.by_id.remove(&id);
}
}
- fn visit_path(&mut self, path: &'tcx Path<'tcx>, _id: HirId) {
- if let Res::Local(id) = path.res {
- self.ret_vars.push((id, false));
- }
+ fn get_by_id_mut(&mut self, id: HirId) -> Option<&mut Param> {
+ self.params.get_mut(*self.by_id.get(&id)?)
}
-}
-impl<'tcx> SideEffectVisit<'tcx> {
- fn visit_assign(&mut self, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>) {
- // Just support array and tuple unwrapping for now.
- //
- // ex) `(a, b) = (c, d);`
- // The graph would look like this:
- // a -> c
- // b -> d
- //
- // This would minimize the connection of the side-effect graph.
- match (&lhs.kind, &rhs.kind) {
- (ExprKind::Array(lhs), ExprKind::Array(rhs)) | (ExprKind::Tup(lhs), ExprKind::Tup(rhs)) => {
- // if not, it is a compile error
- debug_assert!(lhs.len() == rhs.len());
- izip!(*lhs, *rhs).for_each(|(lhs, rhs)| self.visit_assign(lhs, rhs));
- },
- // in other assigns, we have to connect all each other
- // because they can be connected somehow
- _ => {
- self.visit_expr(lhs);
- let lhs_vars = std::mem::take(&mut self.ret_vars);
- self.visit_expr(rhs);
- let rhs_vars = std::mem::take(&mut self.ret_vars);
- self.connect_assign(&lhs_vars, &rhs_vars, false);
- },
- }
+ fn get_by_fn(&self, id: DefId, idx: usize) -> Option<&Param> {
+ self.params.get(*self.by_fn.get(&(id, idx))?)
}
- fn visit_block_label(&mut self, block: &'tcx Block<'tcx>, label: Option<Label>) {
- self.visit_block(block);
- let _ = label.and_then(|label| {
- self.break_vars
- .remove(&label.ident)
- .map(|mut break_vars| self.ret_vars.append(&mut break_vars))
- });
- }
-
- fn visit_bin_op(&mut self, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>) {
- self.visit_expr(lhs);
- let mut ret_vars = std::mem::take(&mut self.ret_vars);
- self.visit_expr(rhs);
- self.ret_vars.append(&mut ret_vars);
-
- // the binary operation between non primitive values are overloaded operators
- // so they can have side-effects
- if !is_primitive(self.ty_res.expr_ty(lhs)) || !is_primitive(self.ty_res.expr_ty(rhs)) {
- self.ret_vars.iter().for_each(|id| {
- self.has_side_effect.insert(id.0);
- });
- self.contains_side_effect = true;
- }
+ fn clear(&mut self) {
+ self.params.clear();
+ self.by_id.clear();
+ self.by_fn.clear();
}
- fn visit_un_op(&mut self, op: UnOp, expr: &'tcx Expr<'tcx>) {
- self.visit_expr(expr);
- let ty = self.ty_res.expr_ty(expr);
- // dereferencing a reference has no side-effect
- if !is_primitive(ty) && !matches!((op, ty.kind()), (UnOp::Deref, ty::Ref(..))) {
- self.add_side_effect(self.ret_vars.clone());
- }
-
- if matches!((op, ty.kind()), (UnOp::Deref, ty::Ref(_, _, Mutability::Mut))) {
- self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
+ /// Sets the `apply_lint` flag on each parameter.
+ fn flag_for_linting(&mut self) {
+ // Stores the list of parameters currently being resolved. Needed to avoid cycles.
+ let mut eval_stack = Vec::new();
+ for param in &self.params {
+ self.try_disable_lint_for_param(param, &mut eval_stack);
}
}
- fn visit_pat_expr(&mut self, pat: &'tcx Pat<'tcx>, expr: &'tcx Expr<'tcx>, connect_self: bool) {
- match (&pat.kind, &expr.kind) {
- (PatKind::Tuple(pats, _), ExprKind::Tup(exprs)) => {
- self.ret_vars = izip!(*pats, *exprs)
- .flat_map(|(pat, expr)| {
- self.visit_pat_expr(pat, expr, connect_self);
- std::mem::take(&mut self.ret_vars)
- })
- .collect();
- },
- (PatKind::Slice(front_exprs, _, back_exprs), ExprKind::Array(exprs)) => {
- let mut vars = izip!(*front_exprs, *exprs)
- .flat_map(|(pat, expr)| {
- self.visit_pat_expr(pat, expr, connect_self);
- std::mem::take(&mut self.ret_vars)
- })
- .collect();
- self.ret_vars = izip!(back_exprs.iter().rev(), exprs.iter().rev())
- .flat_map(|(pat, expr)| {
- self.visit_pat_expr(pat, expr, connect_self);
- std::mem::take(&mut self.ret_vars)
- })
- .collect();
- self.ret_vars.append(&mut vars);
- },
- _ => {
- let mut lhs_vars = Vec::new();
- pat.each_binding(|_, id, _, _| lhs_vars.push((id, false)));
- self.visit_expr(expr);
- let rhs_vars = std::mem::take(&mut self.ret_vars);
- self.connect_assign(&lhs_vars, &rhs_vars, connect_self);
- self.ret_vars = rhs_vars;
- },
+ // Use by calling `flag_for_linting`.
+ fn try_disable_lint_for_param(&self, param: &Param, eval_stack: &mut Vec<usize>) -> bool {
+ if !param.apply_lint.get() {
+ true
+ } else if param.uses.is_empty() {
+ // Don't lint on unused parameters.
+ param.apply_lint.set(false);
+ true
+ } else if eval_stack.contains(¶m.idx) {
+ // Already on the evaluation stack. Returning false will continue to evaluate other dependencies.
+ false
+ } else {
+ eval_stack.push(param.idx);
+ // Check all cases when used at a different parameter index.
+ // Needed to catch cases like: `fn f(x: u32, y: u32) { f(y, x) }`
+ for usage in param.uses.iter().filter(|u| u.idx != param.idx) {
+ if self
+ .get_by_fn(param.fn_id, usage.idx)
+ // If the parameter can't be found, then it's used for more than just recursion.
+ .map_or(true, |p| self.try_disable_lint_for_param(p, eval_stack))
+ {
+ param.apply_lint.set(false);
+ eval_stack.pop();
+ return true;
+ }
+ }
+ eval_stack.pop();
+ false
}
}
+}
- fn visit_fn(&mut self, callee: &'tcx Expr<'tcx>, args: &'tcx [Expr<'tcx>]) {
- self.visit_expr(callee);
- let mut ret_vars = std::mem::take(&mut self.ret_vars);
- self.add_side_effect(ret_vars.clone());
-
- let mut is_recursive = false;
-
- if_chain! {
- if !self.has_self;
- if let ExprKind::Path(QPath::Resolved(_, path)) = callee.kind;
- if let Res::Def(DefKind::Fn, def_id) = path.res;
- if self.fn_def_id == def_id;
- then {
- is_recursive = true;
- }
- }
+#[derive(Default)]
+pub struct OnlyUsedInRecursion {
+ /// Track the top-level body entered. Needed to delay reporting when entering nested bodies.
+ entered_body: Option<HirId>,
+ params: Params,
+}
- if_chain! {
- if !self.has_self && self.is_method;
- if let ExprKind::Path(QPath::TypeRelative(ty, segment)) = callee.kind;
- if segment.ident == self.fn_ident;
- if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
- if let Res::SelfTy{ .. } = path.res;
- then {
- is_recursive = true;
- }
+impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
+ fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) {
+ if body.value.span.from_expansion() {
+ return;
}
-
- if is_recursive {
- izip!(self.params.clone(), args).for_each(|(pat, expr)| {
- self.visit_pat_expr(pat, expr, true);
- self.ret_vars.clear();
- });
- } else {
- // This would set arguments used in closure that does not have side-effect.
- // Closure itself can be detected whether there is a side-effect, but the
- // value of variable that is holding closure can change.
- // So, we just check the variables.
- self.ret_vars = args
- .iter()
- .flat_map(|expr| {
- self.visit_expr(expr);
- std::mem::take(&mut self.ret_vars)
- })
- .collect_vec()
- .into_iter()
- .map(|id| {
- self.has_side_effect.insert(id.0);
- id
- })
- .collect();
- self.contains_side_effect = true;
+ // `skip_params` is either `0` or `1` to skip the `self` parameter in trait functions.
+ // It can't be renamed, and it can't be removed without removing it from multiple functions.
+ let (fn_id, fn_kind, skip_params) = match get_parent_node(cx.tcx, body.value.hir_id) {
+ Some(Node::Item(i)) => (i.def_id.to_def_id(), FnKind::Fn, 0),
+ Some(Node::TraitItem(&TraitItem {
+ kind: TraitItemKind::Fn(ref sig, _),
+ def_id,
+ ..
+ })) => (
+ def_id.to_def_id(),
+ FnKind::TraitFn,
+ if sig.decl.implicit_self.has_implicit_self() {
+ 1
+ } else {
+ 0
+ },
+ ),
+ Some(Node::ImplItem(&ImplItem {
+ kind: ImplItemKind::Fn(ref sig, _),
+ def_id,
+ ..
+ })) => {
+ #[allow(trivial_casts)]
+ if let Some(Node::Item(item)) = get_parent_node(cx.tcx, cx.tcx.hir().local_def_id_to_hir_id(def_id))
+ && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.def_id)
+ && let Some(trait_item_id) = cx.tcx.associated_item(def_id).trait_item_def_id
+ {
+ (
+ trait_item_id,
+ FnKind::ImplTraitFn(cx.tcx.erase_regions(trait_ref.substs) as *const _ as usize),
+ if sig.decl.implicit_self.has_implicit_self() {
+ 1
+ } else {
+ 0
+ },
+ )
+ } else {
+ (def_id.to_def_id(), FnKind::Fn, 0)
+ }
+ },
+ _ => return,
+ };
+ body.params
+ .iter()
+ .enumerate()
+ .skip(skip_params)
+ .filter_map(|(idx, p)| match p.pat.kind {
+ PatKind::Binding(_, id, ident, None) if !ident.as_str().starts_with('_') => {
+ Some((id, Param::new(fn_id, fn_kind, idx, ident)))
+ },
+ _ => None,
+ })
+ .for_each(|(id, param)| self.params.insert(param, id));
+ if self.entered_body.is_none() {
+ self.entered_body = Some(body.value.hir_id);
}
-
- self.ret_vars.append(&mut ret_vars);
}
- fn visit_method_call(&mut self, path: &'tcx PathSegment<'tcx>, args: &'tcx [Expr<'tcx>]) {
- if_chain! {
- if self.is_method;
- if path.ident == self.fn_ident;
- if let ExprKind::Path(QPath::Resolved(_, path)) = args.first().unwrap().kind;
- if let Res::Local(..) = path.res;
- let ident = path.segments.last().unwrap().ident;
- if ident.name == kw::SelfLower;
- then {
- izip!(self.params.clone(), args.iter())
- .for_each(|(pat, expr)| {
- self.visit_pat_expr(pat, expr, true);
- self.ret_vars.clear();
- });
- } else {
- self.ret_vars = args
- .iter()
- .flat_map(|expr| {
- self.visit_expr(expr);
- std::mem::take(&mut self.ret_vars)
- })
- .collect_vec()
- .into_iter()
- .map(|a| {
- self.has_side_effect.insert(a.0);
- a
- })
- .collect();
- self.contains_side_effect = true;
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) {
+ if let Some(id) = path_to_local(e)
+ && let Some(param) = self.params.get_by_id_mut(id)
+ {
+ let typeck = cx.typeck_results();
+ let span = e.span;
+ let mut e = e;
+ loop {
+ match get_expr_use_or_unification_node(cx.tcx, e) {
+ None | Some((Node::Stmt(_), _)) => return,
+ Some((Node::Expr(parent), child_id)) => match parent.kind {
+ // Recursive call. Track which index the parameter is used in.
+ ExprKind::Call(callee, args)
+ if path_def_id(cx, callee).map_or(false, |id| {
+ id == param.fn_id
+ && has_matching_substs(param.fn_kind, typeck.node_substs(callee.hir_id))
+ }) =>
+ {
+ if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) {
+ param.uses.push(Usage::new(span, idx));
+ }
+ return;
+ },
+ ExprKind::MethodCall(_, args, _)
+ if typeck.type_dependent_def_id(parent.hir_id).map_or(false, |id| {
+ id == param.fn_id
+ && has_matching_substs(param.fn_kind, typeck.node_substs(parent.hir_id))
+ }) =>
+ {
+ if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) {
+ param.uses.push(Usage::new(span, idx));
+ }
+ return;
+ },
+ // Assignment to a parameter is fine.
+ ExprKind::Assign(lhs, _, _) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
+ return;
+ },
+ // Parameter update e.g. `x = x + 1`
+ ExprKind::Assign(lhs, rhs, _) | ExprKind::AssignOp(_, lhs, rhs)
+ if rhs.hir_id == child_id && path_to_local_id(lhs, id) =>
+ {
+ return;
+ },
+ // Side-effect free expressions. Walk to the parent expression.
+ ExprKind::Binary(_, lhs, rhs)
+ if typeck.expr_ty(lhs).is_primitive() && typeck.expr_ty(rhs).is_primitive() =>
+ {
+ e = parent;
+ continue;
+ },
+ ExprKind::Unary(_, arg) if typeck.expr_ty(arg).is_primitive() => {
+ e = parent;
+ continue;
+ },
+ ExprKind::AddrOf(..) | ExprKind::Cast(..) => {
+ e = parent;
+ continue;
+ },
+ // Only allow field accesses without auto-deref
+ ExprKind::Field(..) if typeck.adjustments().get(child_id).is_none() => {
+ e = parent;
+ continue
+ }
+ _ => (),
+ },
+ _ => (),
+ }
+ self.params.remove_by_id(id);
+ return;
}
}
}
- fn visit_if(&mut self, bind: &'tcx Expr<'tcx>, then_expr: &'tcx Expr<'tcx>, else_expr: Option<&'tcx Expr<'tcx>>) {
- let contains_side_effect = self.contains_side_effect;
- self.contains_side_effect = false;
- self.visit_expr(bind);
- let mut vars = std::mem::take(&mut self.ret_vars);
- self.visit_expr(then_expr);
- let mut then_vars = std::mem::take(&mut self.ret_vars);
- walk_list!(self, visit_expr, else_expr);
- if self.contains_side_effect {
- self.add_side_effect(vars.clone());
- }
- self.contains_side_effect |= contains_side_effect;
- self.ret_vars.append(&mut vars);
- self.ret_vars.append(&mut then_vars);
- }
-
- fn visit_match(&mut self, expr: &'tcx Expr<'tcx>, arms: &'tcx [Arm<'tcx>]) {
- self.visit_expr(expr);
- let mut expr_vars = std::mem::take(&mut self.ret_vars);
- self.ret_vars = arms
- .iter()
- .flat_map(|arm| {
- let contains_side_effect = self.contains_side_effect;
- self.contains_side_effect = false;
- // this would visit `expr` multiple times
- // but couldn't think of a better way
- self.visit_pat_expr(arm.pat, expr, false);
- let mut vars = std::mem::take(&mut self.ret_vars);
- let _ = arm.guard.as_ref().map(|guard| {
- self.visit_expr(match guard {
- Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => expr,
- });
- vars.append(&mut self.ret_vars);
- });
- self.visit_expr(arm.body);
- if self.contains_side_effect {
- self.add_side_effect(vars.clone());
- self.add_side_effect(expr_vars.clone());
+ fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) {
+ if self.entered_body == Some(body.value.hir_id) {
+ self.entered_body = None;
+ self.params.flag_for_linting();
+ for param in &self.params.params {
+ if param.apply_lint.get() {
+ span_lint_and_then(
+ cx,
+ ONLY_USED_IN_RECURSION,
+ param.ident.span,
+ "parameter is only used in recursion",
+ |diag| {
+ if param.ident.name != kw::SelfLower {
+ diag.span_suggestion(
+ param.ident.span,
+ "if this is intentional, prefix it with an underscore",
+ format!("_{}", param.ident.name),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ diag.span_note(
+ param.uses.iter().map(|x| x.span).collect::<Vec<_>>(),
+ "parameter used here",
+ );
+ },
+ );
}
- self.contains_side_effect |= contains_side_effect;
- vars.append(&mut self.ret_vars);
- vars
- })
- .collect();
- self.ret_vars.append(&mut expr_vars);
- }
-
- fn connect_assign(&mut self, lhs: &[(HirId, bool)], rhs: &[(HirId, bool)], connect_self: bool) {
- // if mutable dereference is on assignment it can have side-effect
- // (this can lead to parameter mutable dereference and change the original value)
- // too hard to detect whether this value is from parameter, so this would all
- // check mutable dereference assignment to side effect
- lhs.iter().filter(|(_, b)| *b).for_each(|(id, _)| {
- self.has_side_effect.insert(*id);
- self.contains_side_effect = true;
- });
-
- // there is no connection
- if lhs.is_empty() || rhs.is_empty() {
- return;
- }
-
- // by connected rhs in cycle, the connections would decrease
- // from `n * m` to `n + m`
- // where `n` and `m` are length of `lhs` and `rhs`.
-
- // unwrap is possible since rhs is not empty
- let rhs_first = rhs.first().unwrap();
- for (id, _) in lhs.iter() {
- if connect_self || *id != rhs_first.0 {
- self.graph
- .entry(*id)
- .or_insert_with(FxHashSet::default)
- .insert(rhs_first.0);
}
+ self.params.clear();
}
-
- let rhs = rhs.iter();
- izip!(rhs.clone().cycle().skip(1), rhs).for_each(|(from, to)| {
- if connect_self || from.0 != to.0 {
- self.graph.entry(from.0).or_insert_with(FxHashSet::default).insert(to.0);
- }
- });
}
+}
- fn add_side_effect(&mut self, v: Vec<(HirId, bool)>) {
- for (id, _) in v {
- self.has_side_effect.insert(id);
- self.contains_side_effect = true;
- }
+fn has_matching_substs(kind: FnKind, substs: SubstsRef<'_>) -> bool {
+ match kind {
+ FnKind::Fn => true,
+ FnKind::TraitFn => substs.iter().enumerate().all(|(idx, subst)| match subst.unpack() {
+ GenericArgKind::Lifetime(_) => true,
+ GenericArgKind::Type(ty) => matches!(*ty.kind(), ty::Param(ty) if ty.index as usize == idx),
+ GenericArgKind::Const(c) => matches!(c.kind(), ConstKind::Param(c) if c.index as usize == idx),
+ }),
+ #[allow(trivial_casts)]
+ FnKind::ImplTraitFn(expected_substs) => substs as *const _ as usize == expected_substs,
}
}
+++ /dev/null
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::paths;
-use clippy_utils::ty::match_type;
-use rustc_ast::ast::LitKind;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::{Span, Spanned};
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for duplicate open options as well as combinations
- /// that make no sense.
- ///
- /// ### Why is this bad?
- /// In the best case, the code will be harder to read than
- /// necessary. I don't know the worst case.
- ///
- /// ### Example
- /// ```rust
- /// use std::fs::OpenOptions;
- ///
- /// OpenOptions::new().read(true).truncate(true);
- /// ```
- #[clippy::version = "pre 1.29.0"]
- pub NONSENSICAL_OPEN_OPTIONS,
- correctness,
- "nonsensical combination of options for opening a file"
-}
-
-declare_lint_pass!(OpenOptions => [NONSENSICAL_OPEN_OPTIONS]);
-
-impl<'tcx> LateLintPass<'tcx> for OpenOptions {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
- if let ExprKind::MethodCall(path, [self_arg, ..], _) = &e.kind {
- let obj_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
- if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) {
- let mut options = Vec::new();
- get_open_options(cx, self_arg, &mut options);
- check_open_options(cx, &options, e.span);
- }
- }
- }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-enum Argument {
- True,
- False,
- Unknown,
-}
-
-#[derive(Debug)]
-enum OpenOption {
- Write,
- Read,
- Truncate,
- Create,
- Append,
-}
-
-fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) {
- if let ExprKind::MethodCall(path, arguments, _) = argument.kind {
- let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs();
-
- // Only proceed if this is a call on some object of type std::fs::OpenOptions
- if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 {
- let argument_option = match arguments[1].kind {
- ExprKind::Lit(ref span) => {
- if let Spanned {
- node: LitKind::Bool(lit),
- ..
- } = *span
- {
- if lit { Argument::True } else { Argument::False }
- } else {
- // The function is called with a literal which is not a boolean literal.
- // This is theoretically possible, but not very likely.
- return;
- }
- },
- _ => Argument::Unknown,
- };
-
- match path.ident.as_str() {
- "create" => {
- options.push((OpenOption::Create, argument_option));
- },
- "append" => {
- options.push((OpenOption::Append, argument_option));
- },
- "truncate" => {
- options.push((OpenOption::Truncate, argument_option));
- },
- "read" => {
- options.push((OpenOption::Read, argument_option));
- },
- "write" => {
- options.push((OpenOption::Write, argument_option));
- },
- _ => (),
- }
-
- get_open_options(cx, &arguments[0], options);
- }
- }
-}
-
-fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], span: Span) {
- let (mut create, mut append, mut truncate, mut read, mut write) = (false, false, false, false, false);
- let (mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) =
- (false, false, false, false, false);
- // This code is almost duplicated (oh, the irony), but I haven't found a way to
- // unify it.
-
- for option in options {
- match *option {
- (OpenOption::Create, arg) => {
- if create {
- span_lint(
- cx,
- NONSENSICAL_OPEN_OPTIONS,
- span,
- "the method `create` is called more than once",
- );
- } else {
- create = true;
- }
- create_arg = create_arg || (arg == Argument::True);
- },
- (OpenOption::Append, arg) => {
- if append {
- span_lint(
- cx,
- NONSENSICAL_OPEN_OPTIONS,
- span,
- "the method `append` is called more than once",
- );
- } else {
- append = true;
- }
- append_arg = append_arg || (arg == Argument::True);
- },
- (OpenOption::Truncate, arg) => {
- if truncate {
- span_lint(
- cx,
- NONSENSICAL_OPEN_OPTIONS,
- span,
- "the method `truncate` is called more than once",
- );
- } else {
- truncate = true;
- }
- truncate_arg = truncate_arg || (arg == Argument::True);
- },
- (OpenOption::Read, arg) => {
- if read {
- span_lint(
- cx,
- NONSENSICAL_OPEN_OPTIONS,
- span,
- "the method `read` is called more than once",
- );
- } else {
- read = true;
- }
- read_arg = read_arg || (arg == Argument::True);
- },
- (OpenOption::Write, arg) => {
- if write {
- span_lint(
- cx,
- NONSENSICAL_OPEN_OPTIONS,
- span,
- "the method `write` is called more than once",
- );
- } else {
- write = true;
- }
- write_arg = write_arg || (arg == Argument::True);
- },
- }
- }
-
- if read && truncate && read_arg && truncate_arg && !(write && write_arg) {
- span_lint(
- cx,
- NONSENSICAL_OPEN_OPTIONS,
- span,
- "file opened with `truncate` and `read`",
- );
- }
- if append && truncate && append_arg && truncate_arg {
- span_lint(
- cx,
- NONSENSICAL_OPEN_OPTIONS,
- span,
- "file opened with `append` and `truncate`",
- );
- }
-}
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{
can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_lang_ctor, peel_blocks,
peel_hir_expr_while, CaptureKind,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir::LangItem::OptionSome;
-use rustc_hir::{def::Res, BindingAnnotation, Expr, ExprKind, Mutability, PatKind, Path, QPath, UnOp};
+use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
+use rustc_hir::{
+ def::Res, Arm, BindingAnnotation, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp,
+};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
- /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more
+ /// Lints usage of `if let Some(v) = ... { y } else { x }` and
+ /// `match .. { Some(v) => y, None/_ => x }` which are more
/// idiomatically done with `Option::map_or` (if the else bit is a pure
/// expression) or `Option::map_or_else` (if the else bit is an impure
/// expression).
/// } else {
/// 5
/// };
+ /// let _ = match optional {
+ /// Some(val) => val + 1,
+ /// None => 5
+ /// };
/// let _ = if let Some(foo) = optional {
/// foo
/// } else {
/// # let optional: Option<u32> = Some(0);
/// # fn do_complicated_function() -> u32 { 5 };
/// let _ = optional.map_or(5, |foo| foo);
+ /// let _ = optional.map_or(5, |val| val + 1);
/// let _ = optional.map_or_else(||{
/// let y = do_complicated_function();
/// y*y
/// }, |foo| foo);
/// ```
+ // FIXME: Before moving this lint out of nursery, the lint name needs to be updated. It now also
+ // covers matches and `Result`.
#[clippy::version = "1.47.0"]
pub OPTION_IF_LET_ELSE,
nursery,
declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]);
-/// Returns true iff the given expression is the result of calling `Result::ok`
-fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
- if let ExprKind::MethodCall(path, &[ref receiver], _) = &expr.kind {
- path.ident.name.as_str() == "ok"
- && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Result)
- } else {
- false
- }
-}
-
-/// A struct containing information about occurrences of the
-/// `if let Some(..) = .. else` construct that this lint detects.
-struct OptionIfLetElseOccurrence {
+/// A struct containing information about occurrences of construct that this lint detects
+///
+/// Such as:
+///
+/// ```ignore
+/// if let Some(..) = {..} else {..}
+/// ```
+/// or
+/// ```ignore
+/// match x {
+/// Some(..) => {..},
+/// None/_ => {..}
+/// }
+/// ```
+struct OptionOccurence {
option: String,
method_sugg: String,
some_expr: String,
)
}
-/// If this expression is the option if let/else construct we're detecting, then
-/// this function returns an `OptionIfLetElseOccurrence` struct with details if
-/// this construct is found, or None if this construct is not found.
-fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurrence> {
+fn try_get_option_occurence<'tcx>(
+ cx: &LateContext<'tcx>,
+ pat: &Pat<'tcx>,
+ expr: &Expr<'_>,
+ if_then: &'tcx Expr<'_>,
+ if_else: &'tcx Expr<'_>,
+) -> Option<OptionOccurence> {
+ let cond_expr = match expr.kind {
+ ExprKind::Unary(UnOp::Deref, inner_expr) | ExprKind::AddrOf(_, _, inner_expr) => inner_expr,
+ _ => expr,
+ };
+ let inner_pat = try_get_inner_pat(cx, pat)?;
if_chain! {
- if !expr.span.from_expansion(); // Don't lint macros, because it behaves weirdly
- if !in_constant(cx, expr.hir_id);
- if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) })
- = higher::IfLet::hir(cx, expr);
- if !is_else_clause(cx.tcx, expr);
- if !is_result_ok(cx, let_expr); // Don't lint on Result::ok because a different lint does it already
- if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &let_pat.kind;
- if is_lang_ctor(cx, struct_qpath, OptionSome);
- if let PatKind::Binding(bind_annotation, _, id, None) = &inner_pat.kind;
+ if let PatKind::Binding(bind_annotation, _, id, None) = inner_pat.kind;
if let Some(some_captures) = can_move_expr_to_closure(cx, if_then);
if let Some(none_captures) = can_move_expr_to_closure(cx, if_else);
if some_captures
.iter()
.filter_map(|(id, &c)| none_captures.get(id).map(|&c2| (c, c2)))
.all(|(x, y)| x.is_imm_ref() && y.is_imm_ref());
-
then {
- let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
+ let capture_mut = if bind_annotation == BindingAnnotation::Mutable { "mut " } else { "" };
let some_body = peel_blocks(if_then);
let none_body = peel_blocks(if_else);
let method_sugg = if eager_or_lazy::switch_to_eager_eval(cx, none_body) { "map_or" } else { "map_or_else" };
let capture_name = id.name.to_ident_string();
- let (as_ref, as_mut) = match &let_expr.kind {
+ let (as_ref, as_mut) = match &expr.kind {
ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true),
- _ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut),
- };
- let cond_expr = match let_expr.kind {
- // Pointer dereferencing happens automatically, so we can omit it in the suggestion
- ExprKind::Unary(UnOp::Deref, expr) | ExprKind::AddrOf(_, _, expr) => expr,
- _ => let_expr,
+ _ => (bind_annotation == BindingAnnotation::Ref, bind_annotation == BindingAnnotation::RefMut),
};
+
// Check if captures the closure will need conflict with borrows made in the scrutinee.
// TODO: check all the references made in the scrutinee expression. This will require interacting
// with the borrow checker. Currently only `<local>[.<field>]*` is checked for.
}
}
}
- Some(OptionIfLetElseOccurrence {
+
+ return Some(OptionOccurence {
option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
method_sugg: method_sugg.to_string(),
some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir_with_macro_callsite(cx, some_body, "..")),
none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir_with_macro_callsite(cx, none_body, "..")),
- })
+ });
+ }
+ }
+
+ None
+}
+
+fn try_get_inner_pat<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<&'tcx Pat<'tcx>> {
+ if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind {
+ if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk) {
+ return Some(inner_pat);
+ }
+ }
+ None
+}
+
+/// If this expression is the option if let/else construct we're detecting, then
+/// this function returns an `OptionOccurence` struct with details if
+/// this construct is found, or None if this construct is not found.
+fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionOccurence> {
+ if let Some(higher::IfLet {
+ let_pat,
+ let_expr,
+ if_then,
+ if_else: Some(if_else),
+ }) = higher::IfLet::hir(cx, expr)
+ {
+ if !is_else_clause(cx.tcx, expr) {
+ return try_get_option_occurence(cx, let_pat, let_expr, if_then, if_else);
+ }
+ }
+ None
+}
+
+fn detect_option_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionOccurence> {
+ if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
+ if let Some((let_pat, if_then, if_else)) = try_convert_match(cx, arms) {
+ return try_get_option_occurence(cx, let_pat, ex, if_then, if_else);
+ }
+ }
+ None
+}
+
+fn try_convert_match<'tcx>(
+ cx: &LateContext<'tcx>,
+ arms: &[Arm<'tcx>],
+) -> Option<(&'tcx Pat<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
+ if arms.len() == 2 {
+ return if is_none_or_err_arm(cx, &arms[1]) {
+ Some((arms[0].pat, arms[0].body, arms[1].body))
+ } else if is_none_or_err_arm(cx, &arms[0]) {
+ Some((arms[1].pat, arms[1].body, arms[0].body))
} else {
None
- }
+ };
+ }
+ None
+}
+
+fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
+ match arm.pat.kind {
+ PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
+ PatKind::TupleStruct(ref qpath, [first_pat], _) => {
+ is_lang_ctor(cx, qpath, ResultErr) && matches!(first_pat.kind, PatKind::Wild)
+ },
+ PatKind::Wild => true,
+ _ => false,
}
}
impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
- if let Some(detection) = detect_option_if_let_else(cx, expr) {
+ // Don't lint macros and constants
+ if expr.span.from_expansion() || in_constant(cx, expr.hir_id) {
+ return;
+ }
+
+ let detection = detect_option_if_let_else(cx, expr).or_else(|| detect_option_match(cx, expr));
+ if let Some(det) = detection {
span_lint_and_sugg(
cx,
OPTION_IF_LET_ELSE,
expr.span,
- format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(),
+ format!("use Option::{} instead of an if let/else", det.method_sugg).as_str(),
"try",
format!(
"{}.{}({}, {})",
- detection.option, detection.method_sugg, detection.none_expr, detection.some_expr,
+ det.option, det.method_sugg, det.none_expr, det.some_expr
),
Applicability::MaybeIncorrect,
);
// If the expression is a literal `Option::None`
let is_none_ctor = |expr: &Expr<'_>| {
- matches!(&peel_hir_expr_refs(expr).0.kind,
+ !expr.span.from_expansion()
+ && matches!(&peel_hir_expr_refs(expr).0.kind,
ExprKind::Path(p) if is_lang_ctor(cx, p, LangItem::OptionNone))
};
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
-use if_chain::if_chain;
-use rustc_ast::ast::LitKind;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::sym;
-use std::path::{Component, Path};
-
-declare_clippy_lint! {
- /// ### What it does
- ///* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)
- /// calls on `PathBuf` that can cause overwrites.
- ///
- /// ### Why is this bad?
- /// Calling `push` with a root path at the start can overwrite the
- /// previous defined path.
- ///
- /// ### Example
- /// ```rust
- /// use std::path::PathBuf;
- ///
- /// let mut x = PathBuf::from("/foo");
- /// x.push("/bar");
- /// assert_eq!(x, PathBuf::from("/bar"));
- /// ```
- /// Could be written:
- ///
- /// ```rust
- /// use std::path::PathBuf;
- ///
- /// let mut x = PathBuf::from("/foo");
- /// x.push("bar");
- /// assert_eq!(x, PathBuf::from("/foo/bar"));
- /// ```
- #[clippy::version = "1.36.0"]
- pub PATH_BUF_PUSH_OVERWRITE,
- nursery,
- "calling `push` with file system root on `PathBuf` can overwrite it"
-}
-
-declare_lint_pass!(PathBufPushOverwrite => [PATH_BUF_PUSH_OVERWRITE]);
-
-impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if_chain! {
- if let ExprKind::MethodCall(path, [recv, get_index_arg], _) = expr.kind;
- if path.ident.name == sym!(push);
- if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::PathBuf);
- if let ExprKind::Lit(ref lit) = get_index_arg.kind;
- if let LitKind::Str(ref path_lit, _) = lit.node;
- if let pushed_path = Path::new(path_lit.as_str());
- if let Some(pushed_path_lit) = pushed_path.to_str();
- if pushed_path.has_root();
- if let Some(root) = pushed_path.components().next();
- if root == Component::RootDir;
- then {
- span_lint_and_sugg(
- cx,
- PATH_BUF_PUSH_OVERWRITE,
- lit.span,
- "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
- "try",
- format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
- Applicability::MachineApplicable,
- );
- }
- }
- }
-}
if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr);
if !is_else_clause(cx.tcx, expr);
if let PatKind::TupleStruct(ref path1, [field], None) = let_pat.kind;
- if let PatKind::Binding(annot, bind_id, ident, _) = field.kind;
+ if let PatKind::Binding(annot, bind_id, ident, None) = field.kind;
let caller_ty = cx.typeck_results().expr_ty(let_expr);
let if_block = IfBlockType::IfLet(path1, caller_ty, ident.name, let_expr, if_then, if_else);
if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id))
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::higher;
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, msrvs, path_to_local};
-use clippy_utils::{higher, SpanlessEq};
use if_chain::if_chain;
use rustc_ast::ast::RangeLimits;
use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind, HirId, PathSegment, QPath};
+use rustc_hir::{BinOpKind, Expr, ExprKind, HirId};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::{Span, Spanned};
-use rustc_span::sym;
use std::cmp::Ordering;
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for zipping a collection with the range of
- /// `0.._.len()`.
- ///
- /// ### Why is this bad?
- /// The code is better expressed with `.enumerate()`.
- ///
- /// ### Example
- /// ```rust
- /// # let x = vec![1];
- /// let _ = x.iter().zip(0..x.len());
- /// ```
- ///
- /// Use instead:
- /// ```rust
- /// # let x = vec![1];
- /// let _ = x.iter().enumerate();
- /// ```
- #[clippy::version = "pre 1.29.0"]
- pub RANGE_ZIP_WITH_LEN,
- complexity,
- "zipping iterator with a range when `enumerate()` would do"
-}
-
declare_clippy_lint! {
/// ### What it does
/// Checks for exclusive ranges where 1 is added to the
}
impl_lint_pass!(Ranges => [
- RANGE_ZIP_WITH_LEN,
RANGE_PLUS_ONE,
RANGE_MINUS_ONE,
REVERSED_EMPTY_RANGES,
impl<'tcx> LateLintPass<'tcx> for Ranges {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- match expr.kind {
- ExprKind::MethodCall(path, args, _) => {
- check_range_zip_with_len(cx, path, args, expr.span);
- },
- ExprKind::Binary(ref op, l, r) => {
- if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
- check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
- }
- },
- _ => {},
+ if let ExprKind::Binary(ref op, l, r) = expr.kind {
+ if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
+ check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
+ }
}
check_exclusive_range_plus_one(cx, expr);
None
}
-fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) {
- if_chain! {
- if path.ident.as_str() == "zip";
- if let [iter, zip_arg] = args;
- // `.iter()` call
- if let ExprKind::MethodCall(iter_path, [iter_caller, ..], _) = iter.kind;
- if iter_path.ident.name == sym::iter;
- // range expression in `.zip()` call: `0..x.len()`
- if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg);
- if is_integer_const(cx, start, 0);
- // `.len()` call
- if let ExprKind::MethodCall(len_path, [len_caller], _) = end.kind;
- if len_path.ident.name == sym::len;
- // `.iter()` and `.len()` called on same `Path`
- if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_caller.kind;
- if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_caller.kind;
- if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
- then {
- span_lint(cx,
- RANGE_ZIP_WITH_LEN,
- span,
- &format!("it is more idiomatic to use `{}.iter().enumerate()`",
- snippet(cx, iter_caller.span, "_"))
- );
- }
- }
-}
-
// exclusive range plus one: `x..(y+1)`
fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
/// let data = std::rc::Rc::new("some data".to_string());
/// let v = vec![data; 100];
/// ```
- #[clippy::version = "1.62.0"]
+ #[clippy::version = "1.63.0"]
pub RC_CLONE_IN_VEC_INIT,
suspicious,
"initializing reference-counted pointer in `vec![elem; len]`"
use rustc_middle::ty::subst::GenericArg;
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use std::iter;
+
declare_clippy_lint! {
/// ### What it does
/// Checks for redundant slicing expressions which use the full range, and
} else if let Some(target_id) = cx.tcx.lang_items().deref_target() {
if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions(
cx.param_env,
- cx.tcx.mk_projection(target_id, cx.tcx.mk_substs([GenericArg::from(indexed_ty)].into_iter())),
+ cx.tcx.mk_projection(target_id, cx.tcx.mk_substs(iter::once(GenericArg::from(indexed_ty)))),
) {
if deref_ty == expr_ty {
let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
impl RedundantStaticLifetimes {
// Recursively visit types
- fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
+ fn visit_type(ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
match ty.kind {
// Be careful of nested structures (arrays and tuples)
TyKind::Array(ref ty, _) | TyKind::Slice(ref ty) => {
- self.visit_type(ty, cx, reason);
+ Self::visit_type(ty, cx, reason);
},
TyKind::Tup(ref tup) => {
for tup_ty in tup {
- self.visit_type(tup_ty, cx, reason);
+ Self::visit_type(tup_ty, cx, reason);
}
},
// This is what we are looking for !
_ => {},
}
}
- self.visit_type(&borrow_type.ty, cx, reason);
+ Self::visit_type(&borrow_type.ty, cx, reason);
},
_ => {},
}
if !item.span.from_expansion() {
if let ItemKind::Const(_, ref var_type, _) = item.kind {
- self.visit_type(var_type, cx, "constants have by default a `'static` lifetime");
+ Self::visit_type(var_type, cx, "constants have by default a `'static` lifetime");
// Don't check associated consts because `'static` cannot be elided on those (issue
// #2438)
}
if let ItemKind::Static(ref var_type, _, _) = item.kind {
- self.visit_type(var_type, cx, "statics have by default a `'static` lifetime");
+ Self::visit_type(var_type, cx, "statics have by default a `'static` lifetime");
}
}
}
+++ /dev/null
-use clippy_utils::consts::{constant_context, Constant};
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet;
-use clippy_utils::ty::is_type_diagnostic_item;
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for usage of `.repeat(1)` and suggest the following method for each types.
- /// - `.to_string()` for `str`
- /// - `.clone()` for `String`
- /// - `.to_vec()` for `slice`
- ///
- /// The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if
- /// they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306))
- ///
- /// ### Why is this bad?
- /// For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning
- /// the string is the intention behind this, `clone()` should be used.
- ///
- /// ### Example
- /// ```rust
- /// fn main() {
- /// let x = String::from("hello world").repeat(1);
- /// }
- /// ```
- /// Use instead:
- /// ```rust
- /// fn main() {
- /// let x = String::from("hello world").clone();
- /// }
- /// ```
- #[clippy::version = "1.47.0"]
- pub REPEAT_ONCE,
- complexity,
- "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
-}
-
-declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]);
-
-impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
- fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
- if_chain! {
- if let ExprKind::MethodCall(path, [receiver, count], _) = &expr.kind;
- if path.ident.name == sym!(repeat);
- if constant_context(cx, cx.typeck_results()).expr(count) == Some(Constant::Int(1));
- if !receiver.span.from_expansion();
- then {
- let ty = cx.typeck_results().expr_ty(receiver).peel_refs();
- if ty.is_str() {
- span_lint_and_sugg(
- cx,
- REPEAT_ONCE,
- expr.span,
- "calling `repeat(1)` on str",
- "consider using `.to_string()` instead",
- format!("{}.to_string()", snippet(cx, receiver.span, r#""...""#)),
- Applicability::MachineApplicable,
- );
- } else if ty.builtin_index().is_some() {
- span_lint_and_sugg(
- cx,
- REPEAT_ONCE,
- expr.span,
- "calling `repeat(1)` on slice",
- "consider using `.to_vec()` instead",
- format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)),
- Applicability::MachineApplicable,
- );
- } else if is_type_diagnostic_item(cx, ty, sym::String) {
- span_lint_and_sugg(
- cx,
- REPEAT_ONCE,
- expr.span,
- "calling `repeat(1)` on a string literal",
- "consider using `.clone()` instead",
- format!("{}.clone()", snippet(cx, receiver.span, r#""...""#)),
- Applicability::MachineApplicable,
- );
- }
- }
- }
- }
-}
use clippy_utils::source::{snippet_opt, snippet_with_context};
use clippy_utils::{fn_def_id, path_to_local_id};
use if_chain::if_chain;
-use rustc_ast::ast::Attribute;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
-use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
}
}
-fn attr_is_cfg(attr: &Attribute) -> bool {
- attr.meta_item_list().is_some() && attr.has_name(sym::cfg)
-}
-
fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) {
if let Some(expr) = block.expr {
check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty);
match expr.kind {
// simple return is always "bad"
ExprKind::Ret(ref inner) => {
- // allow `#[cfg(a)] return a; #[cfg(b)] return b;`
- let attrs = cx.tcx.hir().attrs(expr.hir_id);
- if !attrs.iter().any(attr_is_cfg) {
+ if cx.tcx.hir().attrs(expr.hir_id).is_empty() {
let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
if !borrows {
emit_return_lint(
use clippy_utils::diagnostics::span_lint;
use clippy_utils::return_ty;
-use clippy_utils::ty::{contains_adt_constructor, contains_ty};
+use clippy_utils::ty::contains_adt_constructor;
use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
if !contains_adt_constructor(ret_ty, self_adt) {
return;
}
- } else if !contains_ty(ret_ty, self_ty) {
+ } else if !ret_ty.contains(self_ty) {
return;
}
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::{is_slice_of_primitives, sugg::Sugg};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
- /// ### What it does
- /// When sorting primitive values (integers, bools, chars, as well
- /// as arrays, slices, and tuples of such items), it is typically better to
- /// use an unstable sort than a stable sort.
- ///
- /// ### Why is this bad?
- /// Typically, using a stable sort consumes more memory and cpu cycles.
- /// Because values which compare equal are identical, preserving their
- /// relative order (the guarantee that a stable sort provides) means
- /// nothing, while the extra costs still apply.
- ///
- /// ### Known problems
- ///
- /// As pointed out in
- /// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241),
- /// a stable sort can instead be significantly faster for certain scenarios
- /// (eg. when a sorted vector is extended with new data and resorted).
- ///
- /// For more information and benchmarking results, please refer to the
- /// issue linked above.
- ///
- /// ### Example
- /// ```rust
- /// let mut vec = vec![2, 1, 3];
- /// vec.sort();
- /// ```
- /// Use instead:
- /// ```rust
- /// let mut vec = vec![2, 1, 3];
- /// vec.sort_unstable();
- /// ```
- #[clippy::version = "1.47.0"]
- pub STABLE_SORT_PRIMITIVE,
- pedantic,
- "use of sort() when sort_unstable() is equivalent"
-}
-
-declare_lint_pass!(StableSortPrimitive => [STABLE_SORT_PRIMITIVE]);
-
-/// The three "kinds" of sorts
-enum SortingKind {
- Vanilla,
- /* The other kinds of lint are currently commented out because they
- * can map distinct values to equal ones. If the key function is
- * provably one-to-one, or if the Cmp function conserves equality,
- * then they could be linted on, but I don't know if we can check
- * for that. */
-
- /* ByKey,
- * ByCmp, */
-}
-impl SortingKind {
- /// The name of the stable version of this kind of sort
- fn stable_name(&self) -> &str {
- match self {
- SortingKind::Vanilla => "sort",
- /* SortingKind::ByKey => "sort_by_key",
- * SortingKind::ByCmp => "sort_by", */
- }
- }
- /// The name of the unstable version of this kind of sort
- fn unstable_name(&self) -> &str {
- match self {
- SortingKind::Vanilla => "sort_unstable",
- /* SortingKind::ByKey => "sort_unstable_by_key",
- * SortingKind::ByCmp => "sort_unstable_by", */
- }
- }
- /// Takes the name of a function call and returns the kind of sort
- /// that corresponds to that function name (or None if it isn't)
- fn from_stable_name(name: &str) -> Option<SortingKind> {
- match name {
- "sort" => Some(SortingKind::Vanilla),
- // "sort_by" => Some(SortingKind::ByCmp),
- // "sort_by_key" => Some(SortingKind::ByKey),
- _ => None,
- }
- }
-}
-
-/// A detected instance of this lint
-struct LintDetection {
- slice_name: String,
- method: SortingKind,
- method_args: String,
- slice_type: String,
-}
-
-fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintDetection> {
- if_chain! {
- if let ExprKind::MethodCall(method_name, [slice, args @ ..], _) = &expr.kind;
- if let Some(method) = SortingKind::from_stable_name(method_name.ident.name.as_str());
- if let Some(slice_type) = is_slice_of_primitives(cx, slice);
- then {
- let args_str = args.iter().map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::<Vec<String>>().join(", ");
- Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str, slice_type })
- } else {
- None
- }
- }
-}
-
-impl LateLintPass<'_> for StableSortPrimitive {
- fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
- if let Some(detection) = detect_stable_sort_primitive(cx, expr) {
- span_lint_and_then(
- cx,
- STABLE_SORT_PRIMITIVE,
- expr.span,
- format!(
- "used `{}` on primitive type `{}`",
- detection.method.stable_name(),
- detection.slice_type,
- )
- .as_str(),
- |diag| {
- diag.span_suggestion(
- expr.span,
- "try",
- format!(
- "{}.{}({})",
- detection.slice_name,
- detection.method.unstable_name(),
- detection.method_args,
- ),
- Applicability::MachineApplicable,
- );
- diag.note(
- "an unstable sort typically performs faster without any observable difference for this data type",
- );
- },
- );
- }
- }
-}
use core::hash::{Hash, Hasher};
use if_chain::if_chain;
use itertools::Itertools;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::unhash::UnhashMap;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{BytePos, Span};
+use std::collections::hash_map::Entry;
declare_clippy_lint! {
/// ### What it does
fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
self.check_type_repetition(cx, gen);
check_trait_bound_duplication(cx, gen);
- check_bounds_or_where_duplication(cx, gen);
}
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
}
fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
- if gen.span.from_expansion() || gen.params.is_empty() || gen.predicates.is_empty() {
+ if gen.span.from_expansion() {
return;
}
- let mut map = FxHashMap::<_, Vec<_>>::default();
- for predicate in gen.predicates {
+ // Explanation:
+ // fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+ // where T: Clone + Default, { unimplemented!(); }
+ // ^^^^^^^^^^^^^^^^^^
+ // |
+ // collects each of these where clauses into a set keyed by generic name and comparable trait
+ // eg. (T, Clone)
+ let where_predicates = gen
+ .predicates
+ .iter()
+ .filter_map(|pred| {
+ if_chain! {
+ if pred.in_where_clause();
+ if let WherePredicate::BoundPredicate(bound_predicate) = pred;
+ if let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind;
+ then {
+ return Some(
+ rollup_traits(cx, bound_predicate.bounds, "these where clauses contain repeated elements")
+ .into_iter().map(|(trait_ref, _)| (path.res, trait_ref)))
+ }
+ }
+ None
+ })
+ .flatten()
+ .collect::<FxHashSet<_>>();
+
+ // Explanation:
+ // fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) ...
+ // ^^^^^^^^^^^^^^^^^^ ^^^^^^^
+ // |
+ // compare trait bounds keyed by generic name and comparable trait to collected where
+ // predicates eg. (T, Clone)
+ for predicate in gen.predicates.iter().filter(|pred| !pred.in_where_clause()) {
if_chain! {
- if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
+ if let WherePredicate::BoundPredicate(bound_predicate) = predicate;
if bound_predicate.origin != PredicateOrigin::ImplTrait;
if !bound_predicate.span.from_expansion();
- if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
- if let Some(segment) = segments.first();
+ if let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind;
then {
- for (res_where, _, span_where) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) {
- let trait_resolutions_direct = map.entry(segment.ident).or_default();
- if let Some((_, span_direct)) = trait_resolutions_direct
- .iter()
- .find(|(res_direct, _)| *res_direct == res_where) {
+ let traits = rollup_traits(cx, bound_predicate.bounds, "these bounds contain repeated elements");
+ for (trait_ref, span) in traits {
+ let key = (path.res, trait_ref);
+ if where_predicates.contains(&key) {
span_lint_and_help(
cx,
TRAIT_DUPLICATION_IN_BOUNDS,
- *span_direct,
+ span,
"this trait bound is already specified in the where clause",
None,
"consider removing this trait bound",
- );
- }
- else {
- trait_resolutions_direct.push((res_where, span_where));
+ );
}
}
}
}
}
-#[derive(PartialEq, Eq, Hash, Debug)]
+#[derive(Clone, PartialEq, Eq, Hash, Debug)]
struct ComparableTraitRef(Res, Vec<Res>);
-
-fn check_bounds_or_where_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
- if gen.span.from_expansion() {
- return;
- }
-
- for predicate in gen.predicates {
- if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate {
- let msg = if predicate.in_where_clause() {
- "these where clauses contain repeated elements"
- } else {
- "these bounds contain repeated elements"
- };
- rollup_traits(cx, bound_predicate.bounds, msg);
- }
+impl Default for ComparableTraitRef {
+ fn default() -> Self {
+ Self(Res::Err, Vec::new())
}
}
)
}
-fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
+fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) -> Vec<(ComparableTraitRef, Span)> {
let mut map = FxHashMap::default();
let mut repeated_res = false;
}
};
+ let mut i = 0usize;
for bound in bounds.iter().filter_map(only_comparable_trait_refs) {
let (comparable_bound, span_direct) = bound;
- if map.insert(comparable_bound, span_direct).is_some() {
- repeated_res = true;
+ match map.entry(comparable_bound) {
+ Entry::Occupied(_) => repeated_res = true,
+ Entry::Vacant(e) => {
+ e.insert((span_direct, i));
+ i += 1;
+ },
}
}
+ // Put bounds in source order
+ let mut comparable_bounds = vec![Default::default(); map.len()];
+ for (k, (v, i)) in map {
+ comparable_bounds[i] = (k, v);
+ }
+
if_chain! {
if repeated_res;
if let [first_trait, .., last_trait] = bounds;
then {
let all_trait_span = first_trait.span().to(last_trait.span());
- let mut traits = map.values()
- .filter_map(|span| snippet_opt(cx, *span))
+ let traits = comparable_bounds.iter()
+ .filter_map(|&(_, span)| snippet_opt(cx, span))
.collect::<Vec<_>>();
- traits.sort_unstable();
let traits = traits.join(" + ");
span_lint_and_sugg(
);
}
}
+
+ comparable_bounds
}
mod transmute_ref_to_ref;
mod transmute_undefined_repr;
mod transmutes_expressible_as_ptr_casts;
+mod transmuting_null;
mod unsound_collection_transmute;
mod useless_transmute;
mod utils;
"transmute to or from a type with an undefined representation"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for transmute calls which would receive a null pointer.
+ ///
+ /// ### Why is this bad?
+ /// Transmuting a null pointer is undefined behavior.
+ ///
+ /// ### Known problems
+ /// Not all cases can be detected at the moment of this writing.
+ /// For example, variables which hold a null pointer and are then fed to a `transmute`
+ /// call, aren't detectable yet.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
+ /// ```
+ #[clippy::version = "1.35.0"]
+ pub TRANSMUTING_NULL,
+ correctness,
+ "transmutes from a null pointer to a reference, which is undefined behavior"
+}
+
pub struct Transmute {
msrv: Option<RustcVersion>,
}
UNSOUND_COLLECTION_TRANSMUTE,
TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
TRANSMUTE_UNDEFINED_REPR,
+ TRANSMUTING_NULL,
]);
impl Transmute {
#[must_use]
let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
| crosspointer_transmute::check(cx, e, from_ty, to_ty)
+ | transmuting_null::check(cx, e, arg, to_ty)
| transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
| transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
| transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
use rustc_lint::LateContext;
use rustc_middle::ty::subst::{Subst, SubstsRef};
use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy};
-use rustc_span::Span;
+use rustc_span::DUMMY_SP;
-#[allow(clippy::too_many_lines)]
+#[expect(clippy::too_many_lines)]
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
e: &'tcx Expr<'_>,
let mut to_ty = cx.tcx.erase_regions(to_ty_orig);
while from_ty != to_ty {
- match reduce_refs(cx, e.span, from_ty, to_ty) {
- ReducedTys::FromFatPtr {
- unsized_ty,
- to_ty: to_sub_ty,
- } => match reduce_ty(cx, to_sub_ty) {
- ReducedTy::TypeErasure => break,
- ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break,
- ReducedTy::Ref(to_sub_ty) => {
- from_ty = unsized_ty;
- to_ty = to_sub_ty;
- continue;
- },
- _ => {
- span_lint_and_then(
- cx,
- TRANSMUTE_UNDEFINED_REPR,
- e.span,
- &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
- |diag| {
- if from_ty_orig.peel_refs() != unsized_ty {
- diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
- }
- },
- );
- return true;
- },
+ let reduced_tys = reduce_refs(cx, from_ty, to_ty);
+ match (reduce_ty(cx, reduced_tys.from_ty), reduce_ty(cx, reduced_tys.to_ty)) {
+ // Various forms of type erasure.
+ (ReducedTy::TypeErasure { raw_ptr_only: false }, _)
+ | (_, ReducedTy::TypeErasure { raw_ptr_only: false }) => return false,
+ (ReducedTy::TypeErasure { .. }, _) if reduced_tys.from_raw_ptr => return false,
+ (_, ReducedTy::TypeErasure { .. }) if reduced_tys.to_raw_ptr => return false,
+
+ // `Repr(C)` <-> unordered type.
+ // If the first field of the `Repr(C)` type matches then the transmute is ok
+ (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::UnorderedFields(to_sub_ty))
+ | (ReducedTy::UnorderedFields(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty))) => {
+ from_ty = from_sub_ty;
+ to_ty = to_sub_ty;
+ continue;
},
- ReducedTys::ToFatPtr {
- unsized_ty,
- from_ty: from_sub_ty,
- } => match reduce_ty(cx, from_sub_ty) {
- ReducedTy::TypeErasure => break,
- ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break,
- ReducedTy::Ref(from_sub_ty) => {
- from_ty = from_sub_ty;
- to_ty = unsized_ty;
- continue;
- },
- _ => {
- span_lint_and_then(
- cx,
- TRANSMUTE_UNDEFINED_REPR,
- e.span,
- &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
- |diag| {
- if to_ty_orig.peel_refs() != unsized_ty {
- diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
- }
- },
- );
- return true;
- },
+ (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::Other(to_sub_ty)) if reduced_tys.to_fat_ptr => {
+ from_ty = from_sub_ty;
+ to_ty = to_sub_ty;
+ continue;
},
- ReducedTys::ToPtr {
- from_ty: from_sub_ty,
- to_ty: to_sub_ty,
- } => match reduce_ty(cx, from_sub_ty) {
- ReducedTy::UnorderedFields(from_ty) => {
- span_lint_and_then(
- cx,
- TRANSMUTE_UNDEFINED_REPR,
- e.span,
- &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
- |diag| {
- if from_ty_orig.peel_refs() != from_ty {
- diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
- }
- },
- );
- return true;
- },
- ReducedTy::Ref(from_sub_ty) => {
- from_ty = from_sub_ty;
- to_ty = to_sub_ty;
- continue;
- },
- _ => break,
+ (ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty)))
+ if reduced_tys.from_fat_ptr =>
+ {
+ from_ty = from_sub_ty;
+ to_ty = to_sub_ty;
+ continue;
},
- ReducedTys::FromPtr {
- from_ty: from_sub_ty,
- to_ty: to_sub_ty,
- } => match reduce_ty(cx, to_sub_ty) {
- ReducedTy::UnorderedFields(to_ty) => {
- span_lint_and_then(
- cx,
- TRANSMUTE_UNDEFINED_REPR,
- e.span,
- &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
- |diag| {
- if to_ty_orig.peel_refs() != to_ty {
- diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
- }
- },
- );
- return true;
- },
- ReducedTy::Ref(to_sub_ty) => {
- from_ty = from_sub_ty;
- to_ty = to_sub_ty;
- continue;
- },
- _ => break,
+
+ // ptr <-> ptr
+ (ReducedTy::Other(from_sub_ty), ReducedTy::Other(to_sub_ty))
+ if matches!(from_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_))
+ && matches!(to_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_)) =>
+ {
+ from_ty = from_sub_ty;
+ to_ty = to_sub_ty;
+ continue;
},
- ReducedTys::Other {
- from_ty: from_sub_ty,
- to_ty: to_sub_ty,
- } => match (reduce_ty(cx, from_sub_ty), reduce_ty(cx, to_sub_ty)) {
- (ReducedTy::TypeErasure, _) | (_, ReducedTy::TypeErasure) => return false,
- (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
- let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs))
+
+ // fat ptr <-> (*size, *size)
+ (ReducedTy::Other(_), ReducedTy::UnorderedFields(to_ty))
+ if reduced_tys.from_fat_ptr && is_size_pair(to_ty) =>
+ {
+ return false;
+ },
+ (ReducedTy::UnorderedFields(from_ty), ReducedTy::Other(_))
+ if reduced_tys.to_fat_ptr && is_size_pair(from_ty) =>
+ {
+ return false;
+ },
+
+ // fat ptr -> some struct | some struct -> fat ptr
+ (ReducedTy::Other(_), _) if reduced_tys.from_fat_ptr => {
+ span_lint_and_then(
+ cx,
+ TRANSMUTE_UNDEFINED_REPR,
+ e.span,
+ &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
+ |diag| {
+ if from_ty_orig.peel_refs() != from_ty.peel_refs() {
+ diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
+ }
+ },
+ );
+ return true;
+ },
+ (_, ReducedTy::Other(_)) if reduced_tys.to_fat_ptr => {
+ span_lint_and_then(
+ cx,
+ TRANSMUTE_UNDEFINED_REPR,
+ e.span,
+ &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
+ |diag| {
+ if to_ty_orig.peel_refs() != to_ty.peel_refs() {
+ diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
+ }
+ },
+ );
+ return true;
+ },
+
+ (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
+ let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs))
= (from_ty.kind(), to_ty.kind())
&& from_def == to_def
{
} else {
None
};
- span_lint_and_then(
- cx,
- TRANSMUTE_UNDEFINED_REPR,
- e.span,
- &format!(
- "transmute from `{}` to `{}`, both of which have an undefined layout",
- from_ty_orig, to_ty_orig
- ),
- |diag| {
- if let Some(same_adt_did) = same_adt_did {
- diag.note(&format!(
- "two instances of the same generic type (`{}`) may have different layouts",
- cx.tcx.item_name(same_adt_did)
- ));
- } else {
- if from_ty_orig.peel_refs() != from_ty {
- diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
- }
- if to_ty_orig.peel_refs() != to_ty {
- diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
- }
- }
- },
- );
- return true;
- },
- (
- ReducedTy::UnorderedFields(from_ty),
- ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
- ) => {
- span_lint_and_then(
- cx,
- TRANSMUTE_UNDEFINED_REPR,
- e.span,
- &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
- |diag| {
+ span_lint_and_then(
+ cx,
+ TRANSMUTE_UNDEFINED_REPR,
+ e.span,
+ &format!(
+ "transmute from `{}` to `{}`, both of which have an undefined layout",
+ from_ty_orig, to_ty_orig
+ ),
+ |diag| {
+ if let Some(same_adt_did) = same_adt_did {
+ diag.note(&format!(
+ "two instances of the same generic type (`{}`) may have different layouts",
+ cx.tcx.item_name(same_adt_did)
+ ));
+ } else {
if from_ty_orig.peel_refs() != from_ty {
diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
}
- },
- );
- return true;
- },
- (
- ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
- ReducedTy::UnorderedFields(to_ty),
- ) => {
- span_lint_and_then(
- cx,
- TRANSMUTE_UNDEFINED_REPR,
- e.span,
- &format!("transmute into `{}` which has an undefined layout", to_ty_orig),
- |diag| {
if to_ty_orig.peel_refs() != to_ty {
diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
}
- },
- );
- return true;
- },
- (ReducedTy::Ref(from_sub_ty), ReducedTy::Ref(to_sub_ty)) => {
- from_ty = from_sub_ty;
- to_ty = to_sub_ty;
- continue;
- },
- (
- ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param,
- ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param,
- )
- | (
- ReducedTy::UnorderedFields(_) | ReducedTy::Param,
- ReducedTy::UnorderedFields(_) | ReducedTy::Param,
- ) => break,
+ }
+ },
+ );
+ return true;
+ },
+ (
+ ReducedTy::UnorderedFields(from_ty),
+ ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true },
+ ) => {
+ span_lint_and_then(
+ cx,
+ TRANSMUTE_UNDEFINED_REPR,
+ e.span,
+ &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
+ |diag| {
+ if from_ty_orig.peel_refs() != from_ty {
+ diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
+ }
+ },
+ );
+ return true;
+ },
+ (
+ ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true },
+ ReducedTy::UnorderedFields(to_ty),
+ ) => {
+ span_lint_and_then(
+ cx,
+ TRANSMUTE_UNDEFINED_REPR,
+ e.span,
+ &format!("transmute into `{}` which has an undefined layout", to_ty_orig),
+ |diag| {
+ if to_ty_orig.peel_refs() != to_ty {
+ diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
+ }
+ },
+ );
+ return true;
+ },
+ (
+ ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true },
+ ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true },
+ )
+ | (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => {
+ break;
},
}
}
false
}
-enum ReducedTys<'tcx> {
- FromFatPtr { unsized_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
- ToFatPtr { unsized_ty: Ty<'tcx>, from_ty: Ty<'tcx> },
- ToPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
- FromPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
- Other { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
+#[expect(clippy::struct_excessive_bools)]
+struct ReducedTys<'tcx> {
+ from_ty: Ty<'tcx>,
+ to_ty: Ty<'tcx>,
+ from_raw_ptr: bool,
+ to_raw_ptr: bool,
+ from_fat_ptr: bool,
+ to_fat_ptr: bool,
}
/// Remove references so long as both types are references.
-fn reduce_refs<'tcx>(
- cx: &LateContext<'tcx>,
- span: Span,
- mut from_ty: Ty<'tcx>,
- mut to_ty: Ty<'tcx>,
-) -> ReducedTys<'tcx> {
- loop {
- return match (from_ty.kind(), to_ty.kind()) {
+fn reduce_refs<'tcx>(cx: &LateContext<'tcx>, mut from_ty: Ty<'tcx>, mut to_ty: Ty<'tcx>) -> ReducedTys<'tcx> {
+ let mut from_raw_ptr = false;
+ let mut to_raw_ptr = false;
+ let (from_fat_ptr, to_fat_ptr) = loop {
+ break match (from_ty.kind(), to_ty.kind()) {
(
&(ty::Ref(_, from_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. })),
&(ty::Ref(_, to_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. })),
) => {
+ from_raw_ptr = matches!(*from_ty.kind(), ty::RawPtr(_));
from_ty = from_sub_ty;
+ to_raw_ptr = matches!(*to_ty.kind(), ty::RawPtr(_));
to_ty = to_sub_ty;
continue;
},
(&(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })), _)
- if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
+ if !unsized_ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env) =>
{
- ReducedTys::FromFatPtr { unsized_ty, to_ty }
+ (true, false)
},
(_, &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })))
- if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
+ if !unsized_ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env) =>
{
- ReducedTys::ToFatPtr { unsized_ty, from_ty }
+ (false, true)
},
- (&(ty::Ref(_, from_ty, _) | ty::RawPtr(TypeAndMut { ty: from_ty, .. })), _) => {
- ReducedTys::FromPtr { from_ty, to_ty }
- },
- (_, &(ty::Ref(_, to_ty, _) | ty::RawPtr(TypeAndMut { ty: to_ty, .. }))) => {
- ReducedTys::ToPtr { from_ty, to_ty }
- },
- _ => ReducedTys::Other { from_ty, to_ty },
+ _ => (false, false),
};
+ };
+ ReducedTys {
+ from_ty,
+ to_ty,
+ from_raw_ptr,
+ to_raw_ptr,
+ from_fat_ptr,
+ to_fat_ptr,
}
}
enum ReducedTy<'tcx> {
/// The type can be used for type erasure.
- TypeErasure,
+ TypeErasure { raw_ptr_only: bool },
/// The type is a struct containing either zero non-zero sized fields, or multiple non-zero
/// sized fields with a defined order.
- OrderedFields(Ty<'tcx>),
+ /// The second value is the first non-zero sized type.
+ OrderedFields(Ty<'tcx>, Option<Ty<'tcx>>),
/// The type is a struct containing multiple non-zero sized fields with no defined order.
UnorderedFields(Ty<'tcx>),
- /// The type is a reference to the contained type.
- Ref(Ty<'tcx>),
- /// The type is a generic parameter.
- Param,
/// Any other type.
Other(Ty<'tcx>),
}
loop {
ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty);
return match *ty.kind() {
- ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::TypeErasure,
+ ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => {
+ ReducedTy::TypeErasure { raw_ptr_only: false }
+ },
ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
ty = sub_ty;
continue;
},
- ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure,
+ ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure { raw_ptr_only: false },
ty::Tuple(args) => {
let mut iter = args.iter();
let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
- return ReducedTy::OrderedFields(ty);
+ return ReducedTy::OrderedFields(ty, None);
};
if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
ty = sized_ty;
.iter()
.map(|f| cx.tcx.bound_type_of(f.did).subst(cx.tcx, substs));
let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
- return ReducedTy::TypeErasure;
+ return ReducedTy::TypeErasure { raw_ptr_only: false };
};
if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
ty = sized_ty;
continue;
}
if def.repr().inhibit_struct_field_reordering_opt() {
- ReducedTy::OrderedFields(ty)
+ ReducedTy::OrderedFields(ty, Some(sized_ty))
} else {
ReducedTy::UnorderedFields(ty)
}
},
ty::Adt(def, _) if def.is_enum() && (def.variants().is_empty() || is_c_void(cx, ty)) => {
- ReducedTy::TypeErasure
+ ReducedTy::TypeErasure { raw_ptr_only: false }
},
// TODO: Check if the conversion to or from at least one of a union's fields is valid.
- ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure,
- ty::Foreign(_) => ReducedTy::TypeErasure,
- ty::Ref(_, ty, _) => ReducedTy::Ref(ty),
- ty::RawPtr(ty) => ReducedTy::Ref(ty.ty),
- ty::Param(_) => ReducedTy::Param,
+ ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure { raw_ptr_only: false },
+ ty::Foreign(_) | ty::Param(_) => ReducedTy::TypeErasure { raw_ptr_only: false },
+ ty::Int(_) | ty::Uint(_) => ReducedTy::TypeErasure { raw_ptr_only: true },
_ => ReducedTy::Other(ty),
};
}
--- /dev/null
+use clippy_utils::consts::{constant_context, Constant};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_path_diagnostic_item;
+use if_chain::if_chain;
+use rustc_ast::LitKind;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::Ty;
+use rustc_span::symbol::sym;
+
+use super::TRANSMUTING_NULL;
+
+const LINT_MSG: &str = "transmuting a known null pointer into a reference";
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, to_ty: Ty<'tcx>) -> bool {
+ if !to_ty.is_ref() {
+ return false;
+ }
+
+ // 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) = arg.kind;
+ if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg);
+ if x == 0;
+ then {
+ span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
+ return true;
+ }
+ }
+
+ // Catching:
+ // `std::mem::transmute(0 as *const i32)`
+ if_chain! {
+ 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 {
+ span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
+ return true;
+ }
+ }
+
+ // Catching:
+ // `std::mem::transmute(std::ptr::null::<i32>())`
+ if_chain! {
+ if let ExprKind::Call(func1, []) = arg.kind;
+ if is_path_diagnostic_item(cx, func1, sym::ptr_null);
+ then {
+ span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
+ return true;
+ }
+ }
+
+ // FIXME:
+ // Also catch transmutations of variables which are known nulls.
+ // To do this, MIR const propagation seems to be the better tool.
+ // Whenever MIR const prop routines are more developed, this will
+ // become available. As of this writing (25/03/19) it is not yet.
+ false
+}
+++ /dev/null
-use clippy_utils::consts::{constant_context, Constant};
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::is_expr_diagnostic_item;
-use if_chain::if_chain;
-use rustc_ast::LitKind;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::lint::in_external_macro;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::sym;
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for transmute calls which would receive a null pointer.
- ///
- /// ### Why is this bad?
- /// Transmuting a null pointer is undefined behavior.
- ///
- /// ### Known problems
- /// Not all cases can be detected at the moment of this writing.
- /// For example, variables which hold a null pointer and are then fed to a `transmute`
- /// call, aren't detectable yet.
- ///
- /// ### Example
- /// ```rust
- /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
- /// ```
- #[clippy::version = "1.35.0"]
- pub TRANSMUTING_NULL,
- correctness,
- "transmutes from a null pointer to a reference, which is undefined behavior"
-}
-
-declare_lint_pass!(TransmutingNull => [TRANSMUTING_NULL]);
-
-const LINT_MSG: &str = "transmuting a known null pointer into a reference";
-
-impl<'tcx> LateLintPass<'tcx> for TransmutingNull {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if in_external_macro(cx.sess(), expr.span) {
- return;
- }
-
- if_chain! {
- if let ExprKind::Call(func, [arg]) = expr.kind;
- if is_expr_diagnostic_item(cx, func, sym::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) = arg.kind;
- if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg);
- if x == 0;
- 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) = arg.kind;
- if let ExprKind::Lit(ref lit) = inner_expr.kind;
- if let LitKind::Int(0, _) = lit.node;
- then {
- span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
- }
- }
-
- // Catching:
- // `std::mem::transmute(std::ptr::null::<i32>())`
- if_chain! {
- if let ExprKind::Call(func1, []) = arg.kind;
- if is_expr_diagnostic_item(cx, func1, sym::ptr_null);
- then {
- span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
- }
- }
-
- // FIXME:
- // Also catch transmutations of variables which are known nulls.
- // To do this, MIR const propagation seems to be the better tool.
- // Whenever MIR const prop routines are more developed, this will
- // become available. As of this writing (25/03/19) it is not yet.
- }
- }
- }
-}
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_lint_allowed;
+use clippy_utils::macros::span_is_local;
use clippy_utils::source::snippet;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
}
fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
+ if !span_is_local(span) {
+ return;
+ }
+
let string = snippet(cx, span, "");
if string.chars().any(|c| ['\u{200B}', '\u{ad}', '\u{2060}'].contains(&c)) {
span_lint_and_sugg(
Applicability::MachineApplicable,
);
}
+
if string.chars().any(|c| c as u32 > 0x7F) {
span_lint_and_sugg(
cx,
Applicability::MachineApplicable,
);
}
+
if is_lint_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) {
span_lint_and_sugg(
cx,
/// let mut vec: Vec<MaybeUninit<T>> = Vec::with_capacity(1000);
/// vec.set_len(1000); // `MaybeUninit` can be uninitialized
/// ```
- /// 3. If you are on nightly, `Vec::spare_capacity_mut()` is available:
+ /// 3. If you are on 1.60.0 or later, `Vec::spare_capacity_mut()` is available:
/// ```rust,ignore
/// let mut vec: Vec<u8> = Vec::with_capacity(1000);
/// let remaining = vec.spare_capacity_mut(); // `&mut [MaybeUninit<u8>]`
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-
-declare_clippy_lint! {
- /// ### What it does
- /// Detects `().hash(_)`.
- ///
- /// ### Why is this bad?
- /// Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op.
- ///
- /// ### Example
- /// ```rust
- /// # use std::hash::Hash;
- /// # use std::collections::hash_map::DefaultHasher;
- /// # enum Foo { Empty, WithValue(u8) }
- /// # use Foo::*;
- /// # let mut state = DefaultHasher::new();
- /// # let my_enum = Foo::Empty;
- /// match my_enum {
- /// Empty => ().hash(&mut state),
- /// WithValue(x) => x.hash(&mut state),
- /// }
- /// ```
- /// Use instead:
- /// ```rust
- /// # use std::hash::Hash;
- /// # use std::collections::hash_map::DefaultHasher;
- /// # enum Foo { Empty, WithValue(u8) }
- /// # use Foo::*;
- /// # let mut state = DefaultHasher::new();
- /// # let my_enum = Foo::Empty;
- /// match my_enum {
- /// Empty => 0_u8.hash(&mut state),
- /// WithValue(x) => x.hash(&mut state),
- /// }
- /// ```
- #[clippy::version = "1.58.0"]
- pub UNIT_HASH,
- correctness,
- "hashing a unit value, which does nothing"
-}
-declare_lint_pass!(UnitHash => [UNIT_HASH]);
-
-impl<'tcx> LateLintPass<'tcx> for UnitHash {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
- if_chain! {
- if let ExprKind::MethodCall(name_ident, args, _) = &expr.kind;
- if name_ident.ident.name == sym::hash;
- if let [recv, state_param] = args;
- if cx.typeck_results().expr_ty(recv).is_unit();
- then {
- span_lint_and_then(
- cx,
- UNIT_HASH,
- expr.span,
- "this call to `hash` on the unit type will do nothing",
- |diag| {
- diag.span_suggestion(
- expr.span,
- "remove the call to `hash` or consider using",
- format!(
- "0_u8.hash({})",
- snippet(cx, state_param.span, ".."),
- ),
- Applicability::MaybeIncorrect,
- );
- diag.note("the implementation of `Hash` for `()` is a no-op");
- }
- );
- }
- }
- }
-}
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{self, subst::GenericArgKind};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-use rustc_span::symbol::Ident;
-use std::iter;
-
-declare_clippy_lint! {
- /// ### What it does
- /// Detects uses of `Vec::sort_by` passing in a closure
- /// which compares the two arguments, either directly or indirectly.
- ///
- /// ### Why is this bad?
- /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
- /// possible) than to use `Vec::sort_by` and a more complicated
- /// closure.
- ///
- /// ### Known problems
- /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
- /// imported by a use statement, then it will need to be added manually.
- ///
- /// ### Example
- /// ```rust
- /// # struct A;
- /// # impl A { fn foo(&self) {} }
- /// # let mut vec: Vec<A> = Vec::new();
- /// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
- /// ```
- /// Use instead:
- /// ```rust
- /// # struct A;
- /// # impl A { fn foo(&self) {} }
- /// # let mut vec: Vec<A> = Vec::new();
- /// vec.sort_by_key(|a| a.foo());
- /// ```
- #[clippy::version = "1.46.0"]
- pub UNNECESSARY_SORT_BY,
- complexity,
- "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
-}
-
-declare_lint_pass!(UnnecessarySortBy => [UNNECESSARY_SORT_BY]);
-
-enum LintTrigger {
- Sort(SortDetection),
- SortByKey(SortByKeyDetection),
-}
-
-struct SortDetection {
- vec_name: String,
- unstable: bool,
-}
-
-struct SortByKeyDetection {
- vec_name: String,
- closure_arg: String,
- closure_body: String,
- reverse: bool,
- unstable: bool,
-}
-
-/// Detect if the two expressions are mirrored (identical, except one
-/// contains a and the other replaces it with b)
-fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident: &Ident) -> bool {
- match (&a_expr.kind, &b_expr.kind) {
- // Two boxes with mirrored contents
- (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => {
- mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
- },
- // Two arrays with mirrored contents
- (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => {
- iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
- },
- // The two exprs are function calls.
- // Check to see that the function itself and its arguments are mirrored
- (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => {
- mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
- && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
- },
- // The two exprs are method calls.
- // Check to see that the function is the same and the arguments are mirrored
- // This is enough because the receiver of the method is listed in the arguments
- (ExprKind::MethodCall(left_segment, left_args, _), ExprKind::MethodCall(right_segment, right_args, _)) => {
- left_segment.ident == right_segment.ident
- && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
- },
- // Two tuples with mirrored contents
- (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => {
- iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
- },
- // Two binary ops, which are the same operation and which have mirrored arguments
- (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => {
- left_op.node == right_op.node
- && mirrored_exprs(left_left, a_ident, right_left, b_ident)
- && mirrored_exprs(left_right, a_ident, right_right, b_ident)
- },
- // Two unary ops, which are the same operation and which have the same argument
- (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => {
- left_op == right_op && mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
- },
- // The two exprs are literals of some kind
- (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node,
- (ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(left, a_ident, right, b_ident),
- (ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => {
- mirrored_exprs(left_block, a_ident, right_block, b_ident)
- },
- (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => {
- left_ident.name == right_ident.name && mirrored_exprs(left_expr, a_ident, right_expr, right_ident)
- },
- // Two paths: either one is a and the other is b, or they're identical to each other
- (
- ExprKind::Path(QPath::Resolved(
- _,
- Path {
- segments: left_segments,
- ..
- },
- )),
- ExprKind::Path(QPath::Resolved(
- _,
- Path {
- segments: right_segments,
- ..
- },
- )),
- ) => {
- (iter::zip(*left_segments, *right_segments).all(|(left, right)| left.ident == right.ident)
- && left_segments
- .iter()
- .all(|seg| &seg.ident != a_ident && &seg.ident != b_ident))
- || (left_segments.len() == 1
- && &left_segments[0].ident == a_ident
- && right_segments.len() == 1
- && &right_segments[0].ident == b_ident)
- },
- // Matching expressions, but one or both is borrowed
- (
- ExprKind::AddrOf(left_kind, Mutability::Not, left_expr),
- ExprKind::AddrOf(right_kind, Mutability::Not, right_expr),
- ) => left_kind == right_kind && mirrored_exprs(left_expr, a_ident, right_expr, b_ident),
- (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => mirrored_exprs(a_expr, a_ident, right_expr, b_ident),
- (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(left_expr, a_ident, b_expr, b_ident),
- _ => false,
- }
-}
-
-fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
- if_chain! {
- if let ExprKind::MethodCall(name_ident, args, _) = &expr.kind;
- if let name = name_ident.ident.name.to_ident_string();
- if name == "sort_by" || name == "sort_unstable_by";
- if let [vec, Expr { kind: ExprKind::Closure(Closure { body: closure_body_id, .. }), .. }] = args;
- if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::Vec);
- if let closure_body = cx.tcx.hir().body(*closure_body_id);
- if let &[
- Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},
- Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. }
- ] = &closure_body.params;
- if let ExprKind::MethodCall(method_path, [ref left_expr, ref right_expr], _) = &closure_body.value.kind;
- if method_path.ident.name == sym::cmp;
- then {
- let (closure_body, closure_arg, reverse) = if mirrored_exprs(
- left_expr,
- left_ident,
- right_expr,
- right_ident
- ) {
- (Sugg::hir(cx, left_expr, "..").to_string(), left_ident.name.to_string(), false)
- } else if mirrored_exprs(left_expr, right_ident, right_expr, left_ident) {
- (Sugg::hir(cx, left_expr, "..").to_string(), right_ident.name.to_string(), true)
- } else {
- return None;
- };
- let vec_name = Sugg::hir(cx, &args[0], "..").to_string();
- let unstable = name == "sort_unstable_by";
-
- if_chain! {
- if let ExprKind::Path(QPath::Resolved(_, Path {
- segments: [PathSegment { ident: left_name, .. }], ..
- })) = &left_expr.kind;
- if left_name == left_ident;
- if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| {
- implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[])
- });
- then {
- return Some(LintTrigger::Sort(SortDetection { vec_name, unstable }));
- }
- }
-
- if !expr_borrows(cx, left_expr) {
- return Some(LintTrigger::SortByKey(SortByKeyDetection {
- vec_name,
- closure_arg,
- closure_body,
- reverse,
- unstable,
- }));
- }
- }
- }
-
- None
-}
-
-fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
- let ty = cx.typeck_results().expr_ty(expr);
- matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
-}
-
-impl LateLintPass<'_> for UnnecessarySortBy {
- fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
- match detect_lint(cx, expr) {
- Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg(
- cx,
- UNNECESSARY_SORT_BY,
- expr.span,
- "use Vec::sort_by_key here instead",
- "try",
- format!(
- "{}.sort{}_by_key(|{}| {})",
- trigger.vec_name,
- if trigger.unstable { "_unstable" } else { "" },
- trigger.closure_arg,
- if trigger.reverse {
- format!("std::cmp::Reverse({})", trigger.closure_body)
- } else {
- trigger.closure_body.to_string()
- },
- ),
- if trigger.reverse {
- Applicability::MaybeIncorrect
- } else {
- Applicability::MachineApplicable
- },
- ),
- Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg(
- cx,
- UNNECESSARY_SORT_BY,
- expr.span,
- "use Vec::sort here instead",
- "try",
- format!(
- "{}.sort{}()",
- trigger.vec_name,
- if trigger.unstable { "_unstable" } else { "" },
- ),
- Applicability::MachineApplicable,
- ),
- None => {},
- }
- }
-}
(
ret_expr.span,
if inner_type.is_unit() {
- "".to_string()
+ String::new()
} else {
snippet(cx, arg.span.source_callsite(), "..").to_string()
}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::{match_type, peel_mid_ty_refs_is_mutable};
+use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, paths, peel_ref_operators};
+use rustc_ast::Mutability;
+use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::lang_items::LangItem;
+use rustc_hir::{Block, Expr, ExprKind, HirId, Local, Node, PatKind, PathSegment, StmtKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the creation of a `peekable` iterator that is never `.peek()`ed
+ ///
+ /// ### Why is this bad?
+ /// Creating a peekable iterator without using any of its methods is likely a mistake,
+ /// or just a leftover after a refactor.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let collection = vec![1, 2, 3];
+ /// let iter = collection.iter().peekable();
+ ///
+ /// for item in iter {
+ /// // ...
+ /// }
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// let collection = vec![1, 2, 3];
+ /// let iter = collection.iter();
+ ///
+ /// for item in iter {
+ /// // ...
+ /// }
+ /// ```
+ #[clippy::version = "1.64.0"]
+ pub UNUSED_PEEKABLE,
+ suspicious,
+ "creating a peekable iterator without using any of its methods"
+}
+
+declare_lint_pass!(UnusedPeekable => [UNUSED_PEEKABLE]);
+
+impl<'tcx> LateLintPass<'tcx> for UnusedPeekable {
+ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
+ // Don't lint `Peekable`s returned from a block
+ if let Some(expr) = block.expr
+ && let Some(ty) = cx.typeck_results().expr_ty_opt(peel_ref_operators(cx, expr))
+ && match_type(cx, ty, &paths::PEEKABLE)
+ {
+ return;
+ }
+
+ for (idx, stmt) in block.stmts.iter().enumerate() {
+ if !stmt.span.from_expansion()
+ && let StmtKind::Local(local) = stmt.kind
+ && let PatKind::Binding(_, binding, ident, _) = local.pat.kind
+ && let Some(init) = local.init
+ && !init.span.from_expansion()
+ && let Some(ty) = cx.typeck_results().expr_ty_opt(init)
+ && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty)
+ && match_type(cx, ty, &paths::PEEKABLE)
+ {
+ let mut vis = PeekableVisitor::new(cx, binding);
+
+ if idx + 1 == block.stmts.len() && block.expr.is_none() {
+ return;
+ }
+
+ for stmt in &block.stmts[idx..] {
+ vis.visit_stmt(stmt);
+ }
+
+ if let Some(expr) = block.expr {
+ vis.visit_expr(expr);
+ }
+
+ if !vis.found_peek_call {
+ span_lint_and_help(
+ cx,
+ UNUSED_PEEKABLE,
+ ident.span,
+ "`peek` never called on `Peekable` iterator",
+ None,
+ "consider removing the call to `peekable`"
+ );
+ }
+ }
+ }
+ }
+}
+
+struct PeekableVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ expected_hir_id: HirId,
+ found_peek_call: bool,
+}
+
+impl<'a, 'tcx> PeekableVisitor<'a, 'tcx> {
+ fn new(cx: &'a LateContext<'tcx>, expected_hir_id: HirId) -> Self {
+ Self {
+ cx,
+ expected_hir_id,
+ found_peek_call: false,
+ }
+ }
+}
+
+impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
+ fn visit_expr(&mut self, ex: &'_ Expr<'_>) {
+ if self.found_peek_call {
+ return;
+ }
+
+ if path_to_local_id(ex, self.expected_hir_id) {
+ for (_, node) in self.cx.tcx.hir().parent_iter(ex.hir_id) {
+ match node {
+ Node::Expr(expr) => {
+ match expr.kind {
+ // some_function(peekable)
+ //
+ // If the Peekable is passed to a function, stop
+ ExprKind::Call(_, args) => {
+ if let Some(func_did) = fn_def_id(self.cx, expr)
+ && let Ok(into_iter_did) = self
+ .cx
+ .tcx
+ .lang_items()
+ .require(LangItem::IntoIterIntoIter)
+ && func_did == into_iter_did
+ {
+ // Probably a for loop desugar, stop searching
+ return;
+ }
+
+ if args.iter().any(|arg| {
+ matches!(arg.kind, ExprKind::Path(_)) && arg_is_mut_peekable(self.cx, arg)
+ }) {
+ self.found_peek_call = true;
+ return;
+ }
+ },
+ // Catch anything taking a Peekable mutably
+ ExprKind::MethodCall(
+ PathSegment {
+ ident: method_name_ident,
+ ..
+ },
+ [self_arg, remaining_args @ ..],
+ _,
+ ) => {
+ let method_name = method_name_ident.name.as_str();
+
+ // `Peekable` methods
+ if matches!(method_name, "peek" | "peek_mut" | "next_if" | "next_if_eq")
+ && arg_is_mut_peekable(self.cx, self_arg)
+ {
+ self.found_peek_call = true;
+ return;
+ }
+
+ // foo.some_method() excluding Iterator methods
+ if remaining_args.iter().any(|arg| arg_is_mut_peekable(self.cx, arg))
+ && !is_trait_method(self.cx, expr, sym::Iterator)
+ {
+ self.found_peek_call = true;
+ return;
+ }
+
+ // foo.by_ref(), keep checking for `peek`
+ if method_name == "by_ref" {
+ continue;
+ }
+
+ return;
+ },
+ ExprKind::AddrOf(_, Mutability::Mut, _) | ExprKind::Unary(..) | ExprKind::DropTemps(_) => {
+ },
+ ExprKind::AddrOf(_, Mutability::Not, _) => return,
+ _ => {
+ self.found_peek_call = true;
+ return;
+ },
+ }
+ },
+ Node::Local(Local { init: Some(init), .. }) => {
+ if arg_is_mut_peekable(self.cx, init) {
+ self.found_peek_call = true;
+ return;
+ }
+
+ break;
+ },
+ Node::Stmt(stmt) => match stmt.kind {
+ StmtKind::Expr(_) | StmtKind::Semi(_) => {},
+ _ => {
+ self.found_peek_call = true;
+ return;
+ },
+ },
+ Node::Block(_) | Node::ExprField(_) => {},
+ _ => {
+ break;
+ },
+ }
+ }
+ }
+
+ walk_expr(self, ex);
+ }
+}
+
+fn arg_is_mut_peekable(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool {
+ if let Some(ty) = cx.typeck_results().expr_ty_opt(arg)
+ && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty)
+ && match_type(cx, ty, &paths::PEEKABLE)
+ {
+ true
+ } else {
+ false
+ }
+}
/// ```rust
/// let x = 1f32;
/// ```
- #[clippy::version = "1.62.0"]
+ #[clippy::version = "1.63.0"]
pub UNUSED_ROUNDING,
nursery,
"Uselessly rounding a whole number floating-point literal"
/// Lint: DISALLOWED_SCRIPT_IDENTS.
///
/// The list of unicode scripts allowed to be used in the scope.
- (allowed_scripts: Vec<String> = ["Latin"].iter().map(ToString::to_string).collect()),
+ (allowed_scripts: Vec<String> = vec!["Latin".to_string()]),
/// Lint: NON_SEND_FIELDS_IN_SEND_TY.
///
/// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
///
/// Whether `dbg!` should be allowed in test functions
(allow_dbg_in_tests: bool = false),
+ /// Lint: RESULT_LARGE_ERR
+ ///
+ /// The maximum size of the `Err`-variant in a `Result` returned from a function
+ (large_error_threshold: u64 = 128),
}
/// Search for the configuration file.
attrs.iter().find_map(|attr| {
if_chain! {
// Identify attribute
- if let ast::AttrKind::Normal(ref attr_kind, _) = &attr.kind;
- if let [tool_name, attr_name] = &attr_kind.path.segments[..];
+ if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind;
+ if let [tool_name, attr_name] = &attr_kind.item.path.segments[..];
if tool_name.ident.name == sym::clippy;
if attr_name.ident.name == sym::version;
if let Some(version) = attr.value_str();
let result = cx.lint_store.check_lint_name(
lint_name,
Some(sym::clippy),
- &[Ident::with_dummy_span(sym::clippy)].into_iter().collect(),
+ &std::iter::once(Ident::with_dummy_span(sym::clippy)).collect(),
);
if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
if let Some(group) = get_lint_group(cx, lint_lst[0]) {
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::{match_def_path, paths};
-use if_chain::if_chain;
-use rustc_ast::LitKind;
-use rustc_errors::Applicability;
-use rustc_hir as hir;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Spanned;
-
-declare_clippy_lint! {
- /// ### What it does
- /// Finds occurrences of `Vec::resize(0, an_int)`
- ///
- /// ### Why is this bad?
- /// This is probably an argument inversion mistake.
- ///
- /// ### Example
- /// ```rust
- /// vec!(1, 2, 3, 4, 5).resize(0, 5)
- /// ```
- ///
- /// Use instead:
- /// ```rust
- /// vec!(1, 2, 3, 4, 5).clear()
- /// ```
- #[clippy::version = "1.46.0"]
- pub VEC_RESIZE_TO_ZERO,
- correctness,
- "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake"
-}
-
-declare_lint_pass!(VecResizeToZero => [VEC_RESIZE_TO_ZERO]);
-
-impl<'tcx> LateLintPass<'tcx> for VecResizeToZero {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if_chain! {
- if let hir::ExprKind::MethodCall(path_segment, args, _) = expr.kind;
- if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
- if match_def_path(cx, method_def_id, &paths::VEC_RESIZE) && args.len() == 3;
- if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = args[1].kind;
- if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = args[2].kind;
- then {
- let method_call_span = expr.span.with_lo(path_segment.ident.span.lo());
- span_lint_and_then(
- cx,
- VEC_RESIZE_TO_ZERO,
- expr.span,
- "emptying a vector with `resize`",
- |db| {
- db.help("the arguments may be inverted...");
- db.span_suggestion(
- method_call_span,
- "...or you can empty the vector with",
- "clear()".to_string(),
- Applicability::MaybeIncorrect,
- );
- },
- );
- }
- }
- }
-}
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::paths;
-use clippy_utils::ty::match_type;
-use if_chain::if_chain;
-use rustc_hir::{Expr, ExprKind, QPath};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for use of File::read_to_end and File::read_to_string.
- ///
- /// ### Why is this bad?
- /// `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.
- /// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
- ///
- /// ### Example
- /// ```rust,no_run
- /// # use std::io::Read;
- /// # use std::fs::File;
- /// let mut f = File::open("foo.txt").unwrap();
- /// let mut bytes = Vec::new();
- /// f.read_to_end(&mut bytes).unwrap();
- /// ```
- /// Can be written more concisely as
- /// ```rust,no_run
- /// # use std::fs;
- /// let mut bytes = fs::read("foo.txt").unwrap();
- /// ```
- #[clippy::version = "1.44.0"]
- pub VERBOSE_FILE_READS,
- restriction,
- "use of `File::read_to_end` or `File::read_to_string`"
-}
-
-declare_lint_pass!(VerboseFileReads => [VERBOSE_FILE_READS]);
-
-impl<'tcx> LateLintPass<'tcx> for VerboseFileReads {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
- if is_file_read_to_end(cx, expr) {
- span_lint_and_help(
- cx,
- VERBOSE_FILE_READS,
- expr.span,
- "use of `File::read_to_end`",
- None,
- "consider using `fs::read` instead",
- );
- } else if is_file_read_to_string(cx, expr) {
- span_lint_and_help(
- cx,
- VERBOSE_FILE_READS,
- expr.span,
- "use of `File::read_to_string`",
- None,
- "consider using `fs::read_to_string` instead",
- );
- }
- }
-}
-
-fn is_file_read_to_end<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
- if_chain! {
- if let ExprKind::MethodCall(method_name, [recv, ..], _) = expr.kind;
- if method_name.ident.as_str() == "read_to_end";
- if let ExprKind::Path(QPath::Resolved(None, _)) = &recv.kind;
- let ty = cx.typeck_results().expr_ty(recv);
- if match_type(cx, ty, &paths::FILE);
- then {
- return true
- }
- }
- false
-}
-
-fn is_file_read_to_string<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
- if_chain! {
- if let ExprKind::MethodCall(method_name, exprs, _) = expr.kind;
- if method_name.ident.as_str() == "read_to_string";
- if let ExprKind::Path(QPath::Resolved(None, _)) = &exprs[0].kind;
- let ty = cx.typeck_results().expr_ty(&exprs[0]);
- if match_type(cx, ty, &paths::FILE);
- then {
- return true
- }
- }
- false
-}
use std::ops::{Deref, Range};
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::{snippet_opt, snippet_with_applicability};
+use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
use rustc_ast::ast::{Expr, ExprKind, Impl, Item, ItemKind, MacCall, Path, StrLit, StrStyle};
+use rustc_ast::ptr::P;
use rustc_ast::token::{self, LitKind};
use rustc_ast::tokenstream::TokenStream;
use rustc_errors::{Applicability, DiagnosticBuilder};
"writing a literal with a format string"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// This lint warns when a named parameter in a format string is used as a positional one.
+ ///
+ /// ### Why is this bad?
+ /// It may be confused for an assignment and obfuscates which parameter is being used.
+ ///
+ /// ### Example
+ /// ```rust
+ /// println!("{}", x = 10);
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// println!("{x}", x = 10);
+ /// ```
+ #[clippy::version = "1.63.0"]
+ pub POSITIONAL_NAMED_FORMAT_PARAMETERS,
+ suspicious,
+ "named parameter in a format string is used positionally"
+}
+
#[derive(Default)]
pub struct Write {
in_debug_impl: bool,
PRINT_LITERAL,
WRITE_WITH_NEWLINE,
WRITELN_EMPTY_STRING,
- WRITE_LITERAL
+ WRITE_LITERAL,
+ POSITIONAL_NAMED_FORMAT_PARAMETERS,
]);
impl EarlyLintPass for Write {
#[derive(Default)]
struct SimpleFormatArgs {
unnamed: Vec<Vec<Span>>,
+ complex_unnamed: Vec<Vec<Span>>,
named: Vec<(Symbol, Vec<Span>)>,
}
impl SimpleFormatArgs {
})
}
+ fn get_complex_unnamed(&self) -> impl Iterator<Item = &[Span]> {
+ self.complex_unnamed.iter().map(Vec::as_slice)
+ }
+
fn get_named(&self, n: &Path) -> &[Span] {
self.named.iter().find(|x| *n == x.0).map_or(&[], |x| x.1.as_slice())
}
},
};
}
+
+ fn push_to_complex(&mut self, span: Span, position: usize) {
+ if self.complex_unnamed.len() <= position {
+ self.complex_unnamed.resize_with(position, Vec::new);
+ self.complex_unnamed.push(vec![span]);
+ } else {
+ let args: &mut Vec<Span> = &mut self.complex_unnamed[position];
+ args.push(span);
+ }
+ }
+
+ fn push_complex(
+ &mut self,
+ cx: &EarlyContext<'_>,
+ arg: rustc_parse_format::Argument<'_>,
+ str_lit_span: Span,
+ fmt_span: Span,
+ ) {
+ use rustc_parse_format::{ArgumentImplicitlyIs, ArgumentIs, CountIsParam, CountIsStar};
+
+ let snippet = snippet_opt(cx, fmt_span);
+
+ let end = snippet
+ .as_ref()
+ .and_then(|s| s.find(':'))
+ .or_else(|| fmt_span.hi().0.checked_sub(fmt_span.lo().0 + 1).map(|u| u as usize));
+
+ if let (ArgumentIs(n) | ArgumentImplicitlyIs(n), Some(end)) = (arg.position, end) {
+ let span = fmt_span.from_inner(InnerSpan::new(1, end));
+ self.push_to_complex(span, n);
+ };
+
+ if let (CountIsParam(n) | CountIsStar(n), Some(span)) = (arg.format.precision, arg.format.precision_span) {
+ // We need to do this hack as precision spans should be converted from .* to .foo$
+ let hack = if snippet.as_ref().and_then(|s| s.find('*')).is_some() {
+ 0
+ } else {
+ 1
+ };
+
+ let span = str_lit_span.from_inner(InnerSpan {
+ start: span.start + 1,
+ end: span.end - hack,
+ });
+ self.push_to_complex(span, n);
+ };
+
+ if let (CountIsParam(n), Some(span)) = (arg.format.width, arg.format.width_span) {
+ let span = str_lit_span.from_inner(InnerSpan {
+ start: span.start,
+ end: span.end - 1,
+ });
+ self.push_to_complex(span, n);
+ };
+ }
}
impl Write {
// FIXME: modify rustc's fmt string parser to give us the current span
span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting");
}
-
args.push(arg, span);
+ args.push_complex(cx, arg, str_lit.span, span);
}
parser.errors.is_empty().then_some(args)
let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
let mut unnamed_args = args.get_unnamed();
+ let mut complex_unnamed_args = args.get_complex_unnamed();
loop {
if !parser.eat(&token::Comma) {
return (Some(fmtstr), expr);
} else {
return (Some(fmtstr), None);
};
+ let complex_unnamed_arg = complex_unnamed_args.next();
+
let (fmt_spans, lit) = match &token_expr.kind {
ExprKind::Lit(lit) => (unnamed_args.next().unwrap_or(&[]), lit),
- ExprKind::Assign(lhs, rhs, _) => match (&lhs.kind, &rhs.kind) {
- (ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit),
- _ => continue,
+ ExprKind::Assign(lhs, rhs, _) => {
+ if let Some(span) = complex_unnamed_arg {
+ for x in span {
+ Self::report_positional_named_param(cx, *x, lhs, rhs);
+ }
+ }
+ match (&lhs.kind, &rhs.kind) {
+ (ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit),
+ _ => continue,
+ }
},
_ => {
unnamed_args.next();
}
}
+ fn report_positional_named_param(cx: &EarlyContext<'_>, span: Span, lhs: &P<Expr>, _rhs: &P<Expr>) {
+ if let ExprKind::Path(_, _p) = &lhs.kind {
+ let mut applicability = Applicability::MachineApplicable;
+ let name = snippet_with_applicability(cx, lhs.span, "name", &mut applicability);
+ // We need to do this hack as precision spans should be converted from .* to .foo$
+ let hack = snippet(cx, span, "").contains('*');
+
+ span_lint_and_sugg(
+ cx,
+ POSITIONAL_NAMED_FORMAT_PARAMETERS,
+ span,
+ &format!("named parameter {} is used as a positional parameter", name),
+ "replace it with",
+ if hack {
+ format!("{}$", name)
+ } else {
+ format!("{}", name)
+ },
+ applicability,
+ );
+ };
+ }
+
fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
if fmt_str.symbol == kw::Empty {
[dependencies]
arrayvec = { version = "0.7", default-features = false }
if_chain = "1.0"
+itertools = "0.10.1"
rustc-semver = "1.1"
[features]
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(let_else)]
+#![feature(let_chains)]
#![feature(lint_reasons)]
#![feature(once_cell)]
#![feature(rustc_private)]
extern crate rustc_lexer;
extern crate rustc_lint;
extern crate rustc_middle;
+extern crate rustc_parse_format;
extern crate rustc_session;
extern crate rustc_span;
extern crate rustc_target;
Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind,
TraitRef, TyKind, UnOp,
};
+use rustc_lexer::{tokenize, TokenKind};
use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::place::PlaceBase;
use rustc_middle::ty as rustc_ty;
use rustc_session::Session;
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::original_sp;
+use rustc_span::source_map::SourceMap;
use rustc_span::sym;
use rustc_span::symbol::{kw, Symbol};
use rustc_span::{Span, DUMMY_SP};
/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
///
-/// Please use `is_expr_diagnostic_item` if the target is a diagnostic item.
+/// Please use `is_path_diagnostic_item` if the target is a diagnostic item.
pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
path_def_id(cx, expr).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
-/// diagnostic item.
-pub fn is_expr_diagnostic_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
- path_def_id(cx, expr).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
+/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
+/// it matches the given diagnostic item.
+pub fn is_path_diagnostic_item<'tcx>(
+ cx: &LateContext<'_>,
+ maybe_path: &impl MaybePath<'tcx>,
+ diag_item: Symbol,
+) -> bool {
+ path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
}
/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
None
}
+/// Checks whether a given span has any comment token
+/// This checks for all types of comment: line "//", block "/**", doc "///" "//!"
+pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
+ let Ok(snippet) = sm.span_to_snippet(span) else { return false };
+ return tokenize(&snippet).any(|token| {
+ matches!(
+ token.kind,
+ TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
+ )
+ });
+}
+
macro_rules! op_utils {
($($name:ident $assign:ident)*) => {
/// Binary operation traits like `LangItem::Add`
#![allow(clippy::similar_names)] // `expr` and `expn`
+use crate::is_path_diagnostic_item;
+use crate::source::snippet_opt;
use crate::visitors::expr_visitor_no_bodies;
use arrayvec::ArrayVec;
-use if_chain::if_chain;
+use itertools::{izip, Either, Itertools};
use rustc_ast::ast::LitKind;
use rustc_hir::intravisit::Visitor;
use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
+use rustc_lexer::unescape::unescape_literal;
+use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
use rustc_lint::LateContext;
+use rustc_parse_format::{self as rpf, Alignment};
use rustc_span::def_id::DefId;
use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
-use rustc_span::{sym, ExpnData, ExpnId, ExpnKind, Span, Symbol};
+use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol};
use std::ops::ControlFlow;
const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
}
}
-/// A parsed `format_args!` expansion
+/// The format string doesn't exist in the HIR, so we reassemble it from source code
#[derive(Debug)]
-pub struct FormatArgsExpn<'tcx> {
- /// Span of the first argument, the format string
- pub format_string_span: Span,
- /// The format string split by formatted args like `{..}`
- pub format_string_parts: Vec<Symbol>,
- /// Values passed after the format string
- pub value_args: Vec<&'tcx Expr<'tcx>>,
- /// Each element is a `value_args` index and a formatting trait (e.g. `sym::Debug`)
- pub formatters: Vec<(usize, Symbol)>,
- /// List of `fmt::v1::Argument { .. }` expressions. If this is empty,
- /// then `formatters` represents the format args (`{..}`).
- /// If this is non-empty, it represents the format args, and the `position`
- /// parameters within the struct expressions are indexes of `formatters`.
- pub specs: Vec<&'tcx Expr<'tcx>>,
+pub struct FormatString {
+ /// Span of the whole format string literal, including `[r#]"`.
+ pub span: Span,
+ /// Snippet of the whole format string literal, including `[r#]"`.
+ pub snippet: String,
+ /// If the string is raw `r"..."`/`r#""#`, how many `#`s does it have on each side.
+ pub style: Option<usize>,
+ /// The unescaped value of the format string, e.g. `"val – {}"` for the literal
+ /// `"val \u{2013} {}"`.
+ pub unescaped: String,
+ /// The format string split by format args like `{..}`.
+ pub parts: Vec<Symbol>,
}
-impl<'tcx> FormatArgsExpn<'tcx> {
- /// Parses an expanded `format_args!` or `format_args_nl!` invocation
- pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> {
- macro_backtrace(expr.span).find(|macro_call| {
- matches!(
- cx.tcx.item_name(macro_call.def_id),
- sym::const_format_args | sym::format_args | sym::format_args_nl
- )
- })?;
- let mut format_string_span: Option<Span> = None;
- let mut format_string_parts: Vec<Symbol> = Vec::new();
- let mut value_args: Vec<&Expr<'_>> = Vec::new();
- let mut formatters: Vec<(usize, Symbol)> = Vec::new();
- let mut specs: Vec<&Expr<'_>> = Vec::new();
- expr_visitor_no_bodies(|e| {
- // if we're still inside of the macro definition...
- if e.span.ctxt() == expr.span.ctxt() {
- // ArgumentV1::new_<format_trait>(<value>)
- if_chain! {
- if let ExprKind::Call(callee, [val]) = e.kind;
- if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind;
- if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
- if path.segments.last().unwrap().ident.name == sym::ArgumentV1;
- if seg.ident.name.as_str().starts_with("new_");
- then {
- let val_idx = if_chain! {
- if val.span.ctxt() == expr.span.ctxt();
- if let ExprKind::Field(_, field) = val.kind;
- if let Ok(idx) = field.name.as_str().parse();
- then {
- // tuple index
- idx
- } else {
- // assume the value expression is passed directly
- formatters.len()
- }
- };
- let fmt_trait = match seg.ident.name.as_str() {
- "new_display" => "Display",
- "new_debug" => "Debug",
- "new_lower_exp" => "LowerExp",
- "new_upper_exp" => "UpperExp",
- "new_octal" => "Octal",
- "new_pointer" => "Pointer",
- "new_binary" => "Binary",
- "new_lower_hex" => "LowerHex",
- "new_upper_hex" => "UpperHex",
- _ => unreachable!(),
- };
- formatters.push((val_idx, Symbol::intern(fmt_trait)));
- }
- }
- if let ExprKind::Struct(QPath::Resolved(_, path), ..) = e.kind {
- if path.segments.last().unwrap().ident.name == sym::Argument {
- specs.push(e);
- }
+impl FormatString {
+ fn new(cx: &LateContext<'_>, pieces: &Expr<'_>) -> Option<Self> {
+ // format_args!(r"a {} b \", 1);
+ //
+ // expands to
+ //
+ // ::core::fmt::Arguments::new_v1(&["a ", " b \\"],
+ // &[::core::fmt::ArgumentV1::new_display(&1)]);
+ //
+ // where `pieces` is the expression `&["a ", " b \\"]`. It has the span of `r"a {} b \"`
+ let span = pieces.span;
+ let snippet = snippet_opt(cx, span)?;
+
+ let (inner, style) = match tokenize(&snippet).next()?.kind {
+ TokenKind::Literal { kind, .. } => {
+ let style = match kind {
+ LiteralKind::Str { .. } => None,
+ LiteralKind::RawStr { n_hashes: Some(n), .. } => Some(n.into()),
+ _ => return None,
+ };
+
+ let start = style.map_or(1, |n| 2 + n);
+ let end = snippet.len() - style.map_or(1, |n| 1 + n);
+
+ (&snippet[start..end], style)
+ },
+ _ => return None,
+ };
+
+ let mode = if style.is_some() {
+ unescape::Mode::RawStr
+ } else {
+ unescape::Mode::Str
+ };
+
+ let mut unescaped = String::with_capacity(inner.len());
+ unescape_literal(inner, mode, &mut |_, ch| {
+ unescaped.push(ch.unwrap());
+ });
+
+ let mut parts = Vec::new();
+ expr_visitor_no_bodies(|expr| {
+ if let ExprKind::Lit(lit) = &expr.kind {
+ if let LitKind::Str(symbol, _) = lit.node {
+ parts.push(symbol);
}
- // walk through the macro expansion
- return true;
}
- // assume that the first expr with a differing context represents
- // (and has the span of) the format string
- if format_string_span.is_none() {
- format_string_span = Some(e.span);
- let span = e.span;
- // walk the expr and collect string literals which are format string parts
- expr_visitor_no_bodies(|e| {
- if e.span.ctxt() != span.ctxt() {
- // defensive check, probably doesn't happen
- return false;
- }
- if let ExprKind::Lit(lit) = &e.kind {
- if let LitKind::Str(symbol, _s) = lit.node {
- format_string_parts.push(symbol);
- }
- }
- true
- })
- .visit_expr(e);
+
+ true
+ })
+ .visit_expr(pieces);
+
+ Some(Self {
+ span,
+ snippet,
+ style,
+ unescaped,
+ parts,
+ })
+ }
+}
+
+struct FormatArgsValues<'tcx> {
+ /// See `FormatArgsExpn::value_args`
+ value_args: Vec<&'tcx Expr<'tcx>>,
+ /// Maps an `rt::v1::Argument::position` or an `rt::v1::Count::Param` to its index in
+ /// `value_args`
+ pos_to_value_index: Vec<usize>,
+ /// Used to check if a value is declared inline & to resolve `InnerSpan`s.
+ format_string_span: SpanData,
+}
+
+impl<'tcx> FormatArgsValues<'tcx> {
+ fn new(args: &'tcx Expr<'tcx>, format_string_span: SpanData) -> Self {
+ let mut pos_to_value_index = Vec::new();
+ let mut value_args = Vec::new();
+ expr_visitor_no_bodies(|expr| {
+ if expr.span.ctxt() == args.span.ctxt() {
+ // ArgumentV1::new_<format_trait>(<val>)
+ // ArgumentV1::from_usize(<val>)
+ if let ExprKind::Call(callee, [val]) = expr.kind
+ && let ExprKind::Path(QPath::TypeRelative(ty, _)) = callee.kind
+ && let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind
+ && path.segments.last().unwrap().ident.name == sym::ArgumentV1
+ {
+ let val_idx = if val.span.ctxt() == expr.span.ctxt()
+ && let ExprKind::Field(_, field) = val.kind
+ && let Ok(idx) = field.name.as_str().parse()
+ {
+ // tuple index
+ idx
+ } else {
+ // assume the value expression is passed directly
+ pos_to_value_index.len()
+ };
+
+ pos_to_value_index.push(val_idx);
+ }
+
+ true
} else {
- // assume that any further exprs with a differing context are value args
- value_args.push(e);
+ // assume that any expr with a differing span is a value
+ value_args.push(expr);
+
+ false
}
- // don't walk anything not from the macro expansion (e.a. inputs)
- false
})
- .visit_expr(expr);
- Some(FormatArgsExpn {
- format_string_span: format_string_span?,
- format_string_parts,
+ .visit_expr(args);
+
+ Self {
value_args,
- formatters,
- specs,
+ pos_to_value_index,
+ format_string_span,
+ }
+ }
+}
+
+/// The positions of a format argument's value, precision and width
+///
+/// A position is an index into the second argument of `Arguments::new_v1[_formatted]`
+#[derive(Debug, Default, Copy, Clone)]
+struct ParamPosition {
+ /// The position stored in `rt::v1::Argument::position`.
+ value: usize,
+ /// The position stored in `rt::v1::FormatSpec::width` if it is a `Count::Param`.
+ width: Option<usize>,
+ /// The position stored in `rt::v1::FormatSpec::precision` if it is a `Count::Param`.
+ precision: Option<usize>,
+}
+
+/// Parses the `fmt` arg of `Arguments::new_v1_formatted(pieces, args, fmt, _)`
+fn parse_rt_fmt<'tcx>(fmt_arg: &'tcx Expr<'tcx>) -> Option<impl Iterator<Item = ParamPosition> + 'tcx> {
+ fn parse_count(expr: &Expr<'_>) -> Option<usize> {
+ // ::core::fmt::rt::v1::Count::Param(1usize),
+ if let ExprKind::Call(ctor, [val]) = expr.kind
+ && let ExprKind::Path(QPath::Resolved(_, path)) = ctor.kind
+ && path.segments.last()?.ident.name == sym::Param
+ && let ExprKind::Lit(lit) = &val.kind
+ && let LitKind::Int(pos, _) = lit.node
+ {
+ Some(pos as usize)
+ } else {
+ None
+ }
+ }
+
+ if let ExprKind::AddrOf(.., array) = fmt_arg.kind
+ && let ExprKind::Array(specs) = array.kind
+ {
+ Some(specs.iter().map(|spec| {
+ let mut position = ParamPosition::default();
+
+ // ::core::fmt::rt::v1::Argument {
+ // position: 0usize,
+ // format: ::core::fmt::rt::v1::FormatSpec {
+ // ..
+ // precision: ::core::fmt::rt::v1::Count::Implied,
+ // width: ::core::fmt::rt::v1::Count::Implied,
+ // },
+ // }
+
+ // TODO: this can be made much nicer next sync with `Visitor::visit_expr_field`
+ if let ExprKind::Struct(_, fields, _) = spec.kind {
+ for field in fields {
+ match (field.ident.name, &field.expr.kind) {
+ (sym::position, ExprKind::Lit(lit)) => {
+ if let LitKind::Int(pos, _) = lit.node {
+ position.value = pos as usize;
+ }
+ },
+ (sym::format, &ExprKind::Struct(_, spec_fields, _)) => {
+ for spec_field in spec_fields {
+ match spec_field.ident.name {
+ sym::precision => {
+ position.precision = parse_count(spec_field.expr);
+ },
+ sym::width => {
+ position.width = parse_count(spec_field.expr);
+ },
+ _ => {},
+ }
+ }
+ },
+ _ => {},
+ }
+ }
+ }
+
+ position
+ }))
+ } else {
+ None
+ }
+}
+
+/// `Span::from_inner`, but for `rustc_parse_format`'s `InnerSpan`
+fn span_from_inner(base: SpanData, inner: rpf::InnerSpan) -> Span {
+ Span::new(
+ base.lo + BytePos::from_usize(inner.start),
+ base.lo + BytePos::from_usize(inner.end),
+ base.ctxt,
+ base.parent,
+ )
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum FormatParamKind {
+ /// An implicit parameter , such as `{}` or `{:?}`.
+ Implicit,
+ /// A parameter with an explicit number, or an asterisk precision. e.g. `{1}`, `{0:?}`,
+ /// `{:.0$}` or `{:.*}`.
+ Numbered,
+ /// A named parameter with a named `value_arg`, such as the `x` in `format!("{x}", x = 1)`.
+ Named(Symbol),
+ /// An implicit named parameter, such as the `y` in `format!("{y}")`.
+ NamedInline(Symbol),
+}
+
+/// A `FormatParam` is any place in a `FormatArgument` that refers to a supplied value, e.g.
+///
+/// ```
+/// let precision = 2;
+/// format!("{:.precision$}", 0.1234);
+/// ```
+///
+/// has two `FormatParam`s, a [`FormatParamKind::Implicit`] `.kind` with a `.value` of `0.1234`
+/// and a [`FormatParamKind::NamedInline("precision")`] `.kind` with a `.value` of `2`
+#[derive(Debug, Copy, Clone)]
+pub struct FormatParam<'tcx> {
+ /// The expression this parameter refers to.
+ pub value: &'tcx Expr<'tcx>,
+ /// How this parameter refers to its `value`.
+ pub kind: FormatParamKind,
+ /// Span of the parameter, may be zero width. Includes the whitespace of implicit parameters.
+ ///
+ /// ```text
+ /// format!("{}, { }, {0}, {name}", ...);
+ /// ^ ~~ ~ ~~~~
+ /// ```
+ pub span: Span,
+}
+
+impl<'tcx> FormatParam<'tcx> {
+ fn new(
+ mut kind: FormatParamKind,
+ position: usize,
+ inner: rpf::InnerSpan,
+ values: &FormatArgsValues<'tcx>,
+ ) -> Option<Self> {
+ let value_index = *values.pos_to_value_index.get(position)?;
+ let value = *values.value_args.get(value_index)?;
+ let span = span_from_inner(values.format_string_span, inner);
+
+ // if a param is declared inline, e.g. `format!("{x}")`, the generated expr's span points
+ // into the format string
+ if let FormatParamKind::Named(name) = kind && values.format_string_span.contains(value.span.data()) {
+ kind = FormatParamKind::NamedInline(name);
+ }
+
+ Some(Self { value, kind, span })
+ }
+}
+
+/// Used by [width](https://doc.rust-lang.org/std/fmt/#width) and
+/// [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers.
+#[derive(Debug, Copy, Clone)]
+pub enum Count<'tcx> {
+ /// Specified with a literal number, stores the value.
+ Is(usize, Span),
+ /// Specified using `$` and `*` syntaxes. The `*` format is still considered to be
+ /// `FormatParamKind::Numbered`.
+ Param(FormatParam<'tcx>),
+ /// Not specified.
+ Implied,
+}
+
+impl<'tcx> Count<'tcx> {
+ fn new(
+ count: rpf::Count<'_>,
+ position: Option<usize>,
+ inner: Option<rpf::InnerSpan>,
+ values: &FormatArgsValues<'tcx>,
+ ) -> Option<Self> {
+ Some(match count {
+ rpf::Count::CountIs(val) => Self::Is(val, span_from_inner(values.format_string_span, inner?)),
+ rpf::Count::CountIsName(name, span) => Self::Param(FormatParam::new(
+ FormatParamKind::Named(Symbol::intern(name)),
+ position?,
+ span,
+ values,
+ )?),
+ rpf::Count::CountIsParam(_) | rpf::Count::CountIsStar(_) => {
+ Self::Param(FormatParam::new(FormatParamKind::Numbered, position?, inner?, values)?)
+ },
+ rpf::Count::CountImplied => Self::Implied,
+ })
+ }
+
+ pub fn is_implied(self) -> bool {
+ matches!(self, Count::Implied)
+ }
+
+ pub fn param(self) -> Option<FormatParam<'tcx>> {
+ match self {
+ Count::Param(param) => Some(param),
+ _ => None,
+ }
+ }
+}
+
+/// Specification for the formatting of an argument in the format string. See
+/// <https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters> for the precise meanings.
+#[derive(Debug)]
+pub struct FormatSpec<'tcx> {
+ /// Optionally specified character to fill alignment with.
+ pub fill: Option<char>,
+ /// Optionally specified alignment.
+ pub align: Alignment,
+ /// Packed version of various flags provided, see [`rustc_parse_format::Flag`].
+ pub flags: u32,
+ /// Represents either the maximum width or the integer precision.
+ pub precision: Count<'tcx>,
+ /// The minimum width, will be padded according to `width`/`align`
+ pub width: Count<'tcx>,
+ /// The formatting trait used by the argument, e.g. `sym::Display` for `{}`, `sym::Debug` for
+ /// `{:?}`.
+ pub r#trait: Symbol,
+ pub trait_span: Option<Span>,
+}
+
+impl<'tcx> FormatSpec<'tcx> {
+ fn new(spec: rpf::FormatSpec<'_>, positions: ParamPosition, values: &FormatArgsValues<'tcx>) -> Option<Self> {
+ Some(Self {
+ fill: spec.fill,
+ align: spec.align,
+ flags: spec.flags,
+ precision: Count::new(spec.precision, positions.precision, spec.precision_span, values)?,
+ width: Count::new(spec.width, positions.width, spec.width_span, values)?,
+ r#trait: match spec.ty {
+ "" => sym::Display,
+ "?" => sym::Debug,
+ "o" => sym!(Octal),
+ "x" => sym!(LowerHex),
+ "X" => sym!(UpperHex),
+ "p" => sym::Pointer,
+ "b" => sym!(Binary),
+ "e" => sym!(LowerExp),
+ "E" => sym!(UpperExp),
+ _ => return None,
+ },
+ trait_span: spec
+ .ty_span
+ .map(|span| span_from_inner(values.format_string_span, span)),
})
}
- /// Finds a nested call to `format_args!` within a `format!`-like macro call
+ /// Returns true if this format spec would change the contents of a string when formatted
+ pub fn has_string_formatting(&self) -> bool {
+ self.r#trait != sym::Display || !self.width.is_implied() || !self.precision.is_implied()
+ }
+}
+
+/// A format argument, such as `{}`, `{foo:?}`.
+#[derive(Debug)]
+pub struct FormatArg<'tcx> {
+ /// The parameter the argument refers to.
+ pub param: FormatParam<'tcx>,
+ /// How to format `param`.
+ pub format: FormatSpec<'tcx>,
+ /// span of the whole argument, `{..}`.
+ pub span: Span,
+}
+
+/// A parsed `format_args!` expansion.
+#[derive(Debug)]
+pub struct FormatArgsExpn<'tcx> {
+ /// The format string literal.
+ pub format_string: FormatString,
+ // The format arguments, such as `{:?}`.
+ pub args: Vec<FormatArg<'tcx>>,
+ /// Has an added newline due to `println!()`/`writeln!()`/etc. The last format string part will
+ /// include this added newline.
+ pub newline: bool,
+ /// Values passed after the format string and implicit captures. `[1, z + 2, x]` for
+ /// `format!("{x} {} {y}", 1, z + 2)`.
+ value_args: Vec<&'tcx Expr<'tcx>>,
+}
+
+impl<'tcx> FormatArgsExpn<'tcx> {
+ pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> {
+ let macro_name = macro_backtrace(expr.span)
+ .map(|macro_call| cx.tcx.item_name(macro_call.def_id))
+ .find(|&name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))?;
+ let newline = macro_name == sym::format_args_nl;
+
+ // ::core::fmt::Arguments::new_v1(pieces, args)
+ // ::core::fmt::Arguments::new_v1_formatted(pieces, args, fmt, _unsafe_arg)
+ if let ExprKind::Call(callee, [pieces, args, rest @ ..]) = expr.kind
+ && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind
+ && is_path_diagnostic_item(cx, ty, sym::Arguments)
+ && matches!(seg.ident.as_str(), "new_v1" | "new_v1_formatted")
+ {
+ let format_string = FormatString::new(cx, pieces)?;
+
+ let mut parser = rpf::Parser::new(
+ &format_string.unescaped,
+ format_string.style,
+ Some(format_string.snippet.clone()),
+ // `format_string.unescaped` does not contain the appended newline
+ false,
+ rpf::ParseMode::Format,
+ );
+
+ let parsed_args = parser
+ .by_ref()
+ .filter_map(|piece| match piece {
+ rpf::Piece::NextArgument(a) => Some(a),
+ rpf::Piece::String(_) => None,
+ })
+ .collect_vec();
+ if !parser.errors.is_empty() {
+ return None;
+ }
+
+ let positions = if let Some(fmt_arg) = rest.first() {
+ // If the argument contains format specs, `new_v1_formatted(_, _, fmt, _)`, parse
+ // them.
+
+ Either::Left(parse_rt_fmt(fmt_arg)?)
+ } else {
+ // If no format specs are given, the positions are in the given order and there are
+ // no `precision`/`width`s to consider.
+
+ Either::Right((0..).map(|n| ParamPosition {
+ value: n,
+ width: None,
+ precision: None,
+ }))
+ };
+
+ let values = FormatArgsValues::new(args, format_string.span.data());
+
+ let args = izip!(positions, parsed_args, parser.arg_places)
+ .map(|(position, parsed_arg, arg_span)| {
+ Some(FormatArg {
+ param: FormatParam::new(
+ match parsed_arg.position {
+ rpf::Position::ArgumentImplicitlyIs(_) => FormatParamKind::Implicit,
+ rpf::Position::ArgumentIs(_) => FormatParamKind::Numbered,
+ // NamedInline is handled by `FormatParam::new()`
+ rpf::Position::ArgumentNamed(name) => FormatParamKind::Named(Symbol::intern(name)),
+ },
+ position.value,
+ parsed_arg.position_span,
+ &values,
+ )?,
+ format: FormatSpec::new(parsed_arg.format, position, &values)?,
+ span: span_from_inner(values.format_string_span, arg_span),
+ })
+ })
+ .collect::<Option<Vec<_>>>()?;
+
+ Some(Self {
+ format_string,
+ args,
+ value_args: values.value_args,
+ newline,
+ })
+ } else {
+ None
+ }
+ }
+
pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option<Self> {
let mut format_args = None;
expr_visitor_no_bodies(|e| {
format_args
}
- /// Returns a vector of `FormatArgsArg`.
- pub fn args(&self) -> Option<Vec<FormatArgsArg<'tcx>>> {
- if self.specs.is_empty() {
- let args = std::iter::zip(&self.value_args, &self.formatters)
- .map(|(value, &(_, format_trait))| FormatArgsArg {
- value,
- format_trait,
- spec: None,
- })
- .collect();
- return Some(args);
- }
- self.specs
- .iter()
- .map(|spec| {
- if_chain! {
- // struct `core::fmt::rt::v1::Argument`
- if let ExprKind::Struct(_, fields, _) = spec.kind;
- if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position);
- if let ExprKind::Lit(lit) = &position_field.expr.kind;
- if let LitKind::Int(position, _) = lit.node;
- if let Ok(i) = usize::try_from(position);
- if let Some(&(j, format_trait)) = self.formatters.get(i);
- then {
- Some(FormatArgsArg {
- value: self.value_args[j],
- format_trait,
- spec: Some(spec),
- })
- } else {
- None
- }
- }
- })
- .collect()
- }
-
/// Source callsite span of all inputs
pub fn inputs_span(&self) -> Span {
match *self.value_args {
- [] => self.format_string_span,
+ [] => self.format_string.span,
[.., last] => self
- .format_string_span
- .to(hygiene::walk_chain(last.span, self.format_string_span.ctxt())),
+ .format_string
+ .span
+ .to(hygiene::walk_chain(last.span, self.format_string.span.ctxt())),
}
}
-}
-/// Type representing a `FormatArgsExpn`'s format arguments
-pub struct FormatArgsArg<'tcx> {
- /// An element of `value_args` according to `position`
- pub value: &'tcx Expr<'tcx>,
- /// An element of `args` according to `position`
- pub format_trait: Symbol,
- /// An element of `specs`
- pub spec: Option<&'tcx Expr<'tcx>>,
-}
-
-impl<'tcx> FormatArgsArg<'tcx> {
- /// Returns true if any formatting parameters are used that would have an effect on strings,
- /// like `{:+2}` instead of just `{}`.
- pub fn has_string_formatting(&self) -> bool {
- self.spec.map_or(false, |spec| {
- // `!` because these conditions check that `self` is unformatted.
- !if_chain! {
- // struct `core::fmt::rt::v1::Argument`
- if let ExprKind::Struct(_, fields, _) = spec.kind;
- if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format);
- // struct `core::fmt::rt::v1::FormatSpec`
- if let ExprKind::Struct(_, subfields, _) = format_field.expr.kind;
- if subfields.iter().all(|field| match field.ident.name {
- sym::precision | sym::width => match field.expr.kind {
- ExprKind::Path(QPath::Resolved(_, path)) => {
- path.segments.last().unwrap().ident.name == sym::Implied
- }
- _ => false,
- }
- _ => true,
- });
- then { true } else { false }
- }
- })
+ /// Iterator of all format params, both values and those referenced by `width`/`precision`s.
+ pub fn params(&'tcx self) -> impl Iterator<Item = FormatParam<'tcx>> {
+ self.args
+ .iter()
+ .flat_map(|arg| [Some(arg.param), arg.format.precision.param(), arg.format.width.param()])
+ .flatten()
}
}
// names may refer to stabilized feature flags or library items
msrv_aliases! {
1,62,0 { BOOL_THEN_SOME }
- 1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN }
+ 1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR }
1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS }
1,50,0 { BOOL_THEN }
1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
1,28,0 { FROM_BOOL }
1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN }
+ 1,24,0 { IS_ASCII_DIGIT }
1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN }
1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
1,16,0 { STR_REPEAT }
- 1,24,0 { IS_ASCII_DIGIT }
}
pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"];
pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"];
pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
-#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
#[cfg(feature = "internal")]
pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"];
pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
+pub const PEEKABLE: [&str; 5] = ["core", "iter", "adapters", "peekable", "Peekable"];
pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
#[cfg_attr(not(unix), allow(clippy::invalid_paths))]
pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"];
}
}
-/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty`
-pub fn contains_ty<'tcx>(ty: Ty<'tcx>, other_ty: Ty<'tcx>) -> bool {
- ty.walk().any(|inner| match inner.unpack() {
- GenericArgKind::Type(inner_ty) => other_ty == inner_ty,
- GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
- })
-}
-
/// Walks into `ty` and returns `true` if any inner type is an instance of the given adt
/// constructor.
pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool {
peel(ty, 0)
}
-/// Peels off all references on the type.Returns the underlying type, the number of references
+/// Peels off all references on the type. Returns the underlying type, the number of references
/// removed, and whether the pointer is ultimately mutable or not.
pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) {
})
.unwrap_or(false)
}
+
+/// Comes up with an "at least" guesstimate for the type's size, not taking into
+/// account the layout of type parameters.
+pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 {
+ use rustc_middle::ty::layout::LayoutOf;
+ if !is_normalizable(cx, cx.param_env, ty) {
+ return 0;
+ }
+ match (cx.layout_of(ty).map(|layout| layout.size.bytes()), ty.kind()) {
+ (Ok(size), _) => size,
+ (Err(_), ty::Tuple(list)) => list.as_substs().types().map(|t| approx_ty_size(cx, t)).sum(),
+ (Err(_), ty::Array(t, n)) => {
+ n.try_eval_usize(cx.tcx, cx.param_env).unwrap_or_default() * approx_ty_size(cx, *t)
+ },
+ (Err(_), ty::Adt(def, subst)) if def.is_struct() => def
+ .variants()
+ .iter()
+ .map(|v| {
+ v.fields
+ .iter()
+ .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
+ .sum::<u64>()
+ })
+ .sum(),
+ (Err(_), ty::Adt(def, subst)) if def.is_enum() => def
+ .variants()
+ .iter()
+ .map(|v| {
+ v.fields
+ .iter()
+ .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
+ .sum::<u64>()
+ })
+ .max()
+ .unwrap_or_default(),
+ (Err(_), ty::Adt(def, subst)) if def.is_union() => def
+ .variants()
+ .iter()
+ .map(|v| {
+ v.fields
+ .iter()
+ .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
+ .max()
+ .unwrap_or_default()
+ })
+ .max()
+ .unwrap_or_default(),
+ (Err(_), _) => 0,
+ }
+}
[toolchain]
-channel = "nightly-2022-08-11"
+channel = "nightly-2022-08-27"
components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
#[must_use]
pub fn get_commit_hash() -> Option<String> {
std::process::Command::new("git")
- .args(&["rev-parse", "--short", "HEAD"])
+ .args(["rev-parse", "--short", "HEAD"])
.output()
.ok()
.and_then(|r| String::from_utf8(r.stdout).ok())
#[must_use]
pub fn get_commit_date() -> Option<String> {
std::process::Command::new("git")
- .args(&["log", "-1", "--date=short", "--pretty=format:%cd"])
+ .args(["log", "-1", "--date=short", "--pretty=format:%cd"])
.output()
.ok()
.and_then(|r| String::from_utf8(r.stdout).ok())
let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let output = Command::new("cargo")
.current_dir(root_dir)
- .args(&["dev", "fmt", "--check"])
+ .args(["dev", "fmt", "--check"])
.output()
.unwrap();
"search_is_some.rs",
"single_component_path_imports_nested_first.rs",
"string_add.rs",
+ "suspicious_to_owned.rs",
"toplevel_ref_arg_non_rustfix.rs",
- "trait_duplication_in_bounds.rs",
"unit_arg.rs",
"unnecessary_clone.rs",
"unnecessary_lazy_eval_unfixable.rs",
];
fn check_rustfix_coverage() {
- let missing_coverage_path = Path::new("target/debug/test/ui/rustfix_missing_coverage.txt");
+ let missing_coverage_path = Path::new("debug/test/ui/rustfix_missing_coverage.txt");
+ let missing_coverage_path = if let Ok(target_dir) = std::env::var("CARGO_TARGET_DIR") {
+ PathBuf::from(target_dir).join(missing_coverage_path)
+ } else {
+ missing_coverage_path.to_path_buf()
+ };
if let Ok(missing_coverage_contents) = std::fs::read_to_string(missing_coverage_path) {
assert!(RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS.iter().is_sorted_by_key(Path::new));
- for rs_path in missing_coverage_contents.lines() {
- if Path::new(rs_path).starts_with("tests/ui/crashes") {
+ for rs_file in missing_coverage_contents.lines() {
+ let rs_path = Path::new(rs_file);
+ if rs_path.starts_with("tests/ui/crashes") {
continue;
}
- let filename = Path::new(rs_path).strip_prefix("tests/ui/").unwrap();
+ assert!(rs_path.starts_with("tests/ui/"), "{:?}", rs_file);
+ let filename = rs_path.strip_prefix("tests/ui/").unwrap();
assert!(
RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS
.binary_search_by_key(&filename, Path::new)
"`{}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation. \
Please either add `// run-rustfix` at the top of the file or add the file to \
`RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS` in `tests/compile-test.rs`.",
- rs_path,
+ rs_file,
);
}
}
if cfg!(feature = "internal") {
// internal lints only exist if we build with the internal feature
- command.args(&["-D", "clippy::internal"]);
+ command.args(["-D", "clippy::internal"]);
} else {
// running a clippy built without internal lints on the clippy source
// that contains e.g. `allow(clippy::invalid_paths)`
- command.args(&["-A", "unknown_lints"]);
+ command.args(["-A", "unknown_lints"]);
}
let output = command.output().unwrap();
repo_dir.push(crate_name);
let st = Command::new("git")
- .args(&[
+ .args([
OsStr::new("clone"),
OsStr::new("--depth=1"),
OsStr::new(&repo_url),
.current_dir(repo_dir)
.env("RUST_BACKTRACE", "full")
.env("CARGO_TARGET_DIR", target_dir)
- .args(&[
+ .args([
"clippy",
"--all-targets",
"--all-features",
// we don't want the first letter after "error: ", "help: " ... to be capitalized
// also no punctuation (except for "?" ?) at the end of a line
static REGEX_SET: LazyLock<RegexSet> = LazyLock::new(|| {
- RegexSet::new(&[
+ RegexSet::new([
r"error: [A-Z]",
r"help: [A-Z]",
r"warning: [A-Z]",
// sometimes the first character is capitalized and it is legal (like in "C-like enum variants") or
// we want to ask a question ending in "?"
static EXCEPTIONS_SET: LazyLock<RegexSet> = LazyLock::new(|| {
- RegexSet::new(&[
+ RegexSet::new([
r"\.\.\.$",
r".*C-like enum variant discriminant is not portable to 32-bit targets",
r".*Intel x86 assembly syntax used",
enforced-import-renames
enum-variant-name-threshold
enum-variant-size-threshold
+ large-error-threshold
literal-representation-threshold
max-fn-params-bools
max-include-file-size
fn main() {
// std::string::String and &str should trigger the lint failure with .ext12
- let _ = String::from("").ends_with(".ext12");
+ let _ = String::new().ends_with(".ext12");
let _ = "str".ends_with(".ext12");
// The test struct should not trigger the lint failure with .ext12
TestStruct {}.ends_with(".ext12");
// std::string::String and &str should trigger the lint failure with .EXT12
- let _ = String::from("").ends_with(".EXT12");
+ let _ = String::new().ends_with(".EXT12");
let _ = "str".ends_with(".EXT12");
// The test struct should not trigger the lint failure with .EXT12
TestStruct {}.ends_with(".EXT12");
// Should not trigger the lint failure with .eXT12
- let _ = String::from("").ends_with(".eXT12");
+ let _ = String::new().ends_with(".eXT12");
let _ = "str".ends_with(".eXT12");
TestStruct {}.ends_with(".eXT12");
// Should not trigger the lint failure with .EXT123 (too long)
- let _ = String::from("").ends_with(".EXT123");
+ let _ = String::new().ends_with(".EXT123");
let _ = "str".ends_with(".EXT123");
TestStruct {}.ends_with(".EXT123");
// Shouldn't fail if it doesn't start with a dot
- let _ = String::from("").ends_with("a.ext");
+ let _ = String::new().ends_with("a.ext");
let _ = "str".ends_with("a.extA");
TestStruct {}.ends_with("a.ext");
}
= help: consider using a case-insensitive comparison instead
error: case-sensitive file extension comparison
- --> $DIR/case_sensitive_file_extension_comparisons.rs:17:30
+ --> $DIR/case_sensitive_file_extension_comparisons.rs:17:27
|
-LL | let _ = String::from("").ends_with(".ext12");
- | ^^^^^^^^^^^^^^^^^^^
+LL | let _ = String::new().ends_with(".ext12");
+ | ^^^^^^^^^^^^^^^^^^^
|
= help: consider using a case-insensitive comparison instead
= help: consider using a case-insensitive comparison instead
error: case-sensitive file extension comparison
- --> $DIR/case_sensitive_file_extension_comparisons.rs:24:30
+ --> $DIR/case_sensitive_file_extension_comparisons.rs:24:27
|
-LL | let _ = String::from("").ends_with(".EXT12");
- | ^^^^^^^^^^^^^^^^^^^
+LL | let _ = String::new().ends_with(".EXT12");
+ | ^^^^^^^^^^^^^^^^^^^
|
= help: consider using a case-insensitive comparison instead
--- /dev/null
+// run-rustfix
+#![warn(clippy::cast_slice_from_raw_parts)]
+
+#[allow(unused_imports, unused_unsafe)]
+fn main() {
+ let mut vec = vec![0u8; 1];
+ let ptr: *const u8 = vec.as_ptr();
+ let mptr = vec.as_mut_ptr();
+ let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(ptr, 1) };
+ let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(mptr, 1) };
+ let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+ {
+ use core::slice;
+ let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+ use slice as one;
+ let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+ }
+ {
+ use std::slice;
+ let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+ use slice as one;
+ let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+ }
+}
--- /dev/null
+// run-rustfix
+#![warn(clippy::cast_slice_from_raw_parts)]
+
+#[allow(unused_imports, unused_unsafe)]
+fn main() {
+ let mut vec = vec![0u8; 1];
+ let ptr: *const u8 = vec.as_ptr();
+ let mptr = vec.as_mut_ptr();
+ let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) as *const [u8] };
+ let _: *const [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] };
+ let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) } as *const [u8];
+ {
+ use core::slice;
+ let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
+ use slice as one;
+ let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
+ }
+ {
+ use std::slice;
+ let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
+ use slice as one;
+ let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
+ }
+}
--- /dev/null
+error: casting the result of `from_raw_parts` to *const [u8]
+ --> $DIR/cast_raw_slice_pointer_cast.rs:9:35
+ |
+LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) as *const [u8] };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+ |
+ = note: `-D clippy::cast-slice-from-raw-parts` implied by `-D warnings`
+
+error: casting the result of `from_raw_parts_mut` to *mut [u8]
+ --> $DIR/cast_raw_slice_pointer_cast.rs:10:35
+ |
+LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts_mut(mptr, 1)`
+
+error: casting the result of `from_raw_parts` to *const [u8]
+ --> $DIR/cast_raw_slice_pointer_cast.rs:11:26
+ |
+LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) } as *const [u8];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+
+error: casting the result of `from_raw_parts` to *const [u8]
+ --> $DIR/cast_raw_slice_pointer_cast.rs:14:30
+ |
+LL | let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+
+error: casting the result of `from_raw_parts` to *const [u8]
+ --> $DIR/cast_raw_slice_pointer_cast.rs:16:30
+ |
+LL | let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+
+error: casting the result of `from_raw_parts` to *const [u8]
+ --> $DIR/cast_raw_slice_pointer_cast.rs:20:30
+ |
+LL | let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+
+error: casting the result of `from_raw_parts` to *const [u8]
+ --> $DIR/cast_raw_slice_pointer_cast.rs:22:30
+ |
+LL | let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+
+error: aborting due to 7 previous errors
+
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::collapsible_str_replace)]
+
+fn get_filter() -> char {
+ 'u'
+}
+
+fn main() {
+ let d = 'd';
+ let p = 'p';
+ let s = 's';
+ let u = 'u';
+ let l = "l";
+
+ let mut iter = ["l", "z"].iter();
+
+ // LINT CASES
+ let _ = "hesuo worpd".replace(['s', 'u'], "l");
+
+ let _ = "hesuo worpd".replace(['s', 'u'], l);
+
+ let _ = "hesuo worpd".replace(['s', 'u', 'p'], "l");
+
+ let _ = "hesuo worpd"
+ .replace(['s', 'u', 'p', 'd'], "l");
+
+ let _ = "hesuo world".replace([s, 'u'], "l");
+
+ let _ = "hesuo worpd".replace([s, 'u', 'p'], "l");
+
+ let _ = "hesuo worpd".replace([s, u, 'p'], "l");
+
+ let _ = "hesuo worpd".replace([s, u, p], "l");
+
+ let _ = "hesuo worlp".replace(['s', 'u'], "l").replace('p', "d");
+
+ let _ = "hesuo worpd".replace('s', "x").replace(['u', 'p'], "l");
+
+ // Note: Future iterations could lint `replace(|c| matches!(c, "su" | 'd' | 'p'), "l")`
+ let _ = "hesudo worpd".replace("su", "l").replace(['d', 'p'], "l");
+
+ let _ = "hesudo worpd".replace([d, 'p'], "l").replace("su", "l");
+
+ let _ = "hesuo world".replace([get_filter(), 's'], "l");
+
+ // NO LINT CASES
+ let _ = "hesuo world".replace('s', "l").replace('u', "p");
+
+ let _ = "hesuo worpd".replace('s', "l").replace('p', l);
+
+ let _ = "hesudo worpd".replace('d', "l").replace("su", "l").replace('p', "l");
+
+ // Note: Future iterations of `collapsible_str_replace` might lint this and combine to `[s, u, p]`
+ let _ = "hesuo worpd".replace([s, u], "l").replace([u, p], "l");
+
+ let _ = "hesuo worpd".replace(['s', 'u'], "l").replace(['u', 'p'], "l");
+
+ let _ = "hesuo worpd".replace('s', "l").replace(['u', 'p'], "l");
+
+ let _ = "hesuo worpd".replace(['s', 'u', 'p'], "l").replace('r', "l");
+
+ let _ = "hesuo worpd".replace(['s', 'u', 'p'], l).replace('r', l);
+
+ let _ = "hesuo worpd".replace(['s', u, 'p'], "l").replace('r', "l");
+
+ let _ = "hesuo worpd".replace([s, u], "l").replace(p, "l");
+
+ // Regression test
+ let _ = "hesuo worpd"
+ .replace('u', iter.next().unwrap())
+ .replace('s', iter.next().unwrap());
+}
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::collapsible_str_replace)]
+
+fn get_filter() -> char {
+ 'u'
+}
+
+fn main() {
+ let d = 'd';
+ let p = 'p';
+ let s = 's';
+ let u = 'u';
+ let l = "l";
+
+ let mut iter = ["l", "z"].iter();
+
+ // LINT CASES
+ let _ = "hesuo worpd".replace('s', "l").replace('u', "l");
+
+ let _ = "hesuo worpd".replace('s', l).replace('u', l);
+
+ let _ = "hesuo worpd".replace('s', "l").replace('u', "l").replace('p', "l");
+
+ let _ = "hesuo worpd"
+ .replace('s', "l")
+ .replace('u', "l")
+ .replace('p', "l")
+ .replace('d', "l");
+
+ let _ = "hesuo world".replace(s, "l").replace('u', "l");
+
+ let _ = "hesuo worpd".replace(s, "l").replace('u', "l").replace('p', "l");
+
+ let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace('p', "l");
+
+ let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace(p, "l");
+
+ let _ = "hesuo worlp".replace('s', "l").replace('u', "l").replace('p', "d");
+
+ let _ = "hesuo worpd".replace('s', "x").replace('u', "l").replace('p', "l");
+
+ // Note: Future iterations could lint `replace(|c| matches!(c, "su" | 'd' | 'p'), "l")`
+ let _ = "hesudo worpd".replace("su", "l").replace('d', "l").replace('p', "l");
+
+ let _ = "hesudo worpd".replace(d, "l").replace('p', "l").replace("su", "l");
+
+ let _ = "hesuo world".replace(get_filter(), "l").replace('s', "l");
+
+ // NO LINT CASES
+ let _ = "hesuo world".replace('s', "l").replace('u', "p");
+
+ let _ = "hesuo worpd".replace('s', "l").replace('p', l);
+
+ let _ = "hesudo worpd".replace('d', "l").replace("su", "l").replace('p', "l");
+
+ // Note: Future iterations of `collapsible_str_replace` might lint this and combine to `[s, u, p]`
+ let _ = "hesuo worpd".replace([s, u], "l").replace([u, p], "l");
+
+ let _ = "hesuo worpd".replace(['s', 'u'], "l").replace(['u', 'p'], "l");
+
+ let _ = "hesuo worpd".replace('s', "l").replace(['u', 'p'], "l");
+
+ let _ = "hesuo worpd".replace(['s', 'u', 'p'], "l").replace('r', "l");
+
+ let _ = "hesuo worpd".replace(['s', 'u', 'p'], l).replace('r', l);
+
+ let _ = "hesuo worpd".replace(['s', u, 'p'], "l").replace('r', "l");
+
+ let _ = "hesuo worpd".replace([s, u], "l").replace(p, "l");
+
+ // Regression test
+ let _ = "hesuo worpd"
+ .replace('u', iter.next().unwrap())
+ .replace('s', iter.next().unwrap());
+}
--- /dev/null
+error: used consecutive `str::replace` call
+ --> $DIR/collapsible_str_replace.rs:19:27
+ |
+LL | let _ = "hesuo worpd".replace('s', "l").replace('u', "l");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], "l")`
+ |
+ = note: `-D clippy::collapsible-str-replace` implied by `-D warnings`
+
+error: used consecutive `str::replace` call
+ --> $DIR/collapsible_str_replace.rs:21:27
+ |
+LL | let _ = "hesuo worpd".replace('s', l).replace('u', l);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], l)`
+
+error: used consecutive `str::replace` call
+ --> $DIR/collapsible_str_replace.rs:23:27
+ |
+LL | let _ = "hesuo worpd".replace('s', "l").replace('u', "l").replace('p', "l");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u', 'p'], "l")`
+
+error: used consecutive `str::replace` call
+ --> $DIR/collapsible_str_replace.rs:26:10
+ |
+LL | .replace('s', "l")
+ | __________^
+LL | | .replace('u', "l")
+LL | | .replace('p', "l")
+LL | | .replace('d', "l");
+ | |__________________________^ help: replace with: `replace(['s', 'u', 'p', 'd'], "l")`
+
+error: used consecutive `str::replace` call
+ --> $DIR/collapsible_str_replace.rs:31:27
+ |
+LL | let _ = "hesuo world".replace(s, "l").replace('u', "l");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, 'u'], "l")`
+
+error: used consecutive `str::replace` call
+ --> $DIR/collapsible_str_replace.rs:33:27
+ |
+LL | let _ = "hesuo worpd".replace(s, "l").replace('u', "l").replace('p', "l");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, 'u', 'p'], "l")`
+
+error: used consecutive `str::replace` call
+ --> $DIR/collapsible_str_replace.rs:35:27
+ |
+LL | let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace('p', "l");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, u, 'p'], "l")`
+
+error: used consecutive `str::replace` call
+ --> $DIR/collapsible_str_replace.rs:37:27
+ |
+LL | let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace(p, "l");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, u, p], "l")`
+
+error: used consecutive `str::replace` call
+ --> $DIR/collapsible_str_replace.rs:39:27
+ |
+LL | let _ = "hesuo worlp".replace('s', "l").replace('u', "l").replace('p', "d");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], "l")`
+
+error: used consecutive `str::replace` call
+ --> $DIR/collapsible_str_replace.rs:41:45
+ |
+LL | let _ = "hesuo worpd".replace('s', "x").replace('u', "l").replace('p', "l");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['u', 'p'], "l")`
+
+error: used consecutive `str::replace` call
+ --> $DIR/collapsible_str_replace.rs:44:47
+ |
+LL | let _ = "hesudo worpd".replace("su", "l").replace('d', "l").replace('p', "l");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['d', 'p'], "l")`
+
+error: used consecutive `str::replace` call
+ --> $DIR/collapsible_str_replace.rs:46:28
+ |
+LL | let _ = "hesudo worpd".replace(d, "l").replace('p', "l").replace("su", "l");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([d, 'p'], "l")`
+
+error: used consecutive `str::replace` call
+ --> $DIR/collapsible_str_replace.rs:48:27
+ |
+LL | let _ = "hesuo world".replace(get_filter(), "l").replace('s', "l");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([get_filter(), 's'], "l")`
+
+error: aborting due to 13 previous errors
+
}
fn expect_result() {
- let res: Result<u8, ()> = Ok(0);
+ let res: Result<u8, u8> = Ok(0);
let _ = res.expect("");
+ let _ = res.expect_err("");
}
fn main() {
|
= help: if this value is an `Err`, it will panic
-error: aborting due to 2 previous errors
+error: used `expect_err()` on `a Result` value
+ --> $DIR/expect.rs:11:13
+ |
+LL | let _ = res.expect_err("");
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: if this value is an `Ok`, it will panic
+
+error: aborting due to 3 previous errors
let x = 2f32;
let _ = x.exp_m1();
let _ = x.exp_m1() + 2.0;
+ let _ = (x as f32).exp_m1() + 2.0;
// Cases where the lint shouldn't be applied
let _ = x.exp() - 2.0;
let _ = x.exp() - 1.0 * 2.0;
let x = 2f32;
let _ = x.exp() - 1.0;
let _ = x.exp() - 1.0 + 2.0;
+ let _ = (x as f32).exp() - 1.0 + 2.0;
// Cases where the lint shouldn't be applied
let _ = x.exp() - 2.0;
let _ = x.exp() - 1.0 * 2.0;
| ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
error: (e.pow(x) - 1) can be computed more accurately
- --> $DIR/floating_point_exp.rs:13:13
+ --> $DIR/floating_point_exp.rs:8:13
+ |
+LL | let _ = (x as f32).exp() - 1.0 + 2.0;
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).exp_m1()`
+
+error: (e.pow(x) - 1) can be computed more accurately
+ --> $DIR/floating_point_exp.rs:14:13
|
LL | let _ = x.exp() - 1.0;
| ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
error: (e.pow(x) - 1) can be computed more accurately
- --> $DIR/floating_point_exp.rs:14:13
+ --> $DIR/floating_point_exp.rs:15:13
|
LL | let _ = x.exp() - 1.0 + 2.0;
| ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
-error: aborting due to 4 previous errors
+error: aborting due to 5 previous errors
let _ = x.ln();
let _ = x.log2();
let _ = x.ln();
+ let _ = (x as f32).log2();
let x = 1f64;
let _ = x.log2();
let _ = x.log(std::f32::consts::E);
let _ = x.log(TWO);
let _ = x.log(E);
+ let _ = (x as f32).log(2f32);
let x = 1f64;
let _ = x.log(2f64);
| ^^^^^^^^ help: consider using: `x.ln()`
error: logarithm for bases 2, 10 and e can be computed more accurately
- --> $DIR/floating_point_log.rs:17:13
+ --> $DIR/floating_point_log.rs:15:13
+ |
+LL | let _ = (x as f32).log(2f32);
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).log2()`
+
+error: logarithm for bases 2, 10 and e can be computed more accurately
+ --> $DIR/floating_point_log.rs:18:13
|
LL | let _ = x.log(2f64);
| ^^^^^^^^^^^ help: consider using: `x.log2()`
error: logarithm for bases 2, 10 and e can be computed more accurately
- --> $DIR/floating_point_log.rs:18:13
+ --> $DIR/floating_point_log.rs:19:13
|
LL | let _ = x.log(10f64);
| ^^^^^^^^^^^^ help: consider using: `x.log10()`
error: logarithm for bases 2, 10 and e can be computed more accurately
- --> $DIR/floating_point_log.rs:19:13
+ --> $DIR/floating_point_log.rs:20:13
|
LL | let _ = x.log(std::f64::consts::E);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:24:13
+ --> $DIR/floating_point_log.rs:25:13
|
LL | let _ = (1f32 + 2.).ln();
| ^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()`
= note: `-D clippy::imprecise-flops` implied by `-D warnings`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:25:13
+ --> $DIR/floating_point_log.rs:26:13
|
LL | let _ = (1f32 + 2.0).ln();
| ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:26:13
+ --> $DIR/floating_point_log.rs:27:13
|
LL | let _ = (1.0 + x).ln();
| ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:27:13
+ --> $DIR/floating_point_log.rs:28:13
|
LL | let _ = (1.0 + x / 2.0).ln();
| ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:28:13
+ --> $DIR/floating_point_log.rs:29:13
|
LL | let _ = (1.0 + x.powi(3)).ln();
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:29:13
+ --> $DIR/floating_point_log.rs:30:13
|
LL | let _ = (1.0 + x.powi(3) / 2.0).ln();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(3) / 2.0).ln_1p()`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:30:13
+ --> $DIR/floating_point_log.rs:31:13
|
LL | let _ = (1.0 + (std::f32::consts::E - 1.0)).ln();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(std::f32::consts::E - 1.0).ln_1p()`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:31:13
+ --> $DIR/floating_point_log.rs:32:13
|
LL | let _ = (x + 1.0).ln();
| ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:32:13
+ --> $DIR/floating_point_log.rs:33:13
|
LL | let _ = (x.powi(3) + 1.0).ln();
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:33:13
+ --> $DIR/floating_point_log.rs:34:13
|
LL | let _ = (x + 2.0 + 1.0).ln();
| ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:34:13
+ --> $DIR/floating_point_log.rs:35:13
|
LL | let _ = (x / 2.0 + 1.0).ln();
| ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:42:13
+ --> $DIR/floating_point_log.rs:43:13
|
LL | let _ = (1f64 + 2.).ln();
| ^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:43:13
+ --> $DIR/floating_point_log.rs:44:13
|
LL | let _ = (1f64 + 2.0).ln();
| ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:44:13
+ --> $DIR/floating_point_log.rs:45:13
|
LL | let _ = (1.0 + x).ln();
| ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:45:13
+ --> $DIR/floating_point_log.rs:46:13
|
LL | let _ = (1.0 + x / 2.0).ln();
| ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:46:13
+ --> $DIR/floating_point_log.rs:47:13
|
LL | let _ = (1.0 + x.powi(3)).ln();
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:47:13
+ --> $DIR/floating_point_log.rs:48:13
|
LL | let _ = (x + 1.0).ln();
| ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:48:13
+ --> $DIR/floating_point_log.rs:49:13
|
LL | let _ = (x.powi(3) + 1.0).ln();
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:49:13
+ --> $DIR/floating_point_log.rs:50:13
|
LL | let _ = (x + 2.0 + 1.0).ln();
| ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()`
error: ln(1 + x) can be computed more accurately
- --> $DIR/floating_point_log.rs:50:13
+ --> $DIR/floating_point_log.rs:51:13
|
LL | let _ = (x / 2.0 + 1.0).ln();
| ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
-error: aborting due to 28 previous errors
+error: aborting due to 29 previous errors
let x = 3f32;
let y = 5f32;
let _ = x.log(y);
+ let _ = (x as f32).log(y);
let _ = x.log(y);
let _ = x.log(y);
let _ = x.log(y);
let x = 3f32;
let y = 5f32;
let _ = x.ln() / y.ln();
+ let _ = (x as f32).ln() / y.ln();
let _ = x.log2() / y.log2();
let _ = x.log10() / y.log10();
let _ = x.log(5f32) / y.log(5f32);
error: log base can be expressed more clearly
--> $DIR/floating_point_logbase.rs:8:13
|
+LL | let _ = (x as f32).ln() / y.ln();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).log(y)`
+
+error: log base can be expressed more clearly
+ --> $DIR/floating_point_logbase.rs:9:13
+ |
LL | let _ = x.log2() / y.log2();
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
error: log base can be expressed more clearly
- --> $DIR/floating_point_logbase.rs:9:13
+ --> $DIR/floating_point_logbase.rs:10:13
|
LL | let _ = x.log10() / y.log10();
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
error: log base can be expressed more clearly
- --> $DIR/floating_point_logbase.rs:10:13
+ --> $DIR/floating_point_logbase.rs:11:13
|
LL | let _ = x.log(5f32) / y.log(5f32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
-error: aborting due to 4 previous errors
+error: aborting due to 5 previous errors
let _ = (-3.1f32).exp();
let _ = x.sqrt();
let _ = x.cbrt();
+ let _ = (x as f32).cbrt();
let _ = x.powi(3);
let _ = x.powi(-2);
let _ = x.powi(16_777_215);
let _ = x.powi(-16_777_215);
+ let _ = (x as f32).powi(-16_777_215);
+ let _ = (x as f32).powi(3);
// Cases where the lint shouldn't be applied
let _ = x.powf(2.1);
let _ = x.powf(-2.1);
let _ = std::f32::consts::E.powf(-3.1);
let _ = x.powf(1.0 / 2.0);
let _ = x.powf(1.0 / 3.0);
+ let _ = (x as f32).powf(1.0 / 3.0);
let _ = x.powf(3.0);
let _ = x.powf(-2.0);
let _ = x.powf(16_777_215.0);
let _ = x.powf(-16_777_215.0);
+ let _ = (x as f32).powf(-16_777_215.0);
+ let _ = (x as f32).powf(3.0);
// Cases where the lint shouldn't be applied
let _ = x.powf(2.1);
let _ = x.powf(-2.1);
|
= note: `-D clippy::imprecise-flops` implied by `-D warnings`
-error: exponentiation with integer powers can be computed more efficiently
+error: cube-root of a number can be computed more accurately
--> $DIR/floating_point_powf.rs:14:13
|
+LL | let _ = (x as f32).powf(1.0 / 3.0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).cbrt()`
+
+error: exponentiation with integer powers can be computed more efficiently
+ --> $DIR/floating_point_powf.rs:15:13
+ |
LL | let _ = x.powf(3.0);
| ^^^^^^^^^^^ help: consider using: `x.powi(3)`
error: exponentiation with integer powers can be computed more efficiently
- --> $DIR/floating_point_powf.rs:15:13
+ --> $DIR/floating_point_powf.rs:16:13
|
LL | let _ = x.powf(-2.0);
| ^^^^^^^^^^^^ help: consider using: `x.powi(-2)`
error: exponentiation with integer powers can be computed more efficiently
- --> $DIR/floating_point_powf.rs:16:13
+ --> $DIR/floating_point_powf.rs:17:13
|
LL | let _ = x.powf(16_777_215.0);
| ^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(16_777_215)`
error: exponentiation with integer powers can be computed more efficiently
- --> $DIR/floating_point_powf.rs:17:13
+ --> $DIR/floating_point_powf.rs:18:13
|
LL | let _ = x.powf(-16_777_215.0);
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-16_777_215)`
+error: exponentiation with integer powers can be computed more efficiently
+ --> $DIR/floating_point_powf.rs:19:13
+ |
+LL | let _ = (x as f32).powf(-16_777_215.0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).powi(-16_777_215)`
+
+error: exponentiation with integer powers can be computed more efficiently
+ --> $DIR/floating_point_powf.rs:20:13
+ |
+LL | let _ = (x as f32).powf(3.0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).powi(3)`
+
error: exponent for bases 2 and e can be computed more accurately
- --> $DIR/floating_point_powf.rs:25:13
+ --> $DIR/floating_point_powf.rs:28:13
|
LL | let _ = 2f64.powf(x);
| ^^^^^^^^^^^^ help: consider using: `x.exp2()`
error: exponent for bases 2 and e can be computed more accurately
- --> $DIR/floating_point_powf.rs:26:13
+ --> $DIR/floating_point_powf.rs:29:13
|
LL | let _ = 2f64.powf(3.1);
| ^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp2()`
error: exponent for bases 2 and e can be computed more accurately
- --> $DIR/floating_point_powf.rs:27:13
+ --> $DIR/floating_point_powf.rs:30:13
|
LL | let _ = 2f64.powf(-3.1);
| ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp2()`
error: exponent for bases 2 and e can be computed more accurately
- --> $DIR/floating_point_powf.rs:28:13
+ --> $DIR/floating_point_powf.rs:31:13
|
LL | let _ = std::f64::consts::E.powf(x);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()`
error: exponent for bases 2 and e can be computed more accurately
- --> $DIR/floating_point_powf.rs:29:13
+ --> $DIR/floating_point_powf.rs:32:13
|
LL | let _ = std::f64::consts::E.powf(3.1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp()`
error: exponent for bases 2 and e can be computed more accurately
- --> $DIR/floating_point_powf.rs:30:13
+ --> $DIR/floating_point_powf.rs:33:13
|
LL | let _ = std::f64::consts::E.powf(-3.1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp()`
error: square-root of a number can be computed more efficiently and accurately
- --> $DIR/floating_point_powf.rs:31:13
+ --> $DIR/floating_point_powf.rs:34:13
|
LL | let _ = x.powf(1.0 / 2.0);
| ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()`
error: cube-root of a number can be computed more accurately
- --> $DIR/floating_point_powf.rs:32:13
+ --> $DIR/floating_point_powf.rs:35:13
|
LL | let _ = x.powf(1.0 / 3.0);
| ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()`
error: exponentiation with integer powers can be computed more efficiently
- --> $DIR/floating_point_powf.rs:33:13
+ --> $DIR/floating_point_powf.rs:36:13
|
LL | let _ = x.powf(3.0);
| ^^^^^^^^^^^ help: consider using: `x.powi(3)`
error: exponentiation with integer powers can be computed more efficiently
- --> $DIR/floating_point_powf.rs:34:13
+ --> $DIR/floating_point_powf.rs:37:13
|
LL | let _ = x.powf(-2.0);
| ^^^^^^^^^^^^ help: consider using: `x.powi(-2)`
error: exponentiation with integer powers can be computed more efficiently
- --> $DIR/floating_point_powf.rs:35:13
+ --> $DIR/floating_point_powf.rs:38:13
|
LL | let _ = x.powf(-2_147_483_648.0);
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-2_147_483_648)`
error: exponentiation with integer powers can be computed more efficiently
- --> $DIR/floating_point_powf.rs:36:13
+ --> $DIR/floating_point_powf.rs:39:13
|
LL | let _ = x.powf(2_147_483_647.0);
| ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2_147_483_647)`
-error: aborting due to 24 previous errors
+error: aborting due to 27 previous errors
let y = 4f32;
let _ = x.mul_add(x, y);
let _ = y.mul_add(y, x);
+ let _ = (y as f32).mul_add(y as f32, x);
let _ = x.mul_add(x, y).sqrt();
let _ = y.mul_add(y, x).sqrt();
// Cases where the lint shouldn't be applied
let y = 4f32;
let _ = x.powi(2) + y;
let _ = x + y.powi(2);
+ let _ = x + (y as f32).powi(2);
let _ = (x.powi(2) + y).sqrt();
let _ = (x + y.powi(2)).sqrt();
// Cases where the lint shouldn't be applied
error: multiply and add expressions can be calculated more efficiently and accurately
--> $DIR/floating_point_powi.rs:11:13
|
+LL | let _ = x + (y as f32).powi(2);
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y as f32).mul_add(y as f32, x)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+ --> $DIR/floating_point_powi.rs:12:13
+ |
LL | let _ = (x.powi(2) + y).sqrt();
| ^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)`
error: multiply and add expressions can be calculated more efficiently and accurately
- --> $DIR/floating_point_powi.rs:12:13
+ --> $DIR/floating_point_powi.rs:13:13
|
LL | let _ = (x + y.powi(2)).sqrt();
| ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)`
-error: aborting due to 4 previous errors
+error: aborting due to 5 previous errors
let _ = x * 180f32 / std::f32::consts::PI;
}
+pub fn issue9391(degrees: i64) {
+ let _ = (degrees as f64).to_radians();
+ let _ = (degrees as f64).to_degrees();
+}
+
fn main() {
let x = 3f32;
let _ = x.to_degrees();
let _ = x * 180f32 / std::f32::consts::PI;
}
+pub fn issue9391(degrees: i64) {
+ let _ = degrees as f64 * std::f64::consts::PI / 180.0;
+ let _ = degrees as f64 * 180.0 / std::f64::consts::PI;
+}
+
fn main() {
let x = 3f32;
let _ = x * 180f32 / std::f32::consts::PI;
+error: conversion to radians can be done more accurately
+ --> $DIR/floating_point_rad.rs:12:13
+ |
+LL | let _ = degrees as f64 * std::f64::consts::PI / 180.0;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(degrees as f64).to_radians()`
+ |
+ = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
error: conversion to degrees can be done more accurately
--> $DIR/floating_point_rad.rs:13:13
|
+LL | let _ = degrees as f64 * 180.0 / std::f64::consts::PI;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(degrees as f64).to_degrees()`
+
+error: conversion to degrees can be done more accurately
+ --> $DIR/floating_point_rad.rs:18:13
+ |
LL | let _ = x * 180f32 / std::f32::consts::PI;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_degrees()`
- |
- = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
error: conversion to degrees can be done more accurately
- --> $DIR/floating_point_rad.rs:14:13
+ --> $DIR/floating_point_rad.rs:19:13
|
LL | let _ = 90. * 180f64 / std::f64::consts::PI;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.0_f64.to_degrees()`
error: conversion to degrees can be done more accurately
- --> $DIR/floating_point_rad.rs:15:13
+ --> $DIR/floating_point_rad.rs:20:13
|
LL | let _ = 90.5 * 180f64 / std::f64::consts::PI;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.5_f64.to_degrees()`
error: conversion to radians can be done more accurately
- --> $DIR/floating_point_rad.rs:16:13
+ --> $DIR/floating_point_rad.rs:21:13
|
LL | let _ = x * std::f32::consts::PI / 180f32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_radians()`
error: conversion to radians can be done more accurately
- --> $DIR/floating_point_rad.rs:17:13
+ --> $DIR/floating_point_rad.rs:22:13
|
LL | let _ = 90. * std::f32::consts::PI / 180f32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.0_f64.to_radians()`
error: conversion to radians can be done more accurately
- --> $DIR/floating_point_rad.rs:18:13
+ --> $DIR/floating_point_rad.rs:23:13
|
LL | let _ = 90.5 * std::f32::consts::PI / 180f32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.5_f64.to_radians()`
-error: aborting due to 6 previous errors
+error: aborting due to 8 previous errors
format!("foo {}", "bar");
format!("{} bar", "foo");
- let arg: String = "".to_owned();
+ let arg = String::new();
arg.to_string();
format!("{:?}", arg); // Don't warn about debug.
format!("{:8}", arg);
format!("foo {}", "bar");
format!("{} bar", "foo");
- let arg: String = "".to_owned();
+ let arg = String::new();
format!("{}", arg);
format!("{:?}", arg); // Don't warn about debug.
format!("{:8}", arg);
// run-rustfix
-#![allow(unreachable_code)]
-#![allow(unused_macros)]
-#![allow(unused_variables)]
+#![allow(unused)]
#![allow(clippy::assertions_on_constants)]
#![allow(clippy::eq_op)]
#![allow(clippy::print_literal)]
// https://github.com/rust-lang/rust-clippy/issues/7903
println!("{foo}{foo:?}", foo = "foo".to_string());
}
+
+fn issue8643(vendor_id: usize, product_id: usize, name: &str) {
+ println!(
+ "{:<9} {:<10} {}",
+ format!("0x{:x}", vendor_id),
+ format!("0x{:x}", product_id),
+ name
+ );
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/8855
+mod issue_8855 {
+ #![allow(dead_code)]
+
+ struct A {}
+
+ impl std::fmt::Display for A {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "test")
+ }
+ }
+
+ fn main() {
+ let a = A {};
+ let b = A {};
+
+ let x = format!("{} {}", a, b);
+ dbg!(x);
+
+ let x = format!("{:>6} {:>6}", a, b.to_string());
+ dbg!(x);
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/9256
+mod issue_9256 {
+ #![allow(dead_code)]
+
+ fn print_substring(original: &str) {
+ assert!(original.len() > 10);
+ println!("{}", &original[..10]);
+ }
+
+ fn main() {
+ print_substring("Hello, world!");
+ }
+}
// run-rustfix
-#![allow(unreachable_code)]
-#![allow(unused_macros)]
-#![allow(unused_variables)]
+#![allow(unused)]
#![allow(clippy::assertions_on_constants)]
#![allow(clippy::eq_op)]
#![allow(clippy::print_literal)]
// https://github.com/rust-lang/rust-clippy/issues/7903
println!("{foo}{foo:?}", foo = "foo".to_string());
}
+
+fn issue8643(vendor_id: usize, product_id: usize, name: &str) {
+ println!(
+ "{:<9} {:<10} {}",
+ format!("0x{:x}", vendor_id),
+ format!("0x{:x}", product_id),
+ name
+ );
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/8855
+mod issue_8855 {
+ #![allow(dead_code)]
+
+ struct A {}
+
+ impl std::fmt::Display for A {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "test")
+ }
+ }
+
+ fn main() {
+ let a = A {};
+ let b = A {};
+
+ let x = format!("{} {}", a, b.to_string());
+ dbg!(x);
+
+ let x = format!("{:>6} {:>6}", a, b.to_string());
+ dbg!(x);
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/9256
+mod issue_9256 {
+ #![allow(dead_code)]
+
+ fn print_substring(original: &str) {
+ assert!(original.len() > 10);
+ println!("{}", original[..10].to_string());
+ }
+
+ fn main() {
+ print_substring("Hello, world!");
+ }
+}
error: `to_string` applied to a type that implements `Display` in `format!` args
- --> $DIR/format_args.rs:76:72
+ --> $DIR/format_args.rs:74:72
|
LL | let _ = format!("error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
= note: `-D clippy::to-string-in-format-args` implied by `-D warnings`
error: `to_string` applied to a type that implements `Display` in `write!` args
- --> $DIR/format_args.rs:80:27
+ --> $DIR/format_args.rs:78:27
|
LL | Location::caller().to_string()
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `writeln!` args
- --> $DIR/format_args.rs:85:27
+ --> $DIR/format_args.rs:83:27
|
LL | Location::caller().to_string()
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `print!` args
- --> $DIR/format_args.rs:87:63
+ --> $DIR/format_args.rs:85:63
|
LL | print!("error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:88:65
+ --> $DIR/format_args.rs:86:65
|
LL | println!("error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `eprint!` args
- --> $DIR/format_args.rs:89:64
+ --> $DIR/format_args.rs:87:64
|
LL | eprint!("error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `eprintln!` args
- --> $DIR/format_args.rs:90:66
+ --> $DIR/format_args.rs:88:66
|
LL | eprintln!("error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `format_args!` args
- --> $DIR/format_args.rs:91:77
+ --> $DIR/format_args.rs:89:77
|
LL | let _ = format_args!("error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `assert!` args
- --> $DIR/format_args.rs:92:70
+ --> $DIR/format_args.rs:90:70
|
LL | assert!(true, "error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `assert_eq!` args
- --> $DIR/format_args.rs:93:73
+ --> $DIR/format_args.rs:91:73
|
LL | assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `assert_ne!` args
- --> $DIR/format_args.rs:94:73
+ --> $DIR/format_args.rs:92:73
|
LL | assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `panic!` args
- --> $DIR/format_args.rs:95:63
+ --> $DIR/format_args.rs:93:63
|
LL | panic!("error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:96:20
+ --> $DIR/format_args.rs:94:20
|
LL | println!("{}", X(1).to_string());
| ^^^^^^^^^^^^^^^^ help: use this: `*X(1)`
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:97:20
+ --> $DIR/format_args.rs:95:20
|
LL | println!("{}", Y(&X(1)).to_string());
| ^^^^^^^^^^^^^^^^^^^^ help: use this: `***Y(&X(1))`
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:98:24
+ --> $DIR/format_args.rs:96:24
|
LL | println!("{}", Z(1).to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:99:20
+ --> $DIR/format_args.rs:97:20
|
LL | println!("{}", x.to_string());
| ^^^^^^^^^^^^^ help: use this: `**x`
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:100:20
+ --> $DIR/format_args.rs:98:20
|
LL | println!("{}", x_ref.to_string());
| ^^^^^^^^^^^^^^^^^ help: use this: `***x_ref`
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:102:39
+ --> $DIR/format_args.rs:100:39
|
LL | println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar");
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:103:52
+ --> $DIR/format_args.rs:101:52
|
LL | println!("{foo}{bar}", foo = "foo", bar = "bar".to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:104:39
+ --> $DIR/format_args.rs:102:39
|
LL | println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo");
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:105:52
+ --> $DIR/format_args.rs:103:52
|
LL | println!("{foo}{bar}", bar = "bar", foo = "foo".to_string());
| ^^^^^^^^^^^^ help: remove this
-error: aborting due to 21 previous errors
+error: `to_string` applied to a type that implements `Display` in `format!` args
+ --> $DIR/format_args.rs:142:38
+ |
+LL | let x = format!("{} {}", a, b.to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `println!` args
+ --> $DIR/format_args.rs:156:24
+ |
+LL | println!("{}", original[..10].to_string());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use this: `&original[..10]`
+
+error: aborting due to 23 previous errors
&x;
x;
- let mut a = A("".into());
+ let mut a = A(String::new());
let b = a << 0; // no error: non-integer
1 * Meter; // no error: non-integer
&x >> 0;
x >> &0;
- let mut a = A("".into());
+ let mut a = A(String::new());
let b = a << 0; // no error: non-integer
1 * Meter; // no error: non-integer
};
}
+fn mutex_ref(mutex: &Mutex<i32>) {
+ if let Ok(i) = mutex.lock() {
+ do_stuff(i);
+ } else {
+ let _x = mutex.lock();
+ };
+}
+
fn main() {}
error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
--> $DIR/if_let_mutex.rs:10:5
|
-LL | / if let Err(locked) = m.lock() {
+LL | if let Err(locked) = m.lock() {
+ | ^ - this Mutex will remain locked for the entire `if let`-block...
+ | _____|
+ | |
LL | | do_stuff(locked);
LL | | } else {
LL | | let lock = m.lock().unwrap();
+ | | - ... and is tried to lock again here, which will always deadlock.
LL | | do_stuff(lock);
LL | | };
| |_____^
error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
--> $DIR/if_let_mutex.rs:22:5
|
-LL | / if let Some(locked) = m.lock().unwrap().deref() {
+LL | if let Some(locked) = m.lock().unwrap().deref() {
+ | ^ - this Mutex will remain locked for the entire `if let`-block...
+ | _____|
+ | |
LL | | do_stuff(locked);
LL | | } else {
LL | | let lock = m.lock().unwrap();
+ | | - ... and is tried to lock again here, which will always deadlock.
LL | | do_stuff(lock);
LL | | };
| |_____^
|
= help: move the lock call outside of the `if let ...` expression
-error: aborting due to 2 previous errors
+error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
+ --> $DIR/if_let_mutex.rs:43:5
+ |
+LL | if let Ok(i) = mutex.lock() {
+ | ^ ----- this Mutex will remain locked for the entire `if let`-block...
+ | _____|
+ | |
+LL | | do_stuff(i);
+LL | | } else {
+LL | | let _x = mutex.lock();
+ | | ----- ... and is tried to lock again here, which will always deadlock.
+LL | | };
+ | |_____^
+ |
+ = help: move the lock call outside of the `if let ...` expression
+
+error: aborting due to 3 previous errors
|
= help: consider using `bool::then` like: `matches!(true, true).then(|| { /* snippet */ matches!(true, false) })`
-error: this could be simplified with `bool::then`
+error: this could be simplified with `bool::then_some`
--> $DIR/if_then_some_else_none.rs:23:28
|
LL | let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = help: consider using `bool::then` like: `(o < 32).then(|| o)`
+ = help: consider using `bool::then_some` like: `(o < 32).then_some(o)`
-error: this could be simplified with `bool::then`
+error: this could be simplified with `bool::then_some`
--> $DIR/if_then_some_else_none.rs:27:13
|
LL | let _ = if !x { Some(0) } else { None };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = help: consider using `bool::then` like: `(!x).then(|| 0)`
+ = help: consider using `bool::then_some` like: `(!x).then_some(0)`
error: this could be simplified with `bool::then`
--> $DIR/if_then_some_else_none.rs:82:13
// We also check the out_of_bounds_indexing lint here, because it lints similar things and
// we want to avoid false positives.
#![warn(clippy::out_of_bounds_indexing)]
-#![allow(const_err, clippy::no_effect, clippy::unnecessary_operation)]
+#![allow(const_err, unconditional_panic, clippy::no_effect, clippy::unnecessary_operation)]
const ARR: [i32; 2] = [1, 2];
const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr.
--- /dev/null
+// run-rustfix
+#![warn(clippy::iter_on_empty_collections)]
+#![allow(clippy::iter_next_slice, clippy::redundant_clone)]
+
+fn array() {
+ assert_eq!(std::iter::empty().next(), Option::<i32>::None);
+ assert_eq!(std::iter::empty().next(), Option::<&mut i32>::None);
+ assert_eq!(std::iter::empty().next(), Option::<&i32>::None);
+ assert_eq!(std::iter::empty().next(), Option::<i32>::None);
+ assert_eq!(std::iter::empty().next(), Option::<&mut i32>::None);
+ assert_eq!(std::iter::empty().next(), Option::<&i32>::None);
+
+ // Don't trigger on non-iter methods
+ let _: Option<String> = None.clone();
+ let _: [String; 0] = [].clone();
+
+ // Don't trigger on match or if branches
+ let _ = match 123 {
+ 123 => [].iter(),
+ _ => ["test"].iter(),
+ };
+
+ let _ = if false { ["test"].iter() } else { [].iter() };
+}
+
+macro_rules! in_macros {
+ () => {
+ assert_eq!([].into_iter().next(), Option::<i32>::None);
+ assert_eq!([].iter_mut().next(), Option::<&mut i32>::None);
+ assert_eq!([].iter().next(), Option::<&i32>::None);
+ assert_eq!(None.into_iter().next(), Option::<i32>::None);
+ assert_eq!(None.iter_mut().next(), Option::<&mut i32>::None);
+ assert_eq!(None.iter().next(), Option::<&i32>::None);
+ };
+}
+
+// Don't trigger on a `None` that isn't std's option
+mod custom_option {
+ #[allow(unused)]
+ enum CustomOption {
+ Some(i32),
+ None,
+ }
+
+ impl CustomOption {
+ fn iter(&self) {}
+ fn iter_mut(&mut self) {}
+ fn into_iter(self) {}
+ }
+ use CustomOption::*;
+
+ pub fn custom_option() {
+ None.iter();
+ None.iter_mut();
+ None.into_iter();
+ }
+}
+
+fn main() {
+ array();
+ custom_option::custom_option();
+ in_macros!();
+}
--- /dev/null
+// run-rustfix
+#![warn(clippy::iter_on_empty_collections)]
+#![allow(clippy::iter_next_slice, clippy::redundant_clone)]
+
+fn array() {
+ assert_eq!([].into_iter().next(), Option::<i32>::None);
+ assert_eq!([].iter_mut().next(), Option::<&mut i32>::None);
+ assert_eq!([].iter().next(), Option::<&i32>::None);
+ assert_eq!(None.into_iter().next(), Option::<i32>::None);
+ assert_eq!(None.iter_mut().next(), Option::<&mut i32>::None);
+ assert_eq!(None.iter().next(), Option::<&i32>::None);
+
+ // Don't trigger on non-iter methods
+ let _: Option<String> = None.clone();
+ let _: [String; 0] = [].clone();
+
+ // Don't trigger on match or if branches
+ let _ = match 123 {
+ 123 => [].iter(),
+ _ => ["test"].iter(),
+ };
+
+ let _ = if false { ["test"].iter() } else { [].iter() };
+}
+
+macro_rules! in_macros {
+ () => {
+ assert_eq!([].into_iter().next(), Option::<i32>::None);
+ assert_eq!([].iter_mut().next(), Option::<&mut i32>::None);
+ assert_eq!([].iter().next(), Option::<&i32>::None);
+ assert_eq!(None.into_iter().next(), Option::<i32>::None);
+ assert_eq!(None.iter_mut().next(), Option::<&mut i32>::None);
+ assert_eq!(None.iter().next(), Option::<&i32>::None);
+ };
+}
+
+// Don't trigger on a `None` that isn't std's option
+mod custom_option {
+ #[allow(unused)]
+ enum CustomOption {
+ Some(i32),
+ None,
+ }
+
+ impl CustomOption {
+ fn iter(&self) {}
+ fn iter_mut(&mut self) {}
+ fn into_iter(self) {}
+ }
+ use CustomOption::*;
+
+ pub fn custom_option() {
+ None.iter();
+ None.iter_mut();
+ None.into_iter();
+ }
+}
+
+fn main() {
+ array();
+ custom_option::custom_option();
+ in_macros!();
+}
--- /dev/null
+error: `into_iter` call on an empty collection
+ --> $DIR/iter_on_empty_collections.rs:6:16
+ |
+LL | assert_eq!([].into_iter().next(), Option::<i32>::None);
+ | ^^^^^^^^^^^^^^ help: try: `std::iter::empty()`
+ |
+ = note: `-D clippy::iter-on-empty-collections` implied by `-D warnings`
+
+error: `iter_mut` call on an empty collection
+ --> $DIR/iter_on_empty_collections.rs:7:16
+ |
+LL | assert_eq!([].iter_mut().next(), Option::<&mut i32>::None);
+ | ^^^^^^^^^^^^^ help: try: `std::iter::empty()`
+
+error: `iter` call on an empty collection
+ --> $DIR/iter_on_empty_collections.rs:8:16
+ |
+LL | assert_eq!([].iter().next(), Option::<&i32>::None);
+ | ^^^^^^^^^ help: try: `std::iter::empty()`
+
+error: `into_iter` call on an empty collection
+ --> $DIR/iter_on_empty_collections.rs:9:16
+ |
+LL | assert_eq!(None.into_iter().next(), Option::<i32>::None);
+ | ^^^^^^^^^^^^^^^^ help: try: `std::iter::empty()`
+
+error: `iter_mut` call on an empty collection
+ --> $DIR/iter_on_empty_collections.rs:10:16
+ |
+LL | assert_eq!(None.iter_mut().next(), Option::<&mut i32>::None);
+ | ^^^^^^^^^^^^^^^ help: try: `std::iter::empty()`
+
+error: `iter` call on an empty collection
+ --> $DIR/iter_on_empty_collections.rs:11:16
+ |
+LL | assert_eq!(None.iter().next(), Option::<&i32>::None);
+ | ^^^^^^^^^^^ help: try: `std::iter::empty()`
+
+error: aborting due to 6 previous errors
+
--- /dev/null
+// run-rustfix
+#![warn(clippy::iter_on_single_items)]
+#![allow(clippy::iter_next_slice, clippy::redundant_clone)]
+
+fn array() {
+ assert_eq!(std::iter::once(123).next(), Some(123));
+ assert_eq!(std::iter::once(&mut 123).next(), Some(&mut 123));
+ assert_eq!(std::iter::once(&123).next(), Some(&123));
+ assert_eq!(std::iter::once(123).next(), Some(123));
+ assert_eq!(std::iter::once(&mut 123).next(), Some(&mut 123));
+ assert_eq!(std::iter::once(&123).next(), Some(&123));
+
+ // Don't trigger on non-iter methods
+ let _: Option<String> = Some("test".to_string()).clone();
+ let _: [String; 1] = ["test".to_string()].clone();
+
+ // Don't trigger on match or if branches
+ let _ = match 123 {
+ 123 => [].iter(),
+ _ => ["test"].iter(),
+ };
+
+ let _ = if false { ["test"].iter() } else { [].iter() };
+}
+
+macro_rules! in_macros {
+ () => {
+ assert_eq!([123].into_iter().next(), Some(123));
+ assert_eq!([123].iter_mut().next(), Some(&mut 123));
+ assert_eq!([123].iter().next(), Some(&123));
+ assert_eq!(Some(123).into_iter().next(), Some(123));
+ assert_eq!(Some(123).iter_mut().next(), Some(&mut 123));
+ assert_eq!(Some(123).iter().next(), Some(&123));
+ };
+}
+
+// Don't trigger on a `Some` that isn't std's option
+mod custom_option {
+ #[allow(unused)]
+ enum CustomOption {
+ Some(i32),
+ None,
+ }
+
+ impl CustomOption {
+ fn iter(&self) {}
+ fn iter_mut(&mut self) {}
+ fn into_iter(self) {}
+ }
+ use CustomOption::*;
+
+ pub fn custom_option() {
+ Some(3).iter();
+ Some(3).iter_mut();
+ Some(3).into_iter();
+ }
+}
+
+fn main() {
+ array();
+ custom_option::custom_option();
+ in_macros!();
+}
--- /dev/null
+// run-rustfix
+#![warn(clippy::iter_on_single_items)]
+#![allow(clippy::iter_next_slice, clippy::redundant_clone)]
+
+fn array() {
+ assert_eq!([123].into_iter().next(), Some(123));
+ assert_eq!([123].iter_mut().next(), Some(&mut 123));
+ assert_eq!([123].iter().next(), Some(&123));
+ assert_eq!(Some(123).into_iter().next(), Some(123));
+ assert_eq!(Some(123).iter_mut().next(), Some(&mut 123));
+ assert_eq!(Some(123).iter().next(), Some(&123));
+
+ // Don't trigger on non-iter methods
+ let _: Option<String> = Some("test".to_string()).clone();
+ let _: [String; 1] = ["test".to_string()].clone();
+
+ // Don't trigger on match or if branches
+ let _ = match 123 {
+ 123 => [].iter(),
+ _ => ["test"].iter(),
+ };
+
+ let _ = if false { ["test"].iter() } else { [].iter() };
+}
+
+macro_rules! in_macros {
+ () => {
+ assert_eq!([123].into_iter().next(), Some(123));
+ assert_eq!([123].iter_mut().next(), Some(&mut 123));
+ assert_eq!([123].iter().next(), Some(&123));
+ assert_eq!(Some(123).into_iter().next(), Some(123));
+ assert_eq!(Some(123).iter_mut().next(), Some(&mut 123));
+ assert_eq!(Some(123).iter().next(), Some(&123));
+ };
+}
+
+// Don't trigger on a `Some` that isn't std's option
+mod custom_option {
+ #[allow(unused)]
+ enum CustomOption {
+ Some(i32),
+ None,
+ }
+
+ impl CustomOption {
+ fn iter(&self) {}
+ fn iter_mut(&mut self) {}
+ fn into_iter(self) {}
+ }
+ use CustomOption::*;
+
+ pub fn custom_option() {
+ Some(3).iter();
+ Some(3).iter_mut();
+ Some(3).into_iter();
+ }
+}
+
+fn main() {
+ array();
+ custom_option::custom_option();
+ in_macros!();
+}
--- /dev/null
+error: `into_iter` call on a collection with only one item
+ --> $DIR/iter_on_single_items.rs:6:16
+ |
+LL | assert_eq!([123].into_iter().next(), Some(123));
+ | ^^^^^^^^^^^^^^^^^ help: try: `std::iter::once(123)`
+ |
+ = note: `-D clippy::iter-on-single-items` implied by `-D warnings`
+
+error: `iter_mut` call on a collection with only one item
+ --> $DIR/iter_on_single_items.rs:7:16
+ |
+LL | assert_eq!([123].iter_mut().next(), Some(&mut 123));
+ | ^^^^^^^^^^^^^^^^ help: try: `std::iter::once(&mut 123)`
+
+error: `iter` call on a collection with only one item
+ --> $DIR/iter_on_single_items.rs:8:16
+ |
+LL | assert_eq!([123].iter().next(), Some(&123));
+ | ^^^^^^^^^^^^ help: try: `std::iter::once(&123)`
+
+error: `into_iter` call on a collection with only one item
+ --> $DIR/iter_on_single_items.rs:9:16
+ |
+LL | assert_eq!(Some(123).into_iter().next(), Some(123));
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::iter::once(123)`
+
+error: `iter_mut` call on a collection with only one item
+ --> $DIR/iter_on_single_items.rs:10:16
+ |
+LL | assert_eq!(Some(123).iter_mut().next(), Some(&mut 123));
+ | ^^^^^^^^^^^^^^^^^^^^ help: try: `std::iter::once(&mut 123)`
+
+error: `iter` call on a collection with only one item
+ --> $DIR/iter_on_single_items.rs:11:16
+ |
+LL | assert_eq!(Some(123).iter().next(), Some(&123));
+ | ^^^^^^^^^^^^^^^^ help: try: `std::iter::once(&123)`
+
+error: aborting due to 6 previous errors
+
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::manual_string_new)]
+
+macro_rules! create_strings_from_macro {
+ // When inside a macro, nothing should warn to prevent false positives.
+ ($some_str:expr) => {
+ let _: String = $some_str.into();
+ let _ = $some_str.to_string();
+ };
+}
+
+fn main() {
+ // Method calls
+ let _ = String::new();
+ let _ = "no warning".to_string();
+
+ let _ = String::new();
+ let _ = "no warning".to_owned();
+
+ let _: String = String::new();
+ let _: String = "no warning".into();
+
+ let _: SomeOtherStruct = "no warning".into();
+ let _: SomeOtherStruct = "".into(); // No warning too. We are not converting into String.
+
+ // Calls
+ let _ = String::new();
+ let _ = String::new();
+ let _ = String::from("no warning");
+ let _ = SomeOtherStruct::from("no warning");
+ let _ = SomeOtherStruct::from(""); // Again: no warning.
+
+ let _ = String::new();
+ let _ = String::try_from("no warning").unwrap();
+ let _ = String::try_from("no warning").expect("this should not warn");
+ let _ = SomeOtherStruct::try_from("no warning").unwrap();
+ let _ = SomeOtherStruct::try_from("").unwrap(); // Again: no warning.
+
+ let _: String = String::new();
+ let _: String = From::from("no warning");
+ let _: SomeOtherStruct = From::from("no warning");
+ let _: SomeOtherStruct = From::from(""); // Again: no warning.
+
+ let _: String = String::new();
+ let _: String = TryFrom::try_from("no warning").unwrap();
+ let _: String = TryFrom::try_from("no warning").expect("this should not warn");
+ let _: String = String::new();
+ let _: SomeOtherStruct = TryFrom::try_from("no_warning").unwrap();
+ let _: SomeOtherStruct = TryFrom::try_from("").unwrap(); // Again: no warning.
+
+ // Macros (never warn)
+ create_strings_from_macro!("");
+ create_strings_from_macro!("Hey");
+}
+
+struct SomeOtherStruct {}
+
+impl From<&str> for SomeOtherStruct {
+ fn from(_value: &str) -> Self {
+ Self {}
+ }
+}
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::manual_string_new)]
+
+macro_rules! create_strings_from_macro {
+ // When inside a macro, nothing should warn to prevent false positives.
+ ($some_str:expr) => {
+ let _: String = $some_str.into();
+ let _ = $some_str.to_string();
+ };
+}
+
+fn main() {
+ // Method calls
+ let _ = "".to_string();
+ let _ = "no warning".to_string();
+
+ let _ = "".to_owned();
+ let _ = "no warning".to_owned();
+
+ let _: String = "".into();
+ let _: String = "no warning".into();
+
+ let _: SomeOtherStruct = "no warning".into();
+ let _: SomeOtherStruct = "".into(); // No warning too. We are not converting into String.
+
+ // Calls
+ let _ = String::from("");
+ let _ = <String>::from("");
+ let _ = String::from("no warning");
+ let _ = SomeOtherStruct::from("no warning");
+ let _ = SomeOtherStruct::from(""); // Again: no warning.
+
+ let _ = String::try_from("").unwrap();
+ let _ = String::try_from("no warning").unwrap();
+ let _ = String::try_from("no warning").expect("this should not warn");
+ let _ = SomeOtherStruct::try_from("no warning").unwrap();
+ let _ = SomeOtherStruct::try_from("").unwrap(); // Again: no warning.
+
+ let _: String = From::from("");
+ let _: String = From::from("no warning");
+ let _: SomeOtherStruct = From::from("no warning");
+ let _: SomeOtherStruct = From::from(""); // Again: no warning.
+
+ let _: String = TryFrom::try_from("").unwrap();
+ let _: String = TryFrom::try_from("no warning").unwrap();
+ let _: String = TryFrom::try_from("no warning").expect("this should not warn");
+ let _: String = TryFrom::try_from("").expect("this should warn");
+ let _: SomeOtherStruct = TryFrom::try_from("no_warning").unwrap();
+ let _: SomeOtherStruct = TryFrom::try_from("").unwrap(); // Again: no warning.
+
+ // Macros (never warn)
+ create_strings_from_macro!("");
+ create_strings_from_macro!("Hey");
+}
+
+struct SomeOtherStruct {}
+
+impl From<&str> for SomeOtherStruct {
+ fn from(_value: &str) -> Self {
+ Self {}
+ }
+}
--- /dev/null
+error: empty String is being created manually
+ --> $DIR/manual_string_new.rs:15:13
+ |
+LL | let _ = "".to_string();
+ | ^^^^^^^^^^^^^^ help: consider using: `String::new()`
+ |
+ = note: `-D clippy::manual-string-new` implied by `-D warnings`
+
+error: empty String is being created manually
+ --> $DIR/manual_string_new.rs:18:13
+ |
+LL | let _ = "".to_owned();
+ | ^^^^^^^^^^^^^ help: consider using: `String::new()`
+
+error: empty String is being created manually
+ --> $DIR/manual_string_new.rs:21:21
+ |
+LL | let _: String = "".into();
+ | ^^^^^^^^^ help: consider using: `String::new()`
+
+error: empty String is being created manually
+ --> $DIR/manual_string_new.rs:28:13
+ |
+LL | let _ = String::from("");
+ | ^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
+
+error: empty String is being created manually
+ --> $DIR/manual_string_new.rs:29:13
+ |
+LL | let _ = <String>::from("");
+ | ^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
+
+error: empty String is being created manually
+ --> $DIR/manual_string_new.rs:34:13
+ |
+LL | let _ = String::try_from("").unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
+
+error: empty String is being created manually
+ --> $DIR/manual_string_new.rs:40:21
+ |
+LL | let _: String = From::from("");
+ | ^^^^^^^^^^^^^^ help: consider using: `String::new()`
+
+error: empty String is being created manually
+ --> $DIR/manual_string_new.rs:45:21
+ |
+LL | let _: String = TryFrom::try_from("").unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
+
+error: empty String is being created manually
+ --> $DIR/manual_string_new.rs:48:21
+ |
+LL | let _: String = TryFrom::try_from("").expect("this should warn");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
+
+error: aborting due to 9 previous errors
+
_ => false,
};
}
+
+ let x = ' ';
+ // ignore if match block contains comment
+ let _line_comments = match x {
+ // numbers are bad!
+ '1' | '2' | '3' => true,
+ // spaces are very important to be true.
+ ' ' => true,
+ // as are dots
+ '.' => true,
+ _ => false,
+ };
+
+ let _block_comments = match x {
+ /* numbers are bad!
+ */
+ '1' | '2' | '3' => true,
+ /* spaces are very important to be true.
+ */
+ ' ' => true,
+ /* as are dots
+ */
+ '.' => true,
+ _ => false,
+ };
}
_ => false,
};
}
+
+ let x = ' ';
+ // ignore if match block contains comment
+ let _line_comments = match x {
+ // numbers are bad!
+ '1' | '2' | '3' => true,
+ // spaces are very important to be true.
+ ' ' => true,
+ // as are dots
+ '.' => true,
+ _ => false,
+ };
+
+ let _block_comments = match x {
+ /* numbers are bad!
+ */
+ '1' | '2' | '3' => true,
+ /* spaces are very important to be true.
+ */
+ ' ' => true,
+ /* as are dots
+ */
+ '.' => true,
+ _ => false,
+ };
}
--- /dev/null
+#![warn(clippy::multi_assignments)]
+fn main() {
+ let (mut a, mut b, mut c, mut d) = ((), (), (), ());
+ a = b = c;
+ a = b = c = d;
+ a = b = { c };
+ a = { b = c };
+ a = (b = c);
+}
--- /dev/null
+error: assignments don't nest intuitively
+ --> $DIR/multi_assignments.rs:4:5
+ |
+LL | a = b = c;
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::multi-assignments` implied by `-D warnings`
+
+error: assignments don't nest intuitively
+ --> $DIR/multi_assignments.rs:5:5
+ |
+LL | a = b = c = d;
+ | ^^^^^^^^^^^^^
+
+error: assignments don't nest intuitively
+ --> $DIR/multi_assignments.rs:5:9
+ |
+LL | a = b = c = d;
+ | ^^^^^^^^^
+
+error: assignments don't nest intuitively
+ --> $DIR/multi_assignments.rs:6:5
+ |
+LL | a = b = { c };
+ | ^^^^^^^^^^^^^
+
+error: assignments don't nest intuitively
+ --> $DIR/multi_assignments.rs:7:5
+ |
+LL | a = { b = c };
+ | ^^^^^^^^^^^^^
+
+error: assignments don't nest intuitively
+ --> $DIR/multi_assignments.rs:8:5
+ |
+LL | a = (b = c);
+ | ^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
// run-rustfix
-#![feature(lint_reasons)]
+#![feature(custom_inner_attributes, lint_reasons)]
#[warn(clippy::all, clippy::needless_borrow)]
#[allow(unused_variables, clippy::unnecessary_mut_passed)]
0
}
}
+
+ let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap();
+ let _ = std::path::Path::new(".").join(".");
+ deref_target_is_x(X);
+ multiple_constraints([[""]]);
+ multiple_constraints_normalizes_to_same(X, X);
+ let _ = Some("").unwrap_or("");
+
+ only_sized(&""); // Don't lint. `Sized` is only bound
+ let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound
+ let _ = Box::new(&""); // Don't lint. Type parameter appears in return type
+ ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter
+ refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't
+ multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments
}
#[allow(clippy::needless_borrowed_reference)]
}
}
}
+
+#[derive(Clone, Copy)]
+struct X;
+
+impl std::ops::Deref for X {
+ type Target = X;
+ fn deref(&self) -> &Self::Target {
+ self
+ }
+}
+
+fn deref_target_is_x<T>(_: T)
+where
+ T: std::ops::Deref<Target = X>,
+{
+}
+
+fn multiple_constraints<T, U, V, X, Y>(_: T)
+where
+ T: IntoIterator<Item = U> + IntoIterator<Item = X>,
+ U: IntoIterator<Item = V>,
+ V: AsRef<str>,
+ X: IntoIterator<Item = Y>,
+ Y: AsRef<std::ffi::OsStr>,
+{
+}
+
+fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V)
+where
+ T: std::ops::Deref<Target = U>,
+ U: std::ops::Deref<Target = V>,
+{
+}
+
+fn only_sized<T>(_: T) {}
+
+fn ref_as_ref_path<T: 'static>(_: &'static T)
+where
+ &'static T: AsRef<std::path::Path>,
+{
+}
+
+trait RefsOnly {
+ type Referent;
+}
+
+impl<T> RefsOnly for &T {
+ type Referent = T;
+}
+
+fn refs_only<T, U>(_: T)
+where
+ T: RefsOnly<Referent = U>,
+{
+}
+
+fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U)
+where
+ T: IntoIterator<Item = U>,
+ U: IntoIterator<Item = V>,
+ V: AsRef<str>,
+{
+}
+
+// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
+#[allow(dead_code)]
+mod copyable_iterator {
+ #[derive(Clone, Copy)]
+ struct Iter;
+ impl Iterator for Iter {
+ type Item = ();
+ fn next(&mut self) -> Option<Self::Item> {
+ None
+ }
+ }
+ fn takes_iter(_: impl Iterator) {}
+ fn dont_warn(mut x: Iter) {
+ takes_iter(&mut x);
+ }
+ fn warn(mut x: &mut Iter) {
+ takes_iter(&mut x)
+ }
+}
+
+mod under_msrv {
+ #![allow(dead_code)]
+ #![clippy::msrv = "1.52.0"]
+
+ fn foo() {
+ let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
+ }
+}
+
+mod meets_msrv {
+ #![allow(dead_code)]
+ #![clippy::msrv = "1.53.0"]
+
+ fn foo() {
+ let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap();
+ }
+}
// run-rustfix
-#![feature(lint_reasons)]
+#![feature(custom_inner_attributes, lint_reasons)]
#[warn(clippy::all, clippy::needless_borrow)]
#[allow(unused_variables, clippy::unnecessary_mut_passed)]
0
}
}
+
+ let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
+ let _ = std::path::Path::new(".").join(&&".");
+ deref_target_is_x(&X);
+ multiple_constraints(&[[""]]);
+ multiple_constraints_normalizes_to_same(&X, X);
+ let _ = Some("").unwrap_or(&"");
+
+ only_sized(&""); // Don't lint. `Sized` is only bound
+ let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound
+ let _ = Box::new(&""); // Don't lint. Type parameter appears in return type
+ ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter
+ refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't
+ multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments
}
#[allow(clippy::needless_borrowed_reference)]
}
}
}
+
+#[derive(Clone, Copy)]
+struct X;
+
+impl std::ops::Deref for X {
+ type Target = X;
+ fn deref(&self) -> &Self::Target {
+ self
+ }
+}
+
+fn deref_target_is_x<T>(_: T)
+where
+ T: std::ops::Deref<Target = X>,
+{
+}
+
+fn multiple_constraints<T, U, V, X, Y>(_: T)
+where
+ T: IntoIterator<Item = U> + IntoIterator<Item = X>,
+ U: IntoIterator<Item = V>,
+ V: AsRef<str>,
+ X: IntoIterator<Item = Y>,
+ Y: AsRef<std::ffi::OsStr>,
+{
+}
+
+fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V)
+where
+ T: std::ops::Deref<Target = U>,
+ U: std::ops::Deref<Target = V>,
+{
+}
+
+fn only_sized<T>(_: T) {}
+
+fn ref_as_ref_path<T: 'static>(_: &'static T)
+where
+ &'static T: AsRef<std::path::Path>,
+{
+}
+
+trait RefsOnly {
+ type Referent;
+}
+
+impl<T> RefsOnly for &T {
+ type Referent = T;
+}
+
+fn refs_only<T, U>(_: T)
+where
+ T: RefsOnly<Referent = U>,
+{
+}
+
+fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U)
+where
+ T: IntoIterator<Item = U>,
+ U: IntoIterator<Item = V>,
+ V: AsRef<str>,
+{
+}
+
+// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
+#[allow(dead_code)]
+mod copyable_iterator {
+ #[derive(Clone, Copy)]
+ struct Iter;
+ impl Iterator for Iter {
+ type Item = ();
+ fn next(&mut self) -> Option<Self::Item> {
+ None
+ }
+ }
+ fn takes_iter(_: impl Iterator) {}
+ fn dont_warn(mut x: Iter) {
+ takes_iter(&mut x);
+ }
+ fn warn(mut x: &mut Iter) {
+ takes_iter(&mut x)
+ }
+}
+
+mod under_msrv {
+ #![allow(dead_code)]
+ #![clippy::msrv = "1.52.0"]
+
+ fn foo() {
+ let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
+ }
+}
+
+mod meets_msrv {
+ #![allow(dead_code)]
+ #![clippy::msrv = "1.53.0"]
+
+ fn foo() {
+ let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
+ }
+}
LL | (&&5).foo();
| ^^^^^ help: change this to: `(&5)`
+error: the borrowed expression implements the required traits
+ --> $DIR/needless_borrow.rs:131:51
+ |
+LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
+ | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
+
+error: the borrowed expression implements the required traits
+ --> $DIR/needless_borrow.rs:132:44
+ |
+LL | let _ = std::path::Path::new(".").join(&&".");
+ | ^^^^^ help: change this to: `"."`
+
+error: the borrowed expression implements the required traits
+ --> $DIR/needless_borrow.rs:133:23
+ |
+LL | deref_target_is_x(&X);
+ | ^^ help: change this to: `X`
+
+error: the borrowed expression implements the required traits
+ --> $DIR/needless_borrow.rs:134:26
+ |
+LL | multiple_constraints(&[[""]]);
+ | ^^^^^^^ help: change this to: `[[""]]`
+
+error: the borrowed expression implements the required traits
+ --> $DIR/needless_borrow.rs:135:45
+ |
+LL | multiple_constraints_normalizes_to_same(&X, X);
+ | ^^ help: change this to: `X`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:136:32
+ |
+LL | let _ = Some("").unwrap_or(&"");
+ | ^^^ help: change this to: `""`
+
error: this expression borrows a value the compiler would automatically borrow
- --> $DIR/needless_borrow.rs:173:13
+ --> $DIR/needless_borrow.rs:187:13
|
LL | (&self.f)()
| ^^^^^^^^^ help: change this to: `(self.f)`
error: this expression borrows a value the compiler would automatically borrow
- --> $DIR/needless_borrow.rs:182:13
+ --> $DIR/needless_borrow.rs:196:13
|
LL | (&mut self.f)()
| ^^^^^^^^^^^^^ help: change this to: `(self.f)`
-error: aborting due to 22 previous errors
+error: the borrowed expression implements the required traits
+ --> $DIR/needless_borrow.rs:298:55
+ |
+LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
+ | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
+
+error: aborting due to 29 previous errors
let v = [1].iter().collect::<Vec<_>>();
v.into_iter().collect::<HashSet<_>>();
}
+
+mod issue_8553 {
+ fn test_for() {
+ let vec = vec![1, 2];
+ let w: Vec<usize> = vec.iter().map(|i| i * i).collect();
+
+ for i in 0..2 {
+ // Do not lint, because this method call is in the loop
+ w.contains(&i);
+ }
+
+ for i in 0..2 {
+ let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
+ let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
+ // Do lint
+ y.contains(&i);
+ for j in 0..2 {
+ // Do not lint, because this method call is in the loop
+ z.contains(&j);
+ }
+ }
+
+ // Do not lint, because this variable is used.
+ w.contains(&0);
+ }
+
+ fn test_while() {
+ let vec = vec![1, 2];
+ let x: Vec<usize> = vec.iter().map(|i| i * i).collect();
+ let mut n = 0;
+ while n > 1 {
+ // Do not lint, because this method call is in the loop
+ x.contains(&n);
+ n += 1;
+ }
+
+ while n > 2 {
+ let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
+ let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
+ // Do lint
+ y.contains(&n);
+ n += 1;
+ while n > 4 {
+ // Do not lint, because this method call is in the loop
+ z.contains(&n);
+ n += 1;
+ }
+ }
+ }
+
+ fn test_loop() {
+ let vec = vec![1, 2];
+ let x: Vec<usize> = vec.iter().map(|i| i * i).collect();
+ let mut n = 0;
+ loop {
+ if n < 1 {
+ // Do not lint, because this method call is in the loop
+ x.contains(&n);
+ n += 1;
+ } else {
+ break;
+ }
+ }
+
+ loop {
+ if n < 2 {
+ let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
+ let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
+ // Do lint
+ y.contains(&n);
+ n += 1;
+ loop {
+ if n < 4 {
+ // Do not lint, because this method call is in the loop
+ z.contains(&n);
+ n += 1;
+ } else {
+ break;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ }
+
+ fn test_while_let() {
+ let vec = vec![1, 2];
+ let x: Vec<usize> = vec.iter().map(|i| i * i).collect();
+ let optional = Some(0);
+ let mut n = 0;
+ while let Some(value) = optional {
+ if n < 1 {
+ // Do not lint, because this method call is in the loop
+ x.contains(&n);
+ n += 1;
+ } else {
+ break;
+ }
+ }
+
+ while let Some(value) = optional {
+ let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
+ let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
+ if n < 2 {
+ // Do lint
+ y.contains(&n);
+ n += 1;
+ } else {
+ break;
+ }
+
+ while let Some(value) = optional {
+ if n < 4 {
+ // Do not lint, because this method call is in the loop
+ z.contains(&n);
+ n += 1;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ fn test_if_cond() {
+ let vec = vec![1, 2];
+ let v: Vec<usize> = vec.iter().map(|i| i * i).collect();
+ let w = v.iter().collect::<Vec<_>>();
+ // Do lint
+ for _ in 0..w.len() {
+ todo!();
+ }
+ }
+
+ fn test_if_cond_false_case() {
+ let vec = vec![1, 2];
+ let v: Vec<usize> = vec.iter().map(|i| i * i).collect();
+ let w = v.iter().collect::<Vec<_>>();
+ // Do not lint, because w is used.
+ for _ in 0..w.len() {
+ todo!();
+ }
+
+ w.len();
+ }
+
+ fn test_while_cond() {
+ let mut vec = vec![1, 2];
+ let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
+ let mut w = v.iter().collect::<Vec<_>>();
+ // Do lint
+ while 1 == w.len() {
+ todo!();
+ }
+ }
+
+ fn test_while_cond_false_case() {
+ let mut vec = vec![1, 2];
+ let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
+ let mut w = v.iter().collect::<Vec<_>>();
+ // Do not lint, because w is used.
+ while 1 == w.len() {
+ todo!();
+ }
+
+ w.len();
+ }
+
+ fn test_while_let_cond() {
+ let mut vec = vec![1, 2];
+ let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
+ let mut w = v.iter().collect::<Vec<_>>();
+ // Do lint
+ while let Some(i) = Some(w.len()) {
+ todo!();
+ }
+ }
+
+ fn test_while_let_cond_false_case() {
+ let mut vec = vec![1, 2];
+ let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
+ let mut w = v.iter().collect::<Vec<_>>();
+ // Do not lint, because w is used.
+ while let Some(i) = Some(w.len()) {
+ todo!();
+ }
+ w.len();
+ }
+}
LL ~ sample.iter().count()
|
-error: aborting due to 9 previous errors
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect_indirect.rs:127:59
+ |
+LL | let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
+ | ^^^^^^^
+...
+LL | y.contains(&i);
+ | -------------- the iterator could be used here instead
+ |
+help: check if the original Iterator contains an element instead of collecting then checking
+ |
+LL ~
+LL | let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
+LL | // Do lint
+LL ~ vec.iter().map(|k| k * k).any(|x| x == i);
+ |
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect_indirect.rs:152:59
+ |
+LL | let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
+ | ^^^^^^^
+...
+LL | y.contains(&n);
+ | -------------- the iterator could be used here instead
+ |
+help: check if the original Iterator contains an element instead of collecting then checking
+ |
+LL ~
+LL | let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
+LL | // Do lint
+LL ~ vec.iter().map(|k| k * k).any(|x| x == n);
+ |
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect_indirect.rs:181:63
+ |
+LL | let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
+ | ^^^^^^^
+...
+LL | y.contains(&n);
+ | -------------- the iterator could be used here instead
+ |
+help: check if the original Iterator contains an element instead of collecting then checking
+ |
+LL ~
+LL | let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
+LL | // Do lint
+LL ~ vec.iter().map(|k| k * k).any(|x| x == n);
+ |
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect_indirect.rs:217:59
+ |
+LL | let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
+ | ^^^^^^^
+...
+LL | y.contains(&n);
+ | -------------- the iterator could be used here instead
+ |
+help: check if the original Iterator contains an element instead of collecting then checking
+ |
+LL ~
+LL | let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
+LL | if n < 2 {
+LL | // Do lint
+LL ~ vec.iter().map(|k| k * k).any(|x| x == n);
+ |
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect_indirect.rs:242:26
+ |
+LL | let w = v.iter().collect::<Vec<_>>();
+ | ^^^^^^^
+LL | // Do lint
+LL | for _ in 0..w.len() {
+ | ------- the iterator could be used here instead
+ |
+help: take the original Iterator's count instead of collecting it and finding the length
+ |
+LL ~
+LL | // Do lint
+LL ~ for _ in 0..v.iter().count() {
+ |
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect_indirect.rs:264:30
+ |
+LL | let mut w = v.iter().collect::<Vec<_>>();
+ | ^^^^^^^
+LL | // Do lint
+LL | while 1 == w.len() {
+ | ------- the iterator could be used here instead
+ |
+help: take the original Iterator's count instead of collecting it and finding the length
+ |
+LL ~
+LL | // Do lint
+LL ~ while 1 == v.iter().count() {
+ |
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect_indirect.rs:286:30
+ |
+LL | let mut w = v.iter().collect::<Vec<_>>();
+ | ^^^^^^^
+LL | // Do lint
+LL | while let Some(i) = Some(w.len()) {
+ | ------- the iterator could be used here instead
+ |
+help: take the original Iterator's count instead of collecting it and finding the length
+ |
+LL ~
+LL | // Do lint
+LL ~ while let Some(i) = Some(v.iter().count()) {
+ |
+
+error: aborting due to 16 previous errors
// run-rustfix
+#![feature(let_chains)]
#![allow(
unused,
clippy::assign_op_pattern,
// run-rustfix
+#![feature(let_chains)]
#![allow(
unused,
clippy::assign_op_pattern,
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:22:5
+ --> $DIR/needless_late_init.rs:23:5
|
LL | let a;
| ^^^^^^ created here
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:25:5
+ --> $DIR/needless_late_init.rs:26:5
|
LL | let b;
| ^^^^^^ created here
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:26:5
+ --> $DIR/needless_late_init.rs:27:5
|
LL | let c;
| ^^^^^^ created here
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:30:5
+ --> $DIR/needless_late_init.rs:31:5
|
LL | let d: usize;
| ^^^^^^^^^^^^^ created here
| ~~~~~~~~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:33:5
+ --> $DIR/needless_late_init.rs:34:5
|
LL | let e;
| ^^^^^^ created here
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:38:5
+ --> $DIR/needless_late_init.rs:39:5
|
LL | let a;
| ^^^^^^
| +
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:47:5
+ --> $DIR/needless_late_init.rs:48:5
|
LL | let b;
| ^^^^^^
| +
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:54:5
+ --> $DIR/needless_late_init.rs:55:5
|
LL | let d;
| ^^^^^^
| +
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:62:5
+ --> $DIR/needless_late_init.rs:63:5
|
LL | let e;
| ^^^^^^
| +
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:69:5
+ --> $DIR/needless_late_init.rs:70:5
|
LL | let f;
| ^^^^^^
|
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:75:5
+ --> $DIR/needless_late_init.rs:76:5
|
LL | let g: usize;
| ^^^^^^^^^^^^^
| +
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:83:5
+ --> $DIR/needless_late_init.rs:84:5
|
LL | let x;
| ^^^^^^ created here
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:87:5
+ --> $DIR/needless_late_init.rs:88:5
|
LL | let x;
| ^^^^^^ created here
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:91:5
+ --> $DIR/needless_late_init.rs:92:5
|
LL | let x;
| ^^^^^^ created here
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:110:5
+ --> $DIR/needless_late_init.rs:111:5
|
LL | let a;
| ^^^^^^
| +
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:127:5
+ --> $DIR/needless_late_init.rs:128:5
|
LL | let a;
| ^^^^^^
}
}
+mod issue9084 {
+ fn wildcard_if() {
+ let mut some_bool = true;
+ let e = Some(1);
+
+ // should lint
+ let _ = e;
+
+ // should lint
+ let _ = e;
+
+ // should not lint
+ let _ = match e {
+ _ if some_bool => e,
+ _ => Some(2),
+ };
+
+ // should not lint
+ let _ = match e {
+ Some(i) => Some(i + 1),
+ _ if some_bool => e,
+ _ => e,
+ };
+
+ // should not lint (guard has side effects)
+ let _ = match e {
+ Some(i) => Some(i),
+ _ if {
+ some_bool = false;
+ some_bool
+ } =>
+ {
+ e
+ },
+ _ => e,
+ };
+ }
+}
+
fn main() {}
}
}
+mod issue9084 {
+ fn wildcard_if() {
+ let mut some_bool = true;
+ let e = Some(1);
+
+ // should lint
+ let _ = match e {
+ _ if some_bool => e,
+ _ => e,
+ };
+
+ // should lint
+ let _ = match e {
+ Some(i) => Some(i),
+ _ if some_bool => e,
+ _ => e,
+ };
+
+ // should not lint
+ let _ = match e {
+ _ if some_bool => e,
+ _ => Some(2),
+ };
+
+ // should not lint
+ let _ = match e {
+ Some(i) => Some(i + 1),
+ _ if some_bool => e,
+ _ => e,
+ };
+
+ // should not lint (guard has side effects)
+ let _ = match e {
+ Some(i) => Some(i),
+ _ if {
+ some_bool = false;
+ some_bool
+ } =>
+ {
+ e
+ },
+ _ => e,
+ };
+ }
+}
+
fn main() {}
LL | | };
| |_________^ help: replace it with: `ce`
-error: aborting due to 11 previous errors
+error: this match expression is unnecessary
+ --> $DIR/needless_match.rs:253:17
+ |
+LL | let _ = match e {
+ | _________________^
+LL | | _ if some_bool => e,
+LL | | _ => e,
+LL | | };
+ | |_________^ help: replace it with: `e`
+
+error: this match expression is unnecessary
+ --> $DIR/needless_match.rs:259:17
+ |
+LL | let _ = match e {
+ | _________________^
+LL | | Some(i) => Some(i),
+LL | | _ if some_bool => e,
+LL | | _ => e,
+LL | | };
+ | |_________^ help: replace it with: `e`
+
+error: aborting due to 13 previous errors
format!("Hello {}", "world!")
}
-fn check_expect() -> bool {
- if true {
- // no error!
- return true;
- }
- #[expect(clippy::needless_return)]
- return true;
+fn issue_9361() -> i32 {
+ #[allow(clippy::integer_arithmetic)]
+ return 1 + 2;
}
fn main() {}
return format!("Hello {}", "world!");
}
-fn check_expect() -> bool {
- if true {
- // no error!
- return true;
- }
- #[expect(clippy::needless_return)]
- return true;
+fn issue_9361() -> i32 {
+ #[allow(clippy::integer_arithmetic)]
+ return 1 + 2;
}
fn main() {}
#![warn(clippy::only_used_in_recursion)]
-fn simple(a: usize, b: usize) -> usize {
- if a == 0 { 1 } else { simple(a - 1, b) }
+fn _simple(x: u32) -> u32 {
+ x
}
-fn with_calc(a: usize, b: isize) -> usize {
- if a == 0 { 1 } else { with_calc(a - 1, -b + 1) }
+fn _simple2(x: u32) -> u32 {
+ _simple(x)
}
-fn tuple((a, b): (usize, usize)) -> usize {
- if a == 0 { 1 } else { tuple((a - 1, b + 1)) }
+fn _one_unused(flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { _one_unused(flag - 1, a) }
}
-fn let_tuple(a: usize, b: usize) -> usize {
- let (c, d) = (a, b);
- if c == 0 { 1 } else { let_tuple(c - 1, d + 1) }
+fn _two_unused(flag: u32, a: u32, b: i32) -> usize {
+ if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) }
}
-fn array([a, b]: [usize; 2]) -> usize {
- if a == 0 { 1 } else { array([a - 1, b + 1]) }
-}
-
-fn index(a: usize, mut b: &[usize], c: usize) -> usize {
- if a == 0 { 1 } else { index(a - 1, b, c + b[0]) }
-}
-
-fn break_(a: usize, mut b: usize, mut c: usize) -> usize {
- let c = loop {
- b += 1;
- c += 1;
- if c == 10 {
- break b;
- }
- };
-
- if a == 0 { 1 } else { break_(a - 1, c, c) }
+fn _with_calc(flag: u32, a: i64) -> usize {
+ if flag == 0 {
+ 0
+ } else {
+ _with_calc(flag - 1, (-a + 10) * 5)
+ }
}
-// this has a side effect
-fn mut_ref(a: usize, b: &mut usize) -> usize {
- *b = 1;
- if a == 0 { 1 } else { mut_ref(a - 1, b) }
+// Don't lint
+fn _used_with_flag(flag: u32, a: u32) -> usize {
+ if flag == 0 { 0 } else { _used_with_flag(flag ^ a, a - 1) }
}
-fn mut_ref2(a: usize, b: &mut usize) -> usize {
- let mut c = *b;
- if a == 0 { 1 } else { mut_ref2(a - 1, &mut c) }
+fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize {
+ if flag == 0 {
+ 0
+ } else {
+ _used_with_unused(flag - 1, -a, a + b)
+ }
}
-fn not_primitive(a: usize, b: String) -> usize {
- if a == 0 { 1 } else { not_primitive(a - 1, b) }
+fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize {
+ if flag == 0 {
+ 0
+ } else {
+ _codependent_unused(flag - 1, a * b, a + b)
+ }
}
-// this doesn't have a side effect,
-// but `String` is not primitive.
-fn not_primitive_op(a: usize, b: String, c: &str) -> usize {
- if a == 1 { 1 } else { not_primitive_op(a, b + c, c) }
+fn _not_primitive(flag: u32, b: String) -> usize {
+ if flag == 0 { 0 } else { _not_primitive(flag - 1, b) }
}
struct A;
impl A {
- fn method(a: usize, b: usize) -> usize {
- if a == 0 { 1 } else { A::method(a - 1, b - 1) }
+ fn _method(flag: usize, a: usize) -> usize {
+ if flag == 0 { 0 } else { Self::_method(flag - 1, a) }
}
- fn method2(&self, a: usize, b: usize) -> usize {
- if a == 0 { 1 } else { self.method2(a - 1, b + 1) }
+ fn _method_self(&self, flag: usize, a: usize) -> usize {
+ if flag == 0 { 0 } else { self._method_self(flag - 1, a) }
}
}
trait B {
- fn hello(a: usize, b: usize) -> usize;
-
- fn hello2(&self, a: usize, b: usize) -> usize;
+ fn method(flag: u32, a: usize) -> usize;
+ fn method_self(&self, flag: u32, a: usize) -> usize;
}
impl B for A {
- fn hello(a: usize, b: usize) -> usize {
- if a == 0 { 1 } else { A::hello(a - 1, b + 1) }
+ fn method(flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { Self::method(flag - 1, a) }
}
- fn hello2(&self, a: usize, b: usize) -> usize {
- if a == 0 { 1 } else { self.hello2(a - 1, b + 1) }
+ fn method_self(&self, flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { self.method_self(flag - 1, a) }
}
}
-trait C {
- fn hello(a: usize, b: usize) -> usize {
- if a == 0 { 1 } else { Self::hello(a - 1, b + 1) }
+impl B for () {
+ fn method(flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { a }
}
- fn hello2(&self, a: usize, b: usize) -> usize {
- if a == 0 { 1 } else { self.hello2(a - 1, b + 1) }
+ fn method_self(&self, flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { a }
}
}
-fn ignore(a: usize, _: usize) -> usize {
- if a == 1 { 1 } else { ignore(a - 1, 0) }
-}
+impl B for u32 {
+ fn method(flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { <() as B>::method(flag, a) }
+ }
-fn ignore2(a: usize, _b: usize) -> usize {
- if a == 1 { 1 } else { ignore2(a - 1, _b) }
+ fn method_self(&self, flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { ().method_self(flag, a) }
+ }
}
-fn f1(a: u32) -> u32 {
- a
-}
+trait C {
+ fn method(flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { Self::method(flag - 1, a) }
+ }
-fn f2(a: u32) -> u32 {
- f1(a)
+ fn method_self(&self, flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { self.method_self(flag - 1, a) }
+ }
}
-fn inner_fn(a: u32) -> u32 {
- fn inner_fn(a: u32) -> u32 {
- a
- }
- inner_fn(a)
+fn _ignore(flag: usize, _a: usize) -> usize {
+ if flag == 0 { 0 } else { _ignore(flag - 1, _a) }
}
fn main() {}
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:3:21
+ --> $DIR/only_used_in_recursion.rs:11:27
|
-LL | fn simple(a: usize, b: usize) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+LL | fn _one_unused(flag: u32, a: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
|
= note: `-D clippy::only-used-in-recursion` implied by `-D warnings`
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:12:53
+ |
+LL | if flag == 0 { 0 } else { _one_unused(flag - 1, a) }
+ | ^
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion.rs:15:27
+ |
+LL | fn _two_unused(flag: u32, a: u32, b: i32) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:16:53
+ |
+LL | if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) }
+ | ^
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion.rs:15:35
+ |
+LL | fn _two_unused(flag: u32, a: u32, b: i32) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_b`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:16:56
+ |
+LL | if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) }
+ | ^
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion.rs:19:26
+ |
+LL | fn _with_calc(flag: u32, a: i64) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:23:32
+ |
+LL | _with_calc(flag - 1, (-a + 10) * 5)
+ | ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:7:24
+ --> $DIR/only_used_in_recursion.rs:32:33
+ |
+LL | fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:36:38
|
-LL | fn with_calc(a: usize, b: isize) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+LL | _used_with_unused(flag - 1, -a, a + b)
+ | ^ ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:11:14
+ --> $DIR/only_used_in_recursion.rs:32:41
+ |
+LL | fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_b`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:36:45
|
-LL | fn tuple((a, b): (usize, usize)) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+LL | _used_with_unused(flag - 1, -a, a + b)
+ | ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:15:24
+ --> $DIR/only_used_in_recursion.rs:40:35
|
-LL | fn let_tuple(a: usize, b: usize) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+LL | fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:44:39
+ |
+LL | _codependent_unused(flag - 1, a * b, a + b)
+ | ^ ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:20:14
+ --> $DIR/only_used_in_recursion.rs:40:43
+ |
+LL | fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_b`
|
-LL | fn array([a, b]: [usize; 2]) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:44:43
+ |
+LL | _codependent_unused(flag - 1, a * b, a + b)
+ | ^ ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:24:20
+ --> $DIR/only_used_in_recursion.rs:48:30
+ |
+LL | fn _not_primitive(flag: u32, b: String) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_b`
|
-LL | fn index(a: usize, mut b: &[usize], c: usize) -> usize {
- | ^^^^^ help: if this is intentional, prefix with an underscore: `_b`
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:49:56
+ |
+LL | if flag == 0 { 0 } else { _not_primitive(flag - 1, b) }
+ | ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:24:37
+ --> $DIR/only_used_in_recursion.rs:55:29
+ |
+LL | fn _method(flag: usize, a: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:56:59
|
-LL | fn index(a: usize, mut b: &[usize], c: usize) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_c`
+LL | if flag == 0 { 0 } else { Self::_method(flag - 1, a) }
+ | ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:28:21
+ --> $DIR/only_used_in_recursion.rs:59:22
+ |
+LL | fn _method_self(&self, flag: usize, a: usize) -> usize {
+ | ^^^^
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:60:35
|
-LL | fn break_(a: usize, mut b: usize, mut c: usize) -> usize {
- | ^^^^^ help: if this is intentional, prefix with an underscore: `_b`
+LL | if flag == 0 { 0 } else { self._method_self(flag - 1, a) }
+ | ^^^^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:46:23
+ --> $DIR/only_used_in_recursion.rs:59:41
|
-LL | fn mut_ref2(a: usize, b: &mut usize) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+LL | fn _method_self(&self, flag: usize, a: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:60:63
+ |
+LL | if flag == 0 { 0 } else { self._method_self(flag - 1, a) }
+ | ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:51:28
+ --> $DIR/only_used_in_recursion.rs:70:26
+ |
+LL | fn method(flag: u32, a: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
|
-LL | fn not_primitive(a: usize, b: String) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:71:58
+ |
+LL | if flag == 0 { 0 } else { Self::method(flag - 1, a) }
+ | ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:68:33
+ --> $DIR/only_used_in_recursion.rs:74:38
+ |
+LL | fn method_self(&self, flag: u32, a: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
|
-LL | fn method2(&self, a: usize, b: usize) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:75:62
+ |
+LL | if flag == 0 { 0 } else { self.method_self(flag - 1, a) }
+ | ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:90:24
+ --> $DIR/only_used_in_recursion.rs:100:26
+ |
+LL | fn method(flag: u32, a: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:101:58
|
-LL | fn hello(a: usize, b: usize) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+LL | if flag == 0 { 0 } else { Self::method(flag - 1, a) }
+ | ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:94:32
+ --> $DIR/only_used_in_recursion.rs:104:38
+ |
+LL | fn method_self(&self, flag: u32, a: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:105:62
|
-LL | fn hello2(&self, a: usize, b: usize) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+LL | if flag == 0 { 0 } else { self.method_self(flag - 1, a) }
+ | ^
-error: aborting due to 13 previous errors
+error: aborting due to 16 previous errors
--- /dev/null
+#![warn(clippy::only_used_in_recursion)]
+
+fn _with_inner(flag: u32, a: u32, b: u32) -> usize {
+ fn inner(flag: u32, a: u32) -> u32 {
+ if flag == 0 { 0 } else { inner(flag, a) }
+ }
+
+ let x = inner(flag, a);
+ if flag == 0 { 0 } else { _with_inner(flag, a, b + x) }
+}
+
+fn _with_closure(a: Option<u32>, b: u32, f: impl Fn(u32, u32) -> Option<u32>) -> u32 {
+ if let Some(x) = a.and_then(|x| f(x, x)) {
+ _with_closure(Some(x), b, f)
+ } else {
+ 0
+ }
+}
+
+// Issue #8560
+trait D {
+ fn foo(&mut self, arg: u32) -> u32;
+}
+
+mod m {
+ pub struct S(u32);
+ impl S {
+ pub fn foo(&mut self, arg: u32) -> u32 {
+ arg + self.0
+ }
+ }
+}
+
+impl D for m::S {
+ fn foo(&mut self, arg: u32) -> u32 {
+ self.foo(arg)
+ }
+}
+
+// Issue #8782
+fn only_let(x: u32) {
+ let y = 10u32;
+ let _z = x * y;
+}
+
+trait E<T: E<()>> {
+ fn method(flag: u32, a: usize) -> usize {
+ if flag == 0 {
+ 0
+ } else {
+ <T as E<()>>::method(flag - 1, a)
+ }
+ }
+}
+
+impl E<()> for () {
+ fn method(flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { a }
+ }
+}
+
+fn overwritten_param(flag: u32, mut a: usize) -> usize {
+ if flag == 0 {
+ return 0;
+ } else if flag > 5 {
+ a += flag as usize;
+ } else {
+ a = 5;
+ }
+ overwritten_param(flag, a)
+}
+
+fn field_direct(flag: u32, mut a: (usize,)) -> usize {
+ if flag == 0 {
+ 0
+ } else {
+ a.0 += 5;
+ field_direct(flag - 1, a)
+ }
+}
+
+fn field_deref(flag: u32, a: &mut Box<(usize,)>) -> usize {
+ if flag == 0 {
+ 0
+ } else {
+ a.0 += 5;
+ field_deref(flag - 1, a)
+ }
+}
+
+fn main() {}
--- /dev/null
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion2.rs:3:35
+ |
+LL | fn _with_inner(flag: u32, a: u32, b: u32) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_b`
+ |
+ = note: `-D clippy::only-used-in-recursion` implied by `-D warnings`
+note: parameter used here
+ --> $DIR/only_used_in_recursion2.rs:9:52
+ |
+LL | if flag == 0 { 0 } else { _with_inner(flag, a, b + x) }
+ | ^
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion2.rs:4:25
+ |
+LL | fn inner(flag: u32, a: u32) -> u32 {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion2.rs:5:47
+ |
+LL | if flag == 0 { 0 } else { inner(flag, a) }
+ | ^
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion2.rs:12:34
+ |
+LL | fn _with_closure(a: Option<u32>, b: u32, f: impl Fn(u32, u32) -> Option<u32>) -> u32 {
+ | ^ help: if this is intentional, prefix it with an underscore: `_b`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion2.rs:14:32
+ |
+LL | _with_closure(Some(x), b, f)
+ | ^
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion2.rs:62:37
+ |
+LL | fn overwritten_param(flag: u32, mut a: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion2.rs:70:29
+ |
+LL | overwritten_param(flag, a)
+ | ^
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion2.rs:73:32
+ |
+LL | fn field_direct(flag: u32, mut a: (usize,)) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion2.rs:78:32
+ |
+LL | field_direct(flag - 1, a)
+ | ^
+
+error: aborting due to 5 previous errors
+
let _ = pattern_to_vec("hello world");
let _ = complex_subpat();
+
+ // issue #8492
+ let _ = s.map_or(1, |string| string.len());
+ let _ = Some(10).map_or(5, |a| a + 1);
+
+ let res: Result<i32, i32> = Ok(5);
+ let _ = res.map_or(1, |a| a + 1);
+ let _ = res.map_or(1, |a| a + 1);
+ let _ = res.map_or(5, |a| a + 1);
}
let _ = pattern_to_vec("hello world");
let _ = complex_subpat();
+
+ // issue #8492
+ let _ = match s {
+ Some(string) => string.len(),
+ None => 1,
+ };
+ let _ = match Some(10) {
+ Some(a) => a + 1,
+ None => 5,
+ };
+
+ let res: Result<i32, i32> = Ok(5);
+ let _ = match res {
+ Ok(a) => a + 1,
+ _ => 1,
+ };
+ let _ = match res {
+ Err(_) => 1,
+ Ok(a) => a + 1,
+ };
+ let _ = if let Ok(a) = res { a + 1 } else { 5 };
}
LL ~ });
|
-error: aborting due to 15 previous errors
+error: use Option::map_or instead of an if let/else
+ --> $DIR/option_if_let_else.rs:213:13
+ |
+LL | let _ = match s {
+ | _____________^
+LL | | Some(string) => string.len(),
+LL | | None => 1,
+LL | | };
+ | |_____^ help: try: `s.map_or(1, |string| string.len())`
+
+error: use Option::map_or instead of an if let/else
+ --> $DIR/option_if_let_else.rs:217:13
+ |
+LL | let _ = match Some(10) {
+ | _____________^
+LL | | Some(a) => a + 1,
+LL | | None => 5,
+LL | | };
+ | |_____^ help: try: `Some(10).map_or(5, |a| a + 1)`
+
+error: use Option::map_or instead of an if let/else
+ --> $DIR/option_if_let_else.rs:223:13
+ |
+LL | let _ = match res {
+ | _____________^
+LL | | Ok(a) => a + 1,
+LL | | _ => 1,
+LL | | };
+ | |_____^ help: try: `res.map_or(1, |a| a + 1)`
+
+error: use Option::map_or instead of an if let/else
+ --> $DIR/option_if_let_else.rs:227:13
+ |
+LL | let _ = match res {
+ | _____________^
+LL | | Err(_) => 1,
+LL | | Ok(a) => a + 1,
+LL | | };
+ | |_____^ help: try: `res.map_or(1, |a| a + 1)`
+
+error: use Option::map_or instead of an if let/else
+ --> $DIR/option_if_let_else.rs:231:13
+ |
+LL | let _ = if let Ok(a) = res { a + 1 } else { 5 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or(5, |a| a + 1)`
+
+error: aborting due to 20 previous errors
let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
btree_vec.entry(42).or_insert(vec![]);
- let stringy = Some(String::from(""));
- let _ = stringy.unwrap_or_else(|| "".to_owned());
+ let stringy = Some(String::new());
+ let _ = stringy.unwrap_or_default();
let opt = Some(1);
let hello = "Hello";
let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
btree_vec.entry(42).or_insert(vec![]);
- let stringy = Some(String::from(""));
- let _ = stringy.unwrap_or("".to_owned());
+ let stringy = Some(String::new());
+ let _ = stringy.unwrap_or(String::new());
let opt = Some(1);
let hello = "Hello";
LL | without_default.unwrap_or(Foo::new());
| ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)`
-error: use of `unwrap_or` followed by a function call
+error: use of `unwrap_or` followed by a call to `new`
--> $DIR/or_fun_call.rs:94:21
|
-LL | let _ = stringy.unwrap_or("".to_owned());
- | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())`
+LL | let _ = stringy.unwrap_or(String::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: use of `unwrap_or` followed by a function call
--> $DIR/or_fun_call.rs:102:21
&&None
}
+pub fn macro_expansion() {
+ macro_rules! foo {
+ () => {
+ None::<()>
+ };
+ }
+
+ let _ = foobar() == foo!();
+ let _ = foo!() == foobar();
+ let _ = foo!() == foo!();
+}
+
fn main() {
let x = Some(0);
&&None
}
+pub fn macro_expansion() {
+ macro_rules! foo {
+ () => {
+ None::<()>
+ };
+ }
+
+ let _ = foobar() == foo!();
+ let _ = foo!() == foobar();
+ let _ = foo!() == foo!();
+}
+
fn main() {
let x = Some(0);
= note: `-D clippy::partialeq-to-none` implied by `-D warnings`
error: binary comparison to literal `Option::None`
- --> $DIR/partialeq_to_none.rs:32:13
+ --> $DIR/partialeq_to_none.rs:44:13
|
LL | let _ = x == None;
| ^^^^^^^^^ help: use `Option::is_none()` instead: `x.is_none()`
error: binary comparison to literal `Option::None`
- --> $DIR/partialeq_to_none.rs:33:13
+ --> $DIR/partialeq_to_none.rs:45:13
|
LL | let _ = x != None;
| ^^^^^^^^^ help: use `Option::is_some()` instead: `x.is_some()`
error: binary comparison to literal `Option::None`
- --> $DIR/partialeq_to_none.rs:34:13
+ --> $DIR/partialeq_to_none.rs:46:13
|
LL | let _ = None == x;
| ^^^^^^^^^ help: use `Option::is_none()` instead: `x.is_none()`
error: binary comparison to literal `Option::None`
- --> $DIR/partialeq_to_none.rs:35:13
+ --> $DIR/partialeq_to_none.rs:47:13
|
LL | let _ = None != x;
| ^^^^^^^^^ help: use `Option::is_some()` instead: `x.is_some()`
error: binary comparison to literal `Option::None`
- --> $DIR/partialeq_to_none.rs:37:8
+ --> $DIR/partialeq_to_none.rs:49:8
|
LL | if foobar() == None {}
| ^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `foobar().is_none()`
error: binary comparison to literal `Option::None`
- --> $DIR/partialeq_to_none.rs:39:8
+ --> $DIR/partialeq_to_none.rs:51:8
|
LL | if bar().ok() != None {}
| ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `bar().ok().is_some()`
error: binary comparison to literal `Option::None`
- --> $DIR/partialeq_to_none.rs:41:13
+ --> $DIR/partialeq_to_none.rs:53:13
|
LL | let _ = Some(1 + 2) != None;
| ^^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `Some(1 + 2).is_some()`
error: binary comparison to literal `Option::None`
- --> $DIR/partialeq_to_none.rs:43:13
+ --> $DIR/partialeq_to_none.rs:55:13
|
LL | let _ = { Some(0) } == None;
| ^^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `{ Some(0) }.is_none()`
error: binary comparison to literal `Option::None`
- --> $DIR/partialeq_to_none.rs:45:13
+ --> $DIR/partialeq_to_none.rs:57:13
|
LL | let _ = {
| _____________^
|
error: binary comparison to literal `Option::None`
- --> $DIR/partialeq_to_none.rs:55:13
+ --> $DIR/partialeq_to_none.rs:67:13
|
LL | let _ = optref() == &&None;
| ^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `optref().is_none()`
error: binary comparison to literal `Option::None`
- --> $DIR/partialeq_to_none.rs:56:13
+ --> $DIR/partialeq_to_none.rs:68:13
|
LL | let _ = &&None != optref();
| ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `optref().is_some()`
error: binary comparison to literal `Option::None`
- --> $DIR/partialeq_to_none.rs:57:13
+ --> $DIR/partialeq_to_none.rs:69:13
|
LL | let _ = **optref() == None;
| ^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `optref().is_none()`
error: binary comparison to literal `Option::None`
- --> $DIR/partialeq_to_none.rs:58:13
+ --> $DIR/partialeq_to_none.rs:70:13
|
LL | let _ = &None != *optref();
| ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `optref().is_some()`
error: binary comparison to literal `Option::None`
- --> $DIR/partialeq_to_none.rs:61:13
+ --> $DIR/partialeq_to_none.rs:73:13
|
LL | let _ = None != *x;
| ^^^^^^^^^^ help: use `Option::is_some()` instead: `(*x).is_some()`
--- /dev/null
+// run-rustfix
+#![allow(unused_must_use)]
+#![allow(named_arguments_used_positionally)] // Unstable at time of writing.
+#![warn(clippy::positional_named_format_parameters)]
+
+use std::io::Write;
+
+fn main() {
+ let mut v = Vec::new();
+ let hello = "Hello";
+
+ println!("{hello:.foo$}", foo = 2);
+ writeln!(v, "{hello:.foo$}", foo = 2);
+
+ // Warnings
+ println!("{zero} {one:?}", zero = 0, one = 1);
+ println!("This is a test {zero} {one:?}", zero = 0, one = 1);
+ println!("Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01);
+ println!("Hello {one:zero$}!", zero = 5, one = 1);
+ println!("Hello {zero:one$}!", zero = 4, one = 1);
+ println!("Hello {zero:0one$}!", zero = 4, one = 1);
+ println!("Hello is {one:.zero$}", zero = 5, one = 0.01);
+ println!("Hello is {one:<6.zero$}", zero = 5, one = 0.01);
+ println!("{zero}, `{two:>8.one$}` has 3", zero = hello, one = 3, two = hello);
+ println!("Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01);
+ println!("Hello {world} {world}!", world = 5);
+
+ writeln!(v, "{zero} {one:?}", zero = 0, one = 1);
+ writeln!(v, "This is a test {zero} {one:?}", zero = 0, one = 1);
+ writeln!(v, "Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01);
+ writeln!(v, "Hello {one:zero$}!", zero = 4, one = 1);
+ writeln!(v, "Hello {zero:one$}!", zero = 4, one = 1);
+ writeln!(v, "Hello {zero:0one$}!", zero = 4, one = 1);
+ writeln!(v, "Hello is {one:.zero$}", zero = 3, one = 0.01);
+ writeln!(v, "Hello is {one:<6.zero$}", zero = 2, one = 0.01);
+ writeln!(v, "{zero}, `{two:>8.one$}` has 3", zero = hello, one = 3, two = hello);
+ writeln!(v, "Hello {one} is {two:.zero$}", zero = 1, one = hello, two = 0.01);
+ writeln!(v, "Hello {world} {world}!", world = 0);
+
+ // Tests from other files
+ println!("{w:w$}", w = 1);
+ println!("{p:.p$}", p = 1);
+ println!("{v}", v = 1);
+ println!("{v:v$}", v = 1);
+ println!("{v:v$}", v = 1);
+ println!("{v:v$.v$}", v = 1);
+ println!("{v:v$.v$}", v = 1);
+ println!("{v:v$.v$}", v = 1);
+ println!("{v:v$.v$}", v = 1);
+ println!("{v:v$.v$}", v = 1);
+ println!("{v:v$.v$}", v = 1);
+ println!("{v:v$.v$}", v = 1);
+ println!("{w:w$}", w = 1);
+ println!("{p:.p$}", p = 1);
+ println!("{:p$.w$}", 1, w = 1, p = 1);
+}
--- /dev/null
+// run-rustfix
+#![allow(unused_must_use)]
+#![allow(named_arguments_used_positionally)] // Unstable at time of writing.
+#![warn(clippy::positional_named_format_parameters)]
+
+use std::io::Write;
+
+fn main() {
+ let mut v = Vec::new();
+ let hello = "Hello";
+
+ println!("{hello:.foo$}", foo = 2);
+ writeln!(v, "{hello:.foo$}", foo = 2);
+
+ // Warnings
+ println!("{} {1:?}", zero = 0, one = 1);
+ println!("This is a test { } {000001:?}", zero = 0, one = 1);
+ println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+ println!("Hello {1:0$}!", zero = 5, one = 1);
+ println!("Hello {0:1$}!", zero = 4, one = 1);
+ println!("Hello {0:01$}!", zero = 4, one = 1);
+ println!("Hello is {1:.*}", zero = 5, one = 0.01);
+ println!("Hello is {:<6.*}", zero = 5, one = 0.01);
+ println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
+ println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+ println!("Hello {world} {}!", world = 5);
+
+ writeln!(v, "{} {1:?}", zero = 0, one = 1);
+ writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1);
+ writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+ writeln!(v, "Hello {1:0$}!", zero = 4, one = 1);
+ writeln!(v, "Hello {0:1$}!", zero = 4, one = 1);
+ writeln!(v, "Hello {0:01$}!", zero = 4, one = 1);
+ writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01);
+ writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01);
+ writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
+ writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
+ writeln!(v, "Hello {world} {}!", world = 0);
+
+ // Tests from other files
+ println!("{:w$}", w = 1);
+ println!("{:.p$}", p = 1);
+ println!("{}", v = 1);
+ println!("{:0$}", v = 1);
+ println!("{0:0$}", v = 1);
+ println!("{:0$.0$}", v = 1);
+ println!("{0:0$.0$}", v = 1);
+ println!("{0:0$.v$}", v = 1);
+ println!("{0:v$.0$}", v = 1);
+ println!("{v:0$.0$}", v = 1);
+ println!("{v:v$.0$}", v = 1);
+ println!("{v:0$.v$}", v = 1);
+ println!("{:w$}", w = 1);
+ println!("{:.p$}", p = 1);
+ println!("{:p$.w$}", 1, w = 1, p = 1);
+}
--- /dev/null
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:16:16
+ |
+LL | println!("{} {1:?}", zero = 0, one = 1);
+ | ^ help: replace it with: `zero`
+ |
+ = note: `-D clippy::positional-named-format-parameters` implied by `-D warnings`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:16:19
+ |
+LL | println!("{} {1:?}", zero = 0, one = 1);
+ | ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:17:31
+ |
+LL | println!("This is a test { } {000001:?}", zero = 0, one = 1);
+ | ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:17:35
+ |
+LL | println!("This is a test { } {000001:?}", zero = 0, one = 1);
+ | ^^^^^^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:18:32
+ |
+LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+ | ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:18:22
+ |
+LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+ | ^ help: replace it with: `one`
+
+error: named parameter two is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:18:29
+ |
+LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+ | ^ help: replace it with: `two`
+
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:19:24
+ |
+LL | println!("Hello {1:0$}!", zero = 5, one = 1);
+ | ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:19:22
+ |
+LL | println!("Hello {1:0$}!", zero = 5, one = 1);
+ | ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:20:22
+ |
+LL | println!("Hello {0:1$}!", zero = 4, one = 1);
+ | ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:20:24
+ |
+LL | println!("Hello {0:1$}!", zero = 4, one = 1);
+ | ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:21:22
+ |
+LL | println!("Hello {0:01$}!", zero = 4, one = 1);
+ | ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:21:25
+ |
+LL | println!("Hello {0:01$}!", zero = 4, one = 1);
+ | ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:22:28
+ |
+LL | println!("Hello is {1:.*}", zero = 5, one = 0.01);
+ | ^ help: replace it with: `zero$`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:22:25
+ |
+LL | println!("Hello is {1:.*}", zero = 5, one = 0.01);
+ | ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:23:29
+ |
+LL | println!("Hello is {:<6.*}", zero = 5, one = 0.01);
+ | ^ help: replace it with: `zero$`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:23:25
+ |
+LL | println!("Hello is {:<6.*}", zero = 5, one = 0.01);
+ | ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:24:16
+ |
+LL | println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
+ | ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:24:28
+ |
+LL | println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
+ | ^ help: replace it with: `one$`
+
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:25:32
+ |
+LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+ | ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:25:22
+ |
+LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+ | ^ help: replace it with: `one`
+
+error: named parameter two is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:25:29
+ |
+LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+ | ^ help: replace it with: `two`
+
+error: named parameter world is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:26:30
+ |
+LL | println!("Hello {world} {}!", world = 5);
+ | ^ help: replace it with: `world`
+
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:28:19
+ |
+LL | writeln!(v, "{} {1:?}", zero = 0, one = 1);
+ | ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:28:22
+ |
+LL | writeln!(v, "{} {1:?}", zero = 0, one = 1);
+ | ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:29:34
+ |
+LL | writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1);
+ | ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:29:38
+ |
+LL | writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1);
+ | ^^^^^^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:30:35
+ |
+LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+ | ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:30:25
+ |
+LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+ | ^ help: replace it with: `one`
+
+error: named parameter two is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:30:32
+ |
+LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+ | ^ help: replace it with: `two`
+
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:31:27
+ |
+LL | writeln!(v, "Hello {1:0$}!", zero = 4, one = 1);
+ | ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:31:25
+ |
+LL | writeln!(v, "Hello {1:0$}!", zero = 4, one = 1);
+ | ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:32:25
+ |
+LL | writeln!(v, "Hello {0:1$}!", zero = 4, one = 1);
+ | ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:32:27
+ |
+LL | writeln!(v, "Hello {0:1$}!", zero = 4, one = 1);
+ | ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:33:25
+ |
+LL | writeln!(v, "Hello {0:01$}!", zero = 4, one = 1);
+ | ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:33:28
+ |
+LL | writeln!(v, "Hello {0:01$}!", zero = 4, one = 1);
+ | ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:34:31
+ |
+LL | writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01);
+ | ^ help: replace it with: `zero$`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:34:28
+ |
+LL | writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01);
+ | ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:35:32
+ |
+LL | writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01);
+ | ^ help: replace it with: `zero$`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:35:28
+ |
+LL | writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01);
+ | ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:36:19
+ |
+LL | writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
+ | ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:36:31
+ |
+LL | writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
+ | ^ help: replace it with: `one$`
+
+error: named parameter zero is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:37:35
+ |
+LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
+ | ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:37:25
+ |
+LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
+ | ^ help: replace it with: `one`
+
+error: named parameter two is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:37:32
+ |
+LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
+ | ^ help: replace it with: `two`
+
+error: named parameter world is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:38:33
+ |
+LL | writeln!(v, "Hello {world} {}!", world = 0);
+ | ^ help: replace it with: `world`
+
+error: named parameter w is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:41:16
+ |
+LL | println!("{:w$}", w = 1);
+ | ^ help: replace it with: `w`
+
+error: named parameter p is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:42:16
+ |
+LL | println!("{:.p$}", p = 1);
+ | ^ help: replace it with: `p`
+
+error: named parameter v is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:43:16
+ |
+LL | println!("{}", v = 1);
+ | ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:44:16
+ |
+LL | println!("{:0$}", v = 1);
+ | ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:44:17
+ |
+LL | println!("{:0$}", v = 1);
+ | ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:45:16
+ |
+LL | println!("{0:0$}", v = 1);
+ | ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:45:18
+ |
+LL | println!("{0:0$}", v = 1);
+ | ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:46:16
+ |
+LL | println!("{:0$.0$}", v = 1);
+ | ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:46:20
+ |
+LL | println!("{:0$.0$}", v = 1);
+ | ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:46:17
+ |
+LL | println!("{:0$.0$}", v = 1);
+ | ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:47:16
+ |
+LL | println!("{0:0$.0$}", v = 1);
+ | ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:47:21
+ |
+LL | println!("{0:0$.0$}", v = 1);
+ | ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:47:18
+ |
+LL | println!("{0:0$.0$}", v = 1);
+ | ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:48:16
+ |
+LL | println!("{0:0$.v$}", v = 1);
+ | ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:48:18
+ |
+LL | println!("{0:0$.v$}", v = 1);
+ | ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:49:16
+ |
+LL | println!("{0:v$.0$}", v = 1);
+ | ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:49:21
+ |
+LL | println!("{0:v$.0$}", v = 1);
+ | ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:50:21
+ |
+LL | println!("{v:0$.0$}", v = 1);
+ | ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:50:18
+ |
+LL | println!("{v:0$.0$}", v = 1);
+ | ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:51:21
+ |
+LL | println!("{v:v$.0$}", v = 1);
+ | ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:52:18
+ |
+LL | println!("{v:0$.v$}", v = 1);
+ | ^ help: replace it with: `v`
+
+error: named parameter w is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:53:16
+ |
+LL | println!("{:w$}", w = 1);
+ | ^ help: replace it with: `w`
+
+error: named parameter p is used as a positional parameter
+ --> $DIR/positional_named_format_parameters.rs:54:16
+ |
+LL | println!("{:.p$}", p = 1);
+ | ^ help: replace it with: `p`
+
+error: aborting due to 69 previous errors
+
}
}
+pub struct PatternedError {
+ flag: bool,
+}
+
+// No warning
+fn pattern() -> Result<(), PatternedError> {
+ let res = Ok(());
+
+ if let Err(err @ PatternedError { flag: true }) = res {
+ return Err(err);
+ }
+
+ res
+}
+
fn main() {}
}
}
+pub struct PatternedError {
+ flag: bool,
+}
+
+// No warning
+fn pattern() -> Result<(), PatternedError> {
+ let res = Ok(());
+
+ if let Err(err @ PatternedError { flag: true }) = res {
+ return Err(err);
+ }
+
+ res
+}
+
fn main() {}
-#![allow(unused)]
+#![allow(unused, clippy::needless_borrow)]
#![warn(clippy::invalid_regex, clippy::trivial_regex)]
extern crate regex;
--- /dev/null
+#![warn(clippy::result_large_err)]
+
+pub fn small_err() -> Result<(), u128> {
+ Ok(())
+}
+
+pub fn large_err() -> Result<(), [u8; 512]> {
+ Ok(())
+}
+
+pub struct FullyDefinedLargeError {
+ _foo: u128,
+ _bar: [u8; 100],
+ _foobar: [u8; 120],
+}
+
+impl FullyDefinedLargeError {
+ pub fn ret() -> Result<(), Self> {
+ Ok(())
+ }
+}
+
+pub fn struct_error() -> Result<(), FullyDefinedLargeError> {
+ Ok(())
+}
+
+type Fdlr<T> = std::result::Result<T, FullyDefinedLargeError>;
+pub fn large_err_via_type_alias<T>(x: T) -> Fdlr<T> {
+ Ok(x)
+}
+
+pub fn param_small_error<R>() -> Result<(), (R, u128)> {
+ Ok(())
+}
+
+pub fn param_large_error<R>() -> Result<(), (u128, R, FullyDefinedLargeError)> {
+ Ok(())
+}
+
+pub enum LargeErrorVariants<T> {
+ _Small(u8),
+ _Omg([u8; 512]),
+ _Param(T),
+}
+
+impl LargeErrorVariants<()> {
+ pub fn large_enum_error() -> Result<(), Self> {
+ Ok(())
+ }
+}
+
+trait TraitForcesLargeError {
+ fn large_error() -> Result<(), [u8; 512]> {
+ Ok(())
+ }
+}
+
+struct TraitImpl;
+
+impl TraitForcesLargeError for TraitImpl {
+ // Should not lint
+ fn large_error() -> Result<(), [u8; 512]> {
+ Ok(())
+ }
+}
+
+pub union FullyDefinedUnionError {
+ _maybe: u8,
+ _or_even: [[u8; 16]; 32],
+}
+
+pub fn large_union_err() -> Result<(), FullyDefinedUnionError> {
+ Ok(())
+}
+
+pub union UnionError<T: Copy> {
+ _maybe: T,
+ _or_perhaps_even: (T, [u8; 512]),
+}
+
+pub fn param_large_union<T: Copy>() -> Result<(), UnionError<T>> {
+ Ok(())
+}
+
+pub struct ArrayError<T, U> {
+ _large_array: [T; 32],
+ _other_stuff: U,
+}
+
+pub fn array_error_subst<U>() -> Result<(), ArrayError<i32, U>> {
+ Ok(())
+}
+
+pub fn array_error<T, U>() -> Result<(), ArrayError<(i32, T), U>> {
+ Ok(())
+}
+
+fn main() {}
--- /dev/null
+error: the `Err`-variant returned from this function is very large
+ --> $DIR/result_large_err.rs:7:23
+ |
+LL | pub fn large_err() -> Result<(), [u8; 512]> {
+ | ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
+ |
+ = note: `-D clippy::result-large-err` implied by `-D warnings`
+ = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>`
+
+error: the `Err`-variant returned from this function is very large
+ --> $DIR/result_large_err.rs:18:21
+ |
+LL | pub fn ret() -> Result<(), Self> {
+ | ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes
+ |
+ = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>`
+
+error: the `Err`-variant returned from this function is very large
+ --> $DIR/result_large_err.rs:23:26
+ |
+LL | pub fn struct_error() -> Result<(), FullyDefinedLargeError> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes
+ |
+ = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>`
+
+error: the `Err`-variant returned from this function is very large
+ --> $DIR/result_large_err.rs:28:45
+ |
+LL | pub fn large_err_via_type_alias<T>(x: T) -> Fdlr<T> {
+ | ^^^^^^^ the `Err`-variant is at least 240 bytes
+ |
+ = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>`
+
+error: the `Err`-variant returned from this function is very large
+ --> $DIR/result_large_err.rs:36:34
+ |
+LL | pub fn param_large_error<R>() -> Result<(), (u128, R, FullyDefinedLargeError)> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 256 bytes
+ |
+ = help: try reducing the size of `(u128, R, FullyDefinedLargeError)`, for example by boxing large elements or replacing it with `Box<(u128, R, FullyDefinedLargeError)>`
+
+error: the `Err`-variant returned from this function is very large
+ --> $DIR/result_large_err.rs:47:34
+ |
+LL | pub fn large_enum_error() -> Result<(), Self> {
+ | ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 513 bytes
+ |
+ = help: try reducing the size of `LargeErrorVariants<()>`, for example by boxing large elements or replacing it with `Box<LargeErrorVariants<()>>`
+
+error: the `Err`-variant returned from this function is very large
+ --> $DIR/result_large_err.rs:53:25
+ |
+LL | fn large_error() -> Result<(), [u8; 512]> {
+ | ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
+ |
+ = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>`
+
+error: the `Err`-variant returned from this function is very large
+ --> $DIR/result_large_err.rs:72:29
+ |
+LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
+ |
+ = help: try reducing the size of `FullyDefinedUnionError`, for example by boxing large elements or replacing it with `Box<FullyDefinedUnionError>`
+
+error: the `Err`-variant returned from this function is very large
+ --> $DIR/result_large_err.rs:81:40
+ |
+LL | pub fn param_large_union<T: Copy>() -> Result<(), UnionError<T>> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
+ |
+ = help: try reducing the size of `UnionError<T>`, for example by boxing large elements or replacing it with `Box<UnionError<T>>`
+
+error: the `Err`-variant returned from this function is very large
+ --> $DIR/result_large_err.rs:90:34
+ |
+LL | pub fn array_error_subst<U>() -> Result<(), ArrayError<i32, U>> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes
+ |
+ = help: try reducing the size of `ArrayError<i32, U>`, for example by boxing large elements or replacing it with `Box<ArrayError<i32, U>>`
+
+error: the `Err`-variant returned from this function is very large
+ --> $DIR/result_large_err.rs:94:31
+ |
+LL | pub fn array_error<T, U>() -> Result<(), ArrayError<(i32, T), U>> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes
+ |
+ = help: try reducing the size of `ArrayError<(i32, T), U>`, for example by boxing large elements or replacing it with `Box<ArrayError<(i32, T), U>>`
+
+error: aborting due to 11 previous errors
+
// Fix #6987
let mut vec = Vec::new();
+ #[allow(clippy::needless_borrow)]
for _ in 0..10 {
vec.push(1);
vec.extend(&[2]);
#[allow(clippy::string_add_assign, unused)]
fn main() {
// ignores assignment distinction
- let mut x = "".to_owned();
+ let mut x = String::new();
for _ in 1..3 {
x = x + ".";
}
- let y = "".to_owned();
+ let y = String::new();
let z = y + "...";
assert_eq!(&x, &z);
#[warn(clippy::string_add_assign)]
fn main() {
// ignores assignment distinction
- let mut x = "".to_owned();
+ let mut x = String::new();
for _ in 1..3 {
x += ".";
}
- let y = "".to_owned();
+ let y = String::new();
let z = y + "...";
assert_eq!(&x, &z);
#[warn(clippy::string_add_assign)]
fn main() {
// ignores assignment distinction
- let mut x = "".to_owned();
+ let mut x = String::new();
for _ in 1..3 {
x = x + ".";
}
- let y = "".to_owned();
+ let y = String::new();
let z = y + "...";
assert_eq!(&x, &z);
--- /dev/null
+#![warn(clippy::suspicious_to_owned)]
+#![warn(clippy::implicit_clone)]
+#![allow(clippy::redundant_clone)]
+use std::borrow::Cow;
+use std::ffi::{c_char, CStr};
+
+fn main() {
+ let moo = "Moooo";
+ let c_moo = b"Moooo\0";
+ let c_moo_ptr = c_moo.as_ptr() as *const c_char;
+ let moos = ['M', 'o', 'o'];
+ let moos_vec = moos.to_vec();
+
+ // we expect this to be linted
+ let cow = Cow::Borrowed(moo);
+ let _ = cow.to_owned();
+ // we expect no lints for this
+ let cow = Cow::Borrowed(moo);
+ let _ = cow.into_owned();
+ // we expect no lints for this
+ let cow = Cow::Borrowed(moo);
+ let _ = cow.clone();
+
+ // we expect this to be linted
+ let cow = Cow::Borrowed(&moos);
+ let _ = cow.to_owned();
+ // we expect no lints for this
+ let cow = Cow::Borrowed(&moos);
+ let _ = cow.into_owned();
+ // we expect no lints for this
+ let cow = Cow::Borrowed(&moos);
+ let _ = cow.clone();
+
+ // we expect this to be linted
+ let cow = Cow::Borrowed(&moos_vec);
+ let _ = cow.to_owned();
+ // we expect no lints for this
+ let cow = Cow::Borrowed(&moos_vec);
+ let _ = cow.into_owned();
+ // we expect no lints for this
+ let cow = Cow::Borrowed(&moos_vec);
+ let _ = cow.clone();
+
+ // we expect this to be linted
+ let cow = unsafe { CStr::from_ptr(c_moo_ptr) }.to_string_lossy();
+ let _ = cow.to_owned();
+ // we expect no lints for this
+ let cow = unsafe { CStr::from_ptr(c_moo_ptr) }.to_string_lossy();
+ let _ = cow.into_owned();
+ // we expect no lints for this
+ let cow = unsafe { CStr::from_ptr(c_moo_ptr) }.to_string_lossy();
+ let _ = cow.clone();
+
+ // we expect no lints for these
+ let _ = moo.to_owned();
+ let _ = c_moo.to_owned();
+ let _ = moos.to_owned();
+
+ // we expect implicit_clone lints for these
+ let _ = String::from(moo).to_owned();
+ let _ = moos_vec.to_owned();
+}
--- /dev/null
+error: this `to_owned` call clones the std::borrow::Cow<str> itself and does not cause the std::borrow::Cow<str> contents to become owned
+ --> $DIR/suspicious_to_owned.rs:16:13
+ |
+LL | let _ = cow.to_owned();
+ | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()`
+ |
+ = note: `-D clippy::suspicious-to-owned` implied by `-D warnings`
+
+error: this `to_owned` call clones the std::borrow::Cow<[char; 3]> itself and does not cause the std::borrow::Cow<[char; 3]> contents to become owned
+ --> $DIR/suspicious_to_owned.rs:26:13
+ |
+LL | let _ = cow.to_owned();
+ | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()`
+
+error: this `to_owned` call clones the std::borrow::Cow<std::vec::Vec<char>> itself and does not cause the std::borrow::Cow<std::vec::Vec<char>> contents to become owned
+ --> $DIR/suspicious_to_owned.rs:36:13
+ |
+LL | let _ = cow.to_owned();
+ | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()`
+
+error: this `to_owned` call clones the std::borrow::Cow<str> itself and does not cause the std::borrow::Cow<str> contents to become owned
+ --> $DIR/suspicious_to_owned.rs:46:13
+ |
+LL | let _ = cow.to_owned();
+ | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()`
+
+error: implicitly cloning a `String` by calling `to_owned` on its dereferenced type
+ --> $DIR/suspicious_to_owned.rs:60:13
+ |
+LL | let _ = String::from(moo).to_owned();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::from(moo).clone()`
+ |
+ = note: `-D clippy::implicit-clone` implied by `-D warnings`
+
+error: implicitly cloning a `Vec` by calling `to_owned` on its dereferenced type
+ --> $DIR/suspicious_to_owned.rs:61:13
+ |
+LL | let _ = moos_vec.to_owned();
+ | ^^^^^^^^^^^^^^^^^^^ help: consider using: `moos_vec.clone()`
+
+error: aborting due to 6 previous errors
+
--- /dev/null
+// run-rustfix
+#![deny(clippy::trait_duplication_in_bounds)]
+#![allow(unused)]
+
+fn bad_foo<T: Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
+ unimplemented!();
+}
+
+fn bad_bar<T, U>(arg0: T, arg1: U)
+where
+ T: Clone + Copy,
+ U: Clone + Copy,
+{
+ unimplemented!();
+}
+
+fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) {
+ unimplemented!();
+}
+
+fn good_foo<T, U>(arg0: T, arg1: U)
+where
+ T: Clone + Copy,
+ U: Clone + Copy,
+{
+ unimplemented!();
+}
+
+trait GoodSelfTraitBound: Clone + Copy {
+ fn f();
+}
+
+trait GoodSelfWhereClause {
+ fn f()
+ where
+ Self: Clone + Copy;
+}
+
+trait BadSelfTraitBound: Clone {
+ fn f();
+}
+
+trait BadSelfWhereClause {
+ fn f()
+ where
+ Self: Clone;
+}
+
+trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> {
+ fn f();
+}
+
+trait GoodWhereClause<T, U> {
+ fn f()
+ where
+ T: Clone + Copy,
+ U: Clone + Copy;
+}
+
+trait BadTraitBound<T: Clone + Copy, U: Clone + Copy> {
+ fn f();
+}
+
+trait BadWhereClause<T, U> {
+ fn f()
+ where
+ T: Clone + Copy,
+ U: Clone + Copy;
+}
+
+struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> {
+ t: T,
+ u: U,
+}
+
+impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> {
+ // this should not warn
+ fn f() {}
+}
+
+struct GoodStructWhereClause;
+
+impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause
+where
+ T: Clone + Copy,
+ U: Clone + Copy,
+{
+ // this should not warn
+ fn f() {}
+}
+
+fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {}
+
+trait GenericTrait<T> {}
+
+fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
+ unimplemented!();
+}
+
+fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
+ unimplemented!();
+}
+
+mod foo {
+ pub trait Clone {}
+}
+
+fn qualified_path<T: std::clone::Clone + foo::Clone>(arg0: T) {
+ unimplemented!();
+}
+
+fn main() {}
+// run-rustfix
#![deny(clippy::trait_duplication_in_bounds)]
#![allow(unused)]
-use std::collections::BTreeMap;
-use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
+fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
+ unimplemented!();
+}
-fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+fn bad_bar<T, U>(arg0: T, arg1: U)
where
- T: Clone,
- T: Default,
+ T: Clone + Clone + Clone + Copy,
+ U: Clone + Copy,
{
unimplemented!();
}
-fn good_bar<T: Clone + Default>(arg: T) {
+fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) {
unimplemented!();
}
-fn good_foo<T>(arg: T)
+fn good_foo<T, U>(arg0: T, arg1: U)
where
- T: Clone + Default,
+ T: Clone + Copy,
+ U: Clone + Copy,
{
unimplemented!();
}
-fn good_foobar<T: Default>(arg: T)
-where
- T: Clone,
-{
- unimplemented!();
+trait GoodSelfTraitBound: Clone + Copy {
+ fn f();
}
-trait T: Default {
+trait GoodSelfWhereClause {
fn f()
where
- Self: Default;
+ Self: Clone + Copy;
}
-trait U: Default {
+trait BadSelfTraitBound: Clone + Clone + Clone {
+ fn f();
+}
+
+trait BadSelfWhereClause {
fn f()
where
- Self: Clone;
+ Self: Clone + Clone + Clone;
+}
+
+trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> {
+ fn f();
}
-trait ZZ: Default {
- fn g();
- fn h();
+trait GoodWhereClause<T, U> {
fn f()
where
- Self: Default + Clone;
+ T: Clone + Copy,
+ U: Clone + Copy;
}
-trait BadTrait: Default + Clone {
+trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
+ fn f();
+}
+
+trait BadWhereClause<T, U> {
fn f()
where
- Self: Default + Clone;
- fn g()
- where
- Self: Default;
- fn h()
- where
- Self: Copy;
+ T: Clone + Clone + Clone + Copy,
+ U: Clone + Copy;
}
-#[derive(Default, Clone)]
-struct Life;
+struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> {
+ t: T,
+ u: U,
+}
-impl T for Life {
+impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> {
// this should not warn
fn f() {}
}
-impl U for Life {
+struct GoodStructWhereClause;
+
+impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause
+where
+ T: Clone + Copy,
+ U: Clone + Copy,
+{
// this should not warn
fn f() {}
}
-// should not warn
-trait Iter: Iterator {
- fn into_group_btreemap<K, V>(self) -> BTreeMap<K, Vec<V>>
- where
- Self: Iterator<Item = (K, V)> + Sized,
- K: Ord + Eq,
- {
- unimplemented!();
- }
-}
+fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {}
-struct Foo;
+trait GenericTrait<T> {}
-trait FooIter: Iterator<Item = Foo> {
- fn bar()
- where
- Self: Iterator<Item = Foo>,
- {
- }
+fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
+ unimplemented!();
}
-// This should not lint
-fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
-
-mod repeated_where_clauses_or_trait_bounds {
- fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
- unimplemented!();
- }
-
- fn bad_bar<T, U>(arg0: T, arg1: U)
- where
- T: Clone + Clone + Clone + Copy,
- U: Clone + Copy,
- {
- unimplemented!();
- }
-
- fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) {
- unimplemented!();
- }
-
- fn good_foo<T, U>(arg0: T, arg1: U)
- where
- T: Clone + Copy,
- U: Clone + Copy,
- {
- unimplemented!();
- }
-
- trait GoodSelfTraitBound: Clone + Copy {
- fn f();
- }
-
- trait GoodSelfWhereClause {
- fn f()
- where
- Self: Clone + Copy;
- }
-
- trait BadSelfTraitBound: Clone + Clone + Clone {
- fn f();
- }
-
- trait BadSelfWhereClause {
- fn f()
- where
- Self: Clone + Clone + Clone;
- }
-
- trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> {
- fn f();
- }
-
- trait GoodWhereClause<T, U> {
- fn f()
- where
- T: Clone + Copy,
- U: Clone + Copy;
- }
-
- trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
- fn f();
- }
-
- trait BadWhereClause<T, U> {
- fn f()
- where
- T: Clone + Clone + Clone + Copy,
- U: Clone + Copy;
- }
-
- struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> {
- t: T,
- u: U,
- }
-
- impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> {
- // this should not warn
- fn f() {}
- }
-
- struct GoodStructWhereClause;
-
- impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause
- where
- T: Clone + Copy,
- U: Clone + Copy,
- {
- // this should not warn
- fn f() {}
- }
-
- fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {}
-
- trait GenericTrait<T> {}
-
- // This should not warn but currently does see #8757
- fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
- unimplemented!();
- }
-
- fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
- unimplemented!();
- }
+fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
+ unimplemented!();
+}
- mod foo {
- pub trait Clone {}
- }
+mod foo {
+ pub trait Clone {}
+}
- fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
- unimplemented!();
- }
+fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
+ unimplemented!();
}
fn main() {}
-error: this trait bound is already specified in the where clause
- --> $DIR/trait_duplication_in_bounds.rs:7:15
+error: these bounds contain repeated elements
+ --> $DIR/trait_duplication_in_bounds.rs:5:15
|
-LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
- | ^^^^^
+LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
|
note: the lint level is defined here
- --> $DIR/trait_duplication_in_bounds.rs:1:9
+ --> $DIR/trait_duplication_in_bounds.rs:2:9
|
LL | #![deny(clippy::trait_duplication_in_bounds)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = help: consider removing this trait bound
-
-error: this trait bound is already specified in the where clause
- --> $DIR/trait_duplication_in_bounds.rs:7:23
- |
-LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
- | ^^^^^^^
- |
- = help: consider removing this trait bound
-
-error: this trait bound is already specified in trait declaration
- --> $DIR/trait_duplication_in_bounds.rs:36:15
- |
-LL | Self: Default;
- | ^^^^^^^
- |
- = help: consider removing this trait bound
-
-error: this trait bound is already specified in trait declaration
- --> $DIR/trait_duplication_in_bounds.rs:50:15
- |
-LL | Self: Default + Clone;
- | ^^^^^^^
- |
- = help: consider removing this trait bound
-
-error: this trait bound is already specified in trait declaration
- --> $DIR/trait_duplication_in_bounds.rs:56:15
- |
-LL | Self: Default + Clone;
- | ^^^^^^^
- |
- = help: consider removing this trait bound
-
-error: this trait bound is already specified in trait declaration
- --> $DIR/trait_duplication_in_bounds.rs:56:25
- |
-LL | Self: Default + Clone;
- | ^^^^^
- |
- = help: consider removing this trait bound
-
-error: this trait bound is already specified in trait declaration
- --> $DIR/trait_duplication_in_bounds.rs:59:15
- |
-LL | Self: Default;
- | ^^^^^^^
- |
- = help: consider removing this trait bound
-
-error: this trait bound is already specified in trait declaration
- --> $DIR/trait_duplication_in_bounds.rs:94:15
- |
-LL | Self: Iterator<Item = Foo>,
- | ^^^^^^^^^^^^^^^^^^^^
- |
- = help: consider removing this trait bound
-
-error: this trait bound is already specified in the where clause
- --> $DIR/trait_duplication_in_bounds.rs:103:19
- |
-LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
- | ^^^^^
- |
- = help: consider removing this trait bound
-
-error: these bounds contain repeated elements
- --> $DIR/trait_duplication_in_bounds.rs:103:19
- |
-LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
-
-error: this trait bound is already specified in the where clause
- --> $DIR/trait_duplication_in_bounds.rs:109:12
- |
-LL | T: Clone + Clone + Clone + Copy,
- | ^^^^^
- |
- = help: consider removing this trait bound
error: these where clauses contain repeated elements
- --> $DIR/trait_duplication_in_bounds.rs:109:12
+ --> $DIR/trait_duplication_in_bounds.rs:11:8
|
-LL | T: Clone + Clone + Clone + Copy,
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
+LL | T: Clone + Clone + Clone + Copy,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
error: these bounds contain repeated elements
- --> $DIR/trait_duplication_in_bounds.rs:137:30
+ --> $DIR/trait_duplication_in_bounds.rs:39:26
|
-LL | trait BadSelfTraitBound: Clone + Clone + Clone {
- | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
+LL | trait BadSelfTraitBound: Clone + Clone + Clone {
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
error: these where clauses contain repeated elements
- --> $DIR/trait_duplication_in_bounds.rs:144:19
- |
-LL | Self: Clone + Clone + Clone;
- | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
-
-error: this trait bound is already specified in the where clause
- --> $DIR/trait_duplication_in_bounds.rs:158:28
+ --> $DIR/trait_duplication_in_bounds.rs:46:15
|
-LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
- | ^^^^^
- |
- = help: consider removing this trait bound
+LL | Self: Clone + Clone + Clone;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
error: these bounds contain repeated elements
- --> $DIR/trait_duplication_in_bounds.rs:158:28
+ --> $DIR/trait_duplication_in_bounds.rs:60:24
|
-LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
+LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
error: these where clauses contain repeated elements
- --> $DIR/trait_duplication_in_bounds.rs:165:16
- |
-LL | T: Clone + Clone + Clone + Copy,
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
-
-error: this trait bound is already specified in the where clause
- --> $DIR/trait_duplication_in_bounds.rs:195:24
- |
-LL | fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
- | ^^^^^^^^^^^^^^^^^
- |
- = help: consider removing this trait bound
-
-error: this trait bound is already specified in the where clause
- --> $DIR/trait_duplication_in_bounds.rs:199:23
+ --> $DIR/trait_duplication_in_bounds.rs:67:12
|
-LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
- | ^^^^^^^^^^^^^^^^^
- |
- = help: consider removing this trait bound
+LL | T: Clone + Clone + Clone + Copy,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
error: these bounds contain repeated elements
- --> $DIR/trait_duplication_in_bounds.rs:199:23
- |
-LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `GenericTrait<u32> + GenericTrait<u64>`
-
-error: this trait bound is already specified in the where clause
- --> $DIR/trait_duplication_in_bounds.rs:207:26
- |
-LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
- | ^^^^^^^^^^^^^^^^^
+ --> $DIR/trait_duplication_in_bounds.rs:100:19
|
- = help: consider removing this trait bound
+LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `GenericTrait<u64> + GenericTrait<u32>`
error: these bounds contain repeated elements
- --> $DIR/trait_duplication_in_bounds.rs:207:26
+ --> $DIR/trait_duplication_in_bounds.rs:108:22
|
-LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + foo::Clone`
+LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::clone::Clone + foo::Clone`
-error: aborting due to 22 previous errors
+error: aborting due to 8 previous errors
--- /dev/null
+#![deny(clippy::trait_duplication_in_bounds)]
+
+use std::collections::BTreeMap;
+use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
+
+fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+where
+ T: Clone,
+ T: Default,
+{
+ unimplemented!();
+}
+
+fn good_bar<T: Clone + Default>(arg: T) {
+ unimplemented!();
+}
+
+fn good_foo<T>(arg: T)
+where
+ T: Clone + Default,
+{
+ unimplemented!();
+}
+
+fn good_foobar<T: Default>(arg: T)
+where
+ T: Clone,
+{
+ unimplemented!();
+}
+
+trait T: Default {
+ fn f()
+ where
+ Self: Default;
+}
+
+trait U: Default {
+ fn f()
+ where
+ Self: Clone;
+}
+
+trait ZZ: Default {
+ fn g();
+ fn h();
+ fn f()
+ where
+ Self: Default + Clone;
+}
+
+trait BadTrait: Default + Clone {
+ fn f()
+ where
+ Self: Default + Clone;
+ fn g()
+ where
+ Self: Default;
+ fn h()
+ where
+ Self: Copy;
+}
+
+#[derive(Default, Clone)]
+struct Life;
+
+impl T for Life {
+ // this should not warn
+ fn f() {}
+}
+
+impl U for Life {
+ // this should not warn
+ fn f() {}
+}
+
+// should not warn
+trait Iter: Iterator {
+ fn into_group_btreemap<K, V>(self) -> BTreeMap<K, Vec<V>>
+ where
+ Self: Iterator<Item = (K, V)> + Sized,
+ K: Ord + Eq,
+ {
+ unimplemented!();
+ }
+}
+
+struct Foo;
+
+trait FooIter: Iterator<Item = Foo> {
+ fn bar()
+ where
+ Self: Iterator<Item = Foo>,
+ {
+ }
+}
+
+// The below should not lint and exist to guard against false positives
+fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
+
+pub mod one {
+ #[derive(Clone, Debug)]
+ struct MultiProductIter<I>
+ where
+ I: Iterator + Clone,
+ I::Item: Clone,
+ {
+ _marker: I,
+ }
+
+ pub struct MultiProduct<I>(Vec<MultiProductIter<I>>)
+ where
+ I: Iterator + Clone,
+ I::Item: Clone;
+
+ pub fn multi_cartesian_product<H>(_: H) -> MultiProduct<<H::Item as IntoIterator>::IntoIter>
+ where
+ H: Iterator,
+ H::Item: IntoIterator,
+ <H::Item as IntoIterator>::IntoIter: Clone,
+ <H::Item as IntoIterator>::Item: Clone,
+ {
+ todo!()
+ }
+}
+
+pub mod two {
+ use std::iter::Peekable;
+
+ pub struct MergeBy<I, J, F>
+ where
+ I: Iterator,
+ J: Iterator<Item = I::Item>,
+ {
+ _i: Peekable<I>,
+ _j: Peekable<J>,
+ _f: F,
+ }
+
+ impl<I, J, F> Clone for MergeBy<I, J, F>
+ where
+ I: Iterator,
+ J: Iterator<Item = I::Item>,
+ std::iter::Peekable<I>: Clone,
+ std::iter::Peekable<J>: Clone,
+ F: Clone,
+ {
+ fn clone(&self) -> Self {
+ Self {
+ _i: self._i.clone(),
+ _j: self._j.clone(),
+ _f: self._f.clone(),
+ }
+ }
+ }
+}
+
+pub trait Trait {}
+
+pub fn f(_a: impl Trait, _b: impl Trait) {}
+
+pub trait ImplTrait<T> {}
+
+impl<A, B> ImplTrait<(A, B)> for Foo where Foo: ImplTrait<A> + ImplTrait<B> {}
+
+fn main() {}
--- /dev/null
+error: this trait bound is already specified in the where clause
+ --> $DIR/trait_duplication_in_bounds_unfixable.rs:6:15
+ |
+LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+ | ^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/trait_duplication_in_bounds_unfixable.rs:1:9
+ |
+LL | #![deny(clippy::trait_duplication_in_bounds)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: consider removing this trait bound
+
+error: this trait bound is already specified in the where clause
+ --> $DIR/trait_duplication_in_bounds_unfixable.rs:6:23
+ |
+LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+ | ^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+ --> $DIR/trait_duplication_in_bounds_unfixable.rs:35:15
+ |
+LL | Self: Default;
+ | ^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+ --> $DIR/trait_duplication_in_bounds_unfixable.rs:49:15
+ |
+LL | Self: Default + Clone;
+ | ^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+ --> $DIR/trait_duplication_in_bounds_unfixable.rs:55:15
+ |
+LL | Self: Default + Clone;
+ | ^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+ --> $DIR/trait_duplication_in_bounds_unfixable.rs:55:25
+ |
+LL | Self: Default + Clone;
+ | ^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+ --> $DIR/trait_duplication_in_bounds_unfixable.rs:58:15
+ |
+LL | Self: Default;
+ | ^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+ --> $DIR/trait_duplication_in_bounds_unfixable.rs:93:15
+ |
+LL | Self: Iterator<Item = Foo>,
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: aborting due to 8 previous errors
+
use core::any::TypeId;
use core::ffi::c_void;
use core::mem::{size_of, transmute, MaybeUninit};
+use core::ptr::NonNull;
fn value<T>() -> T {
unimplemented!()
let _: Ty2<u32, u32> = transmute(value::<MaybeUninit<Ty2<u32, u32>>>()); // Ok
let _: Ty<&[u32]> = transmute::<&[u32], _>(value::<&Vec<u32>>()); // Ok
+
+ let _: *const Ty2<u32, u32> = transmute(value::<*const Ty2C<Ty2<u32, u32>, u32>>()); // Ok
+ let _: *const Ty2C<Ty2<u32, u32>, u32> = transmute(value::<*const Ty2<u32, u32>>()); // Ok
+ let _: *const Ty2<u32, u32> = transmute(value::<*const Ty2C<(), Ty2<u32, u32>>>()); // Ok
+ let _: *const Ty2C<(), Ty2<u32, u32>> = transmute(value::<*const Ty2<u32, u32>>()); // Ok
+
+ let _: *const Ty2<u32, u32> = transmute(value::<*const Ty2C<u32, Ty2<u32, u32>>>()); // Err
+ let _: *const Ty2C<u32, Ty2<u32, u32>> = transmute(value::<*const Ty2<u32, u32>>()); // Err
+
+ let _: NonNull<u8> = transmute(value::<NonNull<(String, String)>>()); // Ok
+ let _: NonNull<(String, String)> = transmute(value::<NonNull<u8>>()); // Ok
}
}
error: transmute from `Ty2<u32, i32>` which has an undefined layout
- --> $DIR/transmute_undefined_repr.rs:27:33
+ --> $DIR/transmute_undefined_repr.rs:28:33
|
LL | let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: `-D clippy::transmute-undefined-repr` implied by `-D warnings`
error: transmute into `Ty2<u32, i32>` which has an undefined layout
- --> $DIR/transmute_undefined_repr.rs:28:32
+ --> $DIR/transmute_undefined_repr.rs:29:32
|
LL | let _: Ty2<u32, i32> = transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: transmute from `Ty<Ty2<u32, i32>>` to `Ty2<u32, f32>`, both of which have an undefined layout
- --> $DIR/transmute_undefined_repr.rs:33:32
+ --> $DIR/transmute_undefined_repr.rs:34:32
|
LL | let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: two instances of the same generic type (`Ty2`) may have different layouts
error: transmute from `Ty2<u32, f32>` to `Ty<Ty2<u32, i32>>`, both of which have an undefined layout
- --> $DIR/transmute_undefined_repr.rs:34:36
+ --> $DIR/transmute_undefined_repr.rs:35:36
|
LL | let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: two instances of the same generic type (`Ty2`) may have different layouts
error: transmute from `Ty<&Ty2<u32, i32>>` to `&Ty2<u32, f32>`, both of which have an undefined layout
- --> $DIR/transmute_undefined_repr.rs:39:33
+ --> $DIR/transmute_undefined_repr.rs:40:33
|
LL | let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: two instances of the same generic type (`Ty2`) may have different layouts
error: transmute from `&Ty2<u32, f32>` to `Ty<&Ty2<u32, i32>>`, both of which have an undefined layout
- --> $DIR/transmute_undefined_repr.rs:40:37
+ --> $DIR/transmute_undefined_repr.rs:41:37
|
LL | let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: two instances of the same generic type (`Ty2`) may have different layouts
error: transmute from `std::boxed::Box<Ty2<u32, u32>>` to `&mut Ty2<u32, f32>`, both of which have an undefined layout
- --> $DIR/transmute_undefined_repr.rs:57:45
+ --> $DIR/transmute_undefined_repr.rs:58:45
|
LL | let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Lint, different Ty2 instances
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: two instances of the same generic type (`Ty2`) may have different layouts
error: transmute from `&mut Ty2<u32, f32>` to `std::boxed::Box<Ty2<u32, u32>>`, both of which have an undefined layout
- --> $DIR/transmute_undefined_repr.rs:58:37
+ --> $DIR/transmute_undefined_repr.rs:59:37
|
LL | let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, f32>>()); // Lint, different Ty2 instances
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: two instances of the same generic type (`Ty2`) may have different layouts
+error: transmute into `*const Ty2<u32, u32>` which has an undefined layout
+ --> $DIR/transmute_undefined_repr.rs:119:39
+ |
+LL | let _: *const Ty2<u32, u32> = transmute(value::<*const Ty2C<u32, Ty2<u32, u32>>>()); // Err
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the contained type `Ty2<u32, u32>` has an undefined layout
+
+error: transmute from `*const Ty2<u32, u32>` which has an undefined layout
+ --> $DIR/transmute_undefined_repr.rs:120:50
+ |
+LL | let _: *const Ty2C<u32, Ty2<u32, u32>> = transmute(value::<*const Ty2<u32, u32>>()); // Err
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the contained type `Ty2<u32, u32>` has an undefined layout
+
error: transmute from `std::vec::Vec<Ty2<U, i32>>` to `std::vec::Vec<Ty2<T, u32>>`, both of which have an undefined layout
- --> $DIR/transmute_undefined_repr.rs:138:35
+ --> $DIR/transmute_undefined_repr.rs:150:35
|
LL | let _: Vec<Ty2<T, u32>> = transmute(value::<Vec<Ty2<U, i32>>>()); // Err
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: two instances of the same generic type (`Vec`) may have different layouts
error: transmute from `std::vec::Vec<Ty2<T, u32>>` to `std::vec::Vec<Ty2<U, i32>>`, both of which have an undefined layout
- --> $DIR/transmute_undefined_repr.rs:139:35
+ --> $DIR/transmute_undefined_repr.rs:151:35
|
LL | let _: Vec<Ty2<U, i32>> = transmute(value::<Vec<Ty2<T, u32>>>()); // Err
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: two instances of the same generic type (`Vec`) may have different layouts
-error: aborting due to 10 previous errors
+error: aborting due to 12 previous errors
// run-rustfix
+// compile-flags: --test
+#![allow(dead_code)]
+
#[warn(clippy::invisible_characters)]
fn zero() {
print!("Here >\u{200B}< is a ZWS, and \u{200B}another");
print!("a\u{0300}h?"); // also ok
}
-#[warn(clippy::non_ascii_literal)]
-fn uni() {
- print!("\u{dc}ben!");
- print!("\u{DC}ben!"); // this is ok
-}
+mod non_ascii_literal {
+ #![deny(clippy::non_ascii_literal)]
+
+ fn uni() {
+ print!("\u{dc}ben!");
+ print!("\u{DC}ben!"); // this is ok
+ }
+
+ // issue 8013
+ fn single_quote() {
+ const _EMPTY_BLOCK: char = '\u{25b1}';
+ const _FULL_BLOCK: char = '\u{25b0}';
+ }
+
+ #[test]
+ pub fn issue_7739() {
+ // Ryū crate: https://github.com/dtolnay/ryu
+ }
+
+ mod issue_8263 {
+ #![deny(clippy::non_ascii_literal)]
+
+ // Re-allow for a single test
+ #[test]
+ #[allow(clippy::non_ascii_literal)]
+ fn allowed() {
+ let _ = "悲しいかな、ここに日本語を書くことはできない。";
+ }
-// issue 8013
-#[warn(clippy::non_ascii_literal)]
-fn single_quote() {
- const _EMPTY_BLOCK: char = '\u{25b1}';
- const _FULL_BLOCK: char = '\u{25b0}';
+ #[test]
+ fn denied() {
+ let _ = "\u{60b2}\u{3057}\u{3044}\u{304b}\u{306a}\u{3001}\u{3053}\u{3053}\u{306b}\u{65e5}\u{672c}\u{8a9e}\u{3092}\u{66f8}\u{304f}\u{3053}\u{3068}\u{306f}\u{3067}\u{304d}\u{306a}\u{3044}\u{3002}";
+ }
+ }
}
fn main() {
zero();
- uni();
canon();
- single_quote();
}
// run-rustfix
+// compile-flags: --test
+#![allow(dead_code)]
+
#[warn(clippy::invisible_characters)]
fn zero() {
print!("Here >< is a ZWS, and another");
print!("a\u{0300}h?"); // also ok
}
-#[warn(clippy::non_ascii_literal)]
-fn uni() {
- print!("Üben!");
- print!("\u{DC}ben!"); // this is ok
-}
+mod non_ascii_literal {
+ #![deny(clippy::non_ascii_literal)]
+
+ fn uni() {
+ print!("Üben!");
+ print!("\u{DC}ben!"); // this is ok
+ }
+
+ // issue 8013
+ fn single_quote() {
+ const _EMPTY_BLOCK: char = '▱';
+ const _FULL_BLOCK: char = '▰';
+ }
+
+ #[test]
+ pub fn issue_7739() {
+ // Ryū crate: https://github.com/dtolnay/ryu
+ }
+
+ mod issue_8263 {
+ #![deny(clippy::non_ascii_literal)]
+
+ // Re-allow for a single test
+ #[test]
+ #[allow(clippy::non_ascii_literal)]
+ fn allowed() {
+ let _ = "悲しいかな、ここに日本語を書くことはできない。";
+ }
-// issue 8013
-#[warn(clippy::non_ascii_literal)]
-fn single_quote() {
- const _EMPTY_BLOCK: char = '▱';
- const _FULL_BLOCK: char = '▰';
+ #[test]
+ fn denied() {
+ let _ = "悲しいかな、ここに日本語を書くことはできない。";
+ }
+ }
}
fn main() {
zero();
- uni();
canon();
- single_quote();
}
error: invisible character detected
- --> $DIR/unicode.rs:4:12
+ --> $DIR/unicode.rs:7:12
|
LL | print!("Here >< is a ZWS, and another");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{200B}< is a ZWS, and /u{200B}another"`
= note: `-D clippy::invisible-characters` implied by `-D warnings`
error: invisible character detected
- --> $DIR/unicode.rs:6:12
+ --> $DIR/unicode.rs:9:12
|
LL | print!("Here >< is a SHY, and another");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{AD}< is a SHY, and /u{AD}another"`
error: invisible character detected
- --> $DIR/unicode.rs:8:12
+ --> $DIR/unicode.rs:11:12
|
LL | print!("Here >< is a WJ, and another");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{2060}< is a WJ, and /u{2060}another"`
error: non-NFC Unicode sequence detected
- --> $DIR/unicode.rs:14:12
+ --> $DIR/unicode.rs:17:12
|
LL | print!("̀àh?");
| ^^^^^ help: consider replacing the string with: `"̀àh?"`
= note: `-D clippy::unicode-not-nfc` implied by `-D warnings`
error: literal non-ASCII character detected
- --> $DIR/unicode.rs:20:12
+ --> $DIR/unicode.rs:25:16
|
-LL | print!("Üben!");
- | ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"`
+LL | print!("Üben!");
+ | ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"`
|
- = note: `-D clippy::non-ascii-literal` implied by `-D warnings`
+note: the lint level is defined here
+ --> $DIR/unicode.rs:22:13
+ |
+LL | #![deny(clippy::non_ascii_literal)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: literal non-ASCII character detected
+ --> $DIR/unicode.rs:31:36
+ |
+LL | const _EMPTY_BLOCK: char = '▱';
+ | ^^^ help: consider replacing the string with: `'/u{25b1}'`
error: literal non-ASCII character detected
- --> $DIR/unicode.rs:27:32
+ --> $DIR/unicode.rs:32:35
|
-LL | const _EMPTY_BLOCK: char = '▱';
- | ^^^ help: consider replacing the string with: `'/u{25b1}'`
+LL | const _FULL_BLOCK: char = '▰';
+ | ^^^ help: consider replacing the string with: `'/u{25b0}'`
error: literal non-ASCII character detected
- --> $DIR/unicode.rs:28:31
+ --> $DIR/unicode.rs:52:21
+ |
+LL | let _ = "悲しいかな、ここに日本語を書くことはできない。";
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"/u{60b2}/u{3057}/u{3044}/u{304b}/u{306a}/u{3001}/u{3053}/u{3053}/u{306b}/u{65e5}/u{672c}/u{8a9e}/u{3092}/u{66f8}/u{304f}/u{3053}/u{3068}/u{306f}/u{3067}/u{304d}/u{306a}/u{3044}/u{3002}"`
+ |
+note: the lint level is defined here
+ --> $DIR/unicode.rs:41:17
|
-LL | const _FULL_BLOCK: char = '▰';
- | ^^^ help: consider replacing the string with: `'/u{25b0}'`
+LL | #![deny(clippy::non_ascii_literal)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 7 previous errors
+error: aborting due to 8 previous errors
#![feature(stmt_expr_attributes)]
-#![allow(clippy::let_unit_value)]
+#![allow(clippy::let_unit_value, invalid_value)]
use std::mem::{self, MaybeUninit};
}
type I32Alias = i32;
+
+ fn issue_9380() {
+ let _: i32 = -1_i32;
+ let _: f32 = -(1) as f32;
+ let _: i64 = -1_i64;
+ let _: i64 = -(1.0) as i64;
+
+ let _ = -(1 + 1) as i64;
+ }
}
}
type I32Alias = i32;
+
+ fn issue_9380() {
+ let _: i32 = -(1) as i32;
+ let _: f32 = -(1) as f32;
+ let _: i64 = -(1) as i64;
+ let _: i64 = -(1.0) as i64;
+
+ let _ = -(1 + 1) as i64;
+ }
}
LL | let _ = -1.0 as f32;
| ^^^^^^^^^^^ help: try: `-1.0_f32`
-error: aborting due to 25 previous errors
+error: casting integer literal to `i32` is unnecessary
+ --> $DIR/unnecessary_cast.rs:93:22
+ |
+LL | let _: i32 = -(1) as i32;
+ | ^^^^^^^^^^^ help: try: `-1_i32`
+
+error: casting integer literal to `i64` is unnecessary
+ --> $DIR/unnecessary_cast.rs:95:22
+ |
+LL | let _: i64 = -(1) as i64;
+ | ^^^^^^^^^^^ help: try: `-1_i64`
+
+error: aborting due to 27 previous errors
ref_str_argument("");
// should be linted
+ #[allow(clippy::manual_string_new)]
ref_str_argument("");
// should not be linted
ref_str_argument(&String::new());
// should be linted
+ #[allow(clippy::manual_string_new)]
ref_str_argument(&String::from(""));
// should not be linted
= note: `-D clippy::unnecessary-owned-empty-strings` implied by `-D warnings`
error: usage of `&String::from("")` for a function expecting a `&str` argument
- --> $DIR/unnecessary_owned_empty_strings.rs:15:22
+ --> $DIR/unnecessary_owned_empty_strings.rs:16:22
|
LL | ref_str_argument(&String::from(""));
| ^^^^^^^^^^^^^^^^^ help: try: `""`
rw.set_view(&rw.default_view().to_owned());
}
}
+
+mod issue_9317 {
+ #![allow(dead_code)]
+
+ struct Bytes {}
+
+ impl ToString for Bytes {
+ fn to_string(&self) -> String {
+ "123".to_string()
+ }
+ }
+
+ impl AsRef<[u8]> for Bytes {
+ fn as_ref(&self) -> &[u8] {
+ &[1, 2, 3]
+ }
+ }
+
+ fn consume<C: AsRef<[u8]>>(c: C) {
+ let _ = c;
+ }
+
+ pub fn main() {
+ let b = Bytes {};
+ // Should not lint.
+ consume(b.to_string());
+ }
+}
rw.set_view(&rw.default_view().to_owned());
}
}
+
+mod issue_9317 {
+ #![allow(dead_code)]
+
+ struct Bytes {}
+
+ impl ToString for Bytes {
+ fn to_string(&self) -> String {
+ "123".to_string()
+ }
+ }
+
+ impl AsRef<[u8]> for Bytes {
+ fn as_ref(&self) -> &[u8] {
+ &[1, 2, 3]
+ }
+ }
+
+ fn consume<C: AsRef<[u8]>>(c: C) {
+ let _ = c;
+ }
+
+ pub fn main() {
+ let b = Bytes {};
+ // Should not lint.
+ consume(b.to_string());
+ }
+}
--- /dev/null
+#![warn(clippy::unused_peekable)]
+#![allow(clippy::no_effect)]
+
+use std::iter::Empty;
+use std::iter::Peekable;
+
+fn main() {
+ invalid();
+ valid();
+}
+
+#[allow(clippy::unused_unit)]
+fn invalid() {
+ let peekable = std::iter::empty::<u32>().peekable();
+
+ // Only lint `new_local`
+ let old_local = std::iter::empty::<u32>().peekable();
+ let new_local = old_local;
+
+ // Behind mut ref
+ let mut by_mut_ref_test = std::iter::empty::<u32>().peekable();
+ let by_mut_ref = &mut by_mut_ref_test;
+
+ // Explicitly returns `Peekable`
+ fn returns_peekable() -> Peekable<Empty<u32>> {
+ std::iter::empty().peekable()
+ }
+
+ let peekable_from_fn = returns_peekable();
+
+ // Using a method not exclusive to `Peekable`
+ let mut peekable_using_iterator_method = std::iter::empty::<u32>().peekable();
+ peekable_using_iterator_method.next();
+
+ // Passed by ref to another function
+ fn takes_ref(_peek: &Peekable<Empty<u32>>) {}
+ let passed_along_ref = std::iter::empty::<u32>().peekable();
+ takes_ref(&passed_along_ref);
+
+ // `by_ref` without `peek`
+ let mut by_ref_test = std::iter::empty::<u32>().peekable();
+ let _by_ref = by_ref_test.by_ref();
+
+ let mut peekable_in_for_loop = std::iter::empty::<u32>().peekable();
+ for x in peekable_in_for_loop {}
+}
+
+fn valid() {
+ fn takes_peekable(_peek: Peekable<Empty<u32>>) {}
+
+ // Passed to another function
+ let passed_along = std::iter::empty::<u32>().peekable();
+ takes_peekable(passed_along);
+
+ // Passed to another method
+ struct PeekableConsumer;
+ impl PeekableConsumer {
+ fn consume(&self, _: Peekable<Empty<u32>>) {}
+ fn consume_mut_ref(&self, _: &mut Peekable<Empty<u32>>) {}
+ }
+
+ let peekable_consumer = PeekableConsumer;
+ let mut passed_along_to_method = std::iter::empty::<u32>().peekable();
+ peekable_consumer.consume_mut_ref(&mut passed_along_to_method);
+ peekable_consumer.consume(passed_along_to_method);
+
+ // `peek` called in another block
+ let mut peekable_in_block = std::iter::empty::<u32>().peekable();
+ {
+ peekable_in_block.peek();
+ }
+
+ // Check the other `Peekable` methods :)
+ {
+ let mut peekable_with_peek_mut = std::iter::empty::<u32>().peekable();
+ peekable_with_peek_mut.peek_mut();
+
+ let mut peekable_with_next_if = std::iter::empty::<u32>().peekable();
+ peekable_with_next_if.next_if(|_| true);
+
+ let mut peekable_with_next_if_eq = std::iter::empty::<u32>().peekable();
+ peekable_with_next_if_eq.next_if_eq(&3);
+ }
+
+ let mut peekable_in_closure = std::iter::empty::<u32>().peekable();
+ let call_peek = |p: &mut Peekable<Empty<u32>>| {
+ p.peek();
+ };
+ call_peek(&mut peekable_in_closure);
+
+ // From a macro
+ macro_rules! make_me_a_peekable_please {
+ () => {
+ std::iter::empty::<u32>().peekable()
+ };
+ }
+
+ let _unsuspecting_macro_user = make_me_a_peekable_please!();
+
+ // Generic Iterator returned
+ fn return_an_iter() -> impl Iterator<Item = u32> {
+ std::iter::empty::<u32>().peekable()
+ }
+
+ let _unsuspecting_user = return_an_iter();
+
+ // Call `peek` in a macro
+ macro_rules! peek_iter {
+ ($iter:ident) => {
+ $iter.peek();
+ };
+ }
+
+ let mut peek_in_macro = std::iter::empty::<u32>().peekable();
+ peek_iter!(peek_in_macro);
+
+ // Behind mut ref
+ let mut by_mut_ref_test = std::iter::empty::<u32>().peekable();
+ let by_mut_ref = &mut by_mut_ref_test;
+ by_mut_ref.peek();
+
+ // Behind ref
+ let mut by_ref_test = std::iter::empty::<u32>().peekable();
+ let by_ref = &by_ref_test;
+ by_ref_test.peek();
+
+ // In struct
+ struct PeekableWrapper {
+ f: Peekable<Empty<u32>>,
+ }
+
+ let struct_test = std::iter::empty::<u32>().peekable();
+ PeekableWrapper { f: struct_test };
+
+ // `by_ref` before `peek`
+ let mut by_ref_test = std::iter::empty::<u32>().peekable();
+ let peeked_val = by_ref_test.by_ref().peek();
+
+ // `peek` called in another block as the last expression
+ let mut peekable_last_expr = std::iter::empty::<u32>().peekable();
+ {
+ peekable_last_expr.peek();
+ }
+}
--- /dev/null
+error: `peek` never called on `Peekable` iterator
+ --> $DIR/unused_peekable.rs:14:9
+ |
+LL | let peekable = std::iter::empty::<u32>().peekable();
+ | ^^^^^^^^
+ |
+ = note: `-D clippy::unused-peekable` implied by `-D warnings`
+ = help: consider removing the call to `peekable`
+
+error: `peek` never called on `Peekable` iterator
+ --> $DIR/unused_peekable.rs:18:9
+ |
+LL | let new_local = old_local;
+ | ^^^^^^^^^
+ |
+ = help: consider removing the call to `peekable`
+
+error: `peek` never called on `Peekable` iterator
+ --> $DIR/unused_peekable.rs:22:9
+ |
+LL | let by_mut_ref = &mut by_mut_ref_test;
+ | ^^^^^^^^^^
+ |
+ = help: consider removing the call to `peekable`
+
+error: `peek` never called on `Peekable` iterator
+ --> $DIR/unused_peekable.rs:29:9
+ |
+LL | let peekable_from_fn = returns_peekable();
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing the call to `peekable`
+
+error: `peek` never called on `Peekable` iterator
+ --> $DIR/unused_peekable.rs:32:13
+ |
+LL | let mut peekable_using_iterator_method = std::iter::empty::<u32>().peekable();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing the call to `peekable`
+
+error: `peek` never called on `Peekable` iterator
+ --> $DIR/unused_peekable.rs:37:9
+ |
+LL | let passed_along_ref = std::iter::empty::<u32>().peekable();
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing the call to `peekable`
+
+error: `peek` never called on `Peekable` iterator
+ --> $DIR/unused_peekable.rs:42:9
+ |
+LL | let _by_ref = by_ref_test.by_ref();
+ | ^^^^^^^
+ |
+ = help: consider removing the call to `peekable`
+
+error: `peek` never called on `Peekable` iterator
+ --> $DIR/unused_peekable.rs:44:13
+ |
+LL | let mut peekable_in_for_loop = std::iter::empty::<u32>().peekable();
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing the call to `peekable`
+
+error: aborting due to 8 previous errors
+
}
fn unwrap_result() {
- let res: Result<u8, ()> = Ok(0);
+ let res: Result<u8, u8> = Ok(0);
let _ = res.unwrap();
+ let _ = res.unwrap_err();
}
fn main() {
|
= help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message
-error: aborting due to 2 previous errors
+error: used `unwrap_err()` on `a Result` value
+ --> $DIR/unwrap.rs:11:13
+ |
+LL | let _ = res.unwrap_err();
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `Ok` case gracefully, consider using `expect_err()` to provide a better panic message
+
+error: aborting due to 3 previous errors
#![warn(clippy::unwrap_used, clippy::expect_used)]
+trait OptionExt {
+ type Item;
+
+ fn unwrap_err(self) -> Self::Item;
+
+ fn expect_err(self, msg: &str) -> Self::Item;
+}
+
+impl<T> OptionExt for Option<T> {
+ type Item = T;
+ fn unwrap_err(self) -> T {
+ panic!();
+ }
+
+ fn expect_err(self, msg: &str) -> T {
+ panic!();
+ }
+}
+
fn main() {
Some(3).unwrap();
Some(3).expect("Hello world!");
+ // Don't trigger on unwrap_err on an option
+ Some(3).unwrap_err();
+ Some(3).expect_err("Hellow none!");
+
let a: Result<i32, i32> = Ok(3);
a.unwrap();
a.expect("Hello world!");
+ a.unwrap_err();
+ a.expect_err("Hello error!");
}
error: used `unwrap()` on `an Option` value
- --> $DIR/unwrap_expect_used.rs:4:5
+ --> $DIR/unwrap_expect_used.rs:23:5
|
LL | Some(3).unwrap();
| ^^^^^^^^^^^^^^^^
= help: if this value is `None`, it will panic
error: used `expect()` on `an Option` value
- --> $DIR/unwrap_expect_used.rs:5:5
+ --> $DIR/unwrap_expect_used.rs:24:5
|
LL | Some(3).expect("Hello world!");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: if this value is `None`, it will panic
error: used `unwrap()` on `a Result` value
- --> $DIR/unwrap_expect_used.rs:8:5
+ --> $DIR/unwrap_expect_used.rs:31:5
|
LL | a.unwrap();
| ^^^^^^^^^^
= help: if this value is an `Err`, it will panic
error: used `expect()` on `a Result` value
- --> $DIR/unwrap_expect_used.rs:9:5
+ --> $DIR/unwrap_expect_used.rs:32:5
|
LL | a.expect("Hello world!");
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: if this value is an `Err`, it will panic
-error: aborting due to 4 previous errors
+error: used `unwrap_err()` on `a Result` value
+ --> $DIR/unwrap_expect_used.rs:33:5
+ |
+LL | a.unwrap_err();
+ | ^^^^^^^^^^^^^^
+ |
+ = help: if this value is an `Ok`, it will panic
+
+error: used `expect_err()` on `a Result` value
+ --> $DIR/unwrap_expect_used.rs:34:5
+ |
+LL | a.expect_err("Hello error!");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if this value is an `Ok`, it will panic
+
+error: aborting due to 6 previous errors
let _ = String::try_from("foo".to_string()).unwrap();
let _ = String::try_from(format!("A: {:04}", 123)).unwrap();
let _: String = format!("Hello {}", "world").try_into().unwrap();
- let _: String = "".to_owned().try_into().unwrap();
+ let _: String = String::new().try_into().unwrap();
let _: String = match String::from("_").try_into() {
Ok(a) => a,
- Err(_) => "".into(),
+ Err(_) => String::new(),
};
// FIXME this is a false negative
#[allow(clippy::cmp_owned)]
error: useless conversion to the same type: `std::string::String`
--> $DIR/useless_conversion_try.rs:32:21
|
-LL | let _: String = "".to_owned().try_into().unwrap();
+LL | let _: String = String::new().try_into().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider removing `.try_into()`
#![warn(clippy::vec_resize_to_zero)]
fn main() {
+ let mut v = vec![1, 2, 3, 4, 5];
+
// applicable here
- vec![1, 2, 3, 4, 5].resize(0, 5);
+ v.resize(0, 5);
// not applicable
- vec![1, 2, 3, 4, 5].resize(2, 5);
+ v.resize(2, 5);
+
+ let mut v = vec!["foo", "bar", "baz"];
// applicable here, but only implemented for integer literals for now
- vec!["foo", "bar", "baz"].resize(0, "bar");
+ v.resize(0, "bar");
// not applicable
- vec!["foo", "bar", "baz"].resize(2, "bar")
+ v.resize(2, "bar")
}
error: emptying a vector with `resize`
- --> $DIR/vec_resize_to_zero.rs:5:5
+ --> $DIR/vec_resize_to_zero.rs:7:5
|
-LL | vec![1, 2, 3, 4, 5].resize(0, 5);
- | ^^^^^^^^^^^^^^^^^^^^------------
- | |
- | help: ...or you can empty the vector with: `clear()`
+LL | v.resize(0, 5);
+ | ^^------------
+ | |
+ | help: ...or you can empty the vector with: `clear()`
|
= note: `-D clippy::vec-resize-to-zero` implied by `-D warnings`
= help: the arguments may be inverted...
s.read_to_end();
s.read_to_string();
// Should catch this
- let mut f = File::open(&path)?;
+ let mut f = File::open(path)?;
let mut buffer = Vec::new();
f.read_to_end(&mut buffer)?;
// ...and this
.current_dir(&cwd)
.env("CARGO_TARGET_DIR", &target_dir)
.arg("clean")
- .args(&["-p", "subcrate"])
- .args(&["-p", "path_dep"])
+ .args(["-p", "subcrate"])
+ .args(["-p", "path_dep"])
.output()
.unwrap();
.env("CARGO_INCREMENTAL", "0")
.env("CARGO_TARGET_DIR", &target_dir)
.arg("clippy")
- .args(&["-p", "subcrate"])
+ .args(["-p", "subcrate"])
.arg("--no-deps")
.arg("--")
.arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
- .args(&["--cfg", r#"feature="primary_package_test""#])
+ .args(["--cfg", r#"feature="primary_package_test""#])
.output()
.unwrap();
println!("status: {}", output.status);
.env("CARGO_INCREMENTAL", "0")
.env("CARGO_TARGET_DIR", &target_dir)
.arg("clippy")
- .args(&["-p", "subcrate"])
+ .args(["-p", "subcrate"])
.arg("--")
.arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
- .args(&["--cfg", r#"feature="primary_package_test""#])
+ .args(["--cfg", r#"feature="primary_package_test""#])
.output()
.unwrap();
println!("status: {}", output.status);
.env("CARGO_INCREMENTAL", "0")
.env("CARGO_TARGET_DIR", &target_dir)
.arg("clippy")
- .args(&["-p", "subcrate"])
+ .args(["-p", "subcrate"])
.arg("--")
.arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
.output()
/// Descriptions of rustc lint groups.
static GROUP_DESCRIPTIONS: &[(&str, &str)] = &[
("unused", "Lints that detect things being declared but not used, or excess syntax"),
+ ("let-underscore", "Lints that detect wildcard let bindings that are likely to be invalid"),
("rustdoc", "Rustdoc-specific lints"),
("rust-2018-idioms", "Lints to nudge you toward idiomatic features of Rust 2018"),
("nonstandard-style", "Violation of standard naming conventions"),
-Subproject commit ab88e64b152d3704c35db96dbbc6efaaed67773f
+Subproject commit 8c8b479be723fb103b0b1203faf9246a3f587250
"cfg-if",
]
-[[package]]
-name = "crossbeam"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c"
-dependencies = [
- "cfg-if",
- "crossbeam-channel",
- "crossbeam-deque",
- "crossbeam-epoch",
- "crossbeam-queue",
- "crossbeam-utils",
-]
-
[[package]]
name = "crossbeam-channel"
version = "0.5.6"
"scopeguard",
]
-[[package]]
-name = "crossbeam-queue"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7"
-dependencies = [
- "cfg-if",
- "crossbeam-utils",
-]
-
[[package]]
name = "crossbeam-utils"
version = "0.8.11"
"ide-db",
"itertools",
"parser",
+ "stdx",
"syntax",
"test-utils",
"text-edit",
[[package]]
name = "lsp-types"
-version = "0.93.0"
+version = "0.93.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70c74e2173b2b31f8655d33724b4b45ac13f439386f66290f539c22b144c2212"
+checksum = "a3bcfee315dde785ba887edb540b08765fd7df75a7d948844be6bf5712246734"
dependencies = [
"bitflags",
"serde",
name = "proc-macro-srv"
version = "0.0.0"
dependencies = [
- "crossbeam",
"expect-test",
"libloading",
"mbe",
"tracing",
]
+[[package]]
+name = "protobuf"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ee4a7d8b91800c8f167a6268d1a1026607368e1adc84e98fe044aeb905302f7"
+dependencies = [
+ "once_cell",
+ "protobuf-support",
+ "thiserror",
+]
+
+[[package]]
+name = "protobuf-support"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ca157fe12fc7ee2e315f2f735e27df41b3d97cdd70ea112824dac1ffb08ee1c"
+dependencies = [
+ "thiserror",
+]
+
[[package]]
name = "pulldown-cmark"
version = "0.9.2"
"project-model",
"rayon",
"rustc-hash",
+ "scip",
"serde",
"serde_json",
"sourcegen",
"winapi-util",
]
+[[package]]
+name = "scip"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2bfbb10286f69fad7c78db71004b7839bf957788359fe0c479f029f9849136b"
+dependencies = [
+ "protobuf",
+]
+
[[package]]
name = "scoped-tls"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a"
+[[package]]
+name = "thiserror"
+version = "1.0.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "thread_local"
version = "1.1.4"
"indexmap",
"paths",
"rustc-hash",
+ "stdx",
]
[[package]]
use std::{fmt, ops, panic::RefUnwindSafe, str::FromStr, sync::Arc};
use cfg::CfgOptions;
-use rustc_hash::{FxHashMap, FxHashSet};
+use rustc_hash::FxHashMap;
+use stdx::hash::{NoHashHashMap, NoHashHashSet};
use syntax::SmolStr;
use tt::Subtree;
-use vfs::{file_set::FileSet, FileId, VfsPath};
+use vfs::{file_set::FileSet, AnchoredPath, FileId, VfsPath};
/// Files are grouped into source roots. A source root is a directory on the
/// file systems which is watched for changes. Typically it corresponds to a
/// Libraries are considered mostly immutable, this assumption is used to
/// optimize salsa's query structure
pub is_library: bool,
- pub(crate) file_set: FileSet,
+ file_set: FileSet,
}
impl SourceRoot {
pub fn new_local(file_set: FileSet) -> SourceRoot {
SourceRoot { is_library: false, file_set }
}
+
pub fn new_library(file_set: FileSet) -> SourceRoot {
SourceRoot { is_library: true, file_set }
}
+
pub fn path_for_file(&self, file: &FileId) -> Option<&VfsPath> {
self.file_set.path_for_file(file)
}
+
pub fn file_for_path(&self, path: &VfsPath) -> Option<&FileId> {
self.file_set.file_for_path(path)
}
+
+ pub fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
+ self.file_set.resolve_path(path)
+ }
+
pub fn iter(&self) -> impl Iterator<Item = FileId> + '_ {
self.file_set.iter()
}
/// <https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/architecture.md#serialization>
#[derive(Debug, Clone, Default /* Serialize, Deserialize */)]
pub struct CrateGraph {
- arena: FxHashMap<CrateId, CrateData>,
+ arena: NoHashHashMap<CrateId, CrateData>,
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct CrateId(pub u32);
+impl stdx::hash::NoHashHashable for CrateId {}
+impl std::hash::Hash for CrateId {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ self.0.hash(state);
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CrateName(SmolStr);
// Check if adding a dep from `from` to `to` creates a cycle. To figure
// that out, look for a path in the *opposite* direction, from `to` to
// `from`.
- if let Some(path) = self.find_path(&mut FxHashSet::default(), dep.crate_id, from) {
+ if let Some(path) = self.find_path(&mut NoHashHashSet::default(), dep.crate_id, from) {
let path = path.into_iter().map(|it| (it, self[it].display_name.clone())).collect();
let err = CyclicDependenciesError { path };
assert!(err.from().0 == from && err.to().0 == dep.crate_id);
/// including the crate itself.
pub fn transitive_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> {
let mut worklist = vec![of];
- let mut deps = FxHashSet::default();
+ let mut deps = NoHashHashSet::default();
while let Some(krate) = worklist.pop() {
if !deps.insert(krate) {
/// including the crate itself.
pub fn transitive_rev_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> {
let mut worklist = vec![of];
- let mut rev_deps = FxHashSet::default();
+ let mut rev_deps = NoHashHashSet::default();
rev_deps.insert(of);
- let mut inverted_graph = FxHashMap::<_, Vec<_>>::default();
+ let mut inverted_graph = NoHashHashMap::<_, Vec<_>>::default();
self.arena.iter().for_each(|(&krate, data)| {
data.dependencies
.iter()
/// come before the crate itself).
pub fn crates_in_topological_order(&self) -> Vec<CrateId> {
let mut res = Vec::new();
- let mut visited = FxHashSet::default();
+ let mut visited = NoHashHashSet::default();
for krate in self.arena.keys().copied() {
go(self, &mut visited, &mut res, krate);
fn go(
graph: &CrateGraph,
- visited: &mut FxHashSet<CrateId>,
+ visited: &mut NoHashHashSet<CrateId>,
res: &mut Vec<CrateId>,
source: CrateId,
) {
fn find_path(
&self,
- visited: &mut FxHashSet<CrateId>,
+ visited: &mut NoHashHashSet<CrateId>,
from: CrateId,
to: CrateId,
) -> Option<Vec<CrateId>> {
use std::{panic, sync::Arc};
-use rustc_hash::FxHashSet;
+use stdx::hash::NoHashHashSet;
use syntax::{ast, Parse, SourceFile, TextRange, TextSize};
pub use crate::{
/// Text of the file.
fn file_text(&self, file_id: FileId) -> Arc<String>;
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId>;
- fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>>;
+ fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>>;
}
/// Database which stores all significant input facts: source code and project
#[salsa::input]
fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>;
- fn source_root_crates(&self, id: SourceRootId) -> Arc<FxHashSet<CrateId>>;
+ fn source_root_crates(&self, id: SourceRootId) -> Arc<NoHashHashSet<CrateId>>;
}
-fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<FxHashSet<CrateId>> {
+fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<NoHashHashSet<CrateId>> {
let graph = db.crate_graph();
let res = graph
.iter()
// FIXME: this *somehow* should be platform agnostic...
let source_root = self.0.file_source_root(path.anchor);
let source_root = self.0.source_root(source_root);
- source_root.file_set.resolve_path(path)
+ source_root.resolve_path(path)
}
- fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
+ fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
let _p = profile::span("relevant_crates");
let source_root = self.0.file_source_root(file_id);
self.0.source_root_crates(source_root)
DidCheckCrate(String),
DidFinish(io::Result<()>),
DidCancel,
+ DidFailToRestart(String),
}
enum Restart {
self.progress(Progress::DidStart);
}
Err(error) => {
- tracing::error!(
- command = ?self.check_command(),
- %error, "failed to restart flycheck"
- );
+ self.progress(Progress::DidFailToRestart(format!(
+ "Failed to run the following command: {:?} error={}",
+ self.check_command(),
+ error
+ )));
}
}
}
use std::sync::Arc;
-use hir_expand::{name::Name, AstId, ExpandResult, HirFileId, MacroCallId, MacroDefKind};
+use hir_expand::{name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroDefKind};
use smallvec::SmallVec;
use syntax::ast;
db::DefDatabase,
intern::Interned,
item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, ModItem, Param, TreeId},
- nameres::{attr_resolution::ResolvedAttr, proc_macro::ProcMacroKind, DefMap},
+ nameres::{
+ attr_resolution::ResolvedAttr, diagnostics::DefDiagnostic, proc_macro::ProcMacroKind,
+ DefMap,
+ },
type_ref::{TraitRef, TypeBound, TypeRef},
visibility::RawVisibility,
AssocItemId, AstIdWithPath, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
impl TraitData {
pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> {
+ db.trait_data_with_diagnostics(tr).0
+ }
+
+ pub(crate) fn trait_data_with_diagnostics_query(
+ db: &dyn DefDatabase,
+ tr: TraitId,
+ ) -> (Arc<TraitData>, Arc<Vec<DefDiagnostic>>) {
let tr_loc @ ItemLoc { container: module_id, id: tree_id } = tr.lookup(db);
let item_tree = tree_id.item_tree(db);
let tr_def = &item_tree[tree_id.value];
let mut collector =
AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr));
collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items);
- let (items, attribute_calls) = collector.finish();
-
- Arc::new(TraitData {
- name,
- attribute_calls,
- items,
- is_auto,
- is_unsafe,
- visibility,
- skip_array_during_method_dispatch,
- })
+ let (items, attribute_calls, diagnostics) = collector.finish();
+
+ (
+ Arc::new(TraitData {
+ name,
+ attribute_calls,
+ items,
+ is_auto,
+ is_unsafe,
+ visibility,
+ skip_array_during_method_dispatch,
+ }),
+ Arc::new(diagnostics),
+ )
}
pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ {
impl ImplData {
pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> {
- let _p = profile::span("impl_data_query");
+ db.impl_data_with_diagnostics(id).0
+ }
+
+ pub(crate) fn impl_data_with_diagnostics_query(
+ db: &dyn DefDatabase,
+ id: ImplId,
+ ) -> (Arc<ImplData>, Arc<Vec<DefDiagnostic>>) {
+ let _p = profile::span("impl_data_with_diagnostics_query");
let ItemLoc { container: module_id, id: tree_id } = id.lookup(db);
let item_tree = tree_id.item_tree(db);
AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::ImplId(id));
collector.collect(&item_tree, tree_id.tree_id(), &impl_def.items);
- let (items, attribute_calls) = collector.finish();
+ let (items, attribute_calls, diagnostics) = collector.finish();
let items = items.into_iter().map(|(_, item)| item).collect();
- Arc::new(ImplData { target_trait, self_ty, items, is_negative, attribute_calls })
+ (
+ Arc::new(ImplData { target_trait, self_ty, items, is_negative, attribute_calls }),
+ Arc::new(diagnostics),
+ )
}
pub fn attribute_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
db: &'a dyn DefDatabase,
module_id: ModuleId,
def_map: Arc<DefMap>,
+ inactive_diagnostics: Vec<DefDiagnostic>,
container: ItemContainerId,
expander: Expander,
expander: Expander::new(db, file_id, module_id),
items: Vec::new(),
attr_calls: Vec::new(),
+ inactive_diagnostics: Vec::new(),
}
}
fn finish(
self,
- ) -> (Vec<(Name, AssocItemId)>, Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>) {
+ ) -> (
+ Vec<(Name, AssocItemId)>,
+ Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
+ Vec<DefDiagnostic>,
+ ) {
(
self.items,
if self.attr_calls.is_empty() { None } else { Some(Box::new(self.attr_calls)) },
+ self.inactive_diagnostics,
)
}
'items: for &item in assoc_items {
let attrs = item_tree.attrs(self.db, self.module_id.krate, ModItem::from(item).into());
if !attrs.is_cfg_enabled(self.expander.cfg_options()) {
+ self.inactive_diagnostics.push(DefDiagnostic::unconfigured_code(
+ self.module_id.local_id,
+ InFile::new(self.expander.current_file_id(), item.ast_id(&item_tree).upcast()),
+ attrs.cfg().unwrap(),
+ self.expander.cfg_options().clone(),
+ ));
continue;
}
intern::Interned,
item_tree::{AttrOwner, ItemTree},
lang_item::{LangItemTarget, LangItems},
- nameres::DefMap,
+ nameres::{diagnostics::DefDiagnostic, DefMap},
visibility::{self, Visibility},
AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, ExternBlockId,
ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalEnumVariantId,
#[salsa::invoke(ImplData::impl_data_query)]
fn impl_data(&self, e: ImplId) -> Arc<ImplData>;
+ #[salsa::invoke(ImplData::impl_data_with_diagnostics_query)]
+ fn impl_data_with_diagnostics(&self, e: ImplId) -> (Arc<ImplData>, Arc<Vec<DefDiagnostic>>);
+
#[salsa::invoke(TraitData::trait_data_query)]
fn trait_data(&self, e: TraitId) -> Arc<TraitData>;
+ #[salsa::invoke(TraitData::trait_data_with_diagnostics_query)]
+ fn trait_data_with_diagnostics(&self, tr: TraitId)
+ -> (Arc<TraitData>, Arc<Vec<DefDiagnostic>>);
+
#[salsa::invoke(TypeAliasData::type_alias_data_query)]
fn type_alias_data(&self, e: TypeAliasId) -> Arc<TypeAliasData>;
Self { in_module: container, kind: DefDiagnosticKind::UnresolvedImport { id, index } }
}
- pub(super) fn unconfigured_code(
+ pub fn unconfigured_code(
container: LocalModuleId,
ast: AstId<ast::Item>,
cfg: CfgExpr,
SourceDatabase, Upcast,
};
use hir_expand::{db::AstDatabase, InFile};
-use rustc_hash::FxHashSet;
+use stdx::hash::NoHashHashSet;
use syntax::{algo, ast, AstNode};
use crate::{
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
FileLoaderDelegate(self).resolve_path(path)
}
- fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
+ fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
FileLoaderDelegate(self).relevant_crates(file_id)
}
}
//!
//! This usually involves resolving names, collecting generic arguments etc.
use std::{
- cell::{Cell, RefCell},
+ cell::{Cell, RefCell, RefMut},
iter,
sync::Arc,
};
}
}
TypeRef::Macro(macro_call) => {
- let (expander, recursion_start) = {
- let mut expander = self.expander.borrow_mut();
- if expander.is_some() {
- (Some(expander), false)
- } else {
- *expander = Some(Expander::new(
- self.db.upcast(),
- macro_call.file_id,
- self.resolver.module(),
- ));
- (Some(expander), true)
+ let (mut expander, recursion_start) = {
+ match RefMut::filter_map(self.expander.borrow_mut(), Option::as_mut) {
+ Ok(expander) => (expander, false),
+ Err(expander) => (
+ RefMut::map(expander, |it| {
+ it.insert(Expander::new(
+ self.db.upcast(),
+ macro_call.file_id,
+ self.resolver.module(),
+ ))
+ }),
+ true,
+ ),
}
};
- let ty = if let Some(mut expander) = expander {
- let expander_mut = expander.as_mut().unwrap();
+ let ty = {
let macro_call = macro_call.to_node(self.db.upcast());
- match expander_mut.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
+ match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
- let ctx =
- LowerCtx::new(self.db.upcast(), expander_mut.current_file_id());
+ let ctx = LowerCtx::new(self.db.upcast(), expander.current_file_id());
let type_ref = TypeRef::from_ast(&ctx, expanded);
drop(expander);
}
_ => None,
}
- } else {
- None
};
if recursion_start {
*self.expander.borrow_mut() = None;
TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
}
ParamLoweringMode::Variable => {
- let idx = generics.param_idx(param_id.into()).expect("matching generics");
+ let idx = match generics.param_idx(param_id.into()) {
+ None => {
+ never!("no matching generics");
+ return (TyKind::Error.intern(Interner), None);
+ }
+ Some(idx) => idx,
+ };
+
TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
}
}
};
use hir_def::{db::DefDatabase, ModuleId};
use hir_expand::db::AstDatabase;
-use rustc_hash::{FxHashMap, FxHashSet};
+use stdx::hash::{NoHashHashMap, NoHashHashSet};
use syntax::TextRange;
use test_utils::extract_annotations;
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
FileLoaderDelegate(self).resolve_path(path)
}
- fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
+ fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
FileLoaderDelegate(self).relevant_crates(file_id)
}
}
self.module_for_file_opt(file_id).unwrap()
}
- pub(crate) fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> {
+ pub(crate) fn extract_annotations(&self) -> NoHashHashMap<FileId, Vec<(TextRange, String)>> {
let mut files = Vec::new();
let crate_graph = self.crate_graph();
for krate in crate_graph.iter() {
);
}
+#[test]
+fn gat_crash_3() {
+ // FIXME: This test currently crashes rust analyzer in a debug build but not in a
+ // release build (i.e. for the user). With the assumption that tests will always be run
+ // in debug mode, we catch the unwind and expect that it panicked. See the
+ // [`crate::utils::generics`] function for more information.
+ cov_mark::check!(ignore_gats);
+ std::panic::catch_unwind(|| {
+ check_no_mismatches(
+ r#"
+trait Collection {
+ type Item;
+ type Member<T>: Collection<Item = T>;
+ fn add(&mut self, value: Self::Item) -> Result<(), Self::Error>;
+}
+struct ConstGen<T, const N: usize> {
+ data: [T; N],
+}
+impl<T, const N: usize> Collection for ConstGen<T, N> {
+ type Item = T;
+ type Member<U> = ConstGen<U, N>;
+}
+ "#,
+ );
+ })
+ .expect_err("must panic");
+}
+
#[test]
fn cfgd_out_self_param() {
cov_mark::check!(cfgd_out_self_param);
let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def)));
if parent_generics.is_some() && matches!(def, GenericDefId::TypeAliasId(_)) {
let params = db.generic_params(def);
+ let parent_params = &parent_generics.as_ref().unwrap().params;
let has_consts =
params.iter().any(|(_, x)| matches!(x, TypeOrConstParamData::ConstParamData(_)));
- return if has_consts {
- // XXX: treat const generic associated types as not existing to avoid crashes (#11769)
+ let parent_has_consts =
+ parent_params.iter().any(|(_, x)| matches!(x, TypeOrConstParamData::ConstParamData(_)));
+ return if has_consts || parent_has_consts {
+ // XXX: treat const generic associated types as not existing to avoid crashes
+ // (#11769)
+ //
+ // Note: Also crashes when the parent has const generics (also even if the GAT
+ // doesn't use them), see `tests::regression::gat_crash_3` for an example.
+ // Avoids that by disabling GATs when the parent (i.e. `impl` block) has
+ // const generics (#12193).
//
// Chalk expects the inner associated type's parameters to come
// *before*, not after the trait's generics as we've always done it.
fn find_param(&self, param: TypeOrConstParamId) -> Option<(usize, &TypeOrConstParamData)> {
if param.parent == self.def {
- let (idx, (_local_id, data)) = self
- .params
- .iter()
- .enumerate()
- .find(|(_, (idx, _))| *idx == param.local_id)
- .unwrap();
+ let (idx, (_local_id, data)) =
+ self.params.iter().enumerate().find(|(_, (idx, _))| *idx == param.local_id)?;
let parent_len = self.parent_generics().map_or(0, Generics::len);
Some((parent_len + idx, data))
} else {
.collect()
}
+ /// Fills `acc` with the module's diagnostics.
pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
let _p = profile::span("Module::diagnostics").detail(|| {
format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string()))
m.diagnostics(db, acc)
}
}
+ ModuleDef::Trait(t) => {
+ for diag in db.trait_data_with_diagnostics(t.id).1.iter() {
+ emit_def_diagnostic(db, acc, diag);
+ }
+ acc.extend(decl.diagnostics(db))
+ }
_ => acc.extend(decl.diagnostics(db)),
}
}
for impl_def in self.impl_defs(db) {
+ for diag in db.impl_data_with_diagnostics(impl_def.id).1.iter() {
+ emit_def_diagnostic(db, acc, diag);
+ }
+
for item in impl_def.items(db) {
let def: DefWithBody = match item {
AssocItem::Function(it) => it.into(),
ast::Type::RefType(ref_) => generics.extend(
ref_.lifetime().and_then(|lt| known_generics.iter().find(find_lifetime(<.text()))),
),
+ ast::Type::ArrayType(ar) => {
+ if let Some(expr) = ar.expr() {
+ if let ast::Expr::PathExpr(p) = expr {
+ if let Some(path) = p.path() {
+ if let Some(name_ref) = path.as_single_name_ref() {
+ if let Some(param) = known_generics.iter().find(|gp| {
+ if let ast::GenericParam::ConstParam(cp) = gp {
+ cp.name().map_or(false, |n| n.text() == name_ref.text())
+ } else {
+ false
+ }
+ }) {
+ generics.push(param);
+ }
+ }
+ }
+ }
+ }
+ }
_ => (),
});
// stable resort to lifetime, type, const
"#,
);
}
+
+ #[test]
+ fn issue_11197() {
+ check_assist(
+ extract_type_alias,
+ r#"
+struct Foo<T, const N: usize>
+where
+ [T; N]: Sized,
+{
+ arr: $0[T; N]$0,
+}
+ "#,
+ r#"
+type $0Type<T, const N: usize> = [T; N];
+
+struct Foo<T, const N: usize>
+where
+ [T; N]: Sized,
+{
+ arr: Type<T, N>,
+}
+ "#,
+ );
+ }
}
} else {
fn_body.clone_for_update()
};
- if let Some(t) = body.syntax().ancestors().find_map(ast::Impl::cast).and_then(|i| i.self_ty()) {
- body.syntax()
- .descendants_with_tokens()
- .filter_map(NodeOrToken::into_token)
- .filter(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW)
- .for_each(|tok| ted::replace(tok, t.syntax()));
+ if let Some(imp) = body.syntax().ancestors().find_map(ast::Impl::cast) {
+ if !node.syntax().ancestors().any(|anc| &anc == imp.syntax()) {
+ if let Some(t) = imp.self_ty() {
+ body.syntax()
+ .descendants_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .filter(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW)
+ .for_each(|tok| ted::replace(tok, t.syntax()));
+ }
+ }
}
let usages_for_locals = |local| {
Definition::Local(local)
fn main() {
A(114514);
}
+"#,
+ )
+ }
+
+ #[test]
+ fn inline_call_with_self_type_but_within_same_impl() {
+ check_assist(
+ inline_call,
+ r#"
+struct A(u32);
+impl A {
+ fn f() -> Self { Self(1919810) }
+ fn main() {
+ Self::f$0();
+ }
+}
+"#,
+ r#"
+struct A(u32);
+impl A {
+ fn f() -> Self { Self(1919810) }
+ fn main() {
+ Self(1919810);
+ }
+}
"#,
)
}
pub(super) qualified: Qualified,
/// The parent of the path we are completing.
pub(super) parent: Option<ast::Path>,
+ #[allow(dead_code)]
/// The path of which we are completing the segment
pub(super) path: ast::Path,
+ /// The path of which we are completing the segment in the original file
+ pub(crate) original_path: Option<ast::Path>,
pub(super) kind: PathKind,
/// Whether the path segment has type args or not.
pub(super) has_type_args: bool,
};
let path = segment.parent_path();
+ let original_path = find_node_in_file_compensated(sema, original_file, &path);
+
let mut path_ctx = PathCompletionCtx {
has_call_parens: false,
has_macro_bang: false,
qualified: Qualified::No,
parent: None,
path: path.clone(),
+ original_path,
kind: PathKind::Item { kind: ItemListKind::SourceFile },
has_type_args: false,
use_tree_parent: false,
..CompletionRelevance::default()
});
- if let Some(ref_match) = compute_ref_match(completion, &ty) {
- item.ref_match(ref_match, path_ctx.path.syntax().text_range().start());
- }
+ path_ref_match(completion, path_ctx, &ty, &mut item);
};
item
}
None
}
+fn path_ref_match(
+ completion: &CompletionContext<'_>,
+ path_ctx: &PathCompletionCtx,
+ ty: &hir::Type,
+ item: &mut Builder,
+) {
+ if let Some(original_path) = &path_ctx.original_path {
+ // At least one char was typed by the user already, in that case look for the original path
+ if let Some(original_path) = completion.sema.original_ast_node(original_path.clone()) {
+ if let Some(ref_match) = compute_ref_match(completion, ty) {
+ item.ref_match(ref_match, original_path.syntax().text_range().start());
+ }
+ }
+ } else {
+ // completion requested on an empty identifier, there is no path here yet.
+ // FIXME: This might create inconsistent completions where we show a ref match in macro inputs
+ // as long as nothing was typed yet
+ if let Some(ref_match) = compute_ref_match(completion, ty) {
+ item.ref_match(ref_match, completion.position.offset);
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use std::cmp;
..ctx.completion_relevance()
});
- if let Some(ref_match) = compute_ref_match(completion, &ret_type) {
- match func_kind {
- FuncKind::Function(path_ctx) => {
- item.ref_match(ref_match, path_ctx.path.syntax().text_range().start());
- }
- FuncKind::Method(DotAccess { receiver: Some(receiver), .. }, _) => {
- if let Some(original_expr) = completion.sema.original_ast_node(receiver.clone()) {
+ match func_kind {
+ FuncKind::Function(path_ctx) => {
+ super::path_ref_match(completion, path_ctx, &ret_type, &mut item);
+ }
+ FuncKind::Method(DotAccess { receiver: Some(receiver), .. }, _) => {
+ if let Some(original_expr) = completion.sema.original_ast_node(receiver.clone()) {
+ if let Some(ref_match) = compute_ref_match(completion, &ret_type) {
item.ref_match(ref_match, original_expr.syntax().text_range().start());
}
}
- _ => (),
}
+ _ => (),
}
item.set_documentation(ctx.docs(func))
use hir::{db::HirDatabase, Documentation, HasAttrs, StructKind};
use ide_db::SymbolKind;
-use syntax::AstNode;
use crate::{
context::{CompletionContext, PathCompletionCtx, PathKind},
item::{Builder, CompletionItem},
render::{
- compute_ref_match, compute_type_match,
+ compute_type_match,
variant::{
format_literal_label, format_literal_lookup, render_record_lit, render_tuple_lit,
visible_fields, RenderedLiteral,
type_match: compute_type_match(ctx.completion, &ty),
..ctx.completion_relevance()
});
- if let Some(ref_match) = compute_ref_match(completion, &ty) {
- item.ref_match(ref_match, path_ctx.path.syntax().text_range().start());
- }
+
+ super::path_ref_match(completion, path_ctx, &ty, &mut item);
if let Some(import_to_add) = ctx.import_to_add {
item.add_import(import_to_add);
db::{AstDatabase, DefDatabase, HirDatabase},
symbols::FileSymbolKind,
};
+use stdx::hash::NoHashHashSet;
use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase};
pub use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
FileLoaderDelegate(self).resolve_path(path)
}
- fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
+ fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
FileLoaderDelegate(self).relevant_crates(file_id)
}
}
//! representation.
use std::{iter, mem};
-use rustc_hash::FxHashMap;
+use stdx::hash::NoHashHashMap;
use syntax::{TextRange, TextSize};
#[derive(Clone, Debug, PartialEq, Eq)]
/// Offset the the beginning of each line, zero-based
pub(crate) newlines: Vec<TextSize>,
/// List of non-ASCII characters on each line
- pub(crate) utf16_lines: FxHashMap<u32, Vec<Utf16Char>>,
+ pub(crate) utf16_lines: NoHashHashMap<u32, Vec<Utf16Char>>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
impl LineIndex {
pub fn new(text: &str) -> LineIndex {
- let mut utf16_lines = FxHashMap::default();
+ let mut utf16_lines = NoHashHashMap::default();
let mut utf16_chars = Vec::new();
let mut newlines = vec![0.into()];
use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt};
use hir::{DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility};
use once_cell::unsync::Lazy;
-use rustc_hash::FxHashMap;
+use stdx::hash::NoHashHashMap;
use syntax::{ast, match_ast, AstNode, TextRange, TextSize};
use crate::{
#[derive(Debug, Default, Clone)]
pub struct UsageSearchResult {
- pub references: FxHashMap<FileId, Vec<FileReference>>,
+ pub references: NoHashHashMap<FileId, Vec<FileReference>>,
}
impl UsageSearchResult {
impl IntoIterator for UsageSearchResult {
type Item = (FileId, Vec<FileReference>);
- type IntoIter = <FxHashMap<FileId, Vec<FileReference>> as IntoIterator>::IntoIter;
+ type IntoIter = <NoHashHashMap<FileId, Vec<FileReference>> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.references.into_iter()
/// e.g. for things like local variables.
#[derive(Clone, Debug)]
pub struct SearchScope {
- entries: FxHashMap<FileId, Option<TextRange>>,
+ entries: NoHashHashMap<FileId, Option<TextRange>>,
}
impl SearchScope {
- fn new(entries: FxHashMap<FileId, Option<TextRange>>) -> SearchScope {
+ fn new(entries: NoHashHashMap<FileId, Option<TextRange>>) -> SearchScope {
SearchScope { entries }
}
/// Build a search scope spanning the entire crate graph of files.
fn crate_graph(db: &RootDatabase) -> SearchScope {
- let mut entries = FxHashMap::default();
+ let mut entries = NoHashHashMap::default();
let graph = db.crate_graph();
for krate in graph.iter() {
/// Build a search scope spanning all the reverse dependencies of the given crate.
fn reverse_dependencies(db: &RootDatabase, of: hir::Crate) -> SearchScope {
- let mut entries = FxHashMap::default();
+ let mut entries = NoHashHashMap::default();
for rev_dep in of.transitive_reverse_dependencies(db) {
let root_file = rev_dep.root_file(db);
let source_root_id = db.file_source_root(root_file);
let root_file = of.root_file(db);
let source_root_id = db.file_source_root(root_file);
let source_root = db.source_root(source_root_id);
- SearchScope {
- entries: source_root.iter().map(|id| (id, None)).collect::<FxHashMap<_, _>>(),
- }
+ SearchScope { entries: source_root.iter().map(|id| (id, None)).collect() }
}
/// Build a search scope spanning the given module and all its submodules.
fn module_and_children(db: &RootDatabase, module: hir::Module) -> SearchScope {
- let mut entries = FxHashMap::default();
+ let mut entries = NoHashHashMap::default();
let (file_id, range) = {
let InFile { file_id, value } = module.definition_source(db);
/// Build an empty search scope.
pub fn empty() -> SearchScope {
- SearchScope::new(FxHashMap::default())
+ SearchScope::new(NoHashHashMap::default())
}
/// Build a empty search scope spanning the given file.
use std::{collections::hash_map::Entry, iter, mem};
use base_db::{AnchoredPathBuf, FileId};
-use rustc_hash::FxHashMap;
-use stdx::never;
+use stdx::{hash::NoHashHashMap, never};
use syntax::{algo, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize};
use text_edit::{TextEdit, TextEditBuilder};
#[derive(Default, Debug, Clone)]
pub struct SourceChange {
- pub source_file_edits: FxHashMap<FileId, TextEdit>,
+ pub source_file_edits: NoHashHashMap<FileId, TextEdit>,
pub file_system_edits: Vec<FileSystemEdit>,
pub is_snippet: bool,
}
/// Creates a new SourceChange with the given label
/// from the edits.
pub fn from_edits(
- source_file_edits: FxHashMap<FileId, TextEdit>,
+ source_file_edits: NoHashHashMap<FileId, TextEdit>,
file_system_edits: Vec<FileSystemEdit>,
) -> Self {
SourceChange { source_file_edits, file_system_edits, is_snippet: false }
}
}
-impl From<FxHashMap<FileId, TextEdit>> for SourceChange {
- fn from(source_file_edits: FxHashMap<FileId, TextEdit>) -> SourceChange {
+impl From<NoHashHashMap<FileId, TextEdit>> for SourceChange {
+ fn from(source_file_edits: NoHashHashMap<FileId, TextEdit>) -> SourceChange {
SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false }
}
}
#[test]
fn inactive_assoc_item() {
- // FIXME these currently don't work, hence the *
check(
r#"
struct Foo;
impl Foo {
#[cfg(any())] pub fn f() {}
- //*************************** weak: code is inactive due to #[cfg] directives
+ //^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives
}
trait Bar {
#[cfg(any())] pub fn f() {}
- //*************************** weak: code is inactive due to #[cfg] directives
+ //^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives
}
"#,
);
syntax = { path = "../syntax", version = "0.0.0" }
ide-db = { path = "../ide-db", version = "0.0.0" }
hir = { path = "../hir", version = "0.0.0" }
+stdx = { path = "../stdx", version = "0.0.0" }
[dev-dependencies]
test-utils = { path = "../test-utils" }
use crate::{errors::bail, matching::MatchFailureReason};
use hir::Semantics;
-use ide_db::{
- base_db::{FileId, FilePosition, FileRange},
- FxHashMap,
-};
+use ide_db::base_db::{FileId, FilePosition, FileRange};
use resolving::ResolvedRule;
+use stdx::hash::NoHashHashMap;
use syntax::{ast, AstNode, SyntaxNode, TextRange};
use text_edit::TextEdit;
}
/// Finds matches for all added rules and returns edits for all found matches.
- pub fn edits(&self) -> FxHashMap<FileId, TextEdit> {
+ pub fn edits(&self) -> NoHashHashMap<FileId, TextEdit> {
use ide_db::base_db::SourceDatabaseExt;
- let mut matches_by_file = FxHashMap::default();
+ let mut matches_by_file = NoHashHashMap::default();
for m in self.matches().matches {
matches_by_file
.entry(m.range.file_id)
Definition::TypeAlias(it) => it.resolve_doc_path(db, link, ns),
Definition::Macro(it) => it.resolve_doc_path(db, link, ns),
Definition::Field(it) => it.resolve_doc_path(db, link, ns),
+ Definition::SelfType(it) => it.resolve_doc_path(db, link, ns),
Definition::BuiltinAttr(_)
| Definition::ToolModule(_)
| Definition::BuiltinType(_)
- | Definition::SelfType(_)
| Definition::Local(_)
| Definition::GenericParam(_)
| Definition::Label(_)
},
join_lines::JoinLinesConfig,
markup::Markup,
- moniker::{MonikerKind, MonikerResult, PackageInformation},
+ moniker::{MonikerDescriptorKind, MonikerKind, MonikerResult, PackageInformation},
move_item::Direction,
navigation_target::NavigationTarget,
prime_caches::ParallelPrimeCachesProgress,
static_index::{StaticIndex, StaticIndexedFile, TokenId, TokenStaticData},
syntax_highlighting::{
tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag},
- HlRange,
+ HighlightConfig, HlRange,
},
};
pub use hir::{Documentation, Semantics};
}
/// Computes syntax highlighting for the given file
- pub fn highlight(&self, file_id: FileId) -> Cancellable<Vec<HlRange>> {
- self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false))
+ pub fn highlight(
+ &self,
+ highlight_config: HighlightConfig,
+ file_id: FileId,
+ ) -> Cancellable<Vec<HlRange>> {
+ self.with_db(|db| syntax_highlighting::highlight(db, highlight_config, file_id, None))
}
/// Computes all ranges to highlight for a given item in a file.
}
/// Computes syntax highlighting for the given file range.
- pub fn highlight_range(&self, frange: FileRange) -> Cancellable<Vec<HlRange>> {
+ pub fn highlight_range(
+ &self,
+ highlight_config: HighlightConfig,
+ frange: FileRange,
+ ) -> Cancellable<Vec<HlRange>> {
self.with_db(|db| {
- syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false)
+ syntax_highlighting::highlight(db, highlight_config, frange.file_id, Some(frange.range))
})
}
use crate::{doc_links::token_as_doc_comment, RangeInfo};
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum MonikerDescriptorKind {
+ Namespace,
+ Type,
+ Term,
+ Method,
+ TypeParameter,
+ Parameter,
+ Macro,
+ Meta,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct MonikerDescriptor {
+ pub name: Name,
+ pub desc: MonikerDescriptorKind,
+}
+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MonikerIdentifier {
- crate_name: String,
- path: Vec<Name>,
+ pub crate_name: String,
+ pub description: Vec<MonikerDescriptor>,
}
impl ToString for MonikerIdentifier {
fn to_string(&self) -> String {
match self {
- MonikerIdentifier { path, crate_name } => {
- format!("{}::{}", crate_name, path.iter().map(|x| x.to_string()).join("::"))
+ MonikerIdentifier { description, crate_name } => {
+ format!(
+ "{}::{}",
+ crate_name,
+ description.iter().map(|x| x.name.to_string()).join("::")
+ )
}
}
}
pub package_information: PackageInformation,
}
+impl MonikerResult {
+ pub fn from_def(db: &RootDatabase, def: Definition, from_crate: Crate) -> Option<Self> {
+ def_to_moniker(db, def, from_crate)
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PackageInformation {
pub name: String,
def: Definition,
from_crate: Crate,
) -> Option<MonikerResult> {
- if matches!(def, Definition::GenericParam(_) | Definition::SelfType(_) | Definition::Local(_)) {
+ if matches!(
+ def,
+ Definition::GenericParam(_)
+ | Definition::Label(_)
+ | Definition::DeriveHelper(_)
+ | Definition::BuiltinAttr(_)
+ | Definition::ToolModule(_)
+ ) {
return None;
}
+
let module = def.module(db)?;
let krate = module.krate();
- let mut path = vec![];
- path.extend(module.path_to_root(db).into_iter().filter_map(|x| x.name(db)));
+ let mut description = vec![];
+ description.extend(module.path_to_root(db).into_iter().filter_map(|x| {
+ Some(MonikerDescriptor { name: x.name(db)?, desc: MonikerDescriptorKind::Namespace })
+ }));
// Handle associated items within a trait
if let Some(assoc) = def.as_assoc_item(db) {
AssocItemContainer::Trait(trait_) => {
// Because different traits can have functions with the same name,
// we have to include the trait name as part of the moniker for uniqueness.
- path.push(trait_.name(db));
+ description.push(MonikerDescriptor {
+ name: trait_.name(db),
+ desc: MonikerDescriptorKind::Type,
+ });
}
AssocItemContainer::Impl(impl_) => {
// Because a struct can implement multiple traits, for implementations
// we add both the struct name and the trait name to the path
if let Some(adt) = impl_.self_ty(db).as_adt() {
- path.push(adt.name(db));
+ description.push(MonikerDescriptor {
+ name: adt.name(db),
+ desc: MonikerDescriptorKind::Type,
+ });
}
if let Some(trait_) = impl_.trait_(db) {
- path.push(trait_.name(db));
+ description.push(MonikerDescriptor {
+ name: trait_.name(db),
+ desc: MonikerDescriptorKind::Type,
+ });
}
}
}
}
if let Definition::Field(it) = def {
- path.push(it.parent_def(db).name(db));
+ description.push(MonikerDescriptor {
+ name: it.parent_def(db).name(db),
+ desc: MonikerDescriptorKind::Type,
+ });
}
- path.push(def.name(db)?);
+ let name_desc = match def {
+ // These are handled by top-level guard (for performance).
+ Definition::GenericParam(_)
+ | Definition::Label(_)
+ | Definition::DeriveHelper(_)
+ | Definition::BuiltinAttr(_)
+ | Definition::ToolModule(_) => return None,
+
+ Definition::Local(local) => {
+ if !local.is_param(db) {
+ return None;
+ }
+
+ MonikerDescriptor { name: local.name(db), desc: MonikerDescriptorKind::Parameter }
+ }
+ Definition::Macro(m) => {
+ MonikerDescriptor { name: m.name(db), desc: MonikerDescriptorKind::Macro }
+ }
+ Definition::Function(f) => {
+ MonikerDescriptor { name: f.name(db), desc: MonikerDescriptorKind::Method }
+ }
+ Definition::Variant(v) => {
+ MonikerDescriptor { name: v.name(db), desc: MonikerDescriptorKind::Type }
+ }
+ Definition::Const(c) => {
+ MonikerDescriptor { name: c.name(db)?, desc: MonikerDescriptorKind::Term }
+ }
+ Definition::Trait(trait_) => {
+ MonikerDescriptor { name: trait_.name(db), desc: MonikerDescriptorKind::Type }
+ }
+ Definition::TypeAlias(ta) => {
+ MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::TypeParameter }
+ }
+ Definition::Module(m) => {
+ MonikerDescriptor { name: m.name(db)?, desc: MonikerDescriptorKind::Namespace }
+ }
+ Definition::BuiltinType(b) => {
+ MonikerDescriptor { name: b.name(), desc: MonikerDescriptorKind::Type }
+ }
+ Definition::SelfType(imp) => MonikerDescriptor {
+ name: imp.self_ty(db).as_adt()?.name(db),
+ desc: MonikerDescriptorKind::Type,
+ },
+ Definition::Field(it) => {
+ MonikerDescriptor { name: it.name(db), desc: MonikerDescriptorKind::Term }
+ }
+ Definition::Adt(adt) => {
+ MonikerDescriptor { name: adt.name(db), desc: MonikerDescriptorKind::Type }
+ }
+ Definition::Static(s) => {
+ MonikerDescriptor { name: s.name(db), desc: MonikerDescriptorKind::Meta }
+ }
+ };
+
+ description.push(name_desc);
+
Some(MonikerResult {
identifier: MonikerIdentifier {
crate_name: krate.display_name(db)?.crate_name().to_string(),
- path,
+ description,
},
kind: if krate == from_crate { MonikerKind::Export } else { MonikerKind::Import },
package_information: {
salsa::{Database, ParallelDatabase, Snapshot},
Cancelled, CrateGraph, CrateId, SourceDatabase, SourceDatabaseExt,
},
- FxHashSet, FxIndexMap,
+ FxIndexMap,
};
+use stdx::hash::NoHashHashSet;
use crate::RootDatabase;
}
}
-fn compute_crates_to_prime(db: &RootDatabase, graph: &CrateGraph) -> FxHashSet<CrateId> {
+fn compute_crates_to_prime(db: &RootDatabase, graph: &CrateGraph) -> NoHashHashSet<CrateId> {
// We're only interested in the workspace crates and the `ImportMap`s of their direct
// dependencies, though in practice the latter also compute the `DefMap`s.
// We don't prime transitive dependencies because they're generally not visible in
base_db::FileId,
defs::{Definition, NameClass, NameRefClass},
search::{ReferenceCategory, SearchScope, UsageSearchResult},
- FxHashMap, RootDatabase,
+ RootDatabase,
};
+use stdx::hash::NoHashHashMap;
use syntax::{
algo::find_node_at_offset,
ast::{self, HasName},
#[derive(Debug, Clone)]
pub struct ReferenceSearchResult {
pub declaration: Option<Declaration>,
- pub references: FxHashMap<FileId, Vec<(TextRange, Option<ReferenceCategory>)>>,
+ pub references: NoHashHashMap<FileId, Vec<(TextRange, Option<ReferenceCategory>)>>,
}
#[derive(Debug, Clone)]
mod tests;
use hir::{Name, Semantics};
-use ide_db::{FxHashMap, RootDatabase};
+use ide_db::{FxHashMap, RootDatabase, SymbolKind};
use syntax::{
ast, AstNode, AstToken, NodeOrToken, SyntaxKind::*, SyntaxNode, TextRange, WalkEvent, T,
};
escape::highlight_escape_string, format::highlight_format_string, highlights::Highlights,
macro_::MacroHighlighter, tags::Highlight,
},
- FileId, HlMod, HlTag,
+ FileId, HlMod, HlOperator, HlPunct, HlTag,
};
pub(crate) use html::highlight_as_html;
pub binding_hash: Option<u64>,
}
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct HighlightConfig {
+ /// Whether to highlight strings
+ pub strings: bool,
+ /// Whether to highlight punctuation
+ pub punctuation: bool,
+ /// Whether to specialize punctuation highlights
+ pub specialize_punctuation: bool,
+ /// Whether to highlight operator
+ pub operator: bool,
+ /// Whether to specialize operator highlights
+ pub specialize_operator: bool,
+ /// Whether to inject highlights into doc comments
+ pub inject_doc_comment: bool,
+ /// Whether to highlight the macro call bang
+ pub macro_bang: bool,
+ /// Whether to highlight unresolved things be their syntax
+ pub syntactic_name_ref_highlighting: bool,
+}
+
// Feature: Semantic Syntax Highlighting
//
// rust-analyzer highlights the code semantically.
// image::https://user-images.githubusercontent.com/48062697/113187625-f7f50100-9250-11eb-825e-91c58f236071.png[]
pub(crate) fn highlight(
db: &RootDatabase,
+ config: HighlightConfig,
file_id: FileId,
range_to_highlight: Option<TextRange>,
- syntactic_name_ref_highlighting: bool,
) -> Vec<HlRange> {
let _p = profile::span("highlight");
let sema = Semantics::new(db);
Some(it) => it.krate(),
None => return hl.to_vec(),
};
- traverse(
- &mut hl,
- &sema,
- file_id,
- &root,
- krate,
- range_to_highlight,
- syntactic_name_ref_highlighting,
- );
+ traverse(&mut hl, &sema, config, file_id, &root, krate, range_to_highlight);
hl.to_vec()
}
fn traverse(
hl: &mut Highlights,
sema: &Semantics<'_, RootDatabase>,
+ config: HighlightConfig,
file_id: FileId,
root: &SyntaxNode,
krate: hir::Crate,
range_to_highlight: TextRange,
- syntactic_name_ref_highlighting: bool,
) {
let is_unlinked = sema.to_module_def(file_id).is_none();
let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();
Enter(it) => it,
Leave(NodeOrToken::Token(_)) => continue,
Leave(NodeOrToken::Node(node)) => {
- // Doc comment highlighting injection, we do this when leaving the node
- // so that we overwrite the highlighting of the doc comment itself.
- inject::doc_comment(hl, sema, file_id, &node);
+ if config.inject_doc_comment {
+ // Doc comment highlighting injection, we do this when leaving the node
+ // so that we overwrite the highlighting of the doc comment itself.
+ inject::doc_comment(hl, sema, config, file_id, &node);
+ }
continue;
}
};
let string_to_highlight = ast::String::cast(descended_token.clone());
if let Some((string, expanded_string)) = string.zip(string_to_highlight) {
if string.is_raw() {
- if inject::ra_fixture(hl, sema, &string, &expanded_string).is_some() {
+ if inject::ra_fixture(hl, sema, config, &string, &expanded_string).is_some()
+ {
continue;
}
}
sema,
krate,
&mut bindings_shadow_count,
- syntactic_name_ref_highlighting,
+ config.syntactic_name_ref_highlighting,
name_like,
),
NodeOrToken::Token(token) => highlight::token(sema, token).zip(Some(None)),
// something unresolvable. FIXME: There should be a way to prevent that
continue;
}
+
+ // apply config filtering
+ match &mut highlight.tag {
+ HlTag::StringLiteral if !config.strings => continue,
+ // If punctuation is disabled, make the macro bang part of the macro call again.
+ tag @ HlTag::Punctuation(HlPunct::MacroBang) => {
+ if !config.macro_bang {
+ *tag = HlTag::Symbol(SymbolKind::Macro);
+ } else if !config.specialize_punctuation {
+ *tag = HlTag::Punctuation(HlPunct::Other);
+ }
+ }
+ HlTag::Punctuation(_) if !config.punctuation => continue,
+ tag @ HlTag::Punctuation(_) if !config.specialize_punctuation => {
+ *tag = HlTag::Punctuation(HlPunct::Other);
+ }
+ HlTag::Operator(_) if !config.operator && highlight.mods.is_empty() => continue,
+ tag @ HlTag::Operator(_) if !config.specialize_operator => {
+ *tag = HlTag::Operator(HlOperator::Other);
+ }
+ _ => (),
+ }
+
if inside_attribute {
highlight |= HlMod::Attribute
}
use stdx::format_to;
use syntax::AstNode;
-use crate::{syntax_highlighting::highlight, FileId, RootDatabase};
+use crate::{
+ syntax_highlighting::{highlight, HighlightConfig},
+ FileId, RootDatabase,
+};
pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String {
let parse = db.parse(file_id);
)
}
- let hl_ranges = highlight(db, file_id, None, false);
+ let hl_ranges = highlight(
+ db,
+ HighlightConfig {
+ strings: true,
+ punctuation: true,
+ specialize_punctuation: true,
+ specialize_operator: true,
+ operator: true,
+ inject_doc_comment: true,
+ macro_bang: true,
+ syntactic_name_ref_highlighting: false,
+ },
+ file_id,
+ None,
+ );
let text = parse.tree().syntax().to_string();
let mut buf = String::new();
buf.push_str(STYLE);
use crate::{
doc_links::{doc_attributes, extract_definitions_from_docs, resolve_doc_path_for_def},
- syntax_highlighting::{highlights::Highlights, injector::Injector},
+ syntax_highlighting::{highlights::Highlights, injector::Injector, HighlightConfig},
Analysis, HlMod, HlRange, HlTag, RootDatabase,
};
pub(super) fn ra_fixture(
hl: &mut Highlights,
sema: &Semantics<'_, RootDatabase>,
+ config: HighlightConfig,
literal: &ast::String,
expanded: &ast::String,
) -> Option<()> {
let (analysis, tmp_file_id) = Analysis::from_single_file(inj.take_text());
- for mut hl_range in analysis.highlight(tmp_file_id).unwrap() {
+ for mut hl_range in analysis
+ .highlight(
+ HighlightConfig { syntactic_name_ref_highlighting: false, ..config },
+ tmp_file_id,
+ )
+ .unwrap()
+ {
for range in inj.map_range_up(hl_range.range) {
if let Some(range) = literal.map_range_up(range) {
hl_range.range = range;
pub(super) fn doc_comment(
hl: &mut Highlights,
sema: &Semantics<'_, RootDatabase>,
+ config: HighlightConfig,
src_file_id: FileId,
node: &SyntaxNode,
) {
let (analysis, tmp_file_id) = Analysis::from_single_file(inj.take_text());
- if let Ok(ranges) = analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)) {
+ if let Ok(ranges) = analysis.with_db(|db| {
+ super::highlight(
+ db,
+ HighlightConfig { syntactic_name_ref_highlighting: true, ..config },
+ tmp_file_id,
+ None,
+ )
+ }) {
for HlRange { range, highlight, binding_hash } in ranges {
for range in inj.map_range_up(range) {
hl.add(HlRange { range, highlight: highlight | HlMod::Injected, binding_hash });
}
impl HlMod {
- const ALL: &'static [HlMod; HlMod::Unsafe as u8 as usize + 1] = &[
+ const ALL: &'static [HlMod; 19] = &[
HlMod::Associated,
HlMod::Async,
HlMod::Attribute,
Highlight { tag, mods: HlMods::default() }
}
pub fn is_empty(&self) -> bool {
- self.tag == HlTag::None && self.mods == HlMods::default()
+ self.tag == HlTag::None && self.mods.is_empty()
}
}
}
impl HlMods {
+ pub fn is_empty(&self) -> bool {
+ self.0 == 0
+ }
+
pub fn contains(self, m: HlMod) -> bool {
self.0 & m.mask() == m.mask()
}
<span class="field declaration">bar</span><span class="colon">:</span> <span class="builtin_type">bool</span><span class="comma">,</span>
<span class="brace">}</span>
-<span class="comment documentation">/// This is an impl with a code block.</span>
+<span class="comment documentation">/// This is an impl of </span><span class="struct documentation injected intra_doc_link">[`Foo`]</span><span class="comment documentation"> with a code block.</span>
<span class="comment documentation">///</span>
<span class="comment documentation">/// ```</span>
<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">foo</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span>
use ide_db::SymbolKind;
use test_utils::{bench, bench_fixture, skip_slow_tests, AssertLinear};
-use crate::{fixture, FileRange, HlTag, TextRange};
+use crate::{fixture, FileRange, HighlightConfig, HlTag, TextRange};
+
+const HL_CONFIG: HighlightConfig = HighlightConfig {
+ strings: true,
+ punctuation: true,
+ specialize_punctuation: true,
+ specialize_operator: true,
+ operator: true,
+ inject_doc_comment: true,
+ macro_bang: true,
+ syntactic_name_ref_highlighting: false,
+};
#[test]
fn attributes() {
bar: bool,
}
-/// This is an impl with a code block.
+/// This is an impl of [`Foo`] with a code block.
///
/// ```
/// fn foo() {
// The "x"
let highlights = &analysis
- .highlight_range(FileRange { file_id, range: TextRange::at(45.into(), 1.into()) })
+ .highlight_range(
+ HL_CONFIG,
+ FileRange { file_id, range: TextRange::at(45.into(), 1.into()) },
+ )
.unwrap();
assert_eq!(&highlights[0].highlight.to_string(), "field.declaration.public");
}"#
.trim(),
);
- let _ = analysis.highlight(file_id).unwrap();
+ let _ = analysis.highlight(HL_CONFIG, file_id).unwrap();
}
/// Highlights the code given by the `ra_fixture` argument, renders the
let hash = {
let _pt = bench("syntax highlighting long struct");
analysis
- .highlight(file_id)
+ .highlight(HL_CONFIG, file_id)
.unwrap()
.iter()
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Struct))
let time = Instant::now();
let hash = analysis
- .highlight(file_id)
+ .highlight(HL_CONFIG, file_id)
.unwrap()
.iter()
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Struct))
let hash = {
let _pt = bench("syntax highlighting parser");
analysis
- .highlight(file_id)
+ .highlight(HL_CONFIG, file_id)
.unwrap()
.iter()
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
use dot::{Id, LabelText};
use ide_db::{
base_db::{CrateGraph, CrateId, Dependency, SourceDatabase, SourceDatabaseExt},
- FxHashSet, RootDatabase,
+ RootDatabase,
};
+use stdx::hash::NoHashHashSet;
// Feature: View Crate Graph
//
struct DotCrateGraph {
graph: Arc<CrateGraph>,
- crates_to_render: FxHashSet<CrateId>,
+ crates_to_render: NoHashHashSet<CrateId>,
}
type Edge<'a> = (CrateId, &'a Dependency);
T![.],
]));
+const PAT_TOP_FIRST: TokenSet = PATTERN_FIRST.union(TokenSet::new(&[T![|]]));
+
pub(crate) fn pattern(p: &mut Parser<'_>) {
pattern_r(p, PAT_RECOVERY_SET);
}
// let S(_) = ();
// let S(_,) = ();
// let S(_, .. , x) = ();
+// let S(| a) = ();
// }
fn tuple_pat_fields(p: &mut Parser<'_>) {
assert!(p.at(T!['(']));
// let (a,) = ();
// let (..) = ();
// let () = ();
+// let (| a | a, | b) = ((),());
// }
fn tuple_pat(p: &mut Parser<'_>) -> CompletedMarker {
assert!(p.at(T!['(']));
let mut has_rest = false;
while !p.at(EOF) && !p.at(T![')']) {
has_pat = true;
- if !p.at_ts(PATTERN_FIRST) {
+ if !p.at_ts(PAT_TOP_FIRST) {
p.error("expected a pattern");
break;
}
has_rest |= p.at(T![..]);
- pattern(p);
+ pattern_top(p);
if !p.at(T![')']) {
has_comma = true;
p.expect(T![,]);
// test slice_pat
// fn main() {
// let [a, b, ..] = [];
+// let [| a, ..] = [];
// }
fn slice_pat(p: &mut Parser<'_>) -> CompletedMarker {
assert!(p.at(T!['[']));
fn pat_list(p: &mut Parser<'_>, ket: SyntaxKind) {
while !p.at(EOF) && !p.at(ket) {
- if !p.at_ts(PATTERN_FIRST) {
+ if !p.at_ts(PAT_TOP_FIRST) {
p.error("expected a pattern");
break;
}
- pattern(p);
+ pattern_top(p);
if !p.at(ket) {
p.expect(T![,]);
}
L_BRACK "["
R_BRACK "]"
SEMICOLON ";"
+ WHITESPACE "\n "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ SLICE_PAT
+ L_BRACK "["
+ PIPE "|"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "a"
+ COMMA ","
+ WHITESPACE " "
+ REST_PAT
+ DOT2 ".."
+ R_BRACK "]"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ ARRAY_EXPR
+ L_BRACK "["
+ R_BRACK "]"
+ SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
fn main() {
let [a, b, ..] = [];
+ let [| a, ..] = [];
}
L_PAREN "("
R_PAREN ")"
SEMICOLON ";"
+ WHITESPACE "\n "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ TUPLE_STRUCT_PAT
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
+ L_PAREN "("
+ PIPE "|"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "a"
+ R_PAREN ")"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ TUPLE_EXPR
+ L_PAREN "("
+ R_PAREN ")"
+ SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
let S(_) = ();
let S(_,) = ();
let S(_, .. , x) = ();
+ let S(| a) = ();
}
L_PAREN "("
R_PAREN ")"
SEMICOLON ";"
+ WHITESPACE "\n "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ TUPLE_PAT
+ L_PAREN "("
+ PIPE "|"
+ WHITESPACE " "
+ OR_PAT
+ IDENT_PAT
+ NAME
+ IDENT "a"
+ WHITESPACE " "
+ PIPE "|"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "a"
+ COMMA ","
+ WHITESPACE " "
+ PIPE "|"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "b"
+ R_PAREN ")"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ TUPLE_EXPR
+ L_PAREN "("
+ TUPLE_EXPR
+ L_PAREN "("
+ R_PAREN ")"
+ COMMA ","
+ TUPLE_EXPR
+ L_PAREN "("
+ R_PAREN ")"
+ R_PAREN ")"
+ SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
let (a,) = ();
let (..) = ();
let () = ();
+ let (| a | a, | b) = ((),());
}
mbe = { path = "../mbe", version = "0.0.0" }
paths = { path = "../paths", version = "0.0.0" }
proc-macro-api = { path = "../proc-macro-api", version = "0.0.0" }
-crossbeam = "0.8.1"
[dev-dependencies]
expect-test = "1.4.0"
type Level = super::proc_macro::Level;
type LineColumn = super::proc_macro::LineColumn;
-/// A structure representing a diagnostic message and associated children
-/// messages.
-#[derive(Clone, Debug)]
-pub struct Diagnostic {
- level: Level,
- message: String,
- spans: Vec<Span>,
- children: Vec<Diagnostic>,
-}
-
-impl Diagnostic {
- /// Creates a new diagnostic with the given `level` and `message`.
- pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic {
- Diagnostic { level, message: message.into(), spans: vec![], children: vec![] }
- }
-}
-
pub struct FreeFunctions;
#[derive(Default)]
type FreeFunctions = FreeFunctions;
type TokenStream = TokenStream;
type SourceFile = SourceFile;
- type MultiSpan = Vec<Span>;
- type Diagnostic = Diagnostic;
type Span = Span;
type Symbol = Symbol;
}
span: tt::TokenId::unspecified(),
})
}
+
+ fn emit_diagnostic(&mut self, _: bridge::Diagnostic<Self::Span>) {
+ // FIXME handle diagnostic
+ }
}
impl server::TokenStream for RustAnalyzer {
}
}
-impl server::Diagnostic for RustAnalyzer {
- fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic {
- let mut diag = Diagnostic::new(level, msg);
- diag.spans = spans;
- diag
- }
-
- fn sub(
- &mut self,
- _diag: &mut Self::Diagnostic,
- _level: Level,
- _msg: &str,
- _spans: Self::MultiSpan,
- ) {
- // FIXME handle diagnostic
- //
- }
-
- fn emit(&mut self, _diag: Self::Diagnostic) {
- // FIXME handle diagnostic
- // diag.emit()
- }
-}
-
impl server::Span for RustAnalyzer {
fn debug(&mut self, span: Self::Span) -> String {
format!("{:?}", span.0)
}
}
-impl server::MultiSpan for RustAnalyzer {
- fn new(&mut self) -> Self::MultiSpan {
- // FIXME handle span
- vec![]
- }
-
- fn push(&mut self, other: &mut Self::MultiSpan, span: Self::Span) {
- //TODP
- other.push(span)
- }
-}
-
impl server::Symbol for RustAnalyzer {
fn normalize_and_validate_ident(&mut self, string: &str) -> Result<Self::Symbol, ()> {
// FIXME: nfc-normalize and validate idents
ffi::OsString,
fs,
path::{Path, PathBuf},
+ thread,
time::SystemTime,
};
let macro_body = task.macro_body.to_subtree();
let attributes = task.attributes.map(|it| it.to_subtree());
- // FIXME: replace this with std's scoped threads once they stabilize
- // (then remove dependency on crossbeam)
- let result = crossbeam::scope(|s| {
- let res = match s
- .builder()
+ let result = thread::scope(|s| {
+ let thread = thread::Builder::new()
.stack_size(EXPANDER_STACK_SIZE)
.name(task.macro_name.clone())
- .spawn(|_| {
+ .spawn_scoped(s, || {
expander
.expand(&task.macro_name, ¯o_body, attributes.as_ref())
.map(|it| FlatTree::new(&it))
- }) {
+ });
+ let res = match thread {
Ok(handle) => handle.join(),
Err(e) => std::panic::resume_unwind(Box::new(e)),
};
Err(e) => std::panic::resume_unwind(e),
}
});
- let result = match result {
- Ok(result) => result,
- Err(e) => std::panic::resume_unwind(e),
- };
prev_env.rollback();
is_proc_macro: false,
},
CrateId(
- 2,
+ 1,
): CrateData {
root_file_id: FileId(
- 3,
+ 2,
),
edition: Edition2018,
version: Some(
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
- "an_example",
+ "hello_world",
),
- canonical_name: "an-example",
+ canonical_name: "hello-world",
},
),
cfg_options: CfgOptions(
is_proc_macro: false,
},
CrateId(
- 4,
+ 2,
): CrateData {
root_file_id: FileId(
- 5,
+ 3,
),
- edition: Edition2015,
+ edition: Edition2018,
version: Some(
- "0.2.98",
+ "0.1.0",
),
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
- "libc",
+ "an_example",
),
- canonical_name: "libc",
+ canonical_name: "an-example",
},
),
cfg_options: CfgOptions(
[
"debug_assertions",
- "feature=default",
- "feature=std",
],
),
potential_cfg_options: CfgOptions(
[
"debug_assertions",
- "feature=align",
- "feature=const-extern-fn",
- "feature=default",
- "feature=extra_traits",
- "feature=rustc-dep-of-std",
- "feature=std",
- "feature=use_std",
],
),
env: Env {
entries: {
"CARGO_PKG_LICENSE": "",
"CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
- "CARGO_PKG_VERSION": "0.2.98",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
"CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "libc",
+ "CARGO_CRATE_NAME": "hello_world",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "libc",
- "CARGO_PKG_VERSION_PATCH": "98",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
"CARGO": "cargo",
"CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "2",
+ "CARGO_PKG_VERSION_MINOR": "1",
"CARGO_PKG_VERSION_PRE": "",
},
},
- dependencies: [],
+ dependencies: [
+ Dependency {
+ crate_id: CrateId(
+ 0,
+ ),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: CrateId(
+ 4,
+ ),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
proc_macro: Err(
"crate has not (yet) been built",
),
origin: CratesIo {
- repo: Some(
- "https://github.com/rust-lang/libc",
- ),
+ repo: None,
},
is_proc_macro: false,
},
CrateId(
- 1,
+ 3,
): CrateData {
root_file_id: FileId(
- 2,
+ 4,
),
edition: Edition2018,
version: Some(
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
- "hello_world",
+ "it",
),
- canonical_name: "hello-world",
+ canonical_name: "it",
},
),
cfg_options: CfgOptions(
is_proc_macro: false,
},
CrateId(
- 3,
+ 4,
): CrateData {
root_file_id: FileId(
- 4,
+ 5,
),
- edition: Edition2018,
+ edition: Edition2015,
version: Some(
- "0.1.0",
+ "0.2.98",
),
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
- "it",
+ "libc",
),
- canonical_name: "it",
+ canonical_name: "libc",
},
),
cfg_options: CfgOptions(
[
"debug_assertions",
+ "feature=default",
+ "feature=std",
],
),
potential_cfg_options: CfgOptions(
[
"debug_assertions",
+ "feature=align",
+ "feature=const-extern-fn",
+ "feature=default",
+ "feature=extra_traits",
+ "feature=rustc-dep-of-std",
+ "feature=std",
+ "feature=use_std",
],
),
env: Env {
entries: {
"CARGO_PKG_LICENSE": "",
"CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
+ "CARGO_PKG_VERSION": "0.2.98",
"CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_CRATE_NAME": "libc",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO_PKG_NAME": "libc",
+ "CARGO_PKG_VERSION_PATCH": "98",
"CARGO": "cargo",
"CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_MINOR": "2",
"CARGO_PKG_VERSION_PRE": "",
},
},
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
+ dependencies: [],
proc_macro: Err(
"crate has not (yet) been built",
),
origin: CratesIo {
- repo: None,
+ repo: Some(
+ "https://github.com/rust-lang/libc",
+ ),
},
is_proc_macro: false,
},
is_proc_macro: false,
},
CrateId(
- 2,
+ 1,
): CrateData {
root_file_id: FileId(
- 3,
+ 2,
),
edition: Edition2018,
version: Some(
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
- "an_example",
+ "hello_world",
),
- canonical_name: "an-example",
+ canonical_name: "hello-world",
},
),
cfg_options: CfgOptions(
is_proc_macro: false,
},
CrateId(
- 4,
- ): CrateData {
- root_file_id: FileId(
- 5,
- ),
- edition: Edition2015,
- version: Some(
- "0.2.98",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "libc",
- ),
- canonical_name: "libc",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "feature=default",
- "feature=std",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "feature=align",
- "feature=const-extern-fn",
- "feature=default",
- "feature=extra_traits",
- "feature=rustc-dep-of-std",
- "feature=std",
- "feature=use_std",
- ],
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
- "CARGO_PKG_VERSION": "0.2.98",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "libc",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "libc",
- "CARGO_PKG_VERSION_PATCH": "98",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "2",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: Some(
- "https://github.com/rust-lang/libc",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 1,
+ 2,
): CrateData {
root_file_id: FileId(
- 2,
+ 3,
),
edition: Edition2018,
version: Some(
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
- "hello_world",
+ "an_example",
),
- canonical_name: "hello-world",
+ canonical_name: "an-example",
},
),
cfg_options: CfgOptions(
},
is_proc_macro: false,
},
- },
- }"#]],
- )
-}
-
-#[test]
-fn cargo_hello_world_project_model() {
- let crate_graph = load_cargo("hello-world-metadata.json");
- check_crate_graph(
- crate_graph,
- expect![[r#"
- CrateGraph {
- arena: {
CrateId(
- 0,
+ 4,
): CrateData {
root_file_id: FileId(
- 1,
+ 5,
),
- edition: Edition2018,
+ edition: Edition2015,
version: Some(
- "0.1.0",
+ "0.2.98",
),
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
- "hello_world",
+ "libc",
),
- canonical_name: "hello-world",
+ canonical_name: "libc",
},
),
cfg_options: CfgOptions(
[
"debug_assertions",
- "test",
+ "feature=default",
+ "feature=std",
],
),
potential_cfg_options: CfgOptions(
[
"debug_assertions",
- "test",
+ "feature=align",
+ "feature=const-extern-fn",
+ "feature=default",
+ "feature=extra_traits",
+ "feature=rustc-dep-of-std",
+ "feature=std",
+ "feature=use_std",
],
),
env: Env {
entries: {
"CARGO_PKG_LICENSE": "",
"CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
+ "CARGO_PKG_VERSION": "0.2.98",
"CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_CRATE_NAME": "libc",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO_PKG_NAME": "libc",
+ "CARGO_PKG_VERSION_PATCH": "98",
"CARGO": "cargo",
"CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_MINOR": "2",
"CARGO_PKG_VERSION_PRE": "",
},
},
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
+ dependencies: [],
proc_macro: Err(
"crate has not (yet) been built",
),
origin: CratesIo {
- repo: None,
+ repo: Some(
+ "https://github.com/rust-lang/libc",
+ ),
},
is_proc_macro: false,
},
+ },
+ }"#]],
+ )
+}
+
+#[test]
+fn cargo_hello_world_project_model() {
+ let crate_graph = load_cargo("hello-world-metadata.json");
+ check_crate_graph(
+ crate_graph,
+ expect![[r#"
+ CrateGraph {
+ arena: {
CrateId(
- 2,
+ 0,
): CrateData {
root_file_id: FileId(
- 3,
+ 1,
),
edition: Edition2018,
version: Some(
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
- "an_example",
+ "hello_world",
),
- canonical_name: "an-example",
+ canonical_name: "hello-world",
},
),
cfg_options: CfgOptions(
},
},
dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
Dependency {
crate_id: CrateId(
4,
is_proc_macro: false,
},
CrateId(
- 4,
+ 1,
): CrateData {
root_file_id: FileId(
- 5,
+ 2,
),
- edition: Edition2015,
+ edition: Edition2018,
version: Some(
- "0.2.98",
+ "0.1.0",
),
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
- "libc",
+ "hello_world",
),
- canonical_name: "libc",
+ canonical_name: "hello-world",
},
),
cfg_options: CfgOptions(
[
"debug_assertions",
- "feature=default",
- "feature=std",
+ "test",
],
),
potential_cfg_options: CfgOptions(
[
"debug_assertions",
- "feature=align",
- "feature=const-extern-fn",
- "feature=default",
- "feature=extra_traits",
- "feature=rustc-dep-of-std",
- "feature=std",
- "feature=use_std",
+ "test",
],
),
env: Env {
entries: {
"CARGO_PKG_LICENSE": "",
"CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
- "CARGO_PKG_VERSION": "0.2.98",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
"CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "libc",
+ "CARGO_CRATE_NAME": "hello_world",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "libc",
- "CARGO_PKG_VERSION_PATCH": "98",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
"CARGO": "cargo",
"CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "2",
+ "CARGO_PKG_VERSION_MINOR": "1",
"CARGO_PKG_VERSION_PRE": "",
},
},
- dependencies: [],
+ dependencies: [
+ Dependency {
+ crate_id: CrateId(
+ 0,
+ ),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: CrateId(
+ 4,
+ ),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
proc_macro: Err(
"crate has not (yet) been built",
),
origin: CratesIo {
- repo: Some(
- "https://github.com/rust-lang/libc",
- ),
+ repo: None,
},
is_proc_macro: false,
},
CrateId(
- 1,
+ 2,
): CrateData {
root_file_id: FileId(
- 2,
+ 3,
),
edition: Edition2018,
version: Some(
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
- "hello_world",
+ "an_example",
),
- canonical_name: "hello-world",
+ canonical_name: "an-example",
},
),
cfg_options: CfgOptions(
},
is_proc_macro: false,
},
+ CrateId(
+ 4,
+ ): CrateData {
+ root_file_id: FileId(
+ 5,
+ ),
+ edition: Edition2015,
+ version: Some(
+ "0.2.98",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "libc",
+ ),
+ canonical_name: "libc",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "feature=default",
+ "feature=std",
+ ],
+ ),
+ potential_cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "feature=align",
+ "feature=const-extern-fn",
+ "feature=default",
+ "feature=extra_traits",
+ "feature=rustc-dep-of-std",
+ "feature=std",
+ "feature=use_std",
+ ],
+ ),
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
+ "CARGO_PKG_VERSION": "0.2.98",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "libc",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "libc",
+ "CARGO_PKG_VERSION_PATCH": "98",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "2",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [],
+ proc_macro: Err(
+ "crate has not (yet) been built",
+ ),
+ origin: CratesIo {
+ repo: Some(
+ "https://github.com/rust-lang/libc",
+ ),
+ },
+ is_proc_macro: false,
+ },
},
}"#]],
)
is_proc_macro: false,
},
CrateId(
- 10,
+ 1,
): CrateData {
root_file_id: FileId(
- 11,
+ 2,
),
edition: Edition2018,
version: None,
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
- "unwind",
+ "core",
),
- canonical_name: "unwind",
+ canonical_name: "core",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: CfgOptions(
+ [],
+ ),
+ env: Env {
+ entries: {},
+ },
+ dependencies: [],
+ proc_macro: Err(
+ "no proc macro loaded for sysroot crate",
+ ),
+ origin: Lang(
+ Core,
+ ),
+ is_proc_macro: false,
+ },
+ CrateId(
+ 2,
+ ): CrateData {
+ root_file_id: FileId(
+ 3,
+ ),
+ edition: Edition2018,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "panic_abort",
+ ),
+ canonical_name: "panic_abort",
},
),
cfg_options: CfgOptions(
is_proc_macro: false,
},
CrateId(
- 7,
+ 3,
): CrateData {
root_file_id: FileId(
- 8,
+ 4,
),
edition: Edition2018,
version: None,
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
- "std_detect",
+ "panic_unwind",
),
- canonical_name: "std_detect",
+ canonical_name: "panic_unwind",
},
),
cfg_options: CfgOptions(
is_proc_macro: false,
},
CrateId(
- 1,
+ 5,
): CrateData {
root_file_id: FileId(
- 2,
+ 6,
),
edition: Edition2018,
version: None,
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
- "core",
+ "profiler_builtins",
),
- canonical_name: "core",
+ canonical_name: "profiler_builtins",
},
),
cfg_options: CfgOptions(
"no proc macro loaded for sysroot crate",
),
origin: Lang(
- Core,
+ Other,
),
is_proc_macro: false,
},
CrateId(
- 11,
+ 6,
): CrateData {
root_file_id: FileId(
- 12,
+ 7,
),
edition: Edition2018,
version: None,
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
- "hello_world",
+ "std",
),
- canonical_name: "hello_world",
+ canonical_name: "std",
},
),
cfg_options: CfgOptions(
entries: {},
},
dependencies: [
+ Dependency {
+ crate_id: CrateId(
+ 0,
+ ),
+ name: CrateName(
+ "alloc",
+ ),
+ prelude: true,
+ },
Dependency {
crate_id: CrateId(
1,
},
Dependency {
crate_id: CrateId(
- 0,
+ 2,
),
name: CrateName(
- "alloc",
+ "panic_abort",
),
prelude: true,
},
Dependency {
crate_id: CrateId(
- 6,
+ 3,
),
name: CrateName(
- "std",
+ "panic_unwind",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: CrateId(
+ 5,
+ ),
+ name: CrateName(
+ "profiler_builtins",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: CrateId(
+ 7,
+ ),
+ name: CrateName(
+ "std_detect",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: CrateId(
+ 8,
+ ),
+ name: CrateName(
+ "term",
),
prelude: true,
},
name: CrateName(
"test",
),
- prelude: false,
+ prelude: true,
+ },
+ Dependency {
+ crate_id: CrateId(
+ 10,
+ ),
+ name: CrateName(
+ "unwind",
+ ),
+ prelude: true,
},
],
proc_macro: Err(
- "no proc macro dylib present",
+ "no proc macro loaded for sysroot crate",
+ ),
+ origin: Lang(
+ Std,
),
- origin: CratesIo {
- repo: None,
- },
is_proc_macro: false,
},
CrateId(
- 8,
+ 7,
): CrateData {
root_file_id: FileId(
- 9,
+ 8,
),
edition: Edition2018,
version: None,
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
- "term",
+ "std_detect",
),
- canonical_name: "term",
+ canonical_name: "std_detect",
},
),
cfg_options: CfgOptions(
is_proc_macro: false,
},
CrateId(
- 5,
+ 8,
): CrateData {
root_file_id: FileId(
- 6,
+ 9,
),
edition: Edition2018,
version: None,
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
- "profiler_builtins",
+ "term",
),
- canonical_name: "profiler_builtins",
+ canonical_name: "term",
},
),
cfg_options: CfgOptions(
is_proc_macro: false,
},
CrateId(
- 2,
+ 9,
): CrateData {
root_file_id: FileId(
- 3,
+ 10,
),
edition: Edition2018,
version: None,
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
- "panic_abort",
+ "test",
),
- canonical_name: "panic_abort",
+ canonical_name: "test",
},
),
cfg_options: CfgOptions(
"no proc macro loaded for sysroot crate",
),
origin: Lang(
- Other,
+ Test,
),
is_proc_macro: false,
},
CrateId(
- 9,
+ 10,
): CrateData {
root_file_id: FileId(
- 10,
+ 11,
),
edition: Edition2018,
version: None,
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
- "test",
+ "unwind",
),
- canonical_name: "test",
+ canonical_name: "unwind",
},
),
cfg_options: CfgOptions(
"no proc macro loaded for sysroot crate",
),
origin: Lang(
- Test,
+ Other,
),
is_proc_macro: false,
},
CrateId(
- 6,
+ 11,
): CrateData {
root_file_id: FileId(
- 7,
+ 12,
),
edition: Edition2018,
version: None,
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
- "std",
+ "hello_world",
),
- canonical_name: "std",
+ canonical_name: "hello_world",
},
),
cfg_options: CfgOptions(
entries: {},
},
dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "alloc",
- ),
- prelude: true,
- },
Dependency {
crate_id: CrateId(
1,
},
Dependency {
crate_id: CrateId(
- 2,
- ),
- name: CrateName(
- "panic_abort",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 3,
- ),
- name: CrateName(
- "panic_unwind",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 5,
- ),
- name: CrateName(
- "profiler_builtins",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 7,
+ 0,
),
name: CrateName(
- "std_detect",
+ "alloc",
),
prelude: true,
},
Dependency {
crate_id: CrateId(
- 8,
+ 6,
),
name: CrateName(
- "term",
+ "std",
),
prelude: true,
},
name: CrateName(
"test",
),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 10,
- ),
- name: CrateName(
- "unwind",
- ),
- prelude: true,
+ prelude: false,
},
],
proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Std,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 3,
- ): CrateData {
- root_file_id: FileId(
- 4,
- ),
- edition: Edition2018,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "panic_unwind",
- ),
- canonical_name: "panic_unwind",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
+ "no proc macro dylib present",
),
- env: Env {
- entries: {},
+ origin: CratesIo {
+ repo: None,
},
- dependencies: [],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Other,
- ),
is_proc_macro: false,
},
},
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::{FxHashMap, FxHashSet};
use semver::Version;
-use stdx::always;
+use stdx::{always, hash::NoHashHashMap};
use crate::{
build_scripts::BuildScriptOutput,
.map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
- let crates: FxHashMap<CrateId, CrateId> = project
+ let crates: NoHashHashMap<CrateId, CrateId> = project
.crates()
.filter_map(|(crate_id, krate)| {
let file_path = &krate.root_module;
crossbeam-channel = "0.5.5"
dissimilar = "1.0.4"
itertools = "0.10.3"
-lsp-types = { version = "0.93.0", features = ["proposed"] }
+scip = "0.1.1"
+lsp-types = { version = "0.93.1", features = ["proposed"] }
parking_lot = "0.12.1"
xflags = "0.2.4"
oorandom = "11.1.3"
"proc-macro-srv/sysroot-abi",
"sourcegen/in-rust-tree",
"ide/in-rust-tree",
- "syntax/in-rust-tree"
+ "syntax/in-rust-tree",
]
flags::RustAnalyzerCmd::Ssr(cmd) => cmd.run()?,
flags::RustAnalyzerCmd::Search(cmd) => cmd.run()?,
flags::RustAnalyzerCmd::Lsif(cmd) => cmd.run()?,
+ flags::RustAnalyzerCmd::Scip(cmd) => cmd.run()?,
}
Ok(())
}
mod diagnostics;
mod ssr;
mod lsif;
+mod scip;
mod progress_report;
cmd lsif
required path: PathBuf
{}
+
+ cmd scip
+ required path: PathBuf
+ {}
}
}
Search(Search),
ProcMacro(ProcMacro),
Lsif(Lsif),
+ Scip(Scip),
}
#[derive(Debug)]
pub path: PathBuf,
}
+#[derive(Debug)]
+pub struct Scip {
+ pub path: PathBuf,
+}
+
impl RustAnalyzer {
pub const HELP: &'static str = Self::HELP_;
--- /dev/null
+//! SCIP generator
+
+use std::{
+ collections::{HashMap, HashSet},
+ time::Instant,
+};
+
+use crate::line_index::{LineEndings, LineIndex, OffsetEncoding};
+use hir::Name;
+use ide::{
+ LineCol, MonikerDescriptorKind, MonikerResult, StaticIndex, StaticIndexedFile, TextRange,
+ TokenId,
+};
+use ide_db::LineIndexDatabase;
+use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace};
+use scip::types as scip_types;
+use std::env;
+
+use crate::cli::{
+ flags,
+ load_cargo::{load_workspace, LoadCargoConfig},
+ Result,
+};
+
+impl flags::Scip {
+ pub fn run(self) -> Result<()> {
+ eprintln!("Generating SCIP start...");
+ let now = Instant::now();
+ let cargo_config = CargoConfig::default();
+
+ let no_progress = &|s| (eprintln!("rust-analyzer: Loading {}", s));
+ let load_cargo_config = LoadCargoConfig {
+ load_out_dirs_from_check: true,
+ with_proc_macro: true,
+ prefill_caches: true,
+ };
+ let path = vfs::AbsPathBuf::assert(env::current_dir()?.join(&self.path));
+ let rootpath = path.normalize();
+ let manifest = ProjectManifest::discover_single(&path)?;
+
+ let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;
+
+ let (host, vfs, _) = load_workspace(workspace, &load_cargo_config)?;
+ let db = host.raw_database();
+ let analysis = host.analysis();
+
+ let si = StaticIndex::compute(&analysis);
+
+ let mut index = scip_types::Index {
+ metadata: Some(scip_types::Metadata {
+ version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(),
+ tool_info: Some(scip_types::ToolInfo {
+ name: "rust-analyzer".to_owned(),
+ version: "0.1".to_owned(),
+ arguments: vec![],
+ ..Default::default()
+ })
+ .into(),
+ project_root: format!(
+ "file://{}",
+ path.normalize()
+ .as_os_str()
+ .to_str()
+ .ok_or(anyhow::anyhow!("Unable to normalize project_root path"))?
+ .to_string()
+ ),
+ text_document_encoding: scip_types::TextEncoding::UTF8.into(),
+ ..Default::default()
+ })
+ .into(),
+ ..Default::default()
+ };
+
+ let mut symbols_emitted: HashSet<TokenId> = HashSet::default();
+ let mut tokens_to_symbol: HashMap<TokenId, String> = HashMap::new();
+
+ for file in si.files {
+ let mut local_count = 0;
+ let mut new_local_symbol = || {
+ let new_symbol = scip::types::Symbol::new_local(local_count);
+ local_count += 1;
+
+ new_symbol
+ };
+
+ let StaticIndexedFile { file_id, tokens, .. } = file;
+ let relative_path = match get_relative_filepath(&vfs, &rootpath, file_id) {
+ Some(relative_path) => relative_path,
+ None => continue,
+ };
+
+ let line_index = LineIndex {
+ index: db.line_index(file_id),
+ encoding: OffsetEncoding::Utf8,
+ endings: LineEndings::Unix,
+ };
+
+ let mut doc = scip_types::Document {
+ relative_path,
+ language: "rust".to_string(),
+ ..Default::default()
+ };
+
+ tokens.into_iter().for_each(|(range, id)| {
+ let token = si.tokens.get(id).unwrap();
+
+ let mut occurrence = scip_types::Occurrence::default();
+ occurrence.range = text_range_to_scip_range(&line_index, range);
+ occurrence.symbol = match tokens_to_symbol.get(&id) {
+ Some(symbol) => symbol.clone(),
+ None => {
+ let symbol = match &token.moniker {
+ Some(moniker) => moniker_to_symbol(&moniker),
+ None => new_local_symbol(),
+ };
+
+ let symbol = scip::symbol::format_symbol(symbol);
+ tokens_to_symbol.insert(id, symbol.clone());
+ symbol
+ }
+ };
+
+ if let Some(def) = token.definition {
+ if def.range == range {
+ occurrence.symbol_roles |= scip_types::SymbolRole::Definition as i32;
+ }
+
+ if !symbols_emitted.contains(&id) {
+ symbols_emitted.insert(id);
+
+ let mut symbol_info = scip_types::SymbolInformation::default();
+ symbol_info.symbol = occurrence.symbol.clone();
+ if let Some(hover) = &token.hover {
+ if !hover.markup.as_str().is_empty() {
+ symbol_info.documentation = vec![hover.markup.as_str().to_string()];
+ }
+ }
+
+ doc.symbols.push(symbol_info)
+ }
+ }
+
+ doc.occurrences.push(occurrence);
+ });
+
+ if doc.occurrences.is_empty() {
+ continue;
+ }
+
+ index.documents.push(doc);
+ }
+
+ scip::write_message_to_file("index.scip", index)
+ .map_err(|err| anyhow::anyhow!("Failed to write scip to file: {}", err))?;
+
+ eprintln!("Generating SCIP finished {:?}", now.elapsed());
+ Ok(())
+ }
+}
+
+fn get_relative_filepath(
+ vfs: &vfs::Vfs,
+ rootpath: &vfs::AbsPathBuf,
+ file_id: ide::FileId,
+) -> Option<String> {
+ Some(vfs.file_path(file_id).as_path()?.strip_prefix(&rootpath)?.as_ref().to_str()?.to_string())
+}
+
+// SCIP Ranges have a (very large) optimization that ranges if they are on the same line
+// only encode as a vector of [start_line, start_col, end_col].
+//
+// This transforms a line index into the optimized SCIP Range.
+fn text_range_to_scip_range(line_index: &LineIndex, range: TextRange) -> Vec<i32> {
+ let LineCol { line: start_line, col: start_col } = line_index.index.line_col(range.start());
+ let LineCol { line: end_line, col: end_col } = line_index.index.line_col(range.end());
+
+ if start_line == end_line {
+ vec![start_line as i32, start_col as i32, end_col as i32]
+ } else {
+ vec![start_line as i32, start_col as i32, end_line as i32, end_col as i32]
+ }
+}
+
+fn new_descriptor_str(
+ name: &str,
+ suffix: scip_types::descriptor::Suffix,
+) -> scip_types::Descriptor {
+ scip_types::Descriptor {
+ name: name.to_string(),
+ disambiguator: "".to_string(),
+ suffix: suffix.into(),
+ ..Default::default()
+ }
+}
+
+fn new_descriptor(name: Name, suffix: scip_types::descriptor::Suffix) -> scip_types::Descriptor {
+ let mut name = name.to_string();
+ if name.contains("'") {
+ name = format!("`{}`", name);
+ }
+
+ new_descriptor_str(name.as_str(), suffix)
+}
+
+/// Loosely based on `def_to_moniker`
+///
+/// Only returns a Symbol when it's a non-local symbol.
+/// So if the visibility isn't outside of a document, then it will return None
+fn moniker_to_symbol(moniker: &MonikerResult) -> scip_types::Symbol {
+ use scip_types::descriptor::Suffix::*;
+
+ let package_name = moniker.package_information.name.clone();
+ let version = moniker.package_information.version.clone();
+ let descriptors = moniker
+ .identifier
+ .description
+ .iter()
+ .map(|desc| {
+ new_descriptor(
+ desc.name.clone(),
+ match desc.desc {
+ MonikerDescriptorKind::Namespace => Namespace,
+ MonikerDescriptorKind::Type => Type,
+ MonikerDescriptorKind::Term => Term,
+ MonikerDescriptorKind::Method => Method,
+ MonikerDescriptorKind::TypeParameter => TypeParameter,
+ MonikerDescriptorKind::Parameter => Parameter,
+ MonikerDescriptorKind::Macro => Macro,
+ MonikerDescriptorKind::Meta => Meta,
+ },
+ )
+ })
+ .collect();
+
+ scip_types::Symbol {
+ scheme: "rust-analyzer".into(),
+ package: Some(scip_types::Package {
+ manager: "cargo".to_string(),
+ name: package_name,
+ version,
+ ..Default::default()
+ })
+ .into(),
+ descriptors,
+ ..Default::default()
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use hir::Semantics;
+ use ide::{AnalysisHost, FilePosition};
+ use ide_db::defs::IdentClass;
+ use ide_db::{base_db::fixture::ChangeFixture, helpers::pick_best_token};
+ use scip::symbol::format_symbol;
+ use syntax::SyntaxKind::*;
+ use syntax::{AstNode, T};
+
+ fn position(ra_fixture: &str) -> (AnalysisHost, FilePosition) {
+ let mut host = AnalysisHost::default();
+ let change_fixture = ChangeFixture::parse(ra_fixture);
+ host.raw_database_mut().apply_change(change_fixture.change);
+ let (file_id, range_or_offset) =
+ change_fixture.file_position.expect("expected a marker ($0)");
+ let offset = range_or_offset.expect_offset();
+ (host, FilePosition { file_id, offset })
+ }
+
+ /// If expected == "", then assert that there are no symbols (this is basically local symbol)
+ #[track_caller]
+ fn check_symbol(ra_fixture: &str, expected: &str) {
+ let (host, position) = position(ra_fixture);
+
+ let FilePosition { file_id, offset } = position;
+
+ let db = host.raw_database();
+ let sema = &Semantics::new(db);
+ let file = sema.parse(file_id).syntax().clone();
+ let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
+ IDENT
+ | INT_NUMBER
+ | LIFETIME_IDENT
+ | T![self]
+ | T![super]
+ | T![crate]
+ | T![Self]
+ | COMMENT => 2,
+ kind if kind.is_trivia() => 0,
+ _ => 1,
+ })
+ .expect("OK OK");
+
+ let navs = sema
+ .descend_into_macros(original_token.clone())
+ .into_iter()
+ .filter_map(|token| {
+ IdentClass::classify_token(sema, &token).map(IdentClass::definitions).map(|it| {
+ it.into_iter().flat_map(|def| {
+ let module = def.module(db).unwrap();
+ let current_crate = module.krate();
+
+ match MonikerResult::from_def(sema.db, def, current_crate) {
+ Some(moniker_result) => Some(moniker_to_symbol(&moniker_result)),
+ None => None,
+ }
+ })
+ })
+ })
+ .flatten()
+ .collect::<Vec<_>>();
+
+ if expected == "" {
+ assert_eq!(0, navs.len(), "must have no symbols {:?}", navs);
+ return;
+ }
+
+ assert_eq!(1, navs.len(), "must have one symbol {:?}", navs);
+
+ let res = navs.get(0).unwrap();
+ let formatted = format_symbol(res.clone());
+ assert_eq!(formatted, expected);
+ }
+
+ #[test]
+ fn basic() {
+ check_symbol(
+ r#"
+//- /lib.rs crate:main deps:foo
+use foo::example_mod::func;
+fn main() {
+ func$0();
+}
+//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
+pub mod example_mod {
+ pub fn func() {}
+}
+"#,
+ "rust-analyzer cargo foo 0.1.0 example_mod/func().",
+ );
+ }
+
+ #[test]
+ fn symbol_for_trait() {
+ check_symbol(
+ r#"
+//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
+pub mod module {
+ pub trait MyTrait {
+ pub fn func$0() {}
+ }
+}
+"#,
+ "rust-analyzer cargo foo 0.1.0 module/MyTrait#func().",
+ );
+ }
+
+ #[test]
+ fn symbol_for_trait_constant() {
+ check_symbol(
+ r#"
+ //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
+ pub mod module {
+ pub trait MyTrait {
+ const MY_CONST$0: u8;
+ }
+ }
+ "#,
+ "rust-analyzer cargo foo 0.1.0 module/MyTrait#MY_CONST.",
+ );
+ }
+
+ #[test]
+ fn symbol_for_trait_type() {
+ check_symbol(
+ r#"
+ //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
+ pub mod module {
+ pub trait MyTrait {
+ type MyType$0;
+ }
+ }
+ "#,
+ // "foo::module::MyTrait::MyType",
+ "rust-analyzer cargo foo 0.1.0 module/MyTrait#[MyType]",
+ );
+ }
+
+ #[test]
+ fn symbol_for_trait_impl_function() {
+ check_symbol(
+ r#"
+ //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
+ pub mod module {
+ pub trait MyTrait {
+ pub fn func() {}
+ }
+
+ struct MyStruct {}
+
+ impl MyTrait for MyStruct {
+ pub fn func$0() {}
+ }
+ }
+ "#,
+ // "foo::module::MyStruct::MyTrait::func",
+ "rust-analyzer cargo foo 0.1.0 module/MyStruct#MyTrait#func().",
+ );
+ }
+
+ #[test]
+ fn symbol_for_field() {
+ check_symbol(
+ r#"
+ //- /lib.rs crate:main deps:foo
+ use foo::St;
+ fn main() {
+ let x = St { a$0: 2 };
+ }
+ //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
+ pub struct St {
+ pub a: i32,
+ }
+ "#,
+ "rust-analyzer cargo foo 0.1.0 St#a.",
+ );
+ }
+
+ #[test]
+ fn local_symbol_for_local() {
+ check_symbol(
+ r#"
+ //- /lib.rs crate:main deps:foo
+ use foo::module::func;
+ fn main() {
+ func();
+ }
+ //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
+ pub mod module {
+ pub fn func() {
+ let x$0 = 2;
+ }
+ }
+ "#,
+ "",
+ );
+ }
+}
use flycheck::FlycheckConfig;
use ide::{
AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode,
- HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayHintsConfig, JoinLinesConfig,
- Snippet, SnippetScope,
+ HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayHintsConfig,
+ JoinLinesConfig, Snippet, SnippetScope,
};
use ide_db::{
imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
/// available on a nightly build.
rustfmt_rangeFormatting_enable: bool = "false",
+ /// Inject additional highlighting into doc comments.
+ ///
+ /// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra
+ /// doc links.
+ semanticHighlighting_doc_comment_inject_enable: bool = "true",
+ /// Use semantic tokens for operators.
+ ///
+ /// When disabled, rust-analyzer will emit semantic tokens only for operator tokens when
+ /// they are tagged with modifiers.
+ semanticHighlighting_operator_enable: bool = "true",
+ /// Use specialized semantic tokens for operators.
+ ///
+ /// When enabled, rust-analyzer will emit special token types for operator tokens instead
+ /// of the generic `operator` token type.
+ semanticHighlighting_operator_specialization_enable: bool = "false",
+ /// Use semantic tokens for punctuations.
+ ///
+ /// When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when
+ /// they are tagged with modifiers or have a special role.
+ semanticHighlighting_punctuation_enable: bool = "false",
+ /// When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro
+ /// calls.
+ semanticHighlighting_punctuation_separate_macro_bang: bool = "false",
+ /// Use specialized semantic tokens for punctuations.
+ ///
+ /// When enabled, rust-analyzer will emit special token types for punctuation tokens instead
+ /// of the generic `punctuation` token type.
+ semanticHighlighting_punctuation_specialization_enable: bool = "false",
/// Use semantic tokens for strings.
///
/// In some editors (e.g. vscode) semantic tokens override other highlighting grammars.
}
}
- pub fn highlighting_strings(&self) -> bool {
- self.data.semanticHighlighting_strings_enable
+ pub fn highlighting_config(&self) -> HighlightConfig {
+ HighlightConfig {
+ strings: self.data.semanticHighlighting_strings_enable,
+ punctuation: self.data.semanticHighlighting_punctuation_enable,
+ specialize_punctuation: self
+ .data
+ .semanticHighlighting_punctuation_specialization_enable,
+ macro_bang: self.data.semanticHighlighting_punctuation_separate_macro_bang,
+ operator: self.data.semanticHighlighting_operator_enable,
+ specialize_operator: self.data.semanticHighlighting_operator_specialization_enable,
+ inject_doc_comment: self.data.semanticHighlighting_doc_comment_inject_enable,
+ syntactic_name_ref_highlighting: false,
+ }
}
pub fn hover(&self) -> HoverConfig {
use std::{mem, sync::Arc};
use ide::FileId;
-use rustc_hash::{FxHashMap, FxHashSet};
+use ide_db::FxHashMap;
+use stdx::hash::{NoHashHashMap, NoHashHashSet};
use crate::lsp_ext;
-pub(crate) type CheckFixes = Arc<FxHashMap<usize, FxHashMap<FileId, Vec<Fix>>>>;
+pub(crate) type CheckFixes = Arc<NoHashHashMap<usize, NoHashHashMap<FileId, Vec<Fix>>>>;
#[derive(Debug, Default, Clone)]
pub struct DiagnosticsMapConfig {
#[derive(Debug, Default, Clone)]
pub(crate) struct DiagnosticCollection {
- // FIXME: should be FxHashMap<FileId, Vec<ra_id::Diagnostic>>
- pub(crate) native: FxHashMap<FileId, Vec<lsp_types::Diagnostic>>,
+ // FIXME: should be NoHashHashMap<FileId, Vec<ra_id::Diagnostic>>
+ pub(crate) native: NoHashHashMap<FileId, Vec<lsp_types::Diagnostic>>,
// FIXME: should be Vec<flycheck::Diagnostic>
- pub(crate) check: FxHashMap<usize, FxHashMap<FileId, Vec<lsp_types::Diagnostic>>>,
+ pub(crate) check: NoHashHashMap<usize, NoHashHashMap<FileId, Vec<lsp_types::Diagnostic>>>,
pub(crate) check_fixes: CheckFixes,
- changes: FxHashSet<FileId>,
+ changes: NoHashHashSet<FileId>,
}
#[derive(Debug, Clone)]
native.chain(check)
}
- pub(crate) fn take_changes(&mut self) -> Option<FxHashSet<FileId>> {
+ pub(crate) fn take_changes(&mut self) -> Option<NoHashHashSet<FileId>> {
if self.changes.is_empty() {
return None;
}
use proc_macro_api::ProcMacroServer;
use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
use rustc_hash::FxHashMap;
+use stdx::hash::NoHashHashMap;
use vfs::AnchoredPathBuf;
use crate::{
pub(crate) flycheck_sender: Sender<flycheck::Message>,
pub(crate) flycheck_receiver: Receiver<flycheck::Message>,
- pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
+ pub(crate) vfs: Arc<RwLock<(vfs::Vfs, NoHashHashMap<FileId, LineEndings>)>>,
pub(crate) vfs_config_version: u32,
pub(crate) vfs_progress_config_version: u32,
pub(crate) vfs_progress_n_total: usize,
pub(crate) check_fixes: CheckFixes,
mem_docs: MemDocs,
pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
- vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
+ vfs: Arc<RwLock<(vfs::Vfs, NoHashHashMap<FileId, LineEndings>)>>,
pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
}
flycheck_sender,
flycheck_receiver,
- vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
+ vfs: Arc::new(RwLock::new((vfs::Vfs::default(), NoHashHashMap::default()))),
vfs_config_version: 0,
vfs_progress_config_version: 0,
vfs_progress_n_total: 0,
let text = snap.analysis.file_text(file_id)?;
let line_index = snap.file_line_index(file_id)?;
- let highlights = snap.analysis.highlight(file_id)?;
- let highlight_strings = snap.config.highlighting_strings();
- let semantic_tokens =
- to_proto::semantic_tokens(&text, &line_index, highlights, highlight_strings);
+ let highlights = snap.analysis.highlight(snap.config.highlighting_config(), file_id)?;
+ let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
// Unconditionally cache the tokens
snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens.clone());
let text = snap.analysis.file_text(file_id)?;
let line_index = snap.file_line_index(file_id)?;
- let highlights = snap.analysis.highlight(file_id)?;
- let highlight_strings = snap.config.highlighting_strings();
- let semantic_tokens =
- to_proto::semantic_tokens(&text, &line_index, highlights, highlight_strings);
+ let highlights = snap.analysis.highlight(snap.config.highlighting_config(), file_id)?;
+ let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
let mut cache = snap.semantic_tokens_cache.lock();
let cached_tokens = cache.entry(params.text_document.uri).or_default();
let text = snap.analysis.file_text(frange.file_id)?;
let line_index = snap.file_line_index(frange.file_id)?;
- let highlights = snap.analysis.highlight_range(frange)?;
- let highlight_strings = snap.config.highlighting_strings();
- let semantic_tokens =
- to_proto::semantic_tokens(&text, &line_index, highlights, highlight_strings);
+ let highlights = snap.analysis.highlight_range(snap.config.highlighting_config(), frange)?;
+ let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
Ok(Some(semantic_tokens.into()))
}
}
let uri = file_id_to_url(&self.vfs.read().0, file_id);
- let diagnostics =
+ let mut diagnostics =
self.diagnostics.diagnostics_for(file_id).cloned().collect::<Vec<_>>();
+
+ // VSCode assumes diagnostic messages to be non-empty strings, so we need to patch
+ // empty diagnostics. Neither the docs of VSCode nor the LSP spec say whether
+ // diagnostic messages are actually allowed to be empty or not and patching this
+ // in the VSCode client does not work as the assertion happens in the protocol
+ // conversion. So this hack is here to stay, and will be considered a hack
+ // until the LSP decides to state that empty messages are allowed.
+
+ // See https://github.com/rust-lang/rust-analyzer/issues/11404
+ // See https://github.com/rust-lang/rust-analyzer/issues/13130
+ let patch_empty = |message: &mut String| {
+ if message.is_empty() {
+ *message = " ".to_string();
+ }
+ };
+
+ for d in &mut diagnostics {
+ patch_empty(&mut d.message);
+ if let Some(dri) = &mut d.related_information {
+ for dri in dri {
+ patch_empty(&mut dri.message);
+ }
+ }
+ }
+
let version = from_proto::vfs_path(&uri)
.map(|path| self.mem_docs.get(&path).map(|it| it.version))
.unwrap_or_default();
}
flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)),
flycheck::Progress::DidCancel => (Progress::End, None),
+ flycheck::Progress::DidFailToRestart(err) => {
+ self.show_and_log_error(
+ "cargo check failed".to_string(),
+ Some(err.to_string()),
+ );
+ return;
+ }
flycheck::Progress::DidFinish(result) => {
if let Err(err) = result {
self.show_and_log_error(
};
macro_rules! define_semantic_token_types {
- ($(($ident:ident, $string:literal)),*$(,)?) => {
- $(pub(crate) const $ident: SemanticTokenType = SemanticTokenType::new($string);)*
+ (
+ standard {
+ $($standard:ident),*$(,)?
+ }
+ custom {
+ $(($custom:ident, $string:literal)),*$(,)?
+ }
+
+ ) => {
+ $(pub(crate) const $standard: SemanticTokenType = SemanticTokenType::$standard;)*
+ $(pub(crate) const $custom: SemanticTokenType = SemanticTokenType::new($string);)*
pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[
- SemanticTokenType::COMMENT,
- SemanticTokenType::KEYWORD,
- SemanticTokenType::STRING,
- SemanticTokenType::NUMBER,
- SemanticTokenType::REGEXP,
- SemanticTokenType::OPERATOR,
- SemanticTokenType::NAMESPACE,
- SemanticTokenType::TYPE,
- SemanticTokenType::STRUCT,
- SemanticTokenType::CLASS,
- SemanticTokenType::INTERFACE,
- SemanticTokenType::ENUM,
- SemanticTokenType::ENUM_MEMBER,
- SemanticTokenType::TYPE_PARAMETER,
- SemanticTokenType::FUNCTION,
- SemanticTokenType::METHOD,
- SemanticTokenType::PROPERTY,
- SemanticTokenType::MACRO,
- SemanticTokenType::VARIABLE,
- SemanticTokenType::PARAMETER,
- $($ident),*
+ $(SemanticTokenType::$standard,)*
+ $($custom),*
];
};
}
define_semantic_token_types![
- (ANGLE, "angle"),
- (ARITHMETIC, "arithmetic"),
- (ATTRIBUTE, "attribute"),
- (ATTRIBUTE_BRACKET, "attributeBracket"),
- (BITWISE, "bitwise"),
- (BOOLEAN, "boolean"),
- (BRACE, "brace"),
- (BRACKET, "bracket"),
- (BUILTIN_ATTRIBUTE, "builtinAttribute"),
- (BUILTIN_TYPE, "builtinType"),
- (CHAR, "character"),
- (COLON, "colon"),
- (COMMA, "comma"),
- (COMPARISON, "comparison"),
- (CONST_PARAMETER, "constParameter"),
- (DERIVE, "derive"),
- (DERIVE_HELPER, "deriveHelper"),
- (DOT, "dot"),
- (ESCAPE_SEQUENCE, "escapeSequence"),
- (FORMAT_SPECIFIER, "formatSpecifier"),
- (GENERIC, "generic"),
- (LABEL, "label"),
- (LIFETIME, "lifetime"),
- (LOGICAL, "logical"),
- (MACRO_BANG, "macroBang"),
- (OPERATOR, "operator"),
- (PARENTHESIS, "parenthesis"),
- (PUNCTUATION, "punctuation"),
- (SELF_KEYWORD, "selfKeyword"),
- (SELF_TYPE_KEYWORD, "selfTypeKeyword"),
- (SEMICOLON, "semicolon"),
- (TYPE_ALIAS, "typeAlias"),
- (TOOL_MODULE, "toolModule"),
- (UNION, "union"),
- (UNRESOLVED_REFERENCE, "unresolvedReference"),
+ standard {
+ COMMENT,
+ DECORATOR,
+ ENUM_MEMBER,
+ ENUM,
+ FUNCTION,
+ INTERFACE,
+ KEYWORD,
+ MACRO,
+ METHOD,
+ NAMESPACE,
+ NUMBER,
+ OPERATOR,
+ PARAMETER,
+ PROPERTY,
+ STRING,
+ STRUCT,
+ TYPE_PARAMETER,
+ VARIABLE,
+ }
+
+ custom {
+ (ANGLE, "angle"),
+ (ARITHMETIC, "arithmetic"),
+ (ATTRIBUTE, "attribute"),
+ (ATTRIBUTE_BRACKET, "attributeBracket"),
+ (BITWISE, "bitwise"),
+ (BOOLEAN, "boolean"),
+ (BRACE, "brace"),
+ (BRACKET, "bracket"),
+ (BUILTIN_ATTRIBUTE, "builtinAttribute"),
+ (BUILTIN_TYPE, "builtinType"),
+ (CHAR, "character"),
+ (COLON, "colon"),
+ (COMMA, "comma"),
+ (COMPARISON, "comparison"),
+ (CONST_PARAMETER, "constParameter"),
+ (DERIVE, "derive"),
+ (DERIVE_HELPER, "deriveHelper"),
+ (DOT, "dot"),
+ (ESCAPE_SEQUENCE, "escapeSequence"),
+ (FORMAT_SPECIFIER, "formatSpecifier"),
+ (GENERIC, "generic"),
+ (LABEL, "label"),
+ (LIFETIME, "lifetime"),
+ (LOGICAL, "logical"),
+ (MACRO_BANG, "macroBang"),
+ (PARENTHESIS, "parenthesis"),
+ (PUNCTUATION, "punctuation"),
+ (SELF_KEYWORD, "selfKeyword"),
+ (SELF_TYPE_KEYWORD, "selfTypeKeyword"),
+ (SEMICOLON, "semicolon"),
+ (TYPE_ALIAS, "typeAlias"),
+ (TOOL_MODULE, "toolModule"),
+ (UNION, "union"),
+ (UNRESOLVED_REFERENCE, "unresolvedReference"),
+ }
];
macro_rules! define_semantic_token_modifiers {
- ($(($ident:ident, $string:literal)),*$(,)?) => {
- $(pub(crate) const $ident: SemanticTokenModifier = SemanticTokenModifier::new($string);)*
+ (
+ standard {
+ $($standard:ident),*$(,)?
+ }
+ custom {
+ $(($custom:ident, $string:literal)),*$(,)?
+ }
+
+ ) => {
+
+ $(pub(crate) const $standard: SemanticTokenModifier = SemanticTokenModifier::$standard;)*
+ $(pub(crate) const $custom: SemanticTokenModifier = SemanticTokenModifier::new($string);)*
pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[
- SemanticTokenModifier::DOCUMENTATION,
- SemanticTokenModifier::DECLARATION,
- SemanticTokenModifier::DEFINITION,
- SemanticTokenModifier::STATIC,
- SemanticTokenModifier::ABSTRACT,
- SemanticTokenModifier::DEPRECATED,
- SemanticTokenModifier::READONLY,
- SemanticTokenModifier::DEFAULT_LIBRARY,
- $($ident),*
+ $(SemanticTokenModifier::$standard,)*
+ $($custom),*
];
};
}
define_semantic_token_modifiers![
- (ASYNC, "async"),
- (ATTRIBUTE_MODIFIER, "attribute"),
- (CALLABLE, "callable"),
- (CONSTANT, "constant"),
- (CONSUMING, "consuming"),
- (CONTROL_FLOW, "controlFlow"),
- (CRATE_ROOT, "crateRoot"),
- (INJECTED, "injected"),
- (INTRA_DOC_LINK, "intraDocLink"),
- (LIBRARY, "library"),
- (MUTABLE, "mutable"),
- (PUBLIC, "public"),
- (REFERENCE, "reference"),
- (TRAIT_MODIFIER, "trait"),
- (UNSAFE, "unsafe"),
+ standard {
+ DOCUMENTATION,
+ DECLARATION,
+ STATIC,
+ DEFAULT_LIBRARY,
+ }
+ custom {
+ (ASYNC, "async"),
+ (ATTRIBUTE_MODIFIER, "attribute"),
+ (CALLABLE, "callable"),
+ (CONSTANT, "constant"),
+ (CONSUMING, "consuming"),
+ (CONTROL_FLOW, "controlFlow"),
+ (CRATE_ROOT, "crateRoot"),
+ (INJECTED, "injected"),
+ (INTRA_DOC_LINK, "intraDocLink"),
+ (LIBRARY, "library"),
+ (MUTABLE, "mutable"),
+ (PUBLIC, "public"),
+ (REFERENCE, "reference"),
+ (TRAIT_MODIFIER, "trait"),
+ (UNSAFE, "unsafe"),
+ }
];
#[derive(Default)]
text: &str,
line_index: &LineIndex,
highlights: Vec<HlRange>,
- highlight_strings: bool,
) -> lsp_types::SemanticTokens {
let id = TOKEN_RESULT_COUNTER.fetch_add(1, Ordering::SeqCst).to_string();
let mut builder = semantic_tokens::SemanticTokensBuilder::new(id);
if highlight_range.highlight.is_empty() {
continue;
}
+
let (ty, mods) = semantic_token_type_and_modifiers(highlight_range.highlight);
- if !highlight_strings && ty == lsp_types::SemanticTokenType::STRING {
- continue;
- }
let token_index = semantic_tokens::type_index(ty);
let modifier_bitset = mods.0;
let mut mods = semantic_tokens::ModifierSet::default();
let type_ = match highlight.tag {
HlTag::Symbol(symbol) => match symbol {
- SymbolKind::Attribute => semantic_tokens::ATTRIBUTE,
+ SymbolKind::Attribute => semantic_tokens::DECORATOR,
SymbolKind::Derive => semantic_tokens::DERIVE,
SymbolKind::DeriveHelper => semantic_tokens::DERIVE_HELPER,
- SymbolKind::Module => lsp_types::SemanticTokenType::NAMESPACE,
+ SymbolKind::Module => semantic_tokens::NAMESPACE,
SymbolKind::Impl => semantic_tokens::TYPE_ALIAS,
- SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY,
- SymbolKind::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER,
+ SymbolKind::Field => semantic_tokens::PROPERTY,
+ SymbolKind::TypeParam => semantic_tokens::TYPE_PARAMETER,
SymbolKind::ConstParam => semantic_tokens::CONST_PARAMETER,
SymbolKind::LifetimeParam => semantic_tokens::LIFETIME,
SymbolKind::Label => semantic_tokens::LABEL,
- SymbolKind::ValueParam => lsp_types::SemanticTokenType::PARAMETER,
+ SymbolKind::ValueParam => semantic_tokens::PARAMETER,
SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD,
SymbolKind::SelfType => semantic_tokens::SELF_TYPE_KEYWORD,
- SymbolKind::Local => lsp_types::SemanticTokenType::VARIABLE,
+ SymbolKind::Local => semantic_tokens::VARIABLE,
SymbolKind::Function => {
if highlight.mods.contains(HlMod::Associated) {
- lsp_types::SemanticTokenType::METHOD
+ semantic_tokens::METHOD
} else {
- lsp_types::SemanticTokenType::FUNCTION
+ semantic_tokens::FUNCTION
}
}
SymbolKind::Const => {
mods |= semantic_tokens::CONSTANT;
- mods |= lsp_types::SemanticTokenModifier::STATIC;
- lsp_types::SemanticTokenType::VARIABLE
+ mods |= semantic_tokens::STATIC;
+ semantic_tokens::VARIABLE
}
SymbolKind::Static => {
- mods |= lsp_types::SemanticTokenModifier::STATIC;
- lsp_types::SemanticTokenType::VARIABLE
+ mods |= semantic_tokens::STATIC;
+ semantic_tokens::VARIABLE
}
- SymbolKind::Struct => lsp_types::SemanticTokenType::STRUCT,
- SymbolKind::Enum => lsp_types::SemanticTokenType::ENUM,
- SymbolKind::Variant => lsp_types::SemanticTokenType::ENUM_MEMBER,
+ SymbolKind::Struct => semantic_tokens::STRUCT,
+ SymbolKind::Enum => semantic_tokens::ENUM,
+ SymbolKind::Variant => semantic_tokens::ENUM_MEMBER,
SymbolKind::Union => semantic_tokens::UNION,
SymbolKind::TypeAlias => semantic_tokens::TYPE_ALIAS,
- SymbolKind::Trait => lsp_types::SemanticTokenType::INTERFACE,
- SymbolKind::Macro => lsp_types::SemanticTokenType::MACRO,
+ SymbolKind::Trait => semantic_tokens::INTERFACE,
+ SymbolKind::Macro => semantic_tokens::MACRO,
SymbolKind::BuiltinAttr => semantic_tokens::BUILTIN_ATTRIBUTE,
SymbolKind::ToolModule => semantic_tokens::TOOL_MODULE,
},
HlTag::AttributeBracket => semantic_tokens::ATTRIBUTE_BRACKET,
HlTag::BoolLiteral => semantic_tokens::BOOLEAN,
HlTag::BuiltinType => semantic_tokens::BUILTIN_TYPE,
- HlTag::ByteLiteral | HlTag::NumericLiteral => lsp_types::SemanticTokenType::NUMBER,
+ HlTag::ByteLiteral | HlTag::NumericLiteral => semantic_tokens::NUMBER,
HlTag::CharLiteral => semantic_tokens::CHAR,
- HlTag::Comment => lsp_types::SemanticTokenType::COMMENT,
+ HlTag::Comment => semantic_tokens::COMMENT,
HlTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE,
HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER,
- HlTag::Keyword => lsp_types::SemanticTokenType::KEYWORD,
+ HlTag::Keyword => semantic_tokens::KEYWORD,
HlTag::None => semantic_tokens::GENERIC,
HlTag::Operator(op) => match op {
HlOperator::Bitwise => semantic_tokens::BITWISE,
HlOperator::Comparison => semantic_tokens::COMPARISON,
HlOperator::Other => semantic_tokens::OPERATOR,
},
- HlTag::StringLiteral => lsp_types::SemanticTokenType::STRING,
+ HlTag::StringLiteral => semantic_tokens::STRING,
HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE,
HlTag::Punctuation(punct) => match punct {
HlPunct::Bracket => semantic_tokens::BRACKET,
HlMod::Consuming => semantic_tokens::CONSUMING,
HlMod::ControlFlow => semantic_tokens::CONTROL_FLOW,
HlMod::CrateRoot => semantic_tokens::CRATE_ROOT,
- HlMod::DefaultLibrary => lsp_types::SemanticTokenModifier::DEFAULT_LIBRARY,
- HlMod::Definition => lsp_types::SemanticTokenModifier::DECLARATION,
- HlMod::Documentation => lsp_types::SemanticTokenModifier::DOCUMENTATION,
+ HlMod::DefaultLibrary => semantic_tokens::DEFAULT_LIBRARY,
+ HlMod::Definition => semantic_tokens::DECLARATION,
+ HlMod::Documentation => semantic_tokens::DOCUMENTATION,
HlMod::Injected => semantic_tokens::INJECTED,
HlMod::IntraDocLink => semantic_tokens::INTRA_DOC_LINK,
HlMod::Library => semantic_tokens::LIBRARY,
HlMod::Mutable => semantic_tokens::MUTABLE,
HlMod::Public => semantic_tokens::PUBLIC,
HlMod::Reference => semantic_tokens::REFERENCE,
- HlMod::Static => lsp_types::SemanticTokenModifier::STATIC,
+ HlMod::Static => semantic_tokens::STATIC,
HlMod::Trait => semantic_tokens::TRAIT_MODIFIER,
HlMod::Unsafe => semantic_tokens::UNSAFE,
};
--- /dev/null
+//! A none hashing [`Hasher`] implementation.
+use std::{
+ hash::{BuildHasher, Hasher},
+ marker::PhantomData,
+};
+
+pub type NoHashHashMap<K, V> = std::collections::HashMap<K, V, NoHashHasherBuilder<K>>;
+pub type NoHashHashSet<K> = std::collections::HashSet<K, NoHashHasherBuilder<K>>;
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct NoHashHasherBuilder<T>(PhantomData<T>);
+
+impl<T> Default for NoHashHasherBuilder<T> {
+ fn default() -> Self {
+ Self(Default::default())
+ }
+}
+
+pub trait NoHashHashable {}
+impl NoHashHashable for usize {}
+impl NoHashHashable for u32 {}
+
+pub struct NoHashHasher(u64);
+
+impl<T: NoHashHashable> BuildHasher for NoHashHasherBuilder<T> {
+ type Hasher = NoHashHasher;
+ fn build_hasher(&self) -> Self::Hasher {
+ NoHashHasher(0)
+ }
+}
+
+impl Hasher for NoHashHasher {
+ fn finish(&self) -> u64 {
+ self.0
+ }
+
+ fn write(&mut self, _: &[u8]) {
+ unimplemented!("NoHashHasher should only be used for hashing primitive integers")
+ }
+
+ fn write_u8(&mut self, i: u8) {
+ self.0 = i as u64;
+ }
+
+ fn write_u16(&mut self, i: u16) {
+ self.0 = i as u64;
+ }
+
+ fn write_u32(&mut self, i: u32) {
+ self.0 = i as u64;
+ }
+
+ fn write_u64(&mut self, i: u64) {
+ self.0 = i as u64;
+ }
+
+ fn write_usize(&mut self, i: usize) {
+ self.0 = i as u64;
+ }
+
+ fn write_i8(&mut self, i: i8) {
+ self.0 = i as u64;
+ }
+
+ fn write_i16(&mut self, i: i16) {
+ self.0 = i as u64;
+ }
+
+ fn write_i32(&mut self, i: i32) {
+ self.0 = i as u64;
+ }
+
+ fn write_i64(&mut self, i: i64) {
+ self.0 = i as u64;
+ }
+
+ fn write_isize(&mut self, i: isize) {
+ self.0 = i as u64;
+ }
+}
use std::{io as sio, iter};
mod macros;
+pub mod hash;
pub mod process;
pub mod panic_context;
pub mod non_empty_vec;
[dependencies]
rustc-hash = "1.1.0"
fst = "0.4.7"
+indexmap = "1.9.1"
paths = { path = "../paths", version = "0.0.0" }
-indexmap = "1.9.1"
+stdx = { path = "../stdx", version = "0.0.0" }
use fst::{IntoStreamer, Streamer};
use rustc_hash::FxHashMap;
+use stdx::hash::NoHashHashMap;
use crate::{AnchoredPath, FileId, Vfs, VfsPath};
#[derive(Default, Clone, Eq, PartialEq)]
pub struct FileSet {
files: FxHashMap<VfsPath, FileId>,
- paths: FxHashMap<FileId, VfsPath>,
+ paths: NoHashHashMap<FileId, VfsPath>,
}
impl FileSet {
/// Handle to a file in [`Vfs`]
///
/// Most functions in rust-analyzer use this when they need to refer to a file.
-#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
+#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
pub struct FileId(pub u32);
+impl stdx::hash::NoHashHashable for FileId {}
+impl std::hash::Hash for FileId {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ self.0.hash(state);
+ }
+}
+
/// Storage for all files read by rust-analyzer.
///
/// For more information see the [crate-level](crate) documentation.
`textDocument/rangeFormatting` request. The rustfmt option is unstable and only
available on a nightly build.
--
+[[rust-analyzer.semanticHighlighting.doc.comment.inject.enable]]rust-analyzer.semanticHighlighting.doc.comment.inject.enable (default: `true`)::
++
+--
+Inject additional highlighting into doc comments.
+
+When enabled, rust-analyzer will highlight rust source in doc comments as well as intra
+doc links.
+--
+[[rust-analyzer.semanticHighlighting.operator.enable]]rust-analyzer.semanticHighlighting.operator.enable (default: `true`)::
++
+--
+Use semantic tokens for operators.
+
+When disabled, rust-analyzer will emit semantic tokens only for operator tokens when
+they are tagged with modifiers.
+--
+[[rust-analyzer.semanticHighlighting.operator.specialization.enable]]rust-analyzer.semanticHighlighting.operator.specialization.enable (default: `false`)::
++
+--
+Use specialized semantic tokens for operators.
+
+When enabled, rust-analyzer will emit special token types for operator tokens instead
+of the generic `operator` token type.
+--
+[[rust-analyzer.semanticHighlighting.punctuation.enable]]rust-analyzer.semanticHighlighting.punctuation.enable (default: `false`)::
++
+--
+Use semantic tokens for punctuations.
+
+When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when
+they are tagged with modifiers or have a special role.
+--
+[[rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang]]rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang (default: `false`)::
++
+--
+When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro
+calls.
+--
+[[rust-analyzer.semanticHighlighting.punctuation.specialization.enable]]rust-analyzer.semanticHighlighting.punctuation.specialization.enable (default: `false`)::
++
+--
+Use specialized semantic tokens for punctuations.
+
+When enabled, rust-analyzer will emit special token types for punctuation tokens instead
+of the generic `punctuation` token type.
+--
[[rust-analyzer.semanticHighlighting.strings.enable]]rust-analyzer.semanticHighlighting.strings.enable (default: `true`)::
+
--
"isBackground": true
}
```
+
+==== Live Share
+
+VS Code Live Share has partial support for rust-analyzer.
+
+Live Share _requires_ the official Microsoft build of VS Code, OSS builds will not work correctly.
+
+The host's rust-analyzer instance will be shared with all guests joining the session.
+The guests do not have to have the rust-analyzer extension installed for this to work.
+
+If you are joining a Live Share session and _do_ have rust-analyzer installed locally, commands from the command palette will not work correctly since they will attempt to communicate with the local server.
"default": false,
"type": "boolean"
},
+ "rust-analyzer.semanticHighlighting.doc.comment.inject.enable": {
+ "markdownDescription": "Inject additional highlighting into doc comments.\n\nWhen enabled, rust-analyzer will highlight rust source in doc comments as well as intra\ndoc links.",
+ "default": true,
+ "type": "boolean"
+ },
+ "rust-analyzer.semanticHighlighting.operator.enable": {
+ "markdownDescription": "Use semantic tokens for operators.\n\nWhen disabled, rust-analyzer will emit semantic tokens only for operator tokens when\nthey are tagged with modifiers.",
+ "default": true,
+ "type": "boolean"
+ },
+ "rust-analyzer.semanticHighlighting.operator.specialization.enable": {
+ "markdownDescription": "Use specialized semantic tokens for operators.\n\nWhen enabled, rust-analyzer will emit special token types for operator tokens instead\nof the generic `operator` token type.",
+ "default": false,
+ "type": "boolean"
+ },
+ "rust-analyzer.semanticHighlighting.punctuation.enable": {
+ "markdownDescription": "Use semantic tokens for punctuations.\n\nWhen disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when\nthey are tagged with modifiers or have a special role.",
+ "default": false,
+ "type": "boolean"
+ },
+ "rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang": {
+ "markdownDescription": "When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro\ncalls.",
+ "default": false,
+ "type": "boolean"
+ },
+ "rust-analyzer.semanticHighlighting.punctuation.specialization.enable": {
+ "markdownDescription": "Use specialized semantic tokens for punctuations.\n\nWhen enabled, rust-analyzer will emit special token types for punctuation tokens instead\nof the generic `punctuation` token type.",
+ "default": false,
+ "type": "boolean"
+ },
"rust-analyzer.semanticHighlighting.strings.enable": {
"markdownDescription": "Use semantic tokens for strings.\n\nIn some editors (e.g. vscode) semantic tokens override other highlighting grammars.\nBy disabling semantic tokens for strings, other grammars can be used to highlight\ntheir contents.",
"default": true,
import { assert } from "./util";
import { WorkspaceEdit } from "vscode";
import { Workspace } from "./ctx";
-import { updateConfig } from "./config";
import { substituteVariablesInEnv } from "./config";
import { outputChannel, traceOutputChannel } from "./main";
import { randomUUID } from "crypto";
let initializationOptions = vscode.workspace.getConfiguration("rust-analyzer");
- // Update outdated user configs
- await updateConfig(initializationOptions).catch((err) => {
- void vscode.window.showErrorMessage(`Failed updating old config keys: ${err.message}`);
- });
-
if (workspace.kind === "Detached Files") {
initializationOptions = {
detachedFiles: workspace.files.map((file) => file.uri.fsPath),
traceOutputChannel: traceOutputChannel(),
outputChannel: outputChannel(),
middleware: {
- async handleDiagnostics(uri, diagnostics, next) {
- // Workaround for https://github.com/microsoft/vscode/issues/155531
- for (const diagnostic of diagnostics) {
- if (!diagnostic.message) {
- diagnostic.message = " ";
- }
- if (diagnostic.relatedInformation) {
- for (const relatedInformation of diagnostic.relatedInformation) {
- if (!relatedInformation.message) {
- relatedInformation.message = " ";
- }
- }
- }
- }
- next(uri, diagnostics);
- },
async provideHover(
document: vscode.TextDocument,
position: vscode.Position,
import { Env } from "./client";
import { log } from "./util";
-export type UpdatesChannel = "stable" | "nightly";
-
export type RunnableEnvCfg =
| undefined
| Record<string, string>
}
}
-export async function updateConfig(config: vscode.WorkspaceConfiguration) {
- const renames = [
- ["assist.allowMergingIntoGlobImports", "imports.merge.glob"],
- ["assist.exprFillDefault", "assist.expressionFillDefault"],
- ["assist.importEnforceGranularity", "imports.granularity.enforce"],
- ["assist.importGranularity", "imports.granularity.group"],
- ["assist.importMergeBehavior", "imports.granularity.group"],
- ["assist.importMergeBehaviour", "imports.granularity.group"],
- ["assist.importGroup", "imports.group.enable"],
- ["assist.importPrefix", "imports.prefix"],
- ["primeCaches.enable", "cachePriming.enable"],
- ["cache.warmup", "cachePriming.enable"],
- ["cargo.loadOutDirsFromCheck", "cargo.buildScripts.enable"],
- ["cargo.runBuildScripts", "cargo.buildScripts.enable"],
- ["cargo.runBuildScriptsCommand", "cargo.buildScripts.overrideCommand"],
- ["cargo.useRustcWrapperForBuildScripts", "cargo.buildScripts.useRustcWrapper"],
- ["completion.snippets", "completion.snippets.custom"],
- ["diagnostics.enableExperimental", "diagnostics.experimental.enable"],
- ["experimental.procAttrMacros", "procMacro.attributes.enable"],
- ["highlighting.strings", "semanticHighlighting.strings.enable"],
- ["highlightRelated.breakPoints", "highlightRelated.breakPoints.enable"],
- ["highlightRelated.exitPoints", "highlightRelated.exitPoints.enable"],
- ["highlightRelated.yieldPoints", "highlightRelated.yieldPoints.enable"],
- ["highlightRelated.references", "highlightRelated.references.enable"],
- ["hover.documentation", "hover.documentation.enable"],
- ["hover.linksInHover", "hover.links.enable"],
- ["hoverActions.linksInHover", "hover.links.enable"],
- ["hoverActions.debug", "hover.actions.debug.enable"],
- ["hoverActions.enable", "hover.actions.enable.enable"],
- ["hoverActions.gotoTypeDef", "hover.actions.gotoTypeDef.enable"],
- ["hoverActions.implementations", "hover.actions.implementations.enable"],
- ["hoverActions.references", "hover.actions.references.enable"],
- ["hoverActions.run", "hover.actions.run.enable"],
- ["inlayHints.chainingHints", "inlayHints.chainingHints.enable"],
- ["inlayHints.closureReturnTypeHints", "inlayHints.closureReturnTypeHints.enable"],
- ["inlayHints.hideNamedConstructorHints", "inlayHints.typeHints.hideNamedConstructor"],
- ["inlayHints.parameterHints", "inlayHints.parameterHints.enable"],
- ["inlayHints.reborrowHints", "inlayHints.reborrowHints.enable"],
- ["inlayHints.typeHints", "inlayHints.typeHints.enable"],
- ["lruCapacity", "lru.capacity"],
- ["runnables.cargoExtraArgs", "runnables.extraArgs"],
- ["runnables.overrideCargo", "runnables.command"],
- ["rustcSource", "rustc.source"],
- ["rustfmt.enableRangeFormatting", "rustfmt.rangeFormatting.enable"],
- ];
-
- for (const [oldKey, newKey] of renames) {
- const inspect = config.inspect(oldKey);
- if (inspect !== undefined) {
- const valMatrix = [
- {
- val: inspect.globalValue,
- langVal: inspect.globalLanguageValue,
- target: vscode.ConfigurationTarget.Global,
- },
- {
- val: inspect.workspaceFolderValue,
- langVal: inspect.workspaceFolderLanguageValue,
- target: vscode.ConfigurationTarget.WorkspaceFolder,
- },
- {
- val: inspect.workspaceValue,
- langVal: inspect.workspaceLanguageValue,
- target: vscode.ConfigurationTarget.Workspace,
- },
- ];
- for (const { val, langVal, target } of valMatrix) {
- const patch = (val: unknown) => {
- // some of the updates we do only append "enable" or "custom"
- // that means on the next run we would find these again, but as objects with
- // these properties causing us to destroy the config
- // so filter those already updated ones out
- return (
- val !== undefined &&
- !(
- typeof val === "object" &&
- val !== null &&
- (oldKey === "completion.snippets" || !val.hasOwnProperty("custom"))
- )
- );
- };
- if (patch(val)) {
- await config.update(newKey, val, target, false);
- await config.update(oldKey, undefined, target, false);
- }
- if (patch(langVal)) {
- await config.update(newKey, langVal, target, true);
- await config.update(oldKey, undefined, target, true);
- }
- }
- }
- }
-}
-
export function substituteVariablesInEnv(env: Env): Env {
const missingDeps = new Set<string>();
// vscode uses `env:ENV_NAME` for env vars resolution, and it's easier
}
export interface RustAnalyzerExtensionApi {
- client: lc.LanguageClient;
+ client?: lc.LanguageClient;
}
export async function activate(
}
async function tryActivate(context: vscode.ExtensionContext): Promise<RustAnalyzerExtensionApi> {
+ // We only support local folders, not eg. Live Share (`vlsl:` scheme), so don't activate if
+ // only those are in use.
+ // (r-a still somewhat works with Live Share, because commands are tunneled to the host)
+ const folders = (vscode.workspace.workspaceFolders || []).filter(
+ (folder) => folder.uri.scheme === "file"
+ );
+ const rustDocuments = vscode.workspace.textDocuments.filter((document) =>
+ isRustDocument(document)
+ );
+
+ if (folders.length === 0 && rustDocuments.length === 0) {
+ // FIXME: Ideally we would choose not to activate at all (and avoid registering
+ // non-functional editor commands), but VS Code doesn't seem to have a good way of doing
+ // that
+ return {};
+ }
+
const config = new Config(context);
const state = new PersistentState(context.globalState);
const serverPath = await bootstrap(context, config, state).catch((err) => {
throw new Error(message);
});
- if ((vscode.workspace.workspaceFolders || []).length === 0) {
- const rustDocuments = vscode.workspace.textDocuments.filter((document) =>
- isRustDocument(document)
- );
- if (rustDocuments.length > 0) {
- ctx = await Ctx.create(config, context, serverPath, {
- kind: "Detached Files",
- files: rustDocuments,
- });
- } else {
- throw new Error("no rust files are opened");
- }
+ if (folders.length === 0) {
+ ctx = await Ctx.create(config, context, serverPath, {
+ kind: "Detached Files",
+ files: rustDocuments,
+ });
} else {
// Note: we try to start the server before we activate type hints so that it
// registers its `onDidChangeDocument` handler before us.
stream: TcpStream,
) -> (Sender<Message>, Receiver<Message>, IoThreads) {
let (reader_receiver, reader) = make_reader(stream.try_clone().unwrap());
- let (writer_sender, writer) = make_write(stream.try_clone().unwrap());
+ let (writer_sender, writer) = make_write(stream);
let io_threads = make_io_threads(reader, writer);
(writer_sender, reader_receiver, io_threads)
}
"time",
"tinystr",
"tinyvec",
+ "thin-vec",
"tracing",
"tracing-attributes",
"tracing-core",
"unicode-width",
"unicode-xid",
"vcpkg",
+ "valuable",
"version_check",
"wasi",
"winapi",