about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-11-20 07:16:42 +0000
committerbors <bors@rust-lang.org>2022-11-20 07:16:42 +0000
commite07425d55b77fde99af2092a92109a0da0860692 (patch)
treee7ade368e6c2c2980fbde38df0bff50ff3da7702
parent2ed65da15279d25e8b8b91d60162930ab48f16f6 (diff)
parentbc51f8783cdbee5a1ed121f017ec7360d9be9124 (diff)
downloadrust-e07425d55b77fde99af2092a92109a0da0860692.tar.gz
rust-e07425d55b77fde99af2092a92109a0da0860692.zip
Auto merge of #98914 - fee1-dead-contrib:min-deref-patterns, r=compiler-errors
Minimal implementation of implicit deref patterns for Strings

cc `@compiler-errors` `@BoxyUwU` https://github.com/rust-lang/lang-team/issues/88 #87121

~~I forgot to add a feature gate, will do so in a minute~~ Done
-rw-r--r--compiler/rustc_feature/src/active.rs2
-rw-r--r--compiler/rustc_hir/src/lang_items.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/op.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs10
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/matches/test.rs33
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs2
-rw-r--r--library/alloc/src/string.rs2
-rw-r--r--src/test/mir-opt/deref-patterns/string.foo.PreCodegen.after.mir74
-rw-r--r--src/test/mir-opt/deref-patterns/string.rs12
-rw-r--r--src/test/ui/deref-patterns/basic.rs17
-rw-r--r--src/test/ui/deref-patterns/basic.run.stdout3
-rw-r--r--src/test/ui/deref-patterns/default-infer.rs9
-rw-r--r--src/test/ui/deref-patterns/gate.rs7
-rw-r--r--src/test/ui/deref-patterns/gate.stderr11
-rw-r--r--src/test/ui/deref-patterns/refs.rs18
-rw-r--r--src/test/ui/resolve/blind-item-local-shadow.rs (renamed from src/test/ui/blind-item-local-shadow.rs)0
-rw-r--r--src/tools/clippy/clippy_lints/src/format.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/format_push_string.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/from_str_radix_10.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/inherent_to_string.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_retain.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_string_new.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/repeat_once.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/search_is_some.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_clone.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/strings.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/types/box_collection.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/types/rc_buffer.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs7
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs15
46 files changed, 302 insertions, 99 deletions
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 5cf2fdde392..ae49a76a087 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -506,6 +506,8 @@ declare_features! (
     (active, stmt_expr_attributes, "1.6.0", Some(15701), None),
     /// Allows lints part of the strict provenance effort.
     (active, strict_provenance, "1.61.0", Some(95228), None),
+    /// Allows string patterns to dereference values to match them.
+    (active, string_deref_patterns, "CURRENT_RUSTC_VERSION", Some(87121), None),
     /// Allows the use of `#[target_feature]` on safe functions.
     (active, target_feature_11, "1.45.0", Some(69098), None),
     /// Allows using `#[thread_local]` on `static` items.
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 010373ba547..b68dd25996b 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -312,6 +312,8 @@ language_item_table! {
     Range,                   sym::Range,               range_struct,               Target::Struct,         GenericRequirement::None;
     RangeToInclusive,        sym::RangeToInclusive,    range_to_inclusive_struct,  Target::Struct,         GenericRequirement::None;
     RangeTo,                 sym::RangeTo,             range_to_struct,            Target::Struct,         GenericRequirement::None;
+
+    String,                  sym::String,              string,                     Target::Struct,         GenericRequirement::None;
 }
 
 pub enum GenericRequirement {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 316ecb0ed52..9f5aadca967 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -464,7 +464,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     ref_cnt += 1;
                 }
                 if let ty::Adt(adt, _) = peeled.kind()
-                    && self.tcx.is_diagnostic_item(sym::String, adt.did())
+                    && Some(adt.did()) == self.tcx.lang_items().string()
                 {
                     err.span_suggestion_verbose(
                         expr.span.shrink_to_hi(),
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 9a66e73d9c2..8190163cba1 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -821,10 +821,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 ty.is_str()
                                     || matches!(
                                         ty.kind(),
-                                        ty::Adt(adt, _) if self.tcx.is_diagnostic_item(sym::String, adt.did())
+                                        ty::Adt(adt, _) if Some(adt.did()) == self.tcx.lang_items().string()
                                     )
                             }
-                            ty::Adt(adt, _) => self.tcx.is_diagnostic_item(sym::String, adt.did()),
+                            ty::Adt(adt, _) => Some(adt.did()) == self.tcx.lang_items().string(),
                             _ => false,
                         };
                         if is_string_or_ref_str && item_name.name == sym::iter {
diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs
index 38b3dd218a9..adc7a21f265 100644
--- a/compiler/rustc_hir_typeck/src/op.rs
+++ b/compiler/rustc_hir_typeck/src/op.rs
@@ -556,9 +556,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let rm_borrow_msg = "remove the borrow to obtain an owned `String`";
         let to_owned_msg = "create an owned `String` from a string reference";
 
+        let string_type = self.tcx.lang_items().string();
         let is_std_string = |ty: Ty<'tcx>| {
-            ty.ty_adt_def()
-                .map_or(false, |ty_def| self.tcx.is_diagnostic_item(sym::String, ty_def.did()))
+            ty.ty_adt_def().map_or(false, |ty_def| Some(ty_def.did()) == string_type)
         };
 
         match (lhs_ty.kind(), rhs_ty.kind()) {
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index a62d4356130..1d021f19104 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -401,6 +401,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         }
 
+        if self.tcx.features().string_deref_patterns && let hir::ExprKind::Lit(Spanned { node: ast::LitKind::Str(..), .. }) = lt.kind {
+            let tcx = self.tcx;
+            let expected = self.resolve_vars_if_possible(expected);
+            pat_ty = match expected.kind() {
+                ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().string() => expected,
+                ty::Str => tcx.mk_static_str(),
+                _ => pat_ty,
+            };
+        }
+
         // Somewhat surprising: in this case, the subtyping relation goes the
         // opposite way as the other cases. Actually what we really want is not
         // a subtyping relation at all but rather that there exists a LUB
diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs
index 6ad2e0294b9..83e6f4e33be 100644
--- a/compiler/rustc_lint/src/non_fmt_panic.rs
+++ b/compiler/rustc_lint/src/non_fmt_panic.rs
@@ -148,7 +148,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
                 ty::Ref(_, r, _) if *r.kind() == ty::Str,
             ) || matches!(
                 ty.ty_adt_def(),
-                Some(ty_def) if cx.tcx.is_diagnostic_item(sym::String, ty_def.did()),
+                Some(ty_def) if Some(ty_def.did()) == cx.tcx.lang_items().string(),
             );
 
             let infcx = cx.tcx.infer_ctxt().build();
diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs
index b597ecfaa4d..af0d7879c57 100644
--- a/compiler/rustc_mir_build/src/build/matches/test.rs
+++ b/compiler/rustc_mir_build/src/build/matches/test.rs
@@ -240,6 +240,39 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             }
 
             TestKind::Eq { value, ty } => {
+                let tcx = self.tcx;
+                if let ty::Adt(def, _) = ty.kind() && Some(def.did()) == tcx.lang_items().string() {
+                    if !tcx.features().string_deref_patterns {
+                        bug!("matching on `String` went through without enabling string_deref_patterns");
+                    }
+                    let re_erased = tcx.lifetimes.re_erased;
+                    let ref_string = self.temp(tcx.mk_imm_ref(re_erased, ty), test.span);
+                    let ref_str_ty = tcx.mk_imm_ref(re_erased, tcx.types.str_);
+                    let ref_str = self.temp(ref_str_ty, test.span);
+                    let deref = tcx.require_lang_item(LangItem::Deref, None);
+                    let method = trait_method(tcx, deref, sym::deref, ty, &[]);
+                    let eq_block = self.cfg.start_new_block();
+                    self.cfg.push_assign(block, source_info, ref_string, Rvalue::Ref(re_erased, BorrowKind::Shared, place));
+                    self.cfg.terminate(
+                        block,
+                        source_info,
+                        TerminatorKind::Call {
+                            func: Operand::Constant(Box::new(Constant {
+                                span: test.span,
+                                user_ty: None,
+                                literal: method,
+                            })),
+                            args: vec![Operand::Move(ref_string)],
+                            destination: ref_str,
+                            target: Some(eq_block),
+                            cleanup: None,
+                            from_hir_call: false,
+                            fn_span: source_info.span
+                        }
+                    );
+                    self.non_scalar_compare(eq_block, make_target_blocks, source_info, value, ref_str, ref_str_ty);
+                    return;
+                }
                 if !ty.is_scalar() {
                     // Use `PartialEq::eq` instead of `BinOp::Eq`
                     // (the binop can only handle primitives)
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 199b2d32b9d..1b109ca6b11 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1407,6 +1407,7 @@ symbols! {
         str_trim_end,
         str_trim_start,
         strict_provenance,
+        string_deref_patterns,
         stringify,
         struct_field_attributes,
         struct_inherit,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 6e90b944650..c3547f64b5c 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -1729,7 +1729,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 ty::Bool => Some(0),
                 ty::Char => Some(1),
                 ty::Str => Some(2),
-                ty::Adt(def, _) if tcx.is_diagnostic_item(sym::String, def.did()) => Some(2),
+                ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().string() => Some(2),
                 ty::Int(..)
                 | ty::Uint(..)
                 | ty::Float(..)
diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs
index c9ba8921f6e..7a8e6f088f3 100644
--- a/library/alloc/src/string.rs
+++ b/library/alloc/src/string.rs
@@ -362,8 +362,8 @@ use crate::vec::Vec;
 /// [`Deref`]: core::ops::Deref "ops::Deref"
 /// [`as_str()`]: String::as_str
 #[derive(PartialOrd, Eq, Ord)]
-#[cfg_attr(not(test), rustc_diagnostic_item = "String")]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(all(not(bootstrap), not(test)), lang = "String")]
 pub struct String {
     vec: Vec<u8>,
 }
diff --git a/src/test/mir-opt/deref-patterns/string.foo.PreCodegen.after.mir b/src/test/mir-opt/deref-patterns/string.foo.PreCodegen.after.mir
new file mode 100644
index 00000000000..5b185082d4d
--- /dev/null
+++ b/src/test/mir-opt/deref-patterns/string.foo.PreCodegen.after.mir
@@ -0,0 +1,74 @@
+// MIR for `foo` after PreCodegen
+
+fn foo(_1: Option<String>) -> i32 {
+    debug s => _1;                       // in scope 0 at $DIR/string.rs:+0:12: +0:13
+    let mut _0: i32;                     // return place in scope 0 at $DIR/string.rs:+0:34: +0:37
+    let mut _2: &std::string::String;    // in scope 0 at $DIR/string.rs:+2:14: +2:17
+    let mut _3: &str;                    // in scope 0 at $DIR/string.rs:+2:14: +2:17
+    let mut _4: bool;                    // in scope 0 at $DIR/string.rs:+2:14: +2:17
+    let mut _5: isize;                   // in scope 0 at $DIR/string.rs:+2:9: +2:18
+    let _6: std::option::Option<std::string::String>; // in scope 0 at $DIR/string.rs:+3:9: +3:10
+    let mut _7: bool;                    // in scope 0 at $DIR/string.rs:+5:1: +5:2
+    scope 1 {
+        debug s => _6;                   // in scope 1 at $DIR/string.rs:+3:9: +3:10
+    }
+
+    bb0: {
+        _7 = const false;                // scope 0 at $DIR/string.rs:+1:11: +1:12
+        _7 = const true;                 // scope 0 at $DIR/string.rs:+1:11: +1:12
+        _5 = discriminant(_1);           // scope 0 at $DIR/string.rs:+1:11: +1:12
+        switchInt(move _5) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/string.rs:+1:5: +1:12
+    }
+
+    bb1: {
+        StorageLive(_6);                 // scope 0 at $DIR/string.rs:+3:9: +3:10
+        _7 = const false;                // scope 0 at $DIR/string.rs:+3:9: +3:10
+        _6 = move _1;                    // scope 0 at $DIR/string.rs:+3:9: +3:10
+        _0 = const 4321_i32;             // scope 1 at $DIR/string.rs:+3:14: +3:18
+        drop(_6) -> bb6;                 // scope 0 at $DIR/string.rs:+3:17: +3:18
+    }
+
+    bb2: {
+        _2 = &((_1 as Some).0: std::string::String); // scope 0 at $DIR/string.rs:+2:14: +2:17
+        _3 = <String as Deref>::deref(move _2) -> bb3; // scope 0 at $DIR/string.rs:+2:14: +2:17
+                                         // mir::Constant
+                                         // + span: $DIR/string.rs:9:14: 9:17
+                                         // + literal: Const { ty: for<'a> fn(&'a String) -> &'a <String as Deref>::Target {<String as Deref>::deref}, val: Value(<ZST>) }
+    }
+
+    bb3: {
+        _4 = <str as PartialEq>::eq(_3, const "a") -> bb4; // scope 0 at $DIR/string.rs:+2:14: +2:17
+                                         // mir::Constant
+                                         // + span: $DIR/string.rs:9:14: 9:17
+                                         // + literal: Const { ty: for<'a, 'b> fn(&'a str, &'b str) -> bool {<str as PartialEq>::eq}, val: Value(<ZST>) }
+                                         // mir::Constant
+                                         // + span: $DIR/string.rs:9:14: 9:17
+                                         // + literal: Const { ty: &str, val: Value(Slice(..)) }
+    }
+
+    bb4: {
+        switchInt(move _4) -> [false: bb1, otherwise: bb5]; // scope 0 at $DIR/string.rs:+2:14: +2:17
+    }
+
+    bb5: {
+        _0 = const 1234_i32;             // scope 0 at $DIR/string.rs:+2:22: +2:26
+        goto -> bb9;                     // scope 0 at $DIR/string.rs:+2:22: +2:26
+    }
+
+    bb6: {
+        StorageDead(_6);                 // scope 0 at $DIR/string.rs:+3:17: +3:18
+        goto -> bb9;                     // scope 0 at $DIR/string.rs:+3:17: +3:18
+    }
+
+    bb7: {
+        return;                          // scope 0 at $DIR/string.rs:+5:2: +5:2
+    }
+
+    bb8: {
+        drop(_1) -> bb7;                 // scope 0 at $DIR/string.rs:+5:1: +5:2
+    }
+
+    bb9: {
+        switchInt(_7) -> [false: bb7, otherwise: bb8]; // scope 0 at $DIR/string.rs:+5:1: +5:2
+    }
+}
diff --git a/src/test/mir-opt/deref-patterns/string.rs b/src/test/mir-opt/deref-patterns/string.rs
new file mode 100644
index 00000000000..3a99c44aa0e
--- /dev/null
+++ b/src/test/mir-opt/deref-patterns/string.rs
@@ -0,0 +1,12 @@
+// compile-flags: -Z mir-opt-level=0 -C panic=abort
+
+#![feature(string_deref_patterns)]
+#![crate_type = "lib"]
+
+// EMIT_MIR string.foo.PreCodegen.after.mir
+pub fn foo(s: Option<String>) -> i32 {
+    match s {
+        Some("a") => 1234,
+        s => 4321,
+    }
+}
diff --git a/src/test/ui/deref-patterns/basic.rs b/src/test/ui/deref-patterns/basic.rs
new file mode 100644
index 00000000000..249716040a1
--- /dev/null
+++ b/src/test/ui/deref-patterns/basic.rs
@@ -0,0 +1,17 @@
+// run-pass
+// check-run-results
+#![feature(string_deref_patterns)]
+
+fn main() {
+    test(Some(String::from("42")));
+    test(Some(String::new()));
+    test(None);
+}
+
+fn test(o: Option<String>) {
+    match o {
+        Some("42") => println!("the answer"),
+        Some(_) => println!("something else?"),
+        None => println!("nil"),
+    }
+}
diff --git a/src/test/ui/deref-patterns/basic.run.stdout b/src/test/ui/deref-patterns/basic.run.stdout
new file mode 100644
index 00000000000..e50df058281
--- /dev/null
+++ b/src/test/ui/deref-patterns/basic.run.stdout
@@ -0,0 +1,3 @@
+the answer
+something else?
+nil
diff --git a/src/test/ui/deref-patterns/default-infer.rs b/src/test/ui/deref-patterns/default-infer.rs
new file mode 100644
index 00000000000..050b847305b
--- /dev/null
+++ b/src/test/ui/deref-patterns/default-infer.rs
@@ -0,0 +1,9 @@
+// check-pass
+#![feature(string_deref_patterns)]
+
+fn main() {
+    match <_ as Default>::default() {
+        "" => (),
+        _ => unreachable!(),
+    }
+}
diff --git a/src/test/ui/deref-patterns/gate.rs b/src/test/ui/deref-patterns/gate.rs
new file mode 100644
index 00000000000..ff50e30dea8
--- /dev/null
+++ b/src/test/ui/deref-patterns/gate.rs
@@ -0,0 +1,7 @@
+// gate-test-string_deref_patterns
+fn main() {
+    match String::new() {
+        "" | _ => {}
+        //~^ mismatched types
+    }
+}
diff --git a/src/test/ui/deref-patterns/gate.stderr b/src/test/ui/deref-patterns/gate.stderr
new file mode 100644
index 00000000000..993468b5e82
--- /dev/null
+++ b/src/test/ui/deref-patterns/gate.stderr
@@ -0,0 +1,11 @@
+error[E0308]: mismatched types
+  --> $DIR/gate.rs:4:9
+   |
+LL |     match String::new() {
+   |           ------------- this expression has type `String`
+LL |         "" | _ => {}
+   |         ^^ expected struct `String`, found `&str`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/deref-patterns/refs.rs b/src/test/ui/deref-patterns/refs.rs
new file mode 100644
index 00000000000..97e260d2752
--- /dev/null
+++ b/src/test/ui/deref-patterns/refs.rs
@@ -0,0 +1,18 @@
+// check-pass
+#![feature(string_deref_patterns)]
+
+fn foo(s: &String) -> i32 {
+    match *s {
+        "a" => 42,
+        _ => -1,
+    }
+}
+
+fn bar(s: Option<&&&&String>) -> i32 {
+    match s {
+        Some(&&&&"&&&&") => 1,
+        _ => -1,
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/blind-item-local-shadow.rs b/src/test/ui/resolve/blind-item-local-shadow.rs
index 942aeb6fdf4..942aeb6fdf4 100644
--- a/src/test/ui/blind-item-local-shadow.rs
+++ b/src/test/ui/resolve/blind-item-local-shadow.rs
diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs
index bc0c68f535a..d0fab694960 100644
--- a/src/tools/clippy/clippy_lints/src/format.rs
+++ b/src/tools/clippy/clippy_lints/src/format.rs
@@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
                 if format_args.format_string.parts == [kw::Empty];
                 if arg.format.is_default();
                 if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
-                    ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()),
+                    ty::Adt(adt, _) => Some(adt.did()) == cx.tcx.lang_items().string(),
                     ty::Str => true,
                     _ => false,
                 };
diff --git a/src/tools/clippy/clippy_lints/src/format_push_string.rs b/src/tools/clippy/clippy_lints/src/format_push_string.rs
index 9b9f1872bfc..68c5c3673fe 100644
--- a/src/tools/clippy/clippy_lints/src/format_push_string.rs
+++ b/src/tools/clippy/clippy_lints/src/format_push_string.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::is_type_lang_item;
 use clippy_utils::{match_def_path, paths, peel_hir_expr_refs};
-use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
@@ -41,7 +41,7 @@ declare_clippy_lint! {
 declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]);
 
 fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
-    is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::String)
+    is_type_lang_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), LangItem::String)
 }
 fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
     if let Some(macro_def_id) = e.span.ctxt().outer_expn_data().macro_def_id {
diff --git a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
index cf8b7acd66d..74a60b6a0d2 100644
--- a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
+++ b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
@@ -1,10 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::is_integer_literal;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{def, Expr, ExprKind, PrimTy, QPath, TyKind};
+use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::Ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -98,5 +98,5 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
 
 /// Checks if a Ty is `String` or `&str`
 fn is_ty_stringish(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
-    is_type_diagnostic_item(cx, ty, sym::String) || is_type_diagnostic_item(cx, ty, sym::str)
+    is_type_lang_item(cx, ty, LangItem::String) || is_type_diagnostic_item(cx, ty, sym::str)
 }
diff --git a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs
index 14a37f535b4..aaecc4fa8f2 100644
--- a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs
+++ b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs
@@ -1,8 +1,8 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
+use clippy_utils::ty::{implements_trait, is_type_lang_item};
 use clippy_utils::{return_ty, trait_ref_of_method};
 use if_chain::if_chain;
-use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind};
+use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind, LangItem};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
@@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString {
             if impl_item.generics.params.iter().all(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }));
 
             // Check if return type is String
-            if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id()), sym::String);
+            if is_type_lang_item(cx, return_ty(cx, impl_item.hir_id()), LangItem::String);
 
             // Filters instances of to_string which are required by a trait
             if trait_ref_of_method(cx, impl_item.owner_id.def_id).is_none();
diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs
index 6abbab278fe..d6438ca7fec 100644
--- a/src/tools/clippy/clippy_lints/src/manual_retain.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
 use clippy_utils::{get_parent_expr, match_def_path, paths, SpanlessEq};
 use clippy_utils::{meets_msrv, msrvs};
 use rustc_errors::Applicability;
@@ -140,7 +140,7 @@ fn check_to_owned(
         && let Some(chars_expr_def_id) = cx.typeck_results().type_dependent_def_id(chars_expr.hir_id)
         && match_def_path(cx, chars_expr_def_id, &paths::STR_CHARS)
         && let ty = cx.typeck_results().expr_ty(str_expr).peel_refs()
-        && is_type_diagnostic_item(cx, ty, sym::String)
+        && is_type_lang_item(cx, ty, hir::LangItem::String)
         && SpanlessEq::new(cx).eq_expr(left_expr, str_expr) {
         suggest(cx, parent_expr, left_expr, filter_expr);
     }
diff --git a/src/tools/clippy/clippy_lints/src/manual_string_new.rs b/src/tools/clippy/clippy_lints/src/manual_string_new.rs
index 6acfb2ae347..c20d7959fc4 100644
--- a/src/tools/clippy/clippy_lints/src/manual_string_new.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_string_new.rs
@@ -44,7 +44,7 @@ impl LateLintPass<'_> for ManualStringNew {
         let ty = cx.typeck_results().expr_ty(expr);
         match ty.kind() {
             ty::Adt(adt_def, _) if adt_def.is_struct() => {
-                if !cx.tcx.is_diagnostic_item(sym::String, adt_def.did()) {
+                if cx.tcx.lang_items().string() != Some(adt_def.did()) {
                     return;
                 }
             },
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs
index 6647322caa3..675a85ae555 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs
@@ -1,13 +1,13 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::is_type_lang_item;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_hir::{Arm, Expr, ExprKind, PatKind};
+use rustc_hir::{Arm, Expr, ExprKind, LangItem, PatKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
 use rustc_span::symbol::Symbol;
-use rustc_span::{sym, Span};
+use rustc_span::Span;
 
 use super::MATCH_STR_CASE_MISMATCH;
 
@@ -59,7 +59,7 @@ impl<'a, 'tcx> MatchExprVisitor<'a, 'tcx> {
         if let Some(case_method) = get_case_method(segment_ident) {
             let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs();
 
-            if is_type_diagnostic_item(self.cx, ty, sym::String) || ty.kind() == &ty::Str {
+            if is_type_lang_item(self.cx, ty, LangItem::String) || ty.kind() == &ty::Str {
                 self.case_method = Some(case_method);
                 return true;
             }
diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs
index fcfc25b523d..89aaad359d4 100644
--- a/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs
@@ -1,11 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::is_type_lang_item;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
-use rustc_span::sym;
 
 use super::BYTES_COUNT_TO_LEN;
 
@@ -20,7 +19,7 @@ pub(super) fn check<'tcx>(
         if let Some(impl_id) = cx.tcx.impl_of_method(bytes_id);
         if cx.tcx.type_of(impl_id).is_str();
         let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs();
-        if ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String);
+        if ty.is_str() || is_type_lang_item(cx, ty, hir::LangItem::String);
         then {
             let mut applicability = Applicability::MachineApplicable;
             span_lint_and_sugg(
diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs
index 2e96346be97..d512cc4eeae 100644
--- a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs
@@ -1,10 +1,9 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::is_type_lang_item;
 use rustc_errors::Applicability;
-use rustc_hir::Expr;
+use rustc_hir::{Expr, LangItem};
 use rustc_lint::LateContext;
-use rustc_span::sym;
 
 use super::BYTES_NTH;
 
@@ -12,7 +11,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E
     let ty = cx.typeck_results().expr_ty(recv).peel_refs();
     let caller_type = if ty.is_str() {
         "str"
-    } else if is_type_diagnostic_item(cx, ty, sym::String) {
+    } else if is_type_lang_item(cx, ty, LangItem::String) {
         "String"
     } else {
         return;
diff --git a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
index b3c2c7c9a2d..d226c0bba65 100644
--- a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
@@ -1,10 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::is_type_lang_item;
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, LangItem};
 use rustc_lint::LateContext;
-use rustc_span::{source_map::Spanned, symbol::sym, Span};
+use rustc_span::{source_map::Spanned, Span};
 
 use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS;
 
@@ -26,7 +26,7 @@ pub(super) fn check<'tcx>(
         if ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
             || ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
         let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs();
-        if recv_ty.is_str() || is_type_diagnostic_item(cx, recv_ty, sym::String);
+        if recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String);
         then {
             span_lint_and_help(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
index d0cf411dfd3..a9189b31c57 100644
--- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -33,7 +33,7 @@ pub(super) fn check<'tcx>(
                     if (method_name.ident.name == sym::as_str || method_name.ident.name == sym::as_ref) && {
                         let arg_type = cx.typeck_results().expr_ty(receiver);
                         let base_type = arg_type.peel_refs();
-                        *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym::String)
+                        *base_type.kind() == ty::Str || is_type_lang_item(cx, base_type, hir::LangItem::String)
                     } {
                         receiver
                     } else {
@@ -50,7 +50,7 @@ pub(super) fn check<'tcx>(
     // converted to string.
     fn requires_to_string(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
         let arg_ty = cx.typeck_results().expr_ty(arg);
-        if is_type_diagnostic_item(cx, arg_ty, sym::String) {
+        if is_type_lang_item(cx, arg_ty, hir::LangItem::String) {
             return false;
         }
         if let ty::Ref(_, ty, ..) = arg_ty.kind() {
diff --git a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs
index ede3b8bb74e..4f4f543e8a9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs
@@ -1,13 +1,13 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::{is_type_diagnostic_item, walk_ptrs_ty_depth};
+use clippy_utils::ty::{is_type_lang_item, walk_ptrs_ty_depth};
 use clippy_utils::{match_def_path, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::{Symbol, sym};
 
 use super::INEFFICIENT_TO_STRING;
 
@@ -60,7 +60,7 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
         return true;
     }
 
-    if is_type_diagnostic_item(cx, ty, sym::String) {
+    if is_type_lang_item(cx, ty, hir::LangItem::String) {
         return true;
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs b/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs
index 8b6b8f1bf16..13c47c03a80 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs
@@ -36,14 +36,14 @@ fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<RepeatKind> {
         }
     } else {
         let ty = cx.typeck_results().expr_ty(e);
-        if is_type_diagnostic_item(cx, ty, sym::String)
+        if is_type_lang_item(cx, ty, LangItem::String)
             || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, Ty::is_str))
             || (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).map_or(false, Ty::is_str))
         {
             Some(RepeatKind::String)
         } else {
             let ty = ty.peel_refs();
-            (ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)).then_some(RepeatKind::String)
+            (ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)).then_some(RepeatKind::String)
         }
     }
 }
@@ -58,7 +58,7 @@ pub(super) fn check(
     if_chain! {
         if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind;
         if is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat);
-        if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(collect_expr), sym::String);
+        if is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String);
         if let Some(collect_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id);
         if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id);
         if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
diff --git a/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs b/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs
index a76341855b6..01655e860c4 100644
--- a/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs
@@ -1,11 +1,10 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::is_type_lang_item;
 use clippy_utils::SpanlessEq;
 use if_chain::if_chain;
 use rustc_ast::LitKind;
-use rustc_hir::ExprKind;
+use rustc_hir::{ExprKind, LangItem};
 use rustc_lint::LateContext;
-use rustc_span::sym;
 
 use super::NO_EFFECT_REPLACE;
 
@@ -16,7 +15,7 @@ pub(super) fn check<'tcx>(
     arg2: &'tcx rustc_hir::Expr<'_>,
 ) {
     let ty = cx.typeck_results().expr_ty(expr).peel_refs();
-    if !(ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)) {
+    if !(ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)) {
         return;
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs b/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs
index 0a14f9216ab..a345ec813ff 100644
--- a/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs
@@ -1,11 +1,10 @@
 use clippy_utils::consts::{constant_context, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::is_type_lang_item;
 use rustc_errors::Applicability;
-use rustc_hir::Expr;
+use rustc_hir::{Expr, LangItem};
 use rustc_lint::LateContext;
-use rustc_span::sym;
 
 use super::REPEAT_ONCE;
 
@@ -37,7 +36,7 @@ pub(super) fn check<'tcx>(
                 format!("{}.to_vec()", snippet(cx, recv.span, r#""...""#)),
                 Applicability::MachineApplicable,
             );
-        } else if is_type_diagnostic_item(cx, ty, sym::String) {
+        } else if is_type_lang_item(cx, ty, LangItem::String) {
             span_lint_and_sugg(
                 cx,
                 REPEAT_ONCE,
diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
index 324c9c17b5a..1c031ad6acb 100644
--- a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::sugg::deref_closure_args;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::is_type_lang_item;
 use clippy_utils::{is_trait_method, strip_pat_refs};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -105,7 +105,7 @@ pub(super) fn check<'tcx>(
     else if search_method == "find" {
         let is_string_or_str_slice = |e| {
             let self_ty = cx.typeck_results().expr_ty(e).peel_refs();
-            if is_type_diagnostic_item(cx, self_ty, sym::String) {
+            if is_type_lang_item(cx, self_ty, hir::LangItem::String) {
                 true
             } else {
                 *self_ty.kind() == ty::Str
diff --git a/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs b/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs
index 6974260f70d..6f4cec546e9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs
@@ -1,18 +1,17 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::method_chain_args;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::is_type_lang_item;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::symbol::sym;
 
 use super::STRING_EXTEND_CHARS;
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
     let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
-    if !is_type_diagnostic_item(cx, obj_ty, sym::String) {
+    if !is_type_lang_item(cx, obj_ty, hir::LangItem::String) {
         return;
     }
     if let Some(arglists) = method_chain_args(arg, &["chars"]) {
@@ -20,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
         let self_ty = cx.typeck_results().expr_ty(target).peel_refs();
         let ref_str = if *self_ty.kind() == ty::Str {
             ""
-        } else if is_type_diagnostic_item(cx, self_ty, sym::String) {
+        } else if is_type_lang_item(cx, self_ty, hir::LangItem::String) {
             "&"
         } else {
             return;
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs
index 973b8a7e6bf..c9b87bc6bf2 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs
@@ -1,10 +1,10 @@
-use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item};
+use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_lang_item};
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, LangItem};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{Ref, Slice};
-use rustc_span::{sym, Span};
+use rustc_span::Span;
 
 use super::UNNECESSARY_JOIN;
 
@@ -21,7 +21,7 @@ pub(super) fn check<'tcx>(
         // the turbofish for collect is ::<Vec<String>>
         if let Ref(_, ref_type, _) = collect_output_adjusted_type.kind();
         if let Slice(slice) = ref_type.kind();
-        if is_type_diagnostic_item(cx, *slice, sym::String);
+        if is_type_lang_item(cx, *slice, LangItem::String);
         // the argument for join is ""
         if let ExprKind::Lit(spanned) = &join_arg.kind;
         if let LitKind::Str(symbol, _) = spanned.node;
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index b2e9ce5c94d..79aa15b06ef 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 use clippy_utils::ptr::get_spans;
 use clippy_utils::source::{snippet, snippet_opt};
-use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item};
+use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item};
 use clippy_utils::{get_trait_def_id, is_self, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::Attribute;
@@ -11,7 +11,7 @@ use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
     BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Mutability, Node, PatKind, QPath, TyKind,
 };
-use rustc_hir::{HirIdMap, HirIdSet};
+use rustc_hir::{HirIdMap, HirIdSet, LangItem};
 use rustc_hir_typeck::expr_use_visitor as euv;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
@@ -249,7 +249,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
                             }
                         }
 
-                        if is_type_diagnostic_item(cx, ty, sym::String) {
+                        if is_type_lang_item(cx, ty, LangItem::String) {
                             if let Some(clone_spans) =
                                 get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) {
                                 diag.span_suggestion(
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index c8c6f32c6c9..6aa5e13fe31 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -450,7 +450,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                                 substs.type_at(0),
                             ),
                         ),
-                        Some(sym::String) => (
+                        _ if Some(adt.did()) == cx.tcx.lang_items().string() => (
                             [("clone", ".to_owned()"), ("as_str", "")].as_slice(),
                             DerefTy::Str,
                         ),
diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
index aedbe08e3e4..c1677fb3da1 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
@@ -1,12 +1,12 @@
 use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
 use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap};
 use clippy_utils::source::snippet_opt;
-use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth};
+use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth};
 use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
-use rustc_hir::{def_id, Body, FnDecl, HirId};
+use rustc_hir::{def_id, Body, FnDecl, HirId, LangItem};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::mir;
 use rustc_middle::ty::{self, Ty};
@@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
             let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
                 || match_def_path(cx, fn_def_id, &paths::TO_OWNED_METHOD)
                 || (match_def_path(cx, fn_def_id, &paths::TO_STRING_METHOD)
-                    && is_type_diagnostic_item(cx, arg_ty, sym::String));
+                    && is_type_lang_item(cx, arg_ty, LangItem::String));
 
             let from_deref = !from_borrow
                 && (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF)
diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs
index d356c99c8fc..f4705481d4e 100644
--- a/src/tools/clippy/clippy_lints/src/strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strings.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg};
 use clippy_utils::source::{snippet, snippet_with_applicability};
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::is_type_lang_item;
 use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method_calls, paths};
 use clippy_utils::{peel_blocks, SpanlessEq};
 use if_chain::if_chain;
@@ -190,7 +190,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd {
             },
             ExprKind::Index(target, _idx) => {
                 let e_ty = cx.typeck_results().expr_ty(target).peel_refs();
-                if matches!(e_ty.kind(), ty::Str) || is_type_diagnostic_item(cx, e_ty, sym::String) {
+                if matches!(e_ty.kind(), ty::Str) || is_type_lang_item(cx, e_ty, LangItem::String) {
                     span_lint(
                         cx,
                         STRING_SLICE,
@@ -205,7 +205,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd {
 }
 
 fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
-    is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::String)
+    is_type_lang_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), LangItem::String)
 }
 
 fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {
@@ -446,7 +446,7 @@ impl<'tcx> LateLintPass<'tcx> for StringToString {
             if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind;
             if path.ident.name == sym::to_string;
             let ty = cx.typeck_results().expr_ty(self_arg);
-            if is_type_diagnostic_item(cx, ty, sym::String);
+            if is_type_lang_item(cx, ty, LangItem::String);
             then {
                 span_lint_and_help(
                     cx,
diff --git a/src/tools/clippy/clippy_lints/src/types/box_collection.rs b/src/tools/clippy/clippy_lints/src/types/box_collection.rs
index 08020ce6638..802415e163d 100644
--- a/src/tools/clippy/clippy_lints/src/types/box_collection.rs
+++ b/src/tools/clippy/clippy_lints/src/types/box_collection.rs
@@ -37,18 +37,19 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
 fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<Symbol> {
     let param = qpath_generic_tys(qpath).next()?;
     let id = path_def_id(cx, param)?;
-    cx.tcx.get_diagnostic_name(id).filter(|&name| {
-        matches!(
-            name,
-            sym::HashMap
-                | sym::String
-                | sym::Vec
-                | sym::HashSet
-                | sym::VecDeque
-                | sym::LinkedList
-                | sym::BTreeMap
-                | sym::BTreeSet
-                | sym::BinaryHeap
-        )
-    })
+    cx.tcx
+        .get_diagnostic_name(id)
+        .filter(|&name| matches!(name, sym::HashMap | sym::Vec | sym::HashSet
+            | sym::VecDeque
+            | sym::LinkedList
+            | sym::BTreeMap
+            | sym::BTreeSet
+            | sym::BinaryHeap))
+        .or_else(|| {
+            cx.tcx
+                .lang_items()
+                .string()
+                .filter(|did| id == *did)
+                .map(|_| sym::String)
+        })
 }
diff --git a/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs b/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs
index fa567b9b2d2..855137b14d8 100644
--- a/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs
+++ b/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs
@@ -91,10 +91,10 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
 fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> {
     let ty = qpath_generic_tys(qpath).next()?;
     let id = path_def_id(cx, ty)?;
-    let path = match cx.tcx.get_diagnostic_name(id)? {
-        sym::String => "str",
-        sym::OsString => "std::ffi::OsStr",
-        sym::PathBuf => "std::path::Path",
+    let path = match cx.tcx.get_diagnostic_name(id) {
+        Some(sym::OsString) => "std::ffi::OsStr",
+        Some(sym::PathBuf) => "std::path::Path",
+        _ if Some(id) == cx.tcx.lang_items().string() => "str",
         _ => return None,
     };
     Some(path)
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs
index ab73f0fc44f..9f207d32fcf 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs
@@ -1,13 +1,12 @@
-use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item};
+use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_lang_item};
 use clippy_utils::{match_def_path, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
+use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -61,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings {
                         if let LitKind::Str(symbol, _) = spanned.node;
                         if symbol.is_empty();
                         let inner_expr_type = cx.typeck_results().expr_ty(inner_expr);
-                        if is_type_diagnostic_item(cx, inner_expr_type, sym::String);
+                        if is_type_lang_item(cx, inner_expr_type, LangItem::String);
                         then {
                             span_lint_and_sugg(
                                 cx,
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index bb91317d67f..3f93b9b491d 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -435,6 +435,16 @@ pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[
 }
 
 /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
+/// it matches the given lang item.
+pub fn is_path_lang_item<'tcx>(
+    cx: &LateContext<'_>,
+    maybe_path: &impl MaybePath<'tcx>,
+    lang_item: LangItem,
+) -> bool {
+    path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.lang_items().get(lang_item) == Some(id))
+}
+
+/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
 /// it matches the given diagnostic item.
 pub fn is_path_diagnostic_item<'tcx>(
     cx: &LateContext<'_>,
@@ -760,7 +770,6 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -
 /// constructor from the std library
 fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
     let std_types_symbols = &[
-        sym::String,
         sym::Vec,
         sym::VecDeque,
         sym::LinkedList,
@@ -777,7 +786,7 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<
                 if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
                     return std_types_symbols
                         .iter()
-                        .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did()));
+                        .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string());
                 }
             }
         }
@@ -834,7 +843,7 @@ fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &
             ExprKind::Lit(hir::Lit {
                 node: LitKind::Str(ref sym, _),
                 ..
-            }) => return sym.is_empty() && is_path_diagnostic_item(cx, ty, sym::String),
+            }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
             ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
             ExprKind::Repeat(_, ArrayLen::Body(len)) => {
                 if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind &&