]> git.lizzy.rs Git - rust.git/commitdiff
Rollup merge of #87180 - notriddle:notriddle/sidebar-keyboard-mobile, r=GuillaumeGomez
authorGuillaume Gomez <guillaume1.gomez@gmail.com>
Fri, 16 Jul 2021 17:54:08 +0000 (19:54 +0200)
committerGitHub <noreply@github.com>
Fri, 16 Jul 2021 17:54:08 +0000 (19:54 +0200)
feat(rustdoc): open sidebar menu when links inside it are focused

Fixes #87172

Based on #87167 (which should be merged first)

r? ``@GuillaumeGomez``

Preview it at https://notriddle.com/notriddle-rustdoc-test/std/index.html

215 files changed:
Cargo.lock
compiler/rustc_ast_lowering/src/expr.rs
compiler/rustc_ast_lowering/src/lib.rs
compiler/rustc_ast_lowering/src/pat.rs
compiler/rustc_data_structures/src/vec_map.rs
compiler/rustc_feature/src/active.rs
compiler/rustc_hir/src/hir.rs
compiler/rustc_interface/src/tests.rs
compiler/rustc_middle/src/ich/impls_syntax.rs
compiler/rustc_middle/src/lint.rs
compiler/rustc_middle/src/ty/layout.rs
compiler/rustc_mir/src/interpret/memory.rs
compiler/rustc_mir_build/src/thir/pattern/mod.rs
compiler/rustc_resolve/src/diagnostics.rs
compiler/rustc_resolve/src/lib.rs
compiler/rustc_session/src/options.rs
compiler/rustc_trait_selection/src/traits/project.rs
compiler/rustc_trait_selection/src/traits/query/normalize.rs
compiler/rustc_typeck/src/check/demand.rs
compiler/rustc_typeck/src/check/pat.rs
compiler/rustc_typeck/src/check/upvar.rs
compiler/rustc_typeck/src/check/wfcheck.rs
compiler/rustc_typeck/src/collect/type_of.rs
compiler/rustc_typeck/src/expr_use_visitor.rs
library/alloc/src/sync.rs
library/core/src/array/mod.rs
library/core/src/iter/range.rs
library/core/src/lib.rs
library/core/src/macros/mod.rs
library/core/src/slice/rotate.rs
library/core/tests/lib.rs
library/std/src/lib.rs
library/std/src/os/wasi/fs.rs
library/std/src/os/wasi/io.rs
src/etc/natvis/intrinsic.natvis
src/etc/natvis/liballoc.natvis
src/etc/natvis/libcore.natvis
src/etc/natvis/libstd.natvis
src/librustdoc/clean/types.rs
src/librustdoc/clean/utils.rs
src/librustdoc/html/format.rs
src/librustdoc/html/markdown.rs
src/librustdoc/html/render/mod.rs
src/librustdoc/html/static/css/rustdoc.css
src/test/debuginfo/duration-type.rs [new file with mode: 0644]
src/test/debuginfo/marker-types.rs [new file with mode: 0644]
src/test/debuginfo/mutable-locs.rs
src/test/debuginfo/numeric-types.rs [new file with mode: 0644]
src/test/debuginfo/pretty-std.rs
src/test/debuginfo/range-types.rs
src/test/debuginfo/rc_arc.rs
src/test/debuginfo/rwlock-read.rs
src/test/rustdoc-gui/src/lib2/lib.rs
src/test/rustdoc-gui/type-declation-overflow.goml [new file with mode: 0644]
src/test/rustdoc/auxiliary/issue-86620-1.rs [new file with mode: 0644]
src/test/rustdoc/extern-default-method.rs
src/test/rustdoc/issue-86620.rs [new file with mode: 0644]
src/test/ui/associated-types/associated-types-for-unimpl-trait.stderr
src/test/ui/associated-types/associated-types-no-suitable-bound.stderr
src/test/ui/associated-types/associated-types-no-suitable-supertrait-2.stderr
src/test/ui/associated-types/associated-types-no-suitable-supertrait.stderr
src/test/ui/associated-types/associated-types-projection-to-unrelated-trait-in-method-without-default.stderr
src/test/ui/associated-types/normalization-debruijn-1.rs [new file with mode: 0644]
src/test/ui/associated-types/normalization-debruijn-2.rs [new file with mode: 0644]
src/test/ui/associated-types/normalization-debruijn-3.rs [new file with mode: 0644]
src/test/ui/associated-types/normalization-generality.rs [new file with mode: 0644]
src/test/ui/closures/2229_closure_analysis/issue-87097.rs [new file with mode: 0644]
src/test/ui/closures/2229_closure_analysis/issue-87097.stderr [new file with mode: 0644]
src/test/ui/closures/2229_closure_analysis/move_closure.rs
src/test/ui/closures/2229_closure_analysis/move_closure.stderr
src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs
src/test/ui/conflicting-repr-hints.rs
src/test/ui/conflicting-repr-hints.stderr
src/test/ui/const-generics/defaults/default-const-param-cannot-reference-self.rs [new file with mode: 0644]
src/test/ui/const-generics/defaults/default-const-param-cannot-reference-self.stderr [new file with mode: 0644]
src/test/ui/consts/const-float-classify.rs
src/test/ui/consts/rustc-impl-const-stability.rs
src/test/ui/generic-associated-types/issue-76407.rs [new file with mode: 0644]
src/test/ui/generic-associated-types/issue-76826.rs [new file with mode: 0644]
src/test/ui/generics/issue-61631-default-type-param-cannot-reference-self.rs
src/test/ui/generics/issue-61631-default-type-param-cannot-reference-self.stderr
src/test/ui/issues/issue-18611.stderr
src/test/ui/issues/issue-20831-debruijn.stderr
src/test/ui/lint/future-incompat-test.rs [new file with mode: 0644]
src/test/ui/lint/future-incompat-test.stderr [new file with mode: 0644]
src/test/ui/macros/assert-matches-macro-msg.rs
src/test/ui/matches2021.rs
src/test/ui/nll/normalization-bounds-error.stderr
src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stderr
src/test/ui/proc-macro/issue-73933-procedural-masquerade.stderr
src/test/ui/resolve/resolve-hint-macro.rs
src/test/ui/resolve/resolve-hint-macro.stderr
src/test/ui/rfc-2632-const-trait-impl/assoc-type.rs
src/test/ui/rfc-2632-const-trait-impl/attr-misuse.rs
src/test/ui/rfc-2632-const-trait-impl/attr-misuse.stderr
src/test/ui/rfc-2632-const-trait-impl/auxiliary/cross-crate.rs
src/test/ui/rfc-2632-const-trait-impl/auxiliary/staged-api.rs
src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs
src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr
src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs
src/test/ui/rfc-2632-const-trait-impl/call-generic-method-chain.rs
src/test/ui/rfc-2632-const-trait-impl/call-generic-method-nonconst.rs
src/test/ui/rfc-2632-const-trait-impl/call-generic-method-pass.rs
src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.rs
src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr
src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.rs
src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr
src/test/ui/rfc-2632-const-trait-impl/const-default-method-bodies.rs
src/test/ui/rfc-2632-const-trait-impl/const-default-method-bodies.stderr
src/test/ui/rfc-2632-const-trait-impl/const-impl-norecover.rs
src/test/ui/rfc-2632-const-trait-impl/const-impl-norecover.stderr
src/test/ui/rfc-2632-const-trait-impl/const-impl-recovery.rs
src/test/ui/rfc-2632-const-trait-impl/const-impl-recovery.stderr
src/test/ui/rfc-2632-const-trait-impl/cross-crate.gated.stderr
src/test/ui/rfc-2632-const-trait-impl/cross-crate.rs
src/test/ui/rfc-2632-const-trait-impl/cross-crate.stock.stderr
src/test/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr
src/test/ui/rfc-2632-const-trait-impl/feature-gate.rs
src/test/ui/rfc-2632-const-trait-impl/feature-gate.stock.stderr
src/test/ui/rfc-2632-const-trait-impl/generic-bound.rs
src/test/ui/rfc-2632-const-trait-impl/hir-const-check.rs
src/test/ui/rfc-2632-const-trait-impl/hir-const-check.stderr
src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.rs
src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.stderr
src/test/ui/rfc-2632-const-trait-impl/stability.rs
src/test/ui/rfc-2632-const-trait-impl/stability.stderr
src/test/ui/rfc-2632-const-trait-impl/syntax.rs
src/test/ui/suggestions/suggest-full-enum-variant-for-local-module.rs [new file with mode: 0644]
src/test/ui/suggestions/suggest-full-enum-variant-for-local-module.stderr [new file with mode: 0644]
src/test/ui/type-alias-impl-trait/issue-85113.rs
src/test/ui/type-alias-impl-trait/issue-85113.stderr
src/test/ui/wf/wf-foreign-fn-decl-ret.stderr
src/tools/cargo
src/tools/clippy/CHANGELOG.md
src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs
src/tools/clippy/clippy_lints/src/copies.rs
src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
src/tools/clippy/clippy_lints/src/dereference.rs
src/tools/clippy/clippy_lints/src/derive.rs
src/tools/clippy/clippy_lints/src/doc.rs
src/tools/clippy/clippy_lints/src/escape.rs
src/tools/clippy/clippy_lints/src/eta_reduction.rs
src/tools/clippy/clippy_lints/src/explicit_write.rs
src/tools/clippy/clippy_lints/src/format.rs
src/tools/clippy/clippy_lints/src/inherent_impl.rs
src/tools/clippy/clippy_lints/src/len_zero.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
src/tools/clippy/clippy_lints/src/manual_map.rs
src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs
src/tools/clippy/clippy_lints/src/matches.rs
src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
src/tools/clippy/clippy_lints/src/multiple_crate_versions.rs
src/tools/clippy/clippy_lints/src/mut_key.rs
src/tools/clippy/clippy_lints/src/needless_bool.rs
src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
src/tools/clippy/clippy_lints/src/option_if_let_else.rs
src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
src/tools/clippy/clippy_lints/src/ptr.rs
src/tools/clippy/clippy_lints/src/redundant_clone.rs
src/tools/clippy/clippy_lints/src/strings.rs
src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/types/mod.rs
src/tools/clippy/clippy_lints/src/types/rc_mutex.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
src/tools/clippy/clippy_lints/src/unicode.rs
src/tools/clippy/clippy_lints/src/unused_unit.rs
src/tools/clippy/clippy_lints/src/use_self.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
src/tools/clippy/clippy_lints/src/wildcard_dependencies.rs
src/tools/clippy/clippy_utils/src/higher.rs
src/tools/clippy/clippy_utils/src/lib.rs
src/tools/clippy/clippy_utils/src/numeric_literal.rs
src/tools/clippy/clippy_utils/src/paths.rs
src/tools/clippy/clippy_utils/src/ty.rs
src/tools/clippy/clippy_utils/src/usage.rs
src/tools/clippy/rust-toolchain
src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/auxiliary/proc_macro_derive.rs [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs
src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr
src/tools/clippy/tests/ui/branches_sharing_code/false_positives.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/crashes/ice-7423.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/default_numeric_fallback.rs [deleted file]
src/tools/clippy/tests/ui/default_numeric_fallback.stderr [deleted file]
src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs
src/tools/clippy/tests/ui/eta.fixed
src/tools/clippy/tests/ui/eta.rs
src/tools/clippy/tests/ui/eta.stderr
src/tools/clippy/tests/ui/explicit_write_non_rustfix.stderr
src/tools/clippy/tests/ui/format.fixed
src/tools/clippy/tests/ui/format.rs
src/tools/clippy/tests/ui/panicking_macros.rs
src/tools/clippy/tests/ui/rc_mutex.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/rc_mutex.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/redundant_allocation.fixed [deleted file]
src/tools/clippy/tests/ui/redundant_allocation.rs
src/tools/clippy/tests/ui/redundant_allocation.stderr
src/tools/clippy/tests/ui/redundant_allocation_fixable.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/redundant_allocation_fixable.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/redundant_allocation_fixable.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/redundant_clone.fixed
src/tools/clippy/tests/ui/redundant_clone.rs
src/tools/clippy/tests/ui/redundant_clone.stderr
src/tools/clippy/tests/ui/strlen_on_c_strings.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/strlen_on_c_strings.stderr [new file with mode: 0644]
src/tools/compiletest/src/json.rs

index 0aa132fe29ad56b97373ab45e1e3ba3c0791986e..b261a00f4f3ab92c6c93be4bce9df21b19bea984 100644 (file)
@@ -269,7 +269,7 @@ dependencies = [
  "crossbeam-utils 0.8.3",
  "curl",
  "curl-sys",
- "env_logger 0.8.1",
+ "env_logger 0.9.0",
  "filetime",
  "flate2",
  "fwdansi",
@@ -1094,6 +1094,19 @@ dependencies = [
  "termcolor",
 ]
 
+[[package]]
+name = "env_logger"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
+dependencies = [
+ "atty",
+ "humantime 2.0.1",
+ "log",
+ "regex",
+ "termcolor",
+]
+
 [[package]]
 name = "error_index_generator"
 version = "0.0.0"
index eb8e92f07eacc180ceb0e5bd060c6918be404146..7fecf537cfbd3fc077008e6a2bc5ff4c38fde740 100644 (file)
@@ -1067,6 +1067,15 @@ fn destructure_assign(
         eq_sign_span: Span,
         assignments: &mut Vec<hir::Stmt<'hir>>,
     ) -> &'hir hir::Pat<'hir> {
+        self.arena.alloc(self.destructure_assign_mut(lhs, eq_sign_span, assignments))
+    }
+
+    fn destructure_assign_mut(
+        &mut self,
+        lhs: &Expr,
+        eq_sign_span: Span,
+        assignments: &mut Vec<hir::Stmt<'hir>>,
+    ) -> hir::Pat<'hir> {
         match &lhs.kind {
             // Underscore pattern.
             ExprKind::Underscore => {
@@ -1080,7 +1089,7 @@ fn destructure_assign(
                     let (before, after) = pats.split_at(i);
                     hir::PatKind::Slice(
                         before,
-                        Some(self.pat_without_dbm(span, hir::PatKind::Wild)),
+                        Some(self.arena.alloc(self.pat_without_dbm(span, hir::PatKind::Wild))),
                         after,
                     )
                 } else {
@@ -1165,14 +1174,14 @@ fn destructure_assign(
                     let tuple_pat = hir::PatKind::Tuple(&[], Some(0));
                     return self.pat_without_dbm(lhs.span, tuple_pat);
                 } else {
-                    return self.destructure_assign(e, eq_sign_span, assignments);
+                    return self.destructure_assign_mut(e, eq_sign_span, assignments);
                 }
             }
             _ => {}
         }
         // Treat all other cases as normal lvalue.
         let ident = Ident::new(sym::lhs, lhs.span);
-        let (pat, binding) = self.pat_ident(lhs.span, ident);
+        let (pat, binding) = self.pat_ident_mut(lhs.span, ident);
         let ident = self.expr_ident(lhs.span, ident, binding);
         let assign = hir::ExprKind::Assign(self.lower_expr(lhs), ident, eq_sign_span);
         let expr = self.expr(lhs.span, assign, ThinVec::new());
@@ -1191,7 +1200,7 @@ fn destructure_sequence(
         ctx: &str,
         eq_sign_span: Span,
         assignments: &mut Vec<hir::Stmt<'hir>>,
-    ) -> (&'hir [&'hir hir::Pat<'hir>], Option<(usize, Span)>) {
+    ) -> (&'hir [hir::Pat<'hir>], Option<(usize, Span)>) {
         let mut rest = None;
         let elements =
             self.arena.alloc_from_iter(elements.iter().enumerate().filter_map(|(i, e)| {
@@ -1204,7 +1213,7 @@ fn destructure_sequence(
                     }
                     None
                 } else {
-                    Some(self.destructure_assign(e, eq_sign_span, assignments))
+                    Some(self.destructure_assign_mut(e, eq_sign_span, assignments))
                 }
             }));
         (elements, rest)
index b787158c34368591b889a0b9707614d6d15d92ca..a8d6a99cbeb4c91007ba4d68991f509c6308e088 100644 (file)
@@ -2577,21 +2577,35 @@ fn pat_ident(&mut self, span: Span, ident: Ident) -> (&'hir hir::Pat<'hir>, hir:
         self.pat_ident_binding_mode(span, ident, hir::BindingAnnotation::Unannotated)
     }
 
+    fn pat_ident_mut(&mut self, span: Span, ident: Ident) -> (hir::Pat<'hir>, hir::HirId) {
+        self.pat_ident_binding_mode_mut(span, ident, hir::BindingAnnotation::Unannotated)
+    }
+
     fn pat_ident_binding_mode(
         &mut self,
         span: Span,
         ident: Ident,
         bm: hir::BindingAnnotation,
     ) -> (&'hir hir::Pat<'hir>, hir::HirId) {
+        let (pat, hir_id) = self.pat_ident_binding_mode_mut(span, ident, bm);
+        (self.arena.alloc(pat), hir_id)
+    }
+
+    fn pat_ident_binding_mode_mut(
+        &mut self,
+        span: Span,
+        ident: Ident,
+        bm: hir::BindingAnnotation,
+    ) -> (hir::Pat<'hir>, hir::HirId) {
         let hir_id = self.next_id();
 
         (
-            self.arena.alloc(hir::Pat {
+            hir::Pat {
                 hir_id,
                 kind: hir::PatKind::Binding(bm, hir_id, ident.with_span_pos(span), None),
                 span,
                 default_binding_modes: true,
-            }),
+            },
             hir_id,
         )
     }
@@ -2609,13 +2623,8 @@ fn pat(&mut self, span: Span, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir>
         })
     }
 
-    fn pat_without_dbm(&mut self, span: Span, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
-        self.arena.alloc(hir::Pat {
-            hir_id: self.next_id(),
-            kind,
-            span,
-            default_binding_modes: false,
-        })
+    fn pat_without_dbm(&mut self, span: Span, kind: hir::PatKind<'hir>) -> hir::Pat<'hir> {
+        hir::Pat { hir_id: self.next_id(), kind, span, default_binding_modes: false }
     }
 
     fn ty_path(
index 66e623528f3bd179a189e75414ae59fc0b063f6e..d81ddd2c082e7af2bc08a3aeb96a7b7f2e458b12 100644 (file)
 use rustc_span::{source_map::Spanned, Span};
 
 impl<'a, 'hir> LoweringContext<'a, 'hir> {
-    crate fn lower_pat(&mut self, mut pattern: &Pat) -> &'hir hir::Pat<'hir> {
+    crate fn lower_pat(&mut self, pattern: &Pat) -> &'hir hir::Pat<'hir> {
+        self.arena.alloc(self.lower_pat_mut(pattern))
+    }
+
+    crate fn lower_pat_mut(&mut self, mut pattern: &Pat) -> hir::Pat<'hir> {
         ensure_sufficient_stack(|| {
             // loop here to avoid recursion
             let node = loop {
@@ -34,7 +38,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     }
                     PatKind::Or(ref pats) => {
                         break hir::PatKind::Or(
-                            self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat(x))),
+                            self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat_mut(x))),
                         );
                     }
                     PatKind::Path(ref qself, ref path) => {
@@ -101,7 +105,7 @@ fn lower_pat_tuple(
         &mut self,
         pats: &[P<Pat>],
         ctx: &str,
-    ) -> (&'hir [&'hir hir::Pat<'hir>], Option<usize>) {
+    ) -> (&'hir [hir::Pat<'hir>], Option<usize>) {
         let mut elems = Vec::with_capacity(pats.len());
         let mut rest = None;
 
@@ -140,7 +144,7 @@ fn lower_pat_tuple(
             }
 
             // It was not a sub-tuple pattern so lower it normally.
-            elems.push(self.lower_pat(pat));
+            elems.push(self.lower_pat_mut(pat));
         }
 
         for (_, pat) in iter {
@@ -149,7 +153,7 @@ fn lower_pat_tuple(
                 // ...but there was one again, so error.
                 self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx);
             } else {
-                elems.push(self.lower_pat(pat));
+                elems.push(self.lower_pat_mut(pat));
             }
         }
 
@@ -189,11 +193,11 @@ fn lower_pat_slice(&mut self, pats: &[P<Pat>]) -> hir::PatKind<'hir> {
                 // Record, lower it to `$binding_mode $ident @ _`, and stop here.
                 PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
                     prev_rest_span = Some(sub.span);
-                    slice = Some(lower_rest_sub(self, pat, bm, ident, sub));
+                    slice = Some(self.arena.alloc(lower_rest_sub(self, pat, bm, ident, sub)));
                     break;
                 }
                 // It was not a subslice pattern so lower it normally.
-                _ => before.push(self.lower_pat(pat)),
+                _ => before.push(self.lower_pat_mut(pat)),
             }
         }
 
@@ -214,7 +218,7 @@ fn lower_pat_slice(&mut self, pats: &[P<Pat>]) -> hir::PatKind<'hir> {
                 self.ban_extra_rest_pat(rest_span, prev_rest_span.unwrap(), "slice");
             } else {
                 // Lower the pattern normally.
-                after.push(self.lower_pat(pat));
+                after.push(self.lower_pat_mut(pat));
             }
         }
 
@@ -268,17 +272,17 @@ fn lower_binding_mode(&mut self, b: &BindingMode) -> hir::BindingAnnotation {
     }
 
     fn pat_wild_with_node_id_of(&mut self, p: &Pat) -> &'hir hir::Pat<'hir> {
-        self.pat_with_node_id_of(p, hir::PatKind::Wild)
+        self.arena.alloc(self.pat_with_node_id_of(p, hir::PatKind::Wild))
     }
 
     /// Construct a `Pat` with the `HirId` of `p.id` lowered.
-    fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
-        self.arena.alloc(hir::Pat {
+    fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> hir::Pat<'hir> {
+        hir::Pat {
             hir_id: self.lower_node_id(p.id),
             kind,
             span: p.span,
             default_binding_modes: true,
-        })
+        }
     }
 
     /// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
index 73b04d3329cb8e79e9c9a3c5d63f5f178b307e27..1786fa340cc8bb9831039ef8b839f997009c8ce8 100644 (file)
@@ -127,13 +127,15 @@ fn into_iter(self) -> Self::IntoIter {
     }
 }
 
-impl<K, V> Extend<(K, V)> for VecMap<K, V> {
+impl<K: PartialEq, V> Extend<(K, V)> for VecMap<K, V> {
     fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
-        self.0.extend(iter);
+        for (k, v) in iter {
+            self.insert(k, v);
+        }
     }
 
-    fn extend_one(&mut self, item: (K, V)) {
-        self.0.extend_one(item);
+    fn extend_one(&mut self, (k, v): (K, V)) {
+        self.insert(k, v);
     }
 
     fn extend_reserve(&mut self, additional: usize) {
index ede510b6936798717b62109140994d26ac27b969..fdb15f6849bc0d3fddbc7e4331a8b6b0de5b5c94 100644 (file)
@@ -534,7 +534,7 @@ pub fn set(&self, features: &mut Features, span: Span) {
     (active, bindings_after_at, "1.41.0", Some(65490), None),
 
     /// Allows `impl const Trait for T` syntax.
-    (incomplete, const_trait_impl, "1.42.0", Some(67792), None),
+    (active, const_trait_impl, "1.42.0", Some(67792), None),
 
     /// Allows `T: ?const Trait` syntax in bounds.
     (incomplete, const_trait_bound_opt_out, "1.42.0", Some(67794), None),
index a7ce92ea57917079852b5bf7660d1e10c85bcda4..4b2679e164aac6505208cf0427a9d4c49dc55348 100644 (file)
@@ -808,13 +808,13 @@ fn walk_short_(&self, it: &mut impl FnMut(&Pat<'hir>) -> bool) -> bool {
         }
 
         use PatKind::*;
-        match &self.kind {
+        match self.kind {
             Wild | Lit(_) | Range(..) | Binding(.., None) | Path(_) => true,
             Box(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_short_(it),
             Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)),
             TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)),
             Slice(before, slice, after) => {
-                before.iter().chain(slice.iter()).chain(after.iter()).all(|p| p.walk_short_(it))
+                before.iter().chain(slice).chain(after.iter()).all(|p| p.walk_short_(it))
             }
         }
     }
@@ -836,13 +836,13 @@ fn walk_(&self, it: &mut impl FnMut(&Pat<'hir>) -> bool) {
         }
 
         use PatKind::*;
-        match &self.kind {
+        match self.kind {
             Wild | Lit(_) | Range(..) | Binding(.., None) | Path(_) => {}
             Box(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_(it),
             Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)),
             TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)),
             Slice(before, slice, after) => {
-                before.iter().chain(slice.iter()).chain(after.iter()).for_each(|p| p.walk_(it))
+                before.iter().chain(slice).chain(after.iter()).for_each(|p| p.walk_(it))
             }
         }
     }
@@ -940,11 +940,11 @@ pub enum PatKind<'hir> {
     /// A tuple struct/variant pattern `Variant(x, y, .., z)`.
     /// If the `..` pattern fragment is present, then `Option<usize>` denotes its position.
     /// `0 <= position <= subpats.len()`
-    TupleStruct(QPath<'hir>, &'hir [&'hir Pat<'hir>], Option<usize>),
+    TupleStruct(QPath<'hir>, &'hir [Pat<'hir>], Option<usize>),
 
     /// An or-pattern `A | B | C`.
     /// Invariant: `pats.len() >= 2`.
-    Or(&'hir [&'hir Pat<'hir>]),
+    Or(&'hir [Pat<'hir>]),
 
     /// A path pattern for an unit struct/variant or a (maybe-associated) constant.
     Path(QPath<'hir>),
@@ -952,7 +952,7 @@ pub enum PatKind<'hir> {
     /// A tuple pattern (e.g., `(a, b)`).
     /// If the `..` pattern fragment is present, then `Option<usize>` denotes its position.
     /// `0 <= position <= subpats.len()`
-    Tuple(&'hir [&'hir Pat<'hir>], Option<usize>),
+    Tuple(&'hir [Pat<'hir>], Option<usize>),
 
     /// A `box` pattern.
     Box(&'hir Pat<'hir>),
@@ -975,7 +975,7 @@ pub enum PatKind<'hir> {
     /// ```
     /// PatKind::Slice([Binding(a), Binding(b)], Some(Wild), [Binding(c), Binding(d)])
     /// ```
-    Slice(&'hir [&'hir Pat<'hir>], Option<&'hir Pat<'hir>>, &'hir [&'hir Pat<'hir>]),
+    Slice(&'hir [Pat<'hir>], Option<&'hir Pat<'hir>>, &'hir [Pat<'hir>]),
 }
 
 #[derive(Copy, Clone, PartialEq, Encodable, Debug, HashStable_Generic)]
index a053253ec16e00625cd8c3d04c28a8f9772355d6..b1e4e3ba395217fefa8917c70aaa31836f42b998 100644 (file)
@@ -236,9 +236,9 @@ fn test_lints_tracking_hash_different_values() {
         (String::from("d"), Level::Deny),
     ];
 
-    assert_different_hash(&v1, &v2);
-    assert_different_hash(&v1, &v3);
-    assert_different_hash(&v2, &v3);
+    assert_non_crate_hash_different(&v1, &v2);
+    assert_non_crate_hash_different(&v1, &v3);
+    assert_non_crate_hash_different(&v2, &v3);
 }
 
 #[test]
@@ -261,7 +261,21 @@ fn test_lints_tracking_hash_different_construction_order() {
     ];
 
     // The hash should be order-dependent
-    assert_different_hash(&v1, &v2);
+    assert_non_crate_hash_different(&v1, &v2);
+}
+
+#[test]
+fn test_lint_cap_hash_different() {
+    let mut v1 = Options::default();
+    let mut v2 = Options::default();
+    let v3 = Options::default();
+
+    v1.lint_cap = Some(Level::Forbid);
+    v2.lint_cap = Some(Level::Allow);
+
+    assert_non_crate_hash_different(&v1, &v2);
+    assert_non_crate_hash_different(&v1, &v3);
+    assert_non_crate_hash_different(&v2, &v3);
 }
 
 #[test]
@@ -633,6 +647,7 @@ macro_rules! untracked {
     untracked!(dump_mir_graphviz, true);
     untracked!(emit_future_incompat_report, true);
     untracked!(emit_stack_sizes, true);
+    untracked!(future_incompat_test, true);
     untracked!(hir_stats, true);
     untracked!(identify_regions, true);
     untracked!(incremental_ignore_spans, true);
index 2d8f661ef59e92d233000ff1dd2358221fb242c1..1c66f831b5f4b40b705d0b5b8901c6661a6ba4f3 100644 (file)
@@ -6,6 +6,7 @@
 use rustc_ast as ast;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_span::{BytePos, NormalizedPos, SourceFile};
+use std::assert::assert_matches;
 
 use smallvec::SmallVec;
 
index 484e30027e521ce3961368584152b64a474a02c2..848e60fe1342ebcd8c1448d5c08eb5a28a2b00c0 100644 (file)
@@ -8,7 +8,7 @@
 use rustc_index::vec::IndexVec;
 use rustc_session::lint::{
     builtin::{self, FORBIDDEN_LINT_GROUPS},
-    FutureIncompatibilityReason, FutureIncompatibleInfo, Level, Lint, LintId,
+    FutureIncompatibilityReason, Level, Lint, LintId,
 };
 use rustc_session::{DiagnosticMessageId, Session};
 use rustc_span::hygiene::MacroKind;
@@ -223,12 +223,12 @@ fn struct_lint_level_impl(
         let lint_id = LintId::of(lint);
         let future_incompatible = lint.future_incompatible;
 
-        let has_future_breakage = matches!(
-            future_incompatible,
-            Some(FutureIncompatibleInfo {
-                reason: FutureIncompatibilityReason::FutureReleaseErrorReportNow,
-                ..
-            })
+        let has_future_breakage = future_incompatible.map_or(
+            // Default allow lints trigger too often for testing.
+            sess.opts.debugging_opts.future_incompat_test && lint.default_level != Level::Allow,
+            |incompat| {
+                matches!(incompat.reason, FutureIncompatibilityReason::FutureReleaseErrorReportNow)
+            },
         );
 
         let mut err = match (level, span) {
index dbb5064c4f546f9a0a0dea613934bca8f873fd1c..95ea38d32b69508aeb4eeaa27046bdf096c47bbb 100644 (file)
@@ -312,7 +312,8 @@ fn univariant_uninterned(
         let dl = self.data_layout();
         let pack = repr.pack;
         if pack.is_some() && repr.align.is_some() {
-            bug!("struct cannot be packed and aligned");
+            self.tcx.sess.delay_span_bug(DUMMY_SP, "struct cannot be packed and aligned");
+            return Err(LayoutError::Unknown(ty));
         }
 
         let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align };
@@ -808,7 +809,11 @@ fn layout_raw_uncached(&self, ty: Ty<'tcx>) -> Result<&'tcx Layout, LayoutError<
 
                 if def.is_union() {
                     if def.repr.pack.is_some() && def.repr.align.is_some() {
-                        bug!("union cannot be packed and aligned");
+                        self.tcx.sess.delay_span_bug(
+                            tcx.def_span(def.did),
+                            "union cannot be packed and aligned",
+                        );
+                        return Err(LayoutError::Unknown(ty));
                     }
 
                     let mut align =
@@ -2478,9 +2483,10 @@ fn fn_sig_for_fn_abi(&self, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> {
                 // `src/test/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping
                 // track of a polymorphization `ParamEnv` to allow normalizing later.
                 let mut sig = match *ty.kind() {
-                    ty::FnDef(def_id, substs) => tcx
+                    ty::FnDef(def_id, substs) if tcx.sess.opts.debugging_opts.polymorphize => tcx
                         .normalize_erasing_regions(tcx.param_env(def_id), tcx.fn_sig(def_id))
                         .subst(tcx, substs),
+                    ty::FnDef(def_id, substs) => tcx.fn_sig(def_id).subst(tcx, substs),
                     _ => unreachable!(),
                 };
 
index 5f719cc160706d09aa3fa9d1e0cee62a7ac00d46..194c478cc99d75e3c69eea9a8e91a9b3ccbb39bc 100644 (file)
@@ -6,6 +6,7 @@
 //! integer. It is crucial that these operations call `check_align` *before*
 //! short-circuiting the empty case!
 
+use std::assert::assert_matches;
 use std::borrow::Cow;
 use std::collections::VecDeque;
 use std::convert::{TryFrom, TryInto};
index 3ea76fb99d53171ab2131427aa86a038aebe844f..046f41400366d69c1a3581c29058b685e09f7cf7 100644 (file)
@@ -325,7 +325,7 @@ fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> {
 
     fn lower_tuple_subpats(
         &mut self,
-        pats: &'tcx [&'tcx hir::Pat<'tcx>],
+        pats: &'tcx [hir::Pat<'tcx>],
         expected_len: usize,
         gap_pos: Option<usize>,
     ) -> Vec<FieldPat<'tcx>> {
@@ -338,7 +338,7 @@ fn lower_tuple_subpats(
             .collect()
     }
 
-    fn lower_patterns(&mut self, pats: &'tcx [&'tcx hir::Pat<'tcx>]) -> Vec<Pat<'tcx>> {
+    fn lower_patterns(&mut self, pats: &'tcx [hir::Pat<'tcx>]) -> Vec<Pat<'tcx>> {
         pats.iter().map(|p| self.lower_pattern(p)).collect()
     }
 
@@ -350,9 +350,9 @@ fn slice_or_array_pattern(
         &mut self,
         span: Span,
         ty: Ty<'tcx>,
-        prefix: &'tcx [&'tcx hir::Pat<'tcx>],
+        prefix: &'tcx [hir::Pat<'tcx>],
         slice: &'tcx Option<&'tcx hir::Pat<'tcx>>,
-        suffix: &'tcx [&'tcx hir::Pat<'tcx>],
+        suffix: &'tcx [hir::Pat<'tcx>],
     ) -> PatKind<'tcx> {
         let prefix = self.lower_patterns(prefix);
         let slice = self.lower_opt_pattern(slice);
index 03d94f43897ba19fcd472316db990b0b0ec34f7b..7439cd9a0fe3dc50b99b8c4aca5ded24161db04a 100644 (file)
@@ -502,14 +502,14 @@ impl<'a> Resolver<'a> {
 
                 err
             }
-            ResolutionError::SelfInTyParamDefault => {
+            ResolutionError::SelfInGenericParamDefault => {
                 let mut err = struct_span_err!(
                     self.session,
                     span,
                     E0735,
-                    "type parameters cannot use `Self` in their defaults"
+                    "generic parameters cannot use `Self` in their defaults"
                 );
-                err.span_label(span, "`Self` in type parameter default".to_string());
+                err.span_label(span, "`Self` in generic parameter default".to_string());
                 err
             }
             ResolutionError::UnreachableLabel { name, definition_span, suggestion } => {
index 4d124152151942fe00d4e9d9baa384d00085e604..fb2eb749e118f489fc547ae36f07d1923eb26fef 100644 (file)
@@ -249,7 +249,7 @@ enum ResolutionError<'a> {
     /// This error is only emitted when using `min_const_generics`.
     ParamInNonTrivialAnonConst { name: Symbol, is_type: bool },
     /// Error E0735: generic parameters with a default cannot use `Self`
-    SelfInTyParamDefault,
+    SelfInGenericParamDefault,
     /// Error E0767: use of unreachable label
     UnreachableLabel { name: Symbol, definition_span: Span, suggestion: Option<LabelSuggestion> },
 }
@@ -2643,7 +2643,7 @@ fn validate_res_from_ribs(
         if let ForwardGenericParamBanRibKind = all_ribs[rib_index].kind {
             if record_used {
                 let res_error = if rib_ident.name == kw::SelfUpper {
-                    ResolutionError::SelfInTyParamDefault
+                    ResolutionError::SelfInGenericParamDefault
                 } else {
                     ResolutionError::ForwardDeclaredGenericParam
                 };
index 4c40d0c367ecaae4e318df83844b8a86d5347d32..8e2e33f2d51508d51aed04554ad64d8837009754 100644 (file)
@@ -133,9 +133,9 @@ pub struct Options {
         /// can influence whether overflow checks are done or not.
         debug_assertions: bool [TRACKED],
         debuginfo: DebugInfo [TRACKED],
-        lint_opts: Vec<(String, lint::Level)> [TRACKED],
-        lint_cap: Option<lint::Level> [TRACKED],
-        force_warns: Vec<String> [TRACKED],
+        lint_opts: Vec<(String, lint::Level)> [TRACKED_NO_CRATE_HASH],
+        lint_cap: Option<lint::Level> [TRACKED_NO_CRATE_HASH],
+        force_warns: Vec<String> [TRACKED_NO_CRATE_HASH],
         describe_lints: bool [UNTRACKED],
         output_types: OutputTypes [TRACKED],
         search_paths: Vec<SearchPath> [UNTRACKED],
@@ -1084,6 +1084,8 @@ mod parse {
         "set the optimization fuel quota for a crate"),
     function_sections: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "whether each function should go in its own section"),
+    future_incompat_test: bool = (false, parse_bool, [UNTRACKED],
+        "forces all lints to be future incompatible, used for internal testing (default: no)"),
     gcc_ld: Option<LdImpl> = (None, parse_gcc_ld, [TRACKED], "implementation of ld used by cc"),
     graphviz_dark_mode: bool = (false, parse_bool, [UNTRACKED],
         "use dark-themed colors in graphviz output (default: no)"),
index 0e685205069cb7cd7cd722706378973a67e3e3a8..98fde3707f70ed0613f968590428144582bc22bd 100644 (file)
@@ -29,6 +29,8 @@
 use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness};
 use rustc_span::symbol::sym;
 
+use std::collections::BTreeMap;
+
 pub use rustc_middle::traits::Reveal;
 
 pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>;
@@ -296,6 +298,7 @@ struct AssocTypeNormalizer<'a, 'b, 'tcx> {
     cause: ObligationCause<'tcx>,
     obligations: &'a mut Vec<PredicateObligation<'tcx>>,
     depth: usize,
+    universes: Vec<Option<ty::UniverseIndex>>,
 }
 
 impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
@@ -306,12 +309,18 @@ fn new(
         depth: usize,
         obligations: &'a mut Vec<PredicateObligation<'tcx>>,
     ) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
-        AssocTypeNormalizer { selcx, param_env, cause, obligations, depth }
+        AssocTypeNormalizer { selcx, param_env, cause, obligations, depth, universes: vec![] }
     }
 
     fn fold<T: TypeFoldable<'tcx>>(&mut self, value: T) -> T {
         let value = self.selcx.infcx().resolve_vars_if_possible(value);
 
+        assert!(
+            !value.has_escaping_bound_vars(),
+            "Normalizing {:?} without wrapping in a `Binder`",
+            value
+        );
+
         if !value.has_projections() { value } else { value.fold_with(self) }
     }
 }
@@ -321,6 +330,16 @@ fn tcx<'c>(&'c self) -> TyCtxt<'tcx> {
         self.selcx.tcx()
     }
 
+    fn fold_binder<T: TypeFoldable<'tcx>>(
+        &mut self,
+        t: ty::Binder<'tcx, T>,
+    ) -> ty::Binder<'tcx, T> {
+        self.universes.push(None);
+        let t = t.super_fold_with(self);
+        self.universes.pop();
+        t
+    }
+
     fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
         if !ty.has_projections() {
             return ty;
@@ -396,6 +415,52 @@ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
                 normalized_ty
             }
 
+            ty::Projection(data) if !data.trait_ref(self.tcx()).has_escaping_bound_vars() => {
+                // Okay, so you thought the previous branch was hacky. Well, to
+                // extend upon this, when the *trait ref* doesn't have escaping
+                // bound vars, but the associated item *does* (can only occur
+                // with GATs), then we might still be able to project the type.
+                // For this, we temporarily replace the bound vars with
+                // placeholders. Note though, that in the case that we still
+                // can't project for whatever reason (e.g. self type isn't
+                // known enough), we *can't* register an obligation and return
+                // an inference variable (since then that obligation would have
+                // bound vars and that's a can of worms). Instead, we just
+                // give up and fall back to pretending like we never tried!
+
+                let infcx = self.selcx.infcx();
+                let (data, mapped_regions, mapped_types, mapped_consts) =
+                    BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
+                let normalized_ty = opt_normalize_projection_type(
+                    self.selcx,
+                    self.param_env,
+                    data,
+                    self.cause.clone(),
+                    self.depth,
+                    &mut self.obligations,
+                )
+                .ok()
+                .flatten()
+                .unwrap_or_else(|| ty);
+
+                let normalized_ty = PlaceholderReplacer::replace_placeholders(
+                    infcx,
+                    mapped_regions,
+                    mapped_types,
+                    mapped_consts,
+                    &self.universes,
+                    normalized_ty,
+                );
+                debug!(
+                    ?self.depth,
+                    ?ty,
+                    ?normalized_ty,
+                    obligations.len = ?self.obligations.len(),
+                    "AssocTypeNormalizer: normalized type"
+                );
+                normalized_ty
+            }
+
             _ => ty,
         }
     }
@@ -410,6 +475,279 @@ fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tc
     }
 }
 
+pub struct BoundVarReplacer<'me, 'tcx> {
+    infcx: &'me InferCtxt<'me, 'tcx>,
+    // These three maps track the bound variable that were replaced by placeholders. It might be
+    // nice to remove these since we already have the `kind` in the placeholder; we really just need
+    // the `var` (but we *could* bring that into scope if we were to track them as we pass them).
+    mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+    mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+    mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>,
+    // The current depth relative to *this* folding, *not* the entire normalization. In other words,
+    // the depth of binders we've passed here.
+    current_index: ty::DebruijnIndex,
+    // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy:
+    // we don't actually create a universe until we see a bound var we have to replace.
+    universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>,
+}
+
+impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> {
+    /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that
+    /// use a binding level above `universe_indices.len()`, we fail.
+    pub fn replace_bound_vars<T: TypeFoldable<'tcx>>(
+        infcx: &'me InferCtxt<'me, 'tcx>,
+        universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>,
+        value: T,
+    ) -> (
+        T,
+        BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+        BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+        BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>,
+    ) {
+        let mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion> = BTreeMap::new();
+        let mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy> = BTreeMap::new();
+        let mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar> = BTreeMap::new();
+
+        let mut replacer = BoundVarReplacer {
+            infcx,
+            mapped_regions,
+            mapped_types,
+            mapped_consts,
+            current_index: ty::INNERMOST,
+            universe_indices,
+        };
+
+        let value = value.super_fold_with(&mut replacer);
+
+        (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts)
+    }
+
+    fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex {
+        let infcx = self.infcx;
+        let index =
+            self.universe_indices.len() - debruijn.as_usize() + self.current_index.as_usize() - 1;
+        let universe = self.universe_indices[index].unwrap_or_else(|| {
+            for i in self.universe_indices.iter_mut().take(index + 1) {
+                *i = i.or_else(|| Some(infcx.create_next_universe()))
+            }
+            self.universe_indices[index].unwrap()
+        });
+        universe
+    }
+}
+
+impl TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> {
+    fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn fold_binder<T: TypeFoldable<'tcx>>(
+        &mut self,
+        t: ty::Binder<'tcx, T>,
+    ) -> ty::Binder<'tcx, T> {
+        self.current_index.shift_in(1);
+        let t = t.super_fold_with(self);
+        self.current_index.shift_out(1);
+        t
+    }
+
+    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        match *r {
+            ty::ReLateBound(debruijn, _)
+                if debruijn.as_usize() + 1
+                    > self.current_index.as_usize() + self.universe_indices.len() =>
+            {
+                bug!("Bound vars outside of `self.universe_indices`");
+            }
+            ty::ReLateBound(debruijn, br) if debruijn >= self.current_index => {
+                let universe = self.universe_for(debruijn);
+                let p = ty::PlaceholderRegion { universe, name: br.kind };
+                self.mapped_regions.insert(p.clone(), br);
+                self.infcx.tcx.mk_region(ty::RePlaceholder(p))
+            }
+            _ => r,
+        }
+    }
+
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        match *t.kind() {
+            ty::Bound(debruijn, _)
+                if debruijn.as_usize() + 1
+                    > self.current_index.as_usize() + self.universe_indices.len() =>
+            {
+                bug!("Bound vars outside of `self.universe_indices`");
+            }
+            ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => {
+                let universe = self.universe_for(debruijn);
+                let p = ty::PlaceholderType { universe, name: bound_ty.var };
+                self.mapped_types.insert(p.clone(), bound_ty);
+                self.infcx.tcx.mk_ty(ty::Placeholder(p))
+            }
+            _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self),
+            _ => t,
+        }
+    }
+
+    fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
+        match *ct {
+            ty::Const { val: ty::ConstKind::Bound(debruijn, _), ty: _ }
+                if debruijn.as_usize() + 1
+                    > self.current_index.as_usize() + self.universe_indices.len() =>
+            {
+                bug!("Bound vars outside of `self.universe_indices`");
+            }
+            ty::Const { val: ty::ConstKind::Bound(debruijn, bound_const), ty }
+                if debruijn >= self.current_index =>
+            {
+                let universe = self.universe_for(debruijn);
+                let p = ty::PlaceholderConst {
+                    universe,
+                    name: ty::BoundConst { var: bound_const, ty },
+                };
+                self.mapped_consts.insert(p.clone(), bound_const);
+                self.infcx.tcx.mk_const(ty::Const { val: ty::ConstKind::Placeholder(p), ty })
+            }
+            _ if ct.has_vars_bound_at_or_above(self.current_index) => ct.super_fold_with(self),
+            _ => ct,
+        }
+    }
+}
+
+// The inverse of `BoundVarReplacer`: replaces placeholders with the bound vars from which they came.
+pub struct PlaceholderReplacer<'me, 'tcx> {
+    infcx: &'me InferCtxt<'me, 'tcx>,
+    mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+    mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+    mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>,
+    universe_indices: &'me Vec<Option<ty::UniverseIndex>>,
+    current_index: ty::DebruijnIndex,
+}
+
+impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> {
+    pub fn replace_placeholders<T: TypeFoldable<'tcx>>(
+        infcx: &'me InferCtxt<'me, 'tcx>,
+        mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+        mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+        mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>,
+        universe_indices: &'me Vec<Option<ty::UniverseIndex>>,
+        value: T,
+    ) -> T {
+        let mut replacer = PlaceholderReplacer {
+            infcx,
+            mapped_regions,
+            mapped_types,
+            mapped_consts,
+            universe_indices,
+            current_index: ty::INNERMOST,
+        };
+        value.super_fold_with(&mut replacer)
+    }
+}
+
+impl TypeFolder<'tcx> for PlaceholderReplacer<'_, 'tcx> {
+    fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn fold_binder<T: TypeFoldable<'tcx>>(
+        &mut self,
+        t: ty::Binder<'tcx, T>,
+    ) -> ty::Binder<'tcx, T> {
+        if !t.has_placeholders() && !t.has_infer_regions() {
+            return t;
+        }
+        self.current_index.shift_in(1);
+        let t = t.super_fold_with(self);
+        self.current_index.shift_out(1);
+        t
+    }
+
+    fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        let r1 = match r0 {
+            ty::ReVar(_) => self
+                .infcx
+                .inner
+                .borrow_mut()
+                .unwrap_region_constraints()
+                .opportunistic_resolve_region(self.infcx.tcx, r0),
+            _ => r0,
+        };
+
+        let r2 = match *r1 {
+            ty::RePlaceholder(p) => {
+                let replace_var = self.mapped_regions.get(&p);
+                match replace_var {
+                    Some(replace_var) => {
+                        let index = self
+                            .universe_indices
+                            .iter()
+                            .position(|u| matches!(u, Some(pu) if *pu == p.universe))
+                            .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
+                        let db = ty::DebruijnIndex::from_usize(
+                            self.universe_indices.len() - index + self.current_index.as_usize() - 1,
+                        );
+                        self.tcx().mk_region(ty::ReLateBound(db, *replace_var))
+                    }
+                    None => r1,
+                }
+            }
+            _ => r1,
+        };
+
+        debug!(?r0, ?r1, ?r2, "fold_region");
+
+        r2
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        match *ty.kind() {
+            ty::Placeholder(p) => {
+                let replace_var = self.mapped_types.get(&p);
+                match replace_var {
+                    Some(replace_var) => {
+                        let index = self
+                            .universe_indices
+                            .iter()
+                            .position(|u| matches!(u, Some(pu) if *pu == p.universe))
+                            .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
+                        let db = ty::DebruijnIndex::from_usize(
+                            self.universe_indices.len() - index + self.current_index.as_usize() - 1,
+                        );
+                        self.tcx().mk_ty(ty::Bound(db, *replace_var))
+                    }
+                    None => ty,
+                }
+            }
+
+            _ if ty.has_placeholders() || ty.has_infer_regions() => ty.super_fold_with(self),
+            _ => ty,
+        }
+    }
+
+    fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
+        if let ty::Const { val: ty::ConstKind::Placeholder(p), ty } = *ct {
+            let replace_var = self.mapped_consts.get(&p);
+            match replace_var {
+                Some(replace_var) => {
+                    let index = self
+                        .universe_indices
+                        .iter()
+                        .position(|u| matches!(u, Some(pu) if *pu == p.universe))
+                        .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
+                    let db = ty::DebruijnIndex::from_usize(
+                        self.universe_indices.len() - index + self.current_index.as_usize() - 1,
+                    );
+                    self.tcx()
+                        .mk_const(ty::Const { val: ty::ConstKind::Bound(db, *replace_var), ty })
+                }
+                None => ct,
+            }
+        } else {
+            ct.super_fold_with(self)
+        }
+    }
+}
+
 /// The guts of `normalize`: normalize a specific projection like `<T
 /// as Trait>::Item`. The result is always a type (and possibly
 /// additional obligations). If ambiguity arises, which implies that
index 6673e021bf30897326cb1ab8586ca87ca7f3120c..94539eda0f89eff70bad2efe1e5ca8ef1f54f6e0 100644 (file)
@@ -61,6 +61,7 @@ fn normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
             error: false,
             cache: SsoHashMap::new(),
             anon_depth: 0,
+            universes: vec![],
         };
 
         let result = value.fold_with(&mut normalizer);
@@ -91,6 +92,7 @@ struct QueryNormalizer<'cx, 'tcx> {
     cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>,
     error: bool,
     anon_depth: usize,
+    universes: Vec<Option<ty::UniverseIndex>>,
 }
 
 impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
@@ -98,6 +100,16 @@ fn tcx<'c>(&'c self) -> TyCtxt<'tcx> {
         self.infcx.tcx
     }
 
+    fn fold_binder<T: TypeFoldable<'tcx>>(
+        &mut self,
+        t: ty::Binder<'tcx, T>,
+    ) -> ty::Binder<'tcx, T> {
+        self.universes.push(None);
+        let t = t.super_fold_with(self);
+        self.universes.pop();
+        t
+    }
+
     #[instrument(level = "debug", skip(self))]
     fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
         if !ty.has_projections() {
@@ -204,6 +216,80 @@ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
                     }
                 }
             }
+            ty::Projection(data) if !data.trait_ref(self.infcx.tcx).has_escaping_bound_vars() => {
+                // See note in `rustc_trait_selection::traits::project`
+
+                // One other point mentioning: In `traits::project`, if a
+                // projection can't be normalized, we return an inference variable
+                // and register an obligation to later resolve that. Here, the query
+                // will just return ambiguity. In both cases, the effect is the same: we only want
+                // to return `ty` because there are bound vars that we aren't yet handling in a more
+                // complete way.
+
+                // `BoundVarReplacer` can't handle escaping bound vars. Ideally, we want this before even calling
+                // `QueryNormalizer`, but some const-generics tests pass escaping bound vars.
+                // Also, use `ty` so we get that sweet `outer_exclusive_binder` optimization
+                assert!(!ty.has_vars_bound_at_or_above(ty::DebruijnIndex::from_usize(
+                    self.universes.len()
+                )));
+
+                let tcx = self.infcx.tcx;
+                let infcx = self.infcx;
+                let (data, mapped_regions, mapped_types, mapped_consts) =
+                    crate::traits::project::BoundVarReplacer::replace_bound_vars(
+                        infcx,
+                        &mut self.universes,
+                        data,
+                    );
+                let data = data.super_fold_with(self);
+
+                let mut orig_values = OriginalQueryValues::default();
+                // HACK(matthewjasper) `'static` is special-cased in selection,
+                // so we cannot canonicalize it.
+                let c_data = self
+                    .infcx
+                    .canonicalize_hr_query_hack(self.param_env.and(data), &mut orig_values);
+                debug!("QueryNormalizer: c_data = {:#?}", c_data);
+                debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
+                let normalized_ty = match tcx.normalize_projection_ty(c_data) {
+                    Ok(result) => {
+                        // We don't expect ambiguity.
+                        if result.is_ambiguous() {
+                            self.error = true;
+                            return ty;
+                        }
+                        match self.infcx.instantiate_query_response_and_region_obligations(
+                            self.cause,
+                            self.param_env,
+                            &orig_values,
+                            result,
+                        ) {
+                            Ok(InferOk { value: result, obligations }) => {
+                                debug!("QueryNormalizer: result = {:#?}", result);
+                                debug!("QueryNormalizer: obligations = {:#?}", obligations);
+                                self.obligations.extend(obligations);
+                                result.normalized_ty
+                            }
+                            Err(_) => {
+                                self.error = true;
+                                ty
+                            }
+                        }
+                    }
+                    Err(NoSolution) => {
+                        self.error = true;
+                        ty
+                    }
+                };
+                crate::traits::project::PlaceholderReplacer::replace_placeholders(
+                    infcx,
+                    mapped_regions,
+                    mapped_types,
+                    mapped_consts,
+                    &self.universes,
+                    normalized_ty,
+                )
+            }
 
             _ => ty,
         })();
index 33bc25accb319dceffcf2862d321aaf476cbee1f..3ea59906d3dcd326d241ecf902912180ce41247a 100644 (file)
@@ -10,6 +10,7 @@
 use rustc_hir::{is_range_literal, Node};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::adjustment::AllowTwoPhase;
+use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut};
 use rustc_span::symbol::sym;
 use rustc_span::Span;
@@ -201,7 +202,8 @@ fn suggest_compatible_variants(
                     let sole_field = &variant.fields[0];
                     let sole_field_ty = sole_field.ty(self.tcx, substs);
                     if self.can_coerce(expr_ty, sole_field_ty) {
-                        let variant_path = self.tcx.def_path_str(variant.def_id);
+                        let variant_path =
+                            with_no_trimmed_paths(|| self.tcx.def_path_str(variant.def_id));
                         // FIXME #56861: DRYer prelude filtering
                         if let Some(path) = variant_path.strip_prefix("std::prelude::") {
                             if let Some((_, path)) = path.split_once("::") {
index 2879614d0c8085ff9ebcbbfa0769af46da4a2aaf..981a040e66072991b67be655ec52e41b9d2ca113 100644 (file)
@@ -864,7 +864,7 @@ fn check_pat_tuple_struct(
         &self,
         pat: &'tcx Pat<'tcx>,
         qpath: &'tcx hir::QPath<'tcx>,
-        subpats: &'tcx [&'tcx Pat<'tcx>],
+        subpats: &'tcx [Pat<'tcx>],
         ddpos: Option<usize>,
         expected: Ty<'tcx>,
         def_bm: BindingMode,
@@ -982,7 +982,7 @@ fn e0023(
         pat_span: Span,
         res: Res,
         qpath: &hir::QPath<'_>,
-        subpats: &'tcx [&'tcx Pat<'tcx>],
+        subpats: &'tcx [Pat<'tcx>],
         fields: &'tcx [ty::FieldDef],
         expected: Ty<'tcx>,
         had_err: bool,
@@ -1112,7 +1112,7 @@ fn e0023(
     fn check_pat_tuple(
         &self,
         span: Span,
-        elements: &'tcx [&'tcx Pat<'tcx>],
+        elements: &'tcx [Pat<'tcx>],
         ddpos: Option<usize>,
         expected: Ty<'tcx>,
         def_bm: BindingMode,
@@ -1746,9 +1746,9 @@ fn new_ref_ty(&self, span: Span, mutbl: hir::Mutability, ty: Ty<'tcx>) -> Ty<'tc
     fn check_pat_slice(
         &self,
         span: Span,
-        before: &'tcx [&'tcx Pat<'tcx>],
+        before: &'tcx [Pat<'tcx>],
         slice: Option<&'tcx Pat<'tcx>>,
-        after: &'tcx [&'tcx Pat<'tcx>],
+        after: &'tcx [Pat<'tcx>],
         expected: Ty<'tcx>,
         def_bm: BindingMode,
         ti: TopInfo<'tcx>,
index 9a39a32f6d5d312d52dc2ad9f4a746705b748b66..39874f48eb014071aa5d279b5e3cf48bba7dc5e6 100644 (file)
@@ -1528,20 +1528,11 @@ fn adjust_upvar_borrow_kind_for_consume(
         &mut self,
         place_with_id: &PlaceWithHirId<'tcx>,
         diag_expr_id: hir::HirId,
-        mode: euv::ConsumeMode,
     ) {
         debug!(
-            "adjust_upvar_borrow_kind_for_consume(place_with_id={:?}, diag_expr_id={:?}, mode={:?})",
-            place_with_id, diag_expr_id, mode
+            "adjust_upvar_borrow_kind_for_consume(place_with_id={:?}, diag_expr_id={:?})",
+            place_with_id, diag_expr_id
         );
-
-        // Copy type being used as ByValue are equivalent to ImmBorrow and don't require any
-        // escalation.
-        match mode {
-            euv::ConsumeMode::Copy => return,
-            euv::ConsumeMode::Move => {}
-        };
-
         let tcx = self.fcx.tcx;
         let upvar_id = if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base {
             upvar_id
@@ -1716,22 +1707,14 @@ fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause, diag_expr_id:
         }
     }
 
-    fn consume(
-        &mut self,
-        place_with_id: &PlaceWithHirId<'tcx>,
-        diag_expr_id: hir::HirId,
-        mode: euv::ConsumeMode,
-    ) {
-        debug!(
-            "consume(place_with_id={:?}, diag_expr_id={:?}, mode={:?})",
-            place_with_id, diag_expr_id, mode
-        );
+    fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) {
+        debug!("consume(place_with_id={:?}, diag_expr_id={:?})", place_with_id, diag_expr_id);
 
         if !self.capture_information.contains_key(&place_with_id.place) {
             self.init_capture_info_for_place(&place_with_id, diag_expr_id);
         }
 
-        self.adjust_upvar_borrow_kind_for_consume(&place_with_id, diag_expr_id, mode);
+        self.adjust_upvar_borrow_kind_for_consume(&place_with_id, diag_expr_id);
     }
 
     fn borrow(
index 4838d70a83186843150cf2d5b7f25a8512163a08..26d3cc9d89168cc4d44381294804cf3bb6f89b79 100644 (file)
@@ -406,7 +406,6 @@ fn check_associated_item(
             }
             ty::AssocKind::Fn => {
                 let sig = fcx.tcx.fn_sig(item.def_id);
-                let sig = fcx.normalize_associated_types_in(span, sig);
                 let hir_sig = sig_if_method.expect("bad signature for method");
                 check_fn_or_method(
                     fcx,
@@ -611,7 +610,6 @@ fn check_item_fn(
     for_id(tcx, item_id, span).with_fcx(|fcx| {
         let def_id = tcx.hir().local_def_id(item_id);
         let sig = tcx.fn_sig(def_id);
-        let sig = fcx.normalize_associated_types_in(span, sig);
         let mut implied_bounds = vec![];
         check_fn_or_method(fcx, ident.span, sig, decl, def_id.to_def_id(), &mut implied_bounds);
         implied_bounds
@@ -898,8 +896,8 @@ fn check_fn_or_method<'fcx, 'tcx>(
     def_id: DefId,
     implied_bounds: &mut Vec<Ty<'tcx>>,
 ) {
-    let sig = fcx.normalize_associated_types_in(span, sig);
     let sig = fcx.tcx.liberate_late_bound_regions(def_id, sig);
+    let sig = fcx.normalize_associated_types_in(span, sig);
 
     for (&input_ty, ty) in iter::zip(sig.inputs(), hir_decl.inputs) {
         fcx.register_wf_obligation(input_ty.into(), ty.span, ObligationCauseCode::MiscObligation);
@@ -1077,8 +1075,8 @@ fn check_method_receiver<'fcx, 'tcx>(
     let span = fn_sig.decl.inputs[0].span;
 
     let sig = fcx.tcx.fn_sig(method.def_id);
-    let sig = fcx.normalize_associated_types_in(span, sig);
     let sig = fcx.tcx.liberate_late_bound_regions(method.def_id, sig);
+    let sig = fcx.normalize_associated_types_in(span, sig);
 
     debug!("check_method_receiver: sig={:?}", sig);
 
index ee84974cb73c227f811309f80250e01f1fee7746..7b0002914eca80d20d0df9cf6e94bfa4dbfe933f 100644 (file)
@@ -509,11 +509,10 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
     }
 }
 
+#[instrument(skip(tcx), level = "debug")]
 fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
     use rustc_hir::{Expr, ImplItem, Item, TraitItem};
 
-    debug!("find_opaque_ty_constraints({:?})", def_id);
-
     struct ConstraintLocator<'tcx> {
         tcx: TyCtxt<'tcx>,
         def_id: DefId,
@@ -522,13 +521,11 @@ struct ConstraintLocator<'tcx> {
     }
 
     impl ConstraintLocator<'_> {
+        #[instrument(skip(self), level = "debug")]
         fn check(&mut self, def_id: LocalDefId) {
             // Don't try to check items that cannot possibly constrain the type.
             if !self.tcx.has_typeck_results(def_id) {
-                debug!(
-                    "find_opaque_ty_constraints: no constraint for `{:?}` at `{:?}`: no typeck results",
-                    self.def_id, def_id,
-                );
+                debug!("no constraint: no typeck results");
                 return;
             }
             // Calling `mir_borrowck` can lead to cycle errors through
@@ -540,21 +537,19 @@ fn check(&mut self, def_id: LocalDefId) {
                 .get_by(|(key, _)| key.def_id == self.def_id)
                 .is_none()
             {
-                debug!(
-                    "find_opaque_ty_constraints: no constraint for `{:?}` at `{:?}`",
-                    self.def_id, def_id,
-                );
+                debug!("no constraints in typeck results");
                 return;
             }
             // Use borrowck to get the type with unerased regions.
             let concrete_opaque_types = &self.tcx.mir_borrowck(def_id).concrete_opaque_types;
-            if let Some((opaque_type_key, concrete_type)) =
-                concrete_opaque_types.iter().find(|(key, _)| key.def_id == self.def_id)
-            {
-                debug!(
-                    "find_opaque_ty_constraints: found constraint for `{:?}` at `{:?}`: {:?}",
-                    self.def_id, def_id, concrete_type,
-                );
+            debug!(?concrete_opaque_types);
+            for (opaque_type_key, concrete_type) in concrete_opaque_types {
+                if opaque_type_key.def_id != self.def_id {
+                    // Ignore constraints for other opaque types.
+                    continue;
+                }
+
+                debug!(?concrete_type, ?opaque_type_key.substs, "found constraint");
 
                 // FIXME(oli-obk): trace the actual span from inference to improve errors.
                 let span = self.tcx.def_span(def_id);
@@ -603,7 +598,7 @@ fn check(&mut self, def_id: LocalDefId) {
 
                 if let Some((prev_span, prev_ty)) = self.found {
                     if *concrete_type != prev_ty {
-                        debug!("find_opaque_ty_constraints: span={:?}", span);
+                        debug!(?span);
                         // Found different concrete types for the opaque type.
                         let mut err = self.tcx.sess.struct_span_err(
                             span,
@@ -619,11 +614,6 @@ fn check(&mut self, def_id: LocalDefId) {
                 } else {
                     self.found = Some((span, concrete_type));
                 }
-            } else {
-                debug!(
-                    "find_opaque_ty_constraints: no constraint for `{:?}` at `{:?}`",
-                    self.def_id, def_id,
-                );
             }
         }
     }
index f17494127948414f1c363abde40bf5878701c439..806f1a2711c012b9d52d7830d5d40efd755a5b7a 100644 (file)
@@ -2,8 +2,6 @@
 //! normal visitor, which just walks the entire body in one shot, the
 //! `ExprUseVisitor` determines how expressions are being used.
 
-pub use self::ConsumeMode::*;
-
 // Export these here so that Clippy can use them.
 pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection};
 
 /// This trait defines the callbacks you can expect to receive when
 /// employing the ExprUseVisitor.
 pub trait Delegate<'tcx> {
-    // The value found at `place` is either copied or moved, depending
+    // The value found at `place` is moved, depending
     // on `mode`. Where `diag_expr_id` is the id used for diagnostics for `place`.
     //
+    // Use of a `Copy` type in a ByValue context is considered a use
+    // by `ImmBorrow` and `borrow` is called instead. This is because
+    // a shared borrow is the "minimum access" that would be needed
+    // to perform a copy.
+    //
+    //
     // The parameter `diag_expr_id` indicates the HIR id that ought to be used for
     // diagnostics. Around pattern matching such as `let pat = expr`, the diagnostic
     // id will be the id of the expression `expr` but the place itself will have
     // the id of the binding in the pattern `pat`.
-    fn consume(
-        &mut self,
-        place_with_id: &PlaceWithHirId<'tcx>,
-        diag_expr_id: hir::HirId,
-        mode: ConsumeMode,
-    );
+    fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId);
 
     // The value found at `place` is being borrowed with kind `bk`.
     // `diag_expr_id` is the id used for diagnostics (see `consume` for more details).
@@ -60,7 +59,7 @@ fn borrow(
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
-pub enum ConsumeMode {
+enum ConsumeMode {
     Copy, // reference to x where x has a type that copies
     Move, // reference to x where x has a type that moves
 }
@@ -141,10 +140,7 @@ fn tcx(&self) -> TyCtxt<'tcx> {
     }
 
     fn delegate_consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) {
-        debug!("delegate_consume(place_with_id={:?})", place_with_id);
-
-        let mode = copy_or_move(&self.mc, place_with_id);
-        self.delegate.consume(place_with_id, diag_expr_id, mode);
+        delegate_consume(&self.mc, self.delegate, place_with_id, diag_expr_id)
     }
 
     fn consume_exprs(&mut self, exprs: &[hir::Expr<'_>]) {
@@ -256,12 +252,16 @@ pub fn walk_expr(&mut self, expr: &hir::Expr<'_>) {
                             | PatKind::Path(..)
                             | PatKind::Struct(..)
                             | PatKind::Tuple(..) => {
-                                // If the PatKind is a TupleStruct, Struct or Tuple then we want to check
+                                // If the PatKind is a TupleStruct, Path, Struct or Tuple then we want to check
                                 // whether the Variant is a MultiVariant or a SingleVariant. We only want
                                 // to borrow discr if it is a MultiVariant.
                                 // If it is a SingleVariant and creates a binding we will handle that when
                                 // this callback gets called again.
-                                if let ty::Adt(def, _) = place.place.base_ty.kind() {
+
+                                // Get the type of the Place after all projections have been applied
+                                let place_ty = place.place.ty();
+
+                                if let ty::Adt(def, _) = place_ty.kind() {
                                     if def.variants.len() > 1 {
                                         needs_to_be_read = true;
                                     }
@@ -653,9 +653,8 @@ fn walk_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) {
                             delegate.borrow(place, discr_place.hir_id, bk);
                         }
                         ty::BindByValue(..) => {
-                            let mode = copy_or_move(mc, &place);
                             debug!("walk_pat binding consuming pat");
-                            delegate.consume(place, discr_place.hir_id, mode);
+                            delegate_consume(mc, *delegate, place, discr_place.hir_id);
                         }
                     }
                 }
@@ -773,8 +772,7 @@ fn upvar_is_local_variable(
 
                     match capture_info.capture_kind {
                         ty::UpvarCapture::ByValue(_) => {
-                            let mode = copy_or_move(&self.mc, &place_with_id);
-                            self.delegate.consume(&place_with_id, place_with_id.hir_id, mode);
+                            self.delegate_consume(&place_with_id, place_with_id.hir_id);
                         }
                         ty::UpvarCapture::ByRef(upvar_borrow) => {
                             self.delegate.borrow(
@@ -798,8 +796,28 @@ fn copy_or_move<'a, 'tcx>(
         place_with_id.place.ty(),
         mc.tcx().hir().span(place_with_id.hir_id),
     ) {
-        Move
+        ConsumeMode::Move
     } else {
-        Copy
+        ConsumeMode::Copy
+    }
+}
+
+// - If a place is used in a `ByValue` context then move it if it's not a `Copy` type.
+// - If the place that is a `Copy` type consider it a `ImmBorrow`.
+fn delegate_consume<'a, 'tcx>(
+    mc: &mc::MemCategorizationContext<'a, 'tcx>,
+    delegate: &mut (dyn Delegate<'tcx> + 'a),
+    place_with_id: &PlaceWithHirId<'tcx>,
+    diag_expr_id: hir::HirId,
+) {
+    debug!("delegate_consume(place_with_id={:?})", place_with_id);
+
+    let mode = copy_or_move(&mc, place_with_id);
+
+    match mode {
+        ConsumeMode::Move => delegate.consume(place_with_id, diag_expr_id),
+        ConsumeMode::Copy => {
+            delegate.borrow(place_with_id, diag_expr_id, ty::BorrowKind::ImmBorrow)
+        }
     }
 }
index 4b34a7dc894ae327d426b41a81c59502a25b4189..d821e71562212ed86fd7d083331da0b78f4d6909 100644 (file)
@@ -19,7 +19,6 @@
 use core::mem::size_of_val;
 use core::mem::{self, align_of_val_raw};
 use core::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver};
-#[cfg(not(no_global_oom_handling))]
 use core::pin::Pin;
 use core::ptr::{self, NonNull};
 #[cfg(not(no_global_oom_handling))]
@@ -494,6 +493,13 @@ pub fn pin(data: T) -> Pin<Arc<T>> {
         unsafe { Pin::new_unchecked(Arc::new(data)) }
     }
 
+    /// Constructs a new `Pin<Arc<T>>`, return an error if allocation fails.
+    #[unstable(feature = "allocator_api", issue = "32838")]
+    #[inline]
+    pub fn try_pin(data: T) -> Result<Pin<Arc<T>>, AllocError> {
+        unsafe { Ok(Pin::new_unchecked(Arc::try_new(data)?)) }
+    }
+
     /// Constructs a new `Arc<T>`, returning an error if allocation fails.
     ///
     /// # Examples
index 32d344010aafd3dea55d6f85f06144e0df384cc6..78b799cd709763a6bb8ffbd52fd8ce2ad8773143 100644 (file)
@@ -296,7 +296,6 @@ macro_rules! array_impl_default {
     /// # Examples
     ///
     /// ```
-    /// #![feature(array_map)]
     /// let x = [1, 2, 3];
     /// let y = x.map(|v| v + 1);
     /// assert_eq!(y, [2, 3, 4]);
@@ -310,7 +309,7 @@ macro_rules! array_impl_default {
     /// let y = x.map(|v| v.len());
     /// assert_eq!(y, [6, 9, 3, 3]);
     /// ```
-    #[unstable(feature = "array_map", issue = "75243")]
+    #[stable(feature = "array_map", since = "1.55.0")]
     pub fn map<F, U>(self, f: F) -> [U; N]
     where
         F: FnMut(T) -> U,
@@ -377,7 +376,7 @@ pub fn as_mut_slice(&mut self) -> &mut [T] {
     /// array if its elements are not `Copy`.
     ///
     /// ```
-    /// #![feature(array_methods, array_map)]
+    /// #![feature(array_methods)]
     ///
     /// let strings = ["Ferris".to_string(), "♥".to_string(), "Rust".to_string()];
     /// let is_ascii = strings.each_ref().map(|s| s.is_ascii());
index 4a86d6a100abe66539a41037cfe2de4db9dbb9cd..b9387ef49e5fb211ab343869a8a534c3c262f487 100644 (file)
@@ -30,7 +30,7 @@ pub trait Step: Clone + PartialOrd + Sized {
     /// For any `a`, `b`, and `n`:
     ///
     /// * `steps_between(&a, &b) == Some(n)` if and only if `Step::forward_checked(&a, n) == Some(b)`
-    /// * `steps_between(&a, &b) == Some(n)` if and only if `Step::backward_checked(&a, n) == Some(a)`
+    /// * `steps_between(&a, &b) == Some(n)` if and only if `Step::backward_checked(&b, n) == Some(a)`
     /// * `steps_between(&a, &b) == Some(n)` only if `a <= b`
     ///   * Corollary: `steps_between(&a, &b) == Some(0)` if and only if `a == b`
     ///   * Note that `a <= b` does _not_ imply `steps_between(&a, &b) != None`;
index 3557dbad90cc215f01af0e199f63879b0c2a3183..01d33409a423800c730ea7644a6dd1517056d2fc 100644 (file)
 #[macro_use]
 mod macros;
 
+// We don't export this through #[macro_export] for now, to avoid breakage.
+// See https://github.com/rust-lang/rust/issues/82913
+#[cfg(not(test))]
+#[unstable(feature = "assert_matches", issue = "82775")]
+/// Unstable module containing the unstable `assert_matches` macro.
+pub mod assert {
+    #[unstable(feature = "assert_matches", issue = "82775")]
+    pub use crate::macros::{assert_matches, debug_assert_matches};
+}
+
 #[macro_use]
 mod internal_macros;
 
index 7d22bfedb93b946234540931169e3d71c9cd04d6..8ce441e80bfb7eed4edeed29b7578fea7dfa2dee 100644 (file)
@@ -127,6 +127,8 @@ macro_rules! assert_ne {
 /// ```
 /// #![feature(assert_matches)]
 ///
+/// use std::assert::assert_matches;
+///
 /// let a = 1u32.checked_add(2);
 /// let b = 1u32.checked_sub(2);
 /// assert_matches!(a, Some(_));
@@ -135,10 +137,10 @@ macro_rules! assert_ne {
 /// let c = Ok("abc".to_string());
 /// assert_matches!(c, Ok(x) | Err(x) if x.len() < 100);
 /// ```
-#[macro_export]
 #[unstable(feature = "assert_matches", issue = "82775")]
 #[allow_internal_unstable(core_panic)]
-macro_rules! assert_matches {
+#[rustc_macro_transparency = "semitransparent"]
+pub macro assert_matches {
     ($left:expr, $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)?) => ({
         match $left {
             $( $pattern )|+ $( if $guard )? => {}
@@ -150,7 +152,7 @@ macro_rules! assert_matches {
                 );
             }
         }
-    });
+    }),
     ($left:expr, $( $pattern:pat_param )|+ $( if $guard: expr )?, $($arg:tt)+) => ({
         match $left {
             $( $pattern )|+ $( if $guard )? => {}
@@ -162,7 +164,7 @@ macro_rules! assert_matches {
                 );
             }
         }
-    });
+    }),
 }
 
 /// Asserts that a boolean expression is `true` at runtime.
@@ -284,6 +286,8 @@ macro_rules! debug_assert_ne {
 /// ```
 /// #![feature(assert_matches)]
 ///
+/// use std::assert::debug_assert_matches;
+///
 /// let a = 1u32.checked_add(2);
 /// let b = 1u32.checked_sub(2);
 /// debug_assert_matches!(a, Some(_));
@@ -295,8 +299,9 @@ macro_rules! debug_assert_ne {
 #[macro_export]
 #[unstable(feature = "assert_matches", issue = "82775")]
 #[allow_internal_unstable(assert_matches)]
-macro_rules! debug_assert_matches {
-    ($($arg:tt)*) => (if $crate::cfg!(debug_assertions) { $crate::assert_matches!($($arg)*); })
+#[rustc_macro_transparency = "semitransparent"]
+pub macro debug_assert_matches($($arg:tt)*) {
+    if $crate::cfg!(debug_assertions) { $crate::assert::assert_matches!($($arg)*); }
 }
 
 /// Returns whether the given expression matches any of the given patterns.
index a89596b15ef94c31122c845e3f6041449f6018e8..7528927ef33b9a57297f8330e323856cd518a5bb 100644 (file)
@@ -1,5 +1,3 @@
-// ignore-tidy-undocumented-unsafe
-
 use crate::cmp;
 use crate::mem::{self, MaybeUninit};
 use crate::ptr;
@@ -79,8 +77,10 @@ pub unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize)
             // the way until about `left + right == 32`, but the worst case performance breaks even
             // around 16. 24 was chosen as middle ground. If the size of `T` is larger than 4
             // `usize`s, this algorithm also outperforms other algorithms.
+            // SAFETY: callers must ensure `mid - left` is valid for reading and writing.
             let x = unsafe { mid.sub(left) };
             // beginning of first round
+            // SAFETY: see previous comment.
             let mut tmp: T = unsafe { x.read() };
             let mut i = right;
             // `gcd` can be found before hand by calculating `gcd(left + right, right)`,
@@ -92,6 +92,21 @@ pub unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize)
             // the very end. This is possibly due to the fact that swapping or replacing temporaries
             // uses only one memory address in the loop instead of needing to manage two.
             loop {
+                // [long-safety-expl]
+                // SAFETY: callers must ensure `[left, left+mid+right)` are all valid for reading and
+                // writing.
+                //
+                // - `i` start with `right` so `mid-left <= x+i = x+right = mid-left+right < mid+right`
+                // - `i <= left+right-1` is always true
+                //   - if `i < left`, `right` is added so `i < left+right` and on the next
+                //     iteration `left` is removed from `i` so it doesn't go further
+                //   - if `i >= left`, `left` is removed immediately and so it doesn't go further.
+                // - overflows cannot happen for `i` since the function's safety contract ask for
+                //   `mid+right-1 = x+left+right` to be valid for writing
+                // - underflows cannot happen because `i` must be bigger or equal to `left` for
+                //   a substraction of `left` to happen.
+                //
+                // So `x+i` is valid for reading and writing if the caller respected the contract
                 tmp = unsafe { x.add(i).replace(tmp) };
                 // instead of incrementing `i` and then checking if it is outside the bounds, we
                 // check if `i` will go outside the bounds on the next increment. This prevents
@@ -100,6 +115,8 @@ pub unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize)
                     i -= left;
                     if i == 0 {
                         // end of first round
+                        // SAFETY: tmp has been read from a valid source and x is valid for writing
+                        // according to the caller.
                         unsafe { x.write(tmp) };
                         break;
                     }
@@ -113,13 +130,24 @@ pub unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize)
             }
             // finish the chunk with more rounds
             for start in 1..gcd {
+                // SAFETY: `gcd` is at most equal to `right` so all values in `1..gcd` are valid for
+                // reading and writing as per the function's safety contract, see [long-safety-expl]
+                // above
                 tmp = unsafe { x.add(start).read() };
+                // [safety-expl-addition]
+                //
+                // Here `start < gcd` so `start < right` so `i < right+right`: `right` being the
+                // greatest common divisor of `(left+right, right)` means that `left = right` so
+                // `i < left+right` so `x+i = mid-left+i` is always valid for reading and writing
+                // according to the function's safety contract.
                 i = start + right;
                 loop {
+                    // SAFETY: see [long-safety-expl] and [safety-expl-addition]
                     tmp = unsafe { x.add(i).replace(tmp) };
                     if i >= left {
                         i -= left;
                         if i == start {
+                            // SAFETY: see [long-safety-expl] and [safety-expl-addition]
                             unsafe { x.add(start).write(tmp) };
                             break;
                         }
@@ -135,14 +163,30 @@ pub unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize)
             // The `[T; 0]` here is to ensure this is appropriately aligned for T
             let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit();
             let buf = rawarray.as_mut_ptr() as *mut T;
+            // SAFETY: `mid-left <= mid-left+right < mid+right`
             let dim = unsafe { mid.sub(left).add(right) };
             if left <= right {
+                // SAFETY:
+                //
+                // 1) The `else if` condition about the sizes ensures `[mid-left; left]` will fit in
+                //    `buf` without overflow and `buf` was created just above and so cannot be
+                //    overlapped with any value of `[mid-left; left]`
+                // 2) [mid-left, mid+right) are all valid for reading and writing and we don't care
+                //    about overlaps here.
+                // 3) The `if` condition about `left <= right` ensures writing `left` elements to
+                //    `dim = mid-left+right` is valid because:
+                //    - `buf` is valid and `left` elements were written in it in 1)
+                //    - `dim+left = mid-left+right+left = mid+right` and we write `[dim, dim+left)`
                 unsafe {
+                    // 1)
                     ptr::copy_nonoverlapping(mid.sub(left), buf, left);
+                    // 2)
                     ptr::copy(mid, mid.sub(left), right);
+                    // 3)
                     ptr::copy_nonoverlapping(buf, dim, left);
                 }
             } else {
+                // SAFETY: same reasoning as above but with `left` and `right` reversed
                 unsafe {
                     ptr::copy_nonoverlapping(mid, buf, right);
                     ptr::copy(mid.sub(left), dim, left);
@@ -156,6 +200,10 @@ pub unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize)
             // of this algorithm would be, and swapping using that last chunk instead of swapping
             // adjacent chunks like this algorithm is doing, but this way is still faster.
             loop {
+                // SAFETY:
+                // `left >= right` so `[mid-right, mid+right)` is valid for reading and writing
+                // Substracting `right` from `mid` each turn is counterbalanced by the addition and
+                // check after it.
                 unsafe {
                     ptr::swap_nonoverlapping(mid.sub(right), mid, right);
                     mid = mid.sub(right);
@@ -168,6 +216,10 @@ pub unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize)
         } else {
             // Algorithm 3, `left < right`
             loop {
+                // SAFETY: `[mid-left, mid+left)` is valid for reading and writing because
+                // `left < right` so `mid+left < mid+right`.
+                // Adding `left` to `mid` each turn is counterbalanced by the substraction and check
+                // after it.
                 unsafe {
                     ptr::swap_nonoverlapping(mid.sub(left), mid, left);
                     mid = mid.add(left);
index cc4ff1be563140d2b6d038ead4e0ecd07f2b499b..c7756a503c3e9956d7f1ba1089655fd631b4e25c 100644 (file)
@@ -1,7 +1,6 @@
 #![feature(alloc_layout_extra)]
 #![feature(array_chunks)]
 #![feature(array_methods)]
-#![feature(array_map)]
 #![feature(array_windows)]
 #![feature(bool_to_option)]
 #![feature(box_syntax)]
index 8c120f4af288d0d0fb542515d210ffc0673964df..472bca3460f4678d5d404d6b36279b7be5b9250b 100644 (file)
@@ -548,8 +548,8 @@ pub mod task {
 #[stable(feature = "rust1", since = "1.0.0")]
 #[allow(deprecated, deprecated_in_future)]
 pub use core::{
-    assert_eq, assert_matches, assert_ne, debug_assert, debug_assert_eq, debug_assert_matches,
-    debug_assert_ne, matches, r#try, todo, unimplemented, unreachable, write, writeln,
+    assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, matches, r#try, todo,
+    unimplemented, unreachable, write, writeln,
 };
 
 // Re-export built-in macros defined through libcore.
index 7f26f419a4b7ba7d6d5de0daf2b5b52909b2b739..bd30d6ae3f333daa87cff4d7270ad7d1a02a0bd9 100644 (file)
@@ -1,7 +1,7 @@
 //! WASI-specific extensions to primitives in the `std::fs` module.
 
 #![deny(unsafe_op_in_unsafe_fn)]
-#![unstable(feature = "wasi_ext", issue = "none")]
+#![unstable(feature = "wasi_ext", issue = "71213")]
 
 use crate::ffi::OsStr;
 use crate::fs::{self, File, Metadata, OpenOptions};
index b2e79cc1b4a9d59c59d680e2f22aa038c0f1d97f..cf4501b98cbd42eb43a4e0c1ce098fcc2ecb9cd7 100644 (file)
@@ -1,7 +1,7 @@
 //! WASI-specific extensions to general I/O primitives
 
 #![deny(unsafe_op_in_unsafe_fn)]
-#![unstable(feature = "wasi_ext", issue = "none")]
+#![unstable(feature = "wasi_ext", issue = "71213")]
 
 use crate::fs;
 use crate::io;
index 52e5d37c83fbdc5fa5b02147239cde29392c3a26..558536fa613a5abae81809ef20ead310cb1850e8 100644 (file)
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
   <Type Name="str">
-    <DisplayString>{data_ptr,[length]s8}</DisplayString>
-    <StringView>data_ptr,[length]s8</StringView>
+    <DisplayString>{(char*)data_ptr,[length]s8}</DisplayString>
+    <StringView>(char*)data_ptr,[length]s8</StringView>
     <Expand>
       <Item Name="[len]" ExcludeView="simple">length</Item>
       <Synthetic Name="[chars]">
index 9cc60fc7b47e557e57b0f4b4f7efa056b020a2ab..d001f40fccbc7d573c526511f5772f162bc2403e 100644 (file)
@@ -48,6 +48,7 @@
       <Item Name="[len]" ExcludeView="simple">vec.len</Item>
       <Item Name="[capacity]" ExcludeView="simple">vec.buf.cap</Item>
       <Synthetic Name="[chars]">
+        <DisplayString>{(char*)vec.buf.ptr.pointer,[vec.len]s8}</DisplayString>
         <Expand>
           <ArrayItems>
             <Size>vec.len</Size>
       </Synthetic>
     </Expand>
   </Type>
+
   <Type Name="alloc::rc::Rc&lt;*&gt;">
     <DisplayString>{ptr.pointer->value}</DisplayString>
     <Expand>
       <ExpandedItem>ptr.pointer->value</ExpandedItem>
+      <Item Name="[Reference count]">ptr.pointer->strong</Item>
+      <Item Name="[Weak reference count]">ptr.pointer->weak</Item>
     </Expand>
   </Type>
+  <Type Name="alloc::rc::Weak&lt;*&gt;">
+    <DisplayString>{ptr.pointer->value}</DisplayString>
+    <Expand>
+      <ExpandedItem>ptr.pointer->value</ExpandedItem>
+      <Item Name="[Reference count]">ptr.pointer->strong</Item>
+      <Item Name="[Weak reference count]">ptr.pointer->weak</Item>
+    </Expand>
+  </Type>
+
   <Type Name="alloc::sync::Arc&lt;*&gt;">
     <DisplayString>{ptr.pointer->data}</DisplayString>
     <Expand>
       <ExpandedItem>ptr.pointer->data</ExpandedItem>
+      <Item Name="[Reference count]">ptr.pointer->strong</Item>
+      <Item Name="[Weak reference count]">ptr.pointer->weak</Item>
     </Expand>
   </Type>
   <Type Name="alloc::sync::Weak&lt;*&gt;">
     <DisplayString>{ptr.pointer->data}</DisplayString>
     <Expand>
       <ExpandedItem>ptr.pointer->data</ExpandedItem>
+      <Item Name="[Reference count]">ptr.pointer->strong</Item>
+      <Item Name="[Weak reference count]">ptr.pointer->weak</Item>
     </Expand>
   </Type>
   <Type Name="alloc::borrow::Cow&lt;*&gt;">
index c8196d5c713b200547c43540330c1f09da520e48..fa8ee2d70bbab4a775a38ca7e401639b4f74b564 100644 (file)
 <?xml version="1.0" encoding="utf-8"?>
 <AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
-  <Type Name="core::ptr::Unique&lt;*&gt;">
-    <DisplayString>{{ Unique {pointer} }}</DisplayString>
+  <Type Name="core::cell::Cell&lt;*&gt;">
+    <DisplayString>{value.value}</DisplayString>
     <Expand>
-      <Item Name="[ptr]">pointer</Item>
+      <ExpandedItem>value.value</ExpandedItem>
+    </Expand>
+  </Type>
+  <Type Name="core::cell::Ref&lt;*&gt;">
+    <DisplayString>{value}</DisplayString>
+    <Expand>
+      <ExpandedItem>value</ExpandedItem>
+    </Expand>
+  </Type>
+  <Type Name="core::cell::RefMut&lt;*&gt;">
+    <DisplayString>{value}</DisplayString>
+    <Expand>
+      <ExpandedItem>value</ExpandedItem>
+    </Expand>
+  </Type>
+  <Type Name="core::cell::RefCell&lt;*&gt;">
+    <DisplayString>{value.value}</DisplayString>
+    <Expand>
+      <Item Name="[Borrow state]" Condition="borrow.value.value == 0">"Unborrowed",sb</Item>
+      <Item Name="[Borrow state]" Condition="borrow.value.value &gt; 0">"Immutably borrowed",sb</Item>
+      <Item Name="[Borrow state]" Condition="borrow.value.value &lt; 0">"Mutably borrowed",sb</Item>
+      <ExpandedItem>value.value</ExpandedItem>
+    </Expand>
+  </Type>
+  <Type Name="core::cell::UnsafeCell&lt;*&gt;">
+    <DisplayString>{value}</DisplayString>
+    <Expand>
+      <ExpandedItem>value</ExpandedItem>
     </Expand>
   </Type>
 
-  <Type Name="core::ptr::Shared&lt;*&gt;">
-    <DisplayString>{{ Shared {pointer} }}</DisplayString>
+  <Type Name="core::mem::manually_drop::ManuallyDrop&lt;*&gt;">
+    <DisplayString>{value}</DisplayString>
     <Expand>
-      <Item Name="[ptr]">pointer</Item>
+      <ExpandedItem>value</ExpandedItem>
+    </Expand>
+  </Type>
+
+  <Type Name="core::num::nonzero::NonZeroI8">
+    <DisplayString>{__0}</DisplayString>
+  </Type>
+  <Type Name="core::num::nonzero::NonZeroI16">
+    <DisplayString>{__0}</DisplayString>
+  </Type>
+  <Type Name="core::num::nonzero::NonZeroI32">
+    <DisplayString>{__0}</DisplayString>
+  </Type>
+  <Type Name="core::num::nonzero::NonZeroI64">
+    <DisplayString>{__0}</DisplayString>
+  </Type>
+  <Type Name="core::num::nonzero::NonZeroI128">
+    <DisplayString>{__0}</DisplayString>
+  </Type>
+  <Type Name="core::num::nonzero::NonZeroIsize">
+    <DisplayString>{__0}</DisplayString>
+  </Type>
+  <Type Name="core::num::nonzero::NonZeroU8">
+    <DisplayString>{__0}</DisplayString>
+  </Type>
+  <Type Name="core::num::nonzero::NonZeroU16">
+    <DisplayString>{__0}</DisplayString>
+  </Type>
+  <Type Name="core::num::nonzero::NonZeroU32">
+    <DisplayString>{__0}</DisplayString>
+  </Type>
+  <Type Name="core::num::nonzero::NonZeroU64">
+    <DisplayString>{__0}</DisplayString>
+  </Type>
+  <Type Name="core::num::nonzero::NonZeroU128">
+    <DisplayString>{__0}</DisplayString>
+  </Type>
+  <Type Name="core::num::nonzero::NonZeroUsize">
+    <DisplayString>{__0}</DisplayString>
+  </Type>
+
+  <Type Name="core::num::wrapping::Wrapping&lt;*&gt;">
+    <DisplayString>{__0}</DisplayString>
+  </Type>
+
+  <Type Name="core::ops::range::Range&lt;*&gt;">
+    <DisplayString>({start}..{end})</DisplayString>
+  </Type>
+  <Type Name="core::ops::range::RangeFrom&lt;*&gt;">
+    <DisplayString>({start}..)</DisplayString>
+  </Type>
+  <Type Name="core::ops::range::RangeInclusive&lt;*&gt;">
+    <DisplayString>({start}..={end})</DisplayString>
+  </Type>
+  <Type Name="core::ops::range::RangeTo&lt;*&gt;">
+    <DisplayString>(..{end})</DisplayString>
+  </Type>
+  <Type Name="core::ops::range::RangeToInclusive&lt;*&gt;">
+    <DisplayString>(..={end})</DisplayString>
+  </Type>
+
+  <Type Name="core::pin::Pin&lt;*&gt;">
+    <DisplayString>Pin({(void*)pointer}: {pointer})</DisplayString>
+    <Expand>
+      <ExpandedItem>pointer</ExpandedItem>
     </Expand>
   </Type>
 
   <Type Name="core::ptr::non_null::NonNull&lt;*&gt;">
-    <DisplayString>{(void*) pointer}</DisplayString>
+    <DisplayString>NonNull({(void*) pointer}: {pointer})</DisplayString>
+    <Expand>
+      <ExpandedItem>pointer</ExpandedItem>
+    </Expand>
+  </Type>
+
+  <Type Name="core::ptr::unique::Unique&lt;*&gt;">
+    <DisplayString>Unique({(void*)pointer}: {pointer})</DisplayString>
+    <Expand>
+      <ExpandedItem>pointer</ExpandedItem>
+    </Expand>
+  </Type>
+
+  <Type Name="core::sync::atomic::AtomicBool">
+    <DisplayString>{(bool)v.value}</DisplayString>
+  </Type>
+  <Type Name="core::sync::atomic::AtomicI8">
+    <DisplayString>{v.value}</DisplayString>
+  </Type>
+  <Type Name="core::sync::atomic::AtomicI16">
+    <DisplayString>{v.value}</DisplayString>
+  </Type>
+  <Type Name="core::sync::atomic::AtomicI32">
+    <DisplayString>{v.value}</DisplayString>
+  </Type>
+  <Type Name="core::sync::atomic::AtomicI64">
+    <DisplayString>{v.value}</DisplayString>
+  </Type>
+  <Type Name="core::sync::atomic::AtomicIsize">
+    <DisplayString>{v.value}</DisplayString>
+  </Type>
+    <Type Name="core::sync::atomic::AtomicU8">
+    <DisplayString>{v.value}</DisplayString>
+  </Type>
+  <Type Name="core::sync::atomic::AtomicU16">
+    <DisplayString>{v.value}</DisplayString>
+  </Type>
+  <Type Name="core::sync::atomic::AtomicU32">
+    <DisplayString>{v.value}</DisplayString>
+  </Type>
+  <Type Name="core::sync::atomic::AtomicU64">
+    <DisplayString>{v.value}</DisplayString>
+  </Type>
+  <Type Name="core::sync::atomic::AtomicUsize">
+    <DisplayString>{v.value}</DisplayString>
+  </Type>
+
+  <Type Name="core::time::Duration">
+    <DisplayString>{secs,d}s {nanos,d}ns</DisplayString>
     <Expand>
-      <Item Name="[value]">*pointer</Item>
+      <Item Name="seconds">secs,d</Item>
+      <Item Name="nanoseconds">nanos,d</Item>
     </Expand>
   </Type>
-</AutoVisualizer>
\ No newline at end of file
+</AutoVisualizer>
index 3ccd2e9c30ed5ff98df837d6bf5ad7f6cdd55bd3..c7be0167de9fdb8233f84cb35333688c3e0e36de 100644 (file)
   </Type>
 
   <Type Name="std::ffi::c_str::CString">
-    <DisplayString>{inner.data_ptr,s}</DisplayString>
+    <DisplayString>{(char*)inner.data_ptr}</DisplayString>
     <Expand>
       <Synthetic Name="[chars]">
+        <DisplayString>{(char*)inner.data_ptr}</DisplayString>
         <Expand>
           <ArrayItems>
             <Size>inner.length</Size>
       </Synthetic>
     </Expand>
   </Type>
+
+  <Type Name="std::ffi::os_str::OsString">
+    <DisplayString>{(char*)inner.inner.bytes.buf.ptr.pointer,[inner.inner.bytes.len]}</DisplayString>
+    <Expand>
+      <Synthetic Name="[chars]">
+        <DisplayString>{(char*)inner.inner.bytes.buf.ptr.pointer,[inner.inner.bytes.len]}</DisplayString>
+        <Expand>
+          <ArrayItems>
+            <Size>inner.inner.bytes.len</Size>
+            <ValuePointer>(char*)inner.inner.bytes.buf.ptr.pointer</ValuePointer>
+          </ArrayItems>
+        </Expand>
+      </Synthetic>
+    </Expand>
+  </Type>
 </AutoVisualizer>
index e716e09b8b3f0280985c57e4436a9fe231595b71..859746b6a2df77f8cc4e082bd557b4674b0d6203 100644 (file)
@@ -459,7 +459,7 @@ pub fn from_def_id_and_attrs_and_parts(
             .filter_map(|ItemLink { link: s, link_text, did, ref fragment }| {
                 match did {
                     Some(did) => {
-                        if let Some((mut href, ..)) = href(did.clone(), cx) {
+                        if let Ok((mut href, ..)) = href(did.clone(), cx) {
                             if let Some(ref fragment) = *fragment {
                                 href.push('#');
                                 href.push_str(fragment);
index 7ae602c8033f8f65530936397aca0a6553b3d557..dea016a467d646ca005cccc08d99068228ff5cb9 100644 (file)
@@ -260,17 +260,12 @@ pub(super) fn external_path(
         PatKind::Wild | PatKind::Struct(..) => return kw::Underscore,
         PatKind::Binding(_, _, ident, _) => return ident.name,
         PatKind::TupleStruct(ref p, ..) | PatKind::Path(ref p) => qpath_to_string(p),
-        PatKind::Or(ref pats) => pats
-            .iter()
-            .map(|p| name_from_pat(&**p).to_string())
-            .collect::<Vec<String>>()
-            .join(" | "),
+        PatKind::Or(ref pats) => {
+            pats.iter().map(|p| name_from_pat(p).to_string()).collect::<Vec<String>>().join(" | ")
+        }
         PatKind::Tuple(ref elts, _) => format!(
             "({})",
-            elts.iter()
-                .map(|p| name_from_pat(&**p).to_string())
-                .collect::<Vec<String>>()
-                .join(", ")
+            elts.iter().map(|p| name_from_pat(p).to_string()).collect::<Vec<String>>().join(", ")
         ),
         PatKind::Box(ref p) => return name_from_pat(&**p),
         PatKind::Ref(ref p, _) => return name_from_pat(&**p),
@@ -282,9 +277,9 @@ pub(super) fn external_path(
         }
         PatKind::Range(..) => return kw::Underscore,
         PatKind::Slice(ref begin, ref mid, ref end) => {
-            let begin = begin.iter().map(|p| name_from_pat(&**p).to_string());
+            let begin = begin.iter().map(|p| name_from_pat(p).to_string());
             let mid = mid.as_ref().map(|p| format!("..{}", name_from_pat(&**p))).into_iter();
-            let end = end.iter().map(|p| name_from_pat(&**p).to_string());
+            let end = end.iter().map(|p| name_from_pat(p).to_string());
             format!("[{}]", begin.chain(mid).chain(end).collect::<Vec<_>>().join(", "))
         }
     })
index c08fe47696bf1b85115949e7938a5afe1b0cfb7a..08499cef33ef19cb23564159e605f7408587341c 100644 (file)
@@ -472,7 +472,19 @@ fn print<'a, 'tcx: 'a>(
     }
 }
 
-crate fn href(did: DefId, cx: &Context<'_>) -> Option<(String, ItemType, Vec<String>)> {
+// Possible errors when computing href link source for a `DefId`
+crate enum HrefError {
+    /// This item is known to rustdoc, but from a crate that does not have documentation generated.
+    ///
+    /// This can only happen for non-local items.
+    DocumentationNotBuilt,
+    /// This can only happen for non-local items when `--document-private-items` is not passed.
+    Private,
+    // Not in external cache, href link should be in same page
+    NotInExternalCache,
+}
+
+crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<String>), HrefError> {
     let cache = &cx.cache();
     let relative_to = &cx.current;
     fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] {
@@ -480,7 +492,7 @@ fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] {
     }
 
     if !did.is_local() && !cache.access_levels.is_public(did) && !cache.document_private {
-        return None;
+        return Err(HrefError::Private);
     }
 
     let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) {
@@ -489,22 +501,25 @@ fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] {
             href_relative_parts(module_fqp, relative_to)
         }),
         None => {
-            let &(ref fqp, shortty) = cache.external_paths.get(&did)?;
-            let module_fqp = to_module_fqp(shortty, fqp);
-            (
-                fqp,
-                shortty,
-                match cache.extern_locations[&did.krate] {
-                    ExternalLocation::Remote(ref s) => {
-                        let s = s.trim_end_matches('/');
-                        let mut s = vec![&s[..]];
-                        s.extend(module_fqp[..].iter().map(String::as_str));
-                        s
-                    }
-                    ExternalLocation::Local => href_relative_parts(module_fqp, relative_to),
-                    ExternalLocation::Unknown => return None,
-                },
-            )
+            if let Some(&(ref fqp, shortty)) = cache.external_paths.get(&did) {
+                let module_fqp = to_module_fqp(shortty, fqp);
+                (
+                    fqp,
+                    shortty,
+                    match cache.extern_locations[&did.krate] {
+                        ExternalLocation::Remote(ref s) => {
+                            let s = s.trim_end_matches('/');
+                            let mut s = vec![&s[..]];
+                            s.extend(module_fqp[..].iter().map(String::as_str));
+                            s
+                        }
+                        ExternalLocation::Local => href_relative_parts(module_fqp, relative_to),
+                        ExternalLocation::Unknown => return Err(HrefError::DocumentationNotBuilt),
+                    },
+                )
+            } else {
+                return Err(HrefError::NotInExternalCache);
+            }
         }
     };
     let last = &fqp.last().unwrap()[..];
@@ -518,7 +533,7 @@ fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] {
             url_parts.push(&filename);
         }
     }
-    Some((url_parts.join("/"), shortty, fqp.to_vec()))
+    Ok((url_parts.join("/"), shortty, fqp.to_vec()))
 }
 
 /// Both paths should only be modules.
@@ -567,7 +582,7 @@ fn resolved_path<'a, 'cx: 'a>(
         write!(w, "{}{:#}", &last.name, last.args.print(cx))?;
     } else {
         let path = if use_absolute {
-            if let Some((_, _, fqp)) = href(did, cx) {
+            if let Ok((_, _, fqp)) = href(did, cx) {
                 format!(
                     "{}::{}",
                     fqp[..fqp.len() - 1].join("::"),
@@ -675,7 +690,7 @@ fn tybounds<'a, 'tcx: 'a>(
 ) -> impl fmt::Display + 'a {
     let parts = href(did.into(), cx);
     display_fn(move |f| {
-        if let Some((url, short_ty, fqp)) = parts {
+        if let Ok((url, short_ty, fqp)) = parts {
             write!(
                 f,
                 r#"<a class="{}" href="{}" title="{} {}">{}</a>"#,
@@ -907,7 +922,7 @@ fn fmt_type<'cx>(
                 //        look at).
                 box clean::ResolvedPath { did, .. } => {
                     match href(did.into(), cx) {
-                        Some((ref url, _, ref path)) if !f.alternate() => {
+                        Ok((ref url, _, ref path)) if !f.alternate() => {
                             write!(
                                 f,
                                 "<a class=\"type\" href=\"{url}#{shortty}.{name}\" \
index 3b599e4997a4bbb0c7d16c6d9f7cdb1f61590f32..e21469dc9c343e84d17f295b3bc9588356cb8a83 100644 (file)
@@ -621,8 +621,7 @@ fn next(&mut self) -> Option<Self::Item> {
                                 is_paragraph = true;
                             }
                             html::push_html(&mut ret, content.into_iter());
-                            write!(ret, "&nbsp;<a href=\"#fnref{}\" rev=\"footnote\">↩</a>", id)
-                                .unwrap();
+                            write!(ret, "&nbsp;<a href=\"#fnref{}\">↩</a>", id).unwrap();
                             if is_paragraph {
                                 ret.push_str("</p>");
                             }
index 56d50cb407926acb97b48ef15314bf2a3a84d3d0..7de023cabeff5f10e83154531228c1e288ffe5c6 100644 (file)
@@ -62,7 +62,7 @@
 use crate::html::escape::Escape;
 use crate::html::format::{
     href, print_abi_with_space, print_constness_with_space, print_default_space,
-    print_generic_bounds, print_where_clause, Buffer, PrintWithSpace,
+    print_generic_bounds, print_where_clause, Buffer, HrefError, PrintWithSpace,
 };
 use crate::html::markdown::{Markdown, MarkdownHtml, MarkdownSummaryLine};
 
@@ -856,8 +856,8 @@ fn method(
     ) {
         let name = meth.name.as_ref().unwrap();
         let href = match link {
-            AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id),
-            AssocItemLink::Anchor(None) => format!("#{}.{}", meth.type_(), name),
+            AssocItemLink::Anchor(Some(ref id)) => Some(format!("#{}", id)),
+            AssocItemLink::Anchor(None) => Some(format!("#{}.{}", meth.type_(), name)),
             AssocItemLink::GotoSource(did, provided_methods) => {
                 // We're creating a link from an impl-item to the corresponding
                 // trait-item and need to map the anchored type accordingly.
@@ -867,9 +867,11 @@ fn method(
                     ItemType::TyMethod
                 };
 
-                href(did.expect_def_id(), cx)
-                    .map(|p| format!("{}#{}.{}", p.0, ty, name))
-                    .unwrap_or_else(|| format!("#{}.{}", ty, name))
+                match (href(did.expect_def_id(), cx), ty) {
+                    (Ok(p), ty) => Some(format!("{}#{}.{}", p.0, ty, name)),
+                    (Err(HrefError::DocumentationNotBuilt), ItemType::TyMethod) => None,
+                    (Err(_), ty) => Some(format!("#{}.{}", ty, name)),
+                }
             }
         };
         let vis = meth.visibility.print_with_space(meth.def_id, cx).to_string();
@@ -904,7 +906,7 @@ fn method(
         w.reserve(header_len + "<a href=\"\" class=\"fnname\">{".len() + "</a>".len());
         write!(
             w,
-            "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn <a href=\"{href}\" class=\"fnname\">{name}</a>\
+            "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn <a {href} class=\"fnname\">{name}</a>\
              {generics}{decl}{notable_traits}{where_clause}",
             indent = indent_str,
             vis = vis,
@@ -913,7 +915,8 @@ fn method(
             unsafety = unsafety,
             defaultness = defaultness,
             abi = abi,
-            href = href,
+            // links without a href are valid - https://www.w3schools.com/tags/att_a_href.asp
+            href = href.map(|href| format!("href=\"{}\"", href)).unwrap_or_else(|| "".to_string()),
             name = name,
             generics = g.print(cx),
             decl = d.full_print(header_len, indent, header.asyncness, cx),
index 35c767a3c77ec45edf8a0cc0cece90751026f496..b86f012c0e168669479e4b8a6f461343ed09791c 100644 (file)
@@ -247,6 +247,9 @@ code, pre, a.test-arrow {
 pre {
        padding: 14px;
 }
+.type-decl pre {
+       overflow-x: auto;
+}
 
 .source .content pre {
        padding: 20px;
diff --git a/src/test/debuginfo/duration-type.rs b/src/test/debuginfo/duration-type.rs
new file mode 100644 (file)
index 0000000..bc0266d
--- /dev/null
@@ -0,0 +1,22 @@
+// only-cdb
+// compile-flags:-g
+
+// === CDB TESTS ==================================================================================
+
+// cdb-command: g
+
+// cdb-command: dx duration
+// cdb-check:duration         : 5s 12ns [Type: core::time::Duration]
+// cdb-check:    [<Raw View>]     [Type: core::time::Duration]
+// cdb-check:    seconds          : 5 [Type: unsigned __int64]
+// cdb-check:    nanoseconds      : 12 [Type: unsigned int]
+
+use std::time::Duration;
+
+fn main() {
+    let duration = Duration::new(5, 12);
+
+    zzz(); // #break
+}
+
+fn zzz() { }
diff --git a/src/test/debuginfo/marker-types.rs b/src/test/debuginfo/marker-types.rs
new file mode 100644 (file)
index 0000000..8373d78
--- /dev/null
@@ -0,0 +1,49 @@
+// only-cdb
+// compile-flags:-g
+
+// === CDB TESTS ==================================================================================
+
+// cdb-command: g
+
+// cdb-command: dx nonnull
+// cdb-check:nonnull          : NonNull(0x[...]: 0xc) [Type: core::ptr::non_null::NonNull<u32>]
+// cdb-check:    [<Raw View>]     [Type: core::ptr::non_null::NonNull<u32>]
+// cdb-check:    0xc [Type: unsigned int]
+
+// cdb-command: dx manuallydrop
+// cdb-check:manuallydrop     : 12345 [Type: core::mem::manually_drop::ManuallyDrop<i32>]
+// cdb-check:    [<Raw View>]     [Type: core::mem::manually_drop::ManuallyDrop<i32>]
+
+// cdb-command: dx pin
+// cdb-check:pin              : Pin(0x[...]: "this") [Type: core::pin::Pin<ref_mut$<alloc::string::String> >]
+// cdb-check:    [<Raw View>]     [Type: core::pin::Pin<ref_mut$<alloc::string::String> >]
+// cdb-check:    [len]            : 0x4 [Type: unsigned [...]]
+// cdb-check:    [capacity]       : 0x4 [Type: unsigned [...]]
+// cdb-check:    [chars]          : "this"
+
+// cdb-command: dx unique
+// cdb-check:unique           : Unique(0x[...]: (0x2a, 4321)) [Type: core::ptr::unique::Unique<tuple$<u64,i32> >]
+// cdb-check:    [<Raw View>]     [Type: core::ptr::unique::Unique<tuple$<u64,i32> >]
+// cdb-check:    [0]              : 0x2a [Type: unsigned __int64]
+// cdb-check:    [1]              : 4321 [Type: int]
+
+#![feature(ptr_internals)]
+
+use std::mem::ManuallyDrop;
+use std::pin::Pin;
+use std::ptr::{NonNull, Unique};
+
+fn main() {
+    let nonnull: NonNull<_> = (&12u32).into();
+
+    let manuallydrop = ManuallyDrop::new(12345i32);
+
+    let mut s = "this".to_string();
+    let pin = Pin::new(&mut s);
+
+    let unique: Unique<_> = (&mut (42u64, 4321i32)).into();
+
+    zzz(); // #break
+}
+
+fn zzz() { }
index 428a7e8d9c09b49f8b4f357185419a07dd64ec2e..688483e43e4dbdbf6e0e90895bce185fc90bcc66 100644 (file)
@@ -9,26 +9,64 @@
 // cdb-command: g
 
 // cdb-command:dx static_c,d
-// cdb-check:static_c,d       [Type: core::cell::Cell<i32>]
-// cdb-check:    [...] value            [Type: core::cell::UnsafeCell<i32>]
+// cdb-check:static_c,d       : 10 [Type: core::cell::Cell<i32>]
+// cdb-check:    [<Raw View>]     [Type: core::cell::Cell<i32>]
 
 // cdb-command: dx static_c.value,d
-// cdb-check:static_c.value,d [Type: core::cell::UnsafeCell<i32>]
-// cdb-check:    [...] value            : 10 [Type: int]
+// cdb-check:static_c.value,d : 10 [Type: core::cell::UnsafeCell<i32>]
+// cdb-check:    [<Raw View>]     [Type: core::cell::UnsafeCell<i32>]
 
 // cdb-command:  dx dynamic_c,d
-// cdb-check:dynamic_c,d      [Type: core::cell::RefCell<i32>]
-// cdb-check:    [...] borrow           [Type: core::cell::Cell<isize>]
-// cdb-check:    [...] value            [Type: core::cell::UnsafeCell<i32>]
+// cdb-check:dynamic_c,d      : 15 [Type: core::cell::RefCell<i32>]
+// cdb-check:    [<Raw View>]     [Type: core::cell::RefCell<i32>]
+// cdb-check:    [Borrow state]   : Unborrowed
 
 // cdb-command: dx dynamic_c.value,d
-// cdb-check:dynamic_c.value,d [Type: core::cell::UnsafeCell<i32>]
-// cdb-check:    [...] value            : 15 [Type: int]
+// cdb-check:dynamic_c.value,d : 15 [Type: core::cell::UnsafeCell<i32>]
+// cdb-check:    [<Raw View>]     [Type: core::cell::UnsafeCell<i32>]
 
 // cdb-command: dx b,d
-// cdb-check:b,d              [Type: core::cell::RefMut<i32>]
-// cdb-check:    [...] value            : [...] : 42 [Type: int *]
-// cdb-check:    [...] borrow           [Type: core::cell::BorrowRefMut]
+// cdb-check:b,d              : 42 [Type: core::cell::RefMut<i32>]
+// cdb-check:    [<Raw View>]     [Type: core::cell::RefMut<i32>]
+// cdb-check:    42 [Type: int]
+
+// cdb-command: g
+
+// cdb-command: dx dynamic_c,d
+// cdb-check:dynamic_c,d      : 15 [Type: core::cell::RefCell<i32>]
+// cdb-check:    [<Raw View>]     [Type: core::cell::RefCell<i32>]
+// cdb-check:    [Borrow state]   : Immutably borrowed
+
+// cdb-command: dx r_borrow,d
+// cdb-check:r_borrow,d       : 15 [Type: core::cell::Ref<i32>]
+// cdb-check:    [<Raw View>]     [Type: core::cell::Ref<i32>]
+// cdb-check:    15 [Type: int]
+
+// cdb-command: g
+
+// cdb-command: dx dynamic_c,d
+// cdb-check:dynamic_c,d      : 15 [Type: core::cell::RefCell<i32>]
+// cdb-check:    [<Raw View>]     [Type: core::cell::RefCell<i32>]
+// cdb-check:    [Borrow state]   : Unborrowed
+
+// cdb-command: g
+
+// cdb-command: dx dynamic_c,d
+// cdb-check:dynamic_c,d      : 15 [Type: core::cell::RefCell<i32>]
+// cdb-check:    [<Raw View>]     [Type: core::cell::RefCell<i32>]
+// cdb-check:    [Borrow state]   : Mutably borrowed
+
+// cdb-command: dx r_borrow_mut,d
+// cdb-check:r_borrow_mut,d   : 15 [Type: core::cell::RefMut<i32>]
+// cdb-check:    [<Raw View>]     [Type: core::cell::RefMut<i32>]
+// cdb-check:    15 [Type: int]
+
+// cdb-command: g
+
+// cdb-command: dx dynamic_c,d
+// cdb-check:dynamic_c,d      : 15 [Type: core::cell::RefCell<i32>]
+// cdb-check:    [<Raw View>]     [Type: core::cell::RefCell<i32>]
+// cdb-check:    [Borrow state]   : Unborrowed
 
 #![allow(unused_variables)]
 
@@ -46,6 +84,21 @@ fn main() {
     *b = 42;
 
     zzz(); // #break
+
+    // Check that `RefCell`'s borrow state visualizes correctly
+    {
+        let r_borrow = dynamic_c.borrow();
+        zzz(); // #break
+    }
+
+    zzz(); // #break
+
+    {
+        let r_borrow_mut = dynamic_c.borrow_mut();
+        zzz(); // #break
+    }
+
+    zzz(); // #break
 }
 
 fn zzz() {()}
diff --git a/src/test/debuginfo/numeric-types.rs b/src/test/debuginfo/numeric-types.rs
new file mode 100644 (file)
index 0000000..2eae923
--- /dev/null
@@ -0,0 +1,206 @@
+// only-cdb
+// compile-flags:-g
+
+// Tests the visualizations for `NonZero{I,U}{8,16,32,64,128,size}`, `Wrapping<T>` and
+// `Atomic{Bool,I8,I16,I32,I64,Isize,U8,U16,U32,U64,Usize}` located in `libcore.natvis`.
+
+// === CDB TESTS ==================================================================================
+// cdb-command: g
+
+// cdb-command: dx nz_i8
+// cdb-check:nz_i8            : 11 [Type: core::num::nonzero::NonZeroI8]
+// cdb-check:    [<Raw View>]     [Type: core::num::nonzero::NonZeroI8]
+
+// cdb-command: dx nz_i16
+// cdb-check:nz_i16           : 22 [Type: core::num::nonzero::NonZeroI16]
+// cdb-check:    [<Raw View>]     [Type: core::num::nonzero::NonZeroI16]
+
+// cdb-command: dx nz_i32
+// cdb-check:nz_i32           : 33 [Type: core::num::nonzero::NonZeroI32]
+// cdb-check:    [<Raw View>]     [Type: core::num::nonzero::NonZeroI32]
+
+// cdb-command: dx nz_i64
+// cdb-check:nz_i64           : 44 [Type: core::num::nonzero::NonZeroI64]
+// cdb-check:    [<Raw View>]     [Type: core::num::nonzero::NonZeroI64]
+
+// 128-bit integers don't seem to work in CDB
+// cdb-command: dx nz_i128
+// cdb-check:    [<Raw View>]     [Type: core::num::nonzero::NonZeroI128]
+
+// cdb-command: dx nz_isize
+// cdb-check:nz_isize         : 66 [Type: core::num::nonzero::NonZeroIsize]
+// cdb-check:    [<Raw View>]     [Type: core::num::nonzero::NonZeroIsize]
+
+// cdb-command: dx nz_u8
+// cdb-check:nz_u8            : 0x4d [Type: core::num::nonzero::NonZeroU8]
+// cdb-check:    [<Raw View>]     [Type: core::num::nonzero::NonZeroU8]
+
+// cdb-command: dx nz_u16
+// cdb-check:nz_u16           : 0x58 [Type: core::num::nonzero::NonZeroU16]
+// cdb-check:    [<Raw View>]     [Type: core::num::nonzero::NonZeroU16]
+
+// cdb-command: dx nz_u32
+// cdb-check:nz_u32           : 0x63 [Type: core::num::nonzero::NonZeroU32]
+// cdb-check:    [<Raw View>]     [Type: core::num::nonzero::NonZeroU32]
+
+// cdb-command: dx nz_u64
+// cdb-check:nz_u64           : 0x64 [Type: core::num::nonzero::NonZeroU64]
+// cdb-check:    [<Raw View>]     [Type: core::num::nonzero::NonZeroU64]
+
+// 128-bit integers don't seem to work in CDB
+// cdb-command: dx nz_u128
+// cdb-check:    [<Raw View>]     [Type: core::num::nonzero::NonZeroU128]
+
+// cdb-command: dx nz_usize
+// cdb-check:nz_usize         : 0x7a [Type: core::num::nonzero::NonZeroUsize]
+// cdb-check:    [<Raw View>]     [Type: core::num::nonzero::NonZeroUsize]
+
+// cdb-command: dx w_i8
+// cdb-check:w_i8             : 10 [Type: core::num::wrapping::Wrapping<i8>]
+// cdb-check:    [<Raw View>]     [Type: core::num::wrapping::Wrapping<i8>]
+
+// cdb-command: dx w_i16
+// cdb-check:w_i16            : 20 [Type: core::num::wrapping::Wrapping<i16>]
+// cdb-check:    [<Raw View>]     [Type: core::num::wrapping::Wrapping<i16>]
+
+// cdb-command: dx w_i32
+// cdb-check:w_i32            : 30 [Type: core::num::wrapping::Wrapping<i32>]
+// cdb-check:    [<Raw View>]     [Type: core::num::wrapping::Wrapping<i32>]
+
+// cdb-command: dx w_i64
+// cdb-check:w_i64            : 40 [Type: core::num::wrapping::Wrapping<i64>]
+// cdb-check:    [<Raw View>]     [Type: core::num::wrapping::Wrapping<i64>]
+
+// 128-bit integers don't seem to work in CDB
+// cdb-command: dx w_i128
+// cdb-check:w_i128           [Type: core::num::wrapping::Wrapping<i128>]
+// cdb-check:    [<Raw View>]     [Type: core::num::wrapping::Wrapping<i128>]
+
+// cdb-command: dx w_isize
+// cdb-check:w_isize          : 60 [Type: core::num::wrapping::Wrapping<isize>]
+// cdb-check:    [<Raw View>]     [Type: core::num::wrapping::Wrapping<isize>]
+
+// cdb-command: dx w_u8
+// cdb-check:w_u8             : 0x46 [Type: core::num::wrapping::Wrapping<u8>]
+// cdb-check:    [<Raw View>]     [Type: core::num::wrapping::Wrapping<u8>]
+
+// cdb-command: dx w_u16
+// cdb-check:w_u16            : 0x50 [Type: core::num::wrapping::Wrapping<u16>]
+// cdb-check:    [<Raw View>]     [Type: core::num::wrapping::Wrapping<u16>]
+
+// cdb-command: dx w_u32
+// cdb-check:w_u32            : 0x5a [Type: core::num::wrapping::Wrapping<u32>]
+// cdb-check:    [<Raw View>]     [Type: core::num::wrapping::Wrapping<u32>]
+
+// cdb-command: dx w_u64
+// cdb-check:w_u64            : 0x64 [Type: core::num::wrapping::Wrapping<u64>]
+// cdb-check:    [<Raw View>]     [Type: core::num::wrapping::Wrapping<u64>]
+
+// 128-bit integers don't seem to work in CDB
+// cdb-command: dx w_u128
+// cdb-check:w_u128           [Type: core::num::wrapping::Wrapping<u128>]
+// cdb-check:    [<Raw View>]     [Type: core::num::wrapping::Wrapping<u128>]
+
+// cdb-command: dx w_usize
+// cdb-check:w_usize          : 0x78 [Type: core::num::wrapping::Wrapping<usize>]
+// cdb-check:    [<Raw View>]     [Type: core::num::wrapping::Wrapping<usize>]
+
+// cdb-command: dx a_bool_t
+// cdb-check:a_bool_t         : true [Type: core::sync::atomic::AtomicBool]
+// cdb-check:    [<Raw View>]     [Type: core::sync::atomic::AtomicBool]
+
+// cdb-command: dx a_bool_f
+// cdb-check:a_bool_f         : false [Type: core::sync::atomic::AtomicBool]
+// cdb-check:    [<Raw View>]     [Type: core::sync::atomic::AtomicBool]
+
+// cdb-command: dx a_i8
+// cdb-check:a_i8             : 2 [Type: core::sync::atomic::AtomicI8]
+// cdb-check:    [<Raw View>]     [Type: core::sync::atomic::AtomicI8]
+
+// cdb-command: dx a_i16
+// cdb-check:a_i16            : 4 [Type: core::sync::atomic::AtomicI16]
+// cdb-check:    [<Raw View>]     [Type: core::sync::atomic::AtomicI16]
+
+// cdb-command: dx a_i32
+// cdb-check:a_i32            : 8 [Type: core::sync::atomic::AtomicI32]
+// cdb-check:    [<Raw View>]     [Type: core::sync::atomic::AtomicI32]
+
+// cdb-command: dx a_i64
+// cdb-check:a_i64            : 16 [Type: core::sync::atomic::AtomicI64]
+// cdb-check:    [<Raw View>]     [Type: core::sync::atomic::AtomicI64]
+
+// cdb-command: dx a_isize
+// cdb-check:a_isize          : 32 [Type: core::sync::atomic::AtomicIsize]
+// cdb-check:    [<Raw View>]     [Type: core::sync::atomic::AtomicIsize]
+
+// cdb-command: dx a_u8
+// cdb-check:a_u8             : 0x40 [Type: core::sync::atomic::AtomicU8]
+// cdb-check:    [<Raw View>]     [Type: core::sync::atomic::AtomicU8]
+
+// cdb-command: dx a_u16
+// cdb-check:a_u16            : 0x80 [Type: core::sync::atomic::AtomicU16]
+// cdb-check:    [<Raw View>]     [Type: core::sync::atomic::AtomicU16]
+
+// cdb-command: dx a_u32
+// cdb-check:a_u32            : 0x100 [Type: core::sync::atomic::AtomicU32]
+// cdb-check:    [<Raw View>]     [Type: core::sync::atomic::AtomicU32]
+
+// cdb-command: dx a_u64
+// cdb-check:a_u64            : 0x200 [Type: core::sync::atomic::AtomicU64]
+// cdb-check:    [<Raw View>]     [Type: core::sync::atomic::AtomicU64]
+
+// cdb-command: dx a_usize
+// cdb-check:a_usize          : 0x400 [Type: core::sync::atomic::AtomicUsize]
+// cdb-check:    [<Raw View>]     [Type: core::sync::atomic::AtomicUsize]
+
+use std::num::*;
+use std::sync::atomic::*;
+
+fn main() {
+    let nz_i8 = NonZeroI8::new(11).unwrap();
+    let nz_i16 = NonZeroI16::new(22).unwrap();
+    let nz_i32 = NonZeroI32::new(33).unwrap();
+    let nz_i64 = NonZeroI64::new(44).unwrap();
+    let nz_i128 = NonZeroI128::new(55).unwrap();
+    let nz_isize = NonZeroIsize::new(66).unwrap();
+
+    let nz_u8 = NonZeroU8::new(77).unwrap();
+    let nz_u16 = NonZeroU16::new(88).unwrap();
+    let nz_u32 = NonZeroU32::new(99).unwrap();
+    let nz_u64 = NonZeroU64::new(100).unwrap();
+    let nz_u128 = NonZeroU128::new(111).unwrap();
+    let nz_usize = NonZeroUsize::new(122).unwrap();
+
+    let w_i8 = Wrapping(10i8);
+    let w_i16 = Wrapping(20i16);
+    let w_i32 = Wrapping(30i32);
+    let w_i64 = Wrapping(40i64);
+    let w_i128 = Wrapping(50i128);
+    let w_isize = Wrapping(60isize);
+
+    let w_u8 = Wrapping(70u8);
+    let w_u16 = Wrapping(80u16);
+    let w_u32 = Wrapping(90u32);
+    let w_u64 = Wrapping(100u64);
+    let w_u128 = Wrapping(110u128);
+    let w_usize = Wrapping(120usize);
+
+    let a_bool_t = AtomicBool::new(true);
+    let a_bool_f = AtomicBool::new(false);
+
+    let a_i8 = AtomicI8::new(2);
+    let a_i16 = AtomicI16::new(4);
+    let a_i32 = AtomicI32::new(8);
+    let a_i64 = AtomicI64::new(16);
+    let a_isize = AtomicIsize::new(32);
+
+    let a_u8 = AtomicU8::new(64);
+    let a_u16 = AtomicU16::new(128);
+    let a_u32 = AtomicU32::new(256);
+    let a_u64 = AtomicU64::new(512);
+    let a_usize = AtomicUsize::new(1024);
+
+    zzz(); // #break
+}
+
+fn zzz() { }
index 7ed76beb8c6d91b0717de2c37f8ba747a7e89ee2..d5a6e148b7a66e8b30348bdf687f6935869a18c2 100644 (file)
 // cdb-check:    [11]             : 33 '!' [Type: char]
 
 // cdb-command: dx os_string
-// cdb-check:os_string        [Type: [...]::OsString]
-// NOTE: OsString doesn't have a .natvis entry yet.
+// cdb-check:os_string        : "IAMA OS string ðŸ˜ƒ" [Type: std::ffi::os_str::OsString]
+// cdb-check:    [<Raw View>]     [Type: std::ffi::os_str::OsString]
+// cdb-check:    [chars]          : "IAMA OS string ðŸ˜ƒ"
 
 // cdb-command: dx some
 // cdb-check:some             : Some [Type: enum$<core::option::Option<i16> >]
 // NOTE: cdb fails to interpret debug info of Option enums on i686.
 // cdb-check:some_string      [Type: enum$<core::option::Option<alloc::string::String>, 1, [...], Some>]
 
+// cdb-command: dx linkedlist
+// cdb-check:linkedlist       : { len=0x2 } [Type: alloc::collections::linked_list::LinkedList<i32>]
+// cdb-check:    [<Raw View>]     [Type: alloc::collections::linked_list::LinkedList<i32>]
+// cdb-check:    [0x0]            : 128 [Type: int]
+// cdb-check:    [0x1]            : 42 [Type: int]
+
+// cdb-command: dx vecdeque
+// cdb-check:vecdeque         : { len=0x2 } [Type: alloc::collections::vec_deque::VecDeque<i32>]
+// cdb-check:    [<Raw View>]     [Type: alloc::collections::vec_deque::VecDeque<i32>]
+// cdb-check:    [len]            : 0x2
+// cdb-check:    [capacity]       : 0x8 [Type: unsigned [...]]
+// cdb-check:    [0x0]            : 90 [Type: int]
+// cdb-check:    [0x1]            : 20 [Type: int]
+
 #![allow(unused_variables)]
+use std::collections::{LinkedList, VecDeque};
 use std::ffi::OsString;
 
-
 fn main() {
 
     // &[]
@@ -156,6 +171,16 @@ fn main() {
 
     let some_string = Some("IAMA optional string!".to_owned());
 
+    // LinkedList
+    let mut linkedlist = LinkedList::new();
+    linkedlist.push_back(42);
+    linkedlist.push_front(128);
+
+    // VecDeque
+    let mut vecdeque = VecDeque::new();
+    vecdeque.push_back(20);
+    vecdeque.push_front(90);
+
     zzz(); // #break
 }
 
index c0288b6ba80e01ad636f4252cc80072186e220c0..7362a50a0301510e79e706103f11a7d76a130b68 100644 (file)
@@ -9,26 +9,27 @@
 // cdb-command: g
 
 // cdb-command: dx r1,d
-// cdb-check:r1,d             [Type: core::ops::range::Range<i32>]
-// cdb-check:    [...] start            : 3 [Type: int]
-// cdb-check:    [...] end              : 5 [Type: int]
+// cdb-check:r1,d             : (3..5) [Type: core::ops::range::Range<i32>]
+// cdb-check:    [<Raw View>]     [Type: core::ops::range::Range<i32>]
 
 // cdb-command: dx r2,d
-// cdb-check:r2,d             [Type: core::ops::range::RangeFrom<i32>]
-// cdb-check:    [...] start            : 2 [Type: int]
+// cdb-check:r2,d             : (2..) [Type: core::ops::range::RangeFrom<i32>]
+// cdb-check:    [<Raw View>]     [Type: core::ops::range::RangeFrom<i32>]
 
 // cdb-command: dx r3,d
-// cdb-check:r3,d             [Type: core::ops::range::RangeInclusive<i32>]
-// cdb-check:    [...] start            : 1 [Type: int]
-// cdb-check:    [...] end              : 4 [Type: int]
-// cdb-check:    [...] exhausted        : false [Type: bool]
+// cdb-check:r3,d             : (1..=4) [Type: core::ops::range::RangeInclusive<i32>]
+// cdb-check:    [<Raw View>]     [Type: core::ops::range::RangeInclusive<i32>]
 
 // cdb-command: dx r4,d
-// cdb-check:r4,d             [Type: core::ops::range::RangeToInclusive<i32>]
-// cdb-check:    [...] end              : 3 [Type: int]
+// cdb-check:r4,d             : (..10) [Type: core::ops::range::RangeTo<i32>]
+// cdb-check:    [<Raw View>]     [Type: core::ops::range::RangeTo<i32>]
 
 // cdb-command: dx r5,d
-// cdb-check:r5,d             [Type: core::ops::range::RangeFull]
+// cdb-check:r5,d             : (..=3) [Type: core::ops::range::RangeToInclusive<i32>]
+// cdb-check:    [<Raw View>]     [Type: core::ops::range::RangeToInclusive<i32>]
+
+// cdb-command: dx r6,d
+// cdb-check:r6,d             [Type: core::ops::range::RangeFull]
 
 #[allow(unused_variables)]
 
 
 fn main()
 {
-    let r1 = Range{start: 3, end: 5};
-    let r2 = RangeFrom{start: 2};
-    let r3 = RangeInclusive::new(1, 4);
-    let r4 = RangeToInclusive{end: 3};
-    let r5 = RangeFull{};
+    let r1 = (3..5);
+    let r2 = (2..);
+    let r3 = (1..=4);
+    let r4 = (..10);
+    let r5 = (..=3);
+    let r6 = (..);
     zzz(); // #break
 }
 
index 6e558bd3c13aa847e34a2f07abb3e943fddb7945..55cddf7c6c6b4d762231e39b34578098fcddee20 100644 (file)
 
 // cdb-command:dx r,d
 // cdb-check:r,d              : 42 [Type: alloc::rc::Rc<i32>]
+// cdb-check:    [<Raw View>]     [Type: alloc::rc::Rc<i32>]
+// cdb-check:    [Reference count] : 2 [Type: core::cell::Cell<usize>]
+// cdb-check:    [Weak reference count] : 2 [Type: core::cell::Cell<usize>]
 
 // cdb-command:dx r1,d
 // cdb-check:r1,d             : 42 [Type: alloc::rc::Rc<i32>]
+// cdb-check:    [<Raw View>]     [Type: alloc::rc::Rc<i32>]
+// cdb-check:    [Reference count] : 2 [Type: core::cell::Cell<usize>]
+// cdb-check:    [Weak reference count] : 2 [Type: core::cell::Cell<usize>]
 
 // cdb-command:dx w1,d
-// cdb-check:w1,d             [Type: alloc::rc::Weak<i32>]
-// cdb-check:    [...] ptr              : [...] [Type: core::ptr::non_null::NonNull<alloc::rc::RcBox<i32> >]
+// cdb-check:w1,d             : 42 [Type: alloc::rc::Weak<i32>]
+// cdb-check:    [<Raw View>]     [Type: alloc::rc::Weak<i32>]
+// cdb-check:    [Reference count] : 2 [Type: core::cell::Cell<usize>]
+// cdb-check:    [Weak reference count] : 2 [Type: core::cell::Cell<usize>]
 
 // cdb-command:dx a,d
 // cdb-check:a,d              : 42 [Type: alloc::sync::Arc<i32>]
+// cdb-check:    [<Raw View>]     [Type: alloc::sync::Arc<i32>]
+// cdb-check:    [Reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
+// cdb-check:    [Weak reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
 
 // cdb-command:dx a1,d
 // cdb-check:a1,d             : 42 [Type: alloc::sync::Arc<i32>]
+// cdb-check:    [<Raw View>]     [Type: alloc::sync::Arc<i32>]
+// cdb-check:    [Reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
+// cdb-check:    [Weak reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
 
 // cdb-command:dx w2,d
 // cdb-check:w2,d             : 42 [Type: alloc::sync::Weak<i32>]
+// cdb-check:    [<Raw View>]     [Type: alloc::sync::Weak<i32>]
+// cdb-check:    [Reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
+// cdb-check:    [Weak reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
 
 use std::rc::Rc;
 use std::sync::Arc;
index ac652c8ccf4cc9184e94ff8241257a6239fa46d0..e1c10a4d37fc317bfa6c3c9b4418d9dab6f38e88 100644 (file)
 // cdb-command:dx l
 // cdb-check:l                [Type: std::sync::rwlock::RwLock<i32>]
 // cdb-check:    [...] poison           [Type: std::sync::poison::Flag]
-// cdb-check:    [...] data             [Type: core::cell::UnsafeCell<i32>]
+// cdb-check:    [...] data             : 0 [Type: core::cell::UnsafeCell<i32>]
 //
 // cdb-command:dx r
 // cdb-check:r                [Type: std::sync::rwlock::RwLockReadGuard<i32>]
 // cdb-check:    [...] lock             : [...] [Type: std::sync::rwlock::RwLock<i32> *]
 //
 // cdb-command:dx r.lock->data,d
-// cdb-check:r.lock->data,d   [Type: core::cell::UnsafeCell<i32>]
-// cdb-check:    [...] value            : 0 [Type: int]
+// cdb-check:r.lock->data,d   : 0 [Type: core::cell::UnsafeCell<i32>]
+// cdb-check:    [<Raw View>]     [Type: core::cell::UnsafeCell<i32>]
 
 #[allow(unused_variables)]
 
index ec8ab339e2804ebc9a208c3c2c99005cd71c0a1b..0466909479b67966b71a11ba86a3aebc042f1706 100644 (file)
@@ -47,3 +47,10 @@ pub mod sub_mod {
     /// ```
     pub struct Foo;
 }
+
+pub mod long_trait {
+    use std::ops::DerefMut;
+
+    pub trait ALongNameBecauseItHelpsTestingTheCurrentProblem: DerefMut<Target = u32>
+        + From<u128> + Send + Sync + AsRef<str> + 'static {}
+}
diff --git a/src/test/rustdoc-gui/type-declation-overflow.goml b/src/test/rustdoc-gui/type-declation-overflow.goml
new file mode 100644 (file)
index 0000000..0a316e2
--- /dev/null
@@ -0,0 +1,8 @@
+// This test ensures that the type declaration content overflow is handled inside the <pre> directly.
+goto: file://|DOC_PATH|/lib2/long_trait/trait.ALongNameBecauseItHelpsTestingTheCurrentProblem.html
+// We set a fixed size so there is no chance of "random" resize.
+size: (1100, 800)
+// Logically, the <body> scroll width should be the width of the window.
+assert-property: ("body", {"scrollWidth": "1100"})
+// However, since there is overflow in the type declaration, its scroll width is bigger.
+assert-property: (".type-decl pre", {"scrollWidth": "1324"})
diff --git a/src/test/rustdoc/auxiliary/issue-86620-1.rs b/src/test/rustdoc/auxiliary/issue-86620-1.rs
new file mode 100644 (file)
index 0000000..f6debf6
--- /dev/null
@@ -0,0 +1,11 @@
+#![crate_name = "issue_86620_1"]
+
+pub trait VZip {
+    fn vzip() -> usize;
+}
+
+impl<T> VZip for T {
+    fn vzip() -> usize {
+        0
+    }
+}
index 810c591c53e1e48909ff02f881ad26e10f88148a..efa2025b4b9c229d8d199426528597c34c016a88 100644 (file)
@@ -4,4 +4,6 @@
 extern crate rustdoc_extern_default_method as ext;
 
 // @count extern_default_method/struct.Struct.html '//*[@id="method.provided"]' 1
+// @has extern_default_method/struct.Struct.html '//div[@id="method.provided"]//a[@class="fnname"]/@href' #method.provided
+// @has extern_default_method/struct.Struct.html '//div[@id="method.provided"]//a[@class="anchor"]/@href' #method.provided
 pub use ext::Struct;
diff --git a/src/test/rustdoc/issue-86620.rs b/src/test/rustdoc/issue-86620.rs
new file mode 100644 (file)
index 0000000..b14e266
--- /dev/null
@@ -0,0 +1,9 @@
+// aux-build:issue-86620-1.rs
+
+extern crate issue_86620_1;
+
+use issue_86620_1::*;
+
+// @!has issue_86620/struct.S.html '//div[@id="method.vzip"]//a[@class="fnname"]/@href' #tymethod.vzip
+// @has issue_86620/struct.S.html '//div[@id="method.vzip"]//a[@class="anchor"]/@href' #method.vzip
+pub struct S;
index 6d7289bd0712ba897074def15916d9b3a83d9b34..e8c11a32bf7fdabb47d0fea9e4dd73725e86af4a 100644 (file)
@@ -1,8 +1,8 @@
 error[E0277]: the trait bound `Self: Get` is not satisfied
-  --> $DIR/associated-types-for-unimpl-trait.rs:10:5
+  --> $DIR/associated-types-for-unimpl-trait.rs:10:8
    |
 LL |     fn uhoh<U:Get>(&self, foo: U, bar: <Self as Get>::Value) {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self`
+   |        ^^^^ the trait `Get` is not implemented for `Self`
    |
 help: consider further restricting `Self`
    |
index 0b5dee611e489be85c879e1d2470c4373807f437..e3be434698ab9897ad8d5be41eda0450692cd8e7 100644 (file)
@@ -1,8 +1,8 @@
 error[E0277]: the trait bound `T: Get` is not satisfied
-  --> $DIR/associated-types-no-suitable-bound.rs:11:5
+  --> $DIR/associated-types-no-suitable-bound.rs:11:8
    |
 LL |     fn uhoh<T>(foo: <T as Get>::Value) {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `T`
+   |        ^^^^ the trait `Get` is not implemented for `T`
    |
 help: consider restricting type parameter `T`
    |
index dfe62aa5d6b00b8e5d0ec23137ee721eeb62c42e..9dc3414e9edf0de03844b6a79aa388014848f977 100644 (file)
@@ -1,8 +1,8 @@
 error[E0277]: the trait bound `Self: Get` is not satisfied
-  --> $DIR/associated-types-no-suitable-supertrait-2.rs:17:5
+  --> $DIR/associated-types-no-suitable-supertrait-2.rs:17:8
    |
 LL |     fn uhoh<U:Get>(&self, foo: U, bar: <Self as Get>::Value) {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self`
+   |        ^^^^ the trait `Get` is not implemented for `Self`
    |
 help: consider further restricting `Self`
    |
index f0f2451a1eceac5216475e92a120c398991f7e20..c2aed3f9de548f953162878e5ed4880a21e2e135 100644 (file)
@@ -1,8 +1,8 @@
 error[E0277]: the trait bound `Self: Get` is not satisfied
-  --> $DIR/associated-types-no-suitable-supertrait.rs:17:5
+  --> $DIR/associated-types-no-suitable-supertrait.rs:17:8
    |
 LL |     fn uhoh<U:Get>(&self, foo: U, bar: <Self as Get>::Value) {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self`
+   |        ^^^^ the trait `Get` is not implemented for `Self`
    |
 help: consider further restricting `Self`
    |
@@ -10,10 +10,10 @@ LL |     fn uhoh<U:Get>(&self, foo: U, bar: <Self as Get>::Value) where Self: Ge
    |                                                              ^^^^^^^^^^^^^^^
 
 error[E0277]: the trait bound `(T, U): Get` is not satisfied
-  --> $DIR/associated-types-no-suitable-supertrait.rs:22:5
+  --> $DIR/associated-types-no-suitable-supertrait.rs:22:8
    |
 LL |     fn uhoh<U:Get>(&self, foo: U, bar: <(T, U) as Get>::Value) {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `(T, U)`
+   |        ^^^^ the trait `Get` is not implemented for `(T, U)`
 
 error: aborting due to 2 previous errors
 
index 4528f03c54a639b276004f2fc0eb1727ee0f770f..fb842d968676d40d44105519ac78342f41076a36 100644 (file)
@@ -1,8 +1,8 @@
 error[E0277]: the trait bound `Self: Get` is not satisfied
-  --> $DIR/associated-types-projection-to-unrelated-trait-in-method-without-default.rs:10:5
+  --> $DIR/associated-types-projection-to-unrelated-trait-in-method-without-default.rs:10:8
    |
 LL |     fn okay<U:Get>(&self, foo: U, bar: <Self as Get>::Value);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self`
+   |        ^^^^ the trait `Get` is not implemented for `Self`
    |
 help: consider further restricting `Self`
    |
diff --git a/src/test/ui/associated-types/normalization-debruijn-1.rs b/src/test/ui/associated-types/normalization-debruijn-1.rs
new file mode 100644 (file)
index 0000000..a5abf1b
--- /dev/null
@@ -0,0 +1,36 @@
+// build-pass
+// edition:2018
+
+// Regression test to ensure we handle debruijn indices correctly in projection
+// normalization under binders. Found in crater run for #85499
+
+use std::future::Future;
+use std::pin::Pin;
+pub enum Outcome<S, E> {
+    Success((S, E)),
+}
+pub struct Request<'r> {
+    _marker: std::marker::PhantomData<&'r ()>,
+}
+pub trait FromRequest<'r>: Sized {
+    type Error;
+    fn from_request<'life0>(
+        request: &'r Request<'life0>,
+    ) -> Pin<Box<dyn Future<Output = Outcome<Self, Self::Error>>>>;
+}
+impl<'r, T: FromRequest<'r>> FromRequest<'r> for Option<T> {
+    type Error = ();
+    fn from_request<'life0>(
+        request: &'r Request<'life0>,
+    ) -> Pin<Box<dyn Future<Output = Outcome<Self, Self::Error>>>> {
+        Box::pin(async move {
+            let request = request;
+            match T::from_request(request).await {
+                _ => todo!(),
+            }
+        });
+        todo!()
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/associated-types/normalization-debruijn-2.rs b/src/test/ui/associated-types/normalization-debruijn-2.rs
new file mode 100644 (file)
index 0000000..abe248e
--- /dev/null
@@ -0,0 +1,31 @@
+// build-pass
+// edition:2018
+
+// Regression test to ensure we handle debruijn indices correctly in projection
+// normalization under binders. Found in crater run for #85499
+
+use std::future::Future;
+use std::pin::Pin;
+pub enum Outcome<S, E> {
+    Success(S),
+    Failure(E),
+}
+pub struct Request<'r> {
+    _marker: std::marker::PhantomData<&'r ()>,
+}
+pub trait FromRequest<'r>: Sized {
+    type Error;
+    fn from_request<'life0>(
+        request: &'r Request<'life0>,
+    ) -> Pin<Box<dyn Future<Output = Outcome<Self, Self::Error>>>>;
+}
+pub struct S<T> {
+    _marker: std::marker::PhantomData<T>,
+}
+impl<'r, T: FromRequest<'r>> S<T> {
+    pub async fn from_request(request: &'r Request<'_>) {
+        let _ = T::from_request(request).await;
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/associated-types/normalization-debruijn-3.rs b/src/test/ui/associated-types/normalization-debruijn-3.rs
new file mode 100644 (file)
index 0000000..2bea78c
--- /dev/null
@@ -0,0 +1,41 @@
+// build-pass
+// edition:2018
+
+// Regression test to ensure we handle debruijn indices correctly in projection
+// normalization under binders. Found in crater run for #85499
+
+use std::future::{Future, Ready};
+async fn read() {
+    let _ = connect(&()).await;
+}
+async fn connect<A: ToSocketAddr>(addr: A) {
+    let _ = addr.to_socket_addr().await;
+}
+pub trait ToSocketAddr {
+    type Future: Future<Output = ()>;
+    fn to_socket_addr(&self) -> Self::Future;
+}
+impl ToSocketAddr for &() {
+    type Future = Ready<()>;
+    fn to_socket_addr(&self) -> Self::Future {
+        unimplemented!()
+    }
+}
+struct Server;
+impl Server {
+    fn and_then<F>(self, _fun: F) -> AndThen<F> {
+        unimplemented!()
+    }
+}
+struct AndThen<F> {
+    _marker: std::marker::PhantomData<F>,
+}
+pub async fn run<F>(_: F) {
+}
+fn main() {
+    let _ = async {
+        let server = Server;
+        let verification_route = server.and_then(read);
+        run(verification_route).await;
+    };
+}
diff --git a/src/test/ui/associated-types/normalization-generality.rs b/src/test/ui/associated-types/normalization-generality.rs
new file mode 100644 (file)
index 0000000..f8e3f5b
--- /dev/null
@@ -0,0 +1,36 @@
+// build-pass
+
+// Ensures that we don't regress on "implementation is not general enough" when
+// normalizating under binders.
+
+#![feature(no_core)]
+
+pub trait Yokeable<'a> {
+    type Output: 'a;
+}
+
+pub struct Yoke<Y: for<'a> Yokeable<'a>> {
+    _yokeable: Y,
+}
+
+impl<Y: for<'a> Yokeable<'a>> Yoke<Y> {
+    pub fn project<'this, P>(
+        &'this self,
+        _f: for<'a> fn(<Y as Yokeable<'a>>::Output, &'a ()) -> <P as Yokeable<'a>>::Output,
+    ) -> Yoke<P>
+    where
+        P: for<'a> Yokeable<'a>,
+    {
+        unimplemented!()
+    }
+}
+
+pub fn slice(y: Yoke<&'static ()>) -> Yoke<&'static ()> {
+    y.project(move |yk, _| yk)
+}
+
+impl<'a, T> Yokeable<'a> for &'static T {
+    type Output = &'a T;
+}
+
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-87097.rs b/src/test/ui/closures/2229_closure_analysis/issue-87097.rs
new file mode 100644 (file)
index 0000000..241ddcb
--- /dev/null
@@ -0,0 +1,35 @@
+// run-pass
+// edition:2021
+
+enum Variant {
+    A,
+    B, //~ WARNING: variant is never constructed: `B`
+}
+
+struct A {
+    field: Variant,
+}
+
+fn discriminant_is_a_ref() {
+    let here = A { field: Variant::A };
+    let out_ref = &here.field;
+
+    || match out_ref { //~ WARNING: unused closure that must be used
+        Variant::A => (),
+        Variant::B => (),
+    };
+}
+
+fn discriminant_is_a_field() {
+    let here = A { field: Variant::A };
+
+    || match here.field { //~ WARNING: unused closure that must be used
+        Variant::A => (),
+        Variant::B => (),
+    };
+}
+
+fn main() {
+    discriminant_is_a_ref();
+    discriminant_is_a_field();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-87097.stderr b/src/test/ui/closures/2229_closure_analysis/issue-87097.stderr
new file mode 100644 (file)
index 0000000..38f2929
--- /dev/null
@@ -0,0 +1,33 @@
+warning: variant is never constructed: `B`
+  --> $DIR/issue-87097.rs:6:5
+   |
+LL |     B,
+   |     ^
+   |
+   = note: `#[warn(dead_code)]` on by default
+
+warning: unused closure that must be used
+  --> $DIR/issue-87097.rs:17:5
+   |
+LL | /     || match out_ref {
+LL | |         Variant::A => (),
+LL | |         Variant::B => (),
+LL | |     };
+   | |______^
+   |
+   = note: `#[warn(unused_must_use)]` on by default
+   = note: closures are lazy and do nothing unless called
+
+warning: unused closure that must be used
+  --> $DIR/issue-87097.rs:26:5
+   |
+LL | /     || match here.field {
+LL | |         Variant::A => (),
+LL | |         Variant::B => (),
+LL | |     };
+   | |______^
+   |
+   = note: closures are lazy and do nothing unless called
+
+warning: 3 warnings emitted
+
index 3b284eadbd0e3a6ef1073d66481a45e9850b8bf6..0e7abf64fab068cacc48300a876c8e61d9c2c08a 100644 (file)
@@ -195,6 +195,21 @@ fn box_mut_2() {
     //~| NOTE: Min Capture p_foo[Deref,Deref,(0, 0)] -> UniqueImmBorrow
 }
 
+// Test that move closures can take ownership of Copy type
+fn returned_closure_owns_copy_type_data() -> impl Fn() -> i32 {
+    let x = 10;
+
+    let c = #[rustc_capture_analysis] move || x;
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    //~| First Pass analysis includes:
+    //~| NOTE: Capturing x[] -> ImmBorrow
+    //~| Min Capture analysis includes:
+    //~| NOTE: Min Capture x[] -> ByValue
+
+    c
+}
+
 fn main() {
     simple_move_closure();
     simple_ref();
index c8e2708feee314a3ef7f765ee1174c6604daaea6..82ed99f9444d32243ab26af32c74d853ecd94084 100644 (file)
@@ -88,6 +88,39 @@ LL |     let c = #[rustc_capture_analysis] move || p_foo.x += 10;
    = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
    = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
 
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/move_closure.rs:202:13
+   |
+LL |     let c = #[rustc_capture_analysis] move || x;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/move_closure.rs:202:39
+   |
+LL |     let c = #[rustc_capture_analysis] move || x;
+   |                                       ^^^^^^^^^
+   |
+note: Capturing x[] -> ImmBorrow
+  --> $DIR/move_closure.rs:202:47
+   |
+LL |     let c = #[rustc_capture_analysis] move || x;
+   |                                               ^
+
+error: Min Capture analysis includes:
+  --> $DIR/move_closure.rs:202:39
+   |
+LL |     let c = #[rustc_capture_analysis] move || x;
+   |                                       ^^^^^^^^^
+   |
+note: Min Capture x[] -> ByValue
+  --> $DIR/move_closure.rs:202:47
+   |
+LL |     let c = #[rustc_capture_analysis] move || x;
+   |                                               ^
+
 error: First Pass analysis includes:
   --> $DIR/move_closure.rs:15:5
    |
@@ -424,6 +457,6 @@ note: Min Capture p_foo[Deref,Deref,(0, 0)] -> UniqueImmBorrow
 LL |     let c = #[rustc_capture_analysis] move || p_foo.x += 10;
    |                                               ^^^^^^^
 
-error: aborting due to 30 previous errors
+error: aborting due to 33 previous errors
 
 For more information about this error, try `rustc --explain E0658`.
index 65c8a5a7850fec4ad66863570e43722459547e50..e1b61e85ec1927d18444a778bcb188820ce67bee 100644 (file)
@@ -3,6 +3,8 @@
 
 // Test that move closures compile properly with `capture_disjoint_fields` enabled.
 
+#![allow(unused)]
+
 fn simple_ref() {
     let mut s = 10;
     let ref_s = &mut s;
@@ -92,6 +94,15 @@ fn data_moved_but_not_fn_once() {
     c();
 }
 
+// Test that move closures can take ownership of Copy type
+fn returned_closure_owns_copy_type_data() -> impl Fn() -> i32 {
+    let x = 10;
+
+    let c = move || x;
+
+    c
+}
+
 fn main() {
     simple_ref();
     struct_contains_ref_to_another_struct();
@@ -100,4 +111,6 @@ fn main() {
 
     disjoint_via_ref();
     data_moved_but_not_fn_once();
+
+    returned_closure_owns_copy_type_data();
 }
index 09dade20992ba15234337205d6345200214e9b1a..ed82b6a742c8de7aebef7428e7ccb52728431e70 100644 (file)
@@ -66,4 +66,15 @@ union Z {
     i: i32,
 }
 
+#[repr(packed, align(0x100))]
+pub struct S(u16); //~ ERROR type has conflicting packed and align representation hints
+
+#[repr(packed, align(0x100))]
+pub union U { //~ ERROR type has conflicting packed and align representation hints
+    u: u16
+}
+
+static B: U = U { u: 0 };
+static A: S = S(0);
+
 fn main() {}
index 0b78532c73751fbf9298b29445e88a643ad508a3..0f32fc0481bc6e251c2a581675d76a3ffa02ce63 100644 (file)
@@ -74,7 +74,21 @@ LL | |     i: i32,
 LL | | }
    | |_^
 
-error: aborting due to 10 previous errors
+error[E0587]: type has conflicting packed and align representation hints
+  --> $DIR/conflicting-repr-hints.rs:70:1
+   |
+LL | pub struct S(u16);
+   | ^^^^^^^^^^^^^^^^^^
+
+error[E0587]: type has conflicting packed and align representation hints
+  --> $DIR/conflicting-repr-hints.rs:73:1
+   |
+LL | / pub union U {
+LL | |     u: u16
+LL | | }
+   | |_^
+
+error: aborting due to 12 previous errors
 
 Some errors have detailed explanations: E0566, E0587, E0634.
 For more information about an error, try `rustc --explain E0566`.
diff --git a/src/test/ui/const-generics/defaults/default-const-param-cannot-reference-self.rs b/src/test/ui/const-generics/defaults/default-const-param-cannot-reference-self.rs
new file mode 100644 (file)
index 0000000..9af8443
--- /dev/null
@@ -0,0 +1,16 @@
+#![feature(const_generics_defaults)]
+
+struct Struct<const N: usize = { Self; 10 }>;
+//~^ ERROR generic parameters cannot use `Self` in their defaults [E0735]
+
+enum Enum<const N: usize = { Self; 10 }> { }
+//~^ ERROR generic parameters cannot use `Self` in their defaults [E0735]
+
+union Union<const N: usize = { Self; 10 }> { not_empty: () }
+//~^ ERROR generic parameters cannot use `Self` in their defaults [E0735]
+
+fn main() {
+    let _: Struct;
+    let _: Enum;
+    let _: Union;
+}
diff --git a/src/test/ui/const-generics/defaults/default-const-param-cannot-reference-self.stderr b/src/test/ui/const-generics/defaults/default-const-param-cannot-reference-self.stderr
new file mode 100644 (file)
index 0000000..5dfec2f
--- /dev/null
@@ -0,0 +1,21 @@
+error[E0735]: generic parameters cannot use `Self` in their defaults
+  --> $DIR/default-const-param-cannot-reference-self.rs:3:34
+   |
+LL | struct Struct<const N: usize = { Self; 10 }>;
+   |                                  ^^^^ `Self` in generic parameter default
+
+error[E0735]: generic parameters cannot use `Self` in their defaults
+  --> $DIR/default-const-param-cannot-reference-self.rs:6:30
+   |
+LL | enum Enum<const N: usize = { Self; 10 }> { }
+   |                              ^^^^ `Self` in generic parameter default
+
+error[E0735]: generic parameters cannot use `Self` in their defaults
+  --> $DIR/default-const-param-cannot-reference-self.rs:9:32
+   |
+LL | union Union<const N: usize = { Self; 10 }> { not_empty: () }
+   |                                ^^^^ `Self` in generic parameter default
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0735`.
index 95e7f9e9c837ad6abb2ffe093327d7ea576a5396..5d38e032ac408518ffae53627349156d7856145d 100644 (file)
@@ -5,7 +5,6 @@
 #![feature(const_float_bits_conv)]
 #![feature(const_float_classify)]
 #![feature(const_trait_impl)]
-#![allow(incomplete_features)]
 
 // Don't promote
 const fn nop<T>(x: T) -> T { x }
index 01daa1c9cb42fb1f76753cdcf97a430bc5d50b6d..e3fda57548ea32c3d753a0d38adf2fd02cc55fa8 100644 (file)
@@ -1,7 +1,6 @@
 // build-pass
 
 #![crate_type = "lib"]
-#![allow(incomplete_features)]
 #![feature(staged_api)]
 #![feature(const_trait_impl)]
 #![stable(feature = "foo", since = "1.0.0")]
diff --git a/src/test/ui/generic-associated-types/issue-76407.rs b/src/test/ui/generic-associated-types/issue-76407.rs
new file mode 100644 (file)
index 0000000..42f19fe
--- /dev/null
@@ -0,0 +1,28 @@
+// check-pass
+
+#![feature(generic_associated_types)]
+#![allow(incomplete_features)]
+
+trait Marker {}
+
+impl Marker for u32 {}
+
+trait MyTrait {
+    type Item<'a>;
+}
+
+struct MyStruct;
+
+impl MyTrait for MyStruct {
+    type Item<'a> = u32;
+}
+
+fn ty_check<T>()
+where
+    T: MyTrait,
+    for<'a> T::Item<'a>: Marker
+{}
+
+fn main() {
+    ty_check::<MyStruct>();
+}
diff --git a/src/test/ui/generic-associated-types/issue-76826.rs b/src/test/ui/generic-associated-types/issue-76826.rs
new file mode 100644 (file)
index 0000000..a905ef4
--- /dev/null
@@ -0,0 +1,45 @@
+// run-pass
+
+#![feature(generic_associated_types)]
+#![allow(incomplete_features)]
+
+pub trait Iter {
+    type Item<'a> where Self: 'a;
+
+    fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>;
+
+    fn for_each<F>(mut self, mut f: F)
+        where Self: Sized, F: for<'a> FnMut(Self::Item<'a>)
+    {
+        while let Some(item) = self.next() {
+            f(item);
+        }
+    }
+}
+
+pub struct Windows<T> {
+    items: Vec<T>,
+    start: usize,
+    len: usize,
+}
+
+impl<T> Windows<T> {
+    pub fn new(items: Vec<T>, len: usize) -> Self {
+        Self { items, start: 0, len }
+    }
+}
+
+impl<T> Iter for Windows<T> {
+    type Item<'a> where T: 'a = &'a mut [T];
+
+    fn next<'a>(&'a mut self) -> Option<Self::Item<'a>> {
+        let slice = self.items.get_mut(self.start..self.start + self.len)?;
+        self.start += 1;
+        Some(slice)
+    }
+}
+
+fn main() {
+    Windows::new(vec![1, 2, 3, 4, 5], 3)
+        .for_each(|slice| println!("{:?}", slice));
+}
index b560cc2ce7060d33f03ca8c61584136de9a5e8c7..12db143e474492a8e79a72869082feadbc303502 100644 (file)
 // compatibility concern.
 
 struct Snobound<'a, P = Self> { x: Option<&'a P> }
-//~^ ERROR type parameters cannot use `Self` in their defaults [E0735]
+//~^ ERROR generic parameters cannot use `Self` in their defaults [E0735]
 
 enum Enobound<'a, P = Self> { A, B(Option<&'a P>) }
-//~^ ERROR type parameters cannot use `Self` in their defaults [E0735]
+//~^ ERROR generic parameters cannot use `Self` in their defaults [E0735]
 
 union Unobound<'a, P = Self> { x: i32, y: Option<&'a P> }
-//~^ ERROR type parameters cannot use `Self` in their defaults [E0735]
+//~^ ERROR generic parameters cannot use `Self` in their defaults [E0735]
 
 // Disallowing `Self` in defaults sidesteps need to check the bounds
 // on the defaults in cases like these.
 
 struct Ssized<'a, P: Sized = [Self]> { x: Option<&'a P> }
-//~^ ERROR type parameters cannot use `Self` in their defaults [E0735]
+//~^ ERROR generic parameters cannot use `Self` in their defaults [E0735]
 
 enum Esized<'a, P: Sized = [Self]> { A, B(Option<&'a P>) }
-//~^ ERROR type parameters cannot use `Self` in their defaults [E0735]
+//~^ ERROR generic parameters cannot use `Self` in their defaults [E0735]
 
 union Usized<'a, P: Sized = [Self]> { x: i32, y: Option<&'a P> }
-//~^ ERROR type parameters cannot use `Self` in their defaults [E0735]
+//~^ ERROR generic parameters cannot use `Self` in their defaults [E0735]
 
 fn demo_usages() {
     // An ICE means you only get the error from the first line of the
index 689ffbd0febc2e7bf1ea8a7c076b0bf98f3c7158..f3a550801b9bd57084de7633e165b38eb6f0d223 100644 (file)
@@ -1,38 +1,38 @@
-error[E0735]: type parameters cannot use `Self` in their defaults
+error[E0735]: generic parameters cannot use `Self` in their defaults
   --> $DIR/issue-61631-default-type-param-cannot-reference-self.rs:13:25
    |
 LL | struct Snobound<'a, P = Self> { x: Option<&'a P> }
-   |                         ^^^^ `Self` in type parameter default
+   |                         ^^^^ `Self` in generic parameter default
 
-error[E0735]: type parameters cannot use `Self` in their defaults
+error[E0735]: generic parameters cannot use `Self` in their defaults
   --> $DIR/issue-61631-default-type-param-cannot-reference-self.rs:16:23
    |
 LL | enum Enobound<'a, P = Self> { A, B(Option<&'a P>) }
-   |                       ^^^^ `Self` in type parameter default
+   |                       ^^^^ `Self` in generic parameter default
 
-error[E0735]: type parameters cannot use `Self` in their defaults
+error[E0735]: generic parameters cannot use `Self` in their defaults
   --> $DIR/issue-61631-default-type-param-cannot-reference-self.rs:19:24
    |
 LL | union Unobound<'a, P = Self> { x: i32, y: Option<&'a P> }
-   |                        ^^^^ `Self` in type parameter default
+   |                        ^^^^ `Self` in generic parameter default
 
-error[E0735]: type parameters cannot use `Self` in their defaults
+error[E0735]: generic parameters cannot use `Self` in their defaults
   --> $DIR/issue-61631-default-type-param-cannot-reference-self.rs:25:31
    |
 LL | struct Ssized<'a, P: Sized = [Self]> { x: Option<&'a P> }
-   |                               ^^^^ `Self` in type parameter default
+   |                               ^^^^ `Self` in generic parameter default
 
-error[E0735]: type parameters cannot use `Self` in their defaults
+error[E0735]: generic parameters cannot use `Self` in their defaults
   --> $DIR/issue-61631-default-type-param-cannot-reference-self.rs:28:29
    |
 LL | enum Esized<'a, P: Sized = [Self]> { A, B(Option<&'a P>) }
-   |                             ^^^^ `Self` in type parameter default
+   |                             ^^^^ `Self` in generic parameter default
 
-error[E0735]: type parameters cannot use `Self` in their defaults
+error[E0735]: generic parameters cannot use `Self` in their defaults
   --> $DIR/issue-61631-default-type-param-cannot-reference-self.rs:31:30
    |
 LL | union Usized<'a, P: Sized = [Self]> { x: i32, y: Option<&'a P> }
-   |                              ^^^^ `Self` in type parameter default
+   |                              ^^^^ `Self` in generic parameter default
 
 error: aborting due to 6 previous errors
 
index 22c3470b61edeb955e8cb92b46c1d837c4c241aa..8872f51753c94789fa0841a9a635e7d00c3fdfe2 100644 (file)
@@ -1,10 +1,8 @@
 error[E0277]: the trait bound `isize: HasState` is not satisfied
-  --> $DIR/issue-18611.rs:1:1
+  --> $DIR/issue-18611.rs:1:4
    |
-LL | / fn add_state(op: <isize as HasState>::State) {
-LL | |
-LL | | }
-   | |_^ the trait `HasState` is not implemented for `isize`
+LL | fn add_state(op: <isize as HasState>::State) {
+   |    ^^^^^^^^^ the trait `HasState` is not implemented for `isize`
 
 error: aborting due to previous error
 
index e68482d1caf696db159003465d951dac2fddcc84..03e3311e0f39bac7d151723e1276e51e79bcba52 100644 (file)
@@ -1,8 +1,8 @@
 error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
-  --> $DIR/issue-20831-debruijn.rs:28:33
+  --> $DIR/issue-20831-debruijn.rs:28:8
    |
 LL |     fn subscribe(&mut self, t : Box<dyn Subscriber<Input=<Self as Publisher>::Output> + 'a>) {
-   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^
    |
 note: first, the lifetime cannot outlive the anonymous lifetime defined on the method body at 28:58...
   --> $DIR/issue-20831-debruijn.rs:28:58
@@ -15,10 +15,10 @@ note: ...but the lifetime must also be valid for the lifetime `'a` as defined on
 LL | impl<'a> Publisher<'a> for MyStruct<'a> {
    |      ^^
 note: ...so that the types are compatible
-  --> $DIR/issue-20831-debruijn.rs:28:33
+  --> $DIR/issue-20831-debruijn.rs:28:8
    |
 LL |     fn subscribe(&mut self, t : Box<dyn Subscriber<Input=<Self as Publisher>::Output> + 'a>) {
-   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^
    = note: expected `Publisher<'_>`
               found `Publisher<'_>`
 
diff --git a/src/test/ui/lint/future-incompat-test.rs b/src/test/ui/lint/future-incompat-test.rs
new file mode 100644 (file)
index 0000000..ce8c118
--- /dev/null
@@ -0,0 +1,10 @@
+// compile-flags: -Zfuture-incompat-test -Zemit-future-incompat-report
+// check-pass
+
+// The `-Zfuture-incompat-test flag causes any normal warning to be included
+// in the future-incompatible report. The stderr output here should mention
+// the future incompatible report (as extracted by compiletest).
+
+fn main() {
+    let x = 1;
+}
diff --git a/src/test/ui/lint/future-incompat-test.stderr b/src/test/ui/lint/future-incompat-test.stderr
new file mode 100644 (file)
index 0000000..52674a8
--- /dev/null
@@ -0,0 +1,9 @@
+Future incompatibility report: Future breakage diagnostic:
+warning: unused variable: `x`
+  --> $DIR/future-incompat-test.rs:9:9
+   |
+LL |     let x = 1;
+   |         ^ help: if this is intentional, prefix it with an underscore: `_x`
+   |
+   = note: `-A unused-variables` implied by `-A unused`
+
index 43be9532f5d1c068e70ea61217a6335476783ca8..714a6561a6d0990b0bc2255b6b2b0105deb617f2 100644 (file)
@@ -6,6 +6,8 @@
 
 #![feature(assert_matches)]
 
+use std::assert::assert_matches;
+
 fn main() {
     assert_matches!(1 + 1, 3, "1 + 1 definitely should be 3");
 }
index 1090b1578ba511b6e8a6e2dad8810c73d559d24c..a6fa5128d2f862d66136cdc22e98883f9ed56ed1 100644 (file)
@@ -6,6 +6,8 @@
 
 #![feature(assert_matches)]
 
+use std::assert::assert_matches;
+
 fn main() {
     assert!(matches!((), ()));
     assert_matches!((), ());
index d003acd879a77407d9d161ee6345381fe0944893..8c7c8918f3fc60d5d63127de280eb492ea3cb8dc 100644 (file)
@@ -1,8 +1,8 @@
 error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'d` due to conflicting requirements
-  --> $DIR/normalization-bounds-error.rs:12:1
+  --> $DIR/normalization-bounds-error.rs:12:4
    |
 LL | fn visit_seq<'d, 'a: 'd>() -> <&'a () as Visitor<'d>>::Value {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |    ^^^^^^^^^
    |
 note: first, the lifetime cannot outlive the lifetime `'d` as defined on the function body at 12:14...
   --> $DIR/normalization-bounds-error.rs:12:14
@@ -15,10 +15,10 @@ note: ...but the lifetime must also be valid for the lifetime `'a` as defined on
 LL | fn visit_seq<'d, 'a: 'd>() -> <&'a () as Visitor<'d>>::Value {}
    |                  ^^
 note: ...so that the types are compatible
-  --> $DIR/normalization-bounds-error.rs:12:1
+  --> $DIR/normalization-bounds-error.rs:12:4
    |
 LL | fn visit_seq<'d, 'a: 'd>() -> <&'a () as Visitor<'d>>::Value {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |    ^^^^^^^^^
    = note: expected `Visitor<'d>`
               found `Visitor<'_>`
 
index e764480e8e548b881f8fa0ba37aee390933d65be..070b066721350956fb1c3fa83d6c3add1b43988f 100644 (file)
@@ -81,7 +81,7 @@ LL |     tuple_from_req!(Foo);
 
 warning: 5 warnings emitted
 
-Future incompatibility report: Future breakage date: None, diagnostic:
+Future incompatibility report: Future breakage diagnostic:
 warning: using an old version of `time-macros-impl`
   --> $DIR/time-macros-impl/src/lib.rs:5:32
    |
@@ -99,7 +99,7 @@ LL |     impl_macros!(Foo);
    = note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage
    = note: this warning originates in the macro `impl_macros` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-Future breakage date: None, diagnostic:
+Future breakage diagnostic:
 warning: using an old version of `time-macros-impl`
   --> $DIR/time-macros-impl-0.1.0/src/lib.rs:5:32
    |
@@ -116,7 +116,7 @@ LL |     impl_macros!(Foo);
    = note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage
    = note: this warning originates in the macro `impl_macros` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-Future breakage date: None, diagnostic:
+Future breakage diagnostic:
 warning: using an old version of `js-sys`
   --> $DIR/js-sys-0.3.17/src/lib.rs:5:32
    |
@@ -133,7 +133,7 @@ LL |     arrays!(Foo);
    = note: older versions of the `js-sys` crate will stop compiling in future versions of Rust; please update to `js-sys` v0.3.40 or above
    = note: this warning originates in the macro `arrays` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-Future breakage date: None, diagnostic:
+Future breakage diagnostic:
 warning: using an old version of `actix-web`
   --> $DIR/actix-web/src/extract.rs:5:34
    |
@@ -150,7 +150,7 @@ LL |     tuple_from_req!(Foo);
    = note: the version of `actix-web` you are using might stop compiling in future versions of Rust; please update to the latest version of the `actix-web` crate to avoid breakage
    = note: this warning originates in the macro `tuple_from_req` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-Future breakage date: None, diagnostic:
+Future breakage diagnostic:
 warning: using an old version of `actix-web`
   --> $DIR/actix-web-2.0.0/src/extract.rs:5:34
    |
index 0b930705e35105dd1df67abc970ef59fe1c755b3..4d6edab08e2cf28b85f668ad55a2549780241bff 100644 (file)
@@ -11,7 +11,7 @@ LL | enum ProceduralMasqueradeDummyType {
 
 warning: 1 warning emitted
 
-Future incompatibility report: Future breakage date: None, diagnostic:
+Future incompatibility report: Future breakage diagnostic:
 warning: using `procedural-masquerade` crate
   --> $DIR/issue-73933-procedural-masquerade.rs:8:6
    |
index 6155ae7bcdfdf3f284905352b64360da99a9514c..5532c8b90e32685aecd7ff4f252860c585c92567 100644 (file)
@@ -1,4 +1,4 @@
 fn main() {
-    assert(true);
-    //~^ ERROR expected function, found macro `assert`
+    assert_eq(1, 1);
+    //~^ ERROR expected function, found macro `assert_eq`
 }
index 7d35ce7e65e5879e1bfedd3b0a3c7ade8d9f6cdf..efcfc7198ab51291f49e763c4627838e83298457 100644 (file)
@@ -1,13 +1,13 @@
-error[E0423]: expected function, found macro `assert`
+error[E0423]: expected function, found macro `assert_eq`
   --> $DIR/resolve-hint-macro.rs:2:5
    |
-LL |     assert(true);
-   |     ^^^^^^ not a function
+LL |     assert_eq(1, 1);
+   |     ^^^^^^^^^ not a function
    |
 help: use `!` to invoke the macro
    |
-LL |     assert!(true);
-   |           ^
+LL |     assert_eq!(1, 1);
+   |              ^
 
 error: aborting due to previous error
 
index 5a66af1d29e9483039a6e61457fa68dffc8ba752..4a1bd5da98b8a403876e778d041c7589f7f05a8d 100644 (file)
@@ -3,7 +3,6 @@
 // FIXME: This test should fail since, within a const impl of `Foo`, the bound on `Foo::Bar` should
 // require a const impl of `Add` for the associated type.
 
-#![allow(incomplete_features)]
 #![feature(const_trait_impl)]
 
 struct NonConstAdd(i32);
index 338ac3d25053635ec6fe0a4fd65c54b2a703d0e4..be73ec09ceb3ba8ea50f807aa4215fdd4a0895f6 100644 (file)
@@ -1,5 +1,4 @@
 #![feature(const_trait_impl)]
-#![allow(incomplete_features)]
 
 #[default_method_body_is_const] //~ ERROR attribute should be applied
 trait A {
index 3af71d6ff78fb016c0e80a46d8bfba29bcf84ea5..fcb7e15b205e94535b1499b3d91dc25cccfe1857 100644 (file)
@@ -1,5 +1,5 @@
 error: attribute should be applied to a trait method with body
-  --> $DIR/attr-misuse.rs:4:1
+  --> $DIR/attr-misuse.rs:3:1
    |
 LL |   #[default_method_body_is_const]
    |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -13,7 +13,7 @@ LL | | }
    | |_- not a trait method or missing a body
 
 error: attribute should be applied to a trait method with body
-  --> $DIR/attr-misuse.rs:13:1
+  --> $DIR/attr-misuse.rs:12:1
    |
 LL | #[default_method_body_is_const]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -21,7 +21,7 @@ LL | fn main() {}
    | ------------ not a trait method or missing a body
 
 error: attribute should be applied to a trait method with body
-  --> $DIR/attr-misuse.rs:6:5
+  --> $DIR/attr-misuse.rs:5:5
    |
 LL |     #[default_method_body_is_const]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index 4285eaf18aaad4370de3ba1a73c29f599d8779cc..d8fd7ef3c1f4450ae6c68d6dff6a0ea10997e50f 100644 (file)
@@ -1,5 +1,4 @@
 #![feature(const_trait_impl)]
-#![allow(incomplete_features)]
 
 pub trait MyTrait {
     fn func(self);
index 734ce17001edd8f855dce50698851a4f79fd048d..80e61ab0a9e8bb0ef13dc0c6e32a5407ae78bf0e 100644 (file)
@@ -1,5 +1,4 @@
 #![feature(const_trait_impl)]
-#![allow(incomplete_features)]
 
 #![feature(staged_api)]
 #![stable(feature = "rust1", since = "1.0.0")]
index fa5570d5454a05c2c0659eff12ad5772c4cb501e..33e839fd1203d51b5e70e6d9aa0e24e79df29a91 100644 (file)
@@ -1,4 +1,3 @@
-#![allow(incomplete_features)]
 #![feature(const_trait_impl)]
 
 pub trait Plus {
index d3f350e1b61db98a8c6085b3ec034c51e2076faa..5a73c4debb481d0bfe1c47bb657c2490c9ed5692 100644 (file)
@@ -1,5 +1,5 @@
 error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-  --> $DIR/call-const-trait-method-fail.rs:25:5
+  --> $DIR/call-const-trait-method-fail.rs:24:5
    |
 LL |     a.plus(b)
    |     ^^^^^^^^^
index 44814b0654e91f12d5b868c9f016f67085fc566a..cf38bc3c9645321cafbcf9f9e3e279e07e01a3a7 100644 (file)
@@ -1,6 +1,5 @@
 // run-pass
 
-#![allow(incomplete_features)]
 #![feature(const_trait_impl)]
 
 struct Int(i32);
index 47eed89d03d2dc81d5b35bee1df3c8e7c541a36e..f3391aa63468cf91580aec8019bdd5bcb5e7a73e 100644 (file)
@@ -4,7 +4,6 @@
 
 #![feature(const_trait_impl)]
 #![feature(const_fn_trait_bound)]
-#![allow(incomplete_features)]
 
 struct S;
 
index 9aefe6380cb18133109a0f588a00f070e57cf06f..087f8fbdcd1ea85eaca4ce49b197b9d250159742 100644 (file)
@@ -2,7 +2,6 @@
 // ignore-test
 
 #![feature(const_trait_impl)]
-#![allow(incomplete_features)]
 
 struct S;
 
index 953a65111990e96a787214cd3300f48ca680bace..aee88d6053eee2cee24404f497867fe66abe009e 100644 (file)
@@ -4,7 +4,6 @@
 
 #![feature(const_trait_impl)]
 #![feature(const_fn_trait_bound)]
-#![allow(incomplete_features)]
 
 struct S;
 
index e148ad9a0ee14d217381b80a355a2bbec35615a6..4b2f049b99d9e05063772574e45b0b1f2e34a63c 100644 (file)
@@ -1,4 +1,3 @@
-#![allow(incomplete_features)]
 #![feature(const_trait_impl)]
 
 pub struct Int(i32);
index 99560ed06888aeab78a1e16d1f2c0a2b3e950597..a0960a21d46f87e1c45420e6911e85ba61bce4df 100644 (file)
@@ -1,5 +1,5 @@
 error[E0119]: conflicting implementations of trait `std::ops::Add` for type `i32`
-  --> $DIR/const-and-non-const-impl.rs:6:1
+  --> $DIR/const-and-non-const-impl.rs:5:1
    |
 LL | impl const std::ops::Add for i32 {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL | impl const std::ops::Add for i32 {
            - impl Add for i32;
 
 error[E0119]: conflicting implementations of trait `std::ops::Add` for type `Int`
-  --> $DIR/const-and-non-const-impl.rs:24:1
+  --> $DIR/const-and-non-const-impl.rs:23:1
    |
 LL | impl std::ops::Add for Int {
    | -------------------------- first implementation here
@@ -17,7 +17,7 @@ LL | impl const std::ops::Add for Int {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Int`
 
 error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
-  --> $DIR/const-and-non-const-impl.rs:6:1
+  --> $DIR/const-and-non-const-impl.rs:5:1
    |
 LL | impl const std::ops::Add for i32 {
    | ^^^^^^^^^^^-------------^^^^^---
index fc85e98ef532689d924d1ec0741c7458935cde7e..3a7074163523b0dab00be8a7c79ec3ebf47031d3 100644 (file)
@@ -1,4 +1,3 @@
-#![allow(incomplete_features)]
 #![feature(const_trait_impl)]
 
 struct S;
index c6c78c7d1e89564873908c5bb6cff24f0334bd01..aaec67161a6afe241b7eaee1a55cc7ec3b7f6f7e 100644 (file)
@@ -1,5 +1,5 @@
 error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-  --> $DIR/const-check-fns-in-const-impl.rs:12:16
+  --> $DIR/const-check-fns-in-const-impl.rs:11:16
    |
 LL |     fn foo() { non_const() }
    |                ^^^^^^^^^^^
index d08c01750c37949948a2576bb46dd1c12dd2f024..7a0db9c98ea61d53d8b7135b3429b114218515a2 100644 (file)
@@ -1,6 +1,5 @@
 #![feature(const_trait_impl)]
 #![feature(const_fn_trait_bound)] // FIXME is this needed?
-#![allow(incomplete_features)]
 
 trait ConstDefaultFn: Sized {
     fn b(self);
index d52e83609489ba605741d748aaa211c10f9d82d7..63e4095af29437295bb6bc8a63b28326bba2a94b 100644 (file)
@@ -1,5 +1,5 @@
 error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-  --> $DIR/const-default-method-bodies.rs:26:5
+  --> $DIR/const-default-method-bodies.rs:25:5
    |
 LL |     NonConstImpl.a();
    |     ^^^^^^^^^^^^^^^^
index 936c90e88aaeecdd4d75270ee0865e98c0cce5b7..bed4e9fd1e610acf74fe18e2b6aefe0626104ecb 100644 (file)
@@ -1,5 +1,4 @@
 #![feature(const_trait_impl)]
-#![allow(incomplete_features)]
 
 struct Foo;
 
index 612511a479956d388544f40d7ba023fb7c77d1c4..603f6b7d2835e88b373b31d2a6b2d8b4710d6bed 100644 (file)
@@ -1,5 +1,5 @@
 error: expected identifier, found keyword `impl`
-  --> $DIR/const-impl-norecover.rs:6:7
+  --> $DIR/const-impl-norecover.rs:5:7
    |
 LL | const impl Foo {
    |       ^^^^ expected identifier, found keyword
index fd3dd2cef9d1c9c8ba95d8d6e51ec9fceff8f1e1..470c653dd47019b5b1a978c766e37c5303d8f540 100644 (file)
@@ -1,5 +1,4 @@
 #![feature(const_trait_impl)]
-#![allow(incomplete_features)]
 
 trait Foo {}
 
index 84fb619dc9648815d6c83b6c3bb4054c3838e787..f815389ff01c3cacfbd480fab870243544426b9a 100644 (file)
@@ -1,5 +1,5 @@
 error: expected identifier, found keyword `impl`
-  --> $DIR/const-impl-recovery.rs:6:7
+  --> $DIR/const-impl-recovery.rs:5:7
    |
 LL | const impl Foo for i32 {}
    |       ^^^^ expected identifier, found keyword
@@ -10,7 +10,7 @@ LL | impl const Foo for i32 {}
    |--    ^^^^^
 
 error: expected identifier, found keyword `impl`
-  --> $DIR/const-impl-recovery.rs:10:7
+  --> $DIR/const-impl-recovery.rs:9:7
    |
 LL | const impl<T: Foo> Bar for T {}
    |       ^^^^ expected identifier, found keyword
index 30baa38547580cdd35bc0991803c14f9aeef50e5..3f553a8ee70793d26d3a4f48d1c51fdb1a6974fb 100644 (file)
@@ -1,5 +1,5 @@
 error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-  --> $DIR/cross-crate.rs:16:5
+  --> $DIR/cross-crate.rs:15:5
    |
 LL |     NonConst.func();
    |     ^^^^^^^^^^^^^^^
index c28ff638958419148dfa2ae1bd73b30a68b34871..4bd3359947d948729c35569d4be29bfc912ad8db 100644 (file)
@@ -1,6 +1,5 @@
 // revisions: stock gated
 #![cfg_attr(gated, feature(const_trait_impl))]
-#![allow(incomplete_features)]
 
 // aux-build: cross-crate.rs
 extern crate cross_crate;
index e6a86f251ca3c31d2028c54617c184860c391e47..9908f47a7b28858b0c717963e2cb936f74e35380 100644 (file)
@@ -1,11 +1,11 @@
 error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-  --> $DIR/cross-crate.rs:16:5
+  --> $DIR/cross-crate.rs:15:5
    |
 LL |     NonConst.func();
    |     ^^^^^^^^^^^^^^^
 
 error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-  --> $DIR/cross-crate.rs:18:5
+  --> $DIR/cross-crate.rs:17:5
    |
 LL |     Const.func();
    |     ^^^^^^^^^^^^
index 4c630d33c55163b7eca430c391c21e601613f246..af4d3909e40515d083c322f20741e7b8f3256aff 100644 (file)
@@ -1,5 +1,5 @@
 error: fatal error triggered by #[rustc_error]
-  --> $DIR/feature-gate.rs:14:1
+  --> $DIR/feature-gate.rs:13:1
    |
 LL | fn main() {}
    | ^^^^^^^^^
index d9772431941d373ab5bab07cad3741c6203971e4..7bac72e1bb6fb608c2854ac5a3f89f7d391efed4 100644 (file)
@@ -2,7 +2,6 @@
 // gate-test-const_trait_impl
 
 #![cfg_attr(gated, feature(const_trait_impl))]
-#![allow(incomplete_features)]
 #![feature(rustc_attrs)]
 
 struct S;
index 724090e49cd415ed8e3d2e0ea502eb159a7e67e1..91a8bb578a829ecdd7f0ec9f8312a745fc5b9acf 100644 (file)
@@ -1,5 +1,5 @@
 error[E0658]: const trait impls are experimental
-  --> $DIR/feature-gate.rs:10:6
+  --> $DIR/feature-gate.rs:9:6
    |
 LL | impl const T for S {}
    |      ^^^^^
index d3680820312605d8a57a14353e830254cfa405b4..91c3d2d5920bc6675b625f86ace4209f335d169a 100644 (file)
@@ -1,6 +1,5 @@
 // run-pass
 
-#![allow(incomplete_features)]
 #![feature(const_trait_impl)]
 #![feature(const_fn_trait_bound)]
 
index ad14dd62bc7b5b7c021522214501e23283bd6e11..80a4442de2cba7332df2d0d4ce4787f02d9db52f 100644 (file)
@@ -1,7 +1,6 @@
 // Regression test for #69615.
 
 #![feature(const_trait_impl)]
-#![allow(incomplete_features)]
 
 pub trait MyTrait {
     fn method(&self) -> Option<()>;
index 8c76d7eb597b6a29c08e42820c934acc1a739bc1..3ec329cba1eb2a027bbd07794078ae136b6e5079 100644 (file)
@@ -1,5 +1,5 @@
 error[E0744]: `?` is not allowed in a `const fn`
-  --> $DIR/hir-const-check.rs:12:9
+  --> $DIR/hir-const-check.rs:11:9
    |
 LL |         Some(())?;
    |         ^^^^^^^^^
index def7c34b4e50e2e4cf6435bad316f251e0802ff2..59de9e95719449df6623b7ce10446fdf58a3fe2e 100644 (file)
@@ -1,5 +1,4 @@
 #![feature(const_trait_impl)]
-#![allow(incomplete_features)]
 
 trait Tr {
     fn req(&self);
index eb7f899b4dee212bed27ba19d81da55fb4c2ba78..5301e0ad12ef1329c8360f8417bf788071117387 100644 (file)
@@ -1,5 +1,5 @@
 error: const trait implementations may not use non-const default functions
-  --> $DIR/impl-with-default-fn.rs:18:1
+  --> $DIR/impl-with-default-fn.rs:17:1
    |
 LL | / impl const Tr for S {
 LL | |     fn req(&self) {}
@@ -9,7 +9,7 @@ LL | | }
    = note: `prov` not implemented
 
 error: const trait implementations may not use non-const default functions
-  --> $DIR/impl-with-default-fn.rs:33:1
+  --> $DIR/impl-with-default-fn.rs:32:1
    |
 LL | / impl const Tr for u32 {
 LL | |     fn req(&self) {}
@@ -20,7 +20,7 @@ LL | | }
    = note: `prov` not implemented
 
 error[E0046]: not all trait items implemented, missing: `req`
-  --> $DIR/impl-with-default-fn.rs:27:1
+  --> $DIR/impl-with-default-fn.rs:26:1
    |
 LL |     fn req(&self);
    |     -------------- `req` from trait
index 454fde34a2cdca25aee5f58945b45ba4c88d204a..6b54a9eab5205bb904bfb9474b30d7f44f773962 100644 (file)
@@ -1,4 +1,3 @@
-#![allow(incomplete_features)]
 #![feature(allow_internal_unstable)]
 #![feature(const_add)]
 #![feature(const_trait_impl)]
index 5b2ebccef1c6dd42c5654bafd35d0e1653d5eb31..86b685959a6cbcc77f0239a165cbed1349f82ee8 100644 (file)
@@ -1,5 +1,5 @@
 error: trait methods cannot be stable const fn
-  --> $DIR/stability.rs:14:5
+  --> $DIR/stability.rs:13:5
    |
 LL | /     fn sub(self, rhs: Self) -> Self {
 LL | |
@@ -8,7 +8,7 @@ LL | |     }
    | |_____^
 
 error: `<Int as Add>::add` is not yet stable as a const fn
-  --> $DIR/stability.rs:32:5
+  --> $DIR/stability.rs:31:5
    |
 LL |     Int(1i32) + Int(2i32)
    |     ^^^^^^^^^^^^^^^^^^^^^
index 354d48d630f7b6c14a6d0b6e2a78005c8af5b91d..82a1275bffaedf89ee825a02233a49d1985306f9 100644 (file)
@@ -3,7 +3,6 @@
 
 #![feature(const_trait_bound_opt_out)]
 #![feature(const_trait_impl)]
-#![allow(incomplete_features)]
 
 // For now, this parses since an error does not occur until AST lowering.
 impl ?const T {}
diff --git a/src/test/ui/suggestions/suggest-full-enum-variant-for-local-module.rs b/src/test/ui/suggestions/suggest-full-enum-variant-for-local-module.rs
new file mode 100644 (file)
index 0000000..1dfc078
--- /dev/null
@@ -0,0 +1,10 @@
+mod option {
+    pub enum O<T> {
+        Some(T),
+        None,
+    }
+}
+
+fn main() {
+    let _: option::O<()> = (); //~ ERROR 9:28: 9:30: mismatched types [E0308]
+}
diff --git a/src/test/ui/suggestions/suggest-full-enum-variant-for-local-module.stderr b/src/test/ui/suggestions/suggest-full-enum-variant-for-local-module.stderr
new file mode 100644 (file)
index 0000000..22a0ce1
--- /dev/null
@@ -0,0 +1,16 @@
+error[E0308]: mismatched types
+  --> $DIR/suggest-full-enum-variant-for-local-module.rs:9:28
+   |
+LL |     let _: option::O<()> = ();
+   |            -------------   ^^
+   |            |               |
+   |            |               expected enum `O`, found `()`
+   |            |               help: try using a variant of the expected enum: `option::O::Some(())`
+   |            expected due to this
+   |
+   = note:   expected enum `O<()>`
+           found unit type `()`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
index b09833f3ed01435b83f9d30b32c25379dd70d0a0..0c37399df8dd21fa70195fd42074bfc09447cfcd 100644 (file)
@@ -12,6 +12,7 @@ trait Output<'a> {}
 impl<'a> Output<'a> for &'a str {}
 
 fn cool_fn<'a>(arg: &'a str) -> OpaqueOutputImpl<'a> {
+    //~^ ERROR: concrete type differs from previous defining opaque type use
     let out: OpaqueOutputImpl<'a> = arg;
     arg
 }
index 361d66866ef8b0bece6e1a3a5f7d25e51e552c50..233c996340d844fe57b47485af643d0308d3a703 100644 (file)
@@ -10,6 +10,18 @@ note: hidden type `&'<empty> str` captures lifetime smaller than the function bo
 LL | type OpaqueOutputImpl<'a> = impl Output<'a> + 'a;
    |                             ^^^^^^^^^^^^^^^^^^^^
 
+error: concrete type differs from previous defining opaque type use
+  --> $DIR/issue-85113.rs:14:1
+   |
+LL | fn cool_fn<'a>(arg: &'a str) -> OpaqueOutputImpl<'a> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&'<empty> str`, got `&'a str`
+   |
+note: previous use here
+  --> $DIR/issue-85113.rs:14:1
+   |
+LL | fn cool_fn<'a>(arg: &'a str) -> OpaqueOutputImpl<'a> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 error[E0477]: the type `&'<empty> str` does not fulfill the required lifetime
   --> $DIR/issue-85113.rs:5:29
    |
@@ -42,7 +54,7 @@ LL | type OpaqueOutputImpl<'a> = impl Output<'a> + 'a;
    = note: expected `Output<'a>`
               found `Output<'_>`
 
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
 
 Some errors have detailed explanations: E0477, E0495, E0700.
 For more information about an error, try `rustc --explain E0477`.
index 9081b7929d935676221fddc052407f4c487a4592..a0eb7d10bd94a0ab61200d679765757bf4369659 100644 (file)
@@ -1,8 +1,8 @@
 error[E0277]: the trait bound `(): Foo` is not satisfied
-  --> $DIR/wf-foreign-fn-decl-ret.rs:11:5
+  --> $DIR/wf-foreign-fn-decl-ret.rs:11:12
    |
 LL |     pub fn lint_me() -> <() as Foo>::Assoc;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()`
+   |            ^^^^^^^ the trait `Foo` is not implemented for `()`
 
 error[E0277]: the trait bound `u32: Unsatisfied` is not satisfied
   --> $DIR/wf-foreign-fn-decl-ret.rs:14:32
index 3ebb5f15a940810f250b68821149387af583a79e..27277d966b3cfa454d6dea7f724cb961c036251c 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 3ebb5f15a940810f250b68821149387af583a79e
+Subproject commit 27277d966b3cfa454d6dea7f724cb961c036251c
index 421614057c577771c457fc6e129f071ed68e2de3..9f89f74e7450361f45def111ae1aeea75ef3e328 100644 (file)
@@ -2744,6 +2744,7 @@ Released 2018-09-13
 [`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
 [`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
 [`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer
+[`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex
 [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
 [`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
 [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
@@ -2797,6 +2798,7 @@ Released 2018-09-13
 [`string_from_utf8_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_from_utf8_as_bytes
 [`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes
 [`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string
+[`strlen_on_c_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#strlen_on_c_strings
 [`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools
 [`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops
 [`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
index 8097a1c832613b7c4a1a5b23a953bf8c55315318..21c7b2448cec8dffdc714d22e628b1e1d801e6df 100644 (file)
@@ -2,8 +2,7 @@
 
 use std::path::PathBuf;
 
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::run_lints;
+use clippy_utils::{diagnostics::span_lint, is_lint_allowed};
 use rustc_hir::{hir_id::CRATE_HIR_ID, Crate};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -85,7 +84,7 @@ fn is_empty_vec(value: &[String]) -> bool {
 
 impl LateLintPass<'_> for CargoCommonMetadata {
     fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
-        if !run_lints(cx, &[CARGO_COMMON_METADATA], CRATE_HIR_ID) {
+        if is_lint_allowed(cx, CARGO_COMMON_METADATA, CRATE_HIR_ID) {
             return;
         }
 
index 376a14b8181ff6eb739fbb6f9040f377949246c1..9cbcde597686e8c212c45ce95d4e28ad954624cf 100644 (file)
@@ -2,7 +2,7 @@
 use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
 use clippy_utils::{
     both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, is_else_clause,
-    run_lints, search_same, ContainsName, SpanlessEq, SpanlessHash,
+    is_lint_allowed, search_same, ContainsName, SpanlessEq, SpanlessHash,
 };
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashSet;
     ///
     /// **Why is this bad?** Duplicate code is less maintainable.
     ///
-    /// **Known problems:** Hopefully none.
+    /// **Known problems:**
+    /// * The lint doesn't check if the moved expressions modify values that are beeing used in
+    ///   the if condition. The suggestion can in that case modify the behavior of the program.
+    ///   See [rust-clippy#7452](https://github.com/rust-lang/rust-clippy/issues/7452)
     ///
     /// **Example:**
     /// ```ignore
@@ -337,8 +340,8 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<
             if block_expr_eq;
             if l_stmts.len() == r_stmts.len();
             if l_stmts.len() == current_start_eq;
-            if run_lints(cx, &[IF_SAME_THEN_ELSE], win[0].hir_id);
-            if run_lints(cx, &[IF_SAME_THEN_ELSE], win[1].hir_id);
+            if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win[0].hir_id);
+            if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win[1].hir_id);
             then {
                 span_lint_and_note(
                     cx,
@@ -358,8 +361,7 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<
         expr_eq &= block_expr_eq;
     }
 
-    let has_expr = blocks[0].expr.is_some();
-    if has_expr && !expr_eq {
+    if !expr_eq {
         end_eq = 0;
     }
 
index a125376bffa9fa405089ba1cc483e3543496aee9..e719a1b0abf8744dd2f483d542c9c017e96e6b67 100644 (file)
@@ -1,5 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet;
+use clippy_utils::numeric_literal;
+use clippy_utils::source::snippet_opt;
 use if_chain::if_chain;
 use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
 use rustc_errors::Applicability;
@@ -78,16 +79,25 @@ fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) {
                 if let Some(ty_bound) = self.ty_bounds.last();
                 if matches!(lit.node,
                             LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
-                if !ty_bound.is_integral();
+                if !ty_bound.is_numeric();
                 then {
-                    let suffix = match lit_ty.kind() {
-                        ty::Int(IntTy::I32) => "i32",
-                        ty::Float(FloatTy::F64) => "f64",
+                    let (suffix, is_float) = match lit_ty.kind() {
+                        ty::Int(IntTy::I32) => ("i32", false),
+                        ty::Float(FloatTy::F64) => ("f64", true),
                         // Default numeric fallback never results in other types.
                         _ => return,
                     };
 
-                    let sugg = format!("{}_{}", snippet(self.cx, lit.span, ""), suffix);
+                    let src = if let Some(src) = snippet_opt(self.cx, lit.span) {
+                        src
+                    } else {
+                        match lit.node {
+                            LitKind::Int(src, _) => format!("{}", src),
+                            LitKind::Float(src, _) => format!("{}", src),
+                            _ => return,
+                        }
+                    };
+                    let sugg = numeric_literal::format(&src, Some(suffix), is_float);
                     span_lint_and_sugg(
                         self.cx,
                         DEFAULT_NUMERIC_FALLBACK,
@@ -219,10 +229,10 @@ enum TyBound<'tcx> {
 }
 
 impl<'tcx> TyBound<'tcx> {
-    fn is_integral(self) -> bool {
+    fn is_numeric(self) -> bool {
         match self {
             TyBound::Any => true,
-            TyBound::Ty(t) => t.is_integral(),
+            TyBound::Ty(t) => t.is_numeric(),
             TyBound::Nothing => false,
         }
     }
index 1415f8e235a7e8936ef370fc3613d67a4c05e2a1..682003f9c2c4c99d71d8bccb329b9409ed48d0bf 100644 (file)
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::peel_mid_ty_refs;
-use clippy_utils::{get_parent_node, in_macro, is_allowed};
+use clippy_utils::{get_parent_node, in_macro, is_lint_allowed};
 use rustc_ast::util::parser::PREC_PREFIX;
 use rustc_errors::Applicability;
 use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp};
@@ -107,7 +107,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 
                 match kind {
                     RefOp::Method(target_mut)
-                        if !is_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
+                        if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
                             && is_linted_explicit_deref_position(parent, expr.hir_id, expr.span) =>
                     {
                         self.state = Some((
index 3ac20fd9849eef656fb2cccb4ea2f7f53c426fb7..7aafaf71383011a93c298bf5d7b5931616264344 100644 (file)
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then};
 use clippy_utils::paths;
 use clippy_utils::ty::{implements_trait, is_copy};
-use clippy_utils::{get_trait_def_id, is_allowed, is_automatically_derived, match_def_path};
+use clippy_utils::{get_trait_def_id, is_automatically_derived, is_lint_allowed, match_def_path};
 use if_chain::if_chain;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, NestedVisitorMap, Visitor};
@@ -362,7 +362,7 @@ fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
         if let ty::Adt(def, _) = ty.kind();
         if let Some(local_def_id) = def.did.as_local();
         let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
-        if !is_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
+        if !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
         if cx.tcx.inherent_impls(def.did)
             .iter()
             .map(|imp_did| item_from_def_id(cx, *imp_did))
index d6e3e92fc5eb6b76caf1c6575cfd889f7f4d2d07..0c19988a975a85b156031f4616084317be3c6059 100644 (file)
@@ -550,7 +550,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
             FootnoteReference(text) | Text(text) => {
                 let (begin, span) = get_current_span(spans, range.start);
                 paragraph_span = paragraph_span.with_hi(span.hi());
-                ticks_unbalanced |= text.contains('`');
+                ticks_unbalanced |= text.contains('`') && !in_code;
                 if Some(&text) == in_link.as_ref() || ticks_unbalanced {
                     // Probably a link of the form `<http://example.com>`
                     // Which are represented as a link to "http://example.com" with
@@ -595,7 +595,7 @@ fn has_needless_main(code: String, edition: Edition) -> bool {
                 let handler = Handler::with_emitter(false, None, box emitter);
                 let sess = ParseSess::with_span_handler(handler, sm);
 
-                let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) {
+                let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
                     Ok(p) => p,
                     Err(errs) => {
                         for mut err in errs {
@@ -653,7 +653,10 @@ fn has_needless_main(code: String, edition: Edition) -> bool {
     // Because of the global session, we need to create a new session in a different thread with
     // the edition we need.
     let text = text.to_owned();
-    if thread::spawn(move || has_needless_main(text, edition)).join().expect("thread::spawn failed") {
+    if thread::spawn(move || has_needless_main(text, edition))
+        .join()
+        .expect("thread::spawn failed")
+    {
         span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
     }
 }
index 3581ab41906f414f26e97311c5d8360c2c855f21..5f400d079da2f1d7b5277e8450703003a6d2a0cc 100644 (file)
@@ -11,7 +11,7 @@
 use rustc_span::symbol::kw;
 use rustc_target::abi::LayoutOf;
 use rustc_target::spec::abi::Abi;
-use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
+use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 
 #[derive(Copy, Clone)]
 pub struct BoxedLocal {
@@ -133,13 +133,10 @@ fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool {
 }
 
 impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
-    fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, mode: ConsumeMode) {
+    fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
         if cmt.place.projections.is_empty() {
             if let PlaceBase::Local(lid) = cmt.place.base {
-                if let ConsumeMode::Move = mode {
-                    // moved out or in. clearly can't be localized
-                    self.set.remove(&lid);
-                }
+                self.set.remove(&lid);
                 let map = &self.cx.tcx.hir();
                 if let Some(Node::Binding(_)) = map.find(cmt.hir_id) {
                     if self.set.contains(&lid) {
index 8d066f305ee85e44b8dab91d14b04516fe195f87..667eb8eb283bbb09997d47f8589bd86118e53e52 100644 (file)
@@ -1,15 +1,16 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::higher;
 use clippy_utils::higher::VecArgs;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{implements_trait, type_is_unsafe_function};
+use clippy_utils::usage::UsedAfterExprVisitor;
+use clippy_utils::{get_enclosing_loop_or_closure, higher};
 use clippy_utils::{is_adjusted, iter_input_pats};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{def_id, Expr, ExprKind, Param, PatKind, QPath};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, ClosureKind, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
 declare_clippy_lint! {
@@ -86,7 +87,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
     }
 }
 
-fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) {
+fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
     if let ExprKind::Closure(_, decl, eid, _, _) = expr.kind {
         let body = cx.tcx.hir().body(eid);
         let ex = &body.value;
@@ -131,7 +132,18 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) {
 
             then {
                 span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
-                    if let Some(snippet) = snippet_opt(cx, caller.span) {
+                    if let Some(mut snippet) = snippet_opt(cx, caller.span) {
+                        if_chain! {
+                            if let ty::Closure(_, substs) = fn_ty.kind();
+                            if let ClosureKind::FnMut = substs.as_closure().kind();
+                            if UsedAfterExprVisitor::is_found(cx, caller)
+                                || get_enclosing_loop_or_closure(cx.tcx, expr).is_some();
+
+                            then {
+                                // Mutable closure is used after current expr; we cannot consume it.
+                                snippet = format!("&mut {}", snippet);
+                            }
+                        }
                         diag.span_suggestion(
                             expr.span,
                             "replace the closure with the function itself",
index da4936ff25b6d22d19e38f502e8d32c106cce991..66724294804a8414d1dfbbda3ac5050051fc4349 100644 (file)
@@ -1,9 +1,9 @@
-use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::higher::FormatArgsExpn;
 use clippy_utils::{is_expn_of, match_function_call, paths};
 use if_chain::if_chain;
-use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
@@ -34,29 +34,26 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if_chain! {
             // match call to unwrap
-            if let ExprKind::MethodCall(unwrap_fun, _, unwrap_args, _) = expr.kind;
+            if let ExprKind::MethodCall(unwrap_fun, _, [write_call], _) = expr.kind;
             if unwrap_fun.ident.name == sym::unwrap;
             // match call to write_fmt
-            if !unwrap_args.is_empty();
-            if let ExprKind::MethodCall(write_fun, _, write_args, _) =
-                unwrap_args[0].kind;
+            if let ExprKind::MethodCall(write_fun, _, [write_recv, write_arg], _) = write_call.kind;
             if write_fun.ident.name == sym!(write_fmt);
             // match calls to std::io::stdout() / std::io::stderr ()
-            if !write_args.is_empty();
-            if let Some(dest_name) = if match_function_call(cx, &write_args[0], &paths::STDOUT).is_some() {
+            if let Some(dest_name) = if match_function_call(cx, write_recv, &paths::STDOUT).is_some() {
                 Some("stdout")
-            } else if match_function_call(cx, &write_args[0], &paths::STDERR).is_some() {
+            } else if match_function_call(cx, write_recv, &paths::STDERR).is_some() {
                 Some("stderr")
             } else {
                 None
             };
+            if let Some(format_args) = FormatArgsExpn::parse(write_arg);
             then {
-                let write_span = unwrap_args[0].span;
                 let calling_macro =
                     // ordering is important here, since `writeln!` uses `write!` internally
-                    if is_expn_of(write_span, "writeln").is_some() {
+                    if is_expn_of(write_call.span, "writeln").is_some() {
                         Some("writeln")
-                    } else if is_expn_of(write_span, "write").is_some() {
+                    } else if is_expn_of(write_call.span, "write").is_some() {
                         Some("write")
                     } else {
                         None
@@ -70,82 +67,40 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 // We need to remove the last trailing newline from the string because the
                 // underlying `fmt::write` function doesn't know whether `println!` or `print!` was
                 // used.
-                if let Some(mut write_output) = write_output_string(write_args) {
+                let (used, sugg_mac) = if let Some(macro_name) = calling_macro {
+                    (
+                        format!("{}!({}(), ...)", macro_name, dest_name),
+                        macro_name.replace("write", "print"),
+                    )
+                } else {
+                    (
+                        format!("{}().write_fmt(...)", dest_name),
+                        "print".into(),
+                    )
+                };
+                let msg = format!("use of `{}.unwrap()`", used);
+                if let [write_output] = *format_args.format_string_symbols {
+                    let mut write_output = write_output.to_string();
                     if write_output.ends_with('\n') {
                         write_output.pop();
                     }
 
-                    if let Some(macro_name) = calling_macro {
-                        span_lint_and_sugg(
-                            cx,
-                            EXPLICIT_WRITE,
-                            expr.span,
-                            &format!(
-                                "use of `{}!({}(), ...).unwrap()`",
-                                macro_name,
-                                dest_name
-                            ),
-                            "try this",
-                            format!("{}{}!(\"{}\")", prefix, macro_name.replace("write", "print"), write_output.escape_default()),
-                            Applicability::MachineApplicable
-                        );
-                    } else {
-                        span_lint_and_sugg(
-                            cx,
-                            EXPLICIT_WRITE,
-                            expr.span,
-                            &format!("use of `{}().write_fmt(...).unwrap()`", dest_name),
-                            "try this",
-                            format!("{}print!(\"{}\")", prefix, write_output.escape_default()),
-                            Applicability::MachineApplicable
-                        );
-                    }
+                    let sugg = format!("{}{}!(\"{}\")", prefix, sugg_mac, write_output.escape_default());
+                    span_lint_and_sugg(
+                        cx,
+                        EXPLICIT_WRITE,
+                        expr.span,
+                        &msg,
+                        "try this",
+                        sugg,
+                        Applicability::MachineApplicable
+                    );
                 } else {
                     // We don't have a proper suggestion
-                    if let Some(macro_name) = calling_macro {
-                        span_lint(
-                            cx,
-                            EXPLICIT_WRITE,
-                            expr.span,
-                            &format!(
-                                "use of `{}!({}(), ...).unwrap()`. Consider using `{}{}!` instead",
-                                macro_name,
-                                dest_name,
-                                prefix,
-                                macro_name.replace("write", "print")
-                            )
-                        );
-                    } else {
-                        span_lint(
-                            cx,
-                            EXPLICIT_WRITE,
-                            expr.span,
-                            &format!("use of `{}().write_fmt(...).unwrap()`. Consider using `{}print!` instead", dest_name, prefix),
-                        );
-                    }
+                    let help = format!("consider using `{}{}!` instead", prefix, sugg_mac);
+                    span_lint_and_help(cx, EXPLICIT_WRITE, expr.span, &msg, None, &help);
                 }
-
             }
         }
     }
 }
-
-// Extract the output string from the given `write_args`.
-fn write_output_string(write_args: &[Expr<'_>]) -> Option<String> {
-    if_chain! {
-        // Obtain the string that should be printed
-        if write_args.len() > 1;
-        if let ExprKind::Call(_, output_args) = write_args[1].kind;
-        if !output_args.is_empty();
-        if let ExprKind::AddrOf(BorrowKind::Ref, _, output_string_expr) = output_args[0].kind;
-        if let ExprKind::Array(string_exprs) = output_string_expr.kind;
-        // we only want to provide an automatic suggestion for simple (non-format) strings
-        if string_exprs.len() == 1;
-        if let ExprKind::Lit(ref lit) = string_exprs[0].kind;
-        if let LitKind::Str(ref write_output, _) = lit.node;
-        then {
-            return Some(write_output.to_string())
-        }
-    }
-    None
-}
index c2b055ed6488e4e9bdeb0d34e1980a16c8e09fdc..ca3490d8edad98c95df69f46abab20acf2335791 100644 (file)
@@ -1,17 +1,16 @@
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::paths;
-use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher::FormatExpn;
+use clippy_utils::last_path_segment;
+use clippy_utils::source::{snippet_opt, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_expn_of, last_path_segment, match_def_path, match_function_call};
 use if_chain::if_chain;
-use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
-use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, MatchSource, PatKind};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_hir::{BorrowKind, Expr, ExprKind, QPath};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::symbol::kw;
+use rustc_span::{sym, Span};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for the use of `format!("string literal with no
 
 impl<'tcx> LateLintPass<'tcx> for UselessFormat {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        let span = match is_expn_of(expr.span, "format") {
-            Some(s) if !s.from_expansion() => s,
+        let FormatExpn { call_site, format_args } = match FormatExpn::parse(expr) {
+            Some(e) if !e.call_site.from_expansion() => e,
             _ => return,
         };
 
-        // Operate on the only argument of `alloc::fmt::format`.
-        if let Some(sugg) = on_new_v1(cx, expr) {
-            span_useless_format(cx, span, "consider using `.to_string()`", sugg);
-        } else if let Some(sugg) = on_new_v1_fmt(cx, expr) {
-            span_useless_format(cx, span, "consider using `.to_string()`", sugg);
-        }
-    }
-}
-
-fn span_useless_format<T: LintContext>(cx: &T, span: Span, help: &str, mut sugg: String) {
-    let to_replace = span.source_callsite();
-
-    // The callsite span contains the statement semicolon for some reason.
-    let snippet = snippet(cx, to_replace, "..");
-    if snippet.ends_with(';') {
-        sugg.push(';');
-    }
-
-    span_lint_and_then(cx, USELESS_FORMAT, span, "useless use of `format!`", |diag| {
-        diag.span_suggestion(
-            to_replace,
-            help,
-            sugg,
-            Applicability::MachineApplicable, // snippet
-        );
-    });
-}
-
-fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) -> Option<String> {
-    if_chain! {
-        if let ExprKind::AddrOf(BorrowKind::Ref, _, format_args) = expr.kind;
-        if let ExprKind::Array(elems) = arms[0].body.kind;
-        if elems.len() == 1;
-        if let Some(args) = match_function_call(cx, &elems[0], &paths::FMT_ARGUMENTV1_NEW);
-        // matches `core::fmt::Display::fmt`
-        if args.len() == 2;
-        if let ExprKind::Path(ref qpath) = args[1].kind;
-        if let Some(did) = cx.qpath_res(qpath, args[1].hir_id).opt_def_id();
-        if match_def_path(cx, did, &paths::DISPLAY_FMT_METHOD);
-        // check `(arg0,)` in match block
-        if let PatKind::Tuple(pats, None) = arms[0].pat.kind;
-        if pats.len() == 1;
-        then {
-            let ty = cx.typeck_results().pat_ty(pats[0]).peel_refs();
-            if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym::string_type) {
-                return None;
-            }
-            if let ExprKind::Lit(ref lit) = format_args.kind {
-                if let LitKind::Str(ref s, _) = lit.node {
-                    return Some(format!("{:?}.to_string()", s.as_str()));
+        let mut applicability = Applicability::MachineApplicable;
+        if format_args.value_args.is_empty() {
+            if_chain! {
+                if let [e] = &*format_args.format_string_parts;
+                if let ExprKind::Lit(lit) = &e.kind;
+                if let Some(s_src) = snippet_opt(cx, lit.span);
+                then {
+                    // Simulate macro expansion, converting {{ and }} to { and }.
+                    let s_expand = s_src.replace("{{", "{").replace("}}", "}");
+                    let sugg = format!("{}.to_string()", s_expand);
+                    span_useless_format(cx, call_site, sugg, applicability);
                 }
-            } else {
-                let sugg = Sugg::hir(cx, format_args, "<arg>");
-                if let ExprKind::MethodCall(path, _, _, _) = format_args.kind {
-                    if path.ident.name == sym!(to_string) {
-                        return Some(format!("{}", sugg));
-                    }
-                } else if let ExprKind::Binary(..) = format_args.kind {
-                    return Some(format!("{}", sugg));
+            }
+        } else if let [value] = *format_args.value_args {
+            if_chain! {
+                if format_args.format_string_symbols == [kw::Empty];
+                if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
+                    ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::string_type, adt.did),
+                    ty::Str => true,
+                    _ => false,
+                };
+                if format_args.args.iter().all(|e| is_display_arg(e));
+                if format_args.fmt_expr.map_or(true, |e| check_unformatted(e));
+                then {
+                    let is_new_string = match value.kind {
+                        ExprKind::Binary(..) => true,
+                        ExprKind::MethodCall(path, ..) => path.ident.name.as_str() == "to_string",
+                        _ => false,
+                    };
+                    let sugg = if is_new_string {
+                        snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned()
+                    } else {
+                        let sugg = Sugg::hir_with_applicability(cx, value, "<arg>", &mut applicability);
+                        format!("{}.to_string()", sugg.maybe_par())
+                    };
+                    span_useless_format(cx, call_site, sugg, applicability);
                 }
-                return Some(format!("{}.to_string()", sugg.maybe_par()));
             }
-        }
+        };
     }
-    None
 }
 
-fn on_new_v1<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<String> {
-    if_chain! {
-        if let Some(args) = match_function_call(cx, expr, &paths::FMT_ARGUMENTS_NEW_V1);
-        if args.len() == 2;
-        // Argument 1 in `new_v1()`
-        if let ExprKind::AddrOf(BorrowKind::Ref, _, arr) = args[0].kind;
-        if let ExprKind::Array(pieces) = arr.kind;
-        if pieces.len() == 1;
-        if let ExprKind::Lit(ref lit) = pieces[0].kind;
-        if let LitKind::Str(ref s, _) = lit.node;
-        // Argument 2 in `new_v1()`
-        if let ExprKind::AddrOf(BorrowKind::Ref, _, arg1) = args[1].kind;
-        if let ExprKind::Match(matchee, arms, MatchSource::Normal) = arg1.kind;
-        if arms.len() == 1;
-        if let ExprKind::Tup(tup) = matchee.kind;
-        then {
-            // `format!("foo")` expansion contains `match () { () => [], }`
-            if tup.is_empty() {
-                if let Some(s_src) = snippet_opt(cx, lit.span) {
-                    // Simulate macro expansion, converting {{ and }} to { and }.
-                    let s_expand = s_src.replace("{{", "{").replace("}}", "}");
-                    return Some(format!("{}.to_string()", s_expand));
-                }
-            } else if s.as_str().is_empty() {
-                return on_argumentv1_new(cx, &tup[0], arms);
-            }
-        }
+fn span_useless_format(cx: &LateContext<'_>, span: Span, mut sugg: String, mut applicability: Applicability) {
+    // The callsite span contains the statement semicolon for some reason.
+    if snippet_with_applicability(cx, span, "..", &mut applicability).ends_with(';') {
+        sugg.push(';');
     }
-    None
+
+    span_lint_and_sugg(
+        cx,
+        USELESS_FORMAT,
+        span,
+        "useless use of `format!`",
+        "consider using `.to_string()`",
+        sugg,
+        applicability,
+    );
 }
 
-fn on_new_v1_fmt<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<String> {
+fn is_display_arg(expr: &Expr<'_>) -> bool {
     if_chain! {
-        if let Some(args) = match_function_call(cx, expr, &paths::FMT_ARGUMENTS_NEW_V1_FORMATTED);
-        if args.len() == 3;
-        if check_unformatted(&args[2]);
-        // Argument 1 in `new_v1_formatted()`
-        if let ExprKind::AddrOf(BorrowKind::Ref, _, arr) = args[0].kind;
-        if let ExprKind::Array(pieces) = arr.kind;
-        if pieces.len() == 1;
-        if let ExprKind::Lit(ref lit) = pieces[0].kind;
-        if let LitKind::Str(..) = lit.node;
-        // Argument 2 in `new_v1_formatted()`
-        if let ExprKind::AddrOf(BorrowKind::Ref, _, arg1) = args[1].kind;
-        if let ExprKind::Match(matchee, arms, MatchSource::Normal) = arg1.kind;
-        if arms.len() == 1;
-        if let ExprKind::Tup(tup) = matchee.kind;
-        then {
-            return on_argumentv1_new(cx, &tup[0], arms);
-        }
+        if let ExprKind::Call(_, [_, fmt]) = expr.kind;
+        if let ExprKind::Path(QPath::Resolved(_, path)) = fmt.kind;
+        if let [.., t, _] = path.segments;
+        if t.ident.name.as_str() == "Display";
+        then { true } else { false }
     }
-    None
 }
 
 /// Checks if the expression matches
@@ -184,10 +131,9 @@ fn on_new_v1_fmt<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<S
 fn check_unformatted(expr: &Expr<'_>) -> bool {
     if_chain! {
         if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind;
-        if let ExprKind::Array(exprs) = expr.kind;
-        if exprs.len() == 1;
+        if let ExprKind::Array([expr]) = expr.kind;
         // struct `core::fmt::rt::v1::Argument`
-        if let ExprKind::Struct(_, fields, _) = exprs[0].kind;
+        if let ExprKind::Struct(_, fields, _) = expr.kind;
         if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format);
         // struct `core::fmt::rt::v1::FormatSpec`
         if let ExprKind::Struct(_, fields, _) = format_field.expr.kind;
index ee41c4aea2f7ec12366e016fc574f228c9f12b71..9641784eb9a27cbec1dd95e5886e417d0fb3b7a4 100644 (file)
@@ -1,7 +1,7 @@
 //! lint on inherent implementations
 
 use clippy_utils::diagnostics::span_lint_and_note;
-use clippy_utils::{in_macro, is_allowed};
+use clippy_utils::{in_macro, is_lint_allowed};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::{def_id::LocalDefId, Crate, Item, ItemKind, Node};
 use rustc_lint::{LateContext, LateLintPass};
@@ -59,7 +59,7 @@ fn check_crate_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Crate<'_>) {
             .filter(|(&id, impls)| {
                 impls.len() > 1
                     // Check for `#[allow]` on the type definition
-                    && !is_allowed(
+                    && !is_lint_allowed(
                         cx,
                         MULTIPLE_INHERENT_IMPL,
                         cx.tcx.hir().local_def_id_to_hir_id(id),
@@ -123,7 +123,7 @@ fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option<Span> {
         ..
     }) = cx.tcx.hir().get(id)
     {
-        (!in_macro(span) && impl_item.generics.params.is_empty() && !is_allowed(cx, MULTIPLE_INHERENT_IMPL, id))
+        (!in_macro(span) && impl_item.generics.params.is_empty() && !is_lint_allowed(cx, MULTIPLE_INHERENT_IMPL, id))
             .then(|| span)
     } else {
         None
index d69187f67466dd991ed1c5b046861255be1da678..892b3af0b32036b3d711d80c85b5d37d35c54c1b 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{get_item_name, get_parent_as_impl, is_allowed};
+use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -139,7 +139,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>)
             if let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id();
             if let Some(local_id) = ty_id.as_local();
             let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id);
-            if !is_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id);
+            if !is_lint_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id);
             if let Some(output) = parse_len_output(cx, cx.tcx.fn_sig(item.def_id).skip_binder());
             then {
                 let (name, kind) = match cx.tcx.hir().find(ty_hir_id) {
index c29b3e7c74c2c113fd85fbd0893d52ab4aad2f30..1af3a215f4468b4211bd7aa28a1e74ce8648604f 100644 (file)
@@ -338,6 +338,7 @@ macro_rules! declare_clippy_lint {
 mod slow_vector_initialization;
 mod stable_sort_primitive;
 mod strings;
+mod strlen_on_c_strings;
 mod suspicious_operation_groupings;
 mod suspicious_trait_impl;
 mod swap;
@@ -914,6 +915,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         strings::STRING_LIT_AS_BYTES,
         strings::STRING_TO_STRING,
         strings::STR_TO_STRING,
+        strlen_on_c_strings::STRLEN_ON_C_STRINGS,
         suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS,
         suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL,
         suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL,
@@ -944,6 +946,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         types::LINKEDLIST,
         types::OPTION_OPTION,
         types::RC_BUFFER,
+        types::RC_MUTEX,
         types::REDUNDANT_ALLOCATION,
         types::TYPE_COMPLEXITY,
         types::VEC_BOX,
@@ -1042,6 +1045,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(strings::STRING_TO_STRING),
         LintId::of(strings::STR_TO_STRING),
         LintId::of(types::RC_BUFFER),
+        LintId::of(types::RC_MUTEX),
         LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
         LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
         LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
@@ -1375,7 +1379,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
         LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
         LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
-        LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
         LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
         LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
         LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
@@ -1409,6 +1412,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
         LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
         LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
+        LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
         LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
         LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
         LintId::of(swap::ALMOST_SWAPPED),
@@ -1546,7 +1550,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
         LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
         LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
-        LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
         LintId::of(ptr::CMP_NULL),
         LintId::of(ptr::PTR_ARG),
         LintId::of(ptr_eq::PTR_EQ),
@@ -1639,6 +1642,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(reference::REF_IN_DEREF),
         LintId::of(repeat_once::REPEAT_ONCE),
         LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
+        LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
         LintId::of(swap::MANUAL_SWAP),
         LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT),
         LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
@@ -1791,6 +1795,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
         LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
         LintId::of(mutex_atomic::MUTEX_INTEGER),
+        LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
         LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
         LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
         LintId::of(regex::TRIVIAL_REGEX),
@@ -2095,6 +2100,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || box missing_enforced_import_rename::ImportRename::new(import_renames.clone()));
     let scripts = conf.allowed_scripts.clone();
     store.register_early_pass(move || box disallowed_script_idents::DisallowedScriptIdents::new(&scripts));
+    store.register_late_pass(|| box strlen_on_c_strings::StrlenOnCStrings);
 }
 
 #[rustfmt::skip]
index d07b5a93b67c0505a6bda0238e287cda8a7ee4ca..1e54a1e2de165e8781a4c581606d5da9cbf72efe 100644 (file)
@@ -7,7 +7,7 @@
 use rustc_lint::LateContext;
 use rustc_middle::{mir::FakeReadCause, ty};
 use rustc_span::source_map::Span;
-use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
+use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 
 pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
     if let Some(higher::Range {
@@ -82,7 +82,7 @@ struct MutatePairDelegate<'a, 'tcx> {
 }
 
 impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
-    fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId, _: ConsumeMode) {}
+    fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
 
     fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) {
         if let ty::BorrowKind::MutBorrow = bk {
index 97e4a983f32ea3ca4542d17cf63223d2aeca2303..563d5cdb5fb568046dfb447e2c5db8ee6626fe4a 100644 (file)
@@ -3,7 +3,7 @@
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
 use clippy_utils::{
-    can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, path_to_local_id,
+    can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id,
     peel_hir_expr_refs,
 };
 use rustc_ast::util::parser::PREC_POSTFIX;
@@ -102,7 +102,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 
             // These two lints will go back and forth with each other.
             if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit
-                && !is_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
+                && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
             {
                 return;
             }
@@ -146,7 +146,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                     },
                     _ => {
                         if path_to_local_id(some_expr, id)
-                            && !is_allowed(cx, MATCH_AS_REF, expr.hir_id)
+                            && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id)
                             && binding_ref.is_some()
                         {
                             return;
index 18038dd7819430e004b547a5670b6b8a1a4b0af8..9d8d77cf8f089aba80c1edd48bfd95d0dbda990f 100644 (file)
@@ -61,13 +61,13 @@ fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&'
             if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| {
                 match arm.pat.kind {
                     PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
-                    PatKind::TupleStruct(ref qpath, &[pat], _) =>
+                    PatKind::TupleStruct(ref qpath, [pat], _) =>
                         matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr),
                     _ => false,
                 }
             });
             let unwrap_arm = &arms[1 - idx];
-            if let PatKind::TupleStruct(ref qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind;
+            if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind;
             if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
             if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind;
             if path_to_local_id(unwrap_arm.body, binding_hir_id);
index f1e3492c4ecc5fdc3f698545376b9a3cb912fdfb..6d5ce3373f79d523060a219922875acf3a3cec7c 100644 (file)
@@ -7,7 +7,7 @@
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
 use clippy_utils::visitors::LocalUsedVisitor;
 use clippy_utils::{
-    get_parent_expr, in_macro, is_allowed, is_expn_of, is_lang_ctor, is_refutable, is_wild, meets_msrv, msrvs,
+    get_parent_expr, in_macro, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_wild, meets_msrv, msrvs,
     path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks,
     strip_pat_refs,
 };
@@ -625,7 +625,7 @@ fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
             if let PatKind::TupleStruct(
                 QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
             if args.len() == 1;
-            if let PatKind::Binding(_, arg, ..) = strip_pat_refs(args[0]).kind;
+            if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
             let body = remove_blocks(arms[0].body);
             if path_to_local_id(body, arg);
 
@@ -707,7 +707,7 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp
         };
 
         let ty = cx.typeck_results().expr_ty(ex);
-        if *ty.kind() != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) {
+        if *ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id) {
             check_single_match_single_pattern(cx, ex, arms, expr, els);
             check_single_match_opt_like(cx, ex, arms, expr, ty, els);
         }
index 03cb41697d509dd46717ed6d1a1c43094749f605..f8ee31a00df821ebc0e6f7834d666b3adb3a40d5 100644 (file)
@@ -1,8 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_expn_of;
-use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::higher::FormatExpn;
+use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::is_type_diagnostic_item;
-use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -94,27 +93,6 @@ fn can_be_static_str(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
         }
     }
 
-    fn generate_format_arg_snippet(
-        cx: &LateContext<'_>,
-        a: &hir::Expr<'_>,
-        applicability: &mut Applicability,
-    ) -> Vec<String> {
-        if_chain! {
-            if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, format_arg) = a.kind;
-            if let hir::ExprKind::Match(format_arg_expr, _, _) = format_arg.kind;
-            if let hir::ExprKind::Tup(format_arg_expr_tup) = format_arg_expr.kind;
-
-            then {
-                format_arg_expr_tup
-                    .iter()
-                    .map(|a| snippet_with_applicability(cx, a.span, "..", applicability).into_owned())
-                    .collect()
-            } else {
-                unreachable!()
-            }
-        }
-    }
-
     fn is_call(node: &hir::ExprKind<'_>) -> bool {
         match node {
             hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => {
@@ -150,36 +128,22 @@ fn is_call(node: &hir::ExprKind<'_>) -> bool {
     let mut applicability = Applicability::MachineApplicable;
 
     //Special handling for `format!` as arg_root
-    if_chain! {
-        if let hir::ExprKind::Block(block, None) = &arg_root.kind;
-        if block.stmts.len() == 1;
-        if let hir::StmtKind::Local(local) = &block.stmts[0].kind;
-        if let Some(arg_root) = &local.init;
-        if let hir::ExprKind::Call(inner_fun, inner_args) = arg_root.kind;
-        if is_expn_of(inner_fun.span, "format").is_some() && inner_args.len() == 1;
-        if let hir::ExprKind::Call(_, format_args) = &inner_args[0].kind;
-        then {
-            let fmt_spec = &format_args[0];
-            let fmt_args = &format_args[1];
-
-            let mut args = vec![snippet(cx, fmt_spec.span, "..").into_owned()];
-
-            args.extend(generate_format_arg_snippet(cx, fmt_args, &mut applicability));
-
-            let sugg = args.join(", ");
-
-            span_lint_and_sugg(
-                cx,
-                EXPECT_FUN_CALL,
-                span_replace_word,
-                &format!("use of `{}` followed by a function call", name),
-                "try this",
-                format!("unwrap_or_else({} panic!({}))", closure_args, sugg),
-                applicability,
-            );
-
-            return;
-        }
+    if let Some(format_expn) = FormatExpn::parse(arg_root) {
+        let span = match *format_expn.format_args.value_args {
+            [] => format_expn.format_args.format_string_span,
+            [.., last] => format_expn.format_args.format_string_span.to(last.span),
+        };
+        let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
+        span_lint_and_sugg(
+            cx,
+            EXPECT_FUN_CALL,
+            span_replace_word,
+            &format!("use of `{}` followed by a function call", name),
+            "try this",
+            format!("unwrap_or_else({} panic!({}))", closure_args, sugg),
+            applicability,
+        );
+        return;
     }
 
     let mut arg_root_snippet: Cow<'_, _> = snippet_with_applicability(cx, arg_root.span, "..", &mut applicability);
index 584daa5e11992a17921f4252592051ff8e598524..f5ce3e325512d5b838a9447c8b3517ca44315bbe 100644 (file)
@@ -1,7 +1,7 @@
 //! lint on multiple versions of a crate being used
 
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::run_lints;
+use clippy_utils::is_lint_allowed;
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_hir::{Crate, CRATE_HIR_ID};
 use rustc_lint::{LateContext, LateLintPass};
@@ -39,7 +39,7 @@
 
 impl LateLintPass<'_> for MultipleCrateVersions {
     fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
-        if !run_lints(cx, &[MULTIPLE_CRATE_VERSIONS], CRATE_HIR_ID) {
+        if is_lint_allowed(cx, MULTIPLE_CRATE_VERSIONS, CRATE_HIR_ID) {
             return;
         }
 
index 6c87136e5e14e0a235e697e4bd071d7f1cefcff3..4dbbb14c504fa445f334b4b008ac248ed911a17b 100644 (file)
@@ -120,8 +120,8 @@ fn is_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bo
         },
         Tuple(..) => ty.tuple_fields().any(|ty| is_mutable_type(cx, ty, span)),
         Adt(..) => {
-            cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
-                && !ty.has_escaping_bound_vars()
+            !ty.has_escaping_bound_vars()
+                && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
                 && !ty.is_freeze(cx.tcx.at(span), cx.param_env)
         },
         _ => false,
index 3b3736fd3a19114601421a34cd60e8cd0993f5b9..780690548e52b7008d3ccd590a4592050e054161 100644 (file)
@@ -71,6 +71,9 @@
 impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
         use self::Expression::{Bool, RetBool};
+        if e.span.from_expansion() {
+            return;
+        }
         if let ExprKind::If(pred, then_block, Some(else_expr)) = e.kind {
             let reduce = |ret, not| {
                 let mut applicability = Applicability::MachineApplicable;
index e33a33e238633aeaf5b68930e536070d80b73cb5..57fd03f4e12a68afd6fc8b322cd0b69cf7d599db 100644 (file)
@@ -326,10 +326,8 @@ fn move_common(&mut self, cmt: &euv::PlaceWithHirId<'_>) {
 }
 
 impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt {
-    fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _: HirId, mode: euv::ConsumeMode) {
-        if let euv::ConsumeMode::Move = mode {
-            self.move_common(cmt);
-        }
+    fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _: HirId) {
+        self.move_common(cmt);
     }
 
     fn borrow(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {}
index 1adad5be6ddc591f2cf11944e4b98f8c1d5554fb..043e7fa30d6e96867dec515ecc2c5ed6800de8dc 100644 (file)
@@ -31,7 +31,7 @@
     /// vec![1, 2, 3];
     /// ```
     pub NONSTANDARD_MACRO_BRACES,
-    style,
+    nursery,
     "check consistent use of braces in macro"
 }
 
@@ -92,14 +92,18 @@ fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
     }
 }
 
-fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, this: &'a MacroBraces) -> Option<MacroInfo<'a>> {
+fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, mac_braces: &'a MacroBraces) -> Option<MacroInfo<'a>> {
     if_chain! {
         if in_macro(span);
-        if let Some((name, braces)) = find_matching_macro(span, &this.macro_braces);
+        if let Some((name, braces)) = find_matching_macro(span, &mac_braces.macro_braces);
         if let Some(snip) = snippet_opt(cx, span.ctxt().outer_expn_data().call_site);
-        let c = snip.replace(" ", ""); // make formatting consistent
+        // we must check only invocation sites
+        // https://github.com/rust-lang/rust-clippy/issues/7422
+        if snip.starts_with(name);
+        // make formatting consistent
+        let c = snip.replace(" ", "");
         if !c.starts_with(&format!("{}!{}", name, braces.0));
-        if !this.done.contains(&span.ctxt().outer_expn_data().call_site);
+        if !mac_braces.done.contains(&span.ctxt().outer_expn_data().call_site);
         then {
             Some((name, braces, snip))
         } else {
index b6af4175edfdabce7903ba40233b106a11d8daa7..b2be35bdddb38fbb18625b0c305c092ca9ace406 100644 (file)
@@ -132,7 +132,7 @@ fn detect_option_if_let_else<'tcx>(
         if !is_else_clause(cx.tcx, expr);
         if arms.len() == 2;
         if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already
-        if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind;
+        if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &arms[0].pat.kind;
         if is_lang_ctor(cx, struct_qpath, OptionSome);
         if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind;
         if !contains_return_break_continue_macro(arms[0].body);
index 1a680e7607e0b21e0116df6e0fa4363af52081b9..dc28874c16e67febf474f13cda31f1dcb38e5711 100644 (file)
@@ -74,7 +74,9 @@
 
 impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if match_panic_call(cx, expr).is_some() && is_expn_of(expr.span, "debug_assert").is_none() {
+        if match_panic_call(cx, expr).is_some()
+            && (is_expn_of(expr.span, "debug_assert").is_none() && is_expn_of(expr.span, "assert").is_none())
+        {
             let span = get_outer_span(expr);
             if is_expn_of(expr.span, "unimplemented").is_some() {
                 span_lint(
index 9bab783998aa5693088f8352a3319e55f23086a1..ea4065d371b81a1bb11b243fbd66179ff7265f5b 100644 (file)
@@ -258,7 +258,7 @@ fn get_variant<'a>(adt_def: &'a AdtDef, qpath: &QPath<'_>) -> Option<&'a Variant
 
 fn find_first_mismatch_in_tuple<'tcx, I>(
     cx: &LateContext<'tcx>,
-    pats: &[&Pat<'_>],
+    pats: &[Pat<'_>],
     ty_iter_src: I,
 ) -> Option<(Span, Mutability, Level)>
 where
index 12c44436874e1dbdff49142e105f4ef49275f82a..dba3b1805cd595df2029aa7c67ce823e8b8423c8 100644 (file)
@@ -4,7 +4,7 @@
 use clippy_utils::ptr::get_spans;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty};
-use clippy_utils::{expr_path_res, is_allowed, match_any_def_paths, paths};
+use clippy_utils::{expr_path_res, is_lint_allowed, match_any_def_paths, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{
@@ -246,7 +246,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
     for (idx, (arg, ty)) in decl.inputs.iter().zip(fn_ty.inputs()).enumerate() {
         // Honor the allow attribute on parameters. See issue 5644.
         if let Some(body) = &body {
-            if is_allowed(cx, PTR_ARG, body.params[idx].hir_id) {
+            if is_lint_allowed(cx, PTR_ARG, body.params[idx].hir_id) {
                 continue;
             }
         }
index 119ec21520f13c932acb14b62285a84da3230c13..7ba7ff3a353f976d8ba7bf52f5a191b4d7bff5f2 100644 (file)
@@ -12,6 +12,7 @@
 use rustc_middle::mir::{
     self, traversal,
     visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _},
+    Mutability,
 };
 use rustc_middle::ty::{self, fold::TypeVisitor, Ty};
 use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor};
@@ -87,13 +88,18 @@ fn check_fn(
 
         let mir = cx.tcx.optimized_mir(def_id.to_def_id());
 
+        let possible_origin = {
+            let mut vis = PossibleOriginVisitor::new(mir);
+            vis.visit_body(mir);
+            vis.into_map(cx)
+        };
         let maybe_storage_live_result = MaybeStorageLive
             .into_engine(cx.tcx, mir)
             .pass_name("redundant_clone")
             .iterate_to_fixpoint()
             .into_results_cursor(mir);
         let mut possible_borrower = {
-            let mut vis = PossibleBorrowerVisitor::new(cx, mir);
+            let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin);
             vis.visit_body(mir);
             vis.into_map(cx, maybe_storage_live_result)
         };
@@ -509,14 +515,20 @@ struct PossibleBorrowerVisitor<'a, 'tcx> {
     possible_borrower: TransitiveRelation<mir::Local>,
     body: &'a mir::Body<'tcx>,
     cx: &'a LateContext<'tcx>,
+    possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
 }
 
 impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> {
-    fn new(cx: &'a LateContext<'tcx>, body: &'a mir::Body<'tcx>) -> Self {
+    fn new(
+        cx: &'a LateContext<'tcx>,
+        body: &'a mir::Body<'tcx>,
+        possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
+    ) -> Self {
         Self {
             possible_borrower: TransitiveRelation::default(),
             cx,
             body,
+            possible_origin,
         }
     }
 
@@ -585,21 +597,105 @@ fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Loca
             ..
         } = &terminator.kind
         {
+            // TODO add doc
             // If the call returns something with lifetimes,
             // let's conservatively assume the returned value contains lifetime of all the arguments.
             // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`.
-            if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_continue() {
-                return;
-            }
+
+            let mut immutable_borrowers = vec![];
+            let mut mutable_borrowers = vec![];
 
             for op in args {
                 match op {
                     mir::Operand::Copy(p) | mir::Operand::Move(p) => {
-                        self.possible_borrower.add(p.local, *dest);
+                        if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() {
+                            mutable_borrowers.push(p.local);
+                        } else {
+                            immutable_borrowers.push(p.local);
+                        }
                     },
                     mir::Operand::Constant(..) => (),
                 }
             }
+
+            let mut mutable_variables: Vec<mir::Local> = mutable_borrowers
+                .iter()
+                .filter_map(|r| self.possible_origin.get(r))
+                .flat_map(HybridBitSet::iter)
+                .collect();
+
+            if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() {
+                mutable_variables.push(*dest);
+            }
+
+            for y in mutable_variables {
+                for x in &immutable_borrowers {
+                    self.possible_borrower.add(*x, y);
+                }
+                for x in &mutable_borrowers {
+                    self.possible_borrower.add(*x, y);
+                }
+            }
+        }
+    }
+}
+
+/// Collect possible borrowed for every `&mut` local.
+/// For exampel, `_1 = &mut _2` generate _1: {_2,...}
+/// Known Problems: not sure all borrowed are tracked
+struct PossibleOriginVisitor<'a, 'tcx> {
+    possible_origin: TransitiveRelation<mir::Local>,
+    body: &'a mir::Body<'tcx>,
+}
+
+impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> {
+    fn new(body: &'a mir::Body<'tcx>) -> Self {
+        Self {
+            possible_origin: TransitiveRelation::default(),
+            body,
+        }
+    }
+
+    fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap<mir::Local, HybridBitSet<mir::Local>> {
+        let mut map = FxHashMap::default();
+        for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
+            if is_copy(cx, self.body.local_decls[row].ty) {
+                continue;
+            }
+
+            let borrowers = self.possible_origin.reachable_from(&row);
+            if !borrowers.is_empty() {
+                let mut bs = HybridBitSet::new_empty(self.body.local_decls.len());
+                for &c in borrowers {
+                    if c != mir::Local::from_usize(0) {
+                        bs.insert(c);
+                    }
+                }
+
+                if !bs.is_empty() {
+                    map.insert(row, bs);
+                }
+            }
+        }
+        map
+    }
+}
+
+impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> {
+    fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) {
+        let lhs = place.local;
+        match rvalue {
+            // Only consider `&mut`, which can modify origin place
+            mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) |
+            // _2: &mut _;
+            // _3 = move _2
+            mir::Rvalue::Use(mir::Operand::Move(borrowed))  |
+            // _3 = move _2 as &mut _;
+            mir::Rvalue::Cast(_, mir::Operand::Move(borrowed), _)
+                => {
+                self.possible_origin.add(lhs, borrowed.local);
+            },
+            _ => {},
         }
     }
 }
index 9d91b53e1bbc1ad4b4827198f2380379e596707a..958e462125ef1cafc3d823bdc1d984de575db6f6 100644 (file)
@@ -2,7 +2,7 @@
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::SpanlessEq;
-use clippy_utils::{get_parent_expr, is_allowed, match_function_call, method_calls, paths};
+use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method_calls, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath};
@@ -124,7 +124,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
         ) = e.kind
         {
             if is_string(cx, left) {
-                if !is_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) {
+                if !is_lint_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) {
                     let parent = get_parent_expr(cx, e);
                     if let Some(p) = parent {
                         if let ExprKind::Assign(target, _, _) = p.kind {
diff --git a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs
new file mode 100644 (file)
index 0000000..2ccf3a3
--- /dev/null
@@ -0,0 +1,82 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
+use clippy_utils::paths;
+use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::ty::{is_type_diagnostic_item, is_type_ref_to_diagnostic_item};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::{sym, Symbol};
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for usage of `libc::strlen` on a `CString` or `CStr` value,
+    /// and suggest calling `as_bytes().len()` or `to_bytes().len()` respectively instead.
+    ///
+    /// **Why is this bad?** This avoids calling an unsafe `libc` function.
+    /// Currently, it also avoids calculating the length.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust, ignore
+    /// use std::ffi::CString;
+    /// let cstring = CString::new("foo").expect("CString::new failed");
+    /// let len = unsafe { libc::strlen(cstring.as_ptr()) };
+    /// ```
+    /// Use instead:
+    /// ```rust, no_run
+    /// use std::ffi::CString;
+    /// let cstring = CString::new("foo").expect("CString::new failed");
+    /// let len = cstring.as_bytes().len();
+    /// ```
+    pub STRLEN_ON_C_STRINGS,
+    complexity,
+    "using `libc::strlen` on a `CString` or `CStr` value, while `as_bytes().len()` or `to_bytes().len()` respectively can be used instead"
+}
+
+declare_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]);
+
+impl LateLintPass<'tcx> for StrlenOnCStrings {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+        if in_macro(expr.span) {
+            return;
+        }
+
+        if_chain! {
+            if let hir::ExprKind::Call(func, [recv]) = expr.kind;
+            if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = func.kind;
+
+            if (&paths::LIBC_STRLEN).iter().map(|x| Symbol::intern(x)).eq(
+                path.segments.iter().map(|seg| seg.ident.name));
+            if let hir::ExprKind::MethodCall(path, _, args, _) = recv.kind;
+            if args.len() == 1;
+            if !args.iter().any(|e| e.span.from_expansion());
+            if path.ident.name == sym::as_ptr;
+            then {
+                let cstring = &args[0];
+                let ty = cx.typeck_results().expr_ty(cstring);
+                let val_name = snippet_with_macro_callsite(cx, cstring.span, "..");
+                let sugg = if is_type_diagnostic_item(cx, ty, sym::cstring_type){
+                    format!("{}.as_bytes().len()", val_name)
+                } else if is_type_ref_to_diagnostic_item(cx, ty, sym::CStr){
+                    format!("{}.to_bytes().len()", val_name)
+                } else {
+                    return;
+                };
+
+                span_lint_and_sugg(
+                    cx,
+                    STRLEN_ON_C_STRINGS,
+                    expr.span,
+                    "using `libc::strlen` on a `CString` or `CStr` value",
+                    "try this (you might also need to get rid of `unsafe` block in some cases):",
+                    sugg,
+                    Applicability::Unspecified // Sometimes unnecessary `unsafe` block
+                );
+            }
+        }
+    }
+}
index 70b9e8adef884e56cf34f215cd9d7e442c21455b..7d629b5455b62913f4b480ce67fed09e65c1c4f1 100644 (file)
@@ -3,6 +3,7 @@
 mod linked_list;
 mod option_option;
 mod rc_buffer;
+mod rc_mutex;
 mod redundant_allocation;
 mod type_complexity;
 mod utils;
 declare_clippy_lint! {
     /// **What it does:** Checks for use of redundant allocations anywhere in the code.
     ///
-    /// **Why is this bad?** Expressions such as `Rc<&T>`, `Rc<Rc<T>>`, `Rc<Box<T>>`, `Box<&T>`
-    /// add an unnecessary level of indirection.
+    /// **Why is this bad?** Expressions such as `Rc<&T>`, `Rc<Rc<T>>`, `Rc<Arc<T>>`, `Rc<Box<T>>`, Arc<&T>`, `Arc<Rc<T>>`,
+    /// `Arc<Arc<T>>`, `Arc<Box<T>>`, `Box<&T>`, `Box<Rc<T>>`, `Box<Arc<T>>`, `Box<Box<T>>`, add an unnecessary level of indirection.
     ///
     /// **Known problems:** None.
     ///
     "usage of very complex types that might be better factored into `type` definitions"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Checks for `Rc<Mutex<T>>`.
+    ///
+    /// **Why is this bad?** `Rc` is used in single thread and `Mutex` is used in multi thread.
+    /// Consider using `Rc<RefCell<T>>` in single thread or `Arc<Mutex<T>>` in multi thread.
+    ///
+    /// **Known problems:** Sometimes combining generic types can lead to the requirement that a
+    /// type use Rc in conjunction with Mutex. We must consider those cases false positives, but
+    /// alas they are quite hard to rule out. Luckily they are also rare.
+    ///
+    /// **Example:**
+    /// ```rust,ignore
+    /// use std::rc::Rc;
+    /// use std::sync::Mutex;
+    /// fn foo(interned: Rc<Mutex<i32>>) { ... }
+    /// ```
+    ///
+    /// Better:
+    ///
+    /// ```rust,ignore
+    /// use std::rc::Rc;
+    /// use std::cell::RefCell
+    /// fn foo(interned: Rc<RefCell<i32>>) { ... }
+    /// ```
+    pub RC_MUTEX,
+    restriction,
+    "usage of `Rc<Mutex<T>>`"
+}
+
 pub struct Types {
     vec_box_size_threshold: u64,
     type_complexity_threshold: u64,
 }
 
-impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, TYPE_COMPLEXITY]);
+impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]);
 
 impl<'tcx> LateLintPass<'tcx> for Types {
     fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) {
@@ -375,6 +405,7 @@ fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, mut context:
                     triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold);
                     triggered |= option_option::check(cx, hir_ty, qpath, def_id);
                     triggered |= linked_list::check(cx, hir_ty, def_id);
+                    triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id);
 
                     if triggered {
                         return;
diff --git a/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs b/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs
new file mode 100644 (file)
index 0000000..bd7a0ee
--- /dev/null
@@ -0,0 +1,27 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_ty_param_diagnostic_item;
+use if_chain::if_chain;
+use rustc_hir::{self as hir, def_id::DefId, QPath};
+use rustc_lint::LateContext;
+use rustc_span::symbol::sym;
+
+use super::RC_MUTEX;
+
+pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
+    if_chain! {
+        if cx.tcx.is_diagnostic_item(sym::Rc, def_id) ;
+        if let Some(_) = is_ty_param_diagnostic_item(cx, qpath, sym!(mutex_type)) ;
+
+        then{
+            span_lint(
+                cx,
+                RC_MUTEX,
+                hir_ty.span,
+                "found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead",
+            );
+            return true;
+        }
+    }
+
+    false
+}
index c0c1f340583c1b53cb126407fe8ccb96c53aa9da..8e83dcbf704e0930fecd8384910e51acbc10fa9e 100644 (file)
@@ -1,5 +1,5 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item, is_ty_param_lang_item};
 use rustc_errors::Applicability;
 use rustc_hir::{self as hir, def_id::DefId, LangItem, QPath, TyKind};
@@ -9,74 +9,99 @@
 use super::{utils, REDUNDANT_ALLOCATION};
 
 pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
-    if Some(def_id) == cx.tcx.lang_items().owned_box() {
-        if let Some(span) = utils::match_borrows_parameter(cx, qpath) {
-            let mut applicability = Applicability::MachineApplicable;
-            span_lint_and_sugg(
-                cx,
-                REDUNDANT_ALLOCATION,
-                hir_ty.span,
-                "usage of `Box<&T>`",
-                "try",
-                snippet_with_applicability(cx, span, "..", &mut applicability).to_string(),
-                applicability,
-            );
-            return true;
-        }
+    let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() {
+        "Box"
+    } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
+        "Rc"
+    } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
+        "Arc"
+    } else {
+        return false;
+    };
+
+    if let Some(span) = utils::match_borrows_parameter(cx, qpath) {
+        let mut applicability = Applicability::MaybeIncorrect;
+        let generic_snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
+        span_lint_and_then(
+            cx,
+            REDUNDANT_ALLOCATION,
+            hir_ty.span,
+            &format!("usage of `{}<{}>`", outer_sym, generic_snippet),
+            |diag| {
+                diag.span_suggestion(hir_ty.span, "try", format!("{}", generic_snippet), applicability);
+                diag.note(&format!(
+                    "`{generic}` is already a pointer, `{outer}<{generic}>` allocates a pointer on the heap",
+                    outer = outer_sym,
+                    generic = generic_snippet
+                ));
+            },
+        );
+        return true;
     }
 
-    if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
-        if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Rc) {
-            let mut applicability = Applicability::MachineApplicable;
-            span_lint_and_sugg(
-                cx,
-                REDUNDANT_ALLOCATION,
-                hir_ty.span,
-                "usage of `Rc<Rc<T>>`",
-                "try",
-                snippet_with_applicability(cx, ty.span, "..", &mut applicability).to_string(),
-                applicability,
-            );
-            true
-        } else if let Some(ty) = is_ty_param_lang_item(cx, qpath, LangItem::OwnedBox) {
-            let qpath = match &ty.kind {
-                TyKind::Path(qpath) => qpath,
-                _ => return false,
-            };
-            let inner_span = match get_qpath_generic_tys(qpath).next() {
-                Some(ty) => ty.span,
-                None => return false,
-            };
-            let mut applicability = Applicability::MachineApplicable;
-            span_lint_and_sugg(
-                cx,
-                REDUNDANT_ALLOCATION,
-                hir_ty.span,
-                "usage of `Rc<Box<T>>`",
-                "try",
-                format!(
-                    "Rc<{}>",
-                    snippet_with_applicability(cx, inner_span, "..", &mut applicability)
-                ),
-                applicability,
-            );
-            true
-        } else {
-            utils::match_borrows_parameter(cx, qpath).map_or(false, |span| {
-                let mut applicability = Applicability::MachineApplicable;
-                span_lint_and_sugg(
-                    cx,
-                    REDUNDANT_ALLOCATION,
+    let (inner_sym, ty) = if let Some(ty) = is_ty_param_lang_item(cx, qpath, LangItem::OwnedBox) {
+        ("Box", ty)
+    } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Rc) {
+        ("Rc", ty)
+    } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Arc) {
+        ("Arc", ty)
+    } else {
+        return false;
+    };
+
+    let inner_qpath = match &ty.kind {
+        TyKind::Path(inner_qpath) => inner_qpath,
+        _ => return false,
+    };
+    let inner_span = match get_qpath_generic_tys(inner_qpath).next() {
+        Some(ty) => ty.span,
+        None => return false,
+    };
+    if inner_sym == outer_sym {
+        let mut applicability = Applicability::MaybeIncorrect;
+        let generic_snippet = snippet_with_applicability(cx, inner_span, "..", &mut applicability);
+        span_lint_and_then(
+            cx,
+            REDUNDANT_ALLOCATION,
+            hir_ty.span,
+            &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
+            |diag| {
+                diag.span_suggestion(
                     hir_ty.span,
-                    "usage of `Rc<&T>`",
                     "try",
-                    snippet_with_applicability(cx, span, "..", &mut applicability).to_string(),
+                    format!("{}<{}>", outer_sym, generic_snippet),
                     applicability,
                 );
-                true
-            })
-        }
+                diag.note(&format!(
+                    "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
+                    outer = outer_sym,
+                    inner = inner_sym,
+                    generic = generic_snippet
+                ));
+            },
+        );
     } else {
-        false
+        let generic_snippet = snippet(cx, inner_span, "..");
+        span_lint_and_then(
+            cx,
+            REDUNDANT_ALLOCATION,
+            hir_ty.span,
+            &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
+            |diag| {
+                diag.note(&format!(
+                    "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
+                    outer = outer_sym,
+                    inner = inner_sym,
+                    generic = generic_snippet
+                ));
+                diag.help(&format!(
+                    "consider using just `{outer}<{generic}>` or `{inner}<{generic}>`",
+                    outer = outer_sym,
+                    inner = inner_sym,
+                    generic = generic_snippet
+                ));
+            },
+        );
     }
+    true
 }
index 45291a120ed59b51df71bb3f28fc57ab0438d8b7..2f0a61898ba75347c7a702004c54826e6ca9e045 100644 (file)
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_allowed;
+use clippy_utils::is_lint_allowed;
 use clippy_utils::source::snippet;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -114,7 +114,7 @@ fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
             span,
             "literal non-ASCII character detected",
             "consider replacing the string with",
-            if is_allowed(cx, UNICODE_NOT_NFC, id) {
+            if is_lint_allowed(cx, UNICODE_NOT_NFC, id) {
                 escape(string.chars())
             } else {
                 escape(string.nfc())
@@ -122,7 +122,7 @@ fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
             Applicability::MachineApplicable,
         );
     }
-    if is_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) {
+    if is_lint_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) {
         span_lint_and_sugg(
             cx,
             UNICODE_NOT_NFC,
index e14945651f5010551820308a272693951cc992ab..ab0cdf75ffe0a02feceb516f65091d1f0e0e93ef 100644 (file)
     ///     ()
     /// }
     /// ```
+    /// is equivalent to
+    /// ```rust
+    /// fn return_unit() {}
+    /// ```
     pub UNUSED_UNIT,
     style,
     "needless unit expression"
index 906ac10f4610b92f0c548f2445064dde896b6a87..71117e967e31941af9da90b9239e05edf794c4bf 100644 (file)
@@ -87,11 +87,8 @@ enum StackItem {
 
 impl<'tcx> LateLintPass<'tcx> for UseSelf {
     fn check_item(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) {
-        if !is_item_interesting(item) {
-            // This does two things:
-            //  1) Reduce needless churn on `self.stack`
-            //  2) Don't push `StackItem::NoCheck` when entering `ItemKind::OpaqueTy`,
-            //     in order to lint `foo() -> impl <..>`
+        if matches!(item.kind, ItemKind::OpaqueTy(_)) {
+            // skip over `ItemKind::OpaqueTy` in order to lint `foo() -> impl <..>`
             return;
         }
         // We push the self types of `impl`s on a stack here. Only the top type on the stack is
@@ -119,7 +116,7 @@ fn check_item(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) {
     }
 
     fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) {
-        if is_item_interesting(item) {
+        if !matches!(item.kind, ItemKind::OpaqueTy(_)) {
             self.stack.pop();
         }
     }
@@ -297,11 +294,3 @@ fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) {
         span_lint(cx, span);
     }
 }
-
-fn is_item_interesting(item: &Item<'_>) -> bool {
-    use rustc_hir::ItemKind::{Const, Enum, Fn, Impl, Static, Struct, Trait, Union};
-    matches!(
-        item.kind,
-        Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..)
-    )
-}
index b1523e032af29a05906a64548e3c42491961a3a4..668807f499f3ffde3a121cc87b1a92dc8f0a6a49 100644 (file)
@@ -3,8 +3,8 @@
 use clippy_utils::source::snippet;
 use clippy_utils::ty::match_type;
 use clippy_utils::{
-    is_else_clause, is_expn_of, is_expr_path_def_path, match_def_path, method_calls, path_to_res, paths, run_lints,
-    SpanlessEq,
+    is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path, method_calls, path_to_res,
+    paths, SpanlessEq,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId};
@@ -353,7 +353,7 @@ pub struct LintWithoutLintPass {
 
 impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-        if !run_lints(cx, &[DEFAULT_LINT], item.hir_id()) {
+        if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) {
             return;
         }
 
@@ -411,7 +411,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
     }
 
     fn check_crate_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Crate<'_>) {
-        if !run_lints(cx, &[LINT_WITHOUT_LINT_PASS], CRATE_HIR_ID) {
+        if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) {
             return;
         }
 
@@ -497,7 +497,7 @@ pub fn new() -> Self {
 
 impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !run_lints(cx, &[COMPILER_LINT_FUNCTIONS], expr.hir_id) {
+        if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) {
             return;
         }
 
@@ -526,7 +526,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 
 impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if !run_lints(cx, &[OUTER_EXPN_EXPN_DATA], expr.hir_id) {
+        if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) {
             return;
         }
 
@@ -576,7 +576,7 @@ fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
 
 impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if !run_lints(cx, &[COLLAPSIBLE_SPAN_LINT_CALLS], expr.hir_id) {
+        if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) {
             return;
         }
 
@@ -757,7 +757,7 @@ fn suggest_note(
 
 impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if !run_lints(cx, &[MATCH_TYPE_ON_DIAGNOSTIC_ITEM], expr.hir_id) {
+        if is_lint_allowed(cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.hir_id) {
             return;
         }
 
index e877af09e28900d94f5491a828b93bffabed4388..3eccc89cdeb808f813a32177d5af76f6819c0fed 100644 (file)
@@ -520,7 +520,9 @@ fn get_lint_group_and_level_or_lint(
     lint_name: &str,
     item: &'hir Item<'_>,
 ) -> Option<(String, &'static str)> {
-    let result = cx.lint_store.check_lint_name(lint_name, Some(sym::clippy));
+    let result = cx
+        .lint_store
+        .check_lint_name(cx.sess(), lint_name, Some(sym::clippy), &[]);
     if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
         if let Some(group) = get_lint_group(cx, lint_lst[0]) {
             if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {
index 60c3489a449b6796714a416120985ec2e8a7f33d..1ca1117a41ea5fccef9b7eb067b6a46f3856e81e 100644 (file)
@@ -1,5 +1,4 @@
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::run_lints;
+use clippy_utils::{diagnostics::span_lint, is_lint_allowed};
 use rustc_hir::{hir_id::CRATE_HIR_ID, Crate};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -31,7 +30,7 @@
 
 impl LateLintPass<'_> for WildcardDependencies {
     fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
-        if !run_lints(cx, &[WILDCARD_DEPENDENCIES], CRATE_HIR_ID) {
+        if is_lint_allowed(cx, WILDCARD_DEPENDENCIES, CRATE_HIR_ID) {
             return;
         }
 
index 8be36756b3332a295963c89aa3dace545fd5570d..3e3e472e99fb69215e7e07b2c70d34c4842674dd 100644 (file)
@@ -5,11 +5,11 @@
 
 use crate::{is_expn_of, match_def_path, paths};
 use if_chain::if_chain;
-use rustc_ast::ast;
+use rustc_ast::ast::{self, LitKind};
 use rustc_hir as hir;
 use rustc_hir::{BorrowKind, Expr, ExprKind, StmtKind, UnOp};
 use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
+use rustc_span::{sym, ExpnKind, Span, Symbol};
 
 /// Converts a hir binary operator to the corresponding `ast` type.
 #[must_use]
@@ -266,3 +266,107 @@ fn ast_matchblock(matchblock_expr: &'tcx Expr<'tcx>) -> Option<Vec<&Expr<'_>>> {
     }
     None
 }
+
+/// A parsed `format!` expansion
+pub struct FormatExpn<'tcx> {
+    /// Span of `format!(..)`
+    pub call_site: Span,
+    /// Inner `format_args!` expansion
+    pub format_args: FormatArgsExpn<'tcx>,
+}
+
+impl FormatExpn<'tcx> {
+    /// Parses an expanded `format!` invocation
+    pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
+        if_chain! {
+            if let ExprKind::Block(block, _) = expr.kind;
+            if let [stmt] = block.stmts;
+            if let StmtKind::Local(local) = stmt.kind;
+            if let Some(init) = local.init;
+            if let ExprKind::Call(_, [format_args]) = init.kind;
+            let expn_data = expr.span.ctxt().outer_expn_data();
+            if let ExpnKind::Macro(_, sym::format) = expn_data.kind;
+            if let Some(format_args) = FormatArgsExpn::parse(format_args);
+            then {
+                Some(FormatExpn {
+                    call_site: expn_data.call_site,
+                    format_args,
+                })
+            } else {
+                None
+            }
+        }
+    }
+}
+
+/// A parsed `format_args!` expansion
+pub struct FormatArgsExpn<'tcx> {
+    /// Span of the first argument, the format string
+    pub format_string_span: Span,
+    /// Values passed after the format string
+    pub value_args: Vec<&'tcx Expr<'tcx>>,
+
+    /// String literal expressions which represent the format string split by "{}"
+    pub format_string_parts: &'tcx [Expr<'tcx>],
+    /// Symbols corresponding to [`format_string_parts`]
+    pub format_string_symbols: Vec<Symbol>,
+    /// Expressions like `ArgumentV1::new(arg0, Debug::fmt)`
+    pub args: &'tcx [Expr<'tcx>],
+    /// The final argument passed to `Arguments::new_v1_formatted`, if applicable
+    pub fmt_expr: Option<&'tcx Expr<'tcx>>,
+}
+
+impl FormatArgsExpn<'tcx> {
+    /// Parses an expanded `format_args!` or `format_args_nl!` invocation
+    pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
+        if_chain! {
+            if let ExpnKind::Macro(_, name) = expr.span.ctxt().outer_expn_data().kind;
+            let name = name.as_str();
+            if name.ends_with("format_args") || name.ends_with("format_args_nl");
+            if let ExprKind::Call(_, args) = expr.kind;
+            if let Some((strs_ref, args, fmt_expr)) = match args {
+                // Arguments::new_v1
+                [strs_ref, args] => Some((strs_ref, args, None)),
+                // Arguments::new_v1_formatted
+                [strs_ref, args, fmt_expr] => Some((strs_ref, args, Some(fmt_expr))),
+                _ => None,
+            };
+            if let ExprKind::AddrOf(BorrowKind::Ref, _, strs_arr) = strs_ref.kind;
+            if let ExprKind::Array(format_string_parts) = strs_arr.kind;
+            if let Some(format_string_symbols) = format_string_parts
+                .iter()
+                .map(|e| {
+                    if let ExprKind::Lit(lit) = &e.kind {
+                        if let LitKind::Str(symbol, _style) = lit.node {
+                            return Some(symbol);
+                        }
+                    }
+                    None
+                })
+                .collect();
+            if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args.kind;
+            if let ExprKind::Match(args, [arm], _) = args.kind;
+            if let ExprKind::Tup(value_args) = args.kind;
+            if let Some(value_args) = value_args
+                .iter()
+                .map(|e| match e.kind {
+                    ExprKind::AddrOf(_, _, e) => Some(e),
+                    _ => None,
+                })
+                .collect();
+            if let ExprKind::Array(args) = arm.body.kind;
+            then {
+                Some(FormatArgsExpn {
+                    format_string_span: strs_ref.span,
+                    value_args,
+                    format_string_parts,
+                    format_string_symbols,
+                    args,
+                    fmt_expr,
+                })
+            } else {
+                None
+            }
+        }
+    }
+}
index 2f10472180f5e4f56b00177a927df3aab30dec31..4f0a9f442ed9f5b4893a67d135477aba49075c77 100644 (file)
@@ -255,7 +255,7 @@ pub fn in_macro(span: Span) -> bool {
 }
 
 /// Checks if given pattern is a wildcard (`_`)
-pub fn is_wild<'tcx>(pat: &impl std::ops::Deref<Target = Pat<'tcx>>) -> bool {
+pub fn is_wild(pat: &Pat<'_>) -> bool {
     matches!(pat.kind, PatKind::Wild)
 }
 
@@ -1023,8 +1023,8 @@ fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
         )
     }
 
-    fn are_refutable<'a, I: Iterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, mut i: I) -> bool {
-        i.any(|pat| is_refutable(cx, pat))
+    fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
+        i.into_iter().any(|pat| is_refutable(cx, pat))
     }
 
     match pat.kind {
@@ -1035,23 +1035,23 @@ fn are_refutable<'a, I: Iterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, mut
         PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
         PatKind::Or(pats) => {
             // TODO: should be the honest check, that pats is exhaustive set
-            are_refutable(cx, pats.iter().map(|pat| &**pat))
+            are_refutable(cx, pats)
         },
-        PatKind::Tuple(pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)),
+        PatKind::Tuple(pats, _) => are_refutable(cx, pats),
         PatKind::Struct(ref qpath, fields, _) => {
             is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat))
         },
         PatKind::TupleStruct(ref qpath, pats, _) => {
-            is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats.iter().map(|pat| &**pat))
+            is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats)
         },
-        PatKind::Slice(head, ref middle, tail) => {
+        PatKind::Slice(head, middle, tail) => {
             match &cx.typeck_results().node_type(pat.hir_id).kind() {
                 rustc_ty::Slice(..) => {
                     // [..] is the only irrefutable slice pattern.
                     !head.is_empty() || middle.is_none() || !tail.is_empty()
                 },
                 rustc_ty::Array(..) => {
-                    are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat))
+                    are_refutable(cx, head.iter().chain(middle).chain(tail.iter()))
                 },
                 _ => {
                     // unreachable!()
@@ -1066,7 +1066,7 @@ fn are_refutable<'a, I: Iterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, mut
 /// the function once on the given pattern.
 pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
     if let PatKind::Or(pats) = pat.kind {
-        pats.iter().copied().for_each(f);
+        pats.iter().for_each(f);
     } else {
         f(pat);
     }
@@ -1163,7 +1163,7 @@ fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 /// Returns `true` if the lint is allowed in the current context
 ///
 /// Useful for skipping long running code when it's unnecessary
-pub fn is_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
+pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
     cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
 }
 
@@ -1531,25 +1531,6 @@ pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
     }
 }
 
-/// This function checks if any of the lints in the slice is enabled for the provided `HirId`.
-/// A lint counts as enabled with any of the levels: `Level::Forbid` | `Level::Deny` | `Level::Warn`
-///
-/// ```ignore
-/// #[deny(clippy::YOUR_AWESOME_LINT)]
-/// println!("Hello, World!"); // <- Clippy code: run_lints(cx, &[YOUR_AWESOME_LINT], id) == true
-///
-/// #[allow(clippy::YOUR_AWESOME_LINT)]
-/// println!("See you soon!"); // <- Clippy code: run_lints(cx, &[YOUR_AWESOME_LINT], id) == false
-/// ```
-pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bool {
-    lints.iter().any(|lint| {
-        matches!(
-            cx.tcx.lint_level_at_node(lint, id),
-            (Level::Forbid | Level::Deny | Level::Warn, _)
-        )
-    })
-}
-
 /// Returns Option<String> where String is a textual representation of the type encapsulated in the
 /// slice iff the given expression is a slice of primitives (as defined in the
 /// `is_recursively_primitive_type` function) and None otherwise.
index 546706d51d7b597c3f6d03b90a1448152533d37b..4a28c7dd9a04a3885251ef7ab44e7d5ea65aa7fe 100644 (file)
@@ -162,6 +162,9 @@ pub fn format(&self) -> String {
         }
 
         if let Some(suffix) = self.suffix {
+            if output.ends_with('.') {
+                output.push('0');
+            }
             output.push('_');
             output.push_str(suffix);
         }
index b913d1ce8b1f769cc2b1884feb5fae2d19722eb6..c960eec306414620449cd6d05e189080b8752867 100644 (file)
@@ -38,7 +38,6 @@
 pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
 pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
 pub const DIR_BUILDER: [&str; 3] = ["std", "fs", "DirBuilder"];
-pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"];
 pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
 pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
 pub const DROP: [&str; 3] = ["core", "mem", "drop"];
@@ -50,9 +49,6 @@
 pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
 pub const FILE: [&str; 3] = ["std", "fs", "File"];
 pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
-pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
-pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
-pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
 pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
 pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"];
 pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"];
@@ -82,6 +78,7 @@
 pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
 #[cfg(feature = "internal-lints")]
 pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
+pub const LIBC_STRLEN: [&str; 2] = ["libc", "strlen"];
 pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
 #[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))]
 pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
index 8857e77d8983d34d0e9505c8a00097731275c5d5..3f5c5604d43f5e5b481cb370ad45739cf05e3942 100644 (file)
@@ -14,8 +14,8 @@
 use rustc_span::sym;
 use rustc_span::symbol::{Ident, Symbol};
 use rustc_span::DUMMY_SP;
-use rustc_trait_selection::traits::query::normalize::AtExt;
 use rustc_trait_selection::infer::InferCtxtExt;
+use rustc_trait_selection::traits::query::normalize::AtExt;
 
 use crate::{match_def_path, must_use_attr};
 
@@ -129,10 +129,11 @@ pub fn implements_trait<'tcx>(
         return false;
     }
     let ty_params = cx.tcx.mk_substs(ty_params.iter());
-    cx.tcx.infer_ctxt().enter(|infcx|
-        infcx.type_implements_trait(trait_id, ty, ty_params, cx.param_env)
-        .must_apply_modulo_regions()
-    )
+    cx.tcx.infer_ctxt().enter(|infcx| {
+        infcx
+            .type_implements_trait(trait_id, ty, ty_params, cx.param_env)
+            .must_apply_modulo_regions()
+    })
 }
 
 /// Checks whether this type implements `Drop`.
@@ -235,6 +236,17 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
     }
 }
 
+/// Checks if the type is a reference equals to a diagnostic item
+pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
+    match ty.kind() {
+        ty::Ref(_, ref_ty, _) => match ref_ty.kind() {
+            ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did),
+            _ => false,
+        },
+        _ => false,
+    }
+}
+
 /// Checks if the type is equal to a diagnostic item
 ///
 /// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
index 2c55021ac88374f0c8fe450e8d8d3bbd75127b6d..ac885e9994404b605dd5b0d1c91df3cc45c6b970 100644 (file)
@@ -10,7 +10,7 @@
 use rustc_middle::hir::map::Map;
 use rustc_middle::mir::FakeReadCause;
 use rustc_middle::ty;
-use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
+use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 
 /// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined.
 pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Option<HirIdSet> {
@@ -67,7 +67,7 @@ fn update(&mut self, cat: &PlaceWithHirId<'tcx>) {
 }
 
 impl<'tcx> Delegate<'tcx> for MutVarsDelegate {
-    fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId, _: ConsumeMode) {}
+    fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
 
     fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, bk: ty::BorrowKind) {
         if let ty::BorrowKind::MutBorrow = bk {
@@ -199,3 +199,50 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
     recursive_visitor.visit_expr(expression);
     recursive_visitor.seen_return_break_continue
 }
+
+pub struct UsedAfterExprVisitor<'a, 'tcx> {
+    cx: &'a LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    definition: HirId,
+    past_expr: bool,
+    used_after_expr: bool,
+}
+impl<'a, 'tcx> UsedAfterExprVisitor<'a, 'tcx> {
+    pub fn is_found(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+        utils::path_to_local(expr).map_or(false, |definition| {
+            let mut visitor = UsedAfterExprVisitor {
+                cx,
+                expr,
+                definition,
+                past_expr: false,
+                used_after_expr: false,
+            };
+            utils::get_enclosing_block(cx, definition).map_or(false, |block| {
+                visitor.visit_block(block);
+                visitor.used_after_expr
+            })
+        })
+    }
+}
+
+impl<'a, 'tcx> intravisit::Visitor<'tcx> for UsedAfterExprVisitor<'a, 'tcx> {
+    type Map = Map<'tcx>;
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+    }
+
+    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+        if self.used_after_expr {
+            return;
+        }
+
+        if expr.hir_id == self.expr.hir_id {
+            self.past_expr = true;
+        } else if self.past_expr && utils::path_to_local_id(expr, self.definition) {
+            self.used_after_expr = true;
+        } else {
+            intravisit::walk_expr(self, expr);
+        }
+    }
+}
index 2d3c65c1d3982fa68d7555c7308ee11aecbbde2a..bf9cfb61b46d1a0e5d9895a91f50d36cb4699b5d 100644 (file)
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2021-07-01"
+channel = "nightly-2021-07-15"
 components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
diff --git a/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/auxiliary/proc_macro_derive.rs
new file mode 100644 (file)
index 0000000..6452189
--- /dev/null
@@ -0,0 +1,18 @@
+// compile-flags: --emit=link
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+
+#[proc_macro_derive(DeriveSomething)]
+pub fn derive(_: TokenStream) -> TokenStream {
+    "fn _f() -> Vec<u8> { vec![] }".parse().unwrap()
+}
+
+#[proc_macro]
+pub fn foo_bar(_: TokenStream) -> TokenStream {
+    "fn issue_7422() { eprintln!(); }".parse().unwrap()
+}
index 4ae6864cbb0b8b23f5a9c6d6213fea82b53aea73..e9f042ddefcde5711e361c3979854c555c2272f5 100644 (file)
@@ -1,9 +1,17 @@
-// #![warn(clippy::nonstandard_macro_braces)]
+// aux-build:proc_macro_derive.rs
 
+#![warn(clippy::nonstandard_macro_braces)]
+
+extern crate proc_macro_derive;
 extern crate quote;
 
 use quote::quote;
 
+#[derive(proc_macro_derive::DeriveSomething)]
+pub struct S;
+
+proc_macro_derive::foo_bar!();
+
 #[rustfmt::skip]
 macro_rules! test {
     () => {
index 7bcd1829524ddcb208ae6e791a26c6fb2757e1ad..86063a08280867dd59589b58ecf021ad35b608f4 100644 (file)
@@ -1,54 +1,54 @@
 error: use of irregular braces for `vec!` macro
-  --> $DIR/conf_nonstandard_macro_braces.rs:29:13
+  --> $DIR/conf_nonstandard_macro_braces.rs:37:13
    |
 LL |     let _ = vec! {1, 2, 3};
    |             ^^^^^^^^^^^^^^
    |
    = note: `-D clippy::nonstandard-macro-braces` implied by `-D warnings`
 help: consider writing `vec![1, 2, 3]`
-  --> $DIR/conf_nonstandard_macro_braces.rs:29:13
+  --> $DIR/conf_nonstandard_macro_braces.rs:37:13
    |
 LL |     let _ = vec! {1, 2, 3};
    |             ^^^^^^^^^^^^^^
 
 error: use of irregular braces for `format!` macro
-  --> $DIR/conf_nonstandard_macro_braces.rs:30:13
+  --> $DIR/conf_nonstandard_macro_braces.rs:38:13
    |
 LL |     let _ = format!["ugh {} stop being such a good compiler", "hello"];
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 help: consider writing `format!("ugh () stop being such a good compiler", "hello")`
-  --> $DIR/conf_nonstandard_macro_braces.rs:30:13
+  --> $DIR/conf_nonstandard_macro_braces.rs:38:13
    |
 LL |     let _ = format!["ugh {} stop being such a good compiler", "hello"];
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: use of irregular braces for `quote!` macro
-  --> $DIR/conf_nonstandard_macro_braces.rs:31:13
+  --> $DIR/conf_nonstandard_macro_braces.rs:39:13
    |
 LL |     let _ = quote!(let x = 1;);
    |             ^^^^^^^^^^^^^^^^^^
    |
 help: consider writing `quote! {let x = 1;}`
-  --> $DIR/conf_nonstandard_macro_braces.rs:31:13
+  --> $DIR/conf_nonstandard_macro_braces.rs:39:13
    |
 LL |     let _ = quote!(let x = 1;);
    |             ^^^^^^^^^^^^^^^^^^
 
 error: use of irregular braces for `quote::quote!` macro
-  --> $DIR/conf_nonstandard_macro_braces.rs:32:13
+  --> $DIR/conf_nonstandard_macro_braces.rs:40:13
    |
 LL |     let _ = quote::quote!(match match match);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 help: consider writing `quote::quote! {match match match}`
-  --> $DIR/conf_nonstandard_macro_braces.rs:32:13
+  --> $DIR/conf_nonstandard_macro_braces.rs:40:13
    |
 LL |     let _ = quote::quote!(match match match);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: use of irregular braces for `vec!` macro
-  --> $DIR/conf_nonstandard_macro_braces.rs:10:9
+  --> $DIR/conf_nonstandard_macro_braces.rs:18:9
    |
 LL |         vec!{0, 0, 0}
    |         ^^^^^^^^^^^^^
@@ -57,7 +57,7 @@ LL |     let _ = test!();
    |             ------- in this macro invocation
    |
 help: consider writing `vec![0, 0, 0]`
-  --> $DIR/conf_nonstandard_macro_braces.rs:10:9
+  --> $DIR/conf_nonstandard_macro_braces.rs:18:9
    |
 LL |         vec!{0, 0, 0}
    |         ^^^^^^^^^^^^^
@@ -67,25 +67,25 @@ LL |     let _ = test!();
    = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: use of irregular braces for `type_pos!` macro
-  --> $DIR/conf_nonstandard_macro_braces.rs:41:12
+  --> $DIR/conf_nonstandard_macro_braces.rs:49:12
    |
 LL |     let _: type_pos!(usize) = vec![];
    |            ^^^^^^^^^^^^^^^^
    |
 help: consider writing `type_pos![usize]`
-  --> $DIR/conf_nonstandard_macro_braces.rs:41:12
+  --> $DIR/conf_nonstandard_macro_braces.rs:49:12
    |
 LL |     let _: type_pos!(usize) = vec![];
    |            ^^^^^^^^^^^^^^^^
 
 error: use of irregular braces for `eprint!` macro
-  --> $DIR/conf_nonstandard_macro_braces.rs:43:5
+  --> $DIR/conf_nonstandard_macro_braces.rs:51:5
    |
 LL |     eprint!("test if user config overrides defaults");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 help: consider writing `eprint!["test if user config overrides defaults"];`
-  --> $DIR/conf_nonstandard_macro_braces.rs:43:5
+  --> $DIR/conf_nonstandard_macro_braces.rs:51:5
    |
 LL |     eprint!("test if user config overrides defaults");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/false_positives.rs b/src/tools/clippy/tests/ui/branches_sharing_code/false_positives.rs
new file mode 100644 (file)
index 0000000..7f42df4
--- /dev/null
@@ -0,0 +1,28 @@
+#![allow(dead_code)]
+#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)]
+
+// ##################################
+// # Issue clippy#7369
+// ##################################
+#[derive(Debug)]
+pub struct FooBar {
+    foo: Vec<u32>,
+}
+
+impl FooBar {
+    pub fn bar(&mut self) {
+        if true {
+            self.foo.pop();
+        } else {
+            self.baz();
+
+            self.foo.pop();
+
+            self.baz()
+        }
+    }
+
+    fn baz(&mut self) {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7423.rs b/src/tools/clippy/tests/ui/crashes/ice-7423.rs
new file mode 100644 (file)
index 0000000..31340b0
--- /dev/null
@@ -0,0 +1,13 @@
+pub trait Trait {
+    fn f();
+}
+
+impl Trait for usize {
+    fn f() {
+        extern "C" {
+            fn g() -> usize;
+        }
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback.rs b/src/tools/clippy/tests/ui/default_numeric_fallback.rs
deleted file mode 100644 (file)
index c0625fd..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-// aux-build:macro_rules.rs
-
-#![warn(clippy::default_numeric_fallback)]
-#![allow(unused)]
-#![allow(clippy::never_loop)]
-#![allow(clippy::no_effect)]
-#![allow(clippy::unnecessary_operation)]
-#![allow(clippy::branches_sharing_code)]
-
-#[macro_use]
-extern crate macro_rules;
-
-mod basic_expr {
-    fn test() {
-        // Should lint unsuffixed literals typed `i32`.
-        let x = 22;
-        let x = [1, 2, 3];
-        let x = if true { (1, 2) } else { (3, 4) };
-        let x = match 1 {
-            1 => 1,
-            _ => 2,
-        };
-
-        // Should lint unsuffixed literals typed `f64`.
-        let x = 0.12;
-
-        // Should NOT lint suffixed literals.
-        let x = 22_i32;
-        let x = 0.12_f64;
-
-        // Should NOT lint literals in init expr if `Local` has a type annotation.
-        let x: f64 = 0.1;
-        let x: [i32; 3] = [1, 2, 3];
-        let x: (i32, i32) = if true { (1, 2) } else { (3, 4) };
-        let x: _ = 1;
-    }
-}
-
-mod nested_local {
-    fn test() {
-        let x: _ = {
-            // Should lint this because this literal is not bound to any types.
-            let y = 1;
-
-            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
-            1
-        };
-
-        let x: _ = if true {
-            // Should lint this because this literal is not bound to any types.
-            let y = 1;
-
-            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
-            1
-        } else {
-            // Should lint this because this literal is not bound to any types.
-            let y = 1;
-
-            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
-            2
-        };
-    }
-}
-
-mod function_def {
-    fn ret_i32() -> i32 {
-        // Even though the output type is specified,
-        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
-        1
-    }
-
-    fn test() {
-        // Should lint this because return type is inferred to `i32` and NOT bound to a concrete
-        // type.
-        let f = || -> _ { 1 };
-
-        // Even though the output type is specified,
-        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
-        let f = || -> i32 { 1 };
-    }
-}
-
-mod function_calls {
-    fn concrete_arg(x: i32) {}
-
-    fn generic_arg<T>(t: T) {}
-
-    fn test() {
-        // Should NOT lint this because the argument type is bound to a concrete type.
-        concrete_arg(1);
-
-        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
-        generic_arg(1);
-
-        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
-        let x: _ = generic_arg(1);
-    }
-}
-
-mod struct_ctor {
-    struct ConcreteStruct {
-        x: i32,
-    }
-
-    struct GenericStruct<T> {
-        x: T,
-    }
-
-    fn test() {
-        // Should NOT lint this because the field type is bound to a concrete type.
-        ConcreteStruct { x: 1 };
-
-        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
-        GenericStruct { x: 1 };
-
-        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
-        let _ = GenericStruct { x: 1 };
-    }
-}
-
-mod method_calls {
-    struct StructForMethodCallTest {}
-
-    impl StructForMethodCallTest {
-        fn concrete_arg(&self, x: i32) {}
-
-        fn generic_arg<T>(&self, t: T) {}
-    }
-
-    fn test() {
-        let s = StructForMethodCallTest {};
-
-        // Should NOT lint this because the argument type is bound to a concrete type.
-        s.concrete_arg(1);
-
-        // Should lint this because the argument type is bound to a concrete type.
-        s.generic_arg(1);
-    }
-}
-
-mod in_macro {
-    macro_rules! internal_macro {
-        () => {
-            let x = 22;
-        };
-    }
-
-    // Should lint in internal macro.
-    fn internal() {
-        internal_macro!();
-    }
-
-    // Should NOT lint in external macro.
-    fn external() {
-        default_numeric_fallback!();
-    }
-}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback.stderr
deleted file mode 100644 (file)
index 5862cd9..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:16:17
-   |
-LL |         let x = 22;
-   |                 ^^ help: consider adding suffix: `22_i32`
-   |
-   = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:17:18
-   |
-LL |         let x = [1, 2, 3];
-   |                  ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:17:21
-   |
-LL |         let x = [1, 2, 3];
-   |                     ^ help: consider adding suffix: `2_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:17:24
-   |
-LL |         let x = [1, 2, 3];
-   |                        ^ help: consider adding suffix: `3_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:18:28
-   |
-LL |         let x = if true { (1, 2) } else { (3, 4) };
-   |                            ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:18:31
-   |
-LL |         let x = if true { (1, 2) } else { (3, 4) };
-   |                               ^ help: consider adding suffix: `2_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:18:44
-   |
-LL |         let x = if true { (1, 2) } else { (3, 4) };
-   |                                            ^ help: consider adding suffix: `3_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:18:47
-   |
-LL |         let x = if true { (1, 2) } else { (3, 4) };
-   |                                               ^ help: consider adding suffix: `4_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:19:23
-   |
-LL |         let x = match 1 {
-   |                       ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:20:13
-   |
-LL |             1 => 1,
-   |             ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:20:18
-   |
-LL |             1 => 1,
-   |                  ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:21:18
-   |
-LL |             _ => 2,
-   |                  ^ help: consider adding suffix: `2_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:25:17
-   |
-LL |         let x = 0.12;
-   |                 ^^^^ help: consider adding suffix: `0.12_f64`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:43:21
-   |
-LL |             let y = 1;
-   |                     ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:51:21
-   |
-LL |             let y = 1;
-   |                     ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:57:21
-   |
-LL |             let y = 1;
-   |                     ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:69:9
-   |
-LL |         1
-   |         ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:75:27
-   |
-LL |         let f = || -> _ { 1 };
-   |                           ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:79:29
-   |
-LL |         let f = || -> i32 { 1 };
-   |                             ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:93:21
-   |
-LL |         generic_arg(1);
-   |                     ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:96:32
-   |
-LL |         let x: _ = generic_arg(1);
-   |                                ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:114:28
-   |
-LL |         GenericStruct { x: 1 };
-   |                            ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:117:36
-   |
-LL |         let _ = GenericStruct { x: 1 };
-   |                                    ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:137:23
-   |
-LL |         s.generic_arg(1);
-   |                       ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:144:21
-   |
-LL |             let x = 22;
-   |                     ^^ help: consider adding suffix: `22_i32`
-...
-LL |         internal_macro!();
-   |         ------------------ in this macro invocation
-   |
-   = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to 25 previous errors
-
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
new file mode 100644 (file)
index 0000000..1b0e754
--- /dev/null
@@ -0,0 +1,174 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::default_numeric_fallback)]
+#![allow(unused)]
+#![allow(clippy::never_loop)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::branches_sharing_code)]
+#![allow(clippy::match_single_binding)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+    fn test() {
+        // Should lint unsuffixed literals typed `f64`.
+        let x = 0.12_f64;
+        let x = [1.0_f64, 2.0_f64, 3.0_f64];
+        let x = if true { (1.0_f64, 2.0_f64) } else { (3.0_f64, 4.0_f64) };
+        let x = match 1.0_f64 {
+            _ => 1.0_f64,
+        };
+
+        // Should NOT lint suffixed literals.
+        let x = 0.12_f64;
+
+        // Should NOT lint literals in init expr if `Local` has a type annotation.
+        let x: f64 = 0.1;
+        let x: [f64; 3] = [1., 2., 3.];
+        let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) };
+        let x: _ = 1.;
+    }
+}
+
+mod nested_local {
+    fn test() {
+        let x: _ = {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1.0_f64;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            1.
+        };
+
+        let x: _ = if true {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1.0_f64;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            1.
+        } else {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1.0_f64;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            2.
+        };
+    }
+}
+
+mod function_def {
+    fn ret_f64() -> f64 {
+        // Even though the output type is specified,
+        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+        1.0_f64
+    }
+
+    fn test() {
+        // Should lint this because return type is inferred to `f64` and NOT bound to a concrete
+        // type.
+        let f = || -> _ { 1.0_f64 };
+
+        // Even though the output type is specified,
+        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+        let f = || -> f64 { 1.0_f64 };
+    }
+}
+
+mod function_calls {
+    fn concrete_arg(f: f64) {}
+
+    fn generic_arg<T>(t: T) {}
+
+    fn test() {
+        // Should NOT lint this because the argument type is bound to a concrete type.
+        concrete_arg(1.);
+
+        // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+        generic_arg(1.0_f64);
+
+        // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+        let x: _ = generic_arg(1.0_f64);
+    }
+}
+
+mod struct_ctor {
+    struct ConcreteStruct {
+        x: f64,
+    }
+
+    struct GenericStruct<T> {
+        x: T,
+    }
+
+    fn test() {
+        // Should NOT lint this because the field type is bound to a concrete type.
+        ConcreteStruct { x: 1. };
+
+        // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+        GenericStruct { x: 1.0_f64 };
+
+        // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+        let _ = GenericStruct { x: 1.0_f64 };
+    }
+}
+
+mod enum_ctor {
+    enum ConcreteEnum {
+        X(f64),
+    }
+
+    enum GenericEnum<T> {
+        X(T),
+    }
+
+    fn test() {
+        // Should NOT lint this because the field type is bound to a concrete type.
+        ConcreteEnum::X(1.);
+
+        // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+        GenericEnum::X(1.0_f64);
+    }
+}
+
+mod method_calls {
+    struct StructForMethodCallTest {}
+
+    impl StructForMethodCallTest {
+        fn concrete_arg(&self, f: f64) {}
+
+        fn generic_arg<T>(&self, t: T) {}
+    }
+
+    fn test() {
+        let s = StructForMethodCallTest {};
+
+        // Should NOT lint this because the argument type is bound to a concrete type.
+        s.concrete_arg(1.);
+
+        // Should lint this because the argument type is bound to a concrete type.
+        s.generic_arg(1.0_f64);
+    }
+}
+
+mod in_macro {
+    macro_rules! internal_macro {
+        () => {
+            let x = 22.0_f64;
+        };
+    }
+
+    // Should lint in internal macro.
+    fn internal() {
+        internal_macro!();
+    }
+
+    // Should NOT lint in external macro.
+    fn external() {
+        default_numeric_fallback!();
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
new file mode 100644 (file)
index 0000000..e968777
--- /dev/null
@@ -0,0 +1,174 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::default_numeric_fallback)]
+#![allow(unused)]
+#![allow(clippy::never_loop)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::branches_sharing_code)]
+#![allow(clippy::match_single_binding)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+    fn test() {
+        // Should lint unsuffixed literals typed `f64`.
+        let x = 0.12;
+        let x = [1., 2., 3.];
+        let x = if true { (1., 2.) } else { (3., 4.) };
+        let x = match 1. {
+            _ => 1.,
+        };
+
+        // Should NOT lint suffixed literals.
+        let x = 0.12_f64;
+
+        // Should NOT lint literals in init expr if `Local` has a type annotation.
+        let x: f64 = 0.1;
+        let x: [f64; 3] = [1., 2., 3.];
+        let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) };
+        let x: _ = 1.;
+    }
+}
+
+mod nested_local {
+    fn test() {
+        let x: _ = {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1.;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            1.
+        };
+
+        let x: _ = if true {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1.;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            1.
+        } else {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1.;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            2.
+        };
+    }
+}
+
+mod function_def {
+    fn ret_f64() -> f64 {
+        // Even though the output type is specified,
+        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+        1.
+    }
+
+    fn test() {
+        // Should lint this because return type is inferred to `f64` and NOT bound to a concrete
+        // type.
+        let f = || -> _ { 1. };
+
+        // Even though the output type is specified,
+        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+        let f = || -> f64 { 1. };
+    }
+}
+
+mod function_calls {
+    fn concrete_arg(f: f64) {}
+
+    fn generic_arg<T>(t: T) {}
+
+    fn test() {
+        // Should NOT lint this because the argument type is bound to a concrete type.
+        concrete_arg(1.);
+
+        // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+        generic_arg(1.);
+
+        // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+        let x: _ = generic_arg(1.);
+    }
+}
+
+mod struct_ctor {
+    struct ConcreteStruct {
+        x: f64,
+    }
+
+    struct GenericStruct<T> {
+        x: T,
+    }
+
+    fn test() {
+        // Should NOT lint this because the field type is bound to a concrete type.
+        ConcreteStruct { x: 1. };
+
+        // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+        GenericStruct { x: 1. };
+
+        // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+        let _ = GenericStruct { x: 1. };
+    }
+}
+
+mod enum_ctor {
+    enum ConcreteEnum {
+        X(f64),
+    }
+
+    enum GenericEnum<T> {
+        X(T),
+    }
+
+    fn test() {
+        // Should NOT lint this because the field type is bound to a concrete type.
+        ConcreteEnum::X(1.);
+
+        // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+        GenericEnum::X(1.);
+    }
+}
+
+mod method_calls {
+    struct StructForMethodCallTest {}
+
+    impl StructForMethodCallTest {
+        fn concrete_arg(&self, f: f64) {}
+
+        fn generic_arg<T>(&self, t: T) {}
+    }
+
+    fn test() {
+        let s = StructForMethodCallTest {};
+
+        // Should NOT lint this because the argument type is bound to a concrete type.
+        s.concrete_arg(1.);
+
+        // Should lint this because the argument type is bound to a concrete type.
+        s.generic_arg(1.);
+    }
+}
+
+mod in_macro {
+    macro_rules! internal_macro {
+        () => {
+            let x = 22.;
+        };
+    }
+
+    // Should lint in internal macro.
+    fn internal() {
+        internal_macro!();
+    }
+
+    // Should NOT lint in external macro.
+    fn external() {
+        default_numeric_fallback!();
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr
new file mode 100644 (file)
index 0000000..961c7cb
--- /dev/null
@@ -0,0 +1,147 @@
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:18:17
+   |
+LL |         let x = 0.12;
+   |                 ^^^^ help: consider adding suffix: `0.12_f64`
+   |
+   = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:19:18
+   |
+LL |         let x = [1., 2., 3.];
+   |                  ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:19:22
+   |
+LL |         let x = [1., 2., 3.];
+   |                      ^^ help: consider adding suffix: `2.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:19:26
+   |
+LL |         let x = [1., 2., 3.];
+   |                          ^^ help: consider adding suffix: `3.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:20:28
+   |
+LL |         let x = if true { (1., 2.) } else { (3., 4.) };
+   |                            ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:20:32
+   |
+LL |         let x = if true { (1., 2.) } else { (3., 4.) };
+   |                                ^^ help: consider adding suffix: `2.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:20:46
+   |
+LL |         let x = if true { (1., 2.) } else { (3., 4.) };
+   |                                              ^^ help: consider adding suffix: `3.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:20:50
+   |
+LL |         let x = if true { (1., 2.) } else { (3., 4.) };
+   |                                                  ^^ help: consider adding suffix: `4.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:21:23
+   |
+LL |         let x = match 1. {
+   |                       ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:22:18
+   |
+LL |             _ => 1.,
+   |                  ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:40:21
+   |
+LL |             let y = 1.;
+   |                     ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:48:21
+   |
+LL |             let y = 1.;
+   |                     ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:54:21
+   |
+LL |             let y = 1.;
+   |                     ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:66:9
+   |
+LL |         1.
+   |         ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:72:27
+   |
+LL |         let f = || -> _ { 1. };
+   |                           ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:76:29
+   |
+LL |         let f = || -> f64 { 1. };
+   |                             ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:90:21
+   |
+LL |         generic_arg(1.);
+   |                     ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:93:32
+   |
+LL |         let x: _ = generic_arg(1.);
+   |                                ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:111:28
+   |
+LL |         GenericStruct { x: 1. };
+   |                            ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:114:36
+   |
+LL |         let _ = GenericStruct { x: 1. };
+   |                                    ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:132:24
+   |
+LL |         GenericEnum::X(1.);
+   |                        ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:152:23
+   |
+LL |         s.generic_arg(1.);
+   |                       ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:159:21
+   |
+LL |             let x = 22.;
+   |                     ^^^ help: consider adding suffix: `22.0_f64`
+...
+LL |         internal_macro!();
+   |         ------------------ in this macro invocation
+   |
+   = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 23 previous errors
+
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
new file mode 100644 (file)
index 0000000..55c082f
--- /dev/null
@@ -0,0 +1,173 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::default_numeric_fallback)]
+#![allow(unused)]
+#![allow(clippy::never_loop)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::branches_sharing_code)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+    fn test() {
+        // Should lint unsuffixed literals typed `i32`.
+        let x = 22_i32;
+        let x = [1_i32, 2_i32, 3_i32];
+        let x = if true { (1_i32, 2_i32) } else { (3_i32, 4_i32) };
+        let x = match 1_i32 {
+            1_i32 => 1_i32,
+            _ => 2_i32,
+        };
+
+        // Should NOT lint suffixed literals.
+        let x = 22_i32;
+
+        // Should NOT lint literals in init expr if `Local` has a type annotation.
+        let x: [i32; 3] = [1, 2, 3];
+        let x: (i32, i32) = if true { (1, 2) } else { (3, 4) };
+        let x: _ = 1;
+    }
+}
+
+mod nested_local {
+    fn test() {
+        let x: _ = {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1_i32;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            1
+        };
+
+        let x: _ = if true {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1_i32;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            1
+        } else {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1_i32;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            2
+        };
+    }
+}
+
+mod function_def {
+    fn ret_i32() -> i32 {
+        // Even though the output type is specified,
+        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+        1_i32
+    }
+
+    fn test() {
+        // Should lint this because return type is inferred to `i32` and NOT bound to a concrete
+        // type.
+        let f = || -> _ { 1_i32 };
+
+        // Even though the output type is specified,
+        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+        let f = || -> i32 { 1_i32 };
+    }
+}
+
+mod function_calls {
+    fn concrete_arg(x: i32) {}
+
+    fn generic_arg<T>(t: T) {}
+
+    fn test() {
+        // Should NOT lint this because the argument type is bound to a concrete type.
+        concrete_arg(1);
+
+        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+        generic_arg(1_i32);
+
+        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+        let x: _ = generic_arg(1_i32);
+    }
+}
+
+mod struct_ctor {
+    struct ConcreteStruct {
+        x: i32,
+    }
+
+    struct GenericStruct<T> {
+        x: T,
+    }
+
+    fn test() {
+        // Should NOT lint this because the field type is bound to a concrete type.
+        ConcreteStruct { x: 1 };
+
+        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+        GenericStruct { x: 1_i32 };
+
+        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+        let _ = GenericStruct { x: 1_i32 };
+    }
+}
+
+mod enum_ctor {
+    enum ConcreteEnum {
+        X(i32),
+    }
+
+    enum GenericEnum<T> {
+        X(T),
+    }
+
+    fn test() {
+        // Should NOT lint this because the field type is bound to a concrete type.
+        ConcreteEnum::X(1);
+
+        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+        GenericEnum::X(1_i32);
+    }
+}
+
+mod method_calls {
+    struct StructForMethodCallTest {}
+
+    impl StructForMethodCallTest {
+        fn concrete_arg(&self, x: i32) {}
+
+        fn generic_arg<T>(&self, t: T) {}
+    }
+
+    fn test() {
+        let s = StructForMethodCallTest {};
+
+        // Should NOT lint this because the argument type is bound to a concrete type.
+        s.concrete_arg(1);
+
+        // Should lint this because the argument type is bound to a concrete type.
+        s.generic_arg(1_i32);
+    }
+}
+
+mod in_macro {
+    macro_rules! internal_macro {
+        () => {
+            let x = 22_i32;
+        };
+    }
+
+    // Should lint in internal macro.
+    fn internal() {
+        internal_macro!();
+    }
+
+    // Should NOT lint in external macro.
+    fn external() {
+        default_numeric_fallback!();
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
new file mode 100644 (file)
index 0000000..e0a4828
--- /dev/null
@@ -0,0 +1,173 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::default_numeric_fallback)]
+#![allow(unused)]
+#![allow(clippy::never_loop)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::branches_sharing_code)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+    fn test() {
+        // Should lint unsuffixed literals typed `i32`.
+        let x = 22;
+        let x = [1, 2, 3];
+        let x = if true { (1, 2) } else { (3, 4) };
+        let x = match 1 {
+            1 => 1,
+            _ => 2,
+        };
+
+        // Should NOT lint suffixed literals.
+        let x = 22_i32;
+
+        // Should NOT lint literals in init expr if `Local` has a type annotation.
+        let x: [i32; 3] = [1, 2, 3];
+        let x: (i32, i32) = if true { (1, 2) } else { (3, 4) };
+        let x: _ = 1;
+    }
+}
+
+mod nested_local {
+    fn test() {
+        let x: _ = {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            1
+        };
+
+        let x: _ = if true {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            1
+        } else {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            2
+        };
+    }
+}
+
+mod function_def {
+    fn ret_i32() -> i32 {
+        // Even though the output type is specified,
+        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+        1
+    }
+
+    fn test() {
+        // Should lint this because return type is inferred to `i32` and NOT bound to a concrete
+        // type.
+        let f = || -> _ { 1 };
+
+        // Even though the output type is specified,
+        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+        let f = || -> i32 { 1 };
+    }
+}
+
+mod function_calls {
+    fn concrete_arg(x: i32) {}
+
+    fn generic_arg<T>(t: T) {}
+
+    fn test() {
+        // Should NOT lint this because the argument type is bound to a concrete type.
+        concrete_arg(1);
+
+        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+        generic_arg(1);
+
+        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+        let x: _ = generic_arg(1);
+    }
+}
+
+mod struct_ctor {
+    struct ConcreteStruct {
+        x: i32,
+    }
+
+    struct GenericStruct<T> {
+        x: T,
+    }
+
+    fn test() {
+        // Should NOT lint this because the field type is bound to a concrete type.
+        ConcreteStruct { x: 1 };
+
+        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+        GenericStruct { x: 1 };
+
+        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+        let _ = GenericStruct { x: 1 };
+    }
+}
+
+mod enum_ctor {
+    enum ConcreteEnum {
+        X(i32),
+    }
+
+    enum GenericEnum<T> {
+        X(T),
+    }
+
+    fn test() {
+        // Should NOT lint this because the field type is bound to a concrete type.
+        ConcreteEnum::X(1);
+
+        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+        GenericEnum::X(1);
+    }
+}
+
+mod method_calls {
+    struct StructForMethodCallTest {}
+
+    impl StructForMethodCallTest {
+        fn concrete_arg(&self, x: i32) {}
+
+        fn generic_arg<T>(&self, t: T) {}
+    }
+
+    fn test() {
+        let s = StructForMethodCallTest {};
+
+        // Should NOT lint this because the argument type is bound to a concrete type.
+        s.concrete_arg(1);
+
+        // Should lint this because the argument type is bound to a concrete type.
+        s.generic_arg(1);
+    }
+}
+
+mod in_macro {
+    macro_rules! internal_macro {
+        () => {
+            let x = 22;
+        };
+    }
+
+    // Should lint in internal macro.
+    fn internal() {
+        internal_macro!();
+    }
+
+    // Should NOT lint in external macro.
+    fn external() {
+        default_numeric_fallback!();
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr
new file mode 100644 (file)
index 0000000..5edf48b
--- /dev/null
@@ -0,0 +1,159 @@
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:17:17
+   |
+LL |         let x = 22;
+   |                 ^^ help: consider adding suffix: `22_i32`
+   |
+   = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:18:18
+   |
+LL |         let x = [1, 2, 3];
+   |                  ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:18:21
+   |
+LL |         let x = [1, 2, 3];
+   |                     ^ help: consider adding suffix: `2_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:18:24
+   |
+LL |         let x = [1, 2, 3];
+   |                        ^ help: consider adding suffix: `3_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:19:28
+   |
+LL |         let x = if true { (1, 2) } else { (3, 4) };
+   |                            ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:19:31
+   |
+LL |         let x = if true { (1, 2) } else { (3, 4) };
+   |                               ^ help: consider adding suffix: `2_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:19:44
+   |
+LL |         let x = if true { (1, 2) } else { (3, 4) };
+   |                                            ^ help: consider adding suffix: `3_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:19:47
+   |
+LL |         let x = if true { (1, 2) } else { (3, 4) };
+   |                                               ^ help: consider adding suffix: `4_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:20:23
+   |
+LL |         let x = match 1 {
+   |                       ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:21:13
+   |
+LL |             1 => 1,
+   |             ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:21:18
+   |
+LL |             1 => 1,
+   |                  ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:22:18
+   |
+LL |             _ => 2,
+   |                  ^ help: consider adding suffix: `2_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:39:21
+   |
+LL |             let y = 1;
+   |                     ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:47:21
+   |
+LL |             let y = 1;
+   |                     ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:53:21
+   |
+LL |             let y = 1;
+   |                     ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:65:9
+   |
+LL |         1
+   |         ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:71:27
+   |
+LL |         let f = || -> _ { 1 };
+   |                           ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:75:29
+   |
+LL |         let f = || -> i32 { 1 };
+   |                             ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:89:21
+   |
+LL |         generic_arg(1);
+   |                     ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:92:32
+   |
+LL |         let x: _ = generic_arg(1);
+   |                                ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:110:28
+   |
+LL |         GenericStruct { x: 1 };
+   |                            ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:113:36
+   |
+LL |         let _ = GenericStruct { x: 1 };
+   |                                    ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:131:24
+   |
+LL |         GenericEnum::X(1);
+   |                        ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:151:23
+   |
+LL |         s.generic_arg(1);
+   |                       ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:158:21
+   |
+LL |             let x = 22;
+   |                     ^^ help: consider adding suffix: `22_i32`
+...
+LL |         internal_macro!();
+   |         ------------------ in this macro invocation
+   |
+   = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 25 previous errors
+
index 78e87bc69062a351c4e3bccbd5e773b8b277c443..8e8324b30f0fb02cac16001c22685a7205a48e56 100644 (file)
@@ -34,3 +34,10 @@ fn in_code_block() {}
 /// - This `item has unbalanced tick marks
 /// - This item needs backticks_here
 fn other_markdown() {}
+
+#[rustfmt::skip]
+/// - ```rust
+///   /// `lol`
+///   pub struct Struct;
+///   ```
+fn iss_7421() {}
index 9e752311c67785c55478511712db79a6a003109d..91b837f9a85884d19cc74471d262c1016987cf79 100644 (file)
@@ -220,3 +220,19 @@ impl std::ops::Deref for Bar {
 fn test_deref_with_trait_method() {
     let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
 }
+
+fn mutable_closure_used_again(x: Vec<i32>, y: Vec<i32>, z: Vec<i32>) {
+    let mut res = Vec::new();
+    let mut add_to_res = |n| res.push(n);
+    x.into_iter().for_each(&mut add_to_res);
+    y.into_iter().for_each(&mut add_to_res);
+    z.into_iter().for_each(add_to_res);
+}
+
+fn mutable_closure_in_loop() {
+    let mut value = 0;
+    let mut closure = |n| value += n;
+    for _ in 0..5 {
+        Some(1).map(&mut closure);
+    }
+}
index 44be4628cbd34227eb51118c6698aa38f9d67efd..1b53700289db303678d57850d0b59eb7a17b998d 100644 (file)
@@ -220,3 +220,19 @@ fn deref(&self) -> &str {
 fn test_deref_with_trait_method() {
     let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
 }
+
+fn mutable_closure_used_again(x: Vec<i32>, y: Vec<i32>, z: Vec<i32>) {
+    let mut res = Vec::new();
+    let mut add_to_res = |n| res.push(n);
+    x.into_iter().for_each(|x| add_to_res(x));
+    y.into_iter().for_each(|x| add_to_res(x));
+    z.into_iter().for_each(|x| add_to_res(x));
+}
+
+fn mutable_closure_in_loop() {
+    let mut value = 0;
+    let mut closure = |n| value += n;
+    for _ in 0..5 {
+        Some(1).map(|n| closure(n));
+    }
+}
index 8795d3b42c65af72146bf63c1d110246d81347f0..28da8941346192d0a6e6c61c478a1cc57bb5415d 100644 (file)
@@ -82,5 +82,29 @@ error: redundant closure
 LL |     let a = Some(1u8).map(|a| closure(a));
    |                           ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure`
 
-error: aborting due to 13 previous errors
+error: redundant closure
+  --> $DIR/eta.rs:227:28
+   |
+LL |     x.into_iter().for_each(|x| add_to_res(x));
+   |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
+
+error: redundant closure
+  --> $DIR/eta.rs:228:28
+   |
+LL |     y.into_iter().for_each(|x| add_to_res(x));
+   |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
+
+error: redundant closure
+  --> $DIR/eta.rs:229:28
+   |
+LL |     z.into_iter().for_each(|x| add_to_res(x));
+   |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res`
+
+error: redundant closure
+  --> $DIR/eta.rs:236:21
+   |
+LL |         Some(1).map(|n| closure(n));
+   |                     ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
+
+error: aborting due to 17 previous errors
 
index 77cadb99bb5510601922ea7034a003a2826669a2..b94ec6403ddcea3c63bfd188bbd6914679581c70 100644 (file)
@@ -1,10 +1,11 @@
-error: use of `writeln!(stderr(), ...).unwrap()`. Consider using `eprintln!` instead
+error: use of `writeln!(stderr(), ...).unwrap()`
   --> $DIR/explicit_write_non_rustfix.rs:7:5
    |
 LL |     writeln!(std::io::stderr(), "foo {}", bar).unwrap();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::explicit-write` implied by `-D warnings`
+   = help: consider using `eprintln!` instead
 
 error: aborting due to previous error
 
index e4cfb005fd1d0d8e8a0ac4bd42392dddb56df35c..5dd64140e81165880f3d6285c60825838d96a0e2 100644 (file)
@@ -69,4 +69,6 @@ fn main() {
     // Wrap it with braces
     let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
     let _s: String = (&*v.join("\n")).to_string();
+
+    format!("prepend {:+}", "s");
 }
index 683957f0ff0f79cb452811b66b0db83d90f55c8b..4599fb5207ea85c280f07b863d39f798f6262eaa 100644 (file)
@@ -71,4 +71,6 @@ fn main() {
     // Wrap it with braces
     let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
     let _s: String = format!("{}", &*v.join("\n"));
+
+    format!("prepend {:+}", "s");
 }
index 93b236f7473d5f2d3d823ae63ce8edcd37d00515..7338064646244ca7611a01d73c61203a15a0631f 100644 (file)
@@ -43,6 +43,18 @@ fn core_versions() {
     unreachable!();
 }
 
+fn assert() {
+    assert!(true);
+    assert_eq!(true, true);
+    assert_ne!(true, false);
+}
+
+fn assert_msg() {
+    assert!(true, "this should not panic");
+    assert_eq!(true, true, "this should not panic");
+    assert_ne!(true, false, "this should not panic");
+}
+
 fn debug_assert() {
     debug_assert!(true);
     debug_assert_eq!(true, true);
@@ -61,4 +73,8 @@ fn main() {
     unimplemented();
     unreachable();
     core_versions();
+    assert();
+    assert_msg();
+    debug_assert();
+    debug_assert_msg();
 }
diff --git a/src/tools/clippy/tests/ui/rc_mutex.rs b/src/tools/clippy/tests/ui/rc_mutex.rs
new file mode 100644 (file)
index 0000000..657a3ec
--- /dev/null
@@ -0,0 +1,34 @@
+#![warn(clippy::rc_mutex)]
+#![allow(clippy::blacklisted_name)]
+
+use std::rc::Rc;
+use std::sync::Mutex;
+
+pub struct MyStruct {
+    foo: Rc<Mutex<i32>>,
+}
+
+pub struct SubT<T> {
+    foo: T,
+}
+
+pub enum MyEnum {
+    One,
+    Two,
+}
+
+pub fn test1<T>(foo: Rc<Mutex<T>>) {}
+
+pub fn test2(foo: Rc<Mutex<MyEnum>>) {}
+
+pub fn test3(foo: Rc<Mutex<SubT<usize>>>) {}
+
+fn main() {
+    test1(Rc::new(Mutex::new(1)));
+    test2(Rc::new(Mutex::new(MyEnum::One)));
+    test3(Rc::new(Mutex::new(SubT { foo: 1 })));
+
+    let _my_struct = MyStruct {
+        foo: Rc::new(Mutex::new(1)),
+    };
+}
diff --git a/src/tools/clippy/tests/ui/rc_mutex.stderr b/src/tools/clippy/tests/ui/rc_mutex.stderr
new file mode 100644 (file)
index 0000000..8e58e2b
--- /dev/null
@@ -0,0 +1,28 @@
+error: found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
+  --> $DIR/rc_mutex.rs:8:10
+   |
+LL |     foo: Rc<Mutex<i32>>,
+   |          ^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::rc-mutex` implied by `-D warnings`
+
+error: found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
+  --> $DIR/rc_mutex.rs:20:22
+   |
+LL | pub fn test1<T>(foo: Rc<Mutex<T>>) {}
+   |                      ^^^^^^^^^^^^
+
+error: found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
+  --> $DIR/rc_mutex.rs:22:19
+   |
+LL | pub fn test2(foo: Rc<Mutex<MyEnum>>) {}
+   |                   ^^^^^^^^^^^^^^^^^
+
+error: found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
+  --> $DIR/rc_mutex.rs:24:19
+   |
+LL | pub fn test3(foo: Rc<Mutex<SubT<usize>>>) {}
+   |                   ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_allocation.fixed b/src/tools/clippy/tests/ui/redundant_allocation.fixed
deleted file mode 100644 (file)
index 6514fd6..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-// run-rustfix
-#![warn(clippy::all)]
-#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
-#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
-
-use std::boxed::Box;
-use std::rc::Rc;
-
-pub struct MyStruct {}
-
-pub struct SubT<T> {
-    foo: T,
-}
-
-pub enum MyEnum {
-    One,
-    Two,
-}
-
-// Rc<&T>
-
-pub fn test1<T>(foo: &T) {}
-
-pub fn test2(foo: &MyStruct) {}
-
-pub fn test3(foo: &MyEnum) {}
-
-pub fn test4_neg(foo: Rc<SubT<&usize>>) {}
-
-// Rc<Rc<T>>
-
-pub fn test5(a: Rc<bool>) {}
-
-// Rc<Box<T>>
-
-pub fn test6(a: Rc<bool>) {}
-
-// Box<&T>
-
-pub fn test7<T>(foo: &T) {}
-
-pub fn test8(foo: &MyStruct) {}
-
-pub fn test9(foo: &MyEnum) {}
-
-pub fn test10_neg(foo: Box<SubT<&usize>>) {}
-
-fn main() {}
index 677b3e56d4dcebe5ea58a9996b5cac7c5332cddc..1b4f2a66c705ec088d5396bc3dbed187e12579df 100644 (file)
@@ -1,10 +1,7 @@
-// run-rustfix
 #![warn(clippy::all)]
 #![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
 #![allow(clippy::blacklisted_name, unused_variables, dead_code)]
-
-use std::boxed::Box;
-use std::rc::Rc;
+#![allow(unused_imports)]
 
 pub struct MyStruct {}
 
@@ -17,32 +14,67 @@ pub enum MyEnum {
     Two,
 }
 
-// Rc<&T>
+mod outer_box {
+    use crate::MyEnum;
+    use crate::MyStruct;
+    use crate::SubT;
+    use std::boxed::Box;
+    use std::rc::Rc;
+    use std::sync::Arc;
 
-pub fn test1<T>(foo: Rc<&T>) {}
+    pub fn box_test6<T>(foo: Box<Rc<T>>) {}
 
-pub fn test2(foo: Rc<&MyStruct>) {}
+    pub fn box_test7<T>(foo: Box<Arc<T>>) {}
 
-pub fn test3(foo: Rc<&MyEnum>) {}
+    pub fn box_test8() -> Box<Rc<SubT<usize>>> {
+        unimplemented!();
+    }
 
-pub fn test4_neg(foo: Rc<SubT<&usize>>) {}
+    pub fn box_test9<T>(foo: Box<Arc<T>>) -> Box<Arc<SubT<T>>> {
+        unimplemented!();
+    }
+}
 
-// Rc<Rc<T>>
+mod outer_rc {
+    use crate::MyEnum;
+    use crate::MyStruct;
+    use crate::SubT;
+    use std::boxed::Box;
+    use std::rc::Rc;
+    use std::sync::Arc;
 
-pub fn test5(a: Rc<Rc<bool>>) {}
+    pub fn rc_test5(a: Rc<Box<bool>>) {}
 
-// Rc<Box<T>>
+    pub fn rc_test7(a: Rc<Arc<bool>>) {}
 
-pub fn test6(a: Rc<Box<bool>>) {}
+    pub fn rc_test8() -> Rc<Box<SubT<usize>>> {
+        unimplemented!();
+    }
 
-// Box<&T>
+    pub fn rc_test9<T>(foo: Rc<Arc<T>>) -> Rc<Arc<SubT<T>>> {
+        unimplemented!();
+    }
+}
 
-pub fn test7<T>(foo: Box<&T>) {}
+mod outer_arc {
+    use crate::MyEnum;
+    use crate::MyStruct;
+    use crate::SubT;
+    use std::boxed::Box;
+    use std::rc::Rc;
+    use std::sync::Arc;
 
-pub fn test8(foo: Box<&MyStruct>) {}
+    pub fn arc_test5(a: Arc<Box<bool>>) {}
 
-pub fn test9(foo: Box<&MyEnum>) {}
+    pub fn arc_test6(a: Arc<Rc<bool>>) {}
 
-pub fn test10_neg(foo: Box<SubT<&usize>>) {}
+    pub fn arc_test8() -> Arc<Box<SubT<usize>>> {
+        unimplemented!();
+    }
+
+    pub fn arc_test9<T>(foo: Arc<Rc<T>>) -> Arc<Rc<SubT<T>>> {
+        unimplemented!();
+    }
+}
 
 fn main() {}
index 92e4f67f5db6e40e6f2f0f9bd8ed422b3ec32c0c..fdab74eb538e3e7c88cc341707db01439c91395b 100644 (file)
-error: usage of `Rc<&T>`
-  --> $DIR/redundant_allocation.rs:22:22
+error: usage of `Box<Rc<T>>`
+  --> $DIR/redundant_allocation.rs:25:30
    |
-LL | pub fn test1<T>(foo: Rc<&T>) {}
-   |                      ^^^^^^ help: try: `&T`
+LL |     pub fn box_test6<T>(foo: Box<Rc<T>>) {}
+   |                              ^^^^^^^^^^
    |
    = note: `-D clippy::redundant-allocation` implied by `-D warnings`
+   = note: `Rc<T>` is already on the heap, `Box<Rc<T>>` makes an extra allocation
+   = help: consider using just `Box<T>` or `Rc<T>`
 
-error: usage of `Rc<&T>`
-  --> $DIR/redundant_allocation.rs:24:19
+error: usage of `Box<Arc<T>>`
+  --> $DIR/redundant_allocation.rs:27:30
    |
-LL | pub fn test2(foo: Rc<&MyStruct>) {}
-   |                   ^^^^^^^^^^^^^ help: try: `&MyStruct`
+LL |     pub fn box_test7<T>(foo: Box<Arc<T>>) {}
+   |                              ^^^^^^^^^^^
+   |
+   = note: `Arc<T>` is already on the heap, `Box<Arc<T>>` makes an extra allocation
+   = help: consider using just `Box<T>` or `Arc<T>`
+
+error: usage of `Box<Rc<SubT<usize>>>`
+  --> $DIR/redundant_allocation.rs:29:27
+   |
+LL |     pub fn box_test8() -> Box<Rc<SubT<usize>>> {
+   |                           ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `Rc<SubT<usize>>` is already on the heap, `Box<Rc<SubT<usize>>>` makes an extra allocation
+   = help: consider using just `Box<SubT<usize>>` or `Rc<SubT<usize>>`
+
+error: usage of `Box<Arc<T>>`
+  --> $DIR/redundant_allocation.rs:33:30
+   |
+LL |     pub fn box_test9<T>(foo: Box<Arc<T>>) -> Box<Arc<SubT<T>>> {
+   |                              ^^^^^^^^^^^
+   |
+   = note: `Arc<T>` is already on the heap, `Box<Arc<T>>` makes an extra allocation
+   = help: consider using just `Box<T>` or `Arc<T>`
+
+error: usage of `Box<Arc<SubT<T>>>`
+  --> $DIR/redundant_allocation.rs:33:46
+   |
+LL |     pub fn box_test9<T>(foo: Box<Arc<T>>) -> Box<Arc<SubT<T>>> {
+   |                                              ^^^^^^^^^^^^^^^^^
+   |
+   = note: `Arc<SubT<T>>` is already on the heap, `Box<Arc<SubT<T>>>` makes an extra allocation
+   = help: consider using just `Box<SubT<T>>` or `Arc<SubT<T>>`
+
+error: usage of `Rc<Box<bool>>`
+  --> $DIR/redundant_allocation.rs:46:24
+   |
+LL |     pub fn rc_test5(a: Rc<Box<bool>>) {}
+   |                        ^^^^^^^^^^^^^
+   |
+   = note: `Box<bool>` is already on the heap, `Rc<Box<bool>>` makes an extra allocation
+   = help: consider using just `Rc<bool>` or `Box<bool>`
+
+error: usage of `Rc<Arc<bool>>`
+  --> $DIR/redundant_allocation.rs:48:24
+   |
+LL |     pub fn rc_test7(a: Rc<Arc<bool>>) {}
+   |                        ^^^^^^^^^^^^^
+   |
+   = note: `Arc<bool>` is already on the heap, `Rc<Arc<bool>>` makes an extra allocation
+   = help: consider using just `Rc<bool>` or `Arc<bool>`
+
+error: usage of `Rc<Box<SubT<usize>>>`
+  --> $DIR/redundant_allocation.rs:50:26
+   |
+LL |     pub fn rc_test8() -> Rc<Box<SubT<usize>>> {
+   |                          ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `Box<SubT<usize>>` is already on the heap, `Rc<Box<SubT<usize>>>` makes an extra allocation
+   = help: consider using just `Rc<SubT<usize>>` or `Box<SubT<usize>>`
+
+error: usage of `Rc<Arc<T>>`
+  --> $DIR/redundant_allocation.rs:54:29
+   |
+LL |     pub fn rc_test9<T>(foo: Rc<Arc<T>>) -> Rc<Arc<SubT<T>>> {
+   |                             ^^^^^^^^^^
+   |
+   = note: `Arc<T>` is already on the heap, `Rc<Arc<T>>` makes an extra allocation
+   = help: consider using just `Rc<T>` or `Arc<T>`
 
-error: usage of `Rc<&T>`
-  --> $DIR/redundant_allocation.rs:26:19
+error: usage of `Rc<Arc<SubT<T>>>`
+  --> $DIR/redundant_allocation.rs:54:44
+   |
+LL |     pub fn rc_test9<T>(foo: Rc<Arc<T>>) -> Rc<Arc<SubT<T>>> {
+   |                                            ^^^^^^^^^^^^^^^^
    |
-LL | pub fn test3(foo: Rc<&MyEnum>) {}
-   |                   ^^^^^^^^^^^ help: try: `&MyEnum`
+   = note: `Arc<SubT<T>>` is already on the heap, `Rc<Arc<SubT<T>>>` makes an extra allocation
+   = help: consider using just `Rc<SubT<T>>` or `Arc<SubT<T>>`
 
-error: usage of `Rc<Rc<T>>`
-  --> $DIR/redundant_allocation.rs:32:17
+error: usage of `Arc<Box<bool>>`
+  --> $DIR/redundant_allocation.rs:67:25
    |
-LL | pub fn test5(a: Rc<Rc<bool>>) {}
-   |                 ^^^^^^^^^^^^ help: try: `Rc<bool>`
+LL |     pub fn arc_test5(a: Arc<Box<bool>>) {}
+   |                         ^^^^^^^^^^^^^^
+   |
+   = note: `Box<bool>` is already on the heap, `Arc<Box<bool>>` makes an extra allocation
+   = help: consider using just `Arc<bool>` or `Box<bool>`
 
-error: usage of `Rc<Box<T>>`
-  --> $DIR/redundant_allocation.rs:36:17
+error: usage of `Arc<Rc<bool>>`
+  --> $DIR/redundant_allocation.rs:69:25
+   |
+LL |     pub fn arc_test6(a: Arc<Rc<bool>>) {}
+   |                         ^^^^^^^^^^^^^
    |
-LL | pub fn test6(a: Rc<Box<bool>>) {}
-   |                 ^^^^^^^^^^^^^ help: try: `Rc<bool>`
+   = note: `Rc<bool>` is already on the heap, `Arc<Rc<bool>>` makes an extra allocation
+   = help: consider using just `Arc<bool>` or `Rc<bool>`
 
-error: usage of `Box<&T>`
-  --> $DIR/redundant_allocation.rs:40:22
+error: usage of `Arc<Box<SubT<usize>>>`
+  --> $DIR/redundant_allocation.rs:71:27
    |
-LL | pub fn test7<T>(foo: Box<&T>) {}
-   |                      ^^^^^^^ help: try: `&T`
+LL |     pub fn arc_test8() -> Arc<Box<SubT<usize>>> {
+   |                           ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `Box<SubT<usize>>` is already on the heap, `Arc<Box<SubT<usize>>>` makes an extra allocation
+   = help: consider using just `Arc<SubT<usize>>` or `Box<SubT<usize>>`
 
-error: usage of `Box<&T>`
-  --> $DIR/redundant_allocation.rs:42:19
+error: usage of `Arc<Rc<T>>`
+  --> $DIR/redundant_allocation.rs:75:30
+   |
+LL |     pub fn arc_test9<T>(foo: Arc<Rc<T>>) -> Arc<Rc<SubT<T>>> {
+   |                              ^^^^^^^^^^
    |
-LL | pub fn test8(foo: Box<&MyStruct>) {}
-   |                   ^^^^^^^^^^^^^^ help: try: `&MyStruct`
+   = note: `Rc<T>` is already on the heap, `Arc<Rc<T>>` makes an extra allocation
+   = help: consider using just `Arc<T>` or `Rc<T>`
 
-error: usage of `Box<&T>`
-  --> $DIR/redundant_allocation.rs:44:19
+error: usage of `Arc<Rc<SubT<T>>>`
+  --> $DIR/redundant_allocation.rs:75:45
+   |
+LL |     pub fn arc_test9<T>(foo: Arc<Rc<T>>) -> Arc<Rc<SubT<T>>> {
+   |                                             ^^^^^^^^^^^^^^^^
    |
-LL | pub fn test9(foo: Box<&MyEnum>) {}
-   |                   ^^^^^^^^^^^^ help: try: `&MyEnum`
+   = note: `Rc<SubT<T>>` is already on the heap, `Arc<Rc<SubT<T>>>` makes an extra allocation
+   = help: consider using just `Arc<SubT<T>>` or `Rc<SubT<T>>`
 
-error: aborting due to 8 previous errors
+error: aborting due to 15 previous errors
 
diff --git a/src/tools/clippy/tests/ui/redundant_allocation_fixable.fixed b/src/tools/clippy/tests/ui/redundant_allocation_fixable.fixed
new file mode 100644 (file)
index 0000000..ef089b2
--- /dev/null
@@ -0,0 +1,75 @@
+// run-rustfix
+#![warn(clippy::all)]
+#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
+#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
+#![allow(unused_imports)]
+
+pub struct MyStruct {}
+
+pub struct SubT<T> {
+    foo: T,
+}
+
+pub enum MyEnum {
+    One,
+    Two,
+}
+
+mod outer_box {
+    use crate::MyEnum;
+    use crate::MyStruct;
+    use crate::SubT;
+    use std::boxed::Box;
+    use std::rc::Rc;
+    use std::sync::Arc;
+
+    pub fn box_test1<T>(foo: &T) {}
+
+    pub fn box_test2(foo: &MyStruct) {}
+
+    pub fn box_test3(foo: &MyEnum) {}
+
+    pub fn box_test4_neg(foo: Box<SubT<&usize>>) {}
+
+    pub fn box_test5<T>(foo: Box<T>) {}
+}
+
+mod outer_rc {
+    use crate::MyEnum;
+    use crate::MyStruct;
+    use crate::SubT;
+    use std::boxed::Box;
+    use std::rc::Rc;
+    use std::sync::Arc;
+
+    pub fn rc_test1<T>(foo: &T) {}
+
+    pub fn rc_test2(foo: &MyStruct) {}
+
+    pub fn rc_test3(foo: &MyEnum) {}
+
+    pub fn rc_test4_neg(foo: Rc<SubT<&usize>>) {}
+
+    pub fn rc_test6(a: Rc<bool>) {}
+}
+
+mod outer_arc {
+    use crate::MyEnum;
+    use crate::MyStruct;
+    use crate::SubT;
+    use std::boxed::Box;
+    use std::rc::Rc;
+    use std::sync::Arc;
+
+    pub fn arc_test1<T>(foo: &T) {}
+
+    pub fn arc_test2(foo: &MyStruct) {}
+
+    pub fn arc_test3(foo: &MyEnum) {}
+
+    pub fn arc_test4_neg(foo: Arc<SubT<&usize>>) {}
+
+    pub fn arc_test7(a: Arc<bool>) {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/redundant_allocation_fixable.rs b/src/tools/clippy/tests/ui/redundant_allocation_fixable.rs
new file mode 100644 (file)
index 0000000..fefa877
--- /dev/null
@@ -0,0 +1,75 @@
+// run-rustfix
+#![warn(clippy::all)]
+#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
+#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
+#![allow(unused_imports)]
+
+pub struct MyStruct {}
+
+pub struct SubT<T> {
+    foo: T,
+}
+
+pub enum MyEnum {
+    One,
+    Two,
+}
+
+mod outer_box {
+    use crate::MyEnum;
+    use crate::MyStruct;
+    use crate::SubT;
+    use std::boxed::Box;
+    use std::rc::Rc;
+    use std::sync::Arc;
+
+    pub fn box_test1<T>(foo: Box<&T>) {}
+
+    pub fn box_test2(foo: Box<&MyStruct>) {}
+
+    pub fn box_test3(foo: Box<&MyEnum>) {}
+
+    pub fn box_test4_neg(foo: Box<SubT<&usize>>) {}
+
+    pub fn box_test5<T>(foo: Box<Box<T>>) {}
+}
+
+mod outer_rc {
+    use crate::MyEnum;
+    use crate::MyStruct;
+    use crate::SubT;
+    use std::boxed::Box;
+    use std::rc::Rc;
+    use std::sync::Arc;
+
+    pub fn rc_test1<T>(foo: Rc<&T>) {}
+
+    pub fn rc_test2(foo: Rc<&MyStruct>) {}
+
+    pub fn rc_test3(foo: Rc<&MyEnum>) {}
+
+    pub fn rc_test4_neg(foo: Rc<SubT<&usize>>) {}
+
+    pub fn rc_test6(a: Rc<Rc<bool>>) {}
+}
+
+mod outer_arc {
+    use crate::MyEnum;
+    use crate::MyStruct;
+    use crate::SubT;
+    use std::boxed::Box;
+    use std::rc::Rc;
+    use std::sync::Arc;
+
+    pub fn arc_test1<T>(foo: Arc<&T>) {}
+
+    pub fn arc_test2(foo: Arc<&MyStruct>) {}
+
+    pub fn arc_test3(foo: Arc<&MyEnum>) {}
+
+    pub fn arc_test4_neg(foo: Arc<SubT<&usize>>) {}
+
+    pub fn arc_test7(a: Arc<Arc<bool>>) {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/redundant_allocation_fixable.stderr b/src/tools/clippy/tests/ui/redundant_allocation_fixable.stderr
new file mode 100644 (file)
index 0000000..fdd76ef
--- /dev/null
@@ -0,0 +1,99 @@
+error: usage of `Box<&T>`
+  --> $DIR/redundant_allocation_fixable.rs:26:30
+   |
+LL |     pub fn box_test1<T>(foo: Box<&T>) {}
+   |                              ^^^^^^^ help: try: `&T`
+   |
+   = note: `-D clippy::redundant-allocation` implied by `-D warnings`
+   = note: `&T` is already a pointer, `Box<&T>` allocates a pointer on the heap
+
+error: usage of `Box<&MyStruct>`
+  --> $DIR/redundant_allocation_fixable.rs:28:27
+   |
+LL |     pub fn box_test2(foo: Box<&MyStruct>) {}
+   |                           ^^^^^^^^^^^^^^ help: try: `&MyStruct`
+   |
+   = note: `&MyStruct` is already a pointer, `Box<&MyStruct>` allocates a pointer on the heap
+
+error: usage of `Box<&MyEnum>`
+  --> $DIR/redundant_allocation_fixable.rs:30:27
+   |
+LL |     pub fn box_test3(foo: Box<&MyEnum>) {}
+   |                           ^^^^^^^^^^^^ help: try: `&MyEnum`
+   |
+   = note: `&MyEnum` is already a pointer, `Box<&MyEnum>` allocates a pointer on the heap
+
+error: usage of `Box<Box<T>>`
+  --> $DIR/redundant_allocation_fixable.rs:34:30
+   |
+LL |     pub fn box_test5<T>(foo: Box<Box<T>>) {}
+   |                              ^^^^^^^^^^^ help: try: `Box<T>`
+   |
+   = note: `Box<T>` is already on the heap, `Box<Box<T>>` makes an extra allocation
+
+error: usage of `Rc<&T>`
+  --> $DIR/redundant_allocation_fixable.rs:45:29
+   |
+LL |     pub fn rc_test1<T>(foo: Rc<&T>) {}
+   |                             ^^^^^^ help: try: `&T`
+   |
+   = note: `&T` is already a pointer, `Rc<&T>` allocates a pointer on the heap
+
+error: usage of `Rc<&MyStruct>`
+  --> $DIR/redundant_allocation_fixable.rs:47:26
+   |
+LL |     pub fn rc_test2(foo: Rc<&MyStruct>) {}
+   |                          ^^^^^^^^^^^^^ help: try: `&MyStruct`
+   |
+   = note: `&MyStruct` is already a pointer, `Rc<&MyStruct>` allocates a pointer on the heap
+
+error: usage of `Rc<&MyEnum>`
+  --> $DIR/redundant_allocation_fixable.rs:49:26
+   |
+LL |     pub fn rc_test3(foo: Rc<&MyEnum>) {}
+   |                          ^^^^^^^^^^^ help: try: `&MyEnum`
+   |
+   = note: `&MyEnum` is already a pointer, `Rc<&MyEnum>` allocates a pointer on the heap
+
+error: usage of `Rc<Rc<bool>>`
+  --> $DIR/redundant_allocation_fixable.rs:53:24
+   |
+LL |     pub fn rc_test6(a: Rc<Rc<bool>>) {}
+   |                        ^^^^^^^^^^^^ help: try: `Rc<bool>`
+   |
+   = note: `Rc<bool>` is already on the heap, `Rc<Rc<bool>>` makes an extra allocation
+
+error: usage of `Arc<&T>`
+  --> $DIR/redundant_allocation_fixable.rs:64:30
+   |
+LL |     pub fn arc_test1<T>(foo: Arc<&T>) {}
+   |                              ^^^^^^^ help: try: `&T`
+   |
+   = note: `&T` is already a pointer, `Arc<&T>` allocates a pointer on the heap
+
+error: usage of `Arc<&MyStruct>`
+  --> $DIR/redundant_allocation_fixable.rs:66:27
+   |
+LL |     pub fn arc_test2(foo: Arc<&MyStruct>) {}
+   |                           ^^^^^^^^^^^^^^ help: try: `&MyStruct`
+   |
+   = note: `&MyStruct` is already a pointer, `Arc<&MyStruct>` allocates a pointer on the heap
+
+error: usage of `Arc<&MyEnum>`
+  --> $DIR/redundant_allocation_fixable.rs:68:27
+   |
+LL |     pub fn arc_test3(foo: Arc<&MyEnum>) {}
+   |                           ^^^^^^^^^^^^ help: try: `&MyEnum`
+   |
+   = note: `&MyEnum` is already a pointer, `Arc<&MyEnum>` allocates a pointer on the heap
+
+error: usage of `Arc<Arc<bool>>`
+  --> $DIR/redundant_allocation_fixable.rs:72:25
+   |
+LL |     pub fn arc_test7(a: Arc<Arc<bool>>) {}
+   |                         ^^^^^^^^^^^^^^ help: try: `Arc<bool>`
+   |
+   = note: `Arc<bool>` is already on the heap, `Arc<Arc<bool>>` makes an extra allocation
+
+error: aborting due to 12 previous errors
+
index f5da703cd1dea5e9d524f8c2950ec629525faec2..2d711082746e73aab40867f0bcc2285137e5aca2 100644 (file)
@@ -55,6 +55,8 @@ fn main() {
     issue_5405();
     manually_drop();
     clone_then_move_cloned();
+    hashmap_neg();
+    false_negative_5707();
 }
 
 #[derive(Clone)]
@@ -206,3 +208,29 @@ fn clone_then_move_cloned() {
     let mut x = S(String::new());
     x.0.clone().chars().for_each(|_| x.m());
 }
+
+fn hashmap_neg() {
+    // issue 5707
+    use std::collections::HashMap;
+    use std::path::PathBuf;
+
+    let p = PathBuf::from("/");
+
+    let mut h: HashMap<&str, &str> = HashMap::new();
+    h.insert("orig-p", p.to_str().unwrap());
+
+    let mut q = p.clone();
+    q.push("foo");
+
+    println!("{:?} {}", h, q.display());
+}
+
+fn false_negative_5707() {
+    fn foo(_x: &Alpha, _y: &mut Alpha) {}
+
+    let x = Alpha;
+    let mut y = Alpha;
+    foo(&x, &mut y);
+    let _z = x.clone(); // pr 7346 can't lint on `x`
+    drop(y);
+}
index fd7f31a1cc5b6b944a635ff0282477600f7268b7..bd3d7365229fb83ccd13ca555e53d9941f867c43 100644 (file)
@@ -55,6 +55,8 @@ fn main() {
     issue_5405();
     manually_drop();
     clone_then_move_cloned();
+    hashmap_neg();
+    false_negative_5707();
 }
 
 #[derive(Clone)]
@@ -206,3 +208,29 @@ fn m(&mut self) {}
     let mut x = S(String::new());
     x.0.clone().chars().for_each(|_| x.m());
 }
+
+fn hashmap_neg() {
+    // issue 5707
+    use std::collections::HashMap;
+    use std::path::PathBuf;
+
+    let p = PathBuf::from("/");
+
+    let mut h: HashMap<&str, &str> = HashMap::new();
+    h.insert("orig-p", p.to_str().unwrap());
+
+    let mut q = p.clone();
+    q.push("foo");
+
+    println!("{:?} {}", h, q.display());
+}
+
+fn false_negative_5707() {
+    fn foo(_x: &Alpha, _y: &mut Alpha) {}
+
+    let x = Alpha;
+    let mut y = Alpha;
+    foo(&x, &mut y);
+    let _z = x.clone(); // pr 7346 can't lint on `x`
+    drop(y);
+}
index 529a6de91e266baa93294822e2337b1401e035b2..fbc90493ae94b85986e8381ef82e395fbc202e9e 100644 (file)
@@ -108,61 +108,61 @@ LL |     let _t = tup.0.clone();
    |              ^^^^^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:63:25
+  --> $DIR/redundant_clone.rs:65:25
    |
 LL |     if b { (a.clone(), a.clone()) } else { (Alpha, a) }
    |                         ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:63:24
+  --> $DIR/redundant_clone.rs:65:24
    |
 LL |     if b { (a.clone(), a.clone()) } else { (Alpha, a) }
    |                        ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:120:15
+  --> $DIR/redundant_clone.rs:122:15
    |
 LL |     let _s = s.clone();
    |               ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:120:14
+  --> $DIR/redundant_clone.rs:122:14
    |
 LL |     let _s = s.clone();
    |              ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:121:15
+  --> $DIR/redundant_clone.rs:123:15
    |
 LL |     let _t = t.clone();
    |               ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:121:14
+  --> $DIR/redundant_clone.rs:123:14
    |
 LL |     let _t = t.clone();
    |              ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:131:19
+  --> $DIR/redundant_clone.rs:133:19
    |
 LL |         let _f = f.clone();
    |                   ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:131:18
+  --> $DIR/redundant_clone.rs:133:18
    |
 LL |         let _f = f.clone();
    |                  ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:143:14
+  --> $DIR/redundant_clone.rs:145:14
    |
 LL |     let y = x.clone().join("matthias");
    |              ^^^^^^^^ help: remove this
    |
 note: cloned value is neither consumed nor mutated
-  --> $DIR/redundant_clone.rs:143:13
+  --> $DIR/redundant_clone.rs:145:13
    |
 LL |     let y = x.clone().join("matthias");
    |             ^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/strlen_on_c_strings.rs b/src/tools/clippy/tests/ui/strlen_on_c_strings.rs
new file mode 100644 (file)
index 0000000..21902fa
--- /dev/null
@@ -0,0 +1,16 @@
+#![warn(clippy::strlen_on_c_strings)]
+#![allow(dead_code)]
+#![feature(rustc_private)]
+extern crate libc;
+
+use std::ffi::{CStr, CString};
+
+fn main() {
+    // CString
+    let cstring = CString::new("foo").expect("CString::new failed");
+    let len = unsafe { libc::strlen(cstring.as_ptr()) };
+
+    // CStr
+    let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
+    let len = unsafe { libc::strlen(cstr.as_ptr()) };
+}
diff --git a/src/tools/clippy/tests/ui/strlen_on_c_strings.stderr b/src/tools/clippy/tests/ui/strlen_on_c_strings.stderr
new file mode 100644 (file)
index 0000000..a212bd3
--- /dev/null
@@ -0,0 +1,25 @@
+error: using `libc::strlen` on a `CString` or `CStr` value
+  --> $DIR/strlen_on_c_strings.rs:11:24
+   |
+LL |     let len = unsafe { libc::strlen(cstring.as_ptr()) };
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::strlen-on-c-strings` implied by `-D warnings`
+help: try this (you might also need to get rid of `unsafe` block in some cases):
+   |
+LL |     let len = unsafe { cstring.as_bytes().len() };
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+  --> $DIR/strlen_on_c_strings.rs:15:24
+   |
+LL |     let len = unsafe { libc::strlen(cstr.as_ptr()) };
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try this (you might also need to get rid of `unsafe` block in some cases):
+   |
+LL |     let len = unsafe { cstr.to_bytes().len() };
+   |                        ^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
index 8d23227fdb8b78dab6a6ac3556ac581e81f127de..dc6d67983c5d22da7932f17b07ea77f1e386ba0e 100644 (file)
@@ -43,7 +43,6 @@ struct FutureIncompatReport {
 
 #[derive(Deserialize)]
 struct FutureBreakageItem {
-    future_breakage_date: Option<String>,
     diagnostic: Diagnostic,
 }
 
@@ -104,9 +103,7 @@ pub fn extract_rendered(output: &str) -> String {
                                 .into_iter()
                                 .map(|item| {
                                     format!(
-                                        "Future breakage date: {}, diagnostic:\n{}",
-                                        item.future_breakage_date
-                                            .unwrap_or_else(|| "None".to_string()),
+                                        "Future breakage diagnostic:\n{}",
                                         item.diagnostic
                                             .rendered
                                             .unwrap_or_else(|| "Not rendered".to_string())