name = "clippy"
version = "0.1.58"
dependencies = [
- "cargo_metadata 0.12.0",
+ "cargo_metadata 0.14.0",
"clippy_lints",
"clippy_utils",
"compiletest_rs",
"regex",
"rustc-workspace-hack",
"rustc_tools_util 0.2.0",
- "semver 0.11.0",
+ "semver 1.0.3",
"serde",
"syn",
"tempfile",
name = "clippy_lints"
version = "0.1.58"
dependencies = [
- "cargo_metadata 0.12.0",
+ "cargo_metadata 0.14.0",
"clippy_utils",
"if_chain",
"itertools 0.10.1",
"quine-mc_cluskey",
"regex-syntax",
"rustc-semver",
- "semver 0.11.0",
+ "semver 1.0.3",
"serde",
"serde_json",
"toml",
[[package]]
name = "libc"
-version = "0.2.103"
+version = "0.2.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
+checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673"
dependencies = [
"rustc-std-workspace-core",
]
}
}
- fn address_of_allows_mutation(&self, mt: mir::Mutability, place: mir::Place<'tcx>) -> bool {
- match mt {
- mir::Mutability::Mut => true,
- mir::Mutability::Not => self.shared_borrow_allows_mutation(place),
- }
+ fn address_of_allows_mutation(&self, _mt: mir::Mutability, _place: mir::Place<'tcx>) -> bool {
+ // Exact set of permissions granted by AddressOf is undecided. Conservatively assume that
+ // it might allow mutation until resolution of #56604.
+ true
}
fn ref_allows_mutation(&self, kind: mir::BorrowKind, place: mir::Place<'tcx>) -> bool {
}
}
+ /// `&` only allow mutation if the borrowed place is `!Freeze`.
+ ///
+ /// This assumes that it is UB to take the address of a struct field whose type is
+ /// `Freeze`, then use pointer arithmetic to derive a pointer to a *different* field of
+ /// that same struct whose type is `!Freeze`. If we decide that this is not UB, we will
+ /// have to check the type of the borrowed **local** instead of the borrowed **place**
+ /// below. See [rust-lang/unsafe-code-guidelines#134].
+ ///
+ /// [rust-lang/unsafe-code-guidelines#134]: https://github.com/rust-lang/unsafe-code-guidelines/issues/134
fn shared_borrow_allows_mutation(&self, place: mir::Place<'tcx>) -> bool {
!place
.ty(self.ccx.body, self.ccx.tcx)
(accepted, macro_attributes_in_derive_output, "1.57.0", Some(81119), None),
/// Allows panicking during const eval (producing compile-time errors).
(accepted, const_panic, "1.57.0", Some(51999), None),
+ /// Lessens the requirements for structs to implement `Unsize`.
+ (accepted, relaxed_struct_unsize, "1.58.0", Some(81793), None),
// -------------------------------------------------------------------------
// feature-group-end: accepted features
}
}
+// See https://rustc-dev-guide.rust-lang.org/feature-gates.html#feature-gates for more
+// documentation about handling feature gates.
+//
// If you change this, please modify `src/doc/unstable-book` as well.
//
-// Don't ever remove anything from this list; move them to `removed.rs`.
+// Don't ever remove anything from this list; move them to `accepted.rs` if
+// accepted or `removed.rs` if removed.
//
// The version numbers here correspond to the version in which the current status
// was set. This is most important for knowing when a particular feature became
/// Allows `extern "C-cmse-nonsecure-call" fn()`.
(active, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391), None),
- /// Lessens the requirements for structs to implement `Unsize`.
- (active, relaxed_struct_unsize, "1.51.0", Some(81793), None),
-
/// Allows associated types in inherent impls.
(incomplete, inherent_associated_types, "1.52.0", Some(8995), None),
/// not changed from prior instances of the same struct (RFC #2528)
(incomplete, type_changing_struct_update, "1.58.0", Some(86555), None),
+ /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.
+ (active, doc_auto_cfg, "1.58.0", Some(43781), None),
+
// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------
use crate::infer::lexical_region_resolve::RegionResolutionError;
use crate::infer::SubregionOrigin;
-use rustc_errors::{struct_span_err, ErrorReported};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
+use rustc_hir as hir;
+use rustc_hir::{GenericParamKind, Ty};
+use rustc_middle::ty::Region;
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
/// Print the error message for lifetime errors when both the concerned regions are anonymous.
}
};
- let mut e = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch");
+ let mut err = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch");
- e.span_label(span_1, main_label);
- e.span_label(span_2, String::new());
- e.span_label(span, span_label);
+ err.span_label(span_1, main_label);
+ err.span_label(span_2, String::new());
+ err.span_label(span, span_label);
+
+ self.suggest_adding_lifetime_params(sub, ty_sup, ty_sub, &mut err);
if let Some(t) = future_return_type {
let snip = self
(_, "") => None,
_ => Some(s),
})
- .unwrap_or("{unnamed_type}".to_string());
+ .unwrap_or_else(|| "{unnamed_type}".to_string());
- e.span_label(
+ err.span_label(
t.span,
&format!("this `async fn` implicitly returns an `impl Future<Output = {}>`", snip),
);
}
- e.emit();
+ err.emit();
Some(ErrorReported)
}
+
+ fn suggest_adding_lifetime_params(
+ &self,
+ sub: Region<'tcx>,
+ ty_sup: &Ty<'_>,
+ ty_sub: &Ty<'_>,
+ err: &mut DiagnosticBuilder<'_>,
+ ) {
+ if let (
+ hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. },
+ hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. },
+ ) = (ty_sub, ty_sup)
+ {
+ if lifetime_sub.name.is_elided() && lifetime_sup.name.is_elided() {
+ if let Some(anon_reg) = self.tcx().is_suitable_region(sub) {
+ let hir_id = self.tcx().hir().local_def_id_to_hir_id(anon_reg.def_id);
+ if let hir::Node::Item(&hir::Item {
+ kind: hir::ItemKind::Fn(_, ref generics, ..),
+ ..
+ }) = self.tcx().hir().get(hir_id)
+ {
+ let (suggestion_param_name, introduce_new) = generics
+ .params
+ .iter()
+ .find(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
+ .and_then(|p| self.tcx().sess.source_map().span_to_snippet(p.span).ok())
+ .map(|name| (name, false))
+ .unwrap_or_else(|| ("'a".to_string(), true));
+
+ let mut suggestions = vec![
+ if let hir::LifetimeName::Underscore = lifetime_sub.name {
+ (lifetime_sub.span, suggestion_param_name.clone())
+ } else {
+ (
+ lifetime_sub.span.shrink_to_hi(),
+ suggestion_param_name.clone() + " ",
+ )
+ },
+ if let hir::LifetimeName::Underscore = lifetime_sup.name {
+ (lifetime_sup.span, suggestion_param_name.clone())
+ } else {
+ (
+ lifetime_sup.span.shrink_to_hi(),
+ suggestion_param_name.clone() + " ",
+ )
+ },
+ ];
+
+ if introduce_new {
+ let new_param_suggestion = match &generics.params {
+ [] => (generics.span, format!("<{}>", suggestion_param_name)),
+ [first, ..] => (
+ first.span.shrink_to_lo(),
+ format!("{}, ", suggestion_param_name),
+ ),
+ };
+
+ suggestions.push(new_param_suggestion);
+ }
+
+ err.multipart_suggestion(
+ "consider introducing a named lifetime parameter",
+ suggestions,
+ Applicability::MaybeIncorrect,
+ );
+ err.note(
+ "each elided lifetime in input position becomes a distinct lifetime",
+ );
+ }
+ }
+ }
+ }
+ }
}
use crate::{AnalysisDomain, GenKill, GenKillAnalysis};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::*;
-use rustc_middle::ty::{ParamEnv, TyCtxt};
-use rustc_span::DUMMY_SP;
-
-pub type MaybeMutBorrowedLocals<'mir, 'tcx> = MaybeBorrowedLocals<MutBorrow<'mir, 'tcx>>;
/// A dataflow analysis that tracks whether a pointer or reference could possibly exist that points
/// to a given local.
///
-/// The `K` parameter determines what kind of borrows are tracked. By default,
-/// `MaybeBorrowedLocals` looks for *any* borrow of a local. If you are only interested in borrows
-/// that might allow mutation, use the `MaybeMutBorrowedLocals` type alias instead.
-///
/// At present, this is used as a very limited form of alias analysis. For example,
/// `MaybeBorrowedLocals` is used to compute which locals are live during a yield expression for
-/// immovable generators. `MaybeMutBorrowedLocals` is used during const checking to prove that a
-/// local has not been mutated via indirect assignment (e.g., `*p = 42`), the side-effects of a
-/// function call or inline assembly.
-pub struct MaybeBorrowedLocals<K = AnyBorrow> {
- kind: K,
+/// immovable generators.
+pub struct MaybeBorrowedLocals {
ignore_borrow_on_drop: bool,
}
/// A dataflow analysis that records whether a pointer or reference exists that may alias the
/// given local.
pub fn all_borrows() -> Self {
- MaybeBorrowedLocals { kind: AnyBorrow, ignore_borrow_on_drop: false }
- }
-}
-
-impl MaybeMutBorrowedLocals<'mir, 'tcx> {
- /// A dataflow analysis that records whether a pointer or reference exists that may *mutably*
- /// alias the given local.
- ///
- /// This includes `&mut` and pointers derived from an `&mut`, as well as shared borrows of
- /// types with interior mutability.
- pub fn mut_borrows_only(
- tcx: TyCtxt<'tcx>,
- body: &'mir mir::Body<'tcx>,
- param_env: ParamEnv<'tcx>,
- ) -> Self {
- MaybeBorrowedLocals {
- kind: MutBorrow { body, tcx, param_env },
- ignore_borrow_on_drop: false,
- }
+ MaybeBorrowedLocals { ignore_borrow_on_drop: false }
}
}
-impl<K> MaybeBorrowedLocals<K> {
+impl MaybeBorrowedLocals {
/// During dataflow analysis, ignore the borrow that may occur when a place is dropped.
///
/// Drop terminators may call custom drop glue (`Drop::drop`), which takes `&mut self` as a
MaybeBorrowedLocals { ignore_borrow_on_drop: true, ..self }
}
- fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T, K> {
- TransferFunction {
- kind: &self.kind,
- trans,
- ignore_borrow_on_drop: self.ignore_borrow_on_drop,
- }
+ fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T> {
+ TransferFunction { trans, ignore_borrow_on_drop: self.ignore_borrow_on_drop }
}
}
-impl<K> AnalysisDomain<'tcx> for MaybeBorrowedLocals<K>
-where
- K: BorrowAnalysisKind<'tcx>,
-{
+impl AnalysisDomain<'tcx> for MaybeBorrowedLocals {
type Domain = BitSet<Local>;
- const NAME: &'static str = K::ANALYSIS_NAME;
+ const NAME: &'static str = "maybe_borrowed_locals";
fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
// bottom = unborrowed
}
}
-impl<K> GenKillAnalysis<'tcx> for MaybeBorrowedLocals<K>
-where
- K: BorrowAnalysisKind<'tcx>,
-{
+impl GenKillAnalysis<'tcx> for MaybeBorrowedLocals {
type Idx = Local;
fn statement_effect(
}
/// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`.
-struct TransferFunction<'a, T, K> {
+struct TransferFunction<'a, T> {
trans: &'a mut T,
- kind: &'a K,
ignore_borrow_on_drop: bool,
}
-impl<T, K> Visitor<'tcx> for TransferFunction<'a, T, K>
+impl<T> Visitor<'tcx> for TransferFunction<'a, T>
where
T: GenKill<Local>,
- K: BorrowAnalysisKind<'tcx>,
{
fn visit_statement(&mut self, stmt: &Statement<'tcx>, location: Location) {
self.super_statement(stmt, location);
self.super_rvalue(rvalue, location);
match rvalue {
- mir::Rvalue::AddressOf(mt, borrowed_place) => {
- if !borrowed_place.is_indirect() && self.kind.in_address_of(*mt, *borrowed_place) {
+ mir::Rvalue::AddressOf(_mt, borrowed_place) => {
+ if !borrowed_place.is_indirect() {
self.trans.gen(borrowed_place.local);
}
}
- mir::Rvalue::Ref(_, kind, borrowed_place) => {
- if !borrowed_place.is_indirect() && self.kind.in_ref(*kind, *borrowed_place) {
+ mir::Rvalue::Ref(_, _kind, borrowed_place) => {
+ if !borrowed_place.is_indirect() {
self.trans.gen(borrowed_place.local);
}
}
}
}
}
-
-pub struct AnyBorrow;
-
-pub struct MutBorrow<'mir, 'tcx> {
- tcx: TyCtxt<'tcx>,
- body: &'mir Body<'tcx>,
- param_env: ParamEnv<'tcx>,
-}
-
-impl MutBorrow<'mir, 'tcx> {
- /// `&` and `&raw` only allow mutation if the borrowed place is `!Freeze`.
- ///
- /// This assumes that it is UB to take the address of a struct field whose type is
- /// `Freeze`, then use pointer arithmetic to derive a pointer to a *different* field of
- /// that same struct whose type is `!Freeze`. If we decide that this is not UB, we will
- /// have to check the type of the borrowed **local** instead of the borrowed **place**
- /// below. See [rust-lang/unsafe-code-guidelines#134].
- ///
- /// [rust-lang/unsafe-code-guidelines#134]: https://github.com/rust-lang/unsafe-code-guidelines/issues/134
- fn shared_borrow_allows_mutation(&self, place: Place<'tcx>) -> bool {
- !place.ty(self.body, self.tcx).ty.is_freeze(self.tcx.at(DUMMY_SP), self.param_env)
- }
-}
-
-pub trait BorrowAnalysisKind<'tcx> {
- const ANALYSIS_NAME: &'static str;
-
- fn in_address_of(&self, mt: Mutability, place: Place<'tcx>) -> bool;
- fn in_ref(&self, kind: mir::BorrowKind, place: Place<'tcx>) -> bool;
-}
-
-impl BorrowAnalysisKind<'tcx> for AnyBorrow {
- const ANALYSIS_NAME: &'static str = "maybe_borrowed_locals";
-
- fn in_ref(&self, _: mir::BorrowKind, _: Place<'_>) -> bool {
- true
- }
- fn in_address_of(&self, _: Mutability, _: Place<'_>) -> bool {
- true
- }
-}
-
-impl BorrowAnalysisKind<'tcx> for MutBorrow<'mir, 'tcx> {
- const ANALYSIS_NAME: &'static str = "maybe_mut_borrowed_locals";
-
- fn in_ref(&self, kind: mir::BorrowKind, place: Place<'tcx>) -> bool {
- match kind {
- mir::BorrowKind::Mut { .. } => true,
- mir::BorrowKind::Shared | mir::BorrowKind::Shallow | mir::BorrowKind::Unique => {
- self.shared_borrow_allows_mutation(place)
- }
- }
- }
-
- fn in_address_of(&self, mt: Mutability, place: Place<'tcx>) -> bool {
- match mt {
- Mutability::Mut => true,
- Mutability::Not => self.shared_borrow_allows_mutation(place),
- }
- }
-}
mod liveness;
mod storage_liveness;
-pub use self::borrowed_locals::{MaybeBorrowedLocals, MaybeMutBorrowedLocals};
+pub use self::borrowed_locals::MaybeBorrowedLocals;
pub use self::init_locals::MaybeInitializedLocals;
pub use self::liveness::MaybeLiveLocals;
pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageLive};
use rustc_middle::ty::{self, Ty, TyCtxt};
use crate::impls::{
- DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeLiveLocals, MaybeMutBorrowedLocals,
- MaybeUninitializedPlaces,
+ DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeLiveLocals, MaybeUninitializedPlaces,
};
use crate::move_paths::{HasMoveData, MoveData};
use crate::move_paths::{LookupResult, MovePathIndex};
sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_def_inits);
}
- if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_indirectly_mutable).is_some() {
- let flow_mut_borrowed = MaybeMutBorrowedLocals::mut_borrows_only(tcx, body, param_env)
- .into_engine(tcx, body)
- .iterate_to_fixpoint();
-
- sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_mut_borrowed);
- }
-
if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_liveness).is_some() {
let flow_liveness = MaybeLiveLocals.into_engine(tcx, body).iterate_to_fixpoint();
}
}
-impl<'tcx> RustcPeekAt<'tcx> for MaybeMutBorrowedLocals<'_, 'tcx> {
- fn peek_at(
- &self,
- tcx: TyCtxt<'tcx>,
- place: mir::Place<'tcx>,
- flow_state: &BitSet<Local>,
- call: PeekCall,
- ) {
- info!(?place, "peek_at");
- let Some(local) = place.as_local() else {
- tcx.sess.span_err(call.span, "rustc_peek: argument was not a local");
- return;
- };
-
- if !flow_state.contains(local) {
- tcx.sess.span_err(call.span, "rustc_peek: bit not set");
- }
- }
-}
-
impl<'tcx> RustcPeekAt<'tcx> for MaybeLiveLocals {
fn peek_at(
&self,
div_assign,
doc,
doc_alias,
+ doc_auto_cfg,
doc_cfg,
doc_cfg_hide,
doc_keyword,
rustc_partition_reused,
rustc_peek,
rustc_peek_definite_init,
- rustc_peek_indirectly_mutable,
rustc_peek_liveness,
rustc_peek_maybe_init,
rustc_peek_maybe_uninit,
pub fn target() -> Target {
let mut base = super::apple_base::opts("macos");
- base.cpu = "apple-a12".to_string();
+ base.cpu = "apple-a14".to_string();
base.max_atomic_width = Some(128);
// FIXME: The leak sanitizer currently fails the tests, see #88132.
let tail_field_ty = tcx.type_of(tail_field.did);
let mut unsizing_params = GrowableBitSet::new_empty();
- if tcx.features().relaxed_struct_unsize {
- for arg in tail_field_ty.walk(tcx) {
- if let Some(i) = maybe_unsizing_param_idx(arg) {
- unsizing_params.insert(i);
- }
- }
-
- // Ensure none of the other fields mention the parameters used
- // in unsizing.
- for field in prefix_fields {
- for arg in tcx.type_of(field.did).walk(tcx) {
- if let Some(i) = maybe_unsizing_param_idx(arg) {
- unsizing_params.remove(i);
- }
- }
+ for arg in tail_field_ty.walk(tcx) {
+ if let Some(i) = maybe_unsizing_param_idx(arg) {
+ unsizing_params.insert(i);
}
+ }
- if unsizing_params.is_empty() {
- return Err(Unimplemented);
- }
- } else {
- let mut found = false;
- for arg in tail_field_ty.walk(tcx) {
+ // Ensure none of the other fields mention the parameters used
+ // in unsizing.
+ for field in prefix_fields {
+ for arg in tcx.type_of(field.did).walk(tcx) {
if let Some(i) = maybe_unsizing_param_idx(arg) {
- unsizing_params.insert(i);
- found = true;
+ unsizing_params.remove(i);
}
}
- if !found {
- return Err(Unimplemented);
- }
+ }
- // Ensure none of the other fields mention the parameters used
- // in unsizing.
- // FIXME(eddyb) cache this (including computing `unsizing_params`)
- // by putting it in a query; it would only need the `DefId` as it
- // looks at declared field types, not anything substituted.
- for field in prefix_fields {
- for arg in tcx.type_of(field.did).walk(tcx) {
- if let Some(i) = maybe_unsizing_param_idx(arg) {
- if unsizing_params.contains(i) {
- return Err(Unimplemented);
- }
- }
- }
- }
+ if unsizing_params.is_empty() {
+ return Err(Unimplemented);
}
// Extract `TailField<T>` and `TailField<U>` from `Struct<T>` and `Struct<U>`.
panic_unwind = { path = "../panic_unwind", optional = true }
panic_abort = { path = "../panic_abort" }
core = { path = "../core" }
-libc = { version = "0.2.103", default-features = false, features = ['rustc-dep-of-std'] }
+libc = { version = "0.2.106", default-features = false, features = ['rustc-dep-of-std'] }
compiler_builtins = { version = "0.1.44" }
profiler_builtins = { path = "../profiler_builtins", optional = true }
unwind = { path = "../unwind" }
#[unstable(feature = "c_size_t", issue = "88345")]
pub type c_size_t = usize;
-/// Equivalent to C's `ssize_t` type, from `stddef.h` (or `cstddef` for C++).
+/// Equivalent to C's `ptrdiff_t` type, from `stddef.h` (or `cstddef` for C++).
+///
+/// This type is currently always [`isize`], however in the future there may be
+/// platforms where this is not the case.
+#[unstable(feature = "c_size_t", issue = "88345")]
+pub type c_ptrdiff_t = isize;
+
+/// Equivalent to C's `ssize_t` (on POSIX) or `SSIZE_T` (on Windows) type.
///
/// This type is currently always [`isize`], however in the future there may be
/// platforms where this is not the case.
unsafe { crate::hint::unreachable_unchecked() };
}
-pub fn available_concurrency() -> io::Result<crate::num::NonZeroUsize> {
+pub fn available_parallelism() -> io::Result<crate::num::NonZeroUsize> {
super::unsupported()
}
doc::RustByExample,
doc::RustcBook,
doc::CargoBook,
+ doc::Clippy,
doc::EmbeddedBook,
doc::EditionGuide,
),
t!(fs::create_dir_all(&libdir_self_contained));
let mut target_deps = vec![];
- // Copies the CRT objects.
+ // Copies the libc and CRT objects.
//
// rustc historically provides a more self-contained installation for musl targets
// not requiring the presence of a native musl toolchain. For example, it can fall back
let srcdir = builder.musl_libdir(target).unwrap_or_else(|| {
panic!("Target {:?} does not have a \"musl-libdir\" key", target.triple)
});
- for &obj in &["crt1.o", "Scrt1.o", "rcrt1.o", "crti.o", "crtn.o"] {
+ for &obj in &["libc.a", "crt1.o", "Scrt1.o", "rcrt1.o", "crti.o", "crtn.o"] {
copy_and_stamp(
builder,
&libdir_self_contained,
panic!("Target {:?} does not have a \"wasi-root\" key", target.triple)
})
.join("lib/wasm32-wasi");
- for &obj in &["crt1-command.o", "crt1-reactor.o"] {
+ for &obj in &["libc.a", "crt1-command.o", "crt1-reactor.o"] {
copy_and_stamp(
builder,
&libdir_self_contained,
"src/tools/rustfmt",
["rustfmt-nightly", "rustfmt-config_proc_macro"],
);
+tool_doc!(Clippy, "clippy", "src/tools/clippy", ["clippy_utils"]);
#[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct ErrorIndex {
}
println!(
- "L1 Cache: {}",
+ "L0 Cache: {}",
((ebx >> 22) + 1) * (((ebx >> 12) & 0x3ff) + 1) * ((ebx & 0xfff) + 1) * (ecx + 1)
);
```
fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>> {
let sess = tcx.sess;
let doc_cfg_active = tcx.features().doc_cfg;
+ let doc_auto_cfg_active = tcx.features().doc_auto_cfg;
fn single<T: IntoIterator>(it: T) -> Option<T::Item> {
let mut iter = it.into_iter();
Some(item)
}
- let mut cfg = if doc_cfg_active {
+ let mut cfg = if doc_cfg_active || doc_auto_cfg_active {
let mut doc_cfg = self
.iter()
.filter(|attr| attr.has_name(sym::doc))
.flat_map(|attr| attr.meta_item_list().unwrap_or_else(Vec::new))
.filter(|attr| attr.has_name(sym::cfg))
.peekable();
- if doc_cfg.peek().is_some() {
+ if doc_cfg.peek().is_some() && doc_cfg_active {
doc_cfg
.filter_map(|attr| Cfg::parse(attr.meta_item()?).ok())
.fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg)
- } else {
+ } else if doc_auto_cfg_active {
self.iter()
.filter(|attr| attr.has_name(sym::cfg))
.filter_map(|attr| single(attr.meta_item_list()?))
.filter_map(|attr| Cfg::parse(attr.meta_item()?).ok())
.filter(|cfg| !hidden_cfg.contains(cfg))
.fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg)
+ } else {
+ Cfg::True
}
} else {
Cfg::True
i.items = i.items.into_iter().filter_map(|x| self.fold_item(x)).collect();
ImplItem(i)
}
- VariantItem(i) => {
- let i2 = i.clone(); // this clone is small
- match i {
- Variant::Struct(mut j) => {
- let num_fields = j.fields.len();
- j.fields = j.fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
- j.fields_stripped |= num_fields != j.fields.len()
- || j.fields.iter().any(|f| f.is_stripped());
- VariantItem(Variant::Struct(j))
- }
- Variant::Tuple(fields) => {
- let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
- VariantItem(Variant::Tuple(fields))
- }
- _ => VariantItem(i2),
+ VariantItem(i) => match i {
+ Variant::Struct(mut j) => {
+ let num_fields = j.fields.len();
+ j.fields = j.fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
+ j.fields_stripped |=
+ num_fields != j.fields.len() || j.fields.iter().any(|f| f.is_stripped());
+ VariantItem(Variant::Struct(j))
}
- }
- x => x,
+ Variant::Tuple(fields) => {
+ let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
+ VariantItem(Variant::Tuple(fields))
+ }
+ Variant::CLike => VariantItem(Variant::CLike),
+ },
+ ExternCrateItem { src: _ }
+ | ImportItem(_)
+ | FunctionItem(_)
+ | TypedefItem(_, _)
+ | OpaqueTyItem(_)
+ | StaticItem(_)
+ | ConstantItem(_)
+ | TraitAliasItem(_)
+ | TyMethodItem(_)
+ | MethodItem(_, _)
+ | StructFieldItem(_)
+ | ForeignFunctionItem(_)
+ | ForeignStaticItem(_)
+ | ForeignTypeItem
+ | MacroItem(_)
+ | ProcMacroItem(_)
+ | PrimitiveItem(_)
+ | AssocConstItem(_, _)
+ | AssocTypeItem(_, _)
+ | KeywordItem(_) => kind,
}
}
fn fold_crate(&mut self, mut c: Crate) -> Crate {
c.module = self.fold_item(c.module).unwrap();
- {
- let external_traits = { std::mem::take(&mut *c.external_traits.borrow_mut()) };
- for (k, mut v) in external_traits {
- v.trait_.items =
- v.trait_.items.into_iter().filter_map(|i| self.fold_item(i)).collect();
- c.external_traits.borrow_mut().insert(k, v);
- }
+ let external_traits = { std::mem::take(&mut *c.external_traits.borrow_mut()) };
+ for (k, mut v) in external_traits {
+ v.trait_.items = v.trait_.items.into_iter().filter_map(|i| self.fold_item(i)).collect();
+ c.external_traits.borrow_mut().insert(k, v);
}
+
c
}
}
}
}
- let (mut krate, local_sources, matches) = collect_spans_and_sources(
+ let (local_sources, matches) = collect_spans_and_sources(
tcx,
- krate,
+ &krate,
&src_root,
include_sources,
generate_link_to_definition,
};
if emit_crate {
- krate = sources::render(&mut cx, krate)?;
+ sources::render(&mut cx, &krate)?;
}
// Build our search index
use crate::html::highlight;
use crate::html::markdown::{HeadingOffset, Markdown, MarkdownHtml, MarkdownSummaryLine};
use crate::html::sources;
-use crate::scrape_examples::CallData;
+use crate::scrape_examples::{CallData, CallLocation};
use crate::try_none;
/// A pair of name and its optional document.
id = id
);
+ // Create a URL to a particular location in a reverse-dependency's source file
+ let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) {
+ let (line_lo, line_hi) = loc.call_expr.line_span;
+ let (anchor, title) = if line_lo == line_hi {
+ ((line_lo + 1).to_string(), format!("line {}", line_lo + 1))
+ } else {
+ (
+ format!("{}-{}", line_lo + 1, line_hi + 1),
+ format!("lines {}-{}", line_lo + 1, line_hi + 1),
+ )
+ };
+ let url = format!("{}{}#{}", cx.root_path(), call_data.url, anchor);
+ (url, title)
+ };
+
// Generate the HTML for a single example, being the title and code block
let write_example = |w: &mut Buffer, (path, call_data): (&PathBuf, &CallData)| -> bool {
let contents = match fs::read_to_string(&path) {
let (line_lo, line_hi) = loc.call_expr.line_span;
let byte_range = (byte_lo - byte_min, byte_hi - byte_min);
let line_range = (line_lo - line_min, line_hi - line_min);
- let (anchor, line_title) = if line_lo == line_hi {
- (format!("{}", line_lo + 1), format!("line {}", line_lo + 1))
- } else {
- (
- format!("{}-{}", line_lo + 1, line_hi + 1),
- format!("lines {}-{}", line_lo + 1, line_hi + 1),
- )
- };
- let line_url = format!("{}{}#{}", cx.root_path(), call_data.url, anchor);
+ let (line_url, line_title) = link_to_loc(call_data, loc);
(byte_range, (line_range, line_url, line_title))
})
if it.peek().is_some() {
write!(w, r#"<div class="example-links">Additional examples can be found in:<br><ul>"#);
it.for_each(|(_, call_data)| {
+ let (url, _) = link_to_loc(&call_data, &call_data.locations[0]);
write!(
w,
- r#"<li><a href="{root}{url}">{name}</a></li>"#,
- root = cx.root_path(),
- url = call_data.url,
+ r#"<li><a href="{url}">{name}</a></li>"#,
+ url = url,
name = call_data.display_name
);
});
/// only keep the `lo` and `hi`.
crate fn collect_spans_and_sources(
tcx: TyCtxt<'_>,
- krate: clean::Crate,
+ krate: &clean::Crate,
src_root: &Path,
include_sources: bool,
generate_link_to_definition: bool,
-) -> (clean::Crate, FxHashMap<PathBuf, String>, FxHashMap<Span, LinkFromSrc>) {
+) -> (FxHashMap<PathBuf, String>, FxHashMap<Span, LinkFromSrc>) {
let mut visitor = SpanMapVisitor { tcx, matches: FxHashMap::default() };
if include_sources {
if generate_link_to_definition {
tcx.hir().walk_toplevel_module(&mut visitor);
}
- let (krate, sources) = sources::collect_local_sources(tcx, src_root, krate);
- (krate, sources, visitor.matches)
+ let sources = sources::collect_local_sources(tcx, src_root, &krate);
+ (sources, visitor.matches)
} else {
- (krate, Default::default(), Default::default())
+ (Default::default(), Default::default())
}
}
use crate::clean;
use crate::docfs::PathError;
use crate::error::Error;
-use crate::fold::DocFolder;
use crate::html::format::Buffer;
use crate::html::highlight;
use crate::html::layout;
use crate::html::render::{Context, BASIC_KEYWORDS};
+use crate::visit::DocVisitor;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::ty::TyCtxt;
use std::fs;
use std::path::{Component, Path, PathBuf};
-crate fn render(cx: &mut Context<'_>, krate: clean::Crate) -> Result<clean::Crate, Error> {
+crate fn render(cx: &mut Context<'_>, krate: &clean::Crate) -> Result<(), Error> {
info!("emitting source files");
+
let dst = cx.dst.join("src").join(&*krate.name(cx.tcx()).as_str());
cx.shared.ensure_dir(&dst)?;
- let mut folder = SourceCollector { dst, cx, emitted_local_sources: FxHashSet::default() };
- Ok(folder.fold_crate(krate))
+
+ let mut collector = SourceCollector { dst, cx, emitted_local_sources: FxHashSet::default() };
+ collector.visit_crate(krate);
+ Ok(())
}
crate fn collect_local_sources<'tcx>(
tcx: TyCtxt<'tcx>,
src_root: &Path,
- krate: clean::Crate,
-) -> (clean::Crate, FxHashMap<PathBuf, String>) {
+ krate: &clean::Crate,
+) -> FxHashMap<PathBuf, String> {
let mut lsc = LocalSourcesCollector { tcx, local_sources: FxHashMap::default(), src_root };
-
- let krate = lsc.fold_crate(krate);
- (krate, lsc.local_sources)
+ lsc.visit_crate(krate);
+ lsc.local_sources
}
struct LocalSourcesCollector<'a, 'tcx> {
}
fn is_real_and_local(span: clean::Span, sess: &Session) -> bool {
- span.filename(sess).is_real() && span.cnum(sess) == LOCAL_CRATE
+ span.cnum(sess) == LOCAL_CRATE && span.filename(sess).is_real()
}
impl LocalSourcesCollector<'_, '_> {
return;
}
let filename = span.filename(sess);
- let p = match filename {
- FileName::Real(ref file) => match file.local_path() {
- Some(p) => p.to_path_buf(),
- _ => return,
- },
- _ => return,
+ let p = if let FileName::Real(file) = filename {
+ match file.into_local_path() {
+ Some(p) => p,
+ None => return,
+ }
+ } else {
+ return;
};
if self.local_sources.contains_key(&*p) {
// We've already emitted this source
}
}
-impl DocFolder for LocalSourcesCollector<'_, '_> {
- fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
- self.add_local_source(&item);
+impl DocVisitor for LocalSourcesCollector<'_, '_> {
+ fn visit_item(&mut self, item: &clean::Item) {
+ self.add_local_source(item);
- // FIXME: if `include_sources` isn't set and DocFolder didn't require consuming the crate by value,
- // we could return None here without having to walk the rest of the crate.
- Some(self.fold_item_recur(item))
+ self.visit_item_recur(item)
}
}
emitted_local_sources: FxHashSet<PathBuf>,
}
-impl DocFolder for SourceCollector<'_, '_> {
- fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
+impl DocVisitor for SourceCollector<'_, '_> {
+ fn visit_item(&mut self, item: &clean::Item) {
+ if !self.cx.include_sources {
+ return;
+ }
+
let tcx = self.cx.tcx();
let span = item.span(tcx);
let sess = tcx.sess;
// If we're not rendering sources, there's nothing to do.
// If we're including source files, and we haven't seen this file yet,
// then we need to render it out to the filesystem.
- if self.cx.include_sources && is_real_and_local(span, sess) {
+ if is_real_and_local(span, sess) {
let filename = span.filename(sess);
let span = span.inner();
let pos = sess.source_map().lookup_source_file(span.lo());
}
};
}
- // FIXME: if `include_sources` isn't set and DocFolder didn't require consuming the crate by value,
- // we could return None here without having to walk the rest of the crate.
- Some(self.fold_item_recur(item))
+
+ self.visit_item_recur(item)
}
}
data-search-js="{{static_root_path | safe}}search{{page.resource_suffix}}.js"> {#- -#}
</div>
<script src="{{static_root_path | safe}}main{{page.resource_suffix}}.js"></script> {#- -#}
- {%- if layout.scrape_examples_extension -%}
- <script src="{{static_root_path | safe}}scrape-examples{{page.resource_suffix}}.js"></script> {#- -#}
- {%- endif -%}
{%- for script in page.static_extra_scripts -%}
<script src="{{static_root_path | safe}}{{script}}.js"></script> {#- -#}
{% endfor %}
+ {%- if layout.scrape_examples_extension -%}
+ <script src="{{page.root_path | safe}}scrape-examples{{page.resource_suffix}}.js"></script> {#- -#}
+ {%- endif -%}
{%- for script in page.extra_scripts -%}
<script src="{{page.root_path | safe}}{{script}}.js"></script> {#- -#}
{% endfor %}
mod passes;
mod scrape_examples;
mod theme;
+mod visit;
mod visit_ast;
mod visit_lib;
use super::Pass;
use crate::clean::*;
use crate::core::DocContext;
-use crate::fold::DocFolder;
use crate::html::markdown::main_body_opts;
+use crate::visit::DocVisitor;
use core::ops::Range;
use pulldown_cmark::{Event, Parser, Tag};
use regex::Regex;
}
crate fn check_bare_urls(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
- BareUrlsLinter { cx }.fold_crate(krate)
+ BareUrlsLinter { cx }.visit_crate(&krate);
+ krate
}
-impl<'a, 'tcx> DocFolder for BareUrlsLinter<'a, 'tcx> {
- fn fold_item(&mut self, item: Item) -> Option<Item> {
+impl<'a, 'tcx> DocVisitor for BareUrlsLinter<'a, 'tcx> {
+ fn visit_item(&mut self, item: &Item) {
let hir_id = match DocContext::as_local_hir_id(self.cx.tcx, item.def_id) {
Some(hir_id) => hir_id,
None => {
// If non-local, no need to check anything.
- return Some(self.fold_item_recur(item));
+ return;
}
};
let dox = item.attrs.collapsed_doc_value().unwrap_or_default();
}
}
- Some(self.fold_item_recur(item))
+ self.visit_item_recur(item)
}
}
use crate::clean;
use crate::core::DocContext;
-use crate::fold::{self, DocFolder};
use crate::html::markdown::{find_testable_code, ErrorCodes};
use crate::passes::check_doc_test_visibility::{should_have_doc_example, Tests};
use crate::passes::Pass;
+use crate::visit::DocVisitor;
use rustc_hir as hir;
use rustc_lint::builtin::MISSING_DOCS;
use rustc_middle::lint::LintLevelSource;
fn calculate_doc_coverage(krate: clean::Crate, ctx: &mut DocContext<'_>) -> clean::Crate {
let mut calc = CoverageCalculator { items: Default::default(), ctx };
- let krate = calc.fold_crate(krate);
+ calc.visit_crate(&krate);
calc.print_results();
}
}
-impl<'a, 'b> fold::DocFolder for CoverageCalculator<'a, 'b> {
- fn fold_item(&mut self, i: clean::Item) -> Option<clean::Item> {
+impl<'a, 'b> DocVisitor for CoverageCalculator<'a, 'b> {
+ fn visit_item(&mut self, i: &clean::Item) {
+ if !i.def_id.is_local() {
+ // non-local items are skipped because they can be out of the users control,
+ // especially in the case of trait impls, which rustdoc eagerly inlines
+ return;
+ }
+
match *i.kind {
- _ if !i.def_id.is_local() => {
- // non-local items are skipped because they can be out of the users control,
- // especially in the case of trait impls, which rustdoc eagerly inlines
- return Some(i);
- }
clean::StrippedItem(..) => {
// don't count items in stripped modules
- return Some(i);
+ return;
}
// docs on `use` and `extern crate` statements are not displayed, so they're not
// worth counting
}
}
- Some(self.fold_item_recur(i))
+ self.visit_item_recur(i)
}
}
use crate::clean;
use crate::core::DocContext;
-use crate::fold::DocFolder;
use crate::html::markdown::{self, RustCodeBlock};
use crate::passes::Pass;
+use crate::visit::DocVisitor;
crate const CHECK_CODE_BLOCK_SYNTAX: Pass = Pass {
name: "check-code-block-syntax",
};
crate fn check_code_block_syntax(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
- SyntaxChecker { cx }.fold_crate(krate)
+ SyntaxChecker { cx }.visit_crate(&krate);
+ krate
}
struct SyntaxChecker<'a, 'tcx> {
}
}
-impl<'a, 'tcx> DocFolder for SyntaxChecker<'a, 'tcx> {
- fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
+impl<'a, 'tcx> DocVisitor for SyntaxChecker<'a, 'tcx> {
+ fn visit_item(&mut self, item: &clean::Item) {
if let Some(dox) = &item.attrs.collapsed_doc_value() {
let sp = item.attr_span(self.cx.tcx);
let extra = crate::html::markdown::ExtraInfo::new_did(
}
}
- Some(self.fold_item_recur(item))
+ self.visit_item_recur(item)
}
}
use crate::clean;
use crate::clean::*;
use crate::core::DocContext;
-use crate::fold::DocFolder;
use crate::html::markdown::{find_testable_code, ErrorCodes, Ignore, LangString};
+use crate::visit::DocVisitor;
use crate::visit_ast::inherits_doc_hidden;
use rustc_hir as hir;
use rustc_middle::lint::LintLevelSource;
crate fn check_doc_test_visibility(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
let mut coll = DocTestVisibilityLinter { cx };
-
- coll.fold_crate(krate)
+ coll.visit_crate(&krate);
+ krate
}
-impl<'a, 'tcx> DocFolder for DocTestVisibilityLinter<'a, 'tcx> {
- fn fold_item(&mut self, item: Item) -> Option<Item> {
+impl<'a, 'tcx> DocVisitor for DocTestVisibilityLinter<'a, 'tcx> {
+ fn visit_item(&mut self, item: &Item) {
let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new);
look_for_tests(self.cx, &dox, &item);
- Some(self.fold_item_recur(item))
+ self.visit_item_recur(item)
}
}
use crate::clean::{self, utils::find_nearest_parent_module, Crate, Item, ItemLink, PrimitiveType};
use crate::core::DocContext;
-use crate::fold::DocFolder;
use crate::html::markdown::{markdown_links, MarkdownLink};
use crate::lint::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS};
use crate::passes::Pass;
+use crate::visit::DocVisitor;
mod early;
crate use early::load_intra_link_crates;
};
fn collect_intra_doc_links(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
- LinkCollector {
+ let mut collector = LinkCollector {
cx,
mod_ids: Vec::new(),
kind_side_channel: Cell::new(None),
visited_links: FxHashMap::default(),
- }
- .fold_crate(krate)
+ };
+ collector.visit_crate(&krate);
+ krate
}
/// Top-level errors emitted by this pass.
)
}
-impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
- fn fold_item(&mut self, item: Item) -> Option<Item> {
+impl<'a, 'tcx> DocVisitor for LinkCollector<'a, 'tcx> {
+ fn visit_item(&mut self, item: &Item) {
use rustc_middle::ty::DefIdTree;
let parent_node =
}
}
- Some(if item.is_mod() {
+ if item.is_mod() {
if !inner_docs {
self.mod_ids.push(item.def_id.expect_def_id());
}
- let ret = self.fold_item_recur(item);
+ self.visit_item_recur(item);
self.mod_ids.pop();
- ret
} else {
- self.fold_item_recur(item)
- })
+ self.visit_item_recur(item)
+ }
}
}
use super::Pass;
use crate::clean::*;
use crate::core::DocContext;
-use crate::fold::DocFolder;
+use crate::visit::DocVisitor;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::DefId;
description: "retrieves trait impls for items in the crate",
};
-crate fn collect_trait_impls(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
- let (mut krate, synth_impls) = cx.sess().time("collect_synthetic_impls", || {
+crate fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate {
+ let synth_impls = cx.sess().time("collect_synthetic_impls", || {
let mut synth = SyntheticImplCollector { cx, impls: Vec::new() };
- (synth.fold_crate(krate), synth.impls)
+ synth.visit_crate(&krate);
+ synth.impls
});
let prims: FxHashSet<PrimitiveType> = krate.primitives.iter().map(|p| p.1).collect();
let crate_items = {
let mut coll = ItemCollector::new();
- krate = cx.sess().time("collect_items_for_trait_impls", || coll.fold_crate(krate));
+ cx.sess().time("collect_items_for_trait_impls", || coll.visit_crate(&krate));
coll.items
};
}
}
- let items = if let ModuleItem(Module { ref mut items, .. }) = *krate.module.kind {
- items
+ if let ModuleItem(Module { items, .. }) = &mut *krate.module.kind {
+ items.extend(synth_impls);
+ items.extend(new_items);
} else {
panic!("collect-trait-impls can't run");
};
- items.extend(synth_impls);
- items.extend(new_items);
krate
}
impls: Vec<Item>,
}
-impl<'a, 'tcx> DocFolder for SyntheticImplCollector<'a, 'tcx> {
- fn fold_item(&mut self, i: Item) -> Option<Item> {
+impl<'a, 'tcx> DocVisitor for SyntheticImplCollector<'a, 'tcx> {
+ fn visit_item(&mut self, i: &Item) {
if i.is_struct() || i.is_enum() || i.is_union() {
// FIXME(eddyb) is this `doc(hidden)` check needed?
if !self
}
}
- Some(self.fold_item_recur(i))
+ self.visit_item_recur(i)
}
}
}
}
-impl DocFolder for ItemCollector {
- fn fold_item(&mut self, i: Item) -> Option<Item> {
+impl DocVisitor for ItemCollector {
+ fn visit_item(&mut self, i: &Item) {
self.items.insert(i.def_id);
- Some(self.fold_item_recur(i))
+ self.visit_item_recur(i)
}
}
use super::Pass;
use crate::clean::*;
use crate::core::DocContext;
-use crate::fold::DocFolder;
use crate::html::markdown::main_body_opts;
-use core::ops::Range;
+use crate::visit::DocVisitor;
+
use pulldown_cmark::{Event, Parser, Tag};
+
use std::iter::Peekable;
+use std::ops::Range;
use std::str::CharIndices;
crate const CHECK_INVALID_HTML_TAGS: Pass = Pass {
}
crate fn check_invalid_html_tags(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
- if !cx.tcx.sess.is_nightly_build() {
- krate
- } else {
+ if cx.tcx.sess.is_nightly_build() {
let mut coll = InvalidHtmlTagsLinter { cx };
-
- coll.fold_crate(krate)
+ coll.visit_crate(&krate);
}
+ krate
}
const ALLOWED_UNCLOSED: &[&str] = &[
}
}
-impl<'a, 'tcx> DocFolder for InvalidHtmlTagsLinter<'a, 'tcx> {
- fn fold_item(&mut self, item: Item) -> Option<Item> {
+impl<'a, 'tcx> DocVisitor for InvalidHtmlTagsLinter<'a, 'tcx> {
+ fn visit_item(&mut self, item: &Item) {
let tcx = self.cx.tcx;
let hir_id = match DocContext::as_local_hir_id(tcx, item.def_id) {
Some(hir_id) => hir_id,
None => {
// If non-local, no need to check anything.
- return Some(self.fold_item_recur(item));
+ return;
}
};
let dox = item.attrs.collapsed_doc_value().unwrap_or_default();
}
}
- Some(self.fold_item_recur(item))
+ self.visit_item_recur(item)
}
}
--- /dev/null
+use crate::clean::*;
+
+crate trait DocVisitor: Sized {
+ fn visit_item(&mut self, item: &Item) {
+ self.visit_item_recur(item)
+ }
+
+ /// don't override!
+ fn visit_inner_recur(&mut self, kind: &ItemKind) {
+ match kind {
+ StrippedItem(..) => unreachable!(),
+ ModuleItem(i) => {
+ self.visit_mod(i);
+ return;
+ }
+ StructItem(i) => i.fields.iter().for_each(|x| self.visit_item(x)),
+ UnionItem(i) => i.fields.iter().for_each(|x| self.visit_item(x)),
+ EnumItem(i) => i.variants.iter().for_each(|x| self.visit_item(x)),
+ TraitItem(i) => i.items.iter().for_each(|x| self.visit_item(x)),
+ ImplItem(i) => i.items.iter().for_each(|x| self.visit_item(x)),
+ VariantItem(i) => match i {
+ Variant::Struct(j) => j.fields.iter().for_each(|x| self.visit_item(x)),
+ Variant::Tuple(fields) => fields.iter().for_each(|x| self.visit_item(x)),
+ Variant::CLike => {}
+ },
+ ExternCrateItem { src: _ }
+ | ImportItem(_)
+ | FunctionItem(_)
+ | TypedefItem(_, _)
+ | OpaqueTyItem(_)
+ | StaticItem(_)
+ | ConstantItem(_)
+ | TraitAliasItem(_)
+ | TyMethodItem(_)
+ | MethodItem(_, _)
+ | StructFieldItem(_)
+ | ForeignFunctionItem(_)
+ | ForeignStaticItem(_)
+ | ForeignTypeItem
+ | MacroItem(_)
+ | ProcMacroItem(_)
+ | PrimitiveItem(_)
+ | AssocConstItem(_, _)
+ | AssocTypeItem(_, _)
+ | KeywordItem(_) => {}
+ }
+ }
+
+ /// don't override!
+ fn visit_item_recur(&mut self, item: &Item) {
+ match &*item.kind {
+ StrippedItem(i) => self.visit_inner_recur(i),
+ _ => self.visit_inner_recur(&item.kind),
+ }
+ }
+
+ fn visit_mod(&mut self, m: &Module) {
+ m.items.iter().for_each(|i| self.visit_item(i))
+ }
+
+ fn visit_crate(&mut self, c: &Crate) {
+ self.visit_item(&c.module);
+
+ // FIXME: make this a simple by-ref for loop once external_traits is cleaned up
+ let external_traits = { std::mem::take(&mut *c.external_traits.borrow_mut()) };
+ for (k, v) in external_traits {
+ v.trait_.items.iter().for_each(|i| self.visit_item(i));
+ c.external_traits.borrow_mut().insert(k, v);
+ }
+ }
+}
--- /dev/null
+#![feature(doc_auto_cfg)]
+
+#![crate_name = "foo"]
+
+// @has foo/fn.foo.html
+// @has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-test'
+#[cfg(not(test))]
+pub fn foo() {}
#![crate_name = "oud"]
-#![feature(doc_cfg, doc_cfg_hide)]
+#![feature(doc_auto_cfg, doc_cfg, doc_cfg_hide)]
#![doc(cfg_hide(feature = "solecism"))]
#![crate_name = "funambulism"]
-#![feature(doc_cfg)]
+#![feature(doc_auto_cfg, doc_cfg)]
// @has 'funambulism/struct.Disorbed.html'
// @count - '//*[@class="stab portability"]' 1
--- /dev/null
+#![feature(doc_cfg)]
+
+#![crate_name = "foo"]
+
+// @has foo/fn.foo.html
+// @count - '//*[@class="item-info"]/*[@class="stab portability"]' 0
+#[cfg(not(test))]
+pub fn foo() {}
#![feature(const_mut_refs)]
#![feature(const_precise_live_drops)]
#![feature(const_swap)]
+#![feature(raw_ref_op)]
// Mutable borrow of a field with drop impl.
pub const fn f() {
let _ = x.is_some();
let _y = x; //~ ERROR destructors cannot be evaluated
}
+
+// Mutable raw reference to a Drop type.
+pub const fn address_of_mut() {
+ let mut x: Option<String> = None; //~ ERROR destructors cannot be evaluated
+ &raw mut x;
+
+ let mut y: Option<String> = None; //~ ERROR destructors cannot be evaluated
+ std::ptr::addr_of_mut!(y);
+}
+
+// Const raw reference to a Drop type. Conservatively assumed to allow mutation
+// until resolution of https://github.com/rust-lang/rust/issues/56604.
+pub const fn address_of_const() {
+ let x: Option<String> = None; //~ ERROR destructors cannot be evaluated
+ &raw const x;
+
+ let y: Option<String> = None; //~ ERROR destructors cannot be evaluated
+ std::ptr::addr_of!(y);
+}
error[E0493]: destructors cannot be evaluated at compile-time
- --> $DIR/qualif-indirect-mutation-fail.rs:8:9
+ --> $DIR/qualif-indirect-mutation-fail.rs:9:9
|
LL | let mut a: (u32, Option<String>) = (0, None);
| ^^^^^ constant functions cannot evaluate destructors
error[E0493]: destructors cannot be evaluated at compile-time
- --> $DIR/qualif-indirect-mutation-fail.rs:14:9
+ --> $DIR/qualif-indirect-mutation-fail.rs:15:9
|
LL | let mut x = None;
| ^^^^^ constants cannot evaluate destructors
error[E0493]: destructors cannot be evaluated at compile-time
- --> $DIR/qualif-indirect-mutation-fail.rs:30:9
+ --> $DIR/qualif-indirect-mutation-fail.rs:31:9
|
LL | let _z = x;
| ^^ constants cannot evaluate destructors
error[E0493]: destructors cannot be evaluated at compile-time
- --> $DIR/qualif-indirect-mutation-fail.rs:35:9
+ --> $DIR/qualif-indirect-mutation-fail.rs:36:9
|
LL | let x: Option<T> = None;
| ^ constant functions cannot evaluate destructors
error[E0493]: destructors cannot be evaluated at compile-time
- --> $DIR/qualif-indirect-mutation-fail.rs:43:9
+ --> $DIR/qualif-indirect-mutation-fail.rs:44:9
|
LL | let _y = x;
| ^^ constant functions cannot evaluate destructors
-error: aborting due to 5 previous errors
+error[E0493]: destructors cannot be evaluated at compile-time
+ --> $DIR/qualif-indirect-mutation-fail.rs:52:9
+ |
+LL | let mut y: Option<String> = None;
+ | ^^^^^ constant functions cannot evaluate destructors
+
+error[E0493]: destructors cannot be evaluated at compile-time
+ --> $DIR/qualif-indirect-mutation-fail.rs:49:9
+ |
+LL | let mut x: Option<String> = None;
+ | ^^^^^ constant functions cannot evaluate destructors
+
+error[E0493]: destructors cannot be evaluated at compile-time
+ --> $DIR/qualif-indirect-mutation-fail.rs:62:9
+ |
+LL | let y: Option<String> = None;
+ | ^ constant functions cannot evaluate destructors
+
+error[E0493]: destructors cannot be evaluated at compile-time
+ --> $DIR/qualif-indirect-mutation-fail.rs:59:9
+ |
+LL | let x: Option<String> = None;
+ | ^ constant functions cannot evaluate destructors
+
+error: aborting due to 9 previous errors
For more information about this error, try `rustc --explain E0493`.
#![feature(const_mut_refs)]
#![feature(const_precise_live_drops)]
+// Mutable reference allows only mutation of !Drop place.
pub const fn f() {
let mut x: (Option<String>, u32) = (None, 0);
let mut a = 10;
x.1 = a;
}
+// Mutable reference allows only mutation of !Drop place.
pub const fn g() {
let mut a: (u32, Option<String>) = (0, None);
let _ = &mut a.0;
}
+
+// Shared reference does not allow for mutation.
+pub const fn h() {
+ let x: Option<String> = None;
+ let _ = &x;
+}
+++ /dev/null
-// Test that we allow unsizing even if there is an unchanged param in the
-// field getting unsized.
-struct A<T, U: ?Sized + 'static>(T, B<T, U>);
-struct B<T, U: ?Sized>(T, U);
-
-fn main() {
- let x: A<[u32; 1], [u32; 1]> = A([0; 1], B([0; 1], [0; 1]));
- let y: &A<[u32; 1], [u32]> = &x; //~ ERROR mismatched types
- assert_eq!(y.1.1.len(), 1);
-}
+++ /dev/null
-error[E0308]: mismatched types
- --> $DIR/feature-gate-relaxed_struct_unsize.rs:8:34
- |
-LL | let y: &A<[u32; 1], [u32]> = &x;
- | ------------------- ^^ expected slice `[u32]`, found array `[u32; 1]`
- | |
- | expected due to this
- |
- = note: expected reference `&A<[u32; 1], [u32]>`
- found reference `&A<[u32; 1], [u32; 1]>`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// run-rustfix
+
+pub fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } //~ ERROR lifetime mismatch
+
+pub fn foo2<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } //~ ERROR lifetime mismatch
+
+pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } //~ ERROR lifetime mismatch
+
+fn main() {}
--- /dev/null
+error: lifetime may not live long enough
+ --> $DIR/issue-90170-elision-mismatch.rs:3:40
+ |
+LL | pub fn foo(x: &mut Vec<&u8>, y: &u8) { x.push(y); }
+ | - - ^^^^^^^^^ argument requires that `'1` must outlive `'2`
+ | | |
+ | | let's call the lifetime of this reference `'1`
+ | let's call the lifetime of this reference `'2`
+
+error: lifetime may not live long enough
+ --> $DIR/issue-90170-elision-mismatch.rs:5:44
+ |
+LL | pub fn foo2(x: &mut Vec<&'_ u8>, y: &u8) { x.push(y); }
+ | - - ^^^^^^^^^ argument requires that `'1` must outlive `'2`
+ | | |
+ | | let's call the lifetime of this reference `'1`
+ | let's call the lifetime of this reference `'2`
+
+error: lifetime may not live long enough
+ --> $DIR/issue-90170-elision-mismatch.rs:7:63
+ |
+LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&u8>, y: &u8) { x.push(y); }
+ | - - ^^^^^^^^^ argument requires that `'1` must outlive `'2`
+ | | |
+ | | let's call the lifetime of this reference `'1`
+ | let's call the lifetime of this reference `'2`
+
+error: aborting due to 3 previous errors
+
--- /dev/null
+// run-rustfix
+
+pub fn foo(x: &mut Vec<&u8>, y: &u8) { x.push(y); } //~ ERROR lifetime mismatch
+
+pub fn foo2(x: &mut Vec<&'_ u8>, y: &u8) { x.push(y); } //~ ERROR lifetime mismatch
+
+pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&u8>, y: &u8) { x.push(y); } //~ ERROR lifetime mismatch
+
+fn main() {}
--- /dev/null
+error[E0623]: lifetime mismatch
+ --> $DIR/issue-90170-elision-mismatch.rs:3:47
+ |
+LL | pub fn foo(x: &mut Vec<&u8>, y: &u8) { x.push(y); }
+ | --- --- ^ ...but data from `y` flows into `x` here
+ | |
+ | these two types are declared with different lifetimes...
+ |
+ = note: each elided lifetime in input position becomes a distinct lifetime
+help: consider introducing a named lifetime parameter
+ |
+LL | pub fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
+ | ++++ ++ ++
+
+error[E0623]: lifetime mismatch
+ --> $DIR/issue-90170-elision-mismatch.rs:5:51
+ |
+LL | pub fn foo2(x: &mut Vec<&'_ u8>, y: &u8) { x.push(y); }
+ | ------ --- ^ ...but data from `y` flows into `x` here
+ | |
+ | these two types are declared with different lifetimes...
+ |
+ = note: each elided lifetime in input position becomes a distinct lifetime
+help: consider introducing a named lifetime parameter
+ |
+LL | pub fn foo2<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
+ | ++++ ~~ ++
+
+error[E0623]: lifetime mismatch
+ --> $DIR/issue-90170-elision-mismatch.rs:7:70
+ |
+LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&u8>, y: &u8) { x.push(y); }
+ | --- --- ^ ...but data from `y` flows into `x` here
+ | |
+ | these two types are declared with different lifetimes...
+ |
+ = note: each elided lifetime in input position becomes a distinct lifetime
+help: consider introducing a named lifetime parameter
+ |
+LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
+ | ++ ++
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0623`.
| --- --- these two types are declared with different lifetimes...
LL | *v = x;
| ^ ...but data from `x` flows here
+ |
+ = note: each elided lifetime in input position becomes a distinct lifetime
+help: consider introducing a named lifetime parameter
+ |
+LL | fn foo<'a>(&mut (ref mut v, w): &mut (&'a u8, &u8), x: &'a u8) {
+ | ++++ ++ ++
error: aborting due to previous error
| --- --- these two types are declared with different lifetimes...
LL | z.push((x,y));
| ^ ...but data flows into `z` here
+ |
+ = note: each elided lifetime in input position becomes a distinct lifetime
+help: consider introducing a named lifetime parameter
+ |
+LL | fn foo<'a>(z: &mut Vec<(&'a u8,&u8)>, (x, y): (&'a u8, &u8)) {
+ | ++++ ++ ++
error[E0623]: lifetime mismatch
--> $DIR/ex3-both-anon-regions-3.rs:2:15
| --- --- these two types are declared with different lifetimes...
LL | z.push((x,y));
| ^ ...but data flows into `z` here
+ |
+ = note: each elided lifetime in input position becomes a distinct lifetime
+help: consider introducing a named lifetime parameter
+ |
+LL | fn foo<'a>(z: &mut Vec<(&u8,&'a u8)>, (x, y): (&u8, &'a u8)) {
+ | ++++ ++ ++
error: aborting due to 2 previous errors
| --- --- these two types are declared with different lifetimes...
LL | y.push(z);
| ^ ...but data from `z` flows into `y` here
+ |
+ = note: each elided lifetime in input position becomes a distinct lifetime
+help: consider introducing a named lifetime parameter
+ |
+LL | fn foo<'a>(x:fn(&u8, &u8), y: Vec<&'a u8>, z: &'a u8) {
+ | ++++ ++ ++
error: aborting due to previous error
| --- --- these two types are declared with different lifetimes...
LL | y.push(z);
| ^ ...but data from `z` flows into `y` here
+ |
+ = note: each elided lifetime in input position becomes a distinct lifetime
+help: consider introducing a named lifetime parameter
+ |
+LL | fn foo<'a>(x:Box<dyn Fn(&'a u8, &'a u8)> , y: Vec<&u8>, z: &u8) {
+ | ++++ ++ ++
error: aborting due to previous error
| --- --- these two types are declared with different lifetimes...
LL | x.push(y);
| ^ ...but data from `y` flows into `x` here
+ |
+ = note: each elided lifetime in input position becomes a distinct lifetime
+help: consider introducing a named lifetime parameter
+ |
+LL | fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) {
+ | ++++ ++ ++
error: aborting due to previous error
+++ /dev/null
-// compile-flags: -Zunleash-the-miri-inside-of-you
-
-// This test demonstrates a shortcoming of the `MaybeMutBorrowedLocals` analysis. It does not
-// handle code that takes a reference to one field of a struct, then use pointer arithmetic to
-// transform it to another field of that same struct that may have interior mutability. For now,
-// this is UB, but this may change in the future. See [rust-lang/unsafe-code-guidelines#134].
-//
-// [rust-lang/unsafe-code-guidelines#134]: https://github.com/rust-lang/unsafe-code-guidelines/issues/134
-
-#![feature(core_intrinsics, rustc_attrs, const_raw_ptr_deref)]
-
-use std::cell::UnsafeCell;
-use std::intrinsics::rustc_peek;
-
-#[repr(C)]
-struct PartialInteriorMut {
- zst: [i32; 0],
- cell: UnsafeCell<i32>,
-}
-
-#[rustc_mir(rustc_peek_indirectly_mutable,stop_after_dataflow)]
-const BOO: i32 = {
- let x = PartialInteriorMut {
- zst: [],
- cell: UnsafeCell::new(0),
- };
-
- let p_zst: *const _ = &x.zst ; // Doesn't cause `x` to get marked as indirectly mutable.
-
- let rmut_cell = unsafe {
- // Take advantage of the fact that `zst` and `cell` are at the same location in memory.
- // This trick would work with any size type if miri implemented `ptr::offset`.
- let p_cell = p_zst as *const UnsafeCell<i32>;
-
- let pmut_cell = (*p_cell).get();
- &mut *pmut_cell
- };
-
- *rmut_cell = 42; // Mutates `x` indirectly even though `x` is not marked indirectly mutable!!!
- let val = *rmut_cell;
- rustc_peek(x); //~ ERROR rustc_peek: bit not set
-
- val
-};
-
-fn main() {
- println!("{}", BOO);
-}
+++ /dev/null
-error: rustc_peek: bit not set
- --> $DIR/indirect-mutation-offset.rs:41:5
- |
-LL | rustc_peek(x);
- | ^^^^^^^^^^^^^
-
-error: stop_after_dataflow ended compilation
-
-error: aborting due to 2 previous errors
-
| ------ ------ ^ ...but data from `y` flows into `x` here
| |
| these two types are declared with different lifetimes...
+ |
+ = note: each elided lifetime in input position becomes a distinct lifetime
+help: consider introducing a named lifetime parameter
+ |
+LL | fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
+ | ++++ ~~ ~~
error: aborting due to previous error
-#![feature(relaxed_struct_unsize)]
// run-pass
// Test that we allow unsizing even if there is an unchanged param in the
// field getting unsized.
[dependencies]
clippy_lints = { version = "0.1", path = "clippy_lints" }
-semver = "0.11"
+semver = "1.0"
rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }
tempfile = { version = "3.2", optional = true }
[dev-dependencies]
-cargo_metadata = "0.12"
+cargo_metadata = "0.14"
compiletest_rs = { version = "0.7", features = ["tmp"] }
tester = "0.9"
regex = "1.5"
edition = "2021"
[dependencies]
-cargo_metadata = "0.12"
+cargo_metadata = "0.14"
clippy_utils = { path = "../clippy_utils" }
if_chain = "1.0"
itertools = "0.10"
toml = "0.5"
unicode-normalization = "0.1"
unicode-script = { version = "0.5", default-features = false }
-semver = "0.11"
+semver = "1.0"
rustc-semver = "1.1"
# NOTE: cargo requires serde feat in its url dep
# see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
-Subproject commit 1f47693e02809c97db61b51247ae4e4d46744c61
+Subproject commit 04f03a360ab8fef3d9c0ff84de2d39b8a196c717
&src_path.join("test/ui"),
&src_path.join("test/ui-fulldeps"),
&src_path.join("test/rustdoc-ui"),
+ &src_path.join("test/rustdoc"),
],
&mut |path| super::filter_dirs(path),
&mut |entry, contents| {