"rustc_expand",
"rustc_feature",
"rustc_hir",
+ "rustc_index",
"rustc_metadata",
"rustc_middle",
"rustc_session",
[[package]]
name = "unicode-script"
-version = "0.4.0"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b2c5c29e805da6817f5af6a627d65adb045cebf05cccd5a3493d6109454391c"
+checksum = "58b33414ea8db4b7ea0343548dbdc31d27aef06beacf7044a87e564d9b0feb7d"
[[package]]
name = "unicode-security"
-version = "0.0.3"
+version = "0.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5f9011bbed9c13372bc8df618b55a38138445199caf3b61d432c6859c36dee0"
+checksum = "5d87c28edc5b263377e448d6cdcb935c06b95413d8013ba6fae470558ccab18f"
dependencies = [
"unicode-normalization",
"unicode-script",
-Subproject commit 30cd9dfe71c446de63826bb4472627af45acc9db
+Subproject commit 4e7c00bece1544d409312ec93467beb62b5bd0cb
-Subproject commit 5555a97f04ad7974ac6fb8fb47c267c4274adf4a
+Subproject commit 616962ad0dd80f34d8b802da038d0aed9dd691bb
-Subproject commit 5d40ba5c2515caffa7790cda621239dc21ef5a72
+Subproject commit 04d5d5d7ba624b6f5016298451f3a63d557f3260
-Subproject commit 7aa82129aa23e7e181efbeb8da03a2a897ef6afc
+Subproject commit 6f94ccb48da6fa4ed0031290f21411cf789f7d5e
//! pub struct Foo;
//!
//! #[no_mangle]
+//! #[allow(improper_ctypes_definitions)]
//! pub extern "C" fn foo_new() -> Box<Foo> {
//! Box::new(Foo)
//! }
//!
//! #[no_mangle]
+//! #[allow(improper_ctypes_definitions)]
//! pub extern "C" fn foo_delete(_: Option<Box<Foo>>) {}
//! ```
//!
}
impl<K: Ord, V> BTreeMap<K, V> {
- /// Makes a new empty BTreeMap with a reasonable choice for B.
+ /// Makes a new empty BTreeMap.
+ ///
+ /// Does not allocate anything on its own.
///
/// # Examples
///
use super::*;
-use test;
-
#[bench]
#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks
fn bench_push_back_100(b: &mut test::Bencher) {
use core::array::LengthAtMost32;
use core::cmp::{self, Ordering};
use core::fmt;
-use core::hash::{self, Hash};
+use core::hash::{Hash, Hasher};
use core::intrinsics::{arith_offset, assume};
use core::iter::{FromIterator, FusedIterator, TrustedLen};
use core::marker::PhantomData;
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: Hash> Hash for Vec<T> {
#[inline]
- fn hash<H: hash::Hasher>(&self, state: &mut H) {
+ fn hash<H: Hasher>(&self, state: &mut H) {
Hash::hash(&**self, state)
}
}
nzint_impl_from! { NonZeroU32, NonZeroI64, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
nzint_impl_from! { NonZeroU32, NonZeroI128, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
nzint_impl_from! { NonZeroU64, NonZeroI128, #[stable(feature = "nz_int_conv", since = "1.41.0")] }
+
+macro_rules! nzint_impl_try_from_int {
+ ($Int: ty, $NonZeroInt: ty, #[$attr:meta], $doc: expr) => {
+ #[$attr]
+ #[doc = $doc]
+ impl TryFrom<$Int> for $NonZeroInt {
+ type Error = TryFromIntError;
+
+ #[inline]
+ fn try_from(value: $Int) -> Result<Self, Self::Error> {
+ Self::new(value).ok_or(TryFromIntError(()))
+ }
+ }
+ };
+ ($Int: ty, $NonZeroInt: ty, #[$attr:meta]) => {
+ nzint_impl_try_from_int!($Int,
+ $NonZeroInt,
+ #[$attr],
+ concat!("Attempts to convert `",
+ stringify!($Int),
+ "` to `",
+ stringify!($NonZeroInt),
+ "`."));
+ }
+}
+
+// Int -> Non-zero Int
+nzint_impl_try_from_int! { u8, NonZeroU8, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
+nzint_impl_try_from_int! { u16, NonZeroU16, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
+nzint_impl_try_from_int! { u32, NonZeroU32, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
+nzint_impl_try_from_int! { u64, NonZeroU64, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
+nzint_impl_try_from_int! { u128, NonZeroU128, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
+nzint_impl_try_from_int! { usize, NonZeroUsize, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
+nzint_impl_try_from_int! { i8, NonZeroI8, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
+nzint_impl_try_from_int! { i16, NonZeroI16, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
+nzint_impl_try_from_int! { i32, NonZeroI32, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
+nzint_impl_try_from_int! { i64, NonZeroI64, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
+nzint_impl_try_from_int! { i128, NonZeroI128, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
+nzint_impl_try_from_int! { isize, NonZeroIsize, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] }
/// implements `Copy`.
///
/// If the actual type neither requires drop glue nor implements
- /// `Copy`, then may return `true` or `false`.
+ /// `Copy`, then the return value of this function is unspecified.
///
/// The stabilized version of this intrinsic is
/// [`std::mem::needs_drop`](../../std/mem/fn.needs_drop.html).
#[must_use = "closures are lazy and do nothing unless called"]
pub trait FnOnce<Args> {
/// The returned type after the call operator is used.
+ #[cfg_attr(not(bootstrap), lang = "fn_once_output")]
#[stable(feature = "fn_once_output", since = "1.12.0")]
type Output;
+use core::convert::TryFrom;
use core::num::{IntErrorKind, NonZeroI32, NonZeroI8, NonZeroU32, NonZeroU8};
use core::option::Option::{self, None, Some};
use std::mem::size_of;
target |= 0;
assert_eq!(target.get(), 0b1011_1111);
}
+
+#[test]
+fn test_nonzero_from_int_on_success() {
+ assert_eq!(NonZeroU8::try_from(5), Ok(NonZeroU8::new(5).unwrap()));
+ assert_eq!(NonZeroU32::try_from(5), Ok(NonZeroU32::new(5).unwrap()));
+
+ assert_eq!(NonZeroI8::try_from(-5), Ok(NonZeroI8::new(-5).unwrap()));
+ assert_eq!(NonZeroI32::try_from(-5), Ok(NonZeroI32::new(-5).unwrap()));
+}
+
+#[test]
+fn test_nonzero_from_int_on_err() {
+ assert!(NonZeroU8::try_from(0).is_err());
+ assert!(NonZeroU32::try_from(0).is_err());
+
+ assert!(NonZeroI8::try_from(0).is_err());
+ assert!(NonZeroI32::try_from(0).is_err());
+}
use core::any::Any;
#[rustc_std_internal_symbol]
+#[cfg_attr(not(bootstrap), allow(improper_ctypes_definitions))]
pub unsafe extern "C" fn __rust_panic_cleanup(_: *mut u8) -> *mut (dyn Any + Send + 'static) {
unreachable!()
}
mod dwarf;
#[rustc_std_internal_symbol]
+#[cfg_attr(not(bootstrap), allow(improper_ctypes_definitions))]
pub unsafe extern "C" fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static) {
Box::into_raw(imp::cleanup(payload))
}
hir::ItemKind::Const(ty, body_id)
}
ItemKind::Fn(_, FnSig { ref decl, header }, ref generics, ref body) => {
- let fn_def_id = self.resolver.definitions().local_def_id(id);
+ let fn_def_id = self.resolver.local_def_id(id);
self.with_new_scopes(|this| {
this.current_item = Some(ident.span);
self_ty: ref ty,
items: ref impl_items,
} => {
- let def_id = self.resolver.definitions().local_def_id(id);
+ let def_id = self.resolver.local_def_id(id);
// Lower the "impl header" first. This ordering is important
// for in-band lifetimes! Consider `'a` here:
}
fn lower_foreign_item(&mut self, i: &ForeignItem) -> hir::ForeignItem<'hir> {
- let def_id = self.resolver.definitions().local_def_id(i.id);
+ let def_id = self.resolver.local_def_id(i.id);
hir::ForeignItem {
hir_id: self.lower_node_id(i.id),
ident: i.ident,
}
fn lower_trait_item(&mut self, i: &AssocItem) -> hir::TraitItem<'hir> {
- let trait_item_def_id = self.resolver.definitions().local_def_id(i.id);
+ let trait_item_def_id = self.resolver.local_def_id(i.id);
let (generics, kind) = match i.kind {
AssocItemKind::Const(_, ref ty, ref default) => {
}
fn lower_impl_item(&mut self, i: &AssocItem) -> hir::ImplItem<'hir> {
- let impl_item_def_id = self.resolver.definitions().local_def_id(i.id);
+ let impl_item_def_id = self.resolver.local_def_id(i.id);
let (generics, kind) = match &i.kind {
AssocItemKind::Const(_, ty, expr) => {
if let Some(def_id) = def_id.as_local() {
for param in &generics.params {
if let GenericParamKind::Type { .. } = param.kind {
- if def_id
- == self
- .resolver
- .definitions()
- .local_def_id(param.id)
- {
+ if def_id == self.resolver.local_def_id(param.id) {
add_bounds
.entry(param.id)
.or_default()
use rustc_hir::definitions::{DefKey, DefPathData, Definitions};
use rustc_hir::intravisit;
use rustc_hir::{ConstArg, GenericArg, ParamName};
-use rustc_index::vec::IndexVec;
+use rustc_index::vec::{Idx, IndexVec};
use rustc_session::config::nightly_options;
use rustc_session::lint::{builtin::BARE_TRAIT_OBJECTS, BuiltinLintDiagnostics, LintBuffer};
use rustc_session::parse::ParseSess;
fn next_node_id(&mut self) -> NodeId;
fn trait_map(&self) -> &NodeMap<Vec<hir::TraitCandidate>>;
+
+ fn opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId>;
+
+ fn local_def_id(&self, node: NodeId) -> LocalDefId;
+
+ fn create_def(
+ &mut self,
+ parent: LocalDefId,
+ node_id: ast::NodeId,
+ data: DefPathData,
+ expn_id: ExpnId,
+ span: Span,
+ ) -> LocalDefId;
}
type NtToTokenstream = fn(&Nonterminal, &ParseSess, Span) -> TokenStream;
match tree.kind {
UseTreeKind::Simple(_, id1, id2) => {
for &id in &[id1, id2] {
- self.lctx.resolver.definitions().create_def_with_parent(
+ self.lctx.resolver.create_def(
owner,
id,
DefPathData::Misc,
| ItemKind::Enum(_, ref generics)
| ItemKind::TyAlias(_, ref generics, ..)
| ItemKind::Trait(_, _, ref generics, ..) => {
- let def_id = self.lctx.resolver.definitions().local_def_id(item.id);
+ let def_id = self.lctx.resolver.local_def_id(item.id);
let count = generics
.params
.iter()
.map(|(&k, v)| (self.node_id_to_hir_id[k].unwrap(), v.clone()))
.collect();
- self.resolver.definitions().init_node_id_to_hir_id_mapping(self.node_id_to_hir_id);
+ let mut def_id_to_hir_id = IndexVec::default();
+
+ for (node_id, hir_id) in self.node_id_to_hir_id.into_iter_enumerated() {
+ if let Some(def_id) = self.resolver.opt_local_def_id(node_id) {
+ if def_id_to_hir_id.len() <= def_id.index() {
+ def_id_to_hir_id.resize(def_id.index() + 1, None);
+ }
+ def_id_to_hir_id[def_id] = hir_id;
+ }
+ }
+
+ self.resolver.definitions().init_def_id_to_hir_id_mapping(def_id_to_hir_id);
hir::Crate {
item: hir::CrateItem { module, attrs, span: c.span },
.item_local_id_counters
.insert(owner, HIR_ID_COUNTER_LOCKED)
.unwrap_or_else(|| panic!("no `item_local_id_counters` entry for {:?}", owner));
- let def_id = self.resolver.definitions().local_def_id(owner);
+ let def_id = self.resolver.local_def_id(owner);
self.current_hir_id_owner.push((def_id, counter));
let ret = f(self);
let (new_def_id, new_counter) = self.current_hir_id_owner.pop().unwrap();
debug_assert!(local_id != HIR_ID_COUNTER_LOCKED);
*local_id_counter += 1;
- let owner = this.resolver.definitions().opt_local_def_id(owner).expect(
- "you forgot to call `create_def_with_parent` or are lowering node-IDs \
+ let owner = this.resolver.opt_local_def_id(owner).expect(
+ "you forgot to call `create_def` or are lowering node-IDs \
that do not belong to the current owner",
);
};
// Add a definition for the in-band lifetime def.
- self.resolver.definitions().create_def_with_parent(
+ self.resolver.create_def(
parent_def_id,
node_id,
DefPathData::LifetimeNs(str_name),
let impl_trait_node_id = self.resolver.next_node_id();
let parent_def_id = self.current_hir_id_owner.last().unwrap().0;
- self.resolver.definitions().create_def_with_parent(
+ self.resolver.create_def(
parent_def_id,
impl_trait_node_id,
DefPathData::ImplTrait,
let node_id = self.resolver.next_node_id();
// Add a definition for the in-band const def.
- self.resolver.definitions().create_def_with_parent(
+ self.resolver.create_def(
parent_def_id,
node_id,
DefPathData::AnonConst,
}
ImplTraitContext::Universal(in_band_ty_params) => {
// Add a definition for the in-band `Param`.
- let def_id = self.resolver.definitions().local_def_id(def_node_id);
+ let def_id = self.resolver.local_def_id(def_node_id);
let hir_bounds = self.lower_param_bounds(
bounds,
// frequently opened issues show.
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None);
- let opaque_ty_def_id = self.resolver.definitions().local_def_id(opaque_ty_node_id);
+ let opaque_ty_def_id = self.resolver.local_def_id(opaque_ty_node_id);
self.allocate_hir_id_counter(opaque_ty_node_id);
let def_node_id = self.context.resolver.next_node_id();
let hir_id =
self.context.lower_node_id_with_owner(def_node_id, self.opaque_ty_id);
- self.context.resolver.definitions().create_def_with_parent(
+ self.context.resolver.create_def(
self.parent,
def_node_id,
DefPathData::LifetimeNs(name.ident().name),
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None);
- let opaque_ty_def_id = self.resolver.definitions().local_def_id(opaque_ty_node_id);
+ let opaque_ty_def_id = self.resolver.local_def_id(opaque_ty_node_id);
self.allocate_hir_id_counter(opaque_ty_node_id);
use rustc_ast::ast;
use rustc_ast::with_default_globals;
-use rustc_span;
use rustc_span::source_map::respan;
use rustc_span::symbol::Ident;
use rustc_middle::ty::TyCtxt;
use rustc_session::config::{self, Lto, OutputType, Passes, SanitizerSet, SwitchWithOptPath};
use rustc_session::Session;
+use rustc_span::symbol::sym;
use rustc_span::InnerSpan;
use rustc_target::spec::{CodeModel, RelocModel};
// lower atomic operations to single-threaded operations.
if singlethread
&& sess.target.target.llvm_target.contains("wasm32")
- && features.iter().any(|s| *s == "+atomics")
+ && sess.target_features.contains(&sym::atomics)
{
singlethread = false;
}
use rustc_middle::ty::{self, Ty};
use rustc_middle::{bug, span_bug};
use rustc_span::Span;
-use rustc_span::Symbol;
use rustc_target::abi::{self, HasDataLayout, LayoutOf, Primitive};
use rustc_target::spec::PanicStrategy;
self.call(llfn, &[], None)
}
"count_code_region" => {
- if let ty::InstanceDef::Item(fn_def_id) = caller_instance.def {
- let caller_fn_path = tcx.def_path_str(fn_def_id);
- debug!(
- "count_code_region to llvm.instrprof.increment(fn_name={})",
- caller_fn_path
- );
-
- // FIXME(richkadel): (1) Replace raw function name with mangled function name;
- // (2) Replace hardcoded `1234` in `hash` with a computed hash (as discussed in)
- // the MCP (compiler-team/issues/278); and replace the hardcoded `1` for
- // `num_counters` with the actual number of counters per function (when the
- // changes are made to inject more than one counter per function).
- let (fn_name, _len_val) = self.const_str(Symbol::intern(&caller_fn_path));
- let index = args[0].immediate();
- let hash = self.const_u64(1234);
- let num_counters = self.const_u32(1);
- self.instrprof_increment(fn_name, hash, num_counters, index)
- } else {
- bug!("intrinsic count_code_region: no src.instance");
- }
+ // FIXME(richkadel): The current implementation assumes the MIR for the given
+ // caller_instance represents a single function. Validate and/or correct if inlining
+ // and/or monomorphization invalidates these assumptions.
+ let coverage_data = tcx.coverage_data(caller_instance.def_id());
+ let mangled_fn = tcx.symbol_name(caller_instance);
+ let (mangled_fn_name, _len_val) = self.const_str(mangled_fn.name);
+ let hash = self.const_u64(coverage_data.hash);
+ let num_counters = self.const_u32(coverage_data.num_counters);
+ let index = args[0].immediate();
+ debug!(
+ "count_code_region to LLVM intrinsic instrprof.increment(fn_name={}, hash={:?}, num_counters={:?}, index={:?})",
+ mangled_fn.name, hash, num_counters, index
+ );
+ self.instrprof_increment(mangled_fn_name, hash, num_counters, index)
}
"va_start" => self.va_start(args[0].immediate()),
"va_end" => self.va_end(args[0].immediate()),
// The third parameter is for env vars, used on windows to set up the
// path for MSVC to find its DLLs, and gcc to find its bundled
// toolchain
-fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> Command {
+fn get_linker(
+ sess: &Session,
+ linker: &Path,
+ flavor: LinkerFlavor,
+ self_contained: bool,
+) -> Command {
let msvc_tool = windows_registry::find_tool(&sess.opts.target_triple.triple(), "link.exe");
// If our linker looks like a batch script on Windows then to execute this
// The compiler's sysroot often has some bundled tools, so add it to the
// PATH for the child.
- let mut new_path = sess.host_filesearch(PathKind::All).get_tools_search_paths();
+ let mut new_path = sess.host_filesearch(PathKind::All).get_tools_search_paths(self_contained);
let mut msvc_changed_path = false;
if sess.target.target.options.is_like_msvc {
if let Some(ref tool) = msvc_tool {
"Linker does not support -static-pie command line option. Retrying with -static instead."
);
// Mirror `add_(pre,post)_link_objects` to replace CRT objects.
- let fallback = crt_objects_fallback(sess, crate_type);
+ let self_contained = crt_objects_fallback(sess, crate_type);
let opts = &sess.target.target.options;
- let pre_objects =
- if fallback { &opts.pre_link_objects_fallback } else { &opts.pre_link_objects };
- let post_objects =
- if fallback { &opts.post_link_objects_fallback } else { &opts.post_link_objects };
+ let pre_objects = if self_contained {
+ &opts.pre_link_objects_fallback
+ } else {
+ &opts.pre_link_objects
+ };
+ let post_objects = if self_contained {
+ &opts.post_link_objects_fallback
+ } else {
+ &opts.post_link_objects
+ };
let get_objects = |objects: &CrtObjects, kind| {
objects
.get(&kind)
.iter()
.copied()
.flatten()
- .map(|obj| get_object_file_path(sess, obj).into_os_string())
+ .map(|obj| get_object_file_path(sess, obj, self_contained).into_os_string())
.collect::<Vec<_>>()
};
let pre_objects_static_pie = get_objects(pre_objects, LinkOutputKind::StaticPicExe);
}
}
-fn get_object_file_path(sess: &Session, name: &str) -> PathBuf {
+fn get_object_file_path(sess: &Session, name: &str, self_contained: bool) -> PathBuf {
// prefer system {,dll}crt2.o libs, see get_crt_libs_path comment for more details
- if sess.target.target.llvm_target.contains("windows-gnu") {
+ if sess.opts.debugging_opts.link_self_contained.is_none()
+ && sess.target.target.llvm_target.contains("windows-gnu")
+ {
if let Some(compiler_libs_path) = get_crt_libs_path(sess) {
let file_path = compiler_libs_path.join(name);
if file_path.exists() {
if file_path.exists() {
return file_path;
}
- let file_path = fs.get_selfcontained_lib_path().join(name);
- if file_path.exists() {
- return file_path;
+ // Special directory with objects used only in self-contained linkage mode
+ if self_contained {
+ let file_path = fs.get_self_contained_lib_path().join(name);
+ if file_path.exists() {
+ return file_path;
+ }
}
for search_path in fs.search_paths() {
let file_path = search_path.dir.join(name);
/// Whether we link to our own CRT objects instead of relying on gcc to pull them.
/// We only provide such support for a very limited number of targets.
fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool {
+ if let Some(self_contained) = sess.opts.debugging_opts.link_self_contained {
+ return self_contained;
+ }
+
match sess.target.target.options.crt_objects_fallback {
// FIXME: Find a better heuristic for "native musl toolchain is available",
// based on host and linker path, for example.
cmd: &mut dyn Linker,
sess: &Session,
link_output_kind: LinkOutputKind,
- fallback: bool,
+ self_contained: bool,
) {
let opts = &sess.target.target.options;
- let objects = if fallback { &opts.pre_link_objects_fallback } else { &opts.pre_link_objects };
+ let objects =
+ if self_contained { &opts.pre_link_objects_fallback } else { &opts.pre_link_objects };
for obj in objects.get(&link_output_kind).iter().copied().flatten() {
- cmd.add_object(&get_object_file_path(sess, obj));
+ cmd.add_object(&get_object_file_path(sess, obj, self_contained));
}
}
cmd: &mut dyn Linker,
sess: &Session,
link_output_kind: LinkOutputKind,
- fallback: bool,
+ self_contained: bool,
) {
let opts = &sess.target.target.options;
- let objects = if fallback { &opts.post_link_objects_fallback } else { &opts.post_link_objects };
+ let objects =
+ if self_contained { &opts.post_link_objects_fallback } else { &opts.post_link_objects };
for obj in objects.get(&link_output_kind).iter().copied().flatten() {
- cmd.add_object(&get_object_file_path(sess, obj));
+ cmd.add_object(&get_object_file_path(sess, obj, self_contained));
}
}
}
/// Add sysroot and other globally set directories to the directory search list.
-fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session) {
+fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session, self_contained: bool) {
// Prefer system mingw-w64 libs, see get_crt_libs_path comment for more details.
- if cfg!(windows) && sess.target.target.llvm_target.contains("windows-gnu") {
+ if sess.opts.debugging_opts.link_self_contained.is_none()
+ && cfg!(windows)
+ && sess.target.target.llvm_target.contains("windows-gnu")
+ {
if let Some(compiler_libs_path) = get_crt_libs_path(sess) {
cmd.include_path(&compiler_libs_path);
}
let lib_path = sess.target_filesearch(PathKind::All).get_lib_path();
cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path));
- let lib_path = sess.target_filesearch(PathKind::All).get_selfcontained_lib_path();
- cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path));
+ // Special directory with libraries used only in self-contained linkage mode
+ if self_contained {
+ let lib_path = sess.target_filesearch(PathKind::All).get_self_contained_lib_path();
+ cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path));
+ }
}
/// Add options making relocation sections in the produced ELF files read-only
codegen_results: &CodegenResults,
target_cpu: &str,
) -> Command {
- let base_cmd = get_linker(sess, path, flavor);
+ let crt_objects_fallback = crt_objects_fallback(sess, crate_type);
+ let base_cmd = get_linker(sess, path, flavor, crt_objects_fallback);
// FIXME: Move `/LIBPATH` addition for uwp targets from the linker construction
// to the linker args construction.
assert!(base_cmd.get_args().is_empty() || sess.target.target.target_vendor == "uwp");
let cmd = &mut *codegen_results.linker_info.to_linker(base_cmd, &sess, flavor, target_cpu);
let link_output_kind = link_output_kind(sess, crate_type);
- let crt_objects_fallback = crt_objects_fallback(sess, crate_type);
// NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT
add_pre_link_args(cmd, sess, flavor);
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
// FIXME: Order-dependent, at least relatively to other args adding searh directories.
- add_library_search_dirs(cmd, sess);
+ add_library_search_dirs(cmd, sess, crt_objects_fallback);
// OBJECT-FILES-YES
add_local_crate_regular_objects(cmd, codegen_results);
use super::archive;
use super::command::Command;
use super::symbol_export;
+use rustc_span::symbol::sym;
use std::ffi::{OsStr, OsString};
use std::fs::{self, File};
//
// * `--export=*tls*` - when `#[thread_local]` symbols are used these
// symbols are how the TLS segments are initialized and configured.
- let atomics = sess.opts.cg.target_feature.contains("+atomics")
- || sess.target.target.options.features.contains("+atomics");
- if atomics {
+ if sess.target_features.contains(&sym::atomics) {
cmd.arg("--shared-memory");
cmd.arg("--max-memory=1073741824");
cmd.arg("--import-memory");
use parking_lot::Mutex as InnerLock;
use parking_lot::RwLock as InnerRwLock;
- use std;
use std::thread;
pub use rayon::{join, scope};
compiler.output_file().as_ref().map(|p| &**p),
);
}
+ trace!("finished pretty-printing");
return early_exit();
}
}
fn parse_nt_inner<'a>(p: &mut Parser<'a>, sp: Span, name: Symbol) -> PResult<'a, Nonterminal> {
+ // Any `Nonterminal` which stores its tokens (currently `NtItem` and `NtExpr`)
+ // needs to have them force-captured here.
+ // A `macro_rules!` invocation may pass a captured item/expr to a proc-macro,
+ // which requires having captured tokens available. Since we cannot determine
+ // in advance whether or not a proc-macro will be (transitively) invoked,
+ // we always capture tokens for any `Nonterminal` which needs them.
Ok(match name {
- sym::item => match p.parse_item()? {
- Some(i) => token::NtItem(i),
- None => return Err(p.struct_span_err(p.token.span, "expected an item keyword")),
+ sym::item => match p.collect_tokens(|this| this.parse_item())? {
+ (Some(mut item), tokens) => {
+ // If we captured tokens during parsing (due to outer attributes),
+ // use those.
+ if item.tokens.is_none() {
+ item.tokens = Some(tokens);
+ }
+ token::NtItem(item)
+ }
+ (None, _) => return Err(p.struct_span_err(p.token.span, "expected an item keyword")),
},
sym::block => token::NtBlock(p.parse_block()?),
sym::stmt => match p.parse_stmt()? {
None => return Err(p.struct_span_err(p.token.span, "expected a statement")),
},
sym::pat => token::NtPat(p.parse_pat(None)?),
- sym::expr => token::NtExpr(p.parse_expr()?),
+ sym::expr => {
+ let (mut expr, tokens) = p.collect_tokens(|this| this.parse_expr())?;
+ // If we captured tokens during parsing (due to outer attributes),
+ // use those.
+ if expr.tokens.is_none() {
+ expr.tokens = Some(tokens);
+ }
+ token::NtExpr(expr)
+ }
sym::literal => token::NtLiteral(p.parse_literal_maybe_minus()?),
sym::ty => token::NtTy(p.parse_ty()?),
// this could be handled like a token, since it is one
use crate::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
use crate::hir;
-use rustc_ast::ast;
use rustc_ast::crate_disambiguator::CrateDisambiguator;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::StableHasher;
use rustc_index::vec::IndexVec;
use rustc_span::hygiene::ExpnId;
use rustc_span::symbol::{sym, Symbol};
-use rustc_span::Span;
use log::debug;
use std::fmt::Write;
}
/// The definition table containing node definitions.
-/// It holds the `DefPathTable` for local `DefId`s/`DefPath`s and it also stores a
-/// mapping from `NodeId`s to local `DefId`s.
-#[derive(Clone, Default)]
+/// It holds the `DefPathTable` for `LocalDefId`s/`DefPath`s.
+/// It also stores mappings to convert `LocalDefId`s to/from `HirId`s.
+#[derive(Clone)]
pub struct Definitions {
table: DefPathTable,
- def_id_to_span: IndexVec<LocalDefId, Span>,
-
- node_id_to_def_id: FxHashMap<ast::NodeId, LocalDefId>,
- def_id_to_node_id: IndexVec<LocalDefId, ast::NodeId>,
-
// FIXME(eddyb) ideally all `LocalDefId`s would be HIR owners.
pub(super) def_id_to_hir_id: IndexVec<LocalDefId, Option<hir::HirId>>,
/// The reverse mapping of `def_id_to_hir_id`.
parent_modules_of_macro_defs: FxHashMap<ExpnId, DefId>,
/// Item with a given `LocalDefId` was defined during macro expansion with ID `ExpnId`.
expansions_that_defined: FxHashMap<LocalDefId, ExpnId>,
- next_disambiguator: FxHashMap<(LocalDefId, DefPathData), u32>,
- /// When collecting definitions from an AST fragment produced by a macro invocation `ExpnId`
- /// we know what parent node that fragment should be attached to thanks to this table.
- invocation_parents: FxHashMap<ExpnId, LocalDefId>,
- /// Indices of unnamed struct or variant fields with unresolved attributes.
- placeholder_field_indices: FxHashMap<ast::NodeId, usize>,
}
/// A unique identifier that we can use to lookup a definition
})
}
- #[inline]
- pub fn opt_local_def_id(&self, node: ast::NodeId) -> Option<LocalDefId> {
- self.node_id_to_def_id.get(&node).copied()
- }
-
- #[inline]
- pub fn local_def_id(&self, node: ast::NodeId) -> LocalDefId {
- self.opt_local_def_id(node).unwrap_or_else(|| panic!("no entry for node id: `{:?}`", node))
- }
-
#[inline]
pub fn as_local_hir_id(&self, def_id: LocalDefId) -> hir::HirId {
self.local_def_id_to_hir_id(def_id)
self.hir_id_to_def_id.get(&hir_id).copied()
}
- /// Retrieves the span of the given `DefId` if `DefId` is in the local crate.
- #[inline]
- pub fn opt_span(&self, def_id: DefId) -> Option<Span> {
- if let Some(def_id) = def_id.as_local() { Some(self.def_id_to_span[def_id]) } else { None }
- }
-
/// Adds a root definition (no parent) and a few other reserved definitions.
- pub fn create_root_def(
- &mut self,
- crate_name: &str,
- crate_disambiguator: CrateDisambiguator,
- ) -> LocalDefId {
+ pub fn new(crate_name: &str, crate_disambiguator: CrateDisambiguator) -> Definitions {
let key = DefKey {
parent: None,
disambiguated_data: DisambiguatedDefPathData {
let parent_hash = DefKey::root_parent_stable_hash(crate_name, crate_disambiguator);
let def_path_hash = key.compute_stable_hash(parent_hash);
- // Create the definition.
- let root = LocalDefId { local_def_index: self.table.allocate(key, def_path_hash) };
+ // Create the root definition.
+ let mut table = DefPathTable::default();
+ let root = LocalDefId { local_def_index: table.allocate(key, def_path_hash) };
assert_eq!(root.local_def_index, CRATE_DEF_INDEX);
- assert_eq!(self.def_id_to_node_id.push(ast::CRATE_NODE_ID), root);
- assert_eq!(self.def_id_to_span.push(rustc_span::DUMMY_SP), root);
-
- self.node_id_to_def_id.insert(ast::CRATE_NODE_ID, root);
- self.set_invocation_parent(ExpnId::root(), root);
+ Definitions {
+ table,
+ def_id_to_hir_id: Default::default(),
+ hir_id_to_def_id: Default::default(),
+ expansions_that_defined: Default::default(),
+ parent_modules_of_macro_defs: Default::default(),
+ }
+ }
- root
+ /// Retrieves the root definition.
+ pub fn get_root_def(&self) -> LocalDefId {
+ LocalDefId { local_def_index: CRATE_DEF_INDEX }
}
/// Adds a definition with a parent definition.
- pub fn create_def_with_parent(
+ pub fn create_def(
&mut self,
parent: LocalDefId,
- node_id: ast::NodeId,
data: DefPathData,
expn_id: ExpnId,
- span: Span,
+ mut next_disambiguator: impl FnMut(LocalDefId, DefPathData) -> u32,
) -> LocalDefId {
- debug!(
- "create_def_with_parent(parent={:?}, node_id={:?}, data={:?})",
- parent, node_id, data
- );
-
- assert!(
- !self.node_id_to_def_id.contains_key(&node_id),
- "adding a def'n for node-id {:?} and data {:?} but a previous def'n exists: {:?}",
- node_id,
- data,
- self.table.def_key(self.node_id_to_def_id[&node_id].local_def_index),
- );
+ debug!("create_def(parent={:?}, data={:?}, expn_id={:?})", parent, data, expn_id);
// The root node must be created with `create_root_def()`.
assert!(data != DefPathData::CrateRoot);
- // Find the next free disambiguator for this key.
- let disambiguator = {
- let next_disamb = self.next_disambiguator.entry((parent, data)).or_insert(0);
- let disambiguator = *next_disamb;
- *next_disamb = next_disamb.checked_add(1).expect("disambiguator overflow");
- disambiguator
- };
-
+ let disambiguator = next_disambiguator(parent, data);
let key = DefKey {
parent: Some(parent.local_def_index),
disambiguated_data: DisambiguatedDefPathData { data, disambiguator },
let parent_hash = self.table.def_path_hash(parent.local_def_index);
let def_path_hash = key.compute_stable_hash(parent_hash);
- debug!("create_def_with_parent: after disambiguation, key = {:?}", key);
+ debug!("create_def: after disambiguation, key = {:?}", key);
// Create the definition.
let def_id = LocalDefId { local_def_index: self.table.allocate(key, def_path_hash) };
- assert_eq!(self.def_id_to_node_id.push(node_id), def_id);
- assert_eq!(self.def_id_to_span.push(span), def_id);
-
- // Some things for which we allocate `LocalDefId`s don't correspond to
- // anything in the AST, so they don't have a `NodeId`. For these cases
- // we don't need a mapping from `NodeId` to `LocalDefId`.
- if node_id != ast::DUMMY_NODE_ID {
- debug!("create_def_with_parent: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id);
- self.node_id_to_def_id.insert(node_id, def_id);
- }
-
if expn_id != ExpnId::root() {
self.expansions_that_defined.insert(def_id, expn_id);
}
def_id
}
- /// Initializes the `ast::NodeId` to `HirId` mapping once it has been generated during
+ /// Initializes the `LocalDefId` to `HirId` mapping once it has been generated during
/// AST to HIR lowering.
- pub fn init_node_id_to_hir_id_mapping(
+ pub fn init_def_id_to_hir_id_mapping(
&mut self,
- mapping: IndexVec<ast::NodeId, Option<hir::HirId>>,
+ mapping: IndexVec<LocalDefId, Option<hir::HirId>>,
) {
assert!(
self.def_id_to_hir_id.is_empty(),
"trying to initialize `LocalDefId` <-> `HirId` mappings twice"
);
- self.def_id_to_hir_id = self
- .def_id_to_node_id
- .iter()
- .map(|&node_id| mapping.get(node_id).and_then(|&hir_id| hir_id))
- .collect();
-
// Build the reverse mapping of `def_id_to_hir_id`.
self.hir_id_to_def_id = mapping
- .into_iter_enumerated()
- .filter_map(|(node_id, hir_id)| {
- hir_id.and_then(|hir_id| {
- self.node_id_to_def_id.get(&node_id).map(|&def_id| (hir_id, def_id))
- })
- })
+ .iter_enumerated()
+ .filter_map(|(def_id, hir_id)| hir_id.map(|hir_id| (hir_id, def_id)))
.collect();
+
+ self.def_id_to_hir_id = mapping;
}
pub fn expansion_that_defined(&self, id: LocalDefId) -> ExpnId {
pub fn add_parent_module_of_macro_def(&mut self, expn_id: ExpnId, module: DefId) {
self.parent_modules_of_macro_defs.insert(expn_id, module);
}
-
- pub fn invocation_parent(&self, invoc_id: ExpnId) -> LocalDefId {
- self.invocation_parents[&invoc_id]
- }
-
- pub fn set_invocation_parent(&mut self, invoc_id: ExpnId, parent: LocalDefId) {
- let old_parent = self.invocation_parents.insert(invoc_id, parent);
- assert!(old_parent.is_none(), "parent `LocalDefId` is reset for an invocation");
- }
-
- pub fn placeholder_field_index(&self, node_id: ast::NodeId) -> usize {
- self.placeholder_field_indices[&node_id]
- }
-
- pub fn set_placeholder_field_index(&mut self, node_id: ast::NodeId, index: usize) {
- let old_index = self.placeholder_field_indices.insert(node_id, index);
- assert!(old_index.is_none(), "placeholder field index is reset for a node ID");
- }
-
- pub fn lint_node_id(&mut self, expn_id: ExpnId) -> ast::NodeId {
- self.invocation_parents
- .get(&expn_id)
- .map_or(ast::CRATE_NODE_ID, |id| self.def_id_to_node_id[*id])
- }
}
impl DefPathData {
// Check whether a span corresponding to a range expression is a
// range literal, rather than an explicit struct or `new()` call.
fn is_lit(sm: &SourceMap, span: &Span) -> bool {
- let end_point = sm.end_point(*span);
-
- if let Ok(end_string) = sm.span_to_snippet(end_point) {
- !(end_string.ends_with('}') || end_string.ends_with(')'))
- } else {
- false
- }
+ sm.span_to_snippet(*span).map(|range_src| range_src.contains("..")).unwrap_or(false)
};
match expr.kind {
// So you probably just want to nip down to the end.
macro_rules! language_item_table {
(
- $( $variant:ident, $name:expr, $method:ident, $target:path; )*
+ $( $variant:ident, $name:expr, $method:ident, $target:expr; )*
) => {
enum_from_u32! {
FnMutTraitLangItem, "fn_mut", fn_mut_trait, Target::Trait;
FnOnceTraitLangItem, "fn_once", fn_once_trait, Target::Trait;
+ FnOnceOutputLangItem, "fn_once_output", fn_once_output, Target::AssocTy;
+
FutureTraitLangItem, "future_trait", future_trait, Target::Trait;
GeneratorStateLangItem, "generator_state", gen_state, Target::Enum;
GeneratorTraitLangItem, "generator", gen_trait, Target::Trait;
(ty::FnDef(did1, substs1), ty::FnPtr(sig2)) => {
let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1);
let mut values = self.cmp_fn_sig(&sig1, sig2);
- values.0.push_normal(format!(
+ values.0.push_highlighted(format!(
" {{{}}}",
self.tcx.def_path_str_with_substs(*did1, substs1)
));
}
pub fn run_compiler<R: Send>(mut config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R {
+ log::trace!("run_compiler");
let stderr = config.stderr.take();
util::spawn_thread_pool(
config.opts.edition,
krate: ast::Crate,
crate_name: &str,
) -> Result<(ast::Crate, BoxedResolver)> {
+ log::trace!("configure_and_expand");
// Currently, we ignore the name resolution data structures for the purposes of dependency
// tracking. Instead we will run name resolution and include its output in the hash of each
// item, much like we do for macro expansion. In other words, the hash reflects not just
resolver_arenas: &'a ResolverArenas<'a>,
metadata_loader: &'a MetadataLoaderDyn,
) -> Result<(ast::Crate, Resolver<'a>)> {
+ log::trace!("configure_and_expand_inner");
pre_expansion_lint(sess, lint_store, &krate);
let mut resolver = Resolver::new(sess, &krate, crate_name, metadata_loader, &resolver_arenas);
should_loop |= true;
}
if should_loop {
+ log::debug!("replacing bodies with loop {{}}");
util::ReplaceBodyWithLoop::new(&mut resolver).visit_crate(&mut krate);
}
arena: &'tcx WorkerLocal<Arena<'tcx>>,
) -> QueryContext<'tcx> {
let sess = &compiler.session();
- let defs: &'tcx Definitions = arena.alloc(mem::take(&mut resolver_outputs.definitions));
+ let defs: &'tcx Definitions = arena.alloc(mem::replace(
+ &mut resolver_outputs.definitions,
+ Definitions::new(crate_name, sess.local_crate_disambiguator()),
+ ));
let query_result_on_disk_cache = rustc_incremental::load_query_result_cache(sess);
pub fn expansion(
&self,
) -> Result<&Query<(ast::Crate, Steal<Rc<RefCell<BoxedResolver>>>, Lrc<LintStore>)>> {
+ log::trace!("expansion");
self.expansion.compute(|| {
let crate_name = self.crate_name()?.peek().clone();
let (krate, lint_store) = self.register_plugins()?.take();
[dependencies]
log = "0.4"
-unicode-security = "0.0.3"
+unicode-security = "0.0.5"
rustc_middle = { path = "../librustc_middle" }
rustc_ast_pretty = { path = "../librustc_ast_pretty" }
rustc_attr = { path = "../librustc_attr" }
$args,
[
HardwiredLints: HardwiredLints,
- ImproperCTypes: ImproperCTypes,
+ ImproperCTypesDeclarations: ImproperCTypesDeclarations,
+ ImproperCTypesDefinitions: ImproperCTypesDefinitions,
VariantSizeDifferences: VariantSizeDifferences,
BoxPointers: BoxPointers,
PathStatements: PathStatements,
use crate::{EarlyContext, EarlyLintPass, LintContext};
use rustc_ast::ast;
use rustc_data_structures::fx::FxHashMap;
-use rustc_span::symbol::{Ident, SymbolStr};
-use std::hash::{Hash, Hasher};
-use std::ops::Deref;
+use rustc_span::symbol::SymbolStr;
declare_lint! {
pub NON_ASCII_IDENTS,
crate_level_only
}
-// FIXME: Change this to warn.
declare_lint! {
pub CONFUSABLE_IDENTS,
- Allow,
+ Warn,
"detects visually confusable pairs between identifiers",
crate_level_only
}
-declare_lint_pass!(NonAsciiIdents => [NON_ASCII_IDENTS, UNCOMMON_CODEPOINTS, CONFUSABLE_IDENTS]);
-
-enum CowBoxSymStr {
- Interned(SymbolStr),
- Owned(Box<str>),
-}
-
-impl Deref for CowBoxSymStr {
- type Target = str;
-
- fn deref(&self) -> &str {
- match self {
- CowBoxSymStr::Interned(interned) => interned,
- CowBoxSymStr::Owned(ref owned) => owned,
- }
- }
-}
-
-impl Hash for CowBoxSymStr {
- #[inline]
- fn hash<H: Hasher>(&self, state: &mut H) {
- Hash::hash(&**self, state)
- }
-}
-
-impl PartialEq<CowBoxSymStr> for CowBoxSymStr {
- #[inline]
- fn eq(&self, other: &CowBoxSymStr) -> bool {
- PartialEq::eq(&**self, &**other)
- }
-}
-
-impl Eq for CowBoxSymStr {}
-
-fn calc_skeleton(symbol_str: SymbolStr, buffer: &'_ mut String) -> CowBoxSymStr {
- use std::mem::swap;
- use unicode_security::confusable_detection::skeleton;
- buffer.clear();
- buffer.extend(skeleton(&symbol_str));
- if symbol_str == *buffer {
- CowBoxSymStr::Interned(symbol_str)
- } else {
- let mut owned = String::new();
- swap(buffer, &mut owned);
- CowBoxSymStr::Owned(owned.into_boxed_str())
- }
-}
-
-fn is_in_ascii_confusable_closure(c: char) -> bool {
- // FIXME: move this table to `unicode_security` crate.
- // data here corresponds to Unicode 13.
- const ASCII_CONFUSABLE_CLOSURE: &[(u64, u64)] = &[(0x00, 0x7f), (0xba, 0xba), (0x2080, 0x2080)];
- let c = c as u64;
- for &(range_start, range_end) in ASCII_CONFUSABLE_CLOSURE {
- if c >= range_start && c <= range_end {
- return true;
- }
- }
- false
+declare_lint! {
+ pub MIXED_SCRIPT_CONFUSABLES,
+ Warn,
+ "detects Unicode scripts whose mixed script confusables codepoints are solely used",
+ crate_level_only
}
-fn is_in_ascii_confusable_closure_relevant_list(c: char) -> bool {
- // FIXME: move this table to `unicode_security` crate.
- // data here corresponds to Unicode 13.
- const ASCII_CONFUSABLE_CLOSURE_RELEVANT_LIST: &[u64] = &[
- 0x22, 0x25, 0x27, 0x2f, 0x30, 0x31, 0x49, 0x4f, 0x60, 0x6c, 0x6d, 0x6e, 0x72, 0x7c, 0xba,
- 0x2080,
- ];
- let c = c as u64;
- for &item in ASCII_CONFUSABLE_CLOSURE_RELEVANT_LIST {
- if c == item {
- return true;
- }
- }
- false
-}
+declare_lint_pass!(NonAsciiIdents => [NON_ASCII_IDENTS, UNCOMMON_CODEPOINTS, CONFUSABLE_IDENTS, MIXED_SCRIPT_CONFUSABLES]);
impl EarlyLintPass for NonAsciiIdents {
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
use rustc_session::lint::Level;
- if cx.builder.lint_level(CONFUSABLE_IDENTS).0 == Level::Allow {
+ use rustc_span::Span;
+ use std::collections::BTreeMap;
+ use unicode_security::GeneralSecurityProfile;
+ use utils::CowBoxSymStr;
+
+ let check_non_ascii_idents = cx.builder.lint_level(NON_ASCII_IDENTS).0 != Level::Allow;
+ let check_uncommon_codepoints =
+ cx.builder.lint_level(UNCOMMON_CODEPOINTS).0 != Level::Allow;
+ let check_confusable_idents = cx.builder.lint_level(CONFUSABLE_IDENTS).0 != Level::Allow;
+ let check_mixed_script_confusables =
+ cx.builder.lint_level(MIXED_SCRIPT_CONFUSABLES).0 != Level::Allow;
+
+ if !check_non_ascii_idents
+ && !check_uncommon_codepoints
+ && !check_confusable_idents
+ && !check_mixed_script_confusables
+ {
return;
}
+
+ let mut has_non_ascii_idents = false;
let symbols = cx.sess.parse_sess.symbol_gallery.symbols.lock();
- let mut symbol_strs_and_spans = Vec::with_capacity(symbols.len());
- let mut in_fast_path = true;
- for (symbol, sp) in symbols.iter() {
- // fast path
+ for (symbol, &sp) in symbols.iter() {
let symbol_str = symbol.as_str();
- if !symbol_str.chars().all(is_in_ascii_confusable_closure) {
- // fallback to slow path.
- symbol_strs_and_spans.clear();
- in_fast_path = false;
- break;
+ if symbol_str.is_ascii() {
+ continue;
}
- if symbol_str.chars().any(is_in_ascii_confusable_closure_relevant_list) {
- symbol_strs_and_spans.push((symbol_str, *sp));
+ has_non_ascii_idents = true;
+ cx.struct_span_lint(NON_ASCII_IDENTS, sp, |lint| {
+ lint.build("identifier contains non-ASCII characters").emit()
+ });
+ if check_uncommon_codepoints
+ && !symbol_str.chars().all(GeneralSecurityProfile::identifier_allowed)
+ {
+ cx.struct_span_lint(UNCOMMON_CODEPOINTS, sp, |lint| {
+ lint.build("identifier contains uncommon Unicode codepoints").emit()
+ })
}
}
- if !in_fast_path {
- // slow path
- for (symbol, sp) in symbols.iter() {
+
+ if has_non_ascii_idents && check_confusable_idents {
+ let mut skeleton_map: FxHashMap<CowBoxSymStr, (SymbolStr, Span, bool)> =
+ FxHashMap::with_capacity_and_hasher(symbols.len(), Default::default());
+ let mut str_buf = String::new();
+ for (symbol, &sp) in symbols.iter() {
+ fn calc_skeleton(symbol_str: &SymbolStr, buffer: &mut String) -> CowBoxSymStr {
+ use std::mem::replace;
+ use unicode_security::confusable_detection::skeleton;
+ buffer.clear();
+ buffer.extend(skeleton(symbol_str));
+ if *symbol_str == *buffer {
+ CowBoxSymStr::Interned(symbol_str.clone())
+ } else {
+ let owned = replace(buffer, String::new());
+ CowBoxSymStr::Owned(owned.into_boxed_str())
+ }
+ }
let symbol_str = symbol.as_str();
- symbol_strs_and_spans.push((symbol_str, *sp));
+ let is_ascii = symbol_str.is_ascii();
+ let skeleton = calc_skeleton(&symbol_str, &mut str_buf);
+ skeleton_map
+ .entry(skeleton)
+ .and_modify(|(existing_symbolstr, existing_span, existing_is_ascii)| {
+ if !*existing_is_ascii || !is_ascii {
+ cx.struct_span_lint(CONFUSABLE_IDENTS, sp, |lint| {
+ lint.build(&format!(
+ "identifier pair considered confusable between `{}` and `{}`",
+ existing_symbolstr, symbol_str
+ ))
+ .span_label(
+ *existing_span,
+ "this is where the previous identifier occurred",
+ )
+ .emit();
+ });
+ }
+ if *existing_is_ascii && !is_ascii {
+ *existing_symbolstr = symbol_str.clone();
+ *existing_span = sp;
+ *existing_is_ascii = is_ascii;
+ }
+ })
+ .or_insert((symbol_str, sp, is_ascii));
}
}
- drop(symbols);
- symbol_strs_and_spans.sort_by_key(|x| x.0.clone());
- let mut skeleton_map =
- FxHashMap::with_capacity_and_hasher(symbol_strs_and_spans.len(), Default::default());
- let mut str_buf = String::new();
- for (symbol_str, sp) in symbol_strs_and_spans {
- let skeleton = calc_skeleton(symbol_str.clone(), &mut str_buf);
- skeleton_map
- .entry(skeleton)
- .and_modify(|(existing_symbolstr, existing_span)| {
- cx.struct_span_lint(CONFUSABLE_IDENTS, sp, |lint| {
- lint.build(&format!(
- "identifier pair considered confusable between `{}` and `{}`",
- existing_symbolstr, symbol_str
- ))
- .span_label(
- *existing_span,
- "this is where the previous identifier occurred",
- )
- .emit();
+
+ if has_non_ascii_idents && check_mixed_script_confusables {
+ use unicode_security::is_potential_mixed_script_confusable_char;
+ use unicode_security::mixed_script::AugmentedScriptSet;
+
+ #[derive(Clone)]
+ enum ScriptSetUsage {
+ Suspicious(Vec<char>, Span),
+ Verified,
+ }
+
+ let mut script_states: FxHashMap<AugmentedScriptSet, ScriptSetUsage> =
+ FxHashMap::default();
+ let latin_augmented_script_set = AugmentedScriptSet::for_char('A');
+ script_states.insert(latin_augmented_script_set, ScriptSetUsage::Verified);
+
+ let mut has_suspicous = false;
+ for (symbol, &sp) in symbols.iter() {
+ let symbol_str = symbol.as_str();
+ for ch in symbol_str.chars() {
+ if ch.is_ascii() {
+ // all ascii characters are covered by exception.
+ continue;
+ }
+ if !GeneralSecurityProfile::identifier_allowed(ch) {
+ // this character is covered by `uncommon_codepoints` lint.
+ continue;
+ }
+ let augmented_script_set = AugmentedScriptSet::for_char(ch);
+ script_states
+ .entry(augmented_script_set)
+ .and_modify(|existing_state| {
+ if let ScriptSetUsage::Suspicious(ch_list, _) = existing_state {
+ if is_potential_mixed_script_confusable_char(ch) {
+ ch_list.push(ch);
+ } else {
+ *existing_state = ScriptSetUsage::Verified;
+ }
+ }
+ })
+ .or_insert_with(|| {
+ if !is_potential_mixed_script_confusable_char(ch) {
+ ScriptSetUsage::Verified
+ } else {
+ has_suspicous = true;
+ ScriptSetUsage::Suspicious(vec![ch], sp)
+ }
+ });
+ }
+ }
+
+ if has_suspicous {
+ let verified_augmented_script_sets = script_states
+ .iter()
+ .flat_map(|(k, v)| match v {
+ ScriptSetUsage::Verified => Some(*k),
+ _ => None,
+ })
+ .collect::<Vec<_>>();
+
+ // we're sorting the output here.
+ let mut lint_reports: BTreeMap<(Span, Vec<char>), AugmentedScriptSet> =
+ BTreeMap::new();
+
+ 'outerloop: for (augment_script_set, usage) in script_states {
+ let (mut ch_list, sp) = match usage {
+ ScriptSetUsage::Verified => continue,
+ ScriptSetUsage::Suspicious(ch_list, sp) => (ch_list, sp),
+ };
+
+ if augment_script_set.is_all() {
+ continue;
+ }
+
+ for existing in verified_augmented_script_sets.iter() {
+ if existing.is_all() {
+ continue;
+ }
+ let mut intersect = *existing;
+ intersect.intersect_with(augment_script_set);
+ if !intersect.is_empty() && !intersect.is_all() {
+ continue 'outerloop;
+ }
+ }
+
+ ch_list.sort();
+ ch_list.dedup();
+ lint_reports.insert((sp, ch_list), augment_script_set);
+ }
+
+ for ((sp, ch_list), script_set) in lint_reports {
+ cx.struct_span_lint(MIXED_SCRIPT_CONFUSABLES, sp, |lint| {
+ let message = format!(
+ "The usage of Script Group `{}` in this crate consists solely of mixed script confusables",
+ script_set);
+ let mut note = "The usage includes ".to_string();
+ for (idx, ch) in ch_list.into_iter().enumerate() {
+ if idx != 0 {
+ note += ", ";
+ }
+ let char_info = format!("'{}' (U+{:04X})", ch, ch as u32);
+ note += &char_info;
+ }
+ note += ".";
+ lint.build(&message).note(¬e).note("Please recheck to make sure their usages are indeed what you want.").emit()
});
- })
- .or_insert((symbol_str, sp));
+ }
+ }
}
}
- fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
- use unicode_security::GeneralSecurityProfile;
- let name_str = ident.name.as_str();
- if name_str.is_ascii() {
- return;
+}
+
+mod utils {
+ use rustc_span::symbol::SymbolStr;
+ use std::hash::{Hash, Hasher};
+ use std::ops::Deref;
+
+ pub(super) enum CowBoxSymStr {
+ Interned(SymbolStr),
+ Owned(Box<str>),
+ }
+
+ impl Deref for CowBoxSymStr {
+ type Target = str;
+
+ fn deref(&self) -> &str {
+ match self {
+ CowBoxSymStr::Interned(interned) => interned,
+ CowBoxSymStr::Owned(ref owned) => owned,
+ }
+ }
+ }
+
+ impl Hash for CowBoxSymStr {
+ #[inline]
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ Hash::hash(&**self, state)
}
- cx.struct_span_lint(NON_ASCII_IDENTS, ident.span, |lint| {
- lint.build("identifier contains non-ASCII characters").emit()
- });
- if !name_str.chars().all(GeneralSecurityProfile::identifier_allowed) {
- cx.struct_span_lint(UNCOMMON_CODEPOINTS, ident.span, |lint| {
- lint.build("identifier contains uncommon Unicode codepoints").emit()
- })
+ }
+
+ impl PartialEq<CowBoxSymStr> for CowBoxSymStr {
+ #[inline]
+ fn eq(&self, other: &CowBoxSymStr) -> bool {
+ PartialEq::eq(&**self, &**other)
}
}
+
+ impl Eq for CowBoxSymStr {}
}
use rustc_middle::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt, TypeFoldable};
use rustc_span::source_map;
use rustc_span::symbol::sym;
-use rustc_span::Span;
+use rustc_span::{Span, DUMMY_SP};
use rustc_target::abi::{Integer, LayoutOf, TagEncoding, VariantIdx, Variants};
use rustc_target::spec::abi::Abi;
"proper use of libc types in foreign modules"
}
-declare_lint_pass!(ImproperCTypes => [IMPROPER_CTYPES]);
+declare_lint_pass!(ImproperCTypesDeclarations => [IMPROPER_CTYPES]);
+
+declare_lint! {
+ IMPROPER_CTYPES_DEFINITIONS,
+ Warn,
+ "proper use of libc types in foreign item definitions"
+}
+
+declare_lint_pass!(ImproperCTypesDefinitions => [IMPROPER_CTYPES_DEFINITIONS]);
+
+enum ImproperCTypesMode {
+ Declarations,
+ Definitions,
+}
struct ImproperCTypesVisitor<'a, 'tcx> {
cx: &'a LateContext<'a, 'tcx>,
+ mode: ImproperCTypesMode,
}
enum FfiResult<'tcx> {
help: Some("consider using a struct instead".into()),
},
+ ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _)
+ if {
+ matches!(self.mode, ImproperCTypesMode::Definitions)
+ && ty.is_sized(self.cx.tcx.at(DUMMY_SP), self.cx.param_env)
+ } =>
+ {
+ FfiSafe
+ }
+
ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => {
self.check_type_for_ffi(cache, ty)
}
ty::Array(inner_ty, _) => self.check_type_for_ffi(cache, inner_ty),
ty::FnPtr(sig) => {
- match sig.abi() {
- Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic | Abi::RustCall => {
- return FfiUnsafe {
- ty,
- reason: "this function pointer has Rust-specific calling convention"
+ if self.is_internal_abi(sig.abi()) {
+ return FfiUnsafe {
+ ty,
+ reason: "this function pointer has Rust-specific calling convention".into(),
+ help: Some(
+ "consider using an `extern fn(...) -> ...` \
+ function pointer instead"
.into(),
- help: Some(
- "consider using an `extern fn(...) -> ...` \
- function pointer instead"
- .into(),
- ),
- };
- }
- _ => {}
+ ),
+ };
}
let sig = cx.erase_late_bound_regions(&sig);
FfiUnsafe { ty, reason: "opaque types have no C equivalent".into(), help: None }
}
+ // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
+ // so they are currently ignored for the purposes of this lint.
+ ty::Param(..) | ty::Projection(..)
+ if matches!(self.mode, ImproperCTypesMode::Definitions) =>
+ {
+ FfiSafe
+ }
+
ty::Param(..)
+ | ty::Projection(..)
| ty::Infer(..)
| ty::Bound(..)
| ty::Error(_)
| ty::Generator(..)
| ty::GeneratorWitness(..)
| ty::Placeholder(..)
- | ty::Projection(..)
| ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
}
}
note: &str,
help: Option<&str>,
) {
- self.cx.struct_span_lint(IMPROPER_CTYPES, sp, |lint| {
- let mut diag =
- lint.build(&format!("`extern` block uses type `{}`, which is not FFI-safe", ty));
+ let lint = match self.mode {
+ ImproperCTypesMode::Declarations => IMPROPER_CTYPES,
+ ImproperCTypesMode::Definitions => IMPROPER_CTYPES_DEFINITIONS,
+ };
+
+ self.cx.struct_span_lint(lint, sp, |lint| {
+ let item_description = match self.mode {
+ ImproperCTypesMode::Declarations => "block",
+ ImproperCTypesMode::Definitions => "fn",
+ };
+ let mut diag = lint.build(&format!(
+ "`extern` {} uses type `{}`, which is not FFI-safe",
+ item_description, ty
+ ));
diag.span_label(sp, "not FFI-safe");
if let Some(help) = help {
diag.help(help);
// it is only OK to use this function because extern fns cannot have
// any generic types right now:
- let ty = self.cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty);
+ let ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, ty);
// C doesn't really support passing arrays by value - the only way to pass an array by value
// is through a struct. So, first test that the top level isn't an array, and then
let ty = self.cx.tcx.type_of(def_id);
self.check_type_for_ffi_and_report_errors(span, ty, true, false);
}
+
+ fn is_internal_abi(&self, abi: Abi) -> bool {
+ if let Abi::Rust | Abi::RustCall | Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi {
+ true
+ } else {
+ false
+ }
+ }
}
-impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImproperCTypes {
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImproperCTypesDeclarations {
fn check_foreign_item(&mut self, cx: &LateContext<'_, '_>, it: &hir::ForeignItem<'_>) {
- let mut vis = ImproperCTypesVisitor { cx };
+ let mut vis = ImproperCTypesVisitor { cx, mode: ImproperCTypesMode::Declarations };
let abi = cx.tcx.hir().get_foreign_abi(it.hir_id);
- if let Abi::Rust | Abi::RustCall | Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi {
- // Don't worry about types in internal ABIs.
- } else {
+
+ if !vis.is_internal_abi(abi) {
match it.kind {
hir::ForeignItemKind::Fn(ref decl, _, _) => {
vis.check_foreign_fn(it.hir_id, decl);
}
}
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImproperCTypesDefinitions {
+ fn check_fn(
+ &mut self,
+ cx: &LateContext<'a, 'tcx>,
+ kind: hir::intravisit::FnKind<'tcx>,
+ decl: &'tcx hir::FnDecl<'_>,
+ _: &'tcx hir::Body<'_>,
+ _: Span,
+ hir_id: hir::HirId,
+ ) {
+ use hir::intravisit::FnKind;
+
+ let abi = match kind {
+ FnKind::ItemFn(_, _, header, ..) => header.abi,
+ FnKind::Method(_, sig, ..) => sig.header.abi,
+ _ => return,
+ };
+
+ let mut vis = ImproperCTypesVisitor { cx, mode: ImproperCTypesMode::Definitions };
+ if !vis.is_internal_abi(abi) {
+ vis.check_foreign_fn(hir_id, decl);
+ }
+ }
+}
+
declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences {
/// Appending to a Rust string -- used by RawRustStringOstream.
#[no_mangle]
+#[cfg_attr(not(bootstrap), allow(improper_ctypes_definitions))]
pub unsafe extern "C" fn LLVMRustStringWriteImpl(
sr: &RustString,
ptr: *const c_char,
use rustc_data_structures::sync::Lrc;
use rustc_errors::struct_span_err;
use rustc_expand::base::SyntaxExtension;
-use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
+use rustc_hir::def_id::{CrateNum, LocalDefId, LOCAL_CRATE};
use rustc_hir::definitions::Definitions;
use rustc_index::vec::IndexVec;
use rustc_middle::middle::cstore::DepKind;
&mut self,
item: &ast::Item,
definitions: &Definitions,
+ def_id: LocalDefId,
) -> CrateNum {
match item.kind {
ast::ItemKind::ExternCrate(orig_name) => {
let cnum = self.resolve_crate(name, item.span, dep_kind, None);
- let def_id = definitions.opt_local_def_id(item.id).unwrap();
let path_len = definitions.def_path(def_id).data.len();
self.update_extern_crate(
cnum,
&& self.triple != TargetTriple::from_triple(config::host_triple())
{
err.note(&format!("the `{}` target may not be installed", self.triple));
+ } else if self.crate_name == sym::profiler_builtins {
+ err.note(&"the compiler may have been built without the profiler runtime");
}
err.span_label(self.span, "can't find crate");
err
}
}
-fn associated_body<'hir>(node: Node<'hir>) -> Option<BodyId> {
+pub fn associated_body<'hir>(node: Node<'hir>) -> Option<BodyId> {
match node {
Node::Item(Item {
kind: ItemKind::Const(_, body) | ItemKind::Static(.., body) | ItemKind::Fn(.., body),
/// Don't use it for anything else or you'll run the risk of
/// leaking data out of the tracking system.
#[inline]
- pub fn new(
+ fn new_with_or_without_spans(
sess: &'a Session,
krate: &'a hir::Crate<'a>,
definitions: &'a Definitions,
cstore: &'a dyn CrateStore,
+ always_ignore_spans: bool,
) -> Self {
- let hash_spans_initial = !sess.opts.debugging_opts.incremental_ignore_spans;
+ let hash_spans_initial =
+ !always_ignore_spans && !sess.opts.debugging_opts.incremental_ignore_spans;
StableHashingContext {
sess,
}
}
+ #[inline]
+ pub fn new(
+ sess: &'a Session,
+ krate: &'a hir::Crate<'a>,
+ definitions: &'a Definitions,
+ cstore: &'a dyn CrateStore,
+ ) -> Self {
+ Self::new_with_or_without_spans(
+ sess,
+ krate,
+ definitions,
+ cstore,
+ /*always_ignore_spans=*/ false,
+ )
+ }
+
+ #[inline]
+ pub fn ignore_spans(
+ sess: &'a Session,
+ krate: &'a hir::Crate<'a>,
+ definitions: &'a Definitions,
+ cstore: &'a dyn CrateStore,
+ ) -> Self {
+ let always_ignore_spans = true;
+ Self::new_with_or_without_spans(sess, krate, definitions, cstore, always_ignore_spans)
+ }
+
#[inline]
pub fn sess(&self) -> &'a Session {
self.sess
/// The user may be writing e.g. `&[(SOME_CELL, 42)][i].1` and this would get promoted, because
/// we'd statically know that no thing with interior mutability will ever be available to the
/// user without some serious unsafe code. Now this means that our promoted is actually
- /// `&[(SOME_CELL, 42)]` and the MIR using it will do the `&promoted[i].1` projection because the
- /// index may be a runtime value. Such a promoted value is illegal because it has reachable
+ /// `&[(SOME_CELL, 42)]` and the MIR using it will do the `&promoted[i].1` projection because
+ /// the index may be a runtime value. Such a promoted value is illegal because it has reachable
/// interior mutability. This flag just makes this situation very obvious where the previous
/// implementation without the flag hid this situation silently.
/// FIXME(oli-obk): rewrite the promoted during promotion to eliminate the cell components.
}
}
}
+
+/// Coverage data associated with each function (MIR) instrumented with coverage counters, when
+/// compiled with `-Zinstrument_coverage`. The query `tcx.coverage_data(DefId)` computes these
+/// values on demand (during code generation). This query is only valid after executing the MIR pass
+/// `InstrumentCoverage`.
+#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable)]
+pub struct CoverageData {
+ /// A hash value that can be used by the consumer of the coverage profile data to detect
+ /// changes to the instrumented source of the associated MIR body (typically, for an
+ /// individual function).
+ pub hash: u64,
+
+ /// The total number of coverage region counters added to the MIR `Body`.
+ pub num_counters: u32,
+}
cache_on_disk_if { key.is_local() }
}
+ query coverage_data(key: DefId) -> mir::CoverageData {
+ desc { |tcx| "retrieving coverage data from MIR for `{}`", tcx.def_path_str(key) }
+ storage(ArenaCacheSelector<'tcx>)
+ cache_on_disk_if { key.is_local() }
+ }
+
query promoted_mir(key: DefId) -> IndexVec<mir::Promoted, mir::Body<'tcx>> {
desc { |tcx| "optimizing promoted MIR for `{}`", tcx.def_path_str(key) }
storage(ArenaCacheSelector<'tcx>)
StableHashingContext::new(self.sess, krate, self.definitions, &*self.cstore)
}
+ #[inline(always)]
+ pub fn create_no_span_stable_hashing_context(self) -> StableHashingContext<'tcx> {
+ let krate = self.gcx.untracked_crate;
+
+ StableHashingContext::ignore_spans(self.sess, krate, self.definitions, &*self.cstore)
+ }
+
// This method makes sure that we have a DepNode and a Fingerprint for
// every upstream crate. It needs to be called once right after the tcx is
// created.
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::sync::{par_iter, MTLock, MTRef, ParallelIterator};
-use rustc_errors::ErrorReported;
+use rustc_errors::{ErrorReported, FatalError};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LOCAL_CRATE};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts};
use rustc_middle::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable};
use rustc_session::config::EntryFnType;
+use rustc_span::source_map::{dummy_spanned, respan, Span, Spanned, DUMMY_SP};
use smallvec::SmallVec;
use std::iter;
tcx.sess.time("monomorphization_collector_graph_walk", || {
par_iter(roots).for_each(|root| {
let mut recursion_depths = DefIdMap::default();
- collect_items_rec(tcx, root, visited, &mut recursion_depths, inlining_map);
+ collect_items_rec(
+ tcx,
+ dummy_spanned(root),
+ visited,
+ &mut recursion_depths,
+ inlining_map,
+ );
});
});
}
// We can only codegen items that are instantiable - items all of
// whose predicates hold. Luckily, items that aren't instantiable
// can't actually be used, so we can just skip codegenning them.
- roots.retain(|root| root.is_instantiable(tcx));
-
roots
+ .into_iter()
+ .filter_map(|root| root.node.is_instantiable(tcx).then_some(root.node))
+ .collect()
}
// Collect all monomorphized items reachable from `starting_point`
fn collect_items_rec<'tcx>(
tcx: TyCtxt<'tcx>,
- starting_point: MonoItem<'tcx>,
+ starting_point: Spanned<MonoItem<'tcx>>,
visited: MTRef<'_, MTLock<FxHashSet<MonoItem<'tcx>>>>,
recursion_depths: &mut DefIdMap<usize>,
inlining_map: MTRef<'_, MTLock<InliningMap<'tcx>>>,
) {
- if !visited.lock_mut().insert(starting_point) {
+ if !visited.lock_mut().insert(starting_point.node) {
// We've been here already, no need to search again.
return;
}
- debug!("BEGIN collect_items_rec({})", starting_point.to_string(tcx, true));
+ debug!("BEGIN collect_items_rec({})", starting_point.node.to_string(tcx, true));
let mut neighbors = Vec::new();
let recursion_depth_reset;
- match starting_point {
+ match starting_point.node {
MonoItem::Static(def_id) => {
let instance = Instance::mono(tcx, def_id);
debug_assert!(should_monomorphize_locally(tcx, &instance));
let ty = instance.monomorphic_ty(tcx);
- visit_drop_use(tcx, ty, true, &mut neighbors);
+ visit_drop_use(tcx, ty, true, starting_point.span, &mut neighbors);
recursion_depth_reset = None;
debug_assert!(should_monomorphize_locally(tcx, &instance));
// Keep track of the monomorphization recursion depth
- recursion_depth_reset = Some(check_recursion_limit(tcx, instance, recursion_depths));
+ recursion_depth_reset =
+ Some(check_recursion_limit(tcx, instance, starting_point.span, recursion_depths));
check_type_length_limit(tcx, instance);
rustc_data_structures::stack::ensure_sufficient_stack(|| {
}
}
- record_accesses(tcx, starting_point, &neighbors[..], inlining_map);
+ record_accesses(tcx, starting_point.node, neighbors.iter().map(|i| &i.node), inlining_map);
for neighbour in neighbors {
collect_items_rec(tcx, neighbour, visited, recursion_depths, inlining_map);
recursion_depths.insert(def_id, depth);
}
- debug!("END collect_items_rec({})", starting_point.to_string(tcx, true));
+ debug!("END collect_items_rec({})", starting_point.node.to_string(tcx, true));
}
-fn record_accesses<'tcx>(
+fn record_accesses<'a, 'tcx: 'a>(
tcx: TyCtxt<'tcx>,
caller: MonoItem<'tcx>,
- callees: &[MonoItem<'tcx>],
+ callees: impl Iterator<Item = &'a MonoItem<'tcx>>,
inlining_map: MTRef<'_, MTLock<InliningMap<'tcx>>>,
) {
let is_inlining_candidate = |mono_item: &MonoItem<'tcx>| {
// FIXME: Call `is_inlining_candidate` when pushing to `neighbors` in `collect_items_rec`
// instead to avoid creating this `SmallVec`.
let accesses: SmallVec<[_; 128]> =
- callees.iter().map(|mono_item| (*mono_item, is_inlining_candidate(mono_item))).collect();
+ callees.map(|mono_item| (*mono_item, is_inlining_candidate(mono_item))).collect();
inlining_map.lock_mut().record_accesses(caller, &accesses);
}
fn check_recursion_limit<'tcx>(
tcx: TyCtxt<'tcx>,
instance: Instance<'tcx>,
+ span: Span,
recursion_depths: &mut DefIdMap<usize>,
) -> (DefId, usize) {
let def_id = instance.def_id();
// infinite expansion.
if !tcx.sess.recursion_limit().value_within_limit(adjusted_recursion_depth) {
let error = format!("reached the recursion limit while instantiating `{}`", instance);
- if let Some(def_id) = def_id.as_local() {
- let hir_id = tcx.hir().as_local_hir_id(def_id);
- tcx.sess.span_fatal(tcx.hir().span(hir_id), &error);
- } else {
- tcx.sess.fatal(&error);
- }
+ 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)),
+ );
+ err.emit();
+ FatalError.raise();
}
recursion_depths.insert(def_id, recursion_depth + 1);
struct MirNeighborCollector<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
body: &'a mir::Body<'tcx>,
- output: &'a mut Vec<MonoItem<'tcx>>,
+ output: &'a mut Vec<Spanned<MonoItem<'tcx>>>,
instance: Instance<'tcx>,
}
fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
debug!("visiting rvalue {:?}", *rvalue);
+ let span = self.body.source_info(location).span;
+
match *rvalue {
// When doing an cast from a regular pointer to a fat pointer, we
// have to instantiate all methods of the trait being cast to, so we
self.tcx,
target_ty,
source_ty,
+ span,
self.output,
);
}
) => {
let fn_ty = operand.ty(self.body, self.tcx);
let fn_ty = self.monomorphize(fn_ty);
- visit_fn_use(self.tcx, fn_ty, false, &mut self.output);
+ visit_fn_use(self.tcx, fn_ty, false, span, &mut self.output);
}
mir::Rvalue::Cast(
mir::CastKind::Pointer(PointerCast::ClosureFnPointer(_)),
ty::ClosureKind::FnOnce,
);
if should_monomorphize_locally(self.tcx, &instance) {
- self.output.push(create_fn_mono_item(instance));
+ self.output.push(create_fn_mono_item(instance, span));
}
}
_ => bug!(),
tcx.require_lang_item(ExchangeMallocFnLangItem, None);
let instance = Instance::mono(tcx, exchange_malloc_fn_def_id);
if should_monomorphize_locally(tcx, &instance) {
- self.output.push(create_fn_mono_item(instance));
+ self.output.push(create_fn_mono_item(instance, span));
}
}
mir::Rvalue::ThreadLocalRef(def_id) => {
let instance = Instance::mono(self.tcx, def_id);
if should_monomorphize_locally(self.tcx, &instance) {
trace!("collecting thread-local static {:?}", def_id);
- self.output.push(MonoItem::Static(def_id));
+ self.output.push(respan(span, MonoItem::Static(def_id)));
}
}
_ => { /* not interesting */ }
fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
debug!("visiting terminator {:?} @ {:?}", terminator, location);
+ let source = self.body.source_info(location).span;
let tcx = self.tcx;
match terminator.kind {
mir::TerminatorKind::Call { ref func, .. } => {
let callee_ty = func.ty(self.body, tcx);
let callee_ty = self.monomorphize(callee_ty);
- visit_fn_use(self.tcx, callee_ty, true, &mut self.output);
+ visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output);
}
mir::TerminatorKind::Drop { ref place, .. }
| mir::TerminatorKind::DropAndReplace { ref place, .. } => {
let ty = place.ty(self.body, self.tcx).ty;
let ty = self.monomorphize(ty);
- visit_drop_use(self.tcx, ty, true, self.output);
+ visit_drop_use(self.tcx, ty, true, source, self.output);
}
mir::TerminatorKind::InlineAsm { ref operands, .. } => {
for op in operands {
match *op {
mir::InlineAsmOperand::SymFn { ref value } => {
let fn_ty = self.monomorphize(value.literal.ty);
- visit_fn_use(self.tcx, fn_ty, false, &mut self.output);
+ visit_fn_use(self.tcx, fn_ty, false, source, &mut self.output);
}
mir::InlineAsmOperand::SymStatic { def_id } => {
let instance = Instance::mono(self.tcx, def_id);
if should_monomorphize_locally(self.tcx, &instance) {
trace!("collecting asm sym static {:?}", def_id);
- self.output.push(MonoItem::Static(def_id));
+ self.output.push(respan(source, MonoItem::Static(def_id)));
}
}
_ => {}
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
is_direct_call: bool,
- output: &mut Vec<MonoItem<'tcx>>,
+ source: Span,
+ output: &mut Vec<Spanned<MonoItem<'tcx>>>,
) {
let instance = Instance::resolve_drop_in_place(tcx, ty);
- visit_instance_use(tcx, instance, is_direct_call, output);
+ visit_instance_use(tcx, instance, is_direct_call, source, output);
}
fn visit_fn_use<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
is_direct_call: bool,
- output: &mut Vec<MonoItem<'tcx>>,
+ source: Span,
+ output: &mut Vec<Spanned<MonoItem<'tcx>>>,
) {
if let ty::FnDef(def_id, substs) = ty.kind {
let instance = if is_direct_call {
ty::Instance::resolve_for_fn_ptr(tcx, ty::ParamEnv::reveal_all(), def_id, substs)
.unwrap()
};
- visit_instance_use(tcx, instance, is_direct_call, output);
+ visit_instance_use(tcx, instance, is_direct_call, source, output);
}
}
tcx: TyCtxt<'tcx>,
instance: ty::Instance<'tcx>,
is_direct_call: bool,
- output: &mut Vec<MonoItem<'tcx>>,
+ source: Span,
+ output: &mut Vec<Spanned<MonoItem<'tcx>>>,
) {
debug!("visit_item_use({:?}, is_direct_call={:?})", instance, is_direct_call);
if !should_monomorphize_locally(tcx, &instance) {
ty::InstanceDef::DropGlue(_, None) => {
// Don't need to emit noop drop glue if we are calling directly.
if !is_direct_call {
- output.push(create_fn_mono_item(instance));
+ output.push(create_fn_mono_item(instance, source));
}
}
ty::InstanceDef::DropGlue(_, Some(_))
| ty::InstanceDef::Item(..)
| ty::InstanceDef::FnPtrShim(..)
| ty::InstanceDef::CloneShim(..) => {
- output.push(create_fn_mono_item(instance));
+ output.push(create_fn_mono_item(instance, source));
}
}
}
let ptr_vtable = |inner_source: Ty<'tcx>, inner_target: Ty<'tcx>| {
let param_env = ty::ParamEnv::reveal_all();
let type_has_metadata = |ty: Ty<'tcx>| -> bool {
- use rustc_span::DUMMY_SP;
if ty.is_sized(tcx.at(DUMMY_SP), param_env) {
return false;
}
}
}
-fn create_fn_mono_item(instance: Instance<'_>) -> MonoItem<'_> {
+fn create_fn_mono_item(instance: Instance<'_>, source: Span) -> Spanned<MonoItem<'_>> {
debug!("create_fn_mono_item(instance={})", instance);
- MonoItem::Fn(instance)
+ respan(source, MonoItem::Fn(instance))
}
/// Creates a `MonoItem` for each method that is referenced by the vtable for
tcx: TyCtxt<'tcx>,
trait_ty: Ty<'tcx>,
impl_ty: Ty<'tcx>,
- output: &mut Vec<MonoItem<'tcx>>,
+ source: Span,
+ output: &mut Vec<Spanned<MonoItem<'tcx>>>,
) {
assert!(
!trait_ty.needs_subst()
.unwrap()
})
.filter(|&instance| should_monomorphize_locally(tcx, &instance))
- .map(create_fn_mono_item);
+ .map(|item| create_fn_mono_item(item, source));
output.extend(methods);
}
// Also add the destructor.
- visit_drop_use(tcx, impl_ty, false, output);
+ visit_drop_use(tcx, impl_ty, false, source, output);
}
}
struct RootCollector<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
mode: MonoItemCollectionMode,
- output: &'a mut Vec<MonoItem<'tcx>>,
+ output: &'a mut Vec<Spanned<MonoItem<'tcx>>>,
entry_fn: Option<(LocalDefId, EntryFnType)>,
}
let ty = Instance::new(def_id.to_def_id(), InternalSubsts::empty())
.monomorphic_ty(self.tcx);
- visit_drop_use(self.tcx, ty, true, self.output);
+ visit_drop_use(self.tcx, ty, true, DUMMY_SP, self.output);
}
}
}
"RootCollector: ItemKind::GlobalAsm({})",
def_id_to_string(self.tcx, self.tcx.hir().local_def_id(item.hir_id))
);
- self.output.push(MonoItem::GlobalAsm(item.hir_id));
+ self.output.push(dummy_spanned(MonoItem::GlobalAsm(item.hir_id)));
}
hir::ItemKind::Static(..) => {
let def_id = self.tcx.hir().local_def_id(item.hir_id);
debug!("RootCollector: ItemKind::Static({})", def_id_to_string(self.tcx, def_id));
- self.output.push(MonoItem::Static(def_id.to_def_id()));
+ self.output.push(dummy_spanned(MonoItem::Static(def_id.to_def_id())));
}
hir::ItemKind::Const(..) => {
// const items only generate mono items if they are
debug!("RootCollector::push_if_root: found root def_id={:?}", def_id);
let instance = Instance::mono(self.tcx, def_id.to_def_id());
- self.output.push(create_fn_mono_item(instance));
+ self.output.push(create_fn_mono_item(instance, DUMMY_SP));
}
}
.unwrap()
.unwrap();
- self.output.push(create_fn_mono_item(start_instance));
+ self.output.push(create_fn_mono_item(start_instance, DUMMY_SP));
}
}
fn create_mono_items_for_default_impls<'tcx>(
tcx: TyCtxt<'tcx>,
item: &'tcx hir::Item<'tcx>,
- output: &mut Vec<MonoItem<'tcx>>,
+ output: &mut Vec<Spanned<MonoItem<'tcx>>>,
) {
match item.kind {
hir::ItemKind::Impl { ref generics, ref items, .. } => {
.unwrap()
.unwrap();
- let mono_item = create_fn_mono_item(instance);
- if mono_item.is_instantiable(tcx) && should_monomorphize_locally(tcx, &instance)
+ let mono_item = create_fn_mono_item(instance, DUMMY_SP);
+ if mono_item.node.is_instantiable(tcx)
+ && should_monomorphize_locally(tcx, &instance)
{
output.push(mono_item);
}
}
/// Scans the miri alloc in order to find function calls, closures, and drop-glue.
-fn collect_miri<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut Vec<MonoItem<'tcx>>) {
+fn collect_miri<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ alloc_id: AllocId,
+ output: &mut Vec<Spanned<MonoItem<'tcx>>>,
+) {
match tcx.global_alloc(alloc_id) {
GlobalAlloc::Static(def_id) => {
assert!(!tcx.is_thread_local_static(def_id));
let instance = Instance::mono(tcx, def_id);
if should_monomorphize_locally(tcx, &instance) {
trace!("collecting static {:?}", def_id);
- output.push(MonoItem::Static(def_id));
+ output.push(dummy_spanned(MonoItem::Static(def_id)));
}
}
GlobalAlloc::Memory(alloc) => {
GlobalAlloc::Function(fn_instance) => {
if should_monomorphize_locally(tcx, &fn_instance) {
trace!("collecting {:?} with {:#?}", alloc_id, fn_instance);
- output.push(create_fn_mono_item(fn_instance));
+ output.push(create_fn_mono_item(fn_instance, DUMMY_SP));
}
}
}
fn collect_neighbours<'tcx>(
tcx: TyCtxt<'tcx>,
instance: Instance<'tcx>,
- output: &mut Vec<MonoItem<'tcx>>,
+ output: &mut Vec<Spanned<MonoItem<'tcx>>>,
) {
debug!("collect_neighbours: {:?}", instance.def_id());
let body = tcx.instance_mir(instance.def);
fn collect_const_value<'tcx>(
tcx: TyCtxt<'tcx>,
value: ConstValue<'tcx>,
- output: &mut Vec<MonoItem<'tcx>>,
+ output: &mut Vec<Spanned<MonoItem<'tcx>>>,
) {
match value {
ConstValue::Scalar(Scalar::Ptr(ptr)) => collect_miri(tcx, ptr.alloc_id, output),
}
// Do not try creating references (#67862)
- Rvalue::Ref(_, _, place_ref) => {
- trace!("skipping Ref({:?})", place_ref);
+ Rvalue::AddressOf(_, place) | Rvalue::Ref(_, _, place) => {
+ trace!("skipping AddressOf | Ref for {:?}", place);
+
+ // This may be creating mutable references or immutable references to cells.
+ // If that happens, the pointed to value could be mutated via that reference.
+ // Since we aren't tracking references, the const propagator loses track of what
+ // value the local has right now.
+ // Thus, all locals that have their reference taken
+ // must not take part in propagation.
+ Self::remove_const(&mut self.ecx, place.local);
return None;
}
OnlyInsideOwnBlock,
/// The `Local` can be propagated into but reads cannot be propagated.
OnlyPropagateInto,
+ /// The `Local` cannot be part of propagation at all. Any statement
+ /// referencing it either for reading or writing will not get propagated.
+ NoPropagation,
}
struct CanConstProp {
// end of the block anyway, and inside the block we overwrite previous
// states as applicable.
ConstPropMode::OnlyInsideOwnBlock => {}
- other => {
+ ConstPropMode::NoPropagation => {}
+ ConstPropMode::OnlyPropagateInto => {}
+ other @ ConstPropMode::FullConstProp => {
trace!(
"local {:?} can't be propagated because of multiple assignments",
local,
| MutatingUse(MutatingUseContext::Borrow)
| MutatingUse(MutatingUseContext::AddressOf) => {
trace!("local {:?} can't be propagaged because it's used: {:?}", local, context);
- self.can_const_prop[local] = ConstPropMode::OnlyPropagateInto;
+ self.can_const_prop[local] = ConstPropMode::NoPropagation;
}
}
}
}
}
}
- if can_const_prop == ConstPropMode::OnlyInsideOwnBlock {
- trace!(
- "found local restricted to its block. Will remove it from const-prop after block is finished. Local: {:?}",
- place.local
- );
- self.locals_of_current_block.insert(place.local);
- }
-
- if can_const_prop == ConstPropMode::OnlyPropagateInto {
- trace!("can't propagate into {:?}", place);
- if place.local != RETURN_PLACE {
- Self::remove_const(&mut self.ecx, place.local);
+ match can_const_prop {
+ ConstPropMode::OnlyInsideOwnBlock => {
+ trace!(
+ "found local restricted to its block. \
+ Will remove it from const-prop after block is finished. Local: {:?}",
+ place.local
+ );
+ self.locals_of_current_block.insert(place.local);
}
+ ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => {
+ trace!("can't propagate into {:?}", place);
+ if place.local != RETURN_PLACE {
+ Self::remove_const(&mut self.ecx, place.local);
+ }
+ }
+ ConstPropMode::FullConstProp => {}
}
} else {
// Const prop failed, so erase the destination, ensuring that whatever happens
);
Self::remove_const(&mut self.ecx, place.local);
}
+ } else {
+ trace!(
+ "cannot propagate into {:?}, because the type of the local is generic.",
+ place,
+ );
+ Self::remove_const(&mut self.ecx, place.local);
}
} else {
match statement.kind {
use rustc_hir::lang_items::{GeneratorStateLangItem, PinTypeLangItem};
use rustc_index::bit_set::{BitMatrix, BitSet};
use rustc_index::vec::{Idx, IndexVec};
-use rustc_middle::mir::visit::{MutVisitor, PlaceContext};
+use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::GeneratorSubsts;
use rustc_target::abi::VariantIdx;
use rustc_target::spec::PanicStrategy;
use std::borrow::Cow;
-use std::iter;
+use std::{iter, ops};
pub struct StateTransform;
struct LivenessInfo {
/// Which locals are live across any suspension point.
- ///
- /// GeneratorSavedLocal is indexed in terms of the elements in this set;
- /// i.e. GeneratorSavedLocal::new(1) corresponds to the second local
- /// included in this set.
- live_locals: BitSet<Local>,
+ saved_locals: GeneratorSavedLocals,
/// The set of saved locals live at each suspension point.
live_locals_at_suspension_points: Vec<BitSet<GeneratorSavedLocal>>,
live_locals_at_suspension_points.push(live_locals);
}
}
+
debug!("live_locals_anywhere = {:?}", live_locals_at_any_suspension_point);
+ let saved_locals = GeneratorSavedLocals(live_locals_at_any_suspension_point);
// Renumber our liveness_map bitsets to include only the locals we are
// saving.
let live_locals_at_suspension_points = live_locals_at_suspension_points
.iter()
- .map(|live_here| renumber_bitset(&live_here, &live_locals_at_any_suspension_point))
+ .map(|live_here| saved_locals.renumber_bitset(&live_here))
.collect();
let storage_conflicts = compute_storage_conflicts(
body_ref,
- &live_locals_at_any_suspension_point,
+ &saved_locals,
always_live_locals.clone(),
requires_storage_results,
);
LivenessInfo {
- live_locals: live_locals_at_any_suspension_point,
+ saved_locals,
live_locals_at_suspension_points,
storage_conflicts,
storage_liveness: storage_liveness_map,
}
}
-/// Renumbers the items present in `stored_locals` and applies the renumbering
-/// to 'input`.
+/// The set of `Local`s that must be saved across yield points.
///
-/// For example, if `stored_locals = [1, 3, 5]`, this would be renumbered to
-/// `[0, 1, 2]`. Thus, if `input = [3, 5]` we would return `[1, 2]`.
-fn renumber_bitset(
- input: &BitSet<Local>,
- stored_locals: &BitSet<Local>,
-) -> BitSet<GeneratorSavedLocal> {
- assert!(stored_locals.superset(&input), "{:?} not a superset of {:?}", stored_locals, input);
- let mut out = BitSet::new_empty(stored_locals.count());
- for (idx, local) in stored_locals.iter().enumerate() {
- let saved_local = GeneratorSavedLocal::from(idx);
- if input.contains(local) {
- out.insert(saved_local);
+/// `GeneratorSavedLocal` is indexed in terms of the elements in this set;
+/// i.e. `GeneratorSavedLocal::new(1)` corresponds to the second local
+/// included in this set.
+struct GeneratorSavedLocals(BitSet<Local>);
+
+impl GeneratorSavedLocals {
+ /// Returns an iterator over each `GeneratorSavedLocal` along with the `Local` it corresponds
+ /// to.
+ fn iter_enumerated(&self) -> impl '_ + Iterator<Item = (GeneratorSavedLocal, Local)> {
+ self.iter().enumerate().map(|(i, l)| (GeneratorSavedLocal::from(i), l))
+ }
+
+ /// Transforms a `BitSet<Local>` that contains only locals saved across yield points to the
+ /// equivalent `BitSet<GeneratorSavedLocal>`.
+ fn renumber_bitset(&self, input: &BitSet<Local>) -> BitSet<GeneratorSavedLocal> {
+ assert!(self.superset(&input), "{:?} not a superset of {:?}", self.0, input);
+ let mut out = BitSet::new_empty(self.count());
+ for (saved_local, local) in self.iter_enumerated() {
+ if input.contains(local) {
+ out.insert(saved_local);
+ }
}
+ out
+ }
+
+ fn get(&self, local: Local) -> Option<GeneratorSavedLocal> {
+ if !self.contains(local) {
+ return None;
+ }
+
+ let idx = self.iter().take_while(|&l| l < local).count();
+ Some(GeneratorSavedLocal::new(idx))
+ }
+}
+
+impl ops::Deref for GeneratorSavedLocals {
+ type Target = BitSet<Local>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
}
- debug!("renumber_bitset({:?}, {:?}) => {:?}", input, stored_locals, out);
- out
}
/// For every saved local, looks for which locals are StorageLive at the same
/// computation; see `GeneratorLayout` for more.
fn compute_storage_conflicts(
body: &'mir Body<'tcx>,
- stored_locals: &BitSet<Local>,
+ saved_locals: &GeneratorSavedLocals,
always_live_locals: storage::AlwaysLiveLocals,
requires_storage: dataflow::Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>,
) -> BitMatrix<GeneratorSavedLocal, GeneratorSavedLocal> {
- assert_eq!(body.local_decls.len(), stored_locals.domain_size());
+ assert_eq!(body.local_decls.len(), saved_locals.domain_size());
debug!("compute_storage_conflicts({:?})", body.span);
debug!("always_live = {:?}", always_live_locals);
// Locals that are always live or ones that need to be stored across
// suspension points are not eligible for overlap.
let mut ineligible_locals = always_live_locals.into_inner();
- ineligible_locals.intersect(stored_locals);
+ ineligible_locals.intersect(saved_locals);
// Compute the storage conflicts for all eligible locals.
let mut visitor = StorageConflictVisitor {
body,
- stored_locals: &stored_locals,
+ saved_locals: &saved_locals,
local_conflicts: BitMatrix::from_row_n(&ineligible_locals, body.local_decls.len()),
};
// However, in practice these bitsets are not usually large. The layout code
// also needs to keep track of how many conflicts each local has, so it's
// simpler to keep it this way for now.
- let mut storage_conflicts = BitMatrix::new(stored_locals.count(), stored_locals.count());
- for (idx_a, local_a) in stored_locals.iter().enumerate() {
- let saved_local_a = GeneratorSavedLocal::new(idx_a);
+ let mut storage_conflicts = BitMatrix::new(saved_locals.count(), saved_locals.count());
+ for (saved_local_a, local_a) in saved_locals.iter_enumerated() {
if ineligible_locals.contains(local_a) {
// Conflicts with everything.
storage_conflicts.insert_all_into_row(saved_local_a);
} else {
// Keep overlap information only for stored locals.
- for (idx_b, local_b) in stored_locals.iter().enumerate() {
- let saved_local_b = GeneratorSavedLocal::new(idx_b);
+ for (saved_local_b, local_b) in saved_locals.iter_enumerated() {
if local_conflicts.contains(local_a, local_b) {
storage_conflicts.insert(saved_local_a, saved_local_b);
}
struct StorageConflictVisitor<'mir, 'tcx, 's> {
body: &'mir Body<'tcx>,
- stored_locals: &'s BitSet<Local>,
+ saved_locals: &'s GeneratorSavedLocals,
// FIXME(tmandry): Consider using sparse bitsets here once we have good
// benchmarks for generators.
local_conflicts: BitMatrix<Local, Local>,
}
let mut eligible_storage_live = flow_state.clone();
- eligible_storage_live.intersect(&self.stored_locals);
+ eligible_storage_live.intersect(&self.saved_locals);
for local in eligible_storage_live.iter() {
self.local_conflicts.union_row_with(&eligible_storage_live, local);
}
}
-/// Validates the typeck view of the generator against the actual set of types retained between
+/// Validates the typeck view of the generator against the actual set of types saved between
/// yield points.
fn sanitize_witness<'tcx>(
tcx: TyCtxt<'tcx>,
did: DefId,
witness: Ty<'tcx>,
upvars: &Vec<Ty<'tcx>>,
- retained: &BitSet<Local>,
+ saved_locals: &GeneratorSavedLocals,
) {
let allowed_upvars = tcx.erase_regions(upvars);
let allowed = match witness.kind {
let param_env = tcx.param_env(did);
for (local, decl) in body.local_decls.iter_enumerated() {
- // Ignore locals which are internal or not retained between yields.
- if !retained.contains(local) || decl.internal {
+ // Ignore locals which are internal or not saved between yields.
+ if !saved_locals.contains(local) || decl.internal {
continue;
}
let decl_ty = tcx.normalize_erasing_regions(param_env, decl.ty);
}
fn compute_layout<'tcx>(
- tcx: TyCtxt<'tcx>,
- source: MirSource<'tcx>,
- upvars: &Vec<Ty<'tcx>>,
- interior: Ty<'tcx>,
- always_live_locals: &storage::AlwaysLiveLocals,
- movable: bool,
+ liveness: LivenessInfo,
body: &mut Body<'tcx>,
) -> (
FxHashMap<Local, (Ty<'tcx>, VariantIdx, usize)>,
GeneratorLayout<'tcx>,
IndexVec<BasicBlock, Option<BitSet<Local>>>,
) {
- // Use a liveness analysis to compute locals which are live across a suspension point
let LivenessInfo {
- live_locals,
+ saved_locals,
live_locals_at_suspension_points,
storage_conflicts,
storage_liveness,
- } = locals_live_across_suspend_points(tcx, body, source, always_live_locals, movable);
-
- sanitize_witness(tcx, body, source.def_id(), interior, upvars, &live_locals);
+ } = liveness;
// Gather live local types and their indices.
let mut locals = IndexVec::<GeneratorSavedLocal, _>::new();
let mut tys = IndexVec::<GeneratorSavedLocal, _>::new();
- for (idx, local) in live_locals.iter().enumerate() {
+ for (saved_local, local) in saved_locals.iter_enumerated() {
locals.push(local);
tys.push(body.local_decls[local].ty);
- debug!("generator saved local {:?} => {:?}", GeneratorSavedLocal::from(idx), local);
+ debug!("generator saved local {:?} => {:?}", saved_local, local);
}
// Leave empty variants for the UNRESUMED, RETURNED, and POISONED states.
let always_live_locals = storage::AlwaysLiveLocals::new(&body);
+ let liveness_info =
+ locals_live_across_suspend_points(tcx, body, source, &always_live_locals, movable);
+
+ sanitize_witness(tcx, body, def_id, interior, &upvars, &liveness_info.saved_locals);
+
+ if tcx.sess.opts.debugging_opts.validate_mir {
+ let mut vis = EnsureGeneratorFieldAssignmentsNeverAlias {
+ assigned_local: None,
+ saved_locals: &liveness_info.saved_locals,
+ storage_conflicts: &liveness_info.storage_conflicts,
+ };
+
+ vis.visit_body(body);
+ }
+
// Extract locals which are live across suspension point into `layout`
// `remap` gives a mapping from local indices onto generator struct indices
// `storage_liveness` tells us which locals have live storage at suspension points
- let (remap, layout, storage_liveness) =
- compute_layout(tcx, source, &upvars, interior, &always_live_locals, movable, body);
+ let (remap, layout, storage_liveness) = compute_layout(liveness_info, body);
let can_return = can_return(tcx, body);
create_generator_resume_function(tcx, transform, source, body, can_return);
}
}
+
+/// Looks for any assignments between locals (e.g., `_4 = _5`) that will both be converted to fields
+/// in the generator state machine but whose storage is not marked as conflicting
+///
+/// Validation needs to happen immediately *before* `TransformVisitor` is invoked, not after.
+///
+/// This condition would arise when the assignment is the last use of `_5` but the initial
+/// definition of `_4` if we weren't extra careful to mark all locals used inside a statement as
+/// conflicting. Non-conflicting generator saved locals may be stored at the same location within
+/// the generator state machine, which would result in ill-formed MIR: the left-hand and right-hand
+/// sides of an assignment may not alias. This caused a miscompilation in [#73137].
+///
+/// [#73137]: https://github.com/rust-lang/rust/issues/73137
+struct EnsureGeneratorFieldAssignmentsNeverAlias<'a> {
+ saved_locals: &'a GeneratorSavedLocals,
+ storage_conflicts: &'a BitMatrix<GeneratorSavedLocal, GeneratorSavedLocal>,
+ assigned_local: Option<GeneratorSavedLocal>,
+}
+
+impl EnsureGeneratorFieldAssignmentsNeverAlias<'_> {
+ fn saved_local_for_direct_place(&self, place: Place<'_>) -> Option<GeneratorSavedLocal> {
+ if place.is_indirect() {
+ return None;
+ }
+
+ self.saved_locals.get(place.local)
+ }
+
+ fn check_assigned_place(&mut self, place: Place<'tcx>, f: impl FnOnce(&mut Self)) {
+ if let Some(assigned_local) = self.saved_local_for_direct_place(place) {
+ assert!(self.assigned_local.is_none(), "`check_assigned_place` must not recurse");
+
+ self.assigned_local = Some(assigned_local);
+ f(self);
+ self.assigned_local = None;
+ }
+ }
+}
+
+impl Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> {
+ fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
+ let lhs = match self.assigned_local {
+ Some(l) => l,
+ None => {
+ // This visitor only invokes `visit_place` for the right-hand side of an assignment
+ // and only after setting `self.assigned_local`. However, the default impl of
+ // `Visitor::super_body` may call `visit_place` with a `NonUseContext` for places
+ // with debuginfo. Ignore them here.
+ assert!(!context.is_use());
+ return;
+ }
+ };
+
+ let rhs = match self.saved_local_for_direct_place(*place) {
+ Some(l) => l,
+ None => return,
+ };
+
+ if !self.storage_conflicts.contains(lhs, rhs) {
+ bug!(
+ "Assignment between generator saved locals whose storage is not \
+ marked as conflicting: {:?}: {:?} = {:?}",
+ location,
+ lhs,
+ rhs,
+ );
+ }
+ }
+
+ fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
+ match &statement.kind {
+ StatementKind::Assign(box (lhs, rhs)) => {
+ self.check_assigned_place(*lhs, |this| this.visit_rvalue(rhs, location));
+ }
+
+ // FIXME: Does `llvm_asm!` have any aliasing requirements?
+ StatementKind::LlvmInlineAsm(_) => {}
+
+ StatementKind::FakeRead(..)
+ | StatementKind::SetDiscriminant { .. }
+ | StatementKind::StorageLive(_)
+ | StatementKind::StorageDead(_)
+ | StatementKind::Retag(..)
+ | StatementKind::AscribeUserType(..)
+ | StatementKind::Nop => {}
+ }
+ }
+
+ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
+ // Checking for aliasing in terminators is probably overkill, but until we have actual
+ // semantics, we should be conservative here.
+ match &terminator.kind {
+ TerminatorKind::Call {
+ func,
+ args,
+ destination: Some((dest, _)),
+ cleanup: _,
+ from_hir_call: _,
+ fn_span: _,
+ } => {
+ self.check_assigned_place(*dest, |this| {
+ this.visit_operand(func, location);
+ for arg in args {
+ this.visit_operand(arg, location);
+ }
+ });
+ }
+
+ TerminatorKind::Yield { value, resume: _, resume_arg, drop: _ } => {
+ self.check_assigned_place(*resume_arg, |this| this.visit_operand(value, location));
+ }
+
+ // FIXME: Does `asm!` have any aliasing requirements?
+ TerminatorKind::InlineAsm { .. } => {}
+
+ TerminatorKind::Call { .. }
+ | TerminatorKind::Goto { .. }
+ | TerminatorKind::SwitchInt { .. }
+ | TerminatorKind::Resume
+ | TerminatorKind::Abort
+ | TerminatorKind::Return
+ | TerminatorKind::Unreachable
+ | TerminatorKind::Drop { .. }
+ | TerminatorKind::DropAndReplace { .. }
+ | TerminatorKind::Assert { .. }
+ | TerminatorKind::GeneratorDrop
+ | TerminatorKind::FalseEdge { .. }
+ | TerminatorKind::FalseUnwind { .. } => {}
+ }
+ }
+}
use crate::transform::{MirPass, MirSource};
use crate::util::patch::MirPatch;
+use rustc_data_structures::fingerprint::Fingerprint;
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_hir::lang_items;
-use rustc_middle::mir::interpret::Scalar;
-use rustc_middle::mir::*;
+use rustc_middle::hir;
+use rustc_middle::ich::StableHashingContext;
+use rustc_middle::mir::interpret::{ConstValue, Scalar};
+use rustc_middle::mir::{
+ self, traversal, BasicBlock, BasicBlockData, CoverageData, Operand, Place, SourceInfo,
+ StatementKind, Terminator, TerminatorKind, START_BLOCK,
+};
use rustc_middle::ty;
+use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
+use rustc_middle::ty::{ConstKind, FnDef};
use rustc_span::def_id::DefId;
use rustc_span::Span;
/// the intrinsic llvm.instrprof.increment.
pub struct InstrumentCoverage;
+/// The `query` provider for `CoverageData`, requested by `codegen_intrinsic_call()` when
+/// constructing the arguments for `llvm.instrprof.increment`.
+pub(crate) fn provide(providers: &mut Providers<'_>) {
+ providers.coverage_data = |tcx, def_id| {
+ let mir_body = tcx.optimized_mir(def_id);
+ // FIXME(richkadel): The current implementation assumes the MIR for the given DefId
+ // represents a single function. Validate and/or correct if inlining and/or monomorphization
+ // invalidates these assumptions.
+ let count_code_region_fn =
+ tcx.require_lang_item(lang_items::CountCodeRegionFnLangItem, None);
+ let mut num_counters: u32 = 0;
+ // The `num_counters` argument to `llvm.instrprof.increment` is the number of injected
+ // counters, with each counter having an index from `0..num_counters-1`. MIR optimization
+ // may split and duplicate some BasicBlock sequences. Simply counting the calls may not
+ // not work; but computing the num_counters by adding `1` to the highest index (for a given
+ // instrumented function) is valid.
+ for (_, data) in traversal::preorder(mir_body) {
+ if let Some(terminator) = &data.terminator {
+ if let TerminatorKind::Call { func: Operand::Constant(func), args, .. } =
+ &terminator.kind
+ {
+ if let FnDef(called_fn_def_id, _) = func.literal.ty.kind {
+ if called_fn_def_id == count_code_region_fn {
+ if let Operand::Constant(constant) =
+ args.get(0).expect("count_code_region has at least one arg")
+ {
+ if let ConstKind::Value(ConstValue::Scalar(value)) =
+ constant.literal.val
+ {
+ let index = value
+ .to_u32()
+ .expect("count_code_region index at arg0 is u32");
+ num_counters = std::cmp::max(num_counters, index + 1);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ let hash = if num_counters > 0 { hash_mir_source(tcx, def_id) } else { 0 };
+ CoverageData { num_counters, hash }
+ };
+}
+
+struct Instrumentor<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ num_counters: u32,
+}
+
impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, mir_body: &mut mir::Body<'tcx>) {
if tcx.sess.opts.debugging_opts.instrument_coverage {
- debug!("instrumenting {:?}", src.def_id());
- instrument_coverage(tcx, body);
+ // If the InstrumentCoverage pass is called on promoted MIRs, skip them.
+ // See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601
+ if src.promoted.is_none() {
+ debug!(
+ "instrumenting {:?}, span: {}",
+ src.def_id(),
+ tcx.sess.source_map().span_to_string(mir_body.span)
+ );
+ Instrumentor::new(tcx).inject_counters(mir_body);
+ }
}
}
}
-// The first counter (start of the function) is index zero.
-const INIT_FUNCTION_COUNTER: u32 = 0;
-
-/// Injects calls to placeholder function `count_code_region()`.
-// FIXME(richkadel): As a first step, counters are only injected at the top of each function.
-// The complete solution will inject counters at each conditional code branch.
-pub fn instrument_coverage<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
- let span = body.span.shrink_to_lo();
-
- let count_code_region_fn = function_handle(
- tcx,
- tcx.require_lang_item(lang_items::CountCodeRegionFnLangItem, None),
- span,
- );
- let counter_index = Operand::const_from_scalar(
- tcx,
- tcx.types.u32,
- Scalar::from_u32(INIT_FUNCTION_COUNTER),
- span,
- );
-
- let mut patch = MirPatch::new(body);
-
- let new_block = patch.new_block(placeholder_block(SourceInfo::outermost(body.span)));
- let next_block = START_BLOCK;
-
- let temp = patch.new_temp(tcx.mk_unit(), body.span);
- patch.patch_terminator(
- new_block,
- TerminatorKind::Call {
- func: count_code_region_fn,
- args: vec![counter_index],
- // new_block will swapped with the next_block, after applying patch
- destination: Some((Place::from(temp), new_block)),
- cleanup: None,
- from_hir_call: false,
- fn_span: span,
- },
- );
-
- patch.add_statement(new_block.start_location(), StatementKind::StorageLive(temp));
- patch.add_statement(next_block.start_location(), StatementKind::StorageDead(temp));
-
- patch.apply(body);
-
- // To insert the `new_block` in front of the first block in the counted branch (for example,
- // the START_BLOCK, at the top of the function), just swap the indexes, leaving the rest of the
- // graph unchanged.
- body.basic_blocks_mut().swap(next_block, new_block);
+impl<'tcx> Instrumentor<'tcx> {
+ fn new(tcx: TyCtxt<'tcx>) -> Self {
+ Self { tcx, num_counters: 0 }
+ }
+
+ fn next_counter(&mut self) -> u32 {
+ let next = self.num_counters;
+ self.num_counters += 1;
+ next
+ }
+
+ fn inject_counters(&mut self, mir_body: &mut mir::Body<'tcx>) {
+ // FIXME(richkadel): As a first step, counters are only injected at the top of each
+ // function. The complete solution will inject counters at each conditional code branch.
+ let top_of_function = START_BLOCK;
+ let entire_function = mir_body.span;
+
+ self.inject_counter(mir_body, top_of_function, entire_function);
+ }
+
+ fn inject_counter(
+ &mut self,
+ mir_body: &mut mir::Body<'tcx>,
+ next_block: BasicBlock,
+ code_region: Span,
+ ) {
+ let injection_point = code_region.shrink_to_lo();
+
+ let count_code_region_fn = function_handle(
+ self.tcx,
+ self.tcx.require_lang_item(lang_items::CountCodeRegionFnLangItem, None),
+ injection_point,
+ );
+ let counter_index = Operand::const_from_scalar(
+ self.tcx,
+ self.tcx.types.u32,
+ Scalar::from_u32(self.next_counter()),
+ injection_point,
+ );
+
+ let mut patch = MirPatch::new(mir_body);
+
+ let temp = patch.new_temp(self.tcx.mk_unit(), code_region);
+ let new_block = patch.new_block(placeholder_block(code_region));
+ patch.patch_terminator(
+ new_block,
+ TerminatorKind::Call {
+ func: count_code_region_fn,
+ args: vec![counter_index],
+ // new_block will swapped with the next_block, after applying patch
+ destination: Some((Place::from(temp), new_block)),
+ cleanup: None,
+ from_hir_call: false,
+ fn_span: injection_point,
+ },
+ );
+
+ patch.add_statement(new_block.start_location(), StatementKind::StorageLive(temp));
+ patch.add_statement(next_block.start_location(), StatementKind::StorageDead(temp));
+
+ patch.apply(mir_body);
+
+ // To insert the `new_block` in front of the first block in the counted branch (the
+ // `next_block`), just swap the indexes, leaving the rest of the graph unchanged.
+ mir_body.basic_blocks_mut().swap(next_block, new_block);
+ }
}
fn function_handle<'tcx>(tcx: TyCtxt<'tcx>, fn_def_id: DefId, span: Span) -> Operand<'tcx> {
Operand::function_handle(tcx, fn_def_id, substs, span)
}
-fn placeholder_block<'tcx>(source_info: SourceInfo) -> BasicBlockData<'tcx> {
+fn placeholder_block(span: Span) -> BasicBlockData<'tcx> {
BasicBlockData {
statements: vec![],
terminator: Some(Terminator {
- source_info,
+ source_info: SourceInfo::outermost(span),
// this gets overwritten by the counter Call
kind: TerminatorKind::Unreachable,
}),
is_cleanup: false,
}
}
+
+fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> u64 {
+ let hir_node = tcx.hir().get_if_local(def_id).expect("DefId is local");
+ let fn_body_id = hir::map::associated_body(hir_node).expect("HIR node is a function with body");
+ let hir_body = tcx.hir().body(fn_body_id);
+ let mut hcx = tcx.create_no_span_stable_hashing_context();
+ hash(&mut hcx, &hir_body.value).to_smaller_hash()
+}
+
+fn hash(
+ hcx: &mut StableHashingContext<'tcx>,
+ node: &impl HashStable<StableHashingContext<'tcx>>,
+) -> Fingerprint {
+ let mut stable_hasher = StableHasher::new();
+ node.hash_stable(hcx, &mut stable_hasher);
+ stable_hasher.finish()
+}
promoted_mir,
..*providers
};
+ instrument_coverage::provide(providers);
}
fn is_mir_available(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
/// This restriction shouldn't be an issue in practice,
/// since this function is used to record the tokens for
/// a parsed AST item, which always has matching delimiters.
- fn collect_tokens<R>(
+ pub fn collect_tokens<R>(
&mut self,
f: impl FnOnce(&mut Self) -> PResult<'a, R>,
) -> PResult<'a, (R, TokenStream)> {
use rustc_span::symbol::sym;
use rustc_span::Span;
-fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target {
+pub(crate) fn target_from_impl_item<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_item: &hir::ImplItem<'_>,
+) -> Target {
match impl_item.kind {
hir::ImplItemKind::Const(..) => Target::AssocConst,
hir::ImplItemKind::Fn(..) => {
//! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`.
//! * Functions called by the compiler itself.
+use crate::check_attr::target_from_impl_item;
use crate::weak_lang_items;
use rustc_middle::middle::cstore::ExternCrate;
use rustc_middle::ty::TyCtxt;
+use rustc_ast::ast::Attribute;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::lang_items::{extract, ITEM_REFS};
-use rustc_hir::{LangItem, LanguageItems, Target};
+use rustc_hir::{HirId, LangItem, LanguageItems, Target};
use rustc_middle::ty::query::Providers;
impl ItemLikeVisitor<'v> for LanguageItemCollector<'tcx> {
fn visit_item(&mut self, item: &hir::Item<'_>) {
- if let Some((value, span)) = extract(&item.attrs) {
- let actual_target = Target::from_item(item);
+ self.check_for_lang(Target::from_item(item), item.hir_id, item.attrs)
+ }
+
+ fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) {
+ self.check_for_lang(
+ Target::from_trait_item(trait_item),
+ trait_item.hir_id,
+ trait_item.attrs,
+ )
+ }
+
+ fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) {
+ self.check_for_lang(
+ target_from_impl_item(self.tcx, impl_item),
+ impl_item.hir_id,
+ impl_item.attrs,
+ )
+ }
+}
+
+impl LanguageItemCollector<'tcx> {
+ fn new(tcx: TyCtxt<'tcx>) -> LanguageItemCollector<'tcx> {
+ LanguageItemCollector { tcx, items: LanguageItems::new() }
+ }
+
+ fn check_for_lang(&mut self, actual_target: Target, hir_id: HirId, attrs: &[Attribute]) {
+ if let Some((value, span)) = extract(&attrs) {
match ITEM_REFS.get(&*value.as_str()).cloned() {
// Known lang item with attribute on correct target.
Some((item_index, expected_target)) if actual_target == expected_target => {
- let def_id = self.tcx.hir().local_def_id(item.hir_id);
+ let def_id = self.tcx.hir().local_def_id(hir_id);
self.collect_item(item_index, def_id.to_def_id());
}
// Known lang item with attribute on incorrect target.
}
}
- fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {
- // At present, lang items are always items, not trait items.
- }
-
- fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {
- // At present, lang items are always items, not impl items.
- }
-}
-
-impl LanguageItemCollector<'tcx> {
- fn new(tcx: TyCtxt<'tcx>) -> LanguageItemCollector<'tcx> {
- LanguageItemCollector { tcx, items: LanguageItems::new() }
- }
-
fn collect_item(&mut self, item_index: usize, item_def_id: DefId) {
// Check for duplicates.
if let Some(original_def_id) = self.items.items[item_index] {
rustc_expand = { path = "../librustc_expand" }
rustc_feature = { path = "../librustc_feature" }
rustc_hir = { path = "../librustc_hir" }
+rustc_index = { path = "../librustc_index" }
rustc_metadata = { path = "../librustc_metadata" }
rustc_session = { path = "../librustc_session" }
rustc_span = { path = "../librustc_span" }
use rustc_ast::ast::{AssocItem, AssocItemKind, MetaItemKind, StmtKind};
use rustc_ast::token::{self, Token};
use rustc_ast::visit::{self, AssocCtxt, Visitor};
+use rustc_ast_lowering::Resolver as ResolverAstLowering;
use rustc_attr as attr;
use rustc_data_structures::sync::Lrc;
use rustc_errors::{struct_span_err, Applicability};
fragment: &AstFragment,
parent_scope: ParentScope<'a>,
) -> MacroRulesScope<'a> {
- collect_definitions(&mut self.definitions, fragment, parent_scope.expansion);
+ collect_definitions(self, fragment, parent_scope.expansion);
let mut visitor = BuildReducedGraphVisitor { r: self, parent_scope };
fragment.visit_with(&mut visitor);
visitor.parent_scope.macro_rules
} else if orig_name == Some(kw::SelfLower) {
self.r.graph_root
} else {
- let def_id = self.r.definitions.local_def_id(item.id);
+ let def_id = self.r.local_def_id(item.id);
let crate_id =
- self.r.crate_loader.process_extern_crate(item, &self.r.definitions);
+ self.r.crate_loader.process_extern_crate(item, &self.r.definitions, def_id);
self.r.extern_crate_map.insert(def_id, crate_id);
self.r.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX })
};
ItemKind::Mod(..) if ident.name == kw::Invalid => {} // Crate root
ItemKind::Mod(..) => {
- let def_id = self.r.definitions.local_def_id(item.id);
+ let def_id = self.r.local_def_id(item.id);
let module_kind = ModuleKind::Def(DefKind::Mod, def_id.to_def_id(), ident.name);
let module = self.r.arenas.alloc_module(ModuleData {
no_implicit_prelude: parent.no_implicit_prelude || {
// These items live in the value namespace.
ItemKind::Static(..) => {
- let res =
- Res::Def(DefKind::Static, self.r.definitions.local_def_id(item.id).to_def_id());
+ let res = Res::Def(DefKind::Static, self.r.local_def_id(item.id).to_def_id());
self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion));
}
ItemKind::Const(..) => {
- let res =
- Res::Def(DefKind::Const, self.r.definitions.local_def_id(item.id).to_def_id());
+ let res = Res::Def(DefKind::Const, self.r.local_def_id(item.id).to_def_id());
self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion));
}
ItemKind::Fn(..) => {
- let res =
- Res::Def(DefKind::Fn, self.r.definitions.local_def_id(item.id).to_def_id());
+ let res = Res::Def(DefKind::Fn, self.r.local_def_id(item.id).to_def_id());
self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion));
// Functions introducing procedural macros reserve a slot
// These items live in the type namespace.
ItemKind::TyAlias(..) => {
- let res = Res::Def(
- DefKind::TyAlias,
- self.r.definitions.local_def_id(item.id).to_def_id(),
- );
+ let res = Res::Def(DefKind::TyAlias, self.r.local_def_id(item.id).to_def_id());
self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion));
}
ItemKind::Enum(_, _) => {
- let def_id = self.r.definitions.local_def_id(item.id).to_def_id();
+ let def_id = self.r.local_def_id(item.id).to_def_id();
self.r.variant_vis.insert(def_id, vis);
let module_kind = ModuleKind::Def(DefKind::Enum, def_id, ident.name);
let module = self.r.new_module(
}
ItemKind::TraitAlias(..) => {
- let res = Res::Def(
- DefKind::TraitAlias,
- self.r.definitions.local_def_id(item.id).to_def_id(),
- );
+ let res = Res::Def(DefKind::TraitAlias, self.r.local_def_id(item.id).to_def_id());
self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion));
}
// These items live in both the type and value namespaces.
ItemKind::Struct(ref vdata, _) => {
// Define a name in the type namespace.
- let def_id = self.r.definitions.local_def_id(item.id).to_def_id();
+ let def_id = self.r.local_def_id(item.id).to_def_id();
let res = Res::Def(DefKind::Struct, def_id);
self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion));
}
let ctor_res = Res::Def(
DefKind::Ctor(CtorOf::Struct, CtorKind::from_ast(vdata)),
- self.r.definitions.local_def_id(ctor_node_id).to_def_id(),
+ self.r.local_def_id(ctor_node_id).to_def_id(),
);
self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, sp, expansion));
self.r.struct_constructors.insert(def_id, (ctor_res, ctor_vis));
}
ItemKind::Union(ref vdata, _) => {
- let def_id = self.r.definitions.local_def_id(item.id).to_def_id();
+ let def_id = self.r.local_def_id(item.id).to_def_id();
let res = Res::Def(DefKind::Union, def_id);
self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion));
}
ItemKind::Trait(..) => {
- let def_id = self.r.definitions.local_def_id(item.id).to_def_id();
+ let def_id = self.r.local_def_id(item.id).to_def_id();
// Add all the items within to a new module.
let module_kind = ModuleKind::Def(DefKind::Trait, def_id, ident.name);
/// Constructs the reduced graph for one foreign item.
fn build_reduced_graph_for_foreign_item(&mut self, item: &ForeignItem) {
let (res, ns) = match item.kind {
- ForeignItemKind::Fn(..) => (
- Res::Def(DefKind::Fn, self.r.definitions.local_def_id(item.id).to_def_id()),
- ValueNS,
- ),
- ForeignItemKind::Static(..) => (
- Res::Def(DefKind::Static, self.r.definitions.local_def_id(item.id).to_def_id()),
- ValueNS,
- ),
- ForeignItemKind::TyAlias(..) => (
- Res::Def(DefKind::ForeignTy, self.r.definitions.local_def_id(item.id).to_def_id()),
- TypeNS,
- ),
+ ForeignItemKind::Fn(..) => {
+ (Res::Def(DefKind::Fn, self.r.local_def_id(item.id).to_def_id()), ValueNS)
+ }
+ ForeignItemKind::Static(..) => {
+ (Res::Def(DefKind::Static, self.r.local_def_id(item.id).to_def_id()), ValueNS)
+ }
+ ForeignItemKind::TyAlias(..) => {
+ (Res::Def(DefKind::ForeignTy, self.r.local_def_id(item.id).to_def_id()), TypeNS)
+ }
ForeignItemKind::MacCall(_) => unreachable!(),
};
let parent = self.parent_scope.module;
fn define_macro(&mut self, item: &ast::Item) -> MacroRulesScope<'a> {
let parent_scope = self.parent_scope;
let expansion = parent_scope.expansion;
- let def_id = self.r.definitions.local_def_id(item.id);
+ let def_id = self.r.local_def_id(item.id);
let (ext, ident, span, macro_rules) = match &item.kind {
ItemKind::MacroDef(def) => {
let ext = Lrc::new(self.r.compile_macro(item, self.r.session.edition()));
}
// Add the item to the trait info.
- let item_def_id = self.r.definitions.local_def_id(item.id).to_def_id();
+ let item_def_id = self.r.local_def_id(item.id).to_def_id();
let (res, ns) = match item.kind {
AssocItemKind::Const(..) => (Res::Def(DefKind::AssocConst, item_def_id), ValueNS),
AssocItemKind::Fn(_, ref sig, _, _) => {
let ident = variant.ident;
// Define a name in the type namespace.
- let def_id = self.r.definitions.local_def_id(variant.id).to_def_id();
+ let def_id = self.r.local_def_id(variant.id).to_def_id();
let res = Res::Def(DefKind::Variant, def_id);
self.r.define(parent, ident, TypeNS, (res, vis, variant.span, expn_id));
// It's ok to use the variant's id as a ctor id since an
// error will be reported on any use of such resolution anyway.
let ctor_node_id = variant.data.ctor_id().unwrap_or(variant.id);
- let ctor_def_id = self.r.definitions.local_def_id(ctor_node_id).to_def_id();
+ let ctor_def_id = self.r.local_def_id(ctor_node_id).to_def_id();
let ctor_kind = CtorKind::from_ast(&variant.data);
let ctor_res = Res::Def(DefKind::Ctor(CtorOf::Variant, ctor_kind), ctor_def_id);
self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, variant.span, expn_id));
use rustc_ast::ast;
use rustc_ast::node_id::NodeMap;
use rustc_ast::visit::{self, Visitor};
+use rustc_ast_lowering::Resolver as ResolverAstLowering;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::pluralize;
use rustc_middle::ty;
fn check_import(&mut self, id: ast::NodeId) {
let mut used = false;
self.r.per_ns(|this, ns| used |= this.used_imports.contains(&(id, ns)));
- let def_id = self.r.definitions.local_def_id(id);
+ let def_id = self.r.local_def_id(id);
if !used {
if self.r.maybe_unused_trait_imports.contains(&def_id) {
// Check later.
}
}
ImportKind::ExternCrate { .. } => {
- let def_id = self.definitions.local_def_id(import.id);
+ let def_id = self.local_def_id(import.id);
self.maybe_unused_extern_crates.push((def_id, import.span));
}
ImportKind::MacroUse => {
+use crate::Resolver;
use log::debug;
use rustc_ast::ast::*;
use rustc_ast::token::{self, Token};
use rustc_ast::visit::{self, FnKind};
use rustc_ast::walk_list;
+use rustc_ast_lowering::Resolver as ResolverAstLowering;
use rustc_expand::expand::AstFragment;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::definitions::*;
use rustc_span::Span;
crate fn collect_definitions(
- definitions: &mut Definitions,
+ resolver: &mut Resolver<'_>,
fragment: &AstFragment,
expansion: ExpnId,
) {
- let parent_def = definitions.invocation_parent(expansion);
- fragment.visit_with(&mut DefCollector { definitions, parent_def, expansion });
+ let parent_def = resolver.invocation_parents[&expansion];
+ fragment.visit_with(&mut DefCollector { resolver, parent_def, expansion });
}
/// Creates `DefId`s for nodes in the AST.
-struct DefCollector<'a> {
- definitions: &'a mut Definitions,
+struct DefCollector<'a, 'b> {
+ resolver: &'a mut Resolver<'b>,
parent_def: LocalDefId,
expansion: ExpnId,
}
-impl<'a> DefCollector<'a> {
+impl<'a, 'b> DefCollector<'a, 'b> {
fn create_def(&mut self, node_id: NodeId, data: DefPathData, span: Span) -> LocalDefId {
let parent_def = self.parent_def;
debug!("create_def(node_id={:?}, data={:?}, parent_def={:?})", node_id, data, parent_def);
- self.definitions.create_def_with_parent(parent_def, node_id, data, self.expansion, span)
+ self.resolver.create_def(parent_def, node_id, data, self.expansion, span)
}
fn with_parent<F: FnOnce(&mut Self)>(&mut self, parent_def: LocalDefId, f: F) {
let index = |this: &Self| {
index.unwrap_or_else(|| {
let node_id = NodeId::placeholder_from_expn_id(this.expansion);
- this.definitions.placeholder_field_index(node_id)
+ this.resolver.placeholder_field_indices[&node_id]
})
};
if field.is_placeholder {
- self.definitions.set_placeholder_field_index(field.id, index(self));
+ let old_index = self.resolver.placeholder_field_indices.insert(field.id, index(self));
+ assert!(old_index.is_none(), "placeholder field index is reset for a node ID");
self.visit_macro_invoc(field.id);
} else {
let name = field.ident.map_or_else(|| sym::integer(index(self)), |ident| ident.name);
}
fn visit_macro_invoc(&mut self, id: NodeId) {
- self.definitions.set_invocation_parent(id.placeholder_to_expn_id(), self.parent_def);
+ let old_parent =
+ self.resolver.invocation_parents.insert(id.placeholder_to_expn_id(), self.parent_def);
+ assert!(old_parent.is_none(), "parent `LocalDefId` is reset for an invocation");
}
}
-impl<'a> visit::Visitor<'a> for DefCollector<'a> {
+impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> {
fn visit_item(&mut self, i: &'a Item) {
debug!("visit_item: {:?}", i);
match outer_res {
Res::SelfTy(maybe_trait_defid, maybe_impl_defid) => {
if let Some(impl_span) =
- maybe_impl_defid.and_then(|def_id| self.definitions.opt_span(def_id))
+ maybe_impl_defid.and_then(|def_id| self.opt_span(def_id))
{
err.span_label(
reduce_impl_span_to_impl_keyword(sm, impl_span),
return err;
}
Res::Def(DefKind::TyParam, def_id) => {
- if let Some(span) = self.definitions.opt_span(def_id) {
+ if let Some(span) = self.opt_span(def_id) {
err.span_label(span, "type parameter from outer function");
}
}
Res::Def(DefKind::ConstParam, def_id) => {
- if let Some(span) = self.definitions.opt_span(def_id) {
+ if let Some(span) = self.opt_span(def_id) {
err.span_label(span, "const parameter from outer function");
}
}
let not_local_module = crate_name.name != kw::Crate;
let mut worklist =
vec![(start_module, Vec::<ast::PathSegment>::new(), true, not_local_module)];
+ let mut worklist_via_import = vec![];
- while let Some((in_module, path_segments, accessible, in_module_is_extern)) = worklist.pop()
+ while let Some((in_module, path_segments, accessible, in_module_is_extern)) =
+ match worklist.pop() {
+ None => worklist_via_import.pop(),
+ Some(x) => Some(x),
+ }
{
// We have to visit module children in deterministic order to avoid
// instabilities in reported imports (#43552).
in_module.for_each_child(self, |this, ident, ns, name_binding| {
- // avoid imports entirely
- if name_binding.is_import() && !name_binding.is_extern_crate() {
- return;
- }
-
- // avoid non-importable candidates as well
+ // avoid non-importable candidates
if !name_binding.is_importable() {
return;
}
return;
}
+ let via_import = name_binding.is_import() && !name_binding.is_extern_crate();
+
+ // There is an assumption elsewhere that paths of variants are in the enum's
+ // declaration and not imported. With this assumption, the variant component is
+ // chopped and the rest of the path is assumed to be the enum's own path. For
+ // errors where a variant is used as the type instead of the enum, this causes
+ // funny looking invalid suggestions, i.e `foo` instead of `foo::MyEnum`.
+ if via_import && name_binding.is_possibly_imported_variant() {
+ return;
+ }
+
// collect results based on the filter function
// avoid suggesting anything from the same module in which we are resolving
if ident.name == lookup_ident.name
let is_extern = in_module_is_extern || name_binding.is_extern_crate();
// add the module to the lookup
if seen_modules.insert(module.def_id().unwrap()) {
- worklist.push((module, path_segments, child_accessible, is_extern));
+ if via_import { &mut worklist_via_import } else { &mut worklist }
+ .push((module, path_segments, child_accessible, is_extern));
}
}
}
Applicability::MaybeIncorrect,
);
let def_span = suggestion.res.opt_def_id().and_then(|def_id| match def_id.krate {
- LOCAL_CRATE => self.definitions.opt_span(def_id),
+ LOCAL_CRATE => self.opt_span(def_id),
_ => Some(
self.session
.source_map()
use rustc_ast::ast::NodeId;
use rustc_ast::unwrap_or;
use rustc_ast::util::lev_distance::find_best_match_for_name;
+use rustc_ast_lowering::Resolver as ResolverAstLowering;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::ptr_key::PtrKey;
use rustc_errors::{pluralize, struct_span_err, Applicability};
let is_good_import =
binding.is_import() && !binding.is_ambiguity() && !ident.span.from_expansion();
if is_good_import || binding.is_macro_def() {
- let res = binding.res().map_id(|id| this.definitions.local_def_id(id));
+ let res = binding.res().map_id(|id| this.local_def_id(id));
if res != def::Res::Err {
reexports.push(Export { ident, res, span: binding.span, vis: binding.vis });
}
use rustc_ast::util::lev_distance::find_best_match_for_name;
use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
use rustc_ast::{unwrap_or, walk_list};
+use rustc_ast_lowering::Resolver as ResolverAstLowering;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::DiagnosticId;
use rustc_hir::def::Namespace::{self, *};
}
fn with_scope<T>(&mut self, id: NodeId, f: impl FnOnce(&mut Self) -> T) -> T {
- let id = self.r.definitions.local_def_id(id);
+ let id = self.r.local_def_id(id);
let module = self.r.module_map.get(&id).cloned(); // clones a reference
if let Some(module) = module {
// Move down in the graph.
debug!("resolve_adt");
self.with_current_self_item(item, |this| {
this.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| {
- let item_def_id = this.r.definitions.local_def_id(item.id).to_def_id();
+ let item_def_id = this.r.local_def_id(item.id).to_def_id();
this.with_self_rib(Res::SelfTy(None, Some(item_def_id)), |this| {
visit::walk_item(this, item);
});
ItemKind::Trait(.., ref generics, ref bounds, ref trait_items) => {
// Create a new rib for the trait-wide type parameters.
self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| {
- let local_def_id = this.r.definitions.local_def_id(item.id).to_def_id();
+ let local_def_id = this.r.local_def_id(item.id).to_def_id();
this.with_self_rib(Res::SelfTy(Some(local_def_id), None), |this| {
this.visit_generics(generics);
walk_list!(this, visit_param_bound, bounds);
ItemKind::TraitAlias(ref generics, ref bounds) => {
// Create a new rib for the trait-wide type parameters.
self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| {
- let local_def_id = this.r.definitions.local_def_id(item.id).to_def_id();
+ let local_def_id = this.r.local_def_id(item.id).to_def_id();
this.with_self_rib(Res::SelfTy(Some(local_def_id), None), |this| {
this.visit_generics(generics);
walk_list!(this, visit_param_bound, bounds);
seen_bindings.entry(ident).or_insert(param.ident.span);
// Plain insert (no renaming).
- let res = Res::Def(def_kind, self.r.definitions.local_def_id(param.id).to_def_id());
+ let res = Res::Def(def_kind, self.r.local_def_id(param.id).to_def_id());
match param.kind {
GenericParamKind::Type { .. } => {
this.with_self_rib(Res::SelfTy(None, None), |this| {
// Resolve the trait reference, if necessary.
this.with_optional_trait_ref(opt_trait_reference.as_ref(), |this, trait_id| {
- let item_def_id = this.r.definitions.local_def_id(item_id).to_def_id();
+ let item_def_id = this.r.local_def_id(item_id).to_def_id();
this.with_self_rib(Res::SelfTy(trait_id, Some(item_def_id)), |this| {
if let Some(trait_ref) = opt_trait_reference.as_ref() {
// Resolve type arguments in the trait path.
if let StmtKind::Item(ref item) = stmt.kind {
if let ItemKind::MacroDef(..) = item.kind {
num_macro_definition_ribs += 1;
- let res = self.r.definitions.local_def_id(item.id).to_def_id();
+ let res = self.r.local_def_id(item.id).to_def_id();
self.ribs[ValueNS].push(Rib::new(MacroDefinition(res)));
self.label_ribs.push(Rib::new(MacroDefinition(res)));
}
) -> SmallVec<[LocalDefId; 1]> {
let mut import_ids = smallvec![];
while let NameBindingKind::Import { import, binding, .. } = kind {
- let id = self.r.definitions.local_def_id(import.id);
+ let id = self.r.local_def_id(import.id);
self.r.maybe_unused_trait_imports.insert(id);
self.r.add_to_glob_map(&import, trait_name);
import_ids.push(id);
_ => {}
}
if !suggested {
- if let Some(span) = self.r.definitions.opt_span(def_id) {
+ if let Some(span) = self.r.opt_span(def_id) {
err.span_label(span, &format!("`{}` defined here", path_str));
}
err.span_label(span, format!("did you mean `{} {{ /* fields */ }}`?", path_str));
if nightly_options::is_nightly_build() {
let msg = "you might have meant to use `#![feature(trait_alias)]` instead of a \
`type` alias";
- if let Some(span) = self.r.definitions.opt_span(def_id) {
+ if let Some(span) = self.r.opt_span(def_id) {
err.span_help(span, msg);
} else {
err.help(msg);
bad_struct_syntax_suggestion(def_id);
}
(Res::Def(DefKind::Ctor(_, CtorKind::Fn), def_id), _) if ns == ValueNS => {
- if let Some(span) = self.r.definitions.opt_span(def_id) {
+ if let Some(span) = self.r.opt_span(def_id) {
err.span_label(span, &format!("`{}` defined here", path_str));
}
err.span_label(span, format!("did you mean `{}( /* fields */ )`?", path_str));
use rustc_ast::node_id::NodeMap;
use rustc_ast::unwrap_or;
use rustc_ast::visit::{self, Visitor};
+use rustc_ast_lowering::Resolver as ResolverAstLowering;
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_data_structures::ptr_key::PtrKey;
use rustc_hir::def::Namespace::*;
use rustc_hir::def::{self, CtorOf, DefKind, NonMacroAttrKind, PartialRes};
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX};
-use rustc_hir::definitions::{DefKey, Definitions};
+use rustc_hir::definitions::{DefKey, DefPathData, Definitions};
use rustc_hir::PrimTy::{self, Bool, Char, Float, Int, Str, Uint};
use rustc_hir::TraitCandidate;
+use rustc_index::vec::IndexVec;
use rustc_metadata::creader::{CStore, CrateLoader};
use rustc_middle::hir::exports::ExportMap;
use rustc_middle::middle::cstore::{CrateStore, MetadataLoaderDyn};
}
}
-struct UsePlacementFinder<'d> {
- definitions: &'d Definitions,
- target_module: LocalDefId,
+struct UsePlacementFinder {
+ target_module: NodeId,
span: Option<Span>,
found_use: bool,
}
-impl<'d> UsePlacementFinder<'d> {
- fn check(
- definitions: &'d Definitions,
- krate: &Crate,
- target_module: DefId,
- ) -> (Option<Span>, bool) {
- if let Some(target_module) = target_module.as_local() {
- let mut finder =
- UsePlacementFinder { definitions, target_module, span: None, found_use: false };
- visit::walk_crate(&mut finder, krate);
- (finder.span, finder.found_use)
- } else {
- (None, false)
- }
+impl UsePlacementFinder {
+ fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, bool) {
+ let mut finder = UsePlacementFinder { target_module, span: None, found_use: false };
+ visit::walk_crate(&mut finder, krate);
+ (finder.span, finder.found_use)
}
}
-impl<'tcx, 'd> Visitor<'tcx> for UsePlacementFinder<'d> {
+impl<'tcx> Visitor<'tcx> for UsePlacementFinder {
fn visit_mod(
&mut self,
module: &'tcx ast::Mod,
if self.span.is_some() {
return;
}
- if self.definitions.local_def_id(node_id) != self.target_module {
+ if node_id != self.target_module {
visit::walk_mod(self, module);
return;
}
}
}
+ fn is_possibly_imported_variant(&self) -> bool {
+ match self.kind {
+ NameBindingKind::Import { binding, .. } => binding.is_possibly_imported_variant(),
+ _ => self.is_variant(),
+ }
+ }
+
// We sometimes need to treat variants as `pub` for backwards compatibility.
fn pseudo_vis(&self) -> ty::Visibility {
if self.is_variant() && self.res().def_id().is_local() {
lint_buffer: LintBuffer,
next_node_id: NodeId,
+
+ def_id_to_span: IndexVec<LocalDefId, Span>,
+
+ node_id_to_def_id: FxHashMap<ast::NodeId, LocalDefId>,
+ def_id_to_node_id: IndexVec<LocalDefId, ast::NodeId>,
+
+ /// Indices of unnamed struct or variant fields with unresolved attributes.
+ placeholder_field_indices: FxHashMap<NodeId, usize>,
+ /// When collecting definitions from an AST fragment produced by a macro invocation `ExpnId`
+ /// we know what parent node that fragment should be attached to thanks to this table.
+ invocation_parents: FxHashMap<ExpnId, LocalDefId>,
+
+ next_disambiguator: FxHashMap<(LocalDefId, DefPathData), u32>,
}
/// Nothing really interesting here; it just provides memory for the rest of the crate.
/// This interface is used through the AST→HIR step, to embed full paths into the HIR. After that
/// the resolver is no longer needed as all the relevant information is inline.
-impl rustc_ast_lowering::Resolver for Resolver<'_> {
+impl ResolverAstLowering for Resolver<'_> {
fn def_key(&mut self, id: DefId) -> DefKey {
if let Some(id) = id.as_local() {
self.definitions().def_key(id)
fn trait_map(&self) -> &NodeMap<Vec<TraitCandidate>> {
&self.trait_map
}
+
+ fn opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId> {
+ self.node_id_to_def_id.get(&node).copied()
+ }
+
+ fn local_def_id(&self, node: NodeId) -> LocalDefId {
+ self.opt_local_def_id(node).unwrap_or_else(|| panic!("no entry for node id: `{:?}`", node))
+ }
+
+ /// Adds a definition with a parent definition.
+ fn create_def(
+ &mut self,
+ parent: LocalDefId,
+ node_id: ast::NodeId,
+ data: DefPathData,
+ expn_id: ExpnId,
+ span: Span,
+ ) -> LocalDefId {
+ assert!(
+ !self.node_id_to_def_id.contains_key(&node_id),
+ "adding a def'n for node-id {:?} and data {:?} but a previous def'n exists: {:?}",
+ node_id,
+ data,
+ self.definitions.def_key(self.node_id_to_def_id[&node_id]),
+ );
+
+ // Find the next free disambiguator for this key.
+ let next_disambiguator = &mut self.next_disambiguator;
+ let next_disambiguator = |parent, data| {
+ let next_disamb = next_disambiguator.entry((parent, data)).or_insert(0);
+ let disambiguator = *next_disamb;
+ *next_disamb = next_disamb.checked_add(1).expect("disambiguator overflow");
+ disambiguator
+ };
+
+ let def_id = self.definitions.create_def(parent, data, expn_id, next_disambiguator);
+
+ assert_eq!(self.def_id_to_span.push(span), def_id);
+
+ // Some things for which we allocate `LocalDefId`s don't correspond to
+ // anything in the AST, so they don't have a `NodeId`. For these cases
+ // we don't need a mapping from `NodeId` to `LocalDefId`.
+ if node_id != ast::DUMMY_NODE_ID {
+ debug!("create_def: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id);
+ self.node_id_to_def_id.insert(node_id, def_id);
+ }
+ assert_eq!(self.def_id_to_node_id.push(node_id), def_id);
+
+ def_id
+ }
}
impl<'a> Resolver<'a> {
let mut module_map = FxHashMap::default();
module_map.insert(LocalDefId { local_def_index: CRATE_DEF_INDEX }, graph_root);
- let mut definitions = Definitions::default();
- definitions.create_root_def(crate_name, session.local_crate_disambiguator());
+ let definitions = Definitions::new(crate_name, session.local_crate_disambiguator());
+ let root = definitions.get_root_def();
+
+ let mut def_id_to_span = IndexVec::default();
+ assert_eq!(def_id_to_span.push(rustc_span::DUMMY_SP), root);
+ let mut def_id_to_node_id = IndexVec::default();
+ assert_eq!(def_id_to_node_id.push(CRATE_NODE_ID), root);
+ let mut node_id_to_def_id = FxHashMap::default();
+ node_id_to_def_id.insert(CRATE_NODE_ID, root);
+
+ let mut invocation_parents = FxHashMap::default();
+ invocation_parents.insert(ExpnId::root(), root);
let mut extern_prelude: FxHashMap<Ident, ExternPreludeEntry<'_>> = session
.opts
variant_vis: Default::default(),
lint_buffer: LintBuffer::default(),
next_node_id: NodeId::from_u32(1),
+ def_id_to_span,
+ node_id_to_def_id,
+ def_id_to_node_id,
+ placeholder_field_indices: Default::default(),
+ invocation_parents,
+ next_disambiguator: Default::default(),
}
}
#[inline]
fn add_to_glob_map(&mut self, import: &Import<'_>, ident: Ident) {
if import.is_glob() {
- let def_id = self.definitions.local_def_id(import.id);
+ let def_id = self.local_def_id(import.id);
self.glob_map.entry(def_id).or_default().insert(ident.name);
}
}
for UseError { mut err, candidates, def_id, instead, suggestion } in
self.use_injections.drain(..)
{
- let (span, found_use) = UsePlacementFinder::check(&self.definitions, krate, def_id);
+ let (span, found_use) = if let Some(def_id) = def_id.as_local() {
+ UsePlacementFinder::check(krate, self.def_id_to_node_id[def_id])
+ } else {
+ (None, false)
+ };
if !candidates.is_empty() {
diagnostics::show_candidates(&mut err, span, &candidates, instead, found_use);
} else if let Some((span, msg, sugg, appl)) = suggestion {
pub fn all_macros(&self) -> &FxHashMap<Symbol, Res> {
&self.all_macros
}
+
+ /// Retrieves the span of the given `DefId` if `DefId` is in the local crate.
+ #[inline]
+ pub fn opt_span(&self, def_id: DefId) -> Option<Span> {
+ if let Some(def_id) = def_id.as_local() { Some(self.def_id_to_span[def_id]) } else { None }
+ }
}
fn names_to_string(names: &[Symbol]) -> String {
use crate::{CrateLint, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Weak};
use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding};
use rustc_ast::ast::{self, NodeId};
+use rustc_ast_lowering::Resolver as ResolverAstLowering;
use rustc_ast_pretty::pprust;
use rustc_attr::{self as attr, StabilityLevel};
use rustc_data_structures::fx::FxHashSet;
)));
let parent_scope = if let Some(module_id) = parent_module_id {
- let parent_def_id = self.definitions.local_def_id(module_id);
+ let parent_def_id = self.local_def_id(module_id);
self.definitions.add_parent_module_of_macro_def(expn_id, parent_def_id.to_def_id());
self.module_map[&parent_def_id]
} else {
}
fn lint_node_id(&mut self, expn_id: ExpnId) -> NodeId {
- self.definitions.lint_node_id(expn_id)
+ self.invocation_parents
+ .get(&expn_id)
+ .map_or(ast::CRATE_NODE_ID, |id| self.def_id_to_node_id[*id])
}
fn has_derive_copy(&self, expn_id: ExpnId) -> bool {
}
}
};
+ log::debug!("got unpretty option: {:?}", first);
first
}
}
use PpMode::*;
use PpSourceMode::*;
match *self {
- PpmSource(PpmNormal | PpmEveryBodyLoops | PpmIdentified) => false,
+ PpmSource(PpmNormal | PpmIdentified) => false,
- PpmSource(PpmExpanded | PpmExpandedIdentified | PpmExpandedHygiene)
+ PpmSource(
+ PpmExpanded | PpmEveryBodyLoops | PpmExpandedIdentified | PpmExpandedHygiene,
+ )
| PpmHir(_)
| PpmHirTree(_)
| PpmMir
make_target_lib_path(self.sysroot, self.triple)
}
- pub fn get_selfcontained_lib_path(&self) -> PathBuf {
+ pub fn get_self_contained_lib_path(&self) -> PathBuf {
self.get_lib_path().join("self-contained")
}
}
// Returns a list of directories where target-specific tool binaries are located.
- pub fn get_tools_search_paths(&self) -> Vec<PathBuf> {
+ pub fn get_tools_search_paths(&self, self_contained: bool) -> Vec<PathBuf> {
let mut p = PathBuf::from(self.sysroot);
p.push(find_libdir(self.sysroot).as_ref());
p.push(RUST_LIB_DIR);
p.push(&self.triple);
p.push("bin");
- vec![p.clone(), p.join("self-contained")]
+ if self_contained { vec![p.clone(), p.join("self-contained")] } else { vec![p.clone()] }
}
}
(such as entering an empty infinite loop) by inserting llvm.sideeffect \
(default: no)"),
instrument_coverage: bool = (false, parse_bool, [TRACKED],
- "instrument the generated code with LLVM code region counters to \
- (in the future) generate coverage reports (experimental; default: no)"),
+ "instrument the generated code with LLVM code region counters to (in the \
+ future) generate coverage reports (default: no; note, the compiler build \
+ config must include `profiler = true`)"),
instrument_mcount: bool = (false, parse_bool, [TRACKED],
"insert function instrument code for mcount-based tracing (default: no)"),
keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED],
"keep hygiene data after analysis (default: no)"),
link_native_libraries: bool = (true, parse_bool, [UNTRACKED],
"link native libraries in the linker invocation (default: yes)"),
+ link_self_contained: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "control whether to link Rust provided C objects/libraries or rely
+ on C toolchain installed in the system"),
link_only: bool = (false, parse_bool, [TRACKED],
"link the `.rlink` file generated by `-Z no-link` (default: no)"),
llvm_time_trace: bool = (false, parse_bool, [UNTRACKED],
use rustc_span::source_map::{FilePathMapping, SourceMap};
use rustc_span::{MultiSpan, Span, Symbol};
+use std::collections::BTreeMap;
use std::path::PathBuf;
use std::str;
#[derive(Default)]
pub struct SymbolGallery {
/// All symbols occurred and their first occurrance span.
- pub symbols: Lock<FxHashMap<Symbol, Span>>,
+ pub symbols: Lock<BTreeMap<Symbol, Span>>,
}
impl SymbolGallery {
assume_init,
async_await,
async_closure,
+ atomics,
attr,
attributes,
attr_literals,
proc_macro_mod,
proc_macro_non_items,
proc_macro_path_invoc,
+ profiler_builtins,
profiler_runtime,
ptr_guaranteed_eq,
ptr_guaranteed_ne,
let pre_link_args = build_pre_link_args(arch, os)?;
Ok(TargetOptions {
cpu: target_cpu(arch),
- dynamic_linking: false,
executables: true,
pre_link_args,
link_env_remove: link_env_remove(arch),
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::ErrorReported;
use rustc_hir::def_id::DefId;
-use rustc_hir::lang_items::{FnOnceTraitLangItem, GeneratorTraitLangItem};
+use rustc_hir::lang_items::{FnOnceOutputLangItem, FnOnceTraitLangItem, GeneratorTraitLangItem};
use rustc_infer::infer::resolve::OpportunisticRegionResolver;
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness};
-use rustc_span::symbol::{sym, Ident};
+use rustc_span::symbol::sym;
use rustc_span::DUMMY_SP;
pub use rustc_middle::traits::Reveal;
debug!("confirm_callable_candidate({:?},{:?})", obligation, fn_sig);
- // the `Output` associated type is declared on `FnOnce`
let fn_once_def_id = tcx.require_lang_item(FnOnceTraitLangItem, None);
+ let fn_once_output_def_id = tcx.require_lang_item(FnOnceOutputLangItem, None);
let predicate = super::util::closure_trait_ref_and_return_type(
tcx,
flag,
)
.map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate {
- projection_ty: ty::ProjectionTy::from_ref_and_name(
- tcx,
- trait_ref,
- Ident::with_dummy_span(rustc_hir::FN_OUTPUT_NAME),
- ),
+ projection_ty: ty::ProjectionTy {
+ substs: trait_ref.substs,
+ item_def_id: fn_once_output_def_id,
+ },
ty: ret_type,
});
}
self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
self.suggest_missing_await(err, expr, expected, expr_ty);
+ self.note_need_for_fn_pointer(err, expected, expr_ty);
}
// Requires that the two types unify, and prints an error message if
}
}
+ fn note_need_for_fn_pointer(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ ) {
+ let (sig, did, substs) = match (&expected.kind, &found.kind) {
+ (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
+ let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1);
+ let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2);
+ if sig1 != sig2 {
+ return;
+ }
+ err.note(
+ "different `fn` items always have unique types, even if their signatures are \
+ the same",
+ );
+ (sig1, *did1, substs1)
+ }
+ (ty::FnDef(did, substs), ty::FnPtr(sig2)) => {
+ let sig1 = self.tcx.fn_sig(*did).subst(self.tcx, substs);
+ if sig1 != *sig2 {
+ return;
+ }
+ (sig1, *did, substs)
+ }
+ _ => return,
+ };
+ err.help(&format!("change the expected type to be function pointer `{}`", sig));
+ err.help(&format!(
+ "if the expected type is due to type inference, cast the expected `fn` to a function \
+ pointer: `{} as {}`",
+ self.tcx.def_path_str_with_substs(did, substs),
+ sig
+ ));
+ }
+
/// A common error is to add an extra semicolon:
///
/// ```
})
}
+ /// Enforce the format of attributes inside `#[doc(...)]`.
+ pub fn check_doc_attributes(
+ diagnostic: &::rustc_errors::Handler,
+ mi: &ast::MetaItem,
+ ) -> Option<(String, String)> {
+ mi.meta_item_list().and_then(|list| {
+ for meta in list {
+ if meta.check_name(sym::alias) {
+ if !meta.is_value_str()
+ || meta
+ .value_str()
+ .map(|s| s.to_string())
+ .unwrap_or_else(String::new)
+ .is_empty()
+ {
+ diagnostic.span_err(
+ meta.span(),
+ "doc alias attribute expects a string: #[doc(alias = \"0\")]",
+ );
+ }
+ }
+ }
+
+ None
+ })
+ }
+
pub fn has_doc_flag(&self, flag: Symbol) -> bool {
for attr in &self.other_attrs {
if !attr.check_name(sym::doc) {
} else {
if attr.check_name(sym::doc) {
if let Some(mi) = attr.meta() {
+ Attributes::check_doc_attributes(&diagnostic, &mi);
if let Some(cfg_mi) = Attributes::extract_cfg(&mi) {
// Extracted #[doc(cfg(...))]
match Cfg::parse(cfg_mi) {
var aliases = [];
var crateAliases = [];
var i;
- if (filterCrates !== undefined &&
- ALIASES[filterCrates] &&
- ALIASES[filterCrates][query.search]) {
- for (i = 0; i < ALIASES[crate][query.search].length; ++i) {
- aliases.push(
- createAliasFromItem(searchIndex[ALIASES[filterCrates][query.search]]));
+ if (filterCrates !== undefined) {
+ if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) {
+ for (i = 0; i < ALIASES[filterCrates][query.search].length; ++i) {
+ aliases.push(
+ createAliasFromItem(
+ searchIndex[ALIASES[filterCrates][query.search][i]]));
+ }
}
} else {
Object.keys(ALIASES).forEach(function(crate) {
//
/// The receiver of a method, or the current module.
///
-/// The documentation for this keyword is [not yet complete]. Pull requests welcome!
+/// `self` is used in two situations: referencing the current module and marking
+/// the receiver of a method.
///
-/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601
+/// In paths, `self` can be used to refer to the current module, either in a
+/// [`use`] statement or in a path to access an element:
+///
+/// ```
+/// # #![allow(unused_imports)]
+/// use std::io::{self, Read};
+/// ```
+///
+/// Is functionally the same as:
+///
+/// ```
+/// # #![allow(unused_imports)]
+/// use std::io;
+/// use std::io::Read;
+/// ```
+///
+/// Using `self` to access an element in the current module:
+///
+/// ```
+/// # #![allow(dead_code)]
+/// # fn main() {}
+/// fn foo() {}
+/// fn bar() {
+/// self::foo()
+/// }
+/// ```
+///
+/// `self` as the current receiver for a method allows to omit the parameter
+/// type most of the time. With the exception of this particularity, `self` is
+/// used much like any other parameter:
+///
+/// ```
+/// struct Foo(i32);
+///
+/// impl Foo {
+/// // No `self`.
+/// fn new() -> Self {
+/// Self(0)
+/// }
+///
+/// // Consuming `self`.
+/// fn consume(self) -> Self {
+/// Self(self.0 + 1)
+/// }
+///
+/// // Borrowing `self`.
+/// fn borrow(&self) -> &i32 {
+/// &self.0
+/// }
+///
+/// // Borrowing `self` mutably.
+/// fn borrow_mut(&mut self) -> &mut i32 {
+/// &mut self.0
+/// }
+/// }
+///
+/// // This method must be called with a `Type::` prefix.
+/// let foo = Foo::new();
+/// assert_eq!(foo.0, 0);
+///
+/// // Those two calls produces the same result.
+/// let foo = Foo::consume(foo);
+/// assert_eq!(foo.0, 1);
+/// let foo = foo.consume();
+/// assert_eq!(foo.0, 2);
+///
+/// // Borrowing is handled automatically with the second syntax.
+/// let borrow_1 = Foo::borrow(&foo);
+/// let borrow_2 = foo.borrow();
+/// assert_eq!(borrow_1, borrow_2);
+///
+/// // Borrowing mutably is handled automatically too with the second syntax.
+/// let mut foo = Foo::new();
+/// *Foo::borrow_mut(&mut foo) += 1;
+/// assert_eq!(foo.0, 1);
+/// *foo.borrow_mut() += 1;
+/// assert_eq!(foo.0, 2);
+/// ```
+///
+/// Note that this automatic conversion when calling `foo.method()` is not
+/// limited to the examples above. See the [Reference] for more information.
+///
+/// [`use`]: keyword.use.html
+/// [Reference]: ../reference/items/associated-items.html#methods
mod self_keyword {}
#[doc(keyword = "Self")]
#![stable(feature = "metadata_ext", since = "1.1.0")]
-use libc;
-
use crate::fs::Metadata;
use crate::sys_common::AsInner;
fn default_hook(info: &PanicInfo<'_>) {
// If this is a double panic, make sure that we print a backtrace
// for this panic. Otherwise only print it if logging is enabled.
- let backtrace_env = if update_panic_count(0) >= 2 {
+ let backtrace_env = if panic_count::get() >= 2 {
RustBacktrace::Print(backtrace_rs::PrintFmt::Full)
} else {
backtrace::rust_backtrace_env()
#[cfg(not(test))]
#[doc(hidden)]
#[unstable(feature = "update_panic_count", issue = "none")]
-pub fn update_panic_count(amt: isize) -> usize {
+pub mod panic_count {
use crate::cell::Cell;
- thread_local! { static PANIC_COUNT: Cell<usize> = Cell::new(0) }
+ use crate::sync::atomic::{AtomicUsize, Ordering};
+
+ // Panic count for the current thread.
+ thread_local! { static LOCAL_PANIC_COUNT: Cell<usize> = Cell::new(0) }
+
+ // Sum of panic counts from all threads. The purpose of this is to have
+ // a fast path in `is_zero` (which is used by `panicking`). Access to
+ // this variable can be always be done with relaxed ordering because
+ // it is always guaranteed that, if `GLOBAL_PANIC_COUNT` is zero,
+ // `LOCAL_PANIC_COUNT` will be zero.
+ static GLOBAL_PANIC_COUNT: AtomicUsize = AtomicUsize::new(0);
+
+ pub fn increase() -> usize {
+ GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed);
+ LOCAL_PANIC_COUNT.with(|c| {
+ let next = c.get() + 1;
+ c.set(next);
+ next
+ })
+ }
+
+ pub fn decrease() -> usize {
+ GLOBAL_PANIC_COUNT.fetch_sub(1, Ordering::Relaxed);
+ LOCAL_PANIC_COUNT.with(|c| {
+ let next = c.get() - 1;
+ c.set(next);
+ next
+ })
+ }
- PANIC_COUNT.with(|c| {
- let next = (c.get() as isize + amt) as usize;
- c.set(next);
- next
- })
+ pub fn get() -> usize {
+ LOCAL_PANIC_COUNT.with(|c| c.get())
+ }
+
+ #[inline]
+ pub fn is_zero() -> bool {
+ if GLOBAL_PANIC_COUNT.load(Ordering::Relaxed) == 0 {
+ // Fast path: if `GLOBAL_PANIC_COUNT` is zero, all threads
+ // (including the current one) will have `LOCAL_PANIC_COUNT`
+ // equal to zero, so TLS access can be avoided.
+ true
+ } else {
+ is_zero_slow_path()
+ }
+ }
+
+ // Slow path is in a separate function to reduce the amount of code
+ // inlined from `is_zero`.
+ #[inline(never)]
+ #[cold]
+ fn is_zero_slow_path() -> bool {
+ LOCAL_PANIC_COUNT.with(|c| c.get() == 0)
+ }
}
#[cfg(test)]
-pub use realstd::rt::update_panic_count;
+pub use realstd::rt::panic_count;
/// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
#[cold]
unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send + 'static> {
let obj = Box::from_raw(__rust_panic_cleanup(payload));
- update_panic_count(-1);
+ panic_count::decrease();
obj
}
}
/// Determines whether the current thread is unwinding because of panic.
+#[inline]
pub fn panicking() -> bool {
- update_panic_count(0) != 0
+ !panic_count::is_zero()
}
/// The entry point for panicking with a formatted message.
message: Option<&fmt::Arguments<'_>>,
location: &Location<'_>,
) -> ! {
- let panics = update_panic_count(1);
+ let panics = panic_count::increase();
// If this is the third nested call (e.g., panics == 2, this is 0-indexed),
// the panic hook probably triggered the last panic, otherwise the
/// This is the entry point for `resume_unwind`.
/// It just forwards the payload to the panic runtime.
pub fn rust_panic_without_hook(payload: Box<dyn Any + Send>) -> ! {
- update_panic_count(1);
+ panic_count::increase();
struct RewrapBox(Box<dyn Any + Send>);
#![doc(hidden)]
// Re-export some of our utilities which are expected by other crates.
-pub use crate::panicking::{begin_panic, begin_panic_fmt, update_panic_count};
+pub use crate::panicking::{begin_panic, begin_panic_fmt, panic_count};
// To reduce the generated code of the new `lang_start`, this function is doing
// the real work.
// able to specify this
#[cfg(not(test))]
#[no_mangle]
+#[allow(improper_ctypes_definitions)]
extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> (u64, u64) {
// FIXME: how to support TLS in library mode?
let tls = Box::new(tls::Tls::new());
//! Unix-specific networking functionality
-#[cfg(unix)]
-use libc;
-
// FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here?
#[cfg(not(unix))]
#[allow(non_camel_case_types)]
use crate::ffi::{CStr, OsString};
use crate::marker::PhantomData;
use crate::ptr;
- use libc;
use crate::sys_common::mutex::Mutex;
use crate::sys;
use crate::sys::platform::fs::MetadataExt as UnixMetadataExt;
use crate::sys_common::{AsInner, AsInnerMut, FromInner};
-use libc;
/// Unix-specific extensions to [`File`].
///
mod imp {
use crate::io;
use core::sync::atomic::{AtomicBool, Ordering::Relaxed};
- use libc;
pub fn fill_bytes(v: &mut [u8]) {
static RNG_INIT: AtomicBool = AtomicBool::new(false);
use crate::cell::UnsafeCell;
use crate::sync::atomic::{AtomicUsize, Ordering};
-use libc;
pub struct RWLock {
inner: UnsafeCell<libc::pthread_rwlock_t>,
use crate::cmp::Ordering;
use crate::time::Duration;
use ::core::hash::{Hash, Hasher};
-use libc;
pub use self::inner::{Instant, SystemTime, UNIX_EPOCH};
use crate::convert::TryInto;
use crate::fmt;
use crate::sys::cvt;
use crate::time::Duration;
- use libc;
use super::Timespec;
use crate::alloc::{GlobalAlloc, Layout, System};
use crate::ptr;
use crate::sys_common::alloc::{realloc_fallback, MIN_ALIGN};
-use libc;
#[stable(feature = "alloc_system_type", since = "1.28.0")]
unsafe impl GlobalAlloc for System {
// mir::Constant
// + span: $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24
// + literal: Const { ty: usize, val: Value(Scalar(0x00000003)) }
-- _7 = Len((*_1)); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25
-- _8 = Lt(_6, _7); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25
-+ _7 = const 3usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25
-+ // ty::Const
-+ // + ty: usize
-+ // + val: Value(Scalar(0x00000003))
-+ // mir::Constant
-+ // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25
-+ // + literal: Const { ty: usize, val: Value(Scalar(0x00000003)) }
-+ _8 = const false; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25
-+ // ty::Const
-+ // + ty: bool
-+ // + val: Value(Scalar(0x00))
-+ // mir::Constant
-+ // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25
-+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) }
+ _7 = Len((*_1)); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25
+ _8 = Lt(_6, _7); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25
assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25
}
// mir::Constant
// + span: $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24
// + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000003)) }
-- _7 = Len((*_1)); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25
-- _8 = Lt(_6, _7); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25
-+ _7 = const 3usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25
-+ // ty::Const
-+ // + ty: usize
-+ // + val: Value(Scalar(0x0000000000000003))
-+ // mir::Constant
-+ // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25
-+ // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000003)) }
-+ _8 = const false; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25
-+ // ty::Const
-+ // + ty: bool
-+ // + val: Value(Scalar(0x00))
-+ // mir::Constant
-+ // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25
-+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) }
+ _7 = Len((*_1)); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25
+ _8 = Lt(_6, _7); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25
assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25
}
--- /dev/null
+#![feature(raw_ref_op)]
+
+// EMIT_MIR rustc.foo.ConstProp.diff
+fn foo() {
+ let mut u = (1,);
+ *&mut u.0 = 5;
+ let y = { u.0 } == 5;
+}
+
+// EMIT_MIR rustc.bar.ConstProp.diff
+fn bar() {
+ let mut v = (1,);
+ unsafe {
+ *&raw mut v.0 = 5;
+ }
+ let y = { v.0 } == 5;
+}
+
+fn main() {
+ foo();
+ bar();
+}
--- /dev/null
+- // MIR for `bar` before ConstProp
++ // MIR for `bar` after ConstProp
+
+ fn bar() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/const_prop_miscompile.rs:11:10: 11:10
+ let mut _1: (i32,); // in scope 0 at $DIR/const_prop_miscompile.rs:12:9: 12:14
+ let _2: (); // in scope 0 at $DIR/const_prop_miscompile.rs:13:5: 15:6
+ let mut _3: *mut i32; // in scope 0 at $DIR/const_prop_miscompile.rs:14:10: 14:22
+ let mut _5: i32; // in scope 0 at $DIR/const_prop_miscompile.rs:16:13: 16:20
+ scope 1 {
+ debug v => _1; // in scope 1 at $DIR/const_prop_miscompile.rs:12:9: 12:14
+ let _4: bool; // in scope 1 at $DIR/const_prop_miscompile.rs:16:9: 16:10
+ scope 2 {
+ }
+ scope 3 {
+ debug y => _4; // in scope 3 at $DIR/const_prop_miscompile.rs:16:9: 16:10
+ }
+ }
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/const_prop_miscompile.rs:12:9: 12:14
+- _1 = (const 1i32,); // scope 0 at $DIR/const_prop_miscompile.rs:12:17: 12:21
++ _1 = const (1i32,); // scope 0 at $DIR/const_prop_miscompile.rs:12:17: 12:21
+ // ty::Const
+- // + ty: i32
++ // + ty: (i32,)
+ // + val: Value(Scalar(0x00000001))
+ // mir::Constant
+- // + span: $DIR/const_prop_miscompile.rs:12:18: 12:19
+- // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) }
++ // + span: $DIR/const_prop_miscompile.rs:12:17: 12:21
++ // + literal: Const { ty: (i32,), val: Value(Scalar(0x00000001)) }
+ StorageLive(_2); // scope 1 at $DIR/const_prop_miscompile.rs:13:5: 15:6
+ StorageLive(_3); // scope 2 at $DIR/const_prop_miscompile.rs:14:10: 14:22
+ _3 = &raw mut (_1.0: i32); // scope 2 at $DIR/const_prop_miscompile.rs:14:10: 14:22
+ (*_3) = const 5i32; // scope 2 at $DIR/const_prop_miscompile.rs:14:9: 14:26
+ // ty::Const
+ // + ty: i32
+ // + val: Value(Scalar(0x00000005))
+ // mir::Constant
+ // + span: $DIR/const_prop_miscompile.rs:14:25: 14:26
+ // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) }
+ StorageDead(_3); // scope 2 at $DIR/const_prop_miscompile.rs:14:26: 14:27
+ _2 = const (); // scope 2 at $DIR/const_prop_miscompile.rs:13:5: 15:6
+ // ty::Const
+ // + ty: ()
+ // + val: Value(Scalar(<ZST>))
+ // mir::Constant
+ // + span: $DIR/const_prop_miscompile.rs:13:5: 15:6
+ // + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
+ StorageDead(_2); // scope 1 at $DIR/const_prop_miscompile.rs:15:5: 15:6
+ StorageLive(_4); // scope 1 at $DIR/const_prop_miscompile.rs:16:9: 16:10
+ StorageLive(_5); // scope 1 at $DIR/const_prop_miscompile.rs:16:13: 16:20
+ _5 = (_1.0: i32); // scope 1 at $DIR/const_prop_miscompile.rs:16:15: 16:18
+ _4 = Eq(move _5, const 5i32); // scope 1 at $DIR/const_prop_miscompile.rs:16:13: 16:25
+ // ty::Const
+ // + ty: i32
+ // + val: Value(Scalar(0x00000005))
+ // mir::Constant
+ // + span: $DIR/const_prop_miscompile.rs:16:24: 16:25
+ // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) }
+ StorageDead(_5); // scope 1 at $DIR/const_prop_miscompile.rs:16:24: 16:25
+ _0 = const (); // scope 0 at $DIR/const_prop_miscompile.rs:11:10: 17:2
+ // ty::Const
+ // + ty: ()
+ // + val: Value(Scalar(<ZST>))
+ // mir::Constant
+ // + span: $DIR/const_prop_miscompile.rs:11:10: 17:2
+ // + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
+ StorageDead(_4); // scope 1 at $DIR/const_prop_miscompile.rs:17:1: 17:2
+ StorageDead(_1); // scope 0 at $DIR/const_prop_miscompile.rs:17:1: 17:2
+ return; // scope 0 at $DIR/const_prop_miscompile.rs:17:2: 17:2
+ }
+ }
+
--- /dev/null
+- // MIR for `foo` before ConstProp
++ // MIR for `foo` after ConstProp
+
+ fn foo() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/const_prop_miscompile.rs:4:10: 4:10
+ let mut _1: (i32,); // in scope 0 at $DIR/const_prop_miscompile.rs:5:9: 5:14
+ let mut _2: &mut i32; // in scope 0 at $DIR/const_prop_miscompile.rs:6:6: 6:14
+ let mut _4: i32; // in scope 0 at $DIR/const_prop_miscompile.rs:7:13: 7:20
+ scope 1 {
+ debug u => _1; // in scope 1 at $DIR/const_prop_miscompile.rs:5:9: 5:14
+ let _3: bool; // in scope 1 at $DIR/const_prop_miscompile.rs:7:9: 7:10
+ scope 2 {
+ debug y => _3; // in scope 2 at $DIR/const_prop_miscompile.rs:7:9: 7:10
+ }
+ }
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/const_prop_miscompile.rs:5:9: 5:14
+- _1 = (const 1i32,); // scope 0 at $DIR/const_prop_miscompile.rs:5:17: 5:21
++ _1 = const (1i32,); // scope 0 at $DIR/const_prop_miscompile.rs:5:17: 5:21
+ // ty::Const
+- // + ty: i32
++ // + ty: (i32,)
+ // + val: Value(Scalar(0x00000001))
+ // mir::Constant
+- // + span: $DIR/const_prop_miscompile.rs:5:18: 5:19
+- // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) }
++ // + span: $DIR/const_prop_miscompile.rs:5:17: 5:21
++ // + literal: Const { ty: (i32,), val: Value(Scalar(0x00000001)) }
+ StorageLive(_2); // scope 1 at $DIR/const_prop_miscompile.rs:6:6: 6:14
+ _2 = &mut (_1.0: i32); // scope 1 at $DIR/const_prop_miscompile.rs:6:6: 6:14
+ (*_2) = const 5i32; // scope 1 at $DIR/const_prop_miscompile.rs:6:5: 6:18
+ // ty::Const
+ // + ty: i32
+ // + val: Value(Scalar(0x00000005))
+ // mir::Constant
+ // + span: $DIR/const_prop_miscompile.rs:6:17: 6:18
+ // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) }
+ StorageDead(_2); // scope 1 at $DIR/const_prop_miscompile.rs:6:18: 6:19
+ StorageLive(_3); // scope 1 at $DIR/const_prop_miscompile.rs:7:9: 7:10
+ StorageLive(_4); // scope 1 at $DIR/const_prop_miscompile.rs:7:13: 7:20
+ _4 = (_1.0: i32); // scope 1 at $DIR/const_prop_miscompile.rs:7:15: 7:18
+ _3 = Eq(move _4, const 5i32); // scope 1 at $DIR/const_prop_miscompile.rs:7:13: 7:25
+ // ty::Const
+ // + ty: i32
+ // + val: Value(Scalar(0x00000005))
+ // mir::Constant
+ // + span: $DIR/const_prop_miscompile.rs:7:24: 7:25
+ // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) }
+ StorageDead(_4); // scope 1 at $DIR/const_prop_miscompile.rs:7:24: 7:25
+ _0 = const (); // scope 0 at $DIR/const_prop_miscompile.rs:4:10: 8:2
+ // ty::Const
+ // + ty: ()
+ // + val: Value(Scalar(<ZST>))
+ // mir::Constant
+ // + span: $DIR/const_prop_miscompile.rs:4:10: 8:2
+ // + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
+ StorageDead(_3); // scope 1 at $DIR/const_prop_miscompile.rs:8:1: 8:2
+ StorageDead(_1); // scope 0 at $DIR/const_prop_miscompile.rs:8:1: 8:2
+ return; // scope 0 at $DIR/const_prop_miscompile.rs:8:2: 8:2
+ }
+ }
+
--- /dev/null
+// exact-check
+
+const QUERY = 'true';
+
+const FILTER_CRATE = 'some_other_crate';
+
+const EXPECTED = {
+ 'others': [],
+};
--- /dev/null
+#![feature(doc_alias)]
+
+#[doc(alias = "true")]
+pub struct Foo;
--- /dev/null
+// exact-check
+
+const QUERY = 'true';
+
+const FILTER_CRATE = 'doc_alias_filter';
+
+const EXPECTED = {
+ 'others': [
+ {
+ 'path': 'doc_alias_filter',
+ 'name': 'Foo',
+ 'alias': 'true',
+ 'href': '../doc_alias_filter/struct.Foo.html',
+ 'is_alias': true
+ },
+ ],
+};
--- /dev/null
+#![feature(doc_alias)]
+
+#[doc(alias = "true")]
+pub struct Foo;
+
+#[doc(alias = "false")]
+pub struct Bar;
--- /dev/null
+#![feature(doc_alias)]
+
+#[doc(alias = "foo")] // ok!
+pub struct Bar;
+
+#[doc(alias)] //~ ERROR
+#[doc(alias = 0)] //~ ERROR
+#[doc(alias("bar"))] //~ ERROR
+pub struct Foo;
--- /dev/null
+error: doc alias attribute expects a string: #[doc(alias = "0")]
+ --> $DIR/check-doc-alias-attr.rs:6:7
+ |
+LL | #[doc(alias)]
+ | ^^^^^
+
+error: doc alias attribute expects a string: #[doc(alias = "0")]
+ --> $DIR/check-doc-alias-attr.rs:7:7
+ |
+LL | #[doc(alias = 0)]
+ | ^^^^^^^^^
+
+error: doc alias attribute expects a string: #[doc(alias = "0")]
+ --> $DIR/check-doc-alias-attr.rs:8:7
+ |
+LL | #[doc(alias("bar"))]
+ | ^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
#[cfg(target_arch = "x86_64")]
#[inline(never)]
+#[allow(improper_ctypes_definitions)]
pub extern "sysv64" fn large_struct_by_val(mut foo: LargeStruct) -> LargeStruct {
foo.0 *= 1;
foo.1 *= 2;
#[repr(align(16))]
pub struct A(i64);
+#[allow(improper_ctypes_definitions)]
pub extern "C" fn foo(x: A) {}
fn main() {
--- /dev/null
+#![feature(lang_items)]
+
+trait Foo {
+ #[lang = "dummy_lang_item_1"] //~ ERROR definition
+ fn foo() {}
+
+ #[lang = "dummy_lang_item_2"] //~ ERROR definition
+ fn bar();
+
+ #[lang = "dummy_lang_item_3"] //~ ERROR definition
+ type MyType;
+}
+
+struct Bar;
+
+impl Bar {
+ #[lang = "dummy_lang_item_4"] //~ ERROR definition
+ fn test() {}
+}
+
+fn main() {}
--- /dev/null
+error[E0522]: definition of an unknown language item: `dummy_lang_item_1`
+ --> $DIR/assoc-lang-items.rs:4:5
+ |
+LL | #[lang = "dummy_lang_item_1"]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown language item `dummy_lang_item_1`
+
+error[E0522]: definition of an unknown language item: `dummy_lang_item_2`
+ --> $DIR/assoc-lang-items.rs:7:5
+ |
+LL | #[lang = "dummy_lang_item_2"]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown language item `dummy_lang_item_2`
+
+error[E0522]: definition of an unknown language item: `dummy_lang_item_3`
+ --> $DIR/assoc-lang-items.rs:10:5
+ |
+LL | #[lang = "dummy_lang_item_3"]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown language item `dummy_lang_item_3`
+
+error[E0522]: definition of an unknown language item: `dummy_lang_item_4`
+ --> $DIR/assoc-lang-items.rs:17:5
+ |
+LL | #[lang = "dummy_lang_item_4"]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown language item `dummy_lang_item_4`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0522`.
fn main() {
eq(foo::<u8>, bar::<u8>);
//~^ ERROR mismatched types
- //~| expected fn item `fn(_) -> _ {foo::<u8>}`
- //~| found fn item `fn(_) -> _ {bar::<u8>}`
- //~| expected fn item, found a different fn item
+ //~| expected fn item `fn(_) -> _ {foo::<u8>}`
+ //~| found fn item `fn(_) -> _ {bar::<u8>}`
+ //~| expected fn item, found a different fn item
+ //~| different `fn` items always have unique types, even if their signatures are the same
+ //~| change the expected type to be function pointer
+ //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
eq(foo::<u8>, foo::<i8>);
//~^ ERROR mismatched types
//~| expected `u8`, found `i8`
+ //~| different `fn` items always have unique types, even if their signatures are the same
+ //~| change the expected type to be function pointer
+ //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
eq(bar::<String>, bar::<Vec<u8>>);
//~^ ERROR mismatched types
- //~| expected fn item `fn(_) -> _ {bar::<std::string::String>}`
- //~| found fn item `fn(_) -> _ {bar::<std::vec::Vec<u8>>}`
- //~| expected struct `std::string::String`, found struct `std::vec::Vec`
+ //~| expected fn item `fn(_) -> _ {bar::<std::string::String>}`
+ //~| found fn item `fn(_) -> _ {bar::<std::vec::Vec<u8>>}`
+ //~| expected struct `std::string::String`, found struct `std::vec::Vec`
+ //~| different `fn` items always have unique types, even if their signatures are the same
+ //~| change the expected type to be function pointer
+ //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
// Make sure we distinguish between trait methods correctly.
eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
//~^ ERROR mismatched types
//~| expected `u8`, found `u16`
+ //~| different `fn` items always have unique types, even if their signatures are the same
+ //~| change the expected type to be function pointer
+ //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
+
+ eq(foo::<u8>, bar::<u8> as fn(isize) -> isize);
+ //~^ ERROR mismatched types
+ //~| expected fn item `fn(_) -> _ {foo::<u8>}`
+ //~| found fn pointer `fn(_) -> _`
+ //~| expected fn item, found fn pointer
+ //~| change the expected type to be function pointer
+ //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
+
+ eq(foo::<u8> as fn(isize) -> isize, bar::<u8>); // ok!
}
|
= note: expected fn item `fn(_) -> _ {foo::<u8>}`
found fn item `fn(_) -> _ {bar::<u8>}`
+ = note: different `fn` items always have unique types, even if their signatures are the same
+ = help: change the expected type to be function pointer `fn(isize) -> isize`
+ = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize`
error[E0308]: mismatched types
- --> $DIR/fn-item-type.rs:19:19
+ --> $DIR/fn-item-type.rs:22:19
|
LL | eq(foo::<u8>, foo::<i8>);
| ^^^^^^^^^ expected `u8`, found `i8`
|
= note: expected fn item `fn(_) -> _ {foo::<u8>}`
found fn item `fn(_) -> _ {foo::<i8>}`
+ = note: different `fn` items always have unique types, even if their signatures are the same
+ = help: change the expected type to be function pointer `fn(isize) -> isize`
+ = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize`
error[E0308]: mismatched types
- --> $DIR/fn-item-type.rs:23:23
+ --> $DIR/fn-item-type.rs:29:23
|
LL | eq(bar::<String>, bar::<Vec<u8>>);
| ^^^^^^^^^^^^^^ expected struct `std::string::String`, found struct `std::vec::Vec`
|
= note: expected fn item `fn(_) -> _ {bar::<std::string::String>}`
found fn item `fn(_) -> _ {bar::<std::vec::Vec<u8>>}`
+ = note: different `fn` items always have unique types, even if their signatures are the same
+ = help: change the expected type to be function pointer `fn(isize) -> isize`
+ = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `bar::<std::string::String> as fn(isize) -> isize`
error[E0308]: mismatched types
- --> $DIR/fn-item-type.rs:30:26
+ --> $DIR/fn-item-type.rs:39:26
|
LL | eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
| ^^^^^^^^^^^^^^^^^ expected `u8`, found `u16`
|
= note: expected fn item `fn() {<u8 as Foo>::foo}`
found fn item `fn() {<u16 as Foo>::foo}`
+ = note: different `fn` items always have unique types, even if their signatures are the same
+ = help: change the expected type to be function pointer `fn()`
+ = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `<u8 as Foo>::foo as fn()`
-error: aborting due to 4 previous errors
+error[E0308]: mismatched types
+ --> $DIR/fn-item-type.rs:46:19
+ |
+LL | eq(foo::<u8>, bar::<u8> as fn(isize) -> isize);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer
+ |
+ = note: expected fn item `fn(_) -> _ {foo::<u8>}`
+ found fn pointer `fn(_) -> _`
+ = help: change the expected type to be function pointer `fn(isize) -> isize`
+ = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize`
+
+error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0308`.
foo::<C>(); //~ ERROR: cannot find type `C` in this scope
foo::<D>(); //~ ERROR: cannot find type `D` in this scope
}
+
+mod other {
+ pub fn import() {}
+}
|
LL | import();
| ^^^^^^ not found in this scope
+ |
+help: consider importing this function
+ |
+LL | use other::import;
+ |
error[E0412]: cannot find type `A` in this scope
--> $DIR/glob-resolve1.rs:28:11
--- /dev/null
+// check-pass
+
+#![feature(impl_trait_in_bindings)]
+#![allow(incomplete_features)]
+
+struct A<'a>(&'a ());
+
+trait Trait<T> {}
+
+impl<T> Trait<T> for () {}
+
+pub fn foo<'a>() {
+ let _x: impl Trait<A<'a>> = ();
+}
+
+fn main() {}
-//
-// We get an error message at the top of file (dummy span).
-// This is not helpful, but also kind of annoying to prevent,
-// so for now just live with it.
-// This test case was originally for issue #2258.
-
// build-fail
trait ToOpt: Sized {
}
fn function<T:ToOpt + Clone>(counter: usize, t: T) {
-//~^ ERROR reached the recursion limit while instantiating `function::<std::option::Option<
if counter > 0 {
function(counter - 1, t.to_option());
- // FIXME(#4287) Error message should be here. It should be
- // a type error to instantiate `test` at a type other than T.
+ //~^ ERROR reached the recursion limit while instantiating `function::<std::option::Option<
}
}
error: reached the recursion limit while instantiating `function::<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<usize>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
- --> $DIR/infinite-instantiation.rs:25:1
+ --> $DIR/infinite-instantiation.rs:21:9
+ |
+LL | function(counter - 1, t.to_option());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: `function` defined here
+ --> $DIR/infinite-instantiation.rs:19:1
|
LL | / fn function<T:ToOpt + Clone>(counter: usize, t: T) {
-LL | |
LL | | if counter > 0 {
LL | | function(counter - 1, t.to_option());
-... |
+LL | |
LL | | }
LL | | }
| |_^
struct Empty;
// This used to cause an ICE
+#[allow(improper_ctypes_definitions)]
extern "C" fn ice(_a: Empty) {}
fn main() {
}
impl Foo {
+ #[allow(improper_ctypes_definitions)]
pub extern fn foo_new() -> Foo {
Foo { x: 21, y: 33 }
}
impl Test {
#[allow(dead_code)]
#[allow(unused_variables)]
+ #[allow(improper_ctypes_definitions)]
pub extern fn test(val: &str) {
}
pub struct Foo(i128);
#[no_mangle]
+#[allow(improper_ctypes_definitions)]
pub extern "C" fn foo(x: Foo) -> Foo { x }
fn main() {
struct Bar;
impl Foo for Bar {
+ #[allow(improper_ctypes_definitions)]
extern fn borrow(&self) {}
+ #[allow(improper_ctypes_definitions)]
extern fn take(self: Box<Self>) {}
}
}
fn rec<T>(mut it: T)
-//~^ ERROR reached the recursion limit while instantiating
where
T: Iterator,
{
T::count(it);
} else {
rec(identity(&mut it))
+ //~^ ERROR reached the recursion limit while instantiating
}
}
error: reached the recursion limit while instantiating `rec::<&mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut Empty>`
+ --> $DIR/issue-67552.rs:27:9
+ |
+LL | rec(identity(&mut it))
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: `rec` defined here
--> $DIR/issue-67552.rs:20:1
|
LL | / fn rec<T>(mut it: T)
-LL | |
LL | | where
LL | | T: Iterator,
+LL | | {
... |
LL | | }
LL | | }
// build-fail
-fn generic<T>() {
+fn generic<T>() { //~ WARN function cannot return without recursing
generic::<Option<T>>();
}
-//~^^^ ERROR reached the recursion limit while instantiating `generic::<std::option::Option<
-//~| WARN function cannot return without recursing
-
+//~^^ ERROR reached the recursion limit while instantiating `generic::<std::option::Option<
fn main () {
= help: a `loop` may express intention better if this is on purpose
error: reached the recursion limit while instantiating `generic::<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<std::option::Option<i32>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
+ --> $DIR/issue-8727.rs:7:5
+ |
+LL | generic::<Option<T>>();
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: `generic` defined here
--> $DIR/issue-8727.rs:6:1
|
LL | / fn generic<T>() {
--- /dev/null
+#![feature(rustc_private)]
+
+#![allow(private_in_public)]
+#![deny(improper_ctypes_definitions)]
+
+extern crate libc;
+
+use std::default::Default;
+use std::marker::PhantomData;
+
+trait Mirror { type It: ?Sized; }
+
+impl<T: ?Sized> Mirror for T { type It = Self; }
+
+#[repr(C)]
+pub struct StructWithProjection(*mut <StructWithProjection as Mirror>::It);
+
+#[repr(C)]
+pub struct StructWithProjectionAndLifetime<'a>(
+ &'a mut <StructWithProjectionAndLifetime<'a> as Mirror>::It
+);
+
+pub type I32Pair = (i32, i32);
+
+#[repr(C)]
+pub struct ZeroSize;
+
+pub type RustFn = fn();
+
+pub type RustBadRet = extern fn() -> Box<u32>;
+
+pub type CVoidRet = ();
+
+pub struct Foo;
+
+#[repr(transparent)]
+pub struct TransparentI128(i128);
+
+#[repr(transparent)]
+pub struct TransparentStr(&'static str);
+
+#[repr(transparent)]
+pub struct TransparentBadFn(RustBadRet);
+
+#[repr(transparent)]
+pub struct TransparentInt(u32);
+
+#[repr(transparent)]
+pub struct TransparentRef<'a>(&'a TransparentInt);
+
+#[repr(transparent)]
+pub struct TransparentLifetime<'a>(*const u8, PhantomData<&'a ()>);
+
+#[repr(transparent)]
+pub struct TransparentUnit<U>(f32, PhantomData<U>);
+
+#[repr(transparent)]
+pub struct TransparentCustomZst(i32, ZeroSize);
+
+#[repr(C)]
+pub struct ZeroSizeWithPhantomData(PhantomData<i32>);
+
+pub extern "C" fn ptr_type1(size: *const Foo) { }
+
+pub extern "C" fn ptr_type2(size: *const Foo) { }
+
+pub extern "C" fn slice_type(p: &[u32]) { }
+//~^ ERROR: uses type `[u32]`
+
+pub extern "C" fn str_type(p: &str) { }
+//~^ ERROR: uses type `str`
+
+pub extern "C" fn box_type(p: Box<u32>) { }
+//~^ ERROR uses type `std::boxed::Box<u32>`
+
+pub extern "C" fn char_type(p: char) { }
+//~^ ERROR uses type `char`
+
+pub extern "C" fn i128_type(p: i128) { }
+//~^ ERROR uses type `i128`
+
+pub extern "C" fn u128_type(p: u128) { }
+//~^ ERROR uses type `u128`
+
+pub extern "C" fn tuple_type(p: (i32, i32)) { }
+//~^ ERROR uses type `(i32, i32)`
+
+pub extern "C" fn tuple_type2(p: I32Pair) { }
+//~^ ERROR uses type `(i32, i32)`
+
+pub extern "C" fn zero_size(p: ZeroSize) { }
+//~^ ERROR uses type `ZeroSize`
+
+pub extern "C" fn zero_size_phantom(p: ZeroSizeWithPhantomData) { }
+//~^ ERROR uses type `ZeroSizeWithPhantomData`
+
+pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData<bool> {
+//~^ ERROR uses type `std::marker::PhantomData<bool>`
+ Default::default()
+}
+
+pub extern "C" fn fn_type(p: RustFn) { }
+//~^ ERROR uses type `fn()`
+
+pub extern "C" fn fn_type2(p: fn()) { }
+//~^ ERROR uses type `fn()`
+
+pub extern "C" fn fn_contained(p: RustBadRet) { }
+//~^ ERROR: uses type `std::boxed::Box<u32>`
+
+pub extern "C" fn transparent_i128(p: TransparentI128) { }
+//~^ ERROR: uses type `i128`
+
+pub extern "C" fn transparent_str(p: TransparentStr) { }
+//~^ ERROR: uses type `str`
+
+pub extern "C" fn transparent_fn(p: TransparentBadFn) { }
+//~^ ERROR: uses type `std::boxed::Box<u32>`
+
+pub extern "C" fn good3(fptr: Option<extern fn()>) { }
+
+pub extern "C" fn good4(aptr: &[u8; 4 as usize]) { }
+
+pub extern "C" fn good5(s: StructWithProjection) { }
+
+pub extern "C" fn good6(s: StructWithProjectionAndLifetime) { }
+
+pub extern "C" fn good7(fptr: extern fn() -> ()) { }
+
+pub extern "C" fn good8(fptr: extern fn() -> !) { }
+
+pub extern "C" fn good9() -> () { }
+
+pub extern "C" fn good10() -> CVoidRet { }
+
+pub extern "C" fn good11(size: isize) { }
+
+pub extern "C" fn good12(size: usize) { }
+
+pub extern "C" fn good13(n: TransparentInt) { }
+
+pub extern "C" fn good14(p: TransparentRef) { }
+
+pub extern "C" fn good15(p: TransparentLifetime) { }
+
+pub extern "C" fn good16(p: TransparentUnit<ZeroSize>) { }
+
+pub extern "C" fn good17(p: TransparentCustomZst) { }
+
+#[allow(improper_ctypes_definitions)]
+pub extern "C" fn good18(_: &String) { }
+
+#[cfg(not(target_arch = "wasm32"))]
+pub extern "C" fn good1(size: *const libc::c_int) { }
+
+#[cfg(not(target_arch = "wasm32"))]
+pub extern "C" fn good2(size: *const libc::c_uint) { }
+
+pub extern "C" fn unused_generic1<T>(size: *const Foo) { }
+
+pub extern "C" fn unused_generic2<T>() -> PhantomData<bool> {
+//~^ ERROR uses type `std::marker::PhantomData<bool>`
+ Default::default()
+}
+
+pub extern "C" fn used_generic1<T>(x: T) { }
+
+pub extern "C" fn used_generic2<T>(x: T, size: *const Foo) { }
+
+pub extern "C" fn used_generic3<T: Default>() -> T {
+ Default::default()
+}
+
+pub extern "C" fn used_generic4<T>(x: Vec<T>) { }
+//~^ ERROR: uses type `std::vec::Vec<T>`
+
+pub extern "C" fn used_generic5<T>() -> Vec<T> {
+//~^ ERROR: uses type `std::vec::Vec<T>`
+ Default::default()
+}
+
+fn main() {}
--- /dev/null
+error: `extern` fn uses type `[u32]`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:67:33
+ |
+LL | pub extern "C" fn slice_type(p: &[u32]) { }
+ | ^^^^^^ not FFI-safe
+ |
+note: the lint level is defined here
+ --> $DIR/lint-ctypes-fn.rs:4:9
+ |
+LL | #![deny(improper_ctypes_definitions)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: consider using a raw pointer instead
+ = note: slices have no C equivalent
+
+error: `extern` fn uses type `str`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:70:31
+ |
+LL | pub extern "C" fn str_type(p: &str) { }
+ | ^^^^ not FFI-safe
+ |
+ = help: consider using `*const u8` and a length instead
+ = note: string slices have no C equivalent
+
+error: `extern` fn uses type `std::boxed::Box<u32>`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:73:31
+ |
+LL | pub extern "C" fn box_type(p: Box<u32>) { }
+ | ^^^^^^^^ not FFI-safe
+ |
+ = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
+ = note: this struct has unspecified layout
+
+error: `extern` fn uses type `char`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:76:32
+ |
+LL | pub extern "C" fn char_type(p: char) { }
+ | ^^^^ not FFI-safe
+ |
+ = help: consider using `u32` or `libc::wchar_t` instead
+ = note: the `char` type has no C equivalent
+
+error: `extern` fn uses type `i128`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:79:32
+ |
+LL | pub extern "C" fn i128_type(p: i128) { }
+ | ^^^^ not FFI-safe
+ |
+ = note: 128-bit integers don't currently have a known stable ABI
+
+error: `extern` fn uses type `u128`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:82:32
+ |
+LL | pub extern "C" fn u128_type(p: u128) { }
+ | ^^^^ not FFI-safe
+ |
+ = note: 128-bit integers don't currently have a known stable ABI
+
+error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:85:33
+ |
+LL | pub extern "C" fn tuple_type(p: (i32, i32)) { }
+ | ^^^^^^^^^^ not FFI-safe
+ |
+ = help: consider using a struct instead
+ = note: tuples have unspecified layout
+
+error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:88:34
+ |
+LL | pub extern "C" fn tuple_type2(p: I32Pair) { }
+ | ^^^^^^^ not FFI-safe
+ |
+ = help: consider using a struct instead
+ = note: tuples have unspecified layout
+
+error: `extern` fn uses type `ZeroSize`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:91:32
+ |
+LL | pub extern "C" fn zero_size(p: ZeroSize) { }
+ | ^^^^^^^^ not FFI-safe
+ |
+ = help: consider adding a member to this struct
+ = note: this struct has no fields
+note: the type is defined here
+ --> $DIR/lint-ctypes-fn.rs:26:1
+ |
+LL | pub struct ZeroSize;
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: `extern` fn uses type `ZeroSizeWithPhantomData`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:94:40
+ |
+LL | pub extern "C" fn zero_size_phantom(p: ZeroSizeWithPhantomData) { }
+ | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
+ |
+ = note: composed only of `PhantomData`
+note: the type is defined here
+ --> $DIR/lint-ctypes-fn.rs:61:1
+ |
+LL | pub struct ZeroSizeWithPhantomData(PhantomData<i32>);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `extern` fn uses type `std::marker::PhantomData<bool>`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:97:51
+ |
+LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData<bool> {
+ | ^^^^^^^^^^^^^^^^^ not FFI-safe
+ |
+ = note: composed only of `PhantomData`
+
+error: `extern` fn uses type `fn()`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:102:30
+ |
+LL | pub extern "C" fn fn_type(p: RustFn) { }
+ | ^^^^^^ not FFI-safe
+ |
+ = help: consider using an `extern fn(...) -> ...` function pointer instead
+ = note: this function pointer has Rust-specific calling convention
+
+error: `extern` fn uses type `fn()`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:105:31
+ |
+LL | pub extern "C" fn fn_type2(p: fn()) { }
+ | ^^^^ not FFI-safe
+ |
+ = help: consider using an `extern fn(...) -> ...` function pointer instead
+ = note: this function pointer has Rust-specific calling convention
+
+error: `extern` fn uses type `std::boxed::Box<u32>`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:108:35
+ |
+LL | pub extern "C" fn fn_contained(p: RustBadRet) { }
+ | ^^^^^^^^^^ not FFI-safe
+ |
+ = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
+ = note: this struct has unspecified layout
+
+error: `extern` fn uses type `i128`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:111:39
+ |
+LL | pub extern "C" fn transparent_i128(p: TransparentI128) { }
+ | ^^^^^^^^^^^^^^^ not FFI-safe
+ |
+ = note: 128-bit integers don't currently have a known stable ABI
+
+error: `extern` fn uses type `str`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:114:38
+ |
+LL | pub extern "C" fn transparent_str(p: TransparentStr) { }
+ | ^^^^^^^^^^^^^^ not FFI-safe
+ |
+ = help: consider using `*const u8` and a length instead
+ = note: string slices have no C equivalent
+
+error: `extern` fn uses type `std::boxed::Box<u32>`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:117:37
+ |
+LL | pub extern "C" fn transparent_fn(p: TransparentBadFn) { }
+ | ^^^^^^^^^^^^^^^^ not FFI-safe
+ |
+ = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
+ = note: this struct has unspecified layout
+
+error: `extern` fn uses type `std::marker::PhantomData<bool>`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:161:43
+ |
+LL | pub extern "C" fn unused_generic2<T>() -> PhantomData<bool> {
+ | ^^^^^^^^^^^^^^^^^ not FFI-safe
+ |
+ = note: composed only of `PhantomData`
+
+error: `extern` fn uses type `std::vec::Vec<T>`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:174:39
+ |
+LL | pub extern "C" fn used_generic4<T>(x: Vec<T>) { }
+ | ^^^^^^ not FFI-safe
+ |
+ = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
+ = note: this struct has unspecified layout
+
+error: `extern` fn uses type `std::vec::Vec<T>`, which is not FFI-safe
+ --> $DIR/lint-ctypes-fn.rs:177:41
+ |
+LL | pub extern "C" fn used_generic5<T>() -> Vec<T> {
+ | ^^^^^^ not FFI-safe
+ |
+ = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
+ = note: this struct has unspecified layout
+
+error: aborting due to 20 previous errors
+
#![deny(confusable_idents)]
#![allow(uncommon_codepoints, non_upper_case_globals)]
-const s: usize = 42; //~ ERROR identifier pair considered confusable
+const s: usize = 42;
fn main() {
- let s = "rust";
+ let s = "rust"; //~ ERROR identifier pair considered confusable
+ not_affected();
+}
+
+fn not_affected() {
+ let s1 = 1;
+ let sl = 'l';
}
-error: identifier pair considered confusable between `s` and `s`
- --> $DIR/lint-confusable-idents.rs:5:7
+error: identifier pair considered confusable between `s` and `s`
+ --> $DIR/lint-confusable-idents.rs:8:9
|
LL | const s: usize = 42;
- | ^^
+ | -- this is where the previous identifier occurred
...
LL | let s = "rust";
- | - this is where the previous identifier occurred
+ | ^
|
note: the lint level is defined here
--> $DIR/lint-confusable-idents.rs:2:9
--- /dev/null
+// check-pass
+#![feature(non_ascii_idents)]
+#![deny(mixed_script_confusables)]
+
+struct ΑctuallyNotLatin;
+
+fn main() {
+ let λ = 42; // this usage of Greek confirms that Greek is used intentionally.
+}
+
+mod роре {
+ const エ: &'static str = "アイウ";
+
+ // this usage of Katakana confirms that Katakana is used intentionally.
+ fn ニャン() {
+ let д: usize = 100; // this usage of Cyrillic confirms that Cyrillic is used intentionally.
+
+ println!("meow!");
+ }
+}
--- /dev/null
+#![feature(non_ascii_idents)]
+#![deny(mixed_script_confusables)]
+
+struct ΑctuallyNotLatin;
+//~^ ERROR The usage of Script Group `Greek` in this crate consists solely of
+
+fn main() {
+ let v = ΑctuallyNotLatin;
+}
+
+mod роре {
+//~^ ERROR The usage of Script Group `Cyrillic` in this crate consists solely of
+ const エ: &'static str = "アイウ";
+ //~^ ERROR The usage of Script Group `Japanese, Katakana` in this crate consists solely of
+}
--- /dev/null
+error: The usage of Script Group `Greek` in this crate consists solely of mixed script confusables
+ --> $DIR/lint-mixed-script-confusables.rs:4:8
+ |
+LL | struct ΑctuallyNotLatin;
+ | ^^^^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/lint-mixed-script-confusables.rs:2:9
+ |
+LL | #![deny(mixed_script_confusables)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: The usage includes 'Α' (U+0391).
+ = note: Please recheck to make sure their usages are indeed what you want.
+
+error: The usage of Script Group `Cyrillic` in this crate consists solely of mixed script confusables
+ --> $DIR/lint-mixed-script-confusables.rs:11:5
+ |
+LL | mod роре {
+ | ^^^^
+ |
+ = note: The usage includes 'е' (U+0435), 'о' (U+043E), 'р' (U+0440).
+ = note: Please recheck to make sure their usages are indeed what you want.
+
+error: The usage of Script Group `Japanese, Katakana` in this crate consists solely of mixed script confusables
+ --> $DIR/lint-mixed-script-confusables.rs:13:11
+ |
+LL | const エ: &'static str = "アイウ";
+ | ^^
+ |
+ = note: The usage includes 'エ' (U+30A8).
+ = note: Please recheck to make sure their usages are indeed what you want.
+
+error: aborting due to 3 previous errors
+
fn main() {
let naïveté = 2; //~ ERROR identifier contains non-ASCII characters
- println!("{}", naïveté); //~ ERROR identifier contains non-ASCII characters
+
+ // using the same identifier the second time won't trigger the lint.
+ println!("{}", naïveté);
}
LL | let naïveté = 2;
| ^^^^^^^
-error: identifier contains non-ASCII characters
- --> $DIR/lint-non-ascii-idents.rs:10:20
- |
-LL | println!("{}", naïveté);
- | ^^^^^^^
-
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
fn main() {
let ㇻㇲㇳ = "rust"; //~ ERROR identifier contains uncommon Unicode codepoints
- println!("{}", ㇻㇲㇳ); //~ ERROR identifier contains uncommon Unicode codepoints
+
+ // using the same identifier the second time won't trigger the lint.
+ println!("{}", ㇻㇲㇳ);
}
LL | let ㇻㇲㇳ = "rust";
| ^^^^^^
-error: identifier contains uncommon Unicode codepoints
- --> $DIR/lint-uncommon-codepoints.rs:10:20
- |
-LL | println!("{}", ㇻㇲㇳ);
- | ^^^^^^
-
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
// run-pass
+#[allow(improper_ctypes_definitions)]
pub extern "C" fn tuple2() -> (u16, u8) {
(1, 2)
}
+#[allow(improper_ctypes_definitions)]
pub extern "C" fn tuple3() -> (u8, u8, u8) {
(1, 2, 3)
}
Two::two()
}
+#[allow(improper_ctypes_definitions)]
extern fn simple_extern(x: u32, y: (u32, u32)) -> u32 {
x + y.0 * y.1
}
fn oob_error_for_slices() {
let a: *const [_] = &[1, 2, 3];
unsafe {
- let _b = (*a)[3]; //~ ERROR this operation will panic at runtime [unconditional_panic]
+ let _b = (*a)[3];
}
}
LL | let _z = 1 % y;
| ^^^^^ attempt to calculate the remainder with a divisor of zero
-error: this operation will panic at runtime
- --> $DIR/mir_detects_invalid_ops.rs:22:18
- |
-LL | let _b = (*a)[3];
- | ^^^^^^^ index out of bounds: the len is 3 but the index is 3
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
|
LL | use m2::S;
|
-LL | use namespace_mix::xm2::S;
+LL | use xm2::S;
|
error[E0423]: expected value, found type alias `xm1::S`
|
LL | use m2::S;
|
-LL | use namespace_mix::xm2::S;
+LL | use xm2::S;
|
error[E0423]: expected value, found struct variant `m7::V`
|
LL | use m8::V;
|
-LL | use namespace_mix::xm8::V;
+LL | use xm8::V;
|
error[E0423]: expected value, found struct variant `xm7::V`
|
LL | use m8::V;
|
-LL | use namespace_mix::xm8::V;
+LL | use xm8::V;
|
error[E0277]: the trait bound `c::Item: Impossible` is not satisfied
--- /dev/null
+#![feature(never_type, specialization)]
+#![allow(incomplete_features)]
+
+use std::iter::{self, Empty};
+
+trait Trait {
+ type Out: Iterator<Item = u32>;
+
+ fn f(&self) -> Option<Self::Out>;
+}
+
+impl<T> Trait for T {
+ default type Out = !; //~ ERROR: `!` is not an iterator
+
+ default fn f(&self) -> Option<Self::Out> {
+ None
+ }
+}
+
+struct X;
+
+impl Trait for X {
+ type Out = Empty<u32>;
+
+ fn f(&self) -> Option<Self::Out> {
+ Some(iter::empty())
+ }
+}
+
+fn f<T: Trait>(a: T) {
+ if let Some(iter) = a.f() {
+ println!("Some");
+ for x in iter {
+ println!("x = {}", x);
+ }
+ }
+}
+
+pub fn main() {
+ f(10);
+}
--- /dev/null
+error[E0277]: `!` is not an iterator
+ --> $DIR/issue-51506.rs:13:5
+ |
+LL | type Out: Iterator<Item = u32>;
+ | ------------------------------- required by `Trait::Out`
+...
+LL | default type Out = !;
+ | ^^^^^^^^^^^^^^^^^^^^^ `!` is not an iterator
+ |
+ = help: the trait `std::iter::Iterator` is not implemented for `!`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
// ignore-tidy-trailing-newlines
// error-pattern: aborting due to 3 previous errors
+#![allow(uncommon_codepoints)]
+
y![
Ϥ,
\ No newline at end of file
error: this file contains an unclosed delimiter
- --> $DIR/issue-62524.rs:4:3
+ --> $DIR/issue-62524.rs:6:3
|
LL | y![
| - unclosed delimiter
| ^
error: macros that expand to items must be delimited with braces or followed by a semicolon
- --> $DIR/issue-62524.rs:3:3
+ --> $DIR/issue-62524.rs:5:3
|
LL | y![
| ___^
| ^
error: cannot find macro `y` in this scope
- --> $DIR/issue-62524.rs:3:1
+ --> $DIR/issue-62524.rs:5:1
|
LL | y![
| ^
--- /dev/null
+// force-host
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::{TokenStream, TokenTree, Group, Delimiter};
+
+#[proc_macro_attribute]
+pub fn first(_attr: TokenStream, item: TokenStream) -> TokenStream {
+ let tokens: TokenStream = "#[derive(Second)]".parse().unwrap();
+ let wrapped = TokenTree::Group(Group::new(Delimiter::None, item.into_iter().collect()));
+ tokens.into_iter().chain(std::iter::once(wrapped)).collect()
+}
+
+#[proc_macro_derive(Second)]
+pub fn second(item: TokenStream) -> TokenStream {
+ TokenStream::new()
+}
--- /dev/null
+// force-host
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+use proc_macro::TokenStream;
+
+#[proc_macro]
+pub fn recollect(tokens: TokenStream) -> TokenStream {
+ tokens.into_iter().collect()
+}
--- /dev/null
+// force-host
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::{TokenStream, TokenTree, Group};
+
+fn find_my_ident(tokens: TokenStream) -> Option<TokenStream> {
+ for token in tokens {
+ if let TokenTree::Ident(ident) = &token {
+ if ident.to_string() == "hidden_ident" {
+ return Some(vec![token].into_iter().collect())
+ }
+ } else if let TokenTree::Group(g) = token {
+ if let Some(stream) = find_my_ident(g.stream()) {
+ return Some(stream)
+ }
+ }
+ }
+ return None;
+}
+
+
+#[proc_macro_derive(WeirdDerive)]
+pub fn weird_derive(item: TokenStream) -> TokenStream {
+ let my_ident = find_my_ident(item).expect("Missing 'my_ident'!");
+ let tokens: TokenStream = "call_it!();".parse().unwrap();
+ let final_call = tokens.into_iter().map(|tree| {
+ if let TokenTree::Group(g) = tree {
+ return Group::new(g.delimiter(), my_ident.clone()).into()
+ } else {
+ return tree
+ }
+ }).collect();
+ final_call
+}
+
+#[proc_macro]
+pub fn recollect(item: TokenStream) -> TokenStream {
+ item.into_iter().collect()
+}
+
+#[proc_macro_attribute]
+pub fn recollect_attr(_attr: TokenStream, mut item: TokenStream) -> TokenStream {
+ item.into_iter().collect()
+}
--- /dev/null
+// aux-build:test-macros.rs
+// check-pass
+
+extern crate test_macros;
+use test_macros::recollect;
+
+macro_rules! use_expr {
+ ($expr:expr) => {
+ recollect!($expr)
+ }
+}
+
+#[allow(dead_code)]
+struct Foo;
+impl Foo {
+ #[allow(dead_code)]
+ fn use_self(self) {
+ drop(use_expr!(self));
+ }
+}
+
+fn main() {}
--- /dev/null
+// aux-build:first-second.rs
+// FIXME: The spans here are bad, see PR #73084
+
+extern crate first_second;
+use first_second::*;
+
+macro_rules! produce_it {
+ ($name:ident) => {
+ #[first] //~ ERROR cannot find type
+ struct $name {
+ field: MissingType
+ }
+ }
+}
+
+produce_it!(MyName);
+
+fn main() {
+ println!("Hello, world!");
+}
--- /dev/null
+error[E0412]: cannot find type `MissingType` in this scope
+ --> $DIR/macro-rules-derive.rs:9:9
+ |
+LL | #[first]
+ | ^^^^^^^^ not found in this scope
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0412`.
--- /dev/null
+// aux-build:weird-hygiene.rs
+// check-pass
+// FIXME: This should actually error, see PR #73084
+
+#![feature(stmt_expr_attributes)]
+#![feature(proc_macro_hygiene)]
+
+extern crate weird_hygiene;
+use weird_hygiene::*;
+
+macro_rules! other {
+ ($tokens:expr) => {
+ macro_rules! call_it {
+ ($outer_ident:ident) => {
+ macro_rules! inner {
+ () => {
+ $outer_ident;
+ }
+ }
+ }
+ }
+
+ #[derive(WeirdDerive)]
+ enum MyEnum {
+ Value = (stringify!($tokens + hidden_ident), 1).1
+ }
+
+ inner!();
+ }
+}
+
+macro_rules! invoke_it {
+ ($token:expr) => {
+ #[recollect_attr] {
+ $token;
+ hidden_ident
+ }
+ }
+}
+
+fn main() {
+ // `other` and `invoke_it` are both macro_rules! macros,
+ // so it should be impossible for them to ever see `hidden_ident`,
+ // even if they invoke a proc macro.
+ let hidden_ident = "Hello1";
+ other!(50);
+ invoke_it!(25);
+}
--- /dev/null
+type Range = std::ops::Range<usize>;
+
+fn demo(r: &Range) {
+ println!("{:?}", r);
+}
+
+fn tell(x: usize) -> usize {
+ x
+}
+
+fn main() {
+ demo(tell(1)..tell(10));
+ //~^ ERROR mismatched types
+ demo(1..10);
+ //~^ ERROR mismatched types
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/issue-73553-misinterp-range-literal.rs:12:10
+ |
+LL | demo(tell(1)..tell(10));
+ | ^^^^^^^^^^^^^^^^^
+ | |
+ | expected reference, found struct `std::ops::Range`
+ | help: consider borrowing here: `&(tell(1)..tell(10))`
+ |
+ = note: expected reference `&std::ops::Range<usize>`
+ found struct `std::ops::Range<usize>`
+
+error[E0308]: mismatched types
+ --> $DIR/issue-73553-misinterp-range-literal.rs:14:10
+ |
+LL | demo(1..10);
+ | ^^^^^
+ | |
+ | expected reference, found struct `std::ops::Range`
+ | help: consider borrowing here: `&(1..10)`
+ |
+ = note: expected reference `&std::ops::Range<usize>`
+ found struct `std::ops::Range<{integer}>`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
error: reached the recursion limit while instantiating `std::intrinsics::drop_in_place::<S<fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(u32))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))>> - shim(Some(S<fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(u32))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))>))`
+ --> $SRC_DIR/libcore/ptr/mod.rs:LL:COL
+ |
+LL | / pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+LL | | // Code here does not matter - this is replaced by the
+LL | | // real drop glue by the compiler.
+LL | | drop_in_place(to_drop)
+LL | | }
+ | |_^
+ |
+note: `std::intrinsics::drop_in_place` defined here
+ --> $SRC_DIR/libcore/ptr/mod.rs:LL:COL
+ |
+LL | / pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+LL | | // Code here does not matter - this is replaced by the
+LL | | // real drop glue by the compiler.
+LL | | drop_in_place(to_drop)
+LL | | }
+ | |_^
error: aborting due to previous error
self.head * other.head + self.tail.dot(other.tail)
}
}
-fn test<T:Dot> (n:isize, i:isize, first:T, second:T) ->isize { //~ ERROR recursion limit
+fn test<T:Dot> (n:isize, i:isize, first:T, second:T) ->isize {
match n { 0 => {first.dot(second)}
- // FIXME(#4287) Error message should be here. It should be
- // a type error to instantiate `test` at a type other than T.
_ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})}
+ //~^ ERROR recursion limit
}
}
pub fn main() {
error: reached the recursion limit while instantiating `test::<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Nil>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
+ --> $DIR/recursion.rs:17:11
+ |
+LL | _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: `test` defined here
--> $DIR/recursion.rs:15:1
|
LL | / fn test<T:Dot> (n:isize, i:isize, first:T, second:T) ->isize {
LL | | match n { 0 => {first.dot(second)}
-LL | | // FIXME(#4287) Error message should be here. It should be
-LL | | // a type error to instantiate `test` at a type other than T.
LL | | _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})}
+LL | |
LL | | }
LL | | }
| |_^
LL | impl T for Foo { }
| ^ not found in this scope
|
-help: consider importing this trait
+help: consider importing one of these items
+ |
+LL | use baz::T;
|
LL | use foo::bar::T;
|
| ^
help: consider importing this enum
|
-LL | use m::n::Z;
+LL | use m::Z;
|
error[E0423]: expected value, found enum `m::n::Z`
| ^
help: consider importing this enum
|
-LL | use m::n::Z;
+LL | use m::Z;
|
error[E0412]: cannot find type `Z` in this scope
| ^
help: consider importing this enum
|
-LL | use m::n::Z;
+LL | use m::Z;
|
error[E0423]: expected value, found struct variant `m::n::Z::Struct`
| ^
help: consider importing this enum
|
-LL | use m::n::Z;
+LL | use m::Z;
|
error[E0603]: enum `Z` is private
--- /dev/null
+#![crate_type = "lib"]
+#![feature(specialization)]
+#![feature(unsize, coerce_unsized)]
+#![allow(incomplete_features)]
+
+use std::ops::CoerceUnsized;
+
+pub struct SmartassPtr<A: Smartass+?Sized>(A::Data);
+
+pub trait Smartass {
+ type Data;
+ type Data2: CoerceUnsized<*const [u8]>;
+}
+
+pub trait MaybeObjectSafe {}
+
+impl MaybeObjectSafe for () {}
+
+impl<T> Smartass for T {
+ type Data = <Self as Smartass>::Data2;
+ default type Data2 = ();
+ //~^ ERROR: the trait bound `(): std::ops::CoerceUnsized<*const [u8]>` is not satisfied
+}
+
+impl Smartass for () {
+ type Data2 = *const [u8; 1];
+}
+
+impl Smartass for dyn MaybeObjectSafe {
+ type Data = *const [u8];
+ type Data2 = *const [u8; 0];
+}
+
+impl<U: Smartass+?Sized, T: Smartass+?Sized> CoerceUnsized<SmartassPtr<T>> for SmartassPtr<U>
+ where <U as Smartass>::Data: std::ops::CoerceUnsized<<T as Smartass>::Data>
+{}
+
+pub fn conv(s: SmartassPtr<()>) -> SmartassPtr<dyn MaybeObjectSafe> {
+ s
+}
--- /dev/null
+error[E0277]: the trait bound `(): std::ops::CoerceUnsized<*const [u8]>` is not satisfied
+ --> $DIR/issue-44861.rs:21:5
+ |
+LL | type Data2: CoerceUnsized<*const [u8]>;
+ | --------------------------------------- required by `Smartass::Data2`
+...
+LL | default type Data2 = ();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::ops::CoerceUnsized<*const [u8]>` is not implemented for `()`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+#![feature(specialization)]
+#![allow(incomplete_features)]
+
+struct MyStruct {}
+
+trait MyTrait {
+ type MyType: Default;
+}
+
+impl MyTrait for i32 {
+ default type MyType = MyStruct;
+ //~^ ERROR: the trait bound `MyStruct: std::default::Default` is not satisfied
+}
+
+fn main() {
+ let _x: <i32 as MyTrait>::MyType = <i32 as MyTrait>::MyType::default();
+}
--- /dev/null
+error[E0277]: the trait bound `MyStruct: std::default::Default` is not satisfied
+ --> $DIR/issue-59435.rs:11:5
+ |
+LL | type MyType: Default;
+ | --------------------- required by `MyTrait::MyType`
+...
+LL | default type MyType = MyStruct;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::default::Default` is not implemented for `MyStruct`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// check-pass
+//
+// rust-lang/rust#73592: borrow_mut through Deref should work.
+//
+// Before #72280, when we see something like `&mut *rcvr.method()`, we
+// incorrectly requires `rcvr` to be type-checked as a mut place. While this
+// requirement is usually correct for smart pointers, it is overly restrictive
+// for types like `Mutex` or `RefCell` which can produce a guard that
+// implements `DerefMut` from `&self`.
+//
+// Making it more confusing, because we use Deref as the fallback when DerefMut
+// is implemented, we won't see an issue when the smart pointer does not
+// implement `DerefMut`. It only causes an issue when `rcvr` is obtained via a
+// type that implements both `Deref` or `DerefMut`.
+//
+// This bug is only discovered in #73592 after it is already fixed as a side-effect
+// of a refactoring made in #72280.
+
+#![warn(unused_mut)]
+
+use std::pin::Pin;
+use std::cell::RefCell;
+
+struct S(RefCell<()>);
+
+fn test_pin(s: Pin<&S>) {
+ // This works before #72280.
+ let _ = &mut *s.0.borrow_mut();
+}
+
+fn test_pin_mut(s: Pin<&mut S>) {
+ // This should compile but didn't before #72280.
+ let _ = &mut *s.0.borrow_mut();
+}
+
+fn test_vec(s: &Vec<RefCell<()>>) {
+ // This should compile but didn't before #72280.
+ let _ = &mut *s[0].borrow_mut();
+}
+
+fn test_mut_pin(mut s: Pin<&S>) {
+ //~^ WARN variable does not need to be mutable
+ let _ = &mut *s.0.borrow_mut();
+}
+
+fn test_mut_pin_mut(mut s: Pin<&mut S>) {
+ //~^ WARN variable does not need to be mutable
+ let _ = &mut *s.0.borrow_mut();
+}
+
+fn main() {
+ let mut s = S(RefCell::new(()));
+ test_pin(Pin::new(&s));
+ test_pin_mut(Pin::new(&mut s));
+ test_mut_pin(Pin::new(&s));
+ test_mut_pin_mut(Pin::new(&mut s));
+ test_vec(&vec![s.0]);
+}
--- /dev/null
+warning: variable does not need to be mutable
+ --> $DIR/issue-73592-borrow_mut-through-deref.rs:41:17
+ |
+LL | fn test_mut_pin(mut s: Pin<&S>) {
+ | ----^
+ | |
+ | help: remove this `mut`
+ |
+note: the lint level is defined here
+ --> $DIR/issue-73592-borrow_mut-through-deref.rs:19:9
+ |
+LL | #![warn(unused_mut)]
+ | ^^^^^^^^^^
+
+warning: variable does not need to be mutable
+ --> $DIR/issue-73592-borrow_mut-through-deref.rs:46:21
+ |
+LL | fn test_mut_pin_mut(mut s: Pin<&mut S>) {
+ | ----^
+ | |
+ | help: remove this `mut`
+
+warning: 2 warnings emitted
+
+#![allow(mixed_script_confusables)]
+
fn foo<
'β, //~ ERROR non-ascii idents are not fully supported
γ //~ ERROR non-ascii idents are not fully supported
error[E0658]: non-ascii idents are not fully supported
- --> $DIR/utf8_idents.rs:2:5
+ --> $DIR/utf8_idents.rs:4:5
|
LL | 'β,
| ^^
= help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
error[E0658]: non-ascii idents are not fully supported
- --> $DIR/utf8_idents.rs:3:5
+ --> $DIR/utf8_idents.rs:5:5
|
LL | γ
| ^
= help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
error[E0658]: non-ascii idents are not fully supported
- --> $DIR/utf8_idents.rs:8:5
+ --> $DIR/utf8_idents.rs:10:5
|
LL | δ: usize
| ^
= help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
error[E0658]: non-ascii idents are not fully supported
- --> $DIR/utf8_idents.rs:12:9
+ --> $DIR/utf8_idents.rs:14:9
|
LL | let α = 0.00001f64;
| ^
= help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
warning: type parameter `γ` should have an upper camel case name
- --> $DIR/utf8_idents.rs:3:5
+ --> $DIR/utf8_idents.rs:5:5
|
LL | γ
| ^ help: convert the identifier to upper camel case: `Γ`
#![deny(warnings)]
use serde::Serialize;
-use toml;
use std::collections::BTreeMap;
use std::collections::HashMap;
-Subproject commit 089cbb80b73ba242efdcf5430e89f63fa3b5328d
+Subproject commit c26576f9adddd254b3dd63aecba176434290a9f6
+++ /dev/null
-<!--
-Hi there! Whether you've come to make a suggestion for a new lint, an improvement to an existing lint or to report a bug or a false positive in Clippy, you've come to the right place.
-
-For bug reports and false positives, please include the output of `cargo clippy -V` in the report.
-
-Thank you for using Clippy!
-
-Write your comment below this line: -->
--- /dev/null
+---
+name: Blank Issue
+about: Create a blank issue.
+---
--- /dev/null
+---
+name: Bug Report
+about: Create a bug report for Clippy
+labels: L-bug
+---
+<!--
+Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
+along with any information you feel relevant to replicating the bug.
+-->
+
+I tried this code:
+
+```rust
+<code>
+```
+
+I expected to see this happen: *explanation*
+
+Instead, this happened: *explanation*
+
+### Meta
+
+- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20)
+- `rustc -Vv`:
+ ```
+ rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+ binary: rustc
+ commit-hash: f455e46eae1a227d735091091144601b467e1565
+ commit-date: 2020-06-20
+ host: x86_64-unknown-linux-gnu
+ release: 1.46.0-nightly
+ LLVM version: 10.0
+ ```
+
+<!--
+Include a backtrace in the code block by setting `RUST_BACKTRACE=1` in your
+environment. E.g. `RUST_BACKTRACE=1 cargo clippy`.
+-->
+<details><summary>Backtrace</summary>
+ <p>
+
+ ```
+ <backtrace>
+ ```
+
+ </p>
+</details>
--- /dev/null
+blank_issues_enabled: true
+contact_links:
+ - name: Rust Programming Language Forum
+ url: https://users.rust-lang.org
+ about: Please ask and answer questions about Rust here.
--- /dev/null
+---
+name: Internal Compiler Error
+about: Create a report for an internal compiler error in Clippy.
+labels: L-bug, L-crash
+---
+<!--
+Thank you for finding an Internal Compiler Error! 🧊 If possible, try to provide
+a minimal verifiable example. You can read "Rust Bug Minimization Patterns" for
+how to create smaller examples.
+
+http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/
+
+-->
+
+### Code
+
+```rust
+<code>
+```
+
+### Meta
+
+- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20)
+- `rustc -Vv`:
+ ```
+ rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+ binary: rustc
+ commit-hash: f455e46eae1a227d735091091144601b467e1565
+ commit-date: 2020-06-20
+ host: x86_64-unknown-linux-gnu
+ release: 1.46.0-nightly
+ LLVM version: 10.0
+ ```
+
+### Error output
+
+```
+<output>
+```
+
+<!--
+Include a backtrace in the code block by setting `RUST_BACKTRACE=1` in your
+environment. E.g. `RUST_BACKTRACE=1 cargo clippy`.
+-->
+<details><summary>Backtrace</summary>
+ <p>
+
+ ```
+ <backtrace>
+ ```
+
+ </p>
+</details>
--- /dev/null
+---
+name: New lint suggestion
+about: Suggest a new Clippy lint.
+labels: L-lint
+---
+
+### What it does
+
+*What does this lint do?*
+
+### Categories (optional)
+
+- Kind: *See <https://github.com/rust-lang/rust-clippy/blob/master/README.md#clippy> for list of lint kinds*
+
+*What benefit of this lint over old code?*
+
+For example:
+- Remove bounce checking inserted by ...
+- Remove the need to duplicating/storing/typo ...
+
+### Drawbacks
+
+None.
+
+### Example
+
+```rust
+<code>
+```
+
+Could be written as:
+
+```rust
+<code>
+```
---
+*Please keep the line below*
changelog: none
sed -e "s,tests/ui,\$DIR," -e "/= help/d" cstring.stderr > normalized.stderr
diff normalized.stderr tests/ui/cstring.stderr
+
+# make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same
+SYSROOT=`rustc --print sysroot`
+diff <(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver --rustc --version --verbose) <(rustc --version --verbose)
+
+
+echo "fn main() {}" > target/driver_test.rs
+# we can't run 2 rustcs on the same file at the same time
+CLIPPY=`LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver ./target/driver_test.rs --rustc`
+RUSTC=`rustc ./target/driver_test.rs`
+diff <($CLIPPY) <($RUSTC)
+
# TODO: CLIPPY_CONF_DIR / CARGO_MANIFEST_DIR
let l = self.expr(left)?;
let r = self.expr(right);
match (l, r) {
- (Constant::Int(l), Some(Constant::Int(r))) => match self.tables.expr_ty(left).kind {
+ (Constant::Int(l), Some(Constant::Int(r))) => match self.tables.expr_ty_opt(left)?.kind {
ty::Int(ity) => {
let l = sext(self.lcx.tcx, l, ity);
let r = sext(self.lcx.tcx, r, ity);
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
use rustc_target::abi::LayoutOf;
-use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceWithHirId, PlaceBase};
+use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
use crate::utils::span_lint;
}
fn check_cmp(cx: &LateContext<'_, '_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) {
- if let (&ExprKind::MethodCall(ref method_path, _, ref args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) {
+ if let (&ExprKind::MethodCall(ref method_path, _, ref args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind)
+ {
// check if we are in an is_empty() method
if let Some(name) = get_item_name(cx, method) {
if name.as_str() == "is_empty" {
/// **What it does:** Checks for `let _ = sync_lock`
///
/// **Why is this bad?** This statement immediately drops the lock instead of
- /// extending it's lifetime to the end of the scope, which is often not intended.
+ /// extending its lifetime to the end of the scope, which is often not intended.
/// To extend lock lifetime to the end of the scope, use an underscore-prefixed
/// name instead (i.e. _lock). If you want to explicitly drop the lock,
/// `std::mem::drop` conveys your intention better and is less error-prone.
// error-pattern:cargo-clippy
#![feature(bindings_after_at)]
-#![feature(box_syntax)]
#![feature(box_patterns)]
+#![feature(box_syntax)]
+#![feature(concat_idents)]
+#![feature(crate_visibility_modifier)]
+#![feature(drain_filter)]
#![feature(or_patterns)]
#![feature(rustc_private)]
#![feature(stmt_expr_attributes)]
-#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
#![recursion_limit = "512"]
-#![warn(rust_2018_idioms, trivial_casts, trivial_numeric_casts)]
-#![deny(rustc::internal)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![feature(crate_visibility_modifier)]
-#![feature(concat_idents)]
-#![feature(drain_filter)]
+#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
+#![warn(trivial_casts, trivial_numeric_casts)]
+// warn on lints, that are included in `rust-lang/rust`s bootstrap
+#![warn(rust_2018_idioms, unused_lifetimes)]
+// warn on rustc internal lints
+#![deny(rustc::internal)]
// FIXME: switch to something more ergonomic here, once available.
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
-#[allow(unused_extern_crates)]
extern crate rustc_ast;
-#[allow(unused_extern_crates)]
extern crate rustc_ast_pretty;
-#[allow(unused_extern_crates)]
extern crate rustc_attr;
-#[allow(unused_extern_crates)]
extern crate rustc_data_structures;
-#[allow(unused_extern_crates)]
-extern crate rustc_driver;
-#[allow(unused_extern_crates)]
extern crate rustc_errors;
-#[allow(unused_extern_crates)]
extern crate rustc_hir;
-#[allow(unused_extern_crates)]
extern crate rustc_hir_pretty;
-#[allow(unused_extern_crates)]
extern crate rustc_index;
-#[allow(unused_extern_crates)]
extern crate rustc_infer;
-#[allow(unused_extern_crates)]
extern crate rustc_lexer;
-#[allow(unused_extern_crates)]
extern crate rustc_lint;
-#[allow(unused_extern_crates)]
extern crate rustc_middle;
-#[allow(unused_extern_crates)]
extern crate rustc_mir;
-#[allow(unused_extern_crates)]
extern crate rustc_parse;
-#[allow(unused_extern_crates)]
extern crate rustc_parse_format;
-#[allow(unused_extern_crates)]
extern crate rustc_session;
-#[allow(unused_extern_crates)]
extern crate rustc_span;
-#[allow(unused_extern_crates)]
extern crate rustc_target;
-#[allow(unused_extern_crates)]
extern crate rustc_trait_selection;
-#[allow(unused_extern_crates)]
extern crate rustc_typeck;
use rustc_data_structures::fx::FxHashSet;
/// # Example
///
/// ```
-/// # #![feature(rustc_private)]
-/// # #[allow(unused_extern_crates)]
-/// # extern crate rustc_middle;
-/// # #[allow(unused_extern_crates)]
-/// # extern crate rustc_session;
-/// # #[macro_use]
-/// # use clippy_lints::declare_clippy_lint;
+/// #![feature(rustc_private)]
+/// extern crate rustc_session;
/// use rustc_session::declare_tool_lint;
+/// use clippy_lints::declare_clippy_lint;
///
/// declare_clippy_lint! {
/// /// **What it does:** Checks for ... (describe what the lint matches).
store.register_early_pass(|| box option_env_unwrap::OptionEnvUnwrap);
let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
store.register_late_pass(move || box wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports));
- store.register_early_pass(|| box macro_use::MacroUseImports);
store.register_late_pass(|| box verbose_file_reads::VerboseFileReads);
store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default());
store.register_late_pass(|| box unnamed_address::UnnamedAddress);
single_char_binding_names_threshold,
});
store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns);
+ store.register_late_pass(|| box macro_use::MacroUseImports::default());
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
LintId::of(&types::OPTION_OPTION),
LintId::of(&unicode::NON_ASCII_LITERAL),
LintId::of(&unicode::UNICODE_NOT_NFC),
+ LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS),
LintId::of(&unused_self::UNUSED_SELF),
LintId::of(&wildcard_imports::ENUM_GLOB_USE),
LintId::of(&wildcard_imports::WILDCARD_IMPORTS),
LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY),
- LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS),
LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT),
LintId::of(&unwrap::PANICKING_UNWRAP),
LintId::of(&types::UNNECESSARY_CAST),
LintId::of(&types::VEC_BOX),
LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY),
- LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS),
LintId::of(&unwrap::UNNECESSARY_UNWRAP),
LintId::of(&useless_conversion::USELESS_CONVERSION),
LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO),
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use rustc_span::symbol::Symbol;
-use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceWithHirId, PlaceBase};
+use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
use std::iter::{once, Iterator};
use std::mem;
span_high: Option<Span>,
}
-impl<'a, 'tcx> Delegate<'tcx> for MutatePairDelegate<'a, 'tcx> {
+impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: ConsumeMode) {}
fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, bk: ty::BorrowKind) {
}
}
-impl<'a, 'tcx> MutatePairDelegate<'a, 'tcx> {
+impl MutatePairDelegate<'_, '_> {
fn mutation_span(&self) -> (Option<Span>, Option<Span>) {
(self.span_low, self.span_high)
}
None
}
-fn check_for_mutation<'a, 'tcx> (
+fn check_for_mutation<'a, 'tcx>(
cx: &LateContext<'a, 'tcx>,
body: &Expr<'_>,
bound_ids: &[Option<HirId>],
) -> (Option<Span>, Option<Span>) {
let mut delegate = MutatePairDelegate {
- cx: cx,
+ cx,
hir_id_low: bound_ids[0],
hir_id_high: bound_ids[1],
span_low: None,
if self.state == VarState::DontWarn {
return;
}
- if SpanlessEq::new(self.cx).eq_expr(&expr, self.end_expr) {
+ if expr.hir_id == self.end_expr.hir_id {
self.past_loop = true;
return;
}
has_break_or_return: bool,
}
-impl<'a, 'tcx> Visitor<'tcx> for HasBreakOrReturnVisitor {
+impl<'tcx> Visitor<'tcx> for HasBreakOrReturnVisitor {
type Map = Map<'tcx>;
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
-use crate::utils::{snippet, span_lint_and_sugg};
+use crate::utils::{in_macro, snippet, span_lint_and_sugg};
+use hir::def::{DefKind, Res};
use if_chain::if_chain;
use rustc_ast::ast;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::Applicability;
-use rustc_lint::{EarlyContext, EarlyLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::edition::Edition;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::{edition::Edition, Span};
declare_clippy_lint! {
/// **What it does:** Checks for `#[macro_use] use...`.
/// **Why is this bad?** Since the Rust 2018 edition you can import
/// macro's directly, this is considered idiomatic.
///
- /// **Known problems:** This lint does not generate an auto-applicable suggestion.
+ /// **Known problems:** None.
///
/// **Example:**
/// ```rust
"#[macro_use] is no longer needed"
}
-declare_lint_pass!(MacroUseImports => [MACRO_USE_IMPORTS]);
+const BRACKETS: &[char] = &['<', '>'];
-impl EarlyLintPass for MacroUseImports {
- fn check_item(&mut self, ecx: &EarlyContext<'_>, item: &ast::Item) {
+#[derive(Clone, Debug, PartialEq, Eq)]
+struct PathAndSpan {
+ path: String,
+ span: Span,
+}
+
+/// `MacroRefData` includes the name of the macro
+/// and the path from `SourceMap::span_to_filename`.
+#[derive(Debug, Clone)]
+pub struct MacroRefData {
+ name: String,
+ path: String,
+}
+
+impl MacroRefData {
+ pub fn new(name: String, callee: Span, cx: &LateContext<'_, '_>) -> Self {
+ let mut path = cx.sess().source_map().span_to_filename(callee).to_string();
+
+ // std lib paths are <::std::module::file type>
+ // so remove brackets, space and type.
+ if path.contains('<') {
+ path = path.replace(BRACKETS, "");
+ }
+ if path.contains(' ') {
+ path = path.split(' ').next().unwrap().to_string();
+ }
+ Self { name, path }
+ }
+}
+
+#[derive(Default)]
+#[allow(clippy::module_name_repetitions)]
+pub struct MacroUseImports {
+ /// the actual import path used and the span of the attribute above it.
+ imports: Vec<(String, Span)>,
+ /// the span of the macro reference, kept to ensure only one reference is used per macro call.
+ collected: FxHashSet<Span>,
+ mac_refs: Vec<MacroRefData>,
+}
+
+impl_lint_pass!(MacroUseImports => [MACRO_USE_IMPORTS]);
+
+impl MacroUseImports {
+ fn push_unique_macro(&mut self, cx: &LateContext<'_, '_>, span: Span) {
+ let call_site = span.source_callsite();
+ let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
+ if let Some(callee) = span.source_callee() {
+ if !self.collected.contains(&call_site) {
+ let name = if name.contains("::") {
+ name.split("::").last().unwrap().to_string()
+ } else {
+ name.to_string()
+ };
+
+ self.mac_refs.push(MacroRefData::new(name, callee.def_site, cx));
+ self.collected.insert(call_site);
+ }
+ }
+ }
+
+ fn push_unique_macro_pat_ty(&mut self, cx: &LateContext<'_, '_>, span: Span) {
+ let call_site = span.source_callsite();
+ let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
+ if let Some(callee) = span.source_callee() {
+ if !self.collected.contains(&call_site) {
+ self.mac_refs
+ .push(MacroRefData::new(name.to_string(), callee.def_site, cx));
+ self.collected.insert(call_site);
+ }
+ }
+ }
+}
+
+impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports {
+ fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &hir::Item<'_>) {
if_chain! {
- if ecx.sess.opts.edition == Edition::Edition2018;
- if let ast::ItemKind::Use(use_tree) = &item.kind;
+ if cx.sess().opts.edition == Edition::Edition2018;
+ if let hir::ItemKind::Use(path, _kind) = &item.kind;
if let Some(mac_attr) = item
.attrs
.iter()
.find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string()));
+ if let Res::Def(DefKind::Mod, id) = path.res;
then {
- let msg = "`macro_use` attributes are no longer needed in the Rust 2018 edition";
- let help = format!("use {}::<macro name>", snippet(ecx, use_tree.span, "_"));
+ for kid in cx.tcx.item_children(id).iter() {
+ if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
+ let span = mac_attr.span;
+ let def_path = cx.tcx.def_path_str(mac_id);
+ self.imports.push((def_path, span));
+ }
+ }
+ } else {
+ if in_macro(item.span) {
+ self.push_unique_macro_pat_ty(cx, item.span);
+ }
+ }
+ }
+ }
+ fn check_attribute(&mut self, cx: &LateContext<'_, '_>, attr: &ast::Attribute) {
+ if in_macro(attr.span) {
+ self.push_unique_macro(cx, attr.span);
+ }
+ }
+ fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) {
+ if in_macro(expr.span) {
+ self.push_unique_macro(cx, expr.span);
+ }
+ }
+ fn check_stmt(&mut self, cx: &LateContext<'_, '_>, stmt: &hir::Stmt<'_>) {
+ if in_macro(stmt.span) {
+ self.push_unique_macro(cx, stmt.span);
+ }
+ }
+ fn check_pat(&mut self, cx: &LateContext<'_, '_>, pat: &hir::Pat<'_>) {
+ if in_macro(pat.span) {
+ self.push_unique_macro_pat_ty(cx, pat.span);
+ }
+ }
+ fn check_ty(&mut self, cx: &LateContext<'_, '_>, ty: &hir::Ty<'_>) {
+ if in_macro(ty.span) {
+ self.push_unique_macro_pat_ty(cx, ty.span);
+ }
+ }
+ #[allow(clippy::too_many_lines)]
+ fn check_crate_post(&mut self, cx: &LateContext<'_, '_>, _krate: &hir::Crate<'_>) {
+ let mut used = FxHashMap::default();
+ let mut check_dup = vec![];
+ for (import, span) in &self.imports {
+ let found_idx = self.mac_refs.iter().position(|mac| import.ends_with(&mac.name));
+
+ if let Some(idx) = found_idx {
+ let _ = self.mac_refs.remove(idx);
+ let seg = import.split("::").collect::<Vec<_>>();
+
+ match seg.as_slice() {
+ // an empty path is impossible
+ // a path should always consist of 2 or more segments
+ [] | [_] => return,
+ [root, item] => {
+ if !check_dup.contains(&(*item).to_string()) {
+ used.entry(((*root).to_string(), span))
+ .or_insert_with(Vec::new)
+ .push((*item).to_string());
+ check_dup.push((*item).to_string());
+ }
+ },
+ [root, rest @ ..] => {
+ if rest.iter().all(|item| !check_dup.contains(&(*item).to_string())) {
+ let filtered = rest
+ .iter()
+ .filter_map(|item| {
+ if check_dup.contains(&(*item).to_string()) {
+ None
+ } else {
+ Some((*item).to_string())
+ }
+ })
+ .collect::<Vec<_>>();
+ used.entry(((*root).to_string(), span))
+ .or_insert_with(Vec::new)
+ .push(filtered.join("::"));
+ check_dup.extend(filtered);
+ } else {
+ let rest = rest.to_vec();
+ used.entry(((*root).to_string(), span))
+ .or_insert_with(Vec::new)
+ .push(rest.join("::"));
+ check_dup.extend(rest.iter().map(ToString::to_string));
+ }
+ },
+ }
+ }
+ }
+
+ let mut suggestions = vec![];
+ for ((root, span), path) in used {
+ if path.len() == 1 {
+ suggestions.push((span, format!("{}::{}", root, path[0])))
+ } else {
+ suggestions.push((span, format!("{}::{{{}}}", root, path.join(", "))))
+ }
+ }
+
+ // If mac_refs is not empty we have encountered an import we could not handle
+ // such as `std::prelude::v1::foo` or some other macro that expands to an import.
+ if self.mac_refs.is_empty() {
+ for (span, import) in suggestions {
+ let help = format!("use {};", import);
span_lint_and_sugg(
- ecx,
+ cx,
MACRO_USE_IMPORTS,
- mac_attr.span,
- msg,
+ *span,
+ "`macro_use` attributes are no longer needed in the Rust 2018 edition",
"remove the attribute and import the macro directly, try",
help,
- Applicability::HasPlaceholders,
- );
+ Applicability::MaybeIncorrect,
+ )
}
}
}
}
}
-fn check_replace_with_uninit(cx: &LateContext<'_, '_>, src: &Expr<'_>, expr_span: Span) {
- if let ExprKind::Call(ref repl_func, ref repl_args) = src.kind {
- if_chain! {
- if repl_args.is_empty();
- if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
- if let Some(repl_def_id) = cx.tables.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
- then {
- if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) {
- span_lint_and_help(
- cx,
- MEM_REPLACE_WITH_UNINIT,
- expr_span,
- "replacing with `mem::uninitialized()`",
- None,
- "consider using the `take_mut` crate instead",
- );
- } else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id) &&
- !cx.tables.expr_ty(src).is_primitive() {
- span_lint_and_help(
- cx,
- MEM_REPLACE_WITH_UNINIT,
- expr_span,
- "replacing with `mem::zeroed()`",
- None,
- "consider using a default value or the `take_mut` crate instead",
- );
- }
+fn check_replace_with_uninit(cx: &LateContext<'_, '_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
+ if_chain! {
+ // check if replacement is mem::MaybeUninit::uninit().assume_init()
+ if let Some(method_def_id) = cx.tables.type_dependent_def_id(src.hir_id);
+ if cx.tcx.is_diagnostic_item(sym::assume_init, method_def_id);
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ MEM_REPLACE_WITH_UNINIT,
+ expr_span,
+ "replacing with `mem::MaybeUninit::uninit().assume_init()`",
+ "consider using",
+ format!(
+ "std::ptr::read({})",
+ snippet_with_applicability(cx, dest.span, "", &mut applicability)
+ ),
+ applicability,
+ );
+ return;
+ }
+ }
+
+ if_chain! {
+ if let ExprKind::Call(ref repl_func, ref repl_args) = src.kind;
+ if repl_args.is_empty();
+ if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
+ if let Some(repl_def_id) = cx.tables.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
+ then {
+ if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) {
+ let mut applicability = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ MEM_REPLACE_WITH_UNINIT,
+ expr_span,
+ "replacing with `mem::uninitialized()`",
+ "consider using",
+ format!(
+ "std::ptr::read({})",
+ snippet_with_applicability(cx, dest.span, "", &mut applicability)
+ ),
+ applicability,
+ );
+ } else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id) &&
+ !cx.tables.expr_ty(src).is_primitive() {
+ span_lint_and_help(
+ cx,
+ MEM_REPLACE_WITH_UNINIT,
+ expr_span,
+ "replacing with `mem::zeroed()`",
+ None,
+ "consider using a default value or the `take_mut` crate instead",
+ );
}
}
}
if let [dest, src] = &**func_args;
then {
check_replace_option_with_none(cx, src, dest, expr.span);
- check_replace_with_uninit(cx, src, expr.span);
+ check_replace_with_uninit(cx, src, dest, expr.span);
check_replace_with_default(cx, src, dest, expr.span);
}
}
/// }
/// ```
///
- /// To fix the lint, and a `Default` implementation that delegates to `new`:
+ /// To fix the lint, add a `Default` implementation that delegates to `new`:
///
/// ```ignore
/// struct Foo(Bar);
-use crate::utils::{match_qpath, match_trait_method, paths, snippet, span_lint_and_then};
+use crate::utils::{in_constant, match_qpath, match_trait_method, paths, snippet, span_lint_and_then};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
-use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath};
+use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, PatKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_mir::const_eval::is_const_fn;
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Symbol;
declare_clippy_lint! {
/// **What it does:** Lint for redundant pattern matching over `Result` or
arms: &[Arm<'_>],
keyword: &'static str,
) {
+ fn find_suggestion(cx: &LateContext<'_, '_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> {
+ if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") {
+ return Some("is_ok()");
+ }
+ if match_qpath(path, &paths::RESULT_ERR) && can_suggest(cx, hir_id, sym!(result_type), "is_err") {
+ return Some("is_err()");
+ }
+ if match_qpath(path, &paths::OPTION_SOME) && can_suggest(cx, hir_id, sym!(option_type), "is_some") {
+ return Some("is_some()");
+ }
+ if match_qpath(path, &paths::OPTION_NONE) && can_suggest(cx, hir_id, sym!(option_type), "is_none") {
+ return Some("is_none()");
+ }
+ None
+ }
+
+ let hir_id = expr.hir_id;
let good_method = match arms[0].pat.kind {
PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => {
if let PatKind::Wild = patterns[0].kind {
- if match_qpath(path, &paths::RESULT_OK) {
- "is_ok()"
- } else if match_qpath(path, &paths::RESULT_ERR) {
- "is_err()"
- } else if match_qpath(path, &paths::OPTION_SOME) {
- "is_some()"
- } else {
- return;
- }
+ find_suggestion(cx, hir_id, path)
} else {
- return;
+ None
}
},
-
- PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => "is_none()",
-
- _ => return,
+ PatKind::Path(ref path) => find_suggestion(cx, hir_id, path),
+ _ => None,
+ };
+ let good_method = match good_method {
+ Some(method) => method,
+ None => return,
};
// check that `while_let_on_iterator` lint does not trigger
if arms.len() == 2 {
let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
+ let hir_id = expr.hir_id;
let found_good_method = match node_pair {
(
PatKind::TupleStruct(ref path_left, ref patterns_left, _),
&paths::RESULT_ERR,
"is_ok()",
"is_err()",
+ || can_suggest(cx, hir_id, sym!(result_type), "is_ok"),
+ || can_suggest(cx, hir_id, sym!(result_type), "is_err"),
)
} else {
None
&paths::OPTION_NONE,
"is_some()",
"is_none()",
+ || can_suggest(cx, hir_id, sym!(option_type), "is_some"),
+ || can_suggest(cx, hir_id, sym!(option_type), "is_none"),
)
} else {
None
}
}
+#[allow(clippy::too_many_arguments)]
fn find_good_method_for_match<'a>(
arms: &[Arm<'_>],
path_left: &QPath<'_>,
expected_right: &[&str],
should_be_left: &'a str,
should_be_right: &'a str,
+ can_suggest_left: impl Fn() -> bool,
+ can_suggest_right: impl Fn() -> bool,
) -> Option<&'a str> {
let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) {
(&(*arms[0].body).kind, &(*arms[1].body).kind)
match body_node_pair {
(ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
- (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left),
- (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right),
+ (LitKind::Bool(true), LitKind::Bool(false)) if can_suggest_left() => Some(should_be_left),
+ (LitKind::Bool(false), LitKind::Bool(true)) if can_suggest_right() => Some(should_be_right),
_ => None,
},
_ => None,
}
}
+
+fn can_suggest(cx: &LateContext<'_, '_>, hir_id: HirId, diag_item: Symbol, name: &str) -> bool {
+ if !in_constant(cx, hir_id) {
+ return true;
+ }
+
+ // Avoid suggesting calls to non-`const fn`s in const contexts, see #5697.
+ cx.tcx
+ .get_diagnostic_item(diag_item)
+ .and_then(|def_id| {
+ cx.tcx.inherent_impls(def_id).iter().find_map(|imp| {
+ cx.tcx
+ .associated_items(*imp)
+ .in_definition_order()
+ .find_map(|item| match item.kind {
+ ty::AssocKind::Fn if item.ident.name.as_str() == name => Some(item.def_id),
+ _ => None,
+ })
+ })
+ })
+ .map_or(false, |def_id| is_const_fn(cx.tcx, def_id))
+}
in_binary_expr: bool,
}
-impl<'a, 'tcx> Visitor<'tcx> for BinaryExprVisitor {
+impl<'tcx> Visitor<'tcx> for BinaryExprVisitor {
type Map = Map<'tcx>;
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
limit: u64,
}
-impl<'a, 'tcx> TriviallyCopyPassByRef {
+impl<'tcx> TriviallyCopyPassByRef {
pub fn new(limit: Option<u64>, target: &SessionConfig) -> Self {
let limit = limit.unwrap_or_else(|| {
let bit_width = u64::from(target.ptr_width);
let which = match (&ty.kind, cv) {
(&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => Minimum,
- (&ty::Int(ity), Constant::Int(i))
- if i == unsext(cx.tcx, i128::MIN >> (128 - int_bits(cx.tcx, ity)), ity) =>
- {
+ (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MIN >> (128 - int_bits(cx.tcx, ity)), ity) => {
Minimum
},
(&ty::Bool, Constant::Bool(true)) => Maximum,
- (&ty::Int(ity), Constant::Int(i))
- if i == unsext(cx.tcx, i128::MAX >> (128 - int_bits(cx.tcx, ity)), ity) =>
- {
+ (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MAX >> (128 - int_bits(cx.tcx, ity)), ity) => {
Maximum
},
(&ty::Uint(uty), Constant::Int(i)) if clip(cx.tcx, u128::MAX, uty) == i => Maximum,
}
match pre_cast_ty.kind {
ty::Int(int_ty) => Some(match int_ty {
- IntTy::I8 => (
- FullInt::S(i128::from(i8::MIN)),
- FullInt::S(i128::from(i8::MAX)),
- ),
- IntTy::I16 => (
- FullInt::S(i128::from(i16::MIN)),
- FullInt::S(i128::from(i16::MAX)),
- ),
- IntTy::I32 => (
- FullInt::S(i128::from(i32::MIN)),
- FullInt::S(i128::from(i32::MAX)),
- ),
- IntTy::I64 => (
- FullInt::S(i128::from(i64::MIN)),
- FullInt::S(i128::from(i64::MAX)),
- ),
+ IntTy::I8 => (FullInt::S(i128::from(i8::MIN)), FullInt::S(i128::from(i8::MAX))),
+ IntTy::I16 => (FullInt::S(i128::from(i16::MIN)), FullInt::S(i128::from(i16::MAX))),
+ IntTy::I32 => (FullInt::S(i128::from(i32::MIN)), FullInt::S(i128::from(i32::MAX))),
+ IntTy::I64 => (FullInt::S(i128::from(i64::MIN)), FullInt::S(i128::from(i64::MAX))),
IntTy::I128 => (FullInt::S(i128::MIN), FullInt::S(i128::MAX)),
- IntTy::Isize => (
- FullInt::S(isize::MIN as i128),
- FullInt::S(isize::MAX as i128),
- ),
+ IntTy::Isize => (FullInt::S(isize::MIN as i128), FullInt::S(isize::MAX as i128)),
}),
ty::Uint(uint_ty) => Some(match uint_ty {
- UintTy::U8 => (
- FullInt::U(u128::from(u8::MIN)),
- FullInt::U(u128::from(u8::MAX)),
- ),
- UintTy::U16 => (
- FullInt::U(u128::from(u16::MIN)),
- FullInt::U(u128::from(u16::MAX)),
- ),
- UintTy::U32 => (
- FullInt::U(u128::from(u32::MIN)),
- FullInt::U(u128::from(u32::MAX)),
- ),
- UintTy::U64 => (
- FullInt::U(u128::from(u64::MIN)),
- FullInt::U(u128::from(u64::MAX)),
- ),
+ UintTy::U8 => (FullInt::U(u128::from(u8::MIN)), FullInt::U(u128::from(u8::MAX))),
+ UintTy::U16 => (FullInt::U(u128::from(u16::MIN)), FullInt::U(u128::from(u16::MAX))),
+ UintTy::U32 => (FullInt::U(u128::from(u32::MIN)), FullInt::U(u128::from(u32::MAX))),
+ UintTy::U64 => (FullInt::U(u128::from(u64::MIN)), FullInt::U(u128::from(u64::MAX))),
UintTy::U128 => (FullInt::U(u128::MIN), FullInt::U(u128::MAX)),
- UintTy::Usize => (
- FullInt::U(usize::MIN as u128),
- FullInt::U(usize::MAX as u128),
- ),
+ UintTy::Usize => (FullInt::U(usize::MIN as u128), FullInt::U(usize::MAX as u128)),
}),
_ => None,
}
// 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, _)) => {
+ (
+ ExprKind::MethodCall(left_segment, _, left_args, _),
+ ExprKind::MethodCall(right_segment, _, right_args, _),
+ ) => {
left_segment.ident == right_segment.ident
&& left_args
.iter()
/// }
/// ```
pub UNNESTED_OR_PATTERNS,
- complexity,
+ pedantic,
"unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
}
}
},
ExprKind::MethodCall(ref _method_name, ref _generics, ref _args, ref _fn_span) => {
- println!("MethodCall(ref method_name, ref generics, ref args, ref fn_span) = {};", current);
+ println!(
+ "MethodCall(ref method_name, ref generics, ref args, ref fn_span) = {};",
+ current
+ );
println!(" // unimplemented: `ExprKind::MethodCall` is not further destructured at the moment");
},
ExprKind::Tup(ref elements) => {
pub use self::helpers::Conf;
define_Conf! {
- /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about
- (blacklisted_names, "blacklisted_names": Vec<String>, ["foo", "bar", "baz", "quux"].iter().map(ToString::to_string).collect()),
+ /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
+ (blacklisted_names, "blacklisted_names": Vec<String>, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
/// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have
(cognitive_complexity_threshold, "cognitive_complexity_threshold": u64, 25),
/// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead.
rhs: &'a Expr<'a>,
) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> {
match binop {
- BinOpKind::Add
- | BinOpKind::Mul
- | BinOpKind::Eq
- | BinOpKind::Ne
- | BinOpKind::BitAnd
- | BinOpKind::BitXor
- | BinOpKind::BitOr => Some((binop, rhs, lhs)),
+ BinOpKind::Add | BinOpKind::Eq | BinOpKind::Ne | BinOpKind::BitAnd | BinOpKind::BitXor | BinOpKind::BitOr => {
+ Some((binop, rhs, lhs))
+ },
BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)),
BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)),
BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)),
BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)),
- BinOpKind::Shl
+ BinOpKind::Mul // Not always commutative, e.g. with matrices. See issue #5698
+ | BinOpKind::Shl
| BinOpKind::Shr
| BinOpKind::Rem
| BinOpKind::Sub
}
/// Convenience extension trait for `DiagnosticBuilder`.
-pub trait DiagnosticBuilderExt<'a, T: LintContext> {
+pub trait DiagnosticBuilderExt<T: LintContext> {
/// Suggests to add an attribute to an item.
///
/// Correctly handles indentation of the attribute and item.
fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability);
}
-impl<'a, 'b, 'c, T: LintContext> DiagnosticBuilderExt<'c, T> for rustc_errors::DiagnosticBuilder<'b> {
+impl<T: LintContext> DiagnosticBuilderExt<T> for rustc_errors::DiagnosticBuilder<'_> {
fn suggest_item_with_attr<D: Display + ?Sized>(
&mut self,
cx: &T,
use rustc_middle::hir::map::Map;
use rustc_middle::ty;
use rustc_span::symbol::{Ident, Symbol};
-use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceWithHirId, PlaceBase};
+use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
/// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined.
pub fn mutated_variables<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &'a LateContext<'a, 'tcx>) -> Option<FxHashSet<HirId>> {
declare_clippy_lint! {
/// **What it does:** Checks for wildcard imports `use _::*`.
///
- /// **Why is this bad?** wildcard imports can polute the namespace. This is especially bad if
+ /// **Why is this bad?** wildcard imports can pollute the namespace. This is especially bad if
/// you try to import something through a wildcard, that already has been imported by name from
/// a different source:
///
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![feature(rustc_private)]
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+// warn on lints, that are included in `rust-lang/rust`s bootstrap
+#![warn(rust_2018_idioms, unused_lifetimes)]
+// warn on rustc internal lints
+#![deny(rustc::internal)]
// FIXME: switch to something more ergonomic here, once available.
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
-#[allow(unused_extern_crates)]
+extern crate rustc_data_structures;
extern crate rustc_driver;
-#[allow(unused_extern_crates)]
extern crate rustc_errors;
-#[allow(unused_extern_crates)]
extern crate rustc_interface;
-#[allow(unused_extern_crates)]
extern crate rustc_middle;
use rustc_interface::interface;
#[allow(clippy::find_map, clippy::filter_map)]
fn describe_lints() {
use lintlist::{Level, Lint, ALL_LINTS, LINT_LEVELS};
- use std::collections::HashSet;
+ use rustc_data_structures::fx::FxHashSet;
println!(
"
let scoped = |x: &str| format!("clippy::{}", x);
- let lint_groups: HashSet<_> = lints.iter().map(|lint| lint.group).collect();
+ let lint_groups: FxHashSet<_> = lints.iter().map(|lint| lint.group).collect();
println!("Lint checks provided by clippy:\n");
println!(" {} {:7.7} meaning", padded("name"), "default");
Common options:
-h, --help Print this message
+ --rustc Pass all args to rustc
-V, --version Print version info and exit
Other options are the same as `cargo check`.
exit(rustc_driver::catch_with_exit_code(move || {
let mut orig_args: Vec<String> = env::args().collect();
- if orig_args.iter().any(|a| a == "--version" || a == "-V") {
- let version_info = rustc_tools_util::get_version_info!();
- println!("{}", version_info);
- exit(0);
- }
-
// Get the sysroot, looking from most specific to this invocation to the least:
// - command line
// - runtime environment
.map(|pb| pb.to_string_lossy().to_string())
.expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust");
+ // make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc"
+ // for example `clippy-driver --rustc --version` will print the rustc version that clippy-driver
+ // uses
+ if let Some(pos) = orig_args.iter().position(|arg| arg == "--rustc") {
+ orig_args.remove(pos);
+ orig_args[0] = "rustc".to_string();
+
+ // if we call "rustc", we need to pass --sysroot here as well
+ let mut args: Vec<String> = orig_args.clone();
+ if !have_sys_root_arg {
+ args.extend(vec!["--sysroot".into(), sys_root]);
+ };
+
+ return rustc_driver::run_compiler(&args, &mut DefaultCallbacks, None, None);
+ }
+
+ if orig_args.iter().any(|a| a == "--version" || a == "-V") {
+ let version_info = rustc_tools_util::get_version_info!();
+ println!("{}", version_info);
+ exit(0);
+ }
+
// Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
// We're invoking the compiler programmatically, so we ignore this/
let wrapper_mode = orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref());
},
Lint {
name: "unnested_or_patterns",
- group: "complexity",
+ group: "pedantic",
desc: "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`",
deprecation: None,
module: "unnested_or_patterns",
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+// warn on lints, that are included in `rust-lang/rust`s bootstrap
+#![warn(rust_2018_idioms, unused_lifetimes)]
use rustc_tools_util::VersionInfo;
use std::env;
--- /dev/null
+extern crate macro_rules;
+
+// STMT
+#[macro_export]
+macro_rules! pub_macro {
+ () => {
+ let _ = "hello Mr. Vonnegut";
+ };
+}
+
+pub mod inner {
+ pub use super::*;
+
+ // RE-EXPORT
+ // this will stick in `inner` module
+ pub use macro_rules::foofoo;
+ pub use macro_rules::try_err;
+
+ pub mod nested {
+ pub use macro_rules::string_add;
+ }
+
+ // ITEM
+ #[macro_export]
+ macro_rules! inner_mod_macro {
+ () => {
+ #[allow(dead_code)]
+ pub struct Tardis;
+ };
+ }
+}
+
+// EXPR
+#[macro_export]
+macro_rules! function_macro {
+ () => {
+ if true {
+ } else {
+ }
+ };
+}
+
+// TYPE
+#[macro_export]
+macro_rules! ty_macro {
+ () => {
+ Vec<u8>
+ };
+}
+
+mod extern_exports {
+ pub(super) mod private_inner {
+ #[macro_export]
+ macro_rules! pub_in_private_macro {
+ ($name:ident) => {
+ let $name = String::from("secrets and lies");
+ };
+ }
+ }
+}
fn main() {
let foo = 42;
- let bar = 42;
let baz = 42;
+ let quux = 42;
+ // Unlike these others, `bar` is actually considered an acceptable name.
+ // Among many other legitimate uses, bar commonly refers to a period of time in music.
+ // See https://github.com/rust-lang/rust-clippy/issues/5225.
+ let bar = 42;
- let barb = 42;
- let barbaric = 42;
+ let food = 42;
+ let foodstuffs = 42;
+ let bazaar = 42;
match (42, Some(1337), Some(0)) {
- (foo, Some(bar), baz @ Some(_)) => (),
+ (foo, Some(baz), quux @ Some(_)) => (),
_ => (),
}
}
fn issue_1647(mut foo: u8) {
- let mut bar = 0;
- if let Some(mut baz) = Some(42) {}
+ let mut baz = 0;
+ if let Some(mut quux) = Some(42) {}
}
fn issue_1647_ref() {
- let ref bar = 0;
- if let Some(ref baz) = Some(42) {}
+ let ref baz = 0;
+ if let Some(ref quux) = Some(42) {}
}
fn issue_1647_ref_mut() {
- let ref mut bar = 0;
- if let Some(ref mut baz) = Some(42) {}
+ let ref mut baz = 0;
+ if let Some(ref mut quux) = Some(42) {}
}
LL | let foo = 42;
| ^^^
-error: use of a blacklisted/placeholder name `bar`
+error: use of a blacklisted/placeholder name `baz`
--> $DIR/blacklisted_name.rs:15:9
|
-LL | let bar = 42;
+LL | let baz = 42;
| ^^^
-error: use of a blacklisted/placeholder name `baz`
+error: use of a blacklisted/placeholder name `quux`
--> $DIR/blacklisted_name.rs:16:9
|
-LL | let baz = 42;
- | ^^^
+LL | let quux = 42;
+ | ^^^^
error: use of a blacklisted/placeholder name `foo`
- --> $DIR/blacklisted_name.rs:22:10
+ --> $DIR/blacklisted_name.rs:27:10
|
-LL | (foo, Some(bar), baz @ Some(_)) => (),
+LL | (foo, Some(baz), quux @ Some(_)) => (),
| ^^^
-error: use of a blacklisted/placeholder name `bar`
- --> $DIR/blacklisted_name.rs:22:20
+error: use of a blacklisted/placeholder name `baz`
+ --> $DIR/blacklisted_name.rs:27:20
|
-LL | (foo, Some(bar), baz @ Some(_)) => (),
+LL | (foo, Some(baz), quux @ Some(_)) => (),
| ^^^
-error: use of a blacklisted/placeholder name `baz`
- --> $DIR/blacklisted_name.rs:22:26
+error: use of a blacklisted/placeholder name `quux`
+ --> $DIR/blacklisted_name.rs:27:26
|
-LL | (foo, Some(bar), baz @ Some(_)) => (),
- | ^^^
+LL | (foo, Some(baz), quux @ Some(_)) => (),
+ | ^^^^
error: use of a blacklisted/placeholder name `foo`
- --> $DIR/blacklisted_name.rs:27:19
+ --> $DIR/blacklisted_name.rs:32:19
|
LL | fn issue_1647(mut foo: u8) {
| ^^^
-error: use of a blacklisted/placeholder name `bar`
- --> $DIR/blacklisted_name.rs:28:13
+error: use of a blacklisted/placeholder name `baz`
+ --> $DIR/blacklisted_name.rs:33:13
|
-LL | let mut bar = 0;
+LL | let mut baz = 0;
| ^^^
-error: use of a blacklisted/placeholder name `baz`
- --> $DIR/blacklisted_name.rs:29:21
+error: use of a blacklisted/placeholder name `quux`
+ --> $DIR/blacklisted_name.rs:34:21
|
-LL | if let Some(mut baz) = Some(42) {}
- | ^^^
+LL | if let Some(mut quux) = Some(42) {}
+ | ^^^^
-error: use of a blacklisted/placeholder name `bar`
- --> $DIR/blacklisted_name.rs:33:13
+error: use of a blacklisted/placeholder name `baz`
+ --> $DIR/blacklisted_name.rs:38:13
|
-LL | let ref bar = 0;
+LL | let ref baz = 0;
| ^^^
-error: use of a blacklisted/placeholder name `baz`
- --> $DIR/blacklisted_name.rs:34:21
+error: use of a blacklisted/placeholder name `quux`
+ --> $DIR/blacklisted_name.rs:39:21
|
-LL | if let Some(ref baz) = Some(42) {}
- | ^^^
+LL | if let Some(ref quux) = Some(42) {}
+ | ^^^^
-error: use of a blacklisted/placeholder name `bar`
- --> $DIR/blacklisted_name.rs:38:17
+error: use of a blacklisted/placeholder name `baz`
+ --> $DIR/blacklisted_name.rs:43:17
|
-LL | let ref mut bar = 0;
+LL | let ref mut baz = 0;
| ^^^
-error: use of a blacklisted/placeholder name `baz`
- --> $DIR/blacklisted_name.rs:39:25
+error: use of a blacklisted/placeholder name `quux`
+ --> $DIR/blacklisted_name.rs:44:25
|
-LL | if let Some(ref mut baz) = Some(42) {}
- | ^^^
+LL | if let Some(ref mut quux) = Some(42) {}
+ | ^^^^
error: aborting due to 14 previous errors
--- /dev/null
+#![allow(clippy::explicit_counter_loop)]
+
+fn main() {
+ let v = vec![1, 2, 3];
+ let mut i = 0;
+ let max_storage_size = [0; 128 * 1024];
+ for item in &v {
+ bar(i, *item);
+ i += 1;
+ }
+}
+
+fn bar(_: usize, _: u32) {}
fn f(val: &[u8]) {}
+mod issue_5698 {
+ fn mul_not_always_commutative(x: i32, y: i32) -> i32 {
+ if x == 42 {
+ x * y
+ } else if x == 21 {
+ y * x
+ } else {
+ 0
+ }
+ }
+}
+
fn main() {}
--- /dev/null
+// compile-flags: --edition 2018
+// aux-build:macro_rules.rs
+// aux-build:macro_use_helper.rs
+// run-rustfix
+// ignore-32bit
+
+#![allow(unused_imports, unreachable_code, unused_variables, dead_code)]
+#![allow(clippy::single_component_path_imports)]
+#![warn(clippy::macro_use_imports)]
+
+#[macro_use]
+extern crate macro_use_helper as mac;
+
+#[macro_use]
+extern crate clippy_mini_macro_test as mini_mac;
+
+mod a {
+ use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro};
+ use mac;
+ use mini_mac::ClippyMiniMacroTest;
+ use mini_mac;
+ use mac::{inner::foofoo, inner::try_err};
+ use mac::inner;
+ use mac::inner::nested::string_add;
+ use mac::inner::nested;
+
+ #[derive(ClippyMiniMacroTest)]
+ struct Test;
+
+ fn test() {
+ pub_macro!();
+ inner_mod_macro!();
+ pub_in_private_macro!(_var);
+ function_macro!();
+ let v: ty_macro!() = Vec::default();
+
+ inner::try_err!();
+ inner::foofoo!();
+ nested::string_add!();
+ }
+}
+
+fn main() {}
-// edition:2018
+// compile-flags: --edition 2018
+// aux-build:macro_rules.rs
+// aux-build:macro_use_helper.rs
+// run-rustfix
+// ignore-32bit
+
+#![allow(unused_imports, unreachable_code, unused_variables, dead_code)]
+#![allow(clippy::single_component_path_imports)]
#![warn(clippy::macro_use_imports)]
-use std::collections::HashMap;
#[macro_use]
-use std::prelude;
+extern crate macro_use_helper as mac;
+
+#[macro_use]
+extern crate clippy_mini_macro_test as mini_mac;
+
+mod a {
+ #[macro_use]
+ use mac;
+ #[macro_use]
+ use mini_mac;
+ #[macro_use]
+ use mac::inner;
+ #[macro_use]
+ use mac::inner::nested;
-fn main() {
- let _ = HashMap::<u8, u8>::new();
- println!();
+ #[derive(ClippyMiniMacroTest)]
+ struct Test;
+
+ fn test() {
+ pub_macro!();
+ inner_mod_macro!();
+ pub_in_private_macro!(_var);
+ function_macro!();
+ let v: ty_macro!() = Vec::default();
+
+ inner::try_err!();
+ inner::foofoo!();
+ nested::string_add!();
+ }
}
+
+fn main() {}
error: `macro_use` attributes are no longer needed in the Rust 2018 edition
- --> $DIR/macro_use_imports.rs:5:1
+ --> $DIR/macro_use_imports.rs:18:5
|
-LL | #[macro_use]
- | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use std::prelude::<macro name>`
+LL | #[macro_use]
+ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro};`
|
= note: `-D clippy::macro-use-imports` implied by `-D warnings`
-error: aborting due to previous error
+error: `macro_use` attributes are no longer needed in the Rust 2018 edition
+ --> $DIR/macro_use_imports.rs:20:5
+ |
+LL | #[macro_use]
+ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;`
+
+error: `macro_use` attributes are no longer needed in the Rust 2018 edition
+ --> $DIR/macro_use_imports.rs:22:5
+ |
+LL | #[macro_use]
+ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};`
+
+error: `macro_use` attributes are no longer needed in the Rust 2018 edition
+ --> $DIR/macro_use_imports.rs:24:5
+ |
+LL | #[macro_use]
+ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;`
+
+error: aborting due to 4 previous errors
// run-rustfix
+#![feature(const_if_match)]
+#![feature(const_loop)]
#![warn(clippy::all)]
#![warn(clippy::redundant_pattern_matching)]
#![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)]
takes_bool(x);
issue5504();
+ issue5697();
let _ = if gen_opt().is_some() {
1
if m!().is_some() {}
while m!().is_some() {}
}
+
+// None of these should be linted because none of the suggested methods
+// are `const fn` without toggling a feature.
+const fn issue5697() {
+ if let Ok(_) = Ok::<i32, i32>(42) {}
+
+ if let Err(_) = Err::<i32, i32>(42) {}
+
+ if let Some(_) = Some(42) {}
+
+ if let None = None::<()> {}
+
+ while let Ok(_) = Ok::<i32, i32>(10) {}
+
+ while let Err(_) = Ok::<i32, i32>(10) {}
+
+ while let Some(_) = Some(42) {}
+
+ while let None = None::<()> {}
+
+ match Ok::<i32, i32>(42) {
+ Ok(_) => true,
+ Err(_) => false,
+ };
+
+ match Err::<i32, i32>(42) {
+ Ok(_) => false,
+ Err(_) => true,
+ };
+ match Some(42) {
+ Some(_) => true,
+ None => false,
+ };
+
+ match None::<()> {
+ Some(_) => false,
+ None => true,
+ };
+}
// run-rustfix
+#![feature(const_if_match)]
+#![feature(const_loop)]
#![warn(clippy::all)]
#![warn(clippy::redundant_pattern_matching)]
#![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)]
takes_bool(x);
issue5504();
+ issue5697();
let _ = if let Some(_) = gen_opt() {
1
if let Some(_) = m!() {}
while let Some(_) = m!() {}
}
+
+// None of these should be linted because none of the suggested methods
+// are `const fn` without toggling a feature.
+const fn issue5697() {
+ if let Ok(_) = Ok::<i32, i32>(42) {}
+
+ if let Err(_) = Err::<i32, i32>(42) {}
+
+ if let Some(_) = Some(42) {}
+
+ if let None = None::<()> {}
+
+ while let Ok(_) = Ok::<i32, i32>(10) {}
+
+ while let Err(_) = Ok::<i32, i32>(10) {}
+
+ while let Some(_) = Some(42) {}
+
+ while let None = None::<()> {}
+
+ match Ok::<i32, i32>(42) {
+ Ok(_) => true,
+ Err(_) => false,
+ };
+
+ match Err::<i32, i32>(42) {
+ Ok(_) => false,
+ Err(_) => true,
+ };
+ match Some(42) {
+ Some(_) => true,
+ None => false,
+ };
+
+ match None::<()> {
+ Some(_) => false,
+ None => true,
+ };
+}
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching.rs:8:12
+ --> $DIR/redundant_pattern_matching.rs:10:12
|
LL | if let Ok(_) = Ok::<i32, i32>(42) {}
| -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()`
= note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching.rs:10:12
+ --> $DIR/redundant_pattern_matching.rs:12:12
|
LL | if let Err(_) = Err::<i32, i32>(42) {}
| -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching.rs:12:12
+ --> $DIR/redundant_pattern_matching.rs:14:12
|
LL | if let None = None::<()> {}
| -------^^^^------------- help: try this: `if None::<()>.is_none()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching.rs:14:12
+ --> $DIR/redundant_pattern_matching.rs:16:12
|
LL | if let Some(_) = Some(42) {}
| -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching.rs:16:12
+ --> $DIR/redundant_pattern_matching.rs:18:12
|
LL | if let Some(_) = Some(42) {
| -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching.rs:22:15
+ --> $DIR/redundant_pattern_matching.rs:24:15
|
LL | while let Some(_) = Some(42) {}
| ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching.rs:24:15
+ --> $DIR/redundant_pattern_matching.rs:26:15
|
LL | while let None = Some(42) {}
| ----------^^^^----------- help: try this: `while Some(42).is_none()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching.rs:26:15
+ --> $DIR/redundant_pattern_matching.rs:28:15
|
LL | while let None = None::<()> {}
| ----------^^^^------------- help: try this: `while None::<()>.is_none()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching.rs:28:15
+ --> $DIR/redundant_pattern_matching.rs:30:15
|
LL | while let Ok(_) = Ok::<i32, i32>(10) {}
| ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching.rs:30:15
+ --> $DIR/redundant_pattern_matching.rs:32:15
|
LL | while let Err(_) = Ok::<i32, i32>(10) {}
| ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching.rs:33:15
+ --> $DIR/redundant_pattern_matching.rs:35:15
|
LL | while let Some(_) = v.pop() {
| ----------^^^^^^^---------- help: try this: `while v.pop().is_some()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching.rs:49:5
+ --> $DIR/redundant_pattern_matching.rs:51:5
|
LL | / match Ok::<i32, i32>(42) {
LL | | Ok(_) => true,
| |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching.rs:54:5
+ --> $DIR/redundant_pattern_matching.rs:56:5
|
LL | / match Ok::<i32, i32>(42) {
LL | | Ok(_) => false,
| |_____^ help: try this: `Ok::<i32, i32>(42).is_err()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching.rs:59:5
+ --> $DIR/redundant_pattern_matching.rs:61:5
|
LL | / match Err::<i32, i32>(42) {
LL | | Ok(_) => false,
| |_____^ help: try this: `Err::<i32, i32>(42).is_err()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching.rs:64:5
+ --> $DIR/redundant_pattern_matching.rs:66:5
|
LL | / match Err::<i32, i32>(42) {
LL | | Ok(_) => true,
| |_____^ help: try this: `Err::<i32, i32>(42).is_ok()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching.rs:69:5
+ --> $DIR/redundant_pattern_matching.rs:71:5
|
LL | / match Some(42) {
LL | | Some(_) => true,
| |_____^ help: try this: `Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching.rs:74:5
+ --> $DIR/redundant_pattern_matching.rs:76:5
|
LL | / match None::<()> {
LL | | Some(_) => false,
| |_____^ help: try this: `None::<()>.is_none()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching.rs:79:13
+ --> $DIR/redundant_pattern_matching.rs:81:13
|
LL | let _ = match None::<()> {
| _____________^
| |_____^ help: try this: `None::<()>.is_none()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching.rs:84:20
+ --> $DIR/redundant_pattern_matching.rs:86:20
|
LL | let _ = if let Ok(_) = Ok::<usize, ()>(4) { true } else { false };
| -------^^^^^--------------------- help: try this: `if Ok::<usize, ()>(4).is_ok()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching.rs:87:20
+ --> $DIR/redundant_pattern_matching.rs:89:20
|
LL | let x = if let Some(_) = opt { true } else { false };
| -------^^^^^^^------ help: try this: `if opt.is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching.rs:92:20
+ --> $DIR/redundant_pattern_matching.rs:95:20
|
LL | let _ = if let Some(_) = gen_opt() {
| -------^^^^^^^------------ help: try this: `if gen_opt().is_some()`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching.rs:94:19
+ --> $DIR/redundant_pattern_matching.rs:97:19
|
LL | } else if let None = gen_opt() {
| -------^^^^------------ help: try this: `if gen_opt().is_none()`
error: redundant pattern matching, consider using `is_ok()`
- --> $DIR/redundant_pattern_matching.rs:96:19
+ --> $DIR/redundant_pattern_matching.rs:99:19
|
LL | } else if let Ok(_) = gen_res() {
| -------^^^^^------------ help: try this: `if gen_res().is_ok()`
error: redundant pattern matching, consider using `is_err()`
- --> $DIR/redundant_pattern_matching.rs:98:19
+ --> $DIR/redundant_pattern_matching.rs:101:19
|
LL | } else if let Err(_) = gen_res() {
| -------^^^^^^------------ help: try this: `if gen_res().is_err()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching.rs:131:19
+ --> $DIR/redundant_pattern_matching.rs:134:19
|
LL | while let Some(_) = r#try!(result_opt()) {}
| ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching.rs:132:16
+ --> $DIR/redundant_pattern_matching.rs:135:16
|
LL | if let Some(_) = r#try!(result_opt()) {}
| -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching.rs:138:12
+ --> $DIR/redundant_pattern_matching.rs:141:12
|
LL | if let Some(_) = m!() {}
| -------^^^^^^^------- help: try this: `if m!().is_some()`
error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching.rs:139:15
+ --> $DIR/redundant_pattern_matching.rs:142:15
|
LL | while let Some(_) = m!() {}
| ----------^^^^^^^------- help: try this: `while m!().is_some()`
--- /dev/null
+// run-rustfix
+
+#![feature(const_if_match)]
+#![feature(const_loop)]
+#![feature(const_result)]
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(unused)]
+
+// Test that results are linted with the feature enabled.
+
+const fn issue_5697() {
+ if Ok::<i32, i32>(42).is_ok() {}
+
+ if Err::<i32, i32>(42).is_err() {}
+
+ while Ok::<i32, i32>(10).is_ok() {}
+
+ while Ok::<i32, i32>(10).is_err() {}
+
+ Ok::<i32, i32>(42).is_ok();
+
+ Err::<i32, i32>(42).is_err();
+
+ // These should not be linted until `const_option` is implemented.
+ // See https://github.com/rust-lang/rust/issues/67441
+
+ if let Some(_) = Some(42) {}
+
+ if let None = None::<()> {}
+
+ while let Some(_) = Some(42) {}
+
+ while let None = None::<()> {}
+
+ match Some(42) {
+ Some(_) => true,
+ None => false,
+ };
+
+ match None::<()> {
+ Some(_) => false,
+ None => true,
+ };
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+
+#![feature(const_if_match)]
+#![feature(const_loop)]
+#![feature(const_result)]
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(unused)]
+
+// Test that results are linted with the feature enabled.
+
+const fn issue_5697() {
+ if let Ok(_) = Ok::<i32, i32>(42) {}
+
+ if let Err(_) = Err::<i32, i32>(42) {}
+
+ while let Ok(_) = Ok::<i32, i32>(10) {}
+
+ while let Err(_) = Ok::<i32, i32>(10) {}
+
+ match Ok::<i32, i32>(42) {
+ Ok(_) => true,
+ Err(_) => false,
+ };
+
+ match Err::<i32, i32>(42) {
+ Ok(_) => false,
+ Err(_) => true,
+ };
+
+ // These should not be linted until `const_option` is implemented.
+ // See https://github.com/rust-lang/rust/issues/67441
+
+ if let Some(_) = Some(42) {}
+
+ if let None = None::<()> {}
+
+ while let Some(_) = Some(42) {}
+
+ while let None = None::<()> {}
+
+ match Some(42) {
+ Some(_) => true,
+ None => false,
+ };
+
+ match None::<()> {
+ Some(_) => false,
+ None => true,
+ };
+}
+
+fn main() {}
--- /dev/null
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_const_result.rs:12:12
+ |
+LL | if let Ok(_) = Ok::<i32, i32>(42) {}
+ | -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()`
+ |
+ = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+
+error: redundant pattern matching, consider using `is_err()`
+ --> $DIR/redundant_pattern_matching_const_result.rs:14:12
+ |
+LL | if let Err(_) = Err::<i32, i32>(42) {}
+ | -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()`
+
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_const_result.rs:16:15
+ |
+LL | while let Ok(_) = Ok::<i32, i32>(10) {}
+ | ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()`
+
+error: redundant pattern matching, consider using `is_err()`
+ --> $DIR/redundant_pattern_matching_const_result.rs:18:15
+ |
+LL | while let Err(_) = Ok::<i32, i32>(10) {}
+ | ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()`
+
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_const_result.rs:20:5
+ |
+LL | / match Ok::<i32, i32>(42) {
+LL | | Ok(_) => true,
+LL | | Err(_) => false,
+LL | | };
+ | |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
+
+error: redundant pattern matching, consider using `is_err()`
+ --> $DIR/redundant_pattern_matching_const_result.rs:25:5
+ |
+LL | / match Err::<i32, i32>(42) {
+LL | | Ok(_) => false,
+LL | | Err(_) => true,
+LL | | };
+ | |_____^ help: try this: `Err::<i32, i32>(42).is_err()`
+
+error: aborting due to 6 previous errors
+
std::mem::forget(mem::replace(&mut v, new_v));
}
+ unsafe {
+ let taken_v = mem::replace(&mut v, mem::MaybeUninit::uninit().assume_init());
+ let new_v = might_panic(taken_v);
+ std::mem::forget(mem::replace(&mut v, new_v));
+ }
+
unsafe {
let taken_v = mem::replace(&mut v, mem::zeroed());
let new_v = might_panic(taken_v);
--> $DIR/repl_uninit.rs:15:23
|
LL | let taken_v = mem::replace(&mut v, mem::uninitialized());
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::ptr::read(&mut v)`
|
= note: `-D clippy::mem-replace-with-uninit` implied by `-D warnings`
- = help: consider using the `take_mut` crate instead
-error: replacing with `mem::zeroed()`
+error: replacing with `mem::MaybeUninit::uninit().assume_init()`
--> $DIR/repl_uninit.rs:21:23
|
+LL | let taken_v = mem::replace(&mut v, mem::MaybeUninit::uninit().assume_init());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::ptr::read(&mut v)`
+
+error: replacing with `mem::zeroed()`
+ --> $DIR/repl_uninit.rs:27:23
+ |
LL | let taken_v = mem::replace(&mut v, mem::zeroed());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider using a default value or the `take_mut` crate instead
error: replacing with `mem::uninitialized()`
- --> $DIR/repl_uninit.rs:33:28
+ --> $DIR/repl_uninit.rs:39:28
|
LL | let taken_u = unsafe { mem::replace(uref, mem::uninitialized()) };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = help: consider using the `take_mut` crate instead
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::ptr::read(uref)`
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
use crate::errors::{Error, ErrorKind};
use crate::runtest::ProcRes;
use serde::Deserialize;
-use serde_json;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use crate::common::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS};
use crate::common::{CompareMode, Config, Debugger, Mode, PassMode, Pretty, TestPaths};
use crate::util::logv;
-use env_logger;
-use getopts;
use getopts::Options;
use log::*;
use std::env;
#[cfg(unix)]
mod imp {
- use libc;
use std::io;
use std::io::prelude::*;
use std::mem;
use crate::json;
use crate::util::get_pointer_width;
use crate::util::{logv, PathBufExt};
-use diff;
use regex::{Captures, Regex};
use rustfix::{apply_suggestions, get_suggestions_from_json, Filter};
break;
}
var entry = expected[key];
+
+ if (exact_check == true && entry.length !== results[key].length) {
+ error_text.push(queryName + "==> Expected exactly " + entry.length +
+ " results but found " + results[key].length + " in '" + key + "'");
+ }
+
var prev_pos = -1;
for (var i = 0; i < entry.length; ++i) {
var entry_pos = lookForEntry(entry[i], results[key]);
}
function runChecks(testFile, loaded, index) {
- var loadedFile = loadContent(
- readFile(testFile) + 'exports.QUERY = QUERY;exports.EXPECTED = EXPECTED;');
+ var testFileContent = readFile(testFile) + 'exports.QUERY = QUERY;exports.EXPECTED = EXPECTED;';
+ if (testFileContent.indexOf("FILTER_CRATE") !== -1) {
+ testFileContent += "exports.FILTER_CRATE = FILTER_CRATE;";
+ }
+ var loadedFile = loadContent(testFileContent);
const expected = loadedFile.EXPECTED;
const query = loadedFile.QUERY;
]
[notify-zulip."I-prioritize"]
-zulip_stream = 227806 # #t-compiler/wg-prioritization
+zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts
topic = "I-prioritize #{number} {title}"
-message_on_add = "@*WG-prioritization* issue #{number} has been requested for prioritization."
+message_on_add = """\
+@*WG-prioritization/alerts* issue #{number} has been requested for prioritization.
+
+# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#Unprioritized-I-prioritize)
+- Priority?
+- Regression?
+- Notify people/groups?
+- Needs `I-nominated`?
+"""
message_on_remove = "Issue #{number}'s prioritization request has been removed."
[notify-zulip."I-nominated"]
required_labels = ["T-compiler"]
-zulip_stream = 227806 # #t-compiler/wg-prioritization
+zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts
topic = "I-prioritize #{number} {title}"
-message_on_add = "@*WG-prioritization* #{number} has been nominated for discussion in `T-compiler` meeting."
+message_on_add = """\
+@*WG-prioritization/alerts* #{number} has been nominated for discussion in `T-compiler` meeting.
+
+# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#I-nominated)
+- Already discussed?
+- Worth the meeting time?
+- Add agenda entry:
+ - Why nominated?
+ - Assignee?
+ - Issue? PR? What's the status?
+ - Summary and important details?
+"""
message_on_remove = "#{number}'s nomination has been removed."
[notify-zulip."beta-nominated"]
-zulip_stream = 227806 # #t-compiler/wg-prioritization
+zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts
topic = "Backport #{number} {title}"
-message_on_add = "@*WG-prioritization* PR #{number} has been requested for beta backport."
+message_on_add = """\
+@*WG-prioritization/alerts* PR #{number} has been requested for beta backport.
+
+# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#StableBeta-nominations)
+Prepare agenda entry:
+- Why nominated?
+- Author, assignee?
+- Important details?
+"""
message_on_remove = "PR #{number}'s beta backport request has been removed."
[notify-zulip."stable-nominated"]
-zulip_stream = 227806 # #t-compiler/wg-prioritization
+zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts
topic = "Backport #{number} {title}"
-message_on_add = "@*WG-prioritization* PR #{number} has been requested for stable backport."
+message_on_add = """\
+@*WG-prioritization/alerts* PR #{number} has been requested for stable backport.
+
+# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#StableBeta-nominations)
+Prepare agenda entry:
+- Why nominated?
+- Author, assignee?
+- Important details?
+"""
message_on_remove = "PR #{number}'s stable backport request has been removed."
[notify-zulip."S-waiting-on-team"]
required_labels = ["T-compiler"]
-zulip_stream = 227806 # #t-compiler/wg-prioritization
+zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts
topic = "S-waiting-on-team #{number} {title}"
-message_on_add = "@*WG-prioritization* PR #{number} is waiting on `T-compiler`."
+message_on_add = """\
+@*WG-prioritization/alerts* PR #{number} is waiting on `T-compiler`.
+
+# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#PR%E2%80%99s-waiting-on-team)
+- Prepare agenda entry:
+ - What is it waiting for?
+ - Important details?
+- Could be resolved quickly? Tag `I-nominated`.
+"""
message_on_remove = "PR #{number}'s is no longer waiting on `T-compiler`."
[notify-zulip."P-critical"]
-zulip_stream = 227806 # #t-compiler/wg-prioritization
+zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts
topic = "P-critical #{number} {title}"
-message_on_add = "@*WG-prioritization* issue #{number} has been assigned `P-critical`."
+message_on_add = """\
+@*WG-prioritization/alerts* issue #{number} has been assigned `P-critical`.
+
+# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#P-critical-and-Unassigned-P-high-regressions)
+- Notify people/groups?
+- Assign if possible?
+- Add to agenda:
+ - Assignee?
+ - Summary and important details?
+- Other actions to move forward?
+"""
[notify-zulip."P-high"]
required_labels = ["regression-from-stable-to-[bn]*"] # only nightly and beta regressions
-zulip_stream = 227806 # #t-compiler/wg-prioritization
+zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts
topic = "P-high regression #{number} {title}"
-message_on_add = "@*WG-prioritization* issue #{number} has been assigned `P-high` and is a regression."
+message_on_add = """\
+@*WG-prioritization/alerts* issue #{number} has been assigned `P-high` and is a regression.
+
+# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#P-critical-and-Unassigned-P-high-regressions)
+Is issue assigned? If not:
+- Try to find an assignee?
+- Otherwise add to agenda:
+ - Mark as unassigned.
+ - Summary and important details?
+"""