about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs2
-rw-r--r--compiler/rustc_interface/src/interface.rs139
-rw-r--r--compiler/rustc_interface/src/lib.rs1
-rw-r--r--compiler/rustc_lint/messages.ftl2
-rw-r--r--compiler/rustc_lint/src/internal.rs34
-rw-r--r--compiler/rustc_lint/src/lib.rs3
-rw-r--r--compiler/rustc_lint/src/lints.rs4
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans.rs2
-rw-r--r--compiler/rustc_passes/src/loops.rs2
-rw-r--r--compiler/rustc_smir/src/rustc_internal/mod.rs28
-rw-r--r--compiler/rustc_smir/src/rustc_smir/mod.rs125
-rw-r--r--compiler/rustc_span/src/span_encoding.rs1
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs127
-rw-r--r--compiler/stable_mir/src/error.rs69
-rw-r--r--compiler/stable_mir/src/lib.rs43
-rw-r--r--compiler/stable_mir/src/mir.rs1
-rw-r--r--compiler/stable_mir/src/mir/mono.rs89
-rw-r--r--src/doc/rustdoc/src/unstable-features.md4
-rw-r--r--src/doc/unstable-book/src/compiler-flags/check-cfg.md216
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/entry.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/formatting.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_let_else.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/manual_utils.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_question_mark.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_async_block.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/reference.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/macros.rs2
-rw-r--r--tests/rustdoc-ui/check-cfg/check-cfg-unstable.rs2
-rw-r--r--tests/rustdoc-ui/check-cfg/check-cfg.rs2
-rw-r--r--tests/rustdoc-ui/doctest/check-cfg-test.rs2
-rw-r--r--tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs13
-rw-r--r--tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr14
-rw-r--r--tests/ui-fulldeps/stable-mir/instance.rs91
-rw-r--r--tests/ui/check-cfg/allow-at-crate-level.rs2
-rw-r--r--tests/ui/check-cfg/allow-macro-cfg.rs2
-rw-r--r--tests/ui/check-cfg/allow-same-level.rs2
-rw-r--r--tests/ui/check-cfg/allow-top-level.rs2
-rw-r--r--tests/ui/check-cfg/allow-upper-level.rs2
-rw-r--r--tests/ui/check-cfg/compact-names.rs2
-rw-r--r--tests/ui/check-cfg/compact-values.rs2
-rw-r--r--tests/ui/check-cfg/concat-values.rs13
-rw-r--r--tests/ui/check-cfg/concat-values.stderr19
-rw-r--r--tests/ui/check-cfg/diagnotics.rs2
-rw-r--r--tests/ui/check-cfg/empty-names.rs10
-rw-r--r--tests/ui/check-cfg/empty-values.rs10
-rw-r--r--tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr25
-rw-r--r--tests/ui/check-cfg/exhaustive-names-values.empty_names_values.stderr25
-rw-r--r--tests/ui/check-cfg/exhaustive-names-values.feature.stderr33
-rw-r--r--tests/ui/check-cfg/exhaustive-names-values.full.stderr33
-rw-r--r--tests/ui/check-cfg/exhaustive-names-values.rs34
-rw-r--r--tests/ui/check-cfg/exhaustive-names.empty_names.stderr (renamed from tests/ui/check-cfg/empty-names.stderr)8
-rw-r--r--tests/ui/check-cfg/exhaustive-names.exhaustive_names.stderr15
-rw-r--r--tests/ui/check-cfg/exhaustive-names.rs12
-rw-r--r--tests/ui/check-cfg/exhaustive-values.empty_cfg.stderr17
-rw-r--r--tests/ui/check-cfg/exhaustive-values.empty_values.stderr (renamed from tests/ui/check-cfg/empty-values.stderr)2
-rw-r--r--tests/ui/check-cfg/exhaustive-values.rs13
-rw-r--r--tests/ui/check-cfg/exhaustive-values.without_names.stderr13
-rw-r--r--tests/ui/check-cfg/invalid-arguments.anything_else.stderr2
-rw-r--r--tests/ui/check-cfg/invalid-arguments.giberich.stderr2
-rw-r--r--tests/ui/check-cfg/invalid-arguments.ident_in_values_1.stderr2
-rw-r--r--tests/ui/check-cfg/invalid-arguments.ident_in_values_2.stderr2
-rw-r--r--tests/ui/check-cfg/invalid-arguments.mixed_any.stderr2
-rw-r--r--tests/ui/check-cfg/invalid-arguments.mixed_values_any.stderr2
-rw-r--r--tests/ui/check-cfg/invalid-arguments.multiple_any.stderr2
-rw-r--r--tests/ui/check-cfg/invalid-arguments.multiple_values.stderr2
-rw-r--r--tests/ui/check-cfg/invalid-arguments.multiple_values_any.stderr2
-rw-r--r--tests/ui/check-cfg/invalid-arguments.not_empty_any.stderr2
-rw-r--r--tests/ui/check-cfg/invalid-arguments.not_empty_values_any.stderr2
-rw-r--r--tests/ui/check-cfg/invalid-arguments.rs32
-rw-r--r--tests/ui/check-cfg/invalid-arguments.string_for_name_1.stderr2
-rw-r--r--tests/ui/check-cfg/invalid-arguments.string_for_name_2.stderr2
-rw-r--r--tests/ui/check-cfg/invalid-arguments.unknown_meta_item_1.stderr2
-rw-r--r--tests/ui/check-cfg/invalid-arguments.unknown_meta_item_2.stderr2
-rw-r--r--tests/ui/check-cfg/invalid-arguments.unknown_meta_item_3.stderr2
-rw-r--r--tests/ui/check-cfg/invalid-arguments.values_any_before_ident.stderr2
-rw-r--r--tests/ui/check-cfg/invalid-arguments.values_any_missing_values.stderr2
-rw-r--r--tests/ui/check-cfg/invalid-cfg-name.rs14
-rw-r--r--tests/ui/check-cfg/invalid-cfg-value.rs18
-rw-r--r--tests/ui/check-cfg/mix.cfg.stderr (renamed from tests/ui/check-cfg/mix.stderr)54
-rw-r--r--tests/ui/check-cfg/mix.names_values.stderr192
-rw-r--r--tests/ui/check-cfg/mix.rs6
-rw-r--r--tests/ui/check-cfg/no-expected-values.empty.stderr (renamed from tests/ui/check-cfg/no-values.stderr)4
-rw-r--r--tests/ui/check-cfg/no-expected-values.mixed.stderr23
-rw-r--r--tests/ui/check-cfg/no-expected-values.rs20
-rw-r--r--tests/ui/check-cfg/no-expected-values.simple.stderr23
-rw-r--r--tests/ui/check-cfg/no-expected-values.values.stderr23
-rw-r--r--tests/ui/check-cfg/no-values.rs14
-rw-r--r--tests/ui/check-cfg/stmt-no-ice.rs2
-rw-r--r--tests/ui/check-cfg/unexpected-cfg-name.exhaustive.stderr (renamed from tests/ui/check-cfg/invalid-cfg-name.stderr)2
-rw-r--r--tests/ui/check-cfg/unexpected-cfg-name.names.stderr10
-rw-r--r--tests/ui/check-cfg/unexpected-cfg-name.rs16
-rw-r--r--tests/ui/check-cfg/unexpected-cfg-value.cfg.stderr (renamed from tests/ui/check-cfg/invalid-cfg-value.stderr)4
-rw-r--r--tests/ui/check-cfg/unexpected-cfg-value.rs22
-rw-r--r--tests/ui/check-cfg/unexpected-cfg-value.values.stderr25
-rw-r--r--tests/ui/check-cfg/unknown-values.rs17
-rw-r--r--tests/ui/check-cfg/well-known-names.rs2
-rw-r--r--tests/ui/check-cfg/well-known-names.stderr8
-rw-r--r--tests/ui/check-cfg/well-known-values.rs2
-rw-r--r--tests/ui/iterators/invalid-iterator-chain-fixable.fixed42
-rw-r--r--tests/ui/iterators/invalid-iterator-chain-fixable.rs42
-rw-r--r--tests/ui/iterators/invalid-iterator-chain-fixable.stderr157
-rw-r--r--tests/ui/iterators/invalid-iterator-chain.stderr26
110 files changed, 1905 insertions, 284 deletions
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index b0c95e1bd20..ab18cc32d9b 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -1185,7 +1185,11 @@ fn print_flag_list<T>(
 ///
 /// So with all that in mind, the comments below have some more detail about the
 /// contortions done here to get things to work out correctly.
-fn handle_options(handler: &EarlyErrorHandler, args: &[String]) -> Option<getopts::Matches> {
+///
+/// This does not need to be `pub` for rustc itself, but @chaosite needs it to
+/// be public when using rustc as a library, see
+/// <https://github.com/rust-lang/rust/commit/2b4c33817a5aaecabf4c6598d41e190080ec119e>
+pub fn handle_options(handler: &EarlyErrorHandler, args: &[String]) -> Option<getopts::Matches> {
     if args.is_empty() {
         // user did not write `-v` nor `-Z unstable-options`, so do not
         // include that extra information.
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 512d73fc103..1c23ccd1579 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -650,7 +650,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 .sess
                 .source_map()
                 .is_multiline(call_expr.span.with_lo(callee_expr.span.hi()))
-                && call_expr.span.ctxt() == callee_expr.span.ctxt();
+                && call_expr.span.eq_ctxt(callee_expr.span);
             if call_is_multiline {
                 err.span_suggestion(
                     callee_expr.span.shrink_to_hi(),
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index 3e77a84bf9e..d2ce77ad535 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -125,8 +125,13 @@ pub fn parse_cfgspecs(
 /// Converts strings provided as `--check-cfg [specs]` into a `CheckCfg`.
 pub fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> CheckCfg {
     rustc_span::create_default_session_if_not_set_then(move |_| {
-        let mut check_cfg = CheckCfg::default();
+        // If any --check-cfg is passed then exhaustive_values and exhaustive_names
+        // are enabled by default.
+        let exhaustive_names = !specs.is_empty();
+        let exhaustive_values = !specs.is_empty();
+        let mut check_cfg = CheckCfg { exhaustive_names, exhaustive_values, ..CheckCfg::default() };
 
+        let mut old_syntax = None;
         for s in specs {
             let sess = ParseSess::with_silent_emitter(Some(format!(
                 "this error occurred on the command line: `--check-cfg={s}`"
@@ -142,18 +147,21 @@ pub fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> Check
                 };
             }
 
-            let expected_error = || {
-                error!(
-                    "expected `names(name1, name2, ... nameN)` or \
-                        `values(name, \"value1\", \"value2\", ... \"valueN\")`"
-                )
-            };
+            let expected_error =
+                || error!("expected `cfg(name, values(\"value1\", \"value2\", ... \"valueN\"))`");
 
             match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) {
                 Ok(mut parser) => match parser.parse_meta_item() {
                     Ok(meta_item) if parser.token == token::Eof => {
                         if let Some(args) = meta_item.meta_item_list() {
                             if meta_item.has_name(sym::names) {
+                                // defaults are flipped for the old syntax
+                                if old_syntax == None {
+                                    check_cfg.exhaustive_names = false;
+                                    check_cfg.exhaustive_values = false;
+                                }
+                                old_syntax = Some(true);
+
                                 check_cfg.exhaustive_names = true;
                                 for arg in args {
                                     if arg.is_word() && arg.ident().is_some() {
@@ -167,6 +175,13 @@ pub fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> Check
                                     }
                                 }
                             } else if meta_item.has_name(sym::values) {
+                                // defaults are flipped for the old syntax
+                                if old_syntax == None {
+                                    check_cfg.exhaustive_names = false;
+                                    check_cfg.exhaustive_values = false;
+                                }
+                                old_syntax = Some(true);
+
                                 if let Some((name, values)) = args.split_first() {
                                     if name.is_word() && name.ident().is_some() {
                                         let ident = name.ident().expect("multi-segment cfg key");
@@ -216,6 +231,116 @@ pub fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> Check
                                 } else {
                                     expected_error();
                                 }
+                            } else if meta_item.has_name(sym::cfg) {
+                                old_syntax = Some(false);
+
+                                let mut names = Vec::new();
+                                let mut values: FxHashSet<_> = Default::default();
+
+                                let mut any_specified = false;
+                                let mut values_specified = false;
+                                let mut values_any_specified = false;
+
+                                for arg in args {
+                                    if arg.is_word() && let Some(ident) = arg.ident() {
+                                        if values_specified {
+                                            error!("`cfg()` names cannot be after values");
+                                        }
+                                        names.push(ident);
+                                    } else if arg.has_name(sym::any)
+                                        && let Some(args) = arg.meta_item_list()
+                                    {
+                                        if any_specified {
+                                            error!("`any()` cannot be specified multiple times");
+                                        }
+                                        any_specified = true;
+                                        if !args.is_empty() {
+                                            error!("`any()` must be empty");
+                                        }
+                                    } else if arg.has_name(sym::values)
+                                        && let Some(args) = arg.meta_item_list()
+                                    {
+                                        if names.is_empty() {
+                                            error!(
+                                                "`values()` cannot be specified before the names"
+                                            );
+                                        } else if values_specified {
+                                            error!(
+                                                "`values()` cannot be specified multiple times"
+                                            );
+                                        }
+                                        values_specified = true;
+
+                                        for arg in args {
+                                            if let Some(LitKind::Str(s, _)) =
+                                                arg.lit().map(|lit| &lit.kind)
+                                            {
+                                                values.insert(Some(s.to_string()));
+                                            } else if arg.has_name(sym::any)
+                                                && let Some(args) = arg.meta_item_list()
+                                            {
+                                                if values_any_specified {
+                                                    error!(
+                                                        "`any()` in `values()` cannot be specified multiple times"
+                                                    );
+                                                }
+                                                values_any_specified = true;
+                                                if !args.is_empty() {
+                                                    error!("`any()` must be empty");
+                                                }
+                                            } else {
+                                                error!(
+                                                    "`values()` arguments must be string literals or `any()`"
+                                                );
+                                            }
+                                        }
+                                    } else {
+                                        error!(
+                                            "`cfg()` arguments must be simple identifiers, `any()` or `values(...)`"
+                                        );
+                                    }
+                                }
+
+                                if values.is_empty() && !values_any_specified && !any_specified {
+                                    values.insert(None);
+                                } else if !values.is_empty() && values_any_specified {
+                                    error!(
+                                        "`values()` arguments cannot specify string literals and `any()` at the same time"
+                                    );
+                                }
+
+                                if any_specified {
+                                    if !names.is_empty()
+                                        || !values.is_empty()
+                                        || values_any_specified
+                                    {
+                                        error!("`cfg(any())` can only be provided in isolation");
+                                    }
+
+                                    check_cfg.exhaustive_names = false;
+                                } else {
+                                    for name in names {
+                                        check_cfg
+                                            .expecteds
+                                            .entry(name.to_string())
+                                            .and_modify(|v| match v {
+                                                ExpectedValues::Some(v)
+                                                    if !values_any_specified =>
+                                                {
+                                                    v.extend(values.clone())
+                                                }
+                                                ExpectedValues::Some(_) => *v = ExpectedValues::Any,
+                                                ExpectedValues::Any => {}
+                                            })
+                                            .or_insert_with(|| {
+                                                if values_any_specified {
+                                                    ExpectedValues::Any
+                                                } else {
+                                                    ExpectedValues::Some(values.clone())
+                                                }
+                                            });
+                                    }
+                                }
                             } else {
                                 expected_error();
                             }
diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs
index 76131c1ad69..ffa2667a351 100644
--- a/compiler/rustc_interface/src/lib.rs
+++ b/compiler/rustc_interface/src/lib.rs
@@ -3,6 +3,7 @@
 #![feature(internal_output_capture)]
 #![feature(thread_spawn_unchecked)]
 #![feature(lazy_cell)]
+#![feature(let_chains)]
 #![feature(try_blocks)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 197fe6552d7..4c4d2933bf4 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -494,6 +494,8 @@ lint_renamed_lint = lint `{$name}` has been renamed to `{$replace}`
 
 lint_requested_level = requested on the command line with `{$level} {$lint_name}`
 
+lint_span_use_eq_ctxt = use `.eq_ctxt()` instead of `.ctxt() == .ctxt()`
+
 lint_supertrait_as_deref_target = `{$t}` implements `Deref` with supertrait `{$target_principal}` as target
     .label = target type is set here
 
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index fc2d3d0a254..2d86129c480 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -3,14 +3,14 @@
 
 use crate::lints::{
     BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonExistentDocKeyword,
-    QueryInstability, TyQualified, TykindDiag, TykindKind, UntranslatableDiag,
+    QueryInstability, SpanUseEqCtxtDiag, TyQualified, TykindDiag, TykindKind, UntranslatableDiag,
     UntranslatableDiagnosticTrivial,
 };
 use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 use rustc_ast as ast;
 use rustc_hir::def::Res;
 use rustc_hir::{def_id::DefId, Expr, ExprKind, GenericArg, PatKind, Path, PathSegment, QPath};
-use rustc_hir::{HirId, Impl, Item, ItemKind, Node, Pat, Ty, TyKind};
+use rustc_hir::{BinOp, BinOpKind, HirId, Impl, Item, ItemKind, Node, Pat, Ty, TyKind};
 use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::hygiene::{ExpnKind, MacroKind};
@@ -537,3 +537,33 @@ impl LateLintPass<'_> for BadOptAccess {
         }
     }
 }
+
+declare_tool_lint! {
+    pub rustc::SPAN_USE_EQ_CTXT,
+    Allow,
+    "forbid uses of `==` with `Span::ctxt`, suggest `Span::eq_ctxt` instead",
+    report_in_external_macro: true
+}
+
+declare_lint_pass!(SpanUseEqCtxt => [SPAN_USE_EQ_CTXT]);
+
+impl<'tcx> LateLintPass<'tcx> for SpanUseEqCtxt {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
+        if let ExprKind::Binary(BinOp { node: BinOpKind::Eq, .. }, lhs, rhs) = expr.kind {
+            if is_span_ctxt_call(cx, lhs) && is_span_ctxt_call(cx, rhs) {
+                cx.emit_spanned_lint(SPAN_USE_EQ_CTXT, expr.span, SpanUseEqCtxtDiag);
+            }
+        }
+    }
+}
+
+fn is_span_ctxt_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    match &expr.kind {
+        ExprKind::MethodCall(..) => cx
+            .typeck_results()
+            .type_dependent_def_id(expr.hir_id)
+            .is_some_and(|call_did| cx.tcx.is_diagnostic_item(sym::SpanCtxt, call_did)),
+
+        _ => false,
+    }
+}
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index b9e455e6c2a..d61c59af1e0 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -531,6 +531,8 @@ fn register_internals(store: &mut LintStore) {
     store.register_late_mod_pass(|_| Box::new(BadOptAccess));
     store.register_lints(&PassByValue::get_lints());
     store.register_late_mod_pass(|_| Box::new(PassByValue));
+    store.register_lints(&SpanUseEqCtxt::get_lints());
+    store.register_late_mod_pass(|_| Box::new(SpanUseEqCtxt));
     // FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and
     // `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and
     // these lints will trigger all of the time - change this once migration to diagnostic structs
@@ -548,6 +550,7 @@ fn register_internals(store: &mut LintStore) {
             LintId::of(USAGE_OF_QUALIFIED_TY),
             LintId::of(EXISTING_DOC_KEYWORD),
             LintId::of(BAD_OPT_ACCESS),
+            LintId::of(SPAN_USE_EQ_CTXT),
         ],
     );
 }
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 594ef97b3ff..4eaf8bbf5de 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -901,6 +901,10 @@ pub struct QueryInstability {
 }
 
 #[derive(LintDiagnostic)]
+#[diag(lint_span_use_eq_ctxt)]
+pub struct SpanUseEqCtxtDiag;
+
+#[derive(LintDiagnostic)]
 #[diag(lint_tykind_kind)]
 pub struct TykindKind {
     #[suggestion(code = "ty", applicability = "maybe-incorrect")]
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index 1d1be8f2492..f1a0f762041 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -404,7 +404,7 @@ impl<'a> CoverageSpansGenerator<'a> {
 
         let Some(visible_macro) = curr.visible_macro(self.body_span) else { return };
         if let Some(prev) = &self.some_prev
-            && prev.expn_span.ctxt() == curr.expn_span.ctxt()
+            && prev.expn_span.eq_ctxt(curr.expn_span)
         {
             return;
         }
diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs
index 4590ab9e4f5..25e131d7477 100644
--- a/compiler/rustc_passes/src/loops.rs
+++ b/compiler/rustc_passes/src/loops.rs
@@ -231,7 +231,7 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
             AsyncClosure(closure_span) => {
                 self.sess.emit_err(BreakInsideAsyncBlock { span, closure_span, name });
             }
-            UnlabeledBlock(block_span) if is_break && block_span.ctxt() == break_span.ctxt() => {
+            UnlabeledBlock(block_span) if is_break && block_span.eq_ctxt(break_span) => {
                 let suggestion = Some(OutsideLoopSuggestion { block_span, break_span });
                 self.sess.emit_err(OutsideLoop { span, name, is_break, suggestion });
             }
diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs
index e3c84f06543..5ea805e5739 100644
--- a/compiler/rustc_smir/src/rustc_internal/mod.rs
+++ b/compiler/rustc_smir/src/rustc_internal/mod.rs
@@ -6,9 +6,11 @@
 use crate::rustc_internal;
 use crate::rustc_smir::Tables;
 use rustc_data_structures::fx;
+use rustc_data_structures::fx::FxIndexMap;
 use rustc_driver::{Callbacks, Compilation, RunCompiler};
 use rustc_interface::{interface, Queries};
 use rustc_middle::mir::interpret::AllocId;
+use rustc_middle::ty;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::{CrateNum, DefId};
 use rustc_span::Span;
@@ -97,7 +99,7 @@ impl<'tcx> Tables<'tcx> {
         stable_mir::ty::Prov(self.create_alloc_id(aid))
     }
 
-    fn create_def_id(&mut self, did: DefId) -> stable_mir::DefId {
+    pub(crate) fn create_def_id(&mut self, did: DefId) -> stable_mir::DefId {
         self.def_ids.create_or_fetch(did)
     }
 
@@ -108,6 +110,17 @@ impl<'tcx> Tables<'tcx> {
     pub(crate) fn create_span(&mut self, span: Span) -> stable_mir::ty::Span {
         self.spans.create_or_fetch(span)
     }
+
+    pub(crate) fn instance_def(
+        &mut self,
+        instance: ty::Instance<'tcx>,
+    ) -> stable_mir::mir::mono::InstanceDef {
+        self.instances.create_or_fetch(instance)
+    }
+
+    pub(crate) fn static_def(&mut self, did: DefId) -> stable_mir::mir::mono::StaticDef {
+        stable_mir::mir::mono::StaticDef(self.create_def_id(did))
+    }
 }
 
 pub fn crate_num(item: &stable_mir::Crate) -> CrateNum {
@@ -118,10 +131,11 @@ pub fn run(tcx: TyCtxt<'_>, f: impl FnOnce()) {
     stable_mir::run(
         Tables {
             tcx,
-            def_ids: rustc_internal::IndexMap { index_map: fx::FxIndexMap::default() },
-            alloc_ids: rustc_internal::IndexMap { index_map: fx::FxIndexMap::default() },
-            spans: rustc_internal::IndexMap { index_map: fx::FxIndexMap::default() },
+            def_ids: IndexMap::default(),
+            alloc_ids: IndexMap::default(),
+            spans: IndexMap::default(),
             types: vec![],
+            instances: IndexMap::default(),
         },
         f,
     );
@@ -192,6 +206,12 @@ pub struct IndexMap<K, V> {
     index_map: fx::FxIndexMap<K, V>,
 }
 
+impl<K, V> Default for IndexMap<K, V> {
+    fn default() -> Self {
+        Self { index_map: FxIndexMap::default() }
+    }
+}
+
 impl<K: PartialEq + Hash + Eq, V: Copy + Debug + PartialEq + IndexedVal> IndexMap<K, V> {
     pub fn create_or_fetch(&mut self, key: K) -> V {
         let len = self.index_map.len();
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
index f26d18ad38f..94dc15b4767 100644
--- a/compiler/rustc_smir/src/rustc_smir/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -13,10 +13,12 @@ use crate::rustc_smir::stable_mir::ty::{BoundRegion, EarlyBoundRegion, Region};
 use rustc_hir as hir;
 use rustc_middle::mir;
 use rustc_middle::mir::interpret::{alloc_range, AllocId};
-use rustc_middle::ty::{self, Ty, TyCtxt, Variance};
+use rustc_middle::mir::mono::MonoItem;
+use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt, Variance};
 use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE};
 use rustc_target::abi::FieldIdx;
-use stable_mir::mir::{CopyNonOverlapping, Statement, UserTypeProjection, VariantIdx};
+use stable_mir::mir::mono::InstanceDef;
+use stable_mir::mir::{Body, CopyNonOverlapping, Statement, UserTypeProjection, VariantIdx};
 use stable_mir::ty::{
     FloatTy, GenericParamDef, IntTy, LineInfo, Movability, RigidTy, Span, TyKind, UintTy,
 };
@@ -119,29 +121,7 @@ impl<'tcx> Context for Tables<'tcx> {
 
     fn mir_body(&mut self, item: stable_mir::DefId) -> stable_mir::mir::Body {
         let def_id = self[item];
-        let mir = self.tcx.instance_mir(ty::InstanceDef::Item(def_id));
-        stable_mir::mir::Body {
-            blocks: mir
-                .basic_blocks
-                .iter()
-                .map(|block| stable_mir::mir::BasicBlock {
-                    terminator: block.terminator().stable(self),
-                    statements: block
-                        .statements
-                        .iter()
-                        .map(|statement| statement.stable(self))
-                        .collect(),
-                })
-                .collect(),
-            locals: mir
-                .local_decls
-                .iter()
-                .map(|decl| stable_mir::mir::LocalDecl {
-                    ty: self.intern_ty(decl.ty),
-                    span: decl.source_info.span.stable(self),
-                })
-                .collect(),
-        }
+        self.tcx.instance_mir(ty::InstanceDef::Item(def_id)).stable(self)
     }
 
     fn ty_kind(&mut self, ty: stable_mir::ty::Ty) -> TyKind {
@@ -190,6 +170,34 @@ impl<'tcx> Context for Tables<'tcx> {
                 .collect(),
         }
     }
+
+    fn instance_body(&mut self, _def: InstanceDef) -> Body {
+        todo!("Monomorphize the body")
+    }
+
+    fn instance_ty(&mut self, def: InstanceDef) -> stable_mir::ty::Ty {
+        let instance = self.instances[def];
+        let ty = instance.ty(self.tcx, ParamEnv::empty());
+        self.intern_ty(ty)
+    }
+
+    fn instance_def_id(&mut self, def: InstanceDef) -> stable_mir::DefId {
+        let def_id = self.instances[def].def_id();
+        self.create_def_id(def_id)
+    }
+
+    fn mono_instance(&mut self, item: stable_mir::CrateItem) -> stable_mir::mir::mono::Instance {
+        let def_id = self[item.0];
+        Instance::mono(self.tcx, def_id).stable(self)
+    }
+
+    fn requires_monomorphization(&self, def_id: stable_mir::DefId) -> bool {
+        let def_id = self[def_id];
+        let generics = self.tcx.generics_of(def_id);
+        let result = generics.requires_monomorphization(self.tcx);
+        println!("req {result}: {def_id:?}");
+        result
+    }
 }
 
 #[derive(Clone)]
@@ -224,7 +232,8 @@ pub struct Tables<'tcx> {
     pub def_ids: IndexMap<DefId, stable_mir::DefId>,
     pub alloc_ids: IndexMap<AllocId, stable_mir::AllocId>,
     pub spans: IndexMap<rustc_span::Span, Span>,
-    pub types: Vec<MaybeStable<stable_mir::ty::TyKind, Ty<'tcx>>>,
+    pub types: Vec<MaybeStable<TyKind, Ty<'tcx>>>,
+    pub instances: IndexMap<ty::Instance<'tcx>, InstanceDef>,
 }
 
 impl<'tcx> Tables<'tcx> {
@@ -254,6 +263,35 @@ pub(crate) trait Stable<'tcx> {
     fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T;
 }
 
+impl<'tcx> Stable<'tcx> for mir::Body<'tcx> {
+    type T = stable_mir::mir::Body;
+
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        stable_mir::mir::Body {
+            blocks: self
+                .basic_blocks
+                .iter()
+                .map(|block| stable_mir::mir::BasicBlock {
+                    terminator: block.terminator().stable(tables),
+                    statements: block
+                        .statements
+                        .iter()
+                        .map(|statement| statement.stable(tables))
+                        .collect(),
+                })
+                .collect(),
+            locals: self
+                .local_decls
+                .iter()
+                .map(|decl| stable_mir::mir::LocalDecl {
+                    ty: tables.intern_ty(decl.ty),
+                    span: decl.source_info.span.stable(tables),
+                })
+                .collect(),
+        }
+    }
+}
+
 impl<'tcx> Stable<'tcx> for mir::Statement<'tcx> {
     type T = stable_mir::mir::Statement;
     fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
@@ -1637,3 +1675,38 @@ impl<'tcx> Stable<'tcx> for DefKind {
         opaque(self)
     }
 }
+
+impl<'tcx> Stable<'tcx> for ty::Instance<'tcx> {
+    type T = stable_mir::mir::mono::Instance;
+
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        let def = tables.instance_def(*self);
+        let kind = match self.def {
+            ty::InstanceDef::Item(..) => stable_mir::mir::mono::InstanceKind::Item,
+            ty::InstanceDef::Intrinsic(..) => stable_mir::mir::mono::InstanceKind::Intrinsic,
+            ty::InstanceDef::Virtual(..) => stable_mir::mir::mono::InstanceKind::Virtual,
+            ty::InstanceDef::VTableShim(..)
+            | ty::InstanceDef::ReifyShim(..)
+            | ty::InstanceDef::FnPtrAddrShim(..)
+            | ty::InstanceDef::ClosureOnceShim { .. }
+            | ty::InstanceDef::ThreadLocalShim(..)
+            | ty::InstanceDef::DropGlue(..)
+            | ty::InstanceDef::CloneShim(..)
+            | ty::InstanceDef::FnPtrShim(..) => stable_mir::mir::mono::InstanceKind::Shim,
+        };
+        stable_mir::mir::mono::Instance { def, kind }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for MonoItem<'tcx> {
+    type T = stable_mir::mir::mono::MonoItem;
+
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        use stable_mir::mir::mono::MonoItem as StableMonoItem;
+        match self {
+            MonoItem::Fn(instance) => StableMonoItem::Fn(instance.stable(tables)),
+            MonoItem::Static(def_id) => StableMonoItem::Static(tables.static_def(*def_id)),
+            MonoItem::GlobalAsm(item_id) => StableMonoItem::GlobalAsm(opaque(item_id)),
+        }
+    }
+}
diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs
index bfc9e125362..f7d17a267d6 100644
--- a/compiler/rustc_span/src/span_encoding.rs
+++ b/compiler/rustc_span/src/span_encoding.rs
@@ -212,6 +212,7 @@ impl Span {
 
     /// This function is used as a fast path when decoding the full `SpanData` is not necessary.
     /// It's a cut-down version of `data_untracked`.
+    #[cfg_attr(not(test), rustc_diagnostic_item = "SpanCtxt")]
     #[inline]
     pub fn ctxt(self) -> SyntaxContext {
         if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index ea261923c65..be8c65862dc 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -303,6 +303,7 @@ symbols! {
         SliceIndex,
         SliceIter,
         Some,
+        SpanCtxt,
         String,
         StructuralEq,
         StructuralPartialEq,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 78f7f915f72..4329460b1b7 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -313,6 +313,18 @@ pub trait TypeErrCtxtExt<'tcx> {
         predicate: ty::Predicate<'tcx>,
         call_hir_id: HirId,
     );
+
+    fn look_for_iterator_item_mistakes(
+        &self,
+        assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>],
+        typeck_results: &TypeckResults<'tcx>,
+        type_diffs: &[TypeError<'tcx>],
+        param_env: ty::ParamEnv<'tcx>,
+        path_segment: &hir::PathSegment<'_>,
+        args: &[hir::Expr<'_>],
+        err: &mut Diagnostic,
+    );
+
     fn point_at_chain(
         &self,
         expr: &hir::Expr<'_>,
@@ -321,6 +333,7 @@ pub trait TypeErrCtxtExt<'tcx> {
         param_env: ty::ParamEnv<'tcx>,
         err: &mut Diagnostic,
     );
+
     fn probe_assoc_types_at_expr(
         &self,
         type_diffs: &[TypeError<'tcx>],
@@ -3612,6 +3625,109 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         }
     }
 
+    fn look_for_iterator_item_mistakes(
+        &self,
+        assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>],
+        typeck_results: &TypeckResults<'tcx>,
+        type_diffs: &[TypeError<'tcx>],
+        param_env: ty::ParamEnv<'tcx>,
+        path_segment: &hir::PathSegment<'_>,
+        args: &[hir::Expr<'_>],
+        err: &mut Diagnostic,
+    ) {
+        let tcx = self.tcx;
+        // Special case for iterator chains, we look at potential failures of `Iterator::Item`
+        // not being `: Clone` and `Iterator::map` calls with spurious trailing `;`.
+        for entry in assocs_in_this_method {
+            let Some((_span, (def_id, ty))) = entry else {
+                continue;
+            };
+            for diff in type_diffs {
+                let Sorts(expected_found) = diff else {
+                    continue;
+                };
+                if tcx.is_diagnostic_item(sym::IteratorItem, *def_id)
+                    && path_segment.ident.name == sym::map
+                    && self.can_eq(param_env, expected_found.found, *ty)
+                    && let [arg] = args
+                    && let hir::ExprKind::Closure(closure) = arg.kind
+                {
+                    let body = tcx.hir().body(closure.body);
+                    if let hir::ExprKind::Block(block, None) = body.value.kind
+                        && let None = block.expr
+                        && let [.., stmt] = block.stmts
+                        && let hir::StmtKind::Semi(expr) = stmt.kind
+                        // FIXME: actually check the expected vs found types, but right now
+                        // the expected is a projection that we need to resolve.
+                        // && let Some(tail_ty) = typeck_results.expr_ty_opt(expr)
+                        && expected_found.found.is_unit()
+                    {
+                        err.span_suggestion_verbose(
+                            expr.span.shrink_to_hi().with_hi(stmt.span.hi()),
+                            "consider removing this semicolon",
+                            String::new(),
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                    let expr = if let hir::ExprKind::Block(block, None) = body.value.kind
+                        && let Some(expr) = block.expr
+                    {
+                        expr
+                    } else {
+                        body.value
+                    };
+                    if let hir::ExprKind::MethodCall(path_segment, rcvr, [], span) = expr.kind
+                        && path_segment.ident.name == sym::clone
+                        && let Some(expr_ty) = typeck_results.expr_ty_opt(expr)
+                        && let Some(rcvr_ty) = typeck_results.expr_ty_opt(rcvr)
+                        && self.can_eq(param_env, expr_ty, rcvr_ty)
+                        && let ty::Ref(_, ty, _) = expr_ty.kind()
+                    {
+                        err.span_label(
+                            span,
+                            format!(
+                                "this method call is cloning the reference `{expr_ty}`, not \
+                                 `{ty}` which doesn't implement `Clone`",
+                            ),
+                        );
+                        let ty::Param(..) = ty.kind() else {
+                            continue;
+                        };
+                        let hir = tcx.hir();
+                        let node = hir.get_by_def_id(hir.get_parent_item(expr.hir_id).def_id);
+
+                        let pred = ty::Binder::dummy(ty::TraitPredicate {
+                            trait_ref: ty::TraitRef::from_lang_item(
+                                tcx,
+                                LangItem::Clone,
+                                span,
+                                [*ty],
+                            ),
+                            polarity: ty::ImplPolarity::Positive,
+                        });
+                        let Some(generics) = node.generics() else {
+                            continue;
+                        };
+                        let Some(body_id) = node.body_id() else {
+                            continue;
+                        };
+                        suggest_restriction(
+                            tcx,
+                            hir.body_owner_def_id(body_id),
+                            &generics,
+                            &format!("type parameter `{ty}`"),
+                            err,
+                            node.fn_sig(),
+                            None,
+                            pred,
+                            None,
+                        );
+                    }
+                }
+            }
+        }
+    }
+
     fn point_at_chain(
         &self,
         expr: &hir::Expr<'_>,
@@ -3631,13 +3747,22 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         let mut prev_ty = self.resolve_vars_if_possible(
             typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)),
         );
-        while let hir::ExprKind::MethodCall(_path_segment, rcvr_expr, _args, span) = expr.kind {
+        while let hir::ExprKind::MethodCall(path_segment, rcvr_expr, args, span) = expr.kind {
             // Point at every method call in the chain with the resulting type.
             // vec![1, 2, 3].iter().map(mapper).sum<i32>()
             //               ^^^^^^ ^^^^^^^^^^^
             expr = rcvr_expr;
             let assocs_in_this_method =
                 self.probe_assoc_types_at_expr(&type_diffs, span, prev_ty, expr.hir_id, param_env);
+            self.look_for_iterator_item_mistakes(
+                &assocs_in_this_method,
+                typeck_results,
+                &type_diffs,
+                param_env,
+                path_segment,
+                args,
+                err,
+            );
             assocs.push(assocs_in_this_method);
             prev_ty = self.resolve_vars_if_possible(
                 typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)),
diff --git a/compiler/stable_mir/src/error.rs b/compiler/stable_mir/src/error.rs
new file mode 100644
index 00000000000..12ac8f1ca65
--- /dev/null
+++ b/compiler/stable_mir/src/error.rs
@@ -0,0 +1,69 @@
+//! When things go wrong, we need some error handling.
+//! There are a few different types of errors in StableMIR:
+//!
+//! - [CompilerError]: This represents errors that can be raised when invoking the compiler.
+//! - [Error]: Generic error that represents the reason why a request that could not be fulfilled.
+
+use std::fmt::{Debug, Display, Formatter};
+use std::{error, fmt};
+
+/// An error type used to represent an error that has already been reported by the compiler.
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub enum CompilerError<T> {
+    /// Internal compiler error (I.e.: Compiler crashed).
+    ICE,
+    /// Compilation failed.
+    CompilationFailed,
+    /// Compilation was interrupted.
+    Interrupted(T),
+    /// Compilation skipped. This happens when users invoke rustc to retrieve information such as
+    /// --version.
+    Skipped,
+}
+
+/// A generic error to represent an API request that cannot be fulfilled.
+#[derive(Debug)]
+pub struct Error(String);
+
+impl Error {
+    pub(crate) fn new(msg: String) -> Self {
+        Self(msg)
+    }
+}
+
+impl Display for Error {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        Display::fmt(&self.0, f)
+    }
+}
+
+impl<T> Display for CompilerError<T>
+where
+    T: Display,
+{
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        match self {
+            CompilerError::ICE => write!(f, "Internal Compiler Error"),
+            CompilerError::CompilationFailed => write!(f, "Compilation Failed"),
+            CompilerError::Interrupted(reason) => write!(f, "Compilation Interrupted: {reason}"),
+            CompilerError::Skipped => write!(f, "Compilation Skipped"),
+        }
+    }
+}
+
+impl<T> Debug for CompilerError<T>
+where
+    T: Debug,
+{
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        match self {
+            CompilerError::ICE => write!(f, "Internal Compiler Error"),
+            CompilerError::CompilationFailed => write!(f, "Compilation Failed"),
+            CompilerError::Interrupted(reason) => write!(f, "Compilation Interrupted: {reason:?}"),
+            CompilerError::Skipped => write!(f, "Compilation Skipped"),
+        }
+    }
+}
+
+impl error::Error for Error {}
+impl<T> error::Error for CompilerError<T> where T: Display + Debug {}
diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs
index a3b05f2435e..59af3f64ad3 100644
--- a/compiler/stable_mir/src/lib.rs
+++ b/compiler/stable_mir/src/lib.rs
@@ -17,6 +17,8 @@
 //! The goal is to eventually be published on
 //! [crates.io](https://crates.io).
 
+use crate::mir::mono::InstanceDef;
+use crate::mir::Body;
 use std::cell::Cell;
 use std::fmt;
 use std::fmt::Debug;
@@ -29,11 +31,15 @@ use self::ty::{
 #[macro_use]
 extern crate scoped_tls;
 
+pub mod error;
 pub mod fold;
 pub mod mir;
 pub mod ty;
 pub mod visitor;
 
+pub use error::*;
+use mir::mono::Instance;
+
 /// Use String for now but we should replace it.
 pub type Symbol = String;
 
@@ -85,20 +91,6 @@ pub type TraitDecls = Vec<TraitDef>;
 /// A list of impl trait decls.
 pub type ImplTraitDecls = Vec<ImplDef>;
 
-/// An error type used to represent an error that has already been reported by the compiler.
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub enum CompilerError<T> {
-    /// Internal compiler error (I.e.: Compiler crashed).
-    ICE,
-    /// Compilation failed.
-    CompilationFailed,
-    /// Compilation was interrupted.
-    Interrupted(T),
-    /// Compilation skipped. This happens when users invoke rustc to retrieve information such as
-    /// --version.
-    Skipped,
-}
-
 /// Holds information about a crate.
 #[derive(Clone, PartialEq, Eq, Debug)]
 pub struct Crate {
@@ -113,7 +105,7 @@ pub type Filename = Opaque;
 /// Holds information about an item in the crate.
 /// For now, it only stores the item DefId. Use functions inside `rustc_internal` module to
 /// use this item.
-#[derive(Clone, PartialEq, Eq, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub struct CrateItem(pub DefId);
 
 impl CrateItem {
@@ -132,6 +124,10 @@ impl CrateItem {
     pub fn kind(&self) -> DefKind {
         with(|cx| cx.def_kind(self.0))
     }
+
+    pub fn requires_monomorphization(&self) -> bool {
+        with(|cx| cx.requires_monomorphization(self.0))
+    }
 }
 
 /// Return the function where execution starts if the current
@@ -220,6 +216,23 @@ pub trait Context {
 
     /// Create a new `Ty` from scratch without information from rustc.
     fn mk_ty(&mut self, kind: TyKind) -> Ty;
+
+    /// Get the body of an Instance.
+    /// FIXME: Monomorphize the body.
+    fn instance_body(&mut self, instance: InstanceDef) -> Body;
+
+    /// Get the instance type with generic substitutions applied and lifetimes erased.
+    fn instance_ty(&mut self, instance: InstanceDef) -> Ty;
+
+    /// Get the instance.
+    fn instance_def_id(&mut self, instance: InstanceDef) -> DefId;
+
+    /// Convert a non-generic crate item into an instance.
+    /// This function will panic if the item is generic.
+    fn mono_instance(&mut self, item: CrateItem) -> Instance;
+
+    /// Item requires monomorphization.
+    fn requires_monomorphization(&self, def_id: DefId) -> bool;
 }
 
 // A thread local variable that stores a pointer to the tables mapping between TyCtxt
diff --git a/compiler/stable_mir/src/mir.rs b/compiler/stable_mir/src/mir.rs
index a9dbc3463f8..3138bb1ec83 100644
--- a/compiler/stable_mir/src/mir.rs
+++ b/compiler/stable_mir/src/mir.rs
@@ -1,3 +1,4 @@
 mod body;
+pub mod mono;
 
 pub use body::*;
diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs
new file mode 100644
index 00000000000..d8e8ccb0454
--- /dev/null
+++ b/compiler/stable_mir/src/mir/mono.rs
@@ -0,0 +1,89 @@
+use crate::mir::Body;
+use crate::ty::{IndexedVal, Ty};
+use crate::{with, CrateItem, DefId, Error, Opaque};
+use std::fmt::Debug;
+
+#[derive(Clone, Debug)]
+pub enum MonoItem {
+    Fn(Instance),
+    Static(StaticDef),
+    GlobalAsm(Opaque),
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct Instance {
+    /// The type of instance.
+    pub kind: InstanceKind,
+    /// An ID used to get the instance definition from the compiler.
+    /// Do not use this field directly.
+    pub def: InstanceDef,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum InstanceKind {
+    /// A user defined item.
+    Item,
+    /// A compiler intrinsic function.
+    Intrinsic,
+    /// A virtual function definition stored in a VTable.
+    Virtual,
+    /// A compiler generated shim.
+    Shim,
+}
+
+impl Instance {
+    /// Get the body of an Instance. The body will be eagerly monomorphized.
+    pub fn body(&self) -> Body {
+        with(|context| context.instance_body(self.def))
+    }
+
+    /// Get the instance type with generic substitutions applied and lifetimes erased.
+    pub fn ty(&self) -> Ty {
+        with(|context| context.instance_ty(self.def))
+    }
+}
+
+/// Try to convert a crate item into an instance.
+/// The item cannot be generic in order to be converted into an instance.
+impl TryFrom<CrateItem> for Instance {
+    type Error = crate::Error;
+
+    fn try_from(item: CrateItem) -> Result<Self, Self::Error> {
+        with(|context| {
+            if !context.requires_monomorphization(item.0) {
+                Ok(context.mono_instance(item))
+            } else {
+                Err(Error::new("Item requires monomorphization".to_string()))
+            }
+        })
+    }
+}
+
+/// Try to convert an instance into a crate item.
+/// Only user defined instances can be converted.
+impl TryFrom<Instance> for CrateItem {
+    type Error = crate::Error;
+
+    fn try_from(value: Instance) -> Result<Self, Self::Error> {
+        if value.kind == InstanceKind::Item {
+            Ok(CrateItem(with(|context| context.instance_def_id(value.def))))
+        } else {
+            Err(Error::new(format!("Item kind `{:?}` cannot be converted", value.kind)))
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct InstanceDef(usize);
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct StaticDef(pub DefId);
+
+impl IndexedVal for InstanceDef {
+    fn to_val(index: usize) -> Self {
+        InstanceDef(index)
+    }
+    fn to_index(&self) -> usize {
+        self.0
+    }
+}
diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md
index 199b5d69a1c..41602dec44c 100644
--- a/src/doc/rustdoc/src/unstable-features.md
+++ b/src/doc/rustdoc/src/unstable-features.md
@@ -628,10 +628,10 @@ Using this flag looks like this:
 
 ```bash
 $ rustdoc src/lib.rs -Z unstable-options \
-    --check-cfg='names()' --check-cfg='values(feature, "foo", "bar")'
+    --check-cfg='cfg(feature, values("foo", "bar"))'
 ```
 
-The example above check every well known names (`target_os`, `doc`, `test`, ... via `names()`)
+The example above check every well known names and values (`target_os`, `doc`, `test`, ...)
 and check the values of `feature`: `foo` and `bar`.
 
 ### `--generate-link-to-definition`: Generate links on types in source code
diff --git a/src/doc/unstable-book/src/compiler-flags/check-cfg.md b/src/doc/unstable-book/src/compiler-flags/check-cfg.md
index 10f0fbc5062..ca18ec567a4 100644
--- a/src/doc/unstable-book/src/compiler-flags/check-cfg.md
+++ b/src/doc/unstable-book/src/compiler-flags/check-cfg.md
@@ -10,97 +10,80 @@ This feature allows you to enable complete or partial checking of configuration.
 check them. The `--check-cfg` option takes a value, called the _check cfg specification_. The
 check cfg specification is parsed using the Rust metadata syntax, just as the `--cfg` option is.
 
-`--check-cfg` option can take one of two forms:
+`--check-cfg` option take one form:
 
-1. `--check-cfg names(...)` enables checking condition names.
-2. `--check-cfg values(...)` enables checking the values within list-valued conditions.
-
-These two options are independent. `names` checks only the namespace of condition names
-while `values` checks only the namespace of the values of list-valued conditions.
+1. `--check-cfg cfg(...)` enables checking the values within list-valued conditions.
 
 NOTE: No implicit expectation is added when using `--cfg` for both forms. Users are expected to
-pass all expected names and values using `names(...)` and `values(...)`.
+pass all expected names and values using `cfg(...)`.
 
-## The `names(...)` form
+## The `cfg(...)` form
 
-The `names(...)` form enables checking the names. This form uses a named list:
+The `cfg(...)` form enables checking the values within list-valued conditions. It has this
+basic form:
 
 ```bash
-rustc --check-cfg 'names(name1, name2, ... nameN)'
+rustc --check-cfg 'cfg(name1, ..., nameN, values("value1", "value2", ... "valueN"))'
 ```
 
-where each `name` is a bare identifier (has no quotes). The order of the names is not significant.
+where `name` is a bare identifier (has no quotes) and each `"value"` term is a quoted literal
+string. `name` specifies the name of the condition, such as `feature` or `my_cfg`.
 
-If `--check-cfg names(...)` is specified at least once, then `rustc` will check all references to
-condition names. `rustc` will check every `#[cfg]` attribute, `#[cfg_attr]` attribute, `cfg` clause
-inside `#[link]` attribute and `cfg!(...)` call against the provided list of expected condition
-names. If a name is not present in this list, then `rustc` will report an `unexpected_cfgs` lint
-diagnostic. The default diagnostic level for this lint is `Warn`.
+When the `cfg(...)` option is specified, `rustc` will check every `#[cfg(name = "value")]`
+attribute, `#[cfg_attr(name = "value")]` attribute, `#[link(name = "a", cfg(name = "value"))]`
+and `cfg!(name = "value")` call. It will check that the `"value"` specified is present in the
+list of expected values. If `"value"` is not in it, then `rustc` will report an `unexpected_cfgs`
+lint diagnostic. The default diagnostic level for this lint is `Warn`.
 
-If `--check-cfg names(...)` is not specified, then `rustc` will not check references to condition
-names.
+To enable checking of values, but to provide an empty set of expected values, use these forms:
 
-`--check-cfg names(...)` may be specified more than once. The result is that the list of valid
-condition names is merged across all options. It is legal for a condition name to be specified
-more than once; redundantly specifying a condition name has no effect.
+```bash
+rustc --check-cfg 'cfg(name1, ..., nameN)'
+rustc --check-cfg 'cfg(name1, ..., nameN, values())'
+```
 
-To enable checking condition names with an empty set of valid condition names, use the following
-form. The parentheses are required.
+To enable checking of name but not values (i.e. unknown expected values), use this form:
 
 ```bash
-rustc --check-cfg 'names()'
+rustc --check-cfg 'cfg(name1, ..., nameN, values(any()))'
 ```
 
-Note that `--check-cfg 'names()'` is _not_ equivalent to omitting the option entirely.
-The first form enables checking condition names, while specifying that there are no valid
-condition names (outside of the set of well-known names defined by `rustc`). Omitting the
-`--check-cfg 'names(...)'` option does not enable checking condition names.
-
-## The `values(...)` form
+The `--check-cfg cfg(...)` option can be repeated, both for the same condition name and for
+different names. If it is repeated for the same condition name, then the sets of values for that
+condition are merged together (presedence is given to `any()`).
 
-The `values(...)` form enables checking the values within list-valued conditions. It has this
-form:
+## Well known names and values
 
-```bash
-rustc --check-cfg `values(name, "value1", "value2", ... "valueN")'
-```
+`rustc` has a internal list of well known names and their corresponding values.
+Those well known names and values follows the same stability as what they refer to.
 
-where `name` is a bare identifier (has no quotes) and each `"value"` term is a quoted literal
-string. `name` specifies the name of the condition, such as `feature` or `target_os`.
+Well known values checking is always enabled as long as a `--check-cfg` argument is present.
 
-When the `values(...)` option is specified, `rustc` will check every `#[cfg(name = "value")]`
-attribute, `#[cfg_attr(name = "value")]` attribute, `#[link(name = "a", cfg(name = "value"))]`
-and `cfg!(name = "value")` call. It will check that the `"value"` specified is present in the
-list of expected values. If `"value"` is not in it, then `rustc` will report an `unexpected_cfgs`
-lint diagnostic. The default diagnostic level for this lint is `Warn`.
+Well known names checking is always enable as long as a `--check-cfg` argument is present
+**unless** any `cfg(any())` argument is passed.
 
-To enable checking of values, but to provide an empty set of valid values, use this form:
+To disable checking of well known names, use this form:
 
 ```bash
-rustc --check-cfg `values(name)`
+rustc --check-cfg 'cfg(any())'
 ```
 
-The `--check-cfg values(...)` option can be repeated, both for the same condition name and for
-different names. If it is repeated for the same condition name, then the sets of values for that
-condition are merged together.
-
-If `values()` is specified, then `rustc` will enable the checking of well-known values defined
-by itself. Note that it's necessary to specify the `values()` form to enable the checking of
-well known values, specifying the other forms doesn't implicitly enable it.
+NOTE: If one want to enable values and names checking without having any cfg to declare, one
+can use an empty `cfg()` argument.
 
 ## Examples
 
 Consider this command line:
 
 ```bash
-rustc --check-cfg 'names(feature)' \
-      --check-cfg 'values(feature, "lion", "zebra")' \
+rustc --check-cfg 'cfg(feature, values("lion", "zebra"))' \
       --cfg 'feature="lion"' -Z unstable-options \
       example.rs
 ```
 
 This command line indicates that this crate has two features: `lion` and `zebra`. The `lion`
-feature is enabled, while the `zebra` feature is disabled. Consider compiling this code:
+feature is enabled, while the `zebra` feature is disabled. Exhaustive checking of names and
+values are enabled by default. Consider compiling this code:
 
 ```rust
 // This is expected, and tame_lion() will be compiled
@@ -119,35 +102,36 @@ fn poke_platypus() {}
 // and will cause a compiler warning (by default).
 #[cfg(feechure = "lion")]
 fn tame_lion() {}
-```
 
-> Note: The `--check-cfg names(feature)` option is necessary only to enable checking the condition
-> name, as in the last example. `feature` is a well-known (always-expected) condition name, and so
-> it is not necessary to specify it in a `--check-cfg 'names(...)'` option. That option can be
-> shortened to > `--check-cfg names()` in order to enable checking well-known condition names.
+// This is UNEXPECTED, because 'windows' is a well known condition name,
+// and because 'windows' doens't take any values,
+// and will cause a compiler warning (by default).
+#[cfg(windows = "unix")]
+fn tame_windows() {}
+```
 
 ### Example: Checking condition names, but not values
 
 ```bash
 # This turns on checking for condition names, but not values, such as 'feature' values.
-rustc --check-cfg 'names(is_embedded, has_feathers)' \
+rustc --check-cfg 'cfg(is_embedded, has_feathers, values(any()))' \
       --cfg has_feathers -Z unstable-options
 ```
 
 ```rust
-#[cfg(is_embedded)]         // This is expected as "is_embedded" was provided in names()
-fn do_embedded() {}
+#[cfg(is_embedded)]         // This is expected as "is_embedded" was provided in cfg()
+fn do_embedded() {}         // and because names exhaustiveness was not disabled
 
-#[cfg(has_feathers)]        // This is expected as "has_feathers" was provided in names()
-fn do_features() {}
+#[cfg(has_feathers)]        // This is expected as "has_feathers" was provided in cfg()
+fn do_features() {}         // and because names exhaustiveness was not disbaled
 
-#[cfg(has_feathers = "zapping")] // This is expected as "has_feathers" was provided in names()
+#[cfg(has_feathers = "zapping")] // This is expected as "has_feathers" was provided in cfg()
                                  // and because no value checking was enable for "has_feathers"
                                  // no warning is emitted for the value "zapping"
 fn do_zapping() {}
 
 #[cfg(has_mumble_frotz)]    // This is UNEXPECTED because names checking is enable and
-                            // "has_mumble_frotz" was not provided in names()
+                            // "has_mumble_frotz" was not provided in cfg()
 fn do_mumble_frotz() {}
 ```
 
@@ -155,25 +139,25 @@ fn do_mumble_frotz() {}
 
 ```bash
 # This turns on checking for feature values, but not for condition names.
-rustc --check-cfg 'values(feature, "zapping", "lasers")' \
+rustc --check-cfg 'configure(feature, values("zapping", "lasers"))' \
+      --check-cfg 'cfg(any())' \
       --cfg 'feature="zapping"' -Z unstable-options
 ```
 
 ```rust
-#[cfg(is_embedded)]         // This is doesn't raise a warning, because names checking was not
-                            // enable (ie not names())
+#[cfg(is_embedded)]         // This is doesn't raise a warning, because names checking was
+                            // disabled by 'cfg(any())'
 fn do_embedded() {}
 
-#[cfg(has_feathers)]        // Same as above, --check-cfg names(...) was never used so no name
+#[cfg(has_feathers)]        // Same as above, 'cfg(any())' was provided so no name
                             // checking is performed
 fn do_features() {}
 
-
-#[cfg(feature = "lasers")]  // This is expected, "lasers" is in the values(feature) list
+#[cfg(feature = "lasers")]  // This is expected, "lasers" is in the cfg(feature) list
 fn shoot_lasers() {}
 
 #[cfg(feature = "monkeys")] // This is UNEXPECTED, because "monkeys" is not in the
-                            // --check-cfg values(feature) list
+                            // cfg(feature) list
 fn write_shakespeare() {}
 ```
 
@@ -181,26 +165,92 @@ fn write_shakespeare() {}
 
 ```bash
 # This turns on checking for feature values and for condition names.
-rustc --check-cfg 'names(is_embedded, has_feathers)' \
-      --check-cfg 'values(feature, "zapping", "lasers")' \
+rustc --check-cfg 'cfg(is_embedded, has_feathers)' \
+      --check-cfg 'cfg(feature, values("zapping", "lasers"))' \
       --cfg has_feathers --cfg 'feature="zapping"' -Z unstable-options
 ```
 
 ```rust
-#[cfg(is_embedded)]         // This is expected because "is_embedded" was provided in names()
-fn do_embedded() {}
+#[cfg(is_embedded)]         // This is expected because "is_embedded" was provided in cfg()
+fn do_embedded() {}         // and doesn't take any value
 
-#[cfg(has_feathers)]        // This is expected because "has_feathers" was provided in names()
-fn do_features() {}
+#[cfg(has_feathers)]        // This is expected because "has_feathers" was provided in cfg()
+fn do_features() {}         // and deosn't take any value
 
-#[cfg(has_mumble_frotz)]    // This is UNEXPECTED, because has_mumble_frotz is not in the
-                            // --check-cfg names(...) list
+#[cfg(has_mumble_frotz)]    // This is UNEXPECTED, because "has_mumble_frotz" was never provided
 fn do_mumble_frotz() {}
 
-#[cfg(feature = "lasers")]  // This is expected, "lasers" is in the values(feature) list
+#[cfg(feature = "lasers")]  // This is expected, "lasers" is in the cfg(feature) list
 fn shoot_lasers() {}
 
 #[cfg(feature = "monkeys")] // This is UNEXPECTED, because "monkeys" is not in
-                            // the values(feature) list
+                            // the cfg(feature) list
 fn write_shakespeare() {}
 ```
+
+## The deprecated `names(...)` form
+
+The `names(...)` form enables checking the names. This form uses a named list:
+
+```bash
+rustc --check-cfg 'names(name1, name2, ... nameN)'
+```
+
+where each `name` is a bare identifier (has no quotes). The order of the names is not significant.
+
+If `--check-cfg names(...)` is specified at least once, then `rustc` will check all references to
+condition names. `rustc` will check every `#[cfg]` attribute, `#[cfg_attr]` attribute, `cfg` clause
+inside `#[link]` attribute and `cfg!(...)` call against the provided list of expected condition
+names. If a name is not present in this list, then `rustc` will report an `unexpected_cfgs` lint
+diagnostic. The default diagnostic level for this lint is `Warn`.
+
+If `--check-cfg names(...)` is not specified, then `rustc` will not check references to condition
+names.
+
+`--check-cfg names(...)` may be specified more than once. The result is that the list of valid
+condition names is merged across all options. It is legal for a condition name to be specified
+more than once; redundantly specifying a condition name has no effect.
+
+To enable checking condition names with an empty set of valid condition names, use the following
+form. The parentheses are required.
+
+```bash
+rustc --check-cfg 'names()'
+```
+
+Note that `--check-cfg 'names()'` is _not_ equivalent to omitting the option entirely.
+The first form enables checking condition names, while specifying that there are no valid
+condition names (outside of the set of well-known names defined by `rustc`). Omitting the
+`--check-cfg 'names(...)'` option does not enable checking condition names.
+
+## The deprecated `values(...)` form
+
+The `values(...)` form enables checking the values within list-valued conditions. It has this
+form:
+
+```bash
+rustc --check-cfg `values(name, "value1", "value2", ... "valueN")'
+```
+
+where `name` is a bare identifier (has no quotes) and each `"value"` term is a quoted literal
+string. `name` specifies the name of the condition, such as `feature` or `target_os`.
+
+When the `values(...)` option is specified, `rustc` will check every `#[cfg(name = "value")]`
+attribute, `#[cfg_attr(name = "value")]` attribute, `#[link(name = "a", cfg(name = "value"))]`
+and `cfg!(name = "value")` call. It will check that the `"value"` specified is present in the
+list of expected values. If `"value"` is not in it, then `rustc` will report an `unexpected_cfgs`
+lint diagnostic. The default diagnostic level for this lint is `Warn`.
+
+To enable checking of values, but to provide an empty set of valid values, use this form:
+
+```bash
+rustc --check-cfg `values(name)`
+```
+
+The `--check-cfg values(...)` option can be repeated, both for the same condition name and for
+different names. If it is repeated for the same condition name, then the sets of values for that
+condition are merged together.
+
+If `values()` is specified, then `rustc` will enable the checking of well-known values defined
+by itself. Note that it's necessary to specify the `values()` form to enable the checking of
+well known values, specifying the other forms doesn't implicitly enable it.
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index 14877385646..5134cf66050 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -701,7 +701,7 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
 
 fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
     if let Some(parent) = get_parent_expr(cx, e)
-        && parent.span.ctxt() == e.span.ctxt()
+        && parent.span.eq_ctxt(e.span)
     {
         match parent.kind {
             ExprKind::Call(child, _) | ExprKind::MethodCall(_, child, _, _) | ExprKind::Index(child, _, _)
diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs
index 6197b5b19eb..70a467dde61 100644
--- a/src/tools/clippy/clippy_lints/src/entry.rs
+++ b/src/tools/clippy/clippy_lints/src/entry.rs
@@ -241,7 +241,7 @@ fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Optio
                 },
             ],
             _,
-        ) if key_span.ctxt() == expr.span.ctxt() => {
+        ) if key_span.eq_ctxt(expr.span) => {
             let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
             let expr = ContainsExpr {
                 negated,
diff --git a/src/tools/clippy/clippy_lints/src/formatting.rs b/src/tools/clippy/clippy_lints/src/formatting.rs
index d03480c2108..4ebf0e9667d 100644
--- a/src/tools/clippy/clippy_lints/src/formatting.rs
+++ b/src/tools/clippy/clippy_lints/src/formatting.rs
@@ -274,7 +274,7 @@ fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
         for element in array {
             if_chain! {
                 if let ExprKind::Binary(ref op, ref lhs, _) = element.kind;
-                if has_unary_equivalent(op.node) && lhs.span.ctxt() == op.span.ctxt();
+                if has_unary_equivalent(op.node) && lhs.span.eq_ctxt(op.span);
                 let space_span = lhs.span.between(op.span);
                 if let Some(space_snippet) = snippet_opt(cx, space_span);
                 let lint_span = lhs.span.with_lo(lhs.span.hi());
diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
index 4e9d77ea156..79d728a021c 100644
--- a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
@@ -31,7 +31,7 @@ impl LateLintPass<'_> for UnderscoreTyped {
             if !in_external_macro(cx.tcx.sess, local.span);
             if let Some(ty) = local.ty; // Ensure that it has a type defined
             if let TyKind::Infer = &ty.kind; // that type is '_'
-            if local.span.ctxt() == ty.span.ctxt();
+            if local.span.eq_ctxt(ty.span);
             then {
                 // NOTE: Using `is_from_proc_macro` on `init` will require that it's initialized,
                 // this doesn't. Alternatively, `WithSearchPat` can be implemented for `Ty`
diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs
index 2117308cd40..86bbdb4ea19 100644
--- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs
@@ -59,7 +59,7 @@ impl<'tcx> QuestionMark {
             let Some(init) = local.init &&
             local.els.is_none() &&
             local.ty.is_none() &&
-            init.span.ctxt() == stmt.span.ctxt() &&
+            init.span.eq_ctxt(stmt.span) &&
             let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init)
         {
             match if_let_or_match {
diff --git a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs
index 33a052c41a3..29b935fb61a 100644
--- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs
@@ -57,7 +57,7 @@ fn check_arm<'tcx>(
                 }
             },
         };
-        if outer_pat.span.ctxt() == inner_scrutinee.span.ctxt();
+        if outer_pat.span.eq_ctxt(inner_scrutinee.span);
         // match expression must be a local binding
         // match <local> { .. }
         if let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee));
diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
index 6b611f567ae..781ee138c76 100644
--- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
@@ -119,7 +119,7 @@ where
     // it's being passed by value.
     let scrutinee = peel_hir_expr_refs(scrutinee).0;
     let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
-    let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX {
+    let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence().order() < PREC_POSTFIX {
         format!("({scrutinee_str})")
     } else {
         scrutinee_str.into()
@@ -130,7 +130,7 @@ where
         if_chain! {
             if !some_expr.needs_unsafe_block;
             if let Some(func) = can_pass_as_func(cx, id, some_expr.expr);
-            if func.span.ctxt() == some_expr.expr.span.ctxt();
+            if func.span.eq_ctxt(some_expr.expr.span);
             then {
                 snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
             } else {
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
index 5464e455dea..e70a1bc9879 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
@@ -56,7 +56,7 @@ pub(super) fn check<'tcx>(
         // lint, with note if neither arg is > 1 line and both map() and
         // unwrap_or_else() have the same span
         let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
-        let same_span = map_arg.span.ctxt() == unwrap_arg.span.ctxt();
+        let same_span = map_arg.span.eq_ctxt(unwrap_arg.span);
         if same_span && !multiline {
             let var_snippet = snippet(cx, recv.span, "..");
             span_lint_and_sugg(
diff --git a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
index 7b0f7eaf1f0..0e834fb3ac7 100644
--- a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
@@ -125,7 +125,7 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
         if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar(_)) = &arg.kind;
         if let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind;
         if let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, ..)) = &called.kind;
-        if expr.span.ctxt() == inner_expr.span.ctxt();
+        if expr.span.eq_ctxt(inner_expr.span);
         let expr_ty = cx.typeck_results().expr_ty(expr);
         let inner_ty = cx.typeck_results().expr_ty(inner_expr);
         if expr_ty == inner_ty;
diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
index d47728f190a..e94e4589966 100644
--- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
+++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
@@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
                         || (path.ident.name == sym!(set_mode)
                             && cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did()));
                     if let ExprKind::Lit(_) = param.kind;
-                    if param.span.ctxt() == expr.span.ctxt();
+                    if param.span.eq_ctxt(expr.span);
 
                     then {
                         let Some(snip) = snippet_opt(cx, param.span) else {
@@ -70,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
                     if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id();
                     if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE);
                     if let ExprKind::Lit(_) = param.kind;
-                    if param.span.ctxt() == expr.span.ctxt();
+                    if param.span.eq_ctxt(expr.span);
                     if let Some(snip) = snippet_opt(cx, param.span);
                     if !snip.starts_with("0o");
                     then {
diff --git a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
index 534b2762ba7..8193057a6eb 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
@@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantAsyncBlock {
             let Some(body_expr) = desugar_async_block(cx, expr) &&
             let Some(expr) = desugar_await(peel_blocks(body_expr)) &&
             // The await prefix must not come from a macro as its content could change in the future.
-            expr.span.ctxt() == body_expr.span.ctxt() &&
+            expr.span.eq_ctxt(body_expr.span) &&
             // An async block does not have immediate side-effects from a `.await` point-of-view.
             (!expr.can_have_side_effects() || desugar_async_block(cx, expr).is_some()) &&
             let Some(shortened_span) = walk_span_to_context(expr.span, span.ctxt())
diff --git a/src/tools/clippy/clippy_lints/src/reference.rs b/src/tools/clippy/clippy_lints/src/reference.rs
index db870ec4c5b..12da29f1108 100644
--- a/src/tools/clippy/clippy_lints/src/reference.rs
+++ b/src/tools/clippy/clippy_lints/src/reference.rs
@@ -50,7 +50,7 @@ impl EarlyLintPass for DerefAddrOf {
         if_chain! {
             if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind;
             if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind;
-            if deref_target.span.ctxt() == e.span.ctxt();
+            if deref_target.span.eq_ctxt(e.span);
             if !addrof_target.span.from_expansion();
             then {
                 let mut applicability = Applicability::MachineApplicable;
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs b/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs
index 8e156b8829b..39cd289b67a 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs
@@ -33,7 +33,7 @@ impl LateLintPass<'_> for ConfusingXorAndPow {
         if !in_external_macro(cx.sess(), expr.span)
             && let ExprKind::Binary(op, left, right) = &expr.kind
             && op.node == BinOpKind::BitXor
-            && left.span.ctxt() == right.span.ctxt()
+            && left.span.eq_ctxt(right.span)
             && let ExprKind::Lit(lit_left) = &left.kind
             && let ExprKind::Lit(lit_right) = &right.kind
             && matches!(lit_right.node, LitKind::Int(..) | LitKind::Float(..))
diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs
index eaf590f6ad7..46ce4ffdce5 100644
--- a/src/tools/clippy/clippy_utils/src/macros.rs
+++ b/src/tools/clippy/clippy_utils/src/macros.rs
@@ -245,7 +245,7 @@ impl<'a> PanicExpn<'a> {
             return None;
         };
         let result = match name {
-            "panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty,
+            "panic" if arg.span.eq_ctxt(expr.span) => Self::Empty,
             "panic" | "panic_str" => Self::Str(arg),
             "panic_display" | "panic_cold_display" => {
                 let ExprKind::AddrOf(_, _, e) = &arg.kind else {
diff --git a/tests/rustdoc-ui/check-cfg/check-cfg-unstable.rs b/tests/rustdoc-ui/check-cfg/check-cfg-unstable.rs
index 5c500ce6ce0..806b6d1253d 100644
--- a/tests/rustdoc-ui/check-cfg/check-cfg-unstable.rs
+++ b/tests/rustdoc-ui/check-cfg/check-cfg-unstable.rs
@@ -1,2 +1,2 @@
 // check-fail
-// compile-flags: --check-cfg=names()
+// compile-flags: --check-cfg=cfg()
diff --git a/tests/rustdoc-ui/check-cfg/check-cfg.rs b/tests/rustdoc-ui/check-cfg/check-cfg.rs
index fa8789ad3ed..96fa9e08dde 100644
--- a/tests/rustdoc-ui/check-cfg/check-cfg.rs
+++ b/tests/rustdoc-ui/check-cfg/check-cfg.rs
@@ -1,5 +1,5 @@
 // check-pass
-// compile-flags: --check-cfg=names() -Z unstable-options
+// compile-flags: --check-cfg=cfg() -Z unstable-options
 
 /// uniz is nor a builtin nor pass as arguments so is unexpected
 #[cfg(uniz)]
diff --git a/tests/rustdoc-ui/doctest/check-cfg-test.rs b/tests/rustdoc-ui/doctest/check-cfg-test.rs
index 49a801c3fb3..38cd59aa790 100644
--- a/tests/rustdoc-ui/doctest/check-cfg-test.rs
+++ b/tests/rustdoc-ui/doctest/check-cfg-test.rs
@@ -1,5 +1,5 @@
 // check-pass
-// compile-flags: --test --nocapture --check-cfg=values(feature,"test") -Z unstable-options
+// compile-flags: --test --nocapture --check-cfg=cfg(feature,values("test")) -Z unstable-options
 // normalize-stderr-test: "tests/rustdoc-ui/doctest" -> "$$DIR"
 // normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR"
 // normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME"
diff --git a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs
new file mode 100644
index 00000000000..39980ee7c67
--- /dev/null
+++ b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs
@@ -0,0 +1,13 @@
+// Test the `rustc::span_use_eq_ctxt` internal lint
+// compile-flags: -Z unstable-options
+
+#![feature(rustc_private)]
+#![deny(rustc::span_use_eq_ctxt)]
+#![crate_type = "lib"]
+
+extern crate rustc_span;
+use rustc_span::Span;
+
+pub fn f(s: Span, t: Span) -> bool {
+    s.ctxt() == t.ctxt() //~ ERROR use `.eq_ctxt()` instead of `.ctxt() == .ctxt()`
+}
diff --git a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr
new file mode 100644
index 00000000000..b33f6212545
--- /dev/null
+++ b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr
@@ -0,0 +1,14 @@
+error: use `.eq_ctxt()` instead of `.ctxt() == .ctxt()`
+  --> $DIR/span_use_eq_ctxt.rs:12:5
+   |
+LL |     s.ctxt() == t.ctxt()
+   |     ^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/span_use_eq_ctxt.rs:5:9
+   |
+LL | #![deny(rustc::span_use_eq_ctxt)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui-fulldeps/stable-mir/instance.rs b/tests/ui-fulldeps/stable-mir/instance.rs
new file mode 100644
index 00000000000..fe06d9b5cc9
--- /dev/null
+++ b/tests/ui-fulldeps/stable-mir/instance.rs
@@ -0,0 +1,91 @@
+// run-pass
+// Test that users are able to use stable mir APIs to retrieve monomorphized instances
+
+// ignore-stage1
+// ignore-cross-compile
+// ignore-remote
+// edition: 2021
+
+#![feature(rustc_private)]
+#![feature(assert_matches)]
+#![feature(control_flow_enum)]
+
+extern crate rustc_middle;
+extern crate rustc_smir;
+extern crate stable_mir;
+
+use rustc_middle::ty::TyCtxt;
+
+use stable_mir::*;
+use rustc_smir::rustc_internal;
+use std::io::Write;
+use std::ops::ControlFlow;
+
+const CRATE_NAME: &str = "input";
+
+/// This function uses the Stable MIR APIs to get information about the test crate.
+fn test_stable_mir(_tcx: TyCtxt<'_>) -> ControlFlow<()> {
+    let items = stable_mir::all_local_items();
+
+    // Get all items and split generic vs monomorphic items.
+    let (generic, mono) : (Vec<_>, Vec<_>) = items.into_iter().partition(|item| {
+        item.requires_monomorphization()
+    });
+    assert_eq!(mono.len(), 3, "Expected 2 mono functions and one constant");
+    assert_eq!(generic.len(), 2, "Expected 2 generic functions");
+
+    // For all monomorphic items, get the correspondent instances.
+    let instances = mono.iter().filter_map(|item| {
+        mir::mono::Instance::try_from(*item).ok()
+    }).collect::<Vec<mir::mono::Instance>>();
+    assert_eq!(instances.len(), mono.len());
+
+    // For all generic items, try_from should fail.
+    assert!(generic.iter().all(|item| mir::mono::Instance::try_from(*item).is_err()));
+
+    ControlFlow::Continue(())
+}
+
+
+/// This test will generate and analyze a dummy crate using the stable mir.
+/// For that, it will first write the dummy crate into a file.
+/// Then it will create a `StableMir` using custom arguments and then
+/// it will run the compiler.
+fn main() {
+    let path = "instance_input.rs";
+    generate_input(&path).unwrap();
+    let args = vec![
+        "rustc".to_string(),
+        "--crate-type=lib".to_string(),
+        "--crate-name".to_string(),
+        CRATE_NAME.to_string(),
+        path.to_string(),
+    ];
+    rustc_internal::StableMir::new(args, test_stable_mir).run().unwrap();
+}
+
+fn generate_input(path: &str) -> std::io::Result<()> {
+    let mut file = std::fs::File::create(path)?;
+    write!(
+        file,
+        r#"
+    pub fn ty_param<T>(t: &T) -> T where T: Clone {{
+        t.clone()
+    }}
+
+    pub fn const_param<const LEN: usize>(a: [bool; LEN]) -> bool {{
+        LEN > 0 && a[0]
+    }}
+
+    pub fn monomorphic() {{
+    }}
+
+    pub mod foo {{
+        pub fn bar_mono(i: i32) -> i64 {{
+            i as i64
+        }}
+    }}
+    "#
+    )?;
+    Ok(())
+}
diff --git a/tests/ui/check-cfg/allow-at-crate-level.rs b/tests/ui/check-cfg/allow-at-crate-level.rs
index ce3383a2961..1629d2e0b67 100644
--- a/tests/ui/check-cfg/allow-at-crate-level.rs
+++ b/tests/ui/check-cfg/allow-at-crate-level.rs
@@ -1,7 +1,7 @@
 // This test check that #![allow(unexpected_cfgs)] works with --cfg
 //
 // check-pass
-// compile-flags: --cfg=unexpected --check-cfg=names() -Z unstable-options
+// compile-flags: --cfg=unexpected --check-cfg=cfg() -Z unstable-options
 
 #![allow(unexpected_cfgs)]
 
diff --git a/tests/ui/check-cfg/allow-macro-cfg.rs b/tests/ui/check-cfg/allow-macro-cfg.rs
index 8016a4d190c..ea26355aca8 100644
--- a/tests/ui/check-cfg/allow-macro-cfg.rs
+++ b/tests/ui/check-cfg/allow-macro-cfg.rs
@@ -1,7 +1,7 @@
 // This test check that local #[allow(unexpected_cfgs)] works
 //
 // check-pass
-// compile-flags:--check-cfg=names() -Z unstable-options
+// compile-flags: --check-cfg=cfg() -Z unstable-options
 
 #[allow(unexpected_cfgs)]
 fn foo() {
diff --git a/tests/ui/check-cfg/allow-same-level.rs b/tests/ui/check-cfg/allow-same-level.rs
index 6c869dc4202..29491e0b39e 100644
--- a/tests/ui/check-cfg/allow-same-level.rs
+++ b/tests/ui/check-cfg/allow-same-level.rs
@@ -1,7 +1,7 @@
 // This test check that #[allow(unexpected_cfgs)] doesn't work if put on the same level
 //
 // check-pass
-// compile-flags:--check-cfg=names() -Z unstable-options
+// compile-flags: --check-cfg=cfg() -Z unstable-options
 
 #[allow(unexpected_cfgs)]
 #[cfg(FALSE)]
diff --git a/tests/ui/check-cfg/allow-top-level.rs b/tests/ui/check-cfg/allow-top-level.rs
index d14b0eae5cc..df06f655d9a 100644
--- a/tests/ui/check-cfg/allow-top-level.rs
+++ b/tests/ui/check-cfg/allow-top-level.rs
@@ -1,7 +1,7 @@
 // This test check that a top-level #![allow(unexpected_cfgs)] works
 //
 // check-pass
-// compile-flags:--check-cfg=names() -Z unstable-options
+// compile-flags: --check-cfg=cfg() -Z unstable-options
 
 #![allow(unexpected_cfgs)]
 
diff --git a/tests/ui/check-cfg/allow-upper-level.rs b/tests/ui/check-cfg/allow-upper-level.rs
index 04340694d9c..bd5c97815f2 100644
--- a/tests/ui/check-cfg/allow-upper-level.rs
+++ b/tests/ui/check-cfg/allow-upper-level.rs
@@ -1,7 +1,7 @@
 // This test check that #[allow(unexpected_cfgs)] work if put on an upper level
 //
 // check-pass
-// compile-flags:--check-cfg=names() -Z unstable-options
+// compile-flags: --check-cfg=cfg() -Z unstable-options
 
 #[allow(unexpected_cfgs)]
 mod aa {
diff --git a/tests/ui/check-cfg/compact-names.rs b/tests/ui/check-cfg/compact-names.rs
index bff80740039..4f7168255cf 100644
--- a/tests/ui/check-cfg/compact-names.rs
+++ b/tests/ui/check-cfg/compact-names.rs
@@ -1,7 +1,7 @@
 // This test check that we correctly emit an warning for compact cfg
 //
 // check-pass
-// compile-flags:--check-cfg=names() -Z unstable-options
+// compile-flags: --check-cfg=cfg() -Z unstable-options
 
 #![feature(cfg_target_compact)]
 
diff --git a/tests/ui/check-cfg/compact-values.rs b/tests/ui/check-cfg/compact-values.rs
index 1f17057840c..13c072fe920 100644
--- a/tests/ui/check-cfg/compact-values.rs
+++ b/tests/ui/check-cfg/compact-values.rs
@@ -1,7 +1,7 @@
 // This test check that we correctly emit an warning for compact cfg
 //
 // check-pass
-// compile-flags:--check-cfg=values() -Z unstable-options
+// compile-flags: --check-cfg=cfg() -Z unstable-options
 
 #![feature(cfg_target_compact)]
 
diff --git a/tests/ui/check-cfg/concat-values.rs b/tests/ui/check-cfg/concat-values.rs
new file mode 100644
index 00000000000..0f9178ce6a5
--- /dev/null
+++ b/tests/ui/check-cfg/concat-values.rs
@@ -0,0 +1,13 @@
+// check-pass
+// compile-flags: -Z unstable-options
+// compile-flags: --check-cfg=cfg(my_cfg,values("foo")) --check-cfg=cfg(my_cfg,values("bar"))
+
+#[cfg(my_cfg)]
+//~^ WARNING unexpected `cfg` condition value
+fn my_cfg() {}
+
+#[cfg(my_cfg = "unk")]
+//~^ WARNING unexpected `cfg` condition value
+fn my_cfg() {}
+
+fn main() {}
diff --git a/tests/ui/check-cfg/concat-values.stderr b/tests/ui/check-cfg/concat-values.stderr
new file mode 100644
index 00000000000..da2bd7d6ad9
--- /dev/null
+++ b/tests/ui/check-cfg/concat-values.stderr
@@ -0,0 +1,19 @@
+warning: unexpected `cfg` condition value: (none)
+  --> $DIR/concat-values.rs:5:7
+   |
+LL | #[cfg(my_cfg)]
+   |       ^^^^^^
+   |
+   = note: expected values for `my_cfg` are: `bar`, `foo`
+   = note: `#[warn(unexpected_cfgs)]` on by default
+
+warning: unexpected `cfg` condition value: `unk`
+  --> $DIR/concat-values.rs:9:7
+   |
+LL | #[cfg(my_cfg = "unk")]
+   |       ^^^^^^^^^^^^^^
+   |
+   = note: expected values for `my_cfg` are: `bar`, `foo`
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/check-cfg/diagnotics.rs b/tests/ui/check-cfg/diagnotics.rs
index 49e127d079a..45875bddc17 100644
--- a/tests/ui/check-cfg/diagnotics.rs
+++ b/tests/ui/check-cfg/diagnotics.rs
@@ -1,5 +1,5 @@
 // check-pass
-// compile-flags: --check-cfg=names() --check-cfg=values(feature,"foo") --check-cfg=values(no_values) -Z unstable-options
+// compile-flags: --check-cfg=cfg(feature,values("foo")) --check-cfg=cfg(no_values) -Z unstable-options
 
 #[cfg(featur)]
 //~^ WARNING unexpected `cfg` condition name
diff --git a/tests/ui/check-cfg/empty-names.rs b/tests/ui/check-cfg/empty-names.rs
deleted file mode 100644
index 046ff0364e2..00000000000
--- a/tests/ui/check-cfg/empty-names.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-// Check warning for unexpected cfg
-//
-// check-pass
-// compile-flags: --check-cfg=names() -Z unstable-options
-
-#[cfg(unknown_key = "value")]
-//~^ WARNING unexpected `cfg` condition name
-pub fn f() {}
-
-fn main() {}
diff --git a/tests/ui/check-cfg/empty-values.rs b/tests/ui/check-cfg/empty-values.rs
deleted file mode 100644
index 9bda42e5d15..00000000000
--- a/tests/ui/check-cfg/empty-values.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-// Check warning for unexpected cfg value
-//
-// check-pass
-// compile-flags: --check-cfg=values() -Z unstable-options
-
-#[cfg(test = "value")]
-//~^ WARNING unexpected `cfg` condition value
-pub fn f() {}
-
-fn main() {}
diff --git a/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr b/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr
new file mode 100644
index 00000000000..53ccc0f4d31
--- /dev/null
+++ b/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr
@@ -0,0 +1,25 @@
+warning: unexpected `cfg` condition name: `unknown_key`
+  --> $DIR/exhaustive-names-values.rs:12:7
+   |
+LL | #[cfg(unknown_key = "value")]
+   |       ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expected names are: `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
+   = note: `#[warn(unexpected_cfgs)]` on by default
+
+warning: unexpected `cfg` condition value: `value`
+  --> $DIR/exhaustive-names-values.rs:16:7
+   |
+LL | #[cfg(test = "value")]
+   |       ^^^^----------
+   |           |
+   |           help: remove the value
+   |
+   = note: no expected value for `test`
+
+warning: unexpected `empty_cfg` as condition name
+   |
+   = help: was set with `--cfg` but isn't in the `--check-cfg` expected names
+
+warning: 3 warnings emitted
+
diff --git a/tests/ui/check-cfg/exhaustive-names-values.empty_names_values.stderr b/tests/ui/check-cfg/exhaustive-names-values.empty_names_values.stderr
new file mode 100644
index 00000000000..5e8b74054ce
--- /dev/null
+++ b/tests/ui/check-cfg/exhaustive-names-values.empty_names_values.stderr
@@ -0,0 +1,25 @@
+warning: unexpected `cfg` condition name: `unknown_key`
+  --> $DIR/exhaustive-names-values.rs:12:7
+   |
+LL | #[cfg(unknown_key = "value")]
+   |       ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expected names are: `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
+   = note: `#[warn(unexpected_cfgs)]` on by default
+
+warning: unexpected `cfg` condition value: `value`
+  --> $DIR/exhaustive-names-values.rs:16:7
+   |
+LL | #[cfg(test = "value")]
+   |       ^^^^----------
+   |           |
+   |           help: remove the value
+   |
+   = note: no expected value for `test`
+
+warning: unexpected `empty_names_values` as condition name
+   |
+   = help: was set with `--cfg` but isn't in the `--check-cfg` expected names
+
+warning: 3 warnings emitted
+
diff --git a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr
new file mode 100644
index 00000000000..7705a665eb7
--- /dev/null
+++ b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr
@@ -0,0 +1,33 @@
+warning: unexpected `cfg` condition name: `unknown_key`
+  --> $DIR/exhaustive-names-values.rs:12:7
+   |
+LL | #[cfg(unknown_key = "value")]
+   |       ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expected names are: `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
+   = note: `#[warn(unexpected_cfgs)]` on by default
+
+warning: unexpected `cfg` condition value: `value`
+  --> $DIR/exhaustive-names-values.rs:16:7
+   |
+LL | #[cfg(test = "value")]
+   |       ^^^^----------
+   |           |
+   |           help: remove the value
+   |
+   = note: no expected value for `test`
+
+warning: unexpected `cfg` condition value: `unk`
+  --> $DIR/exhaustive-names-values.rs:20:7
+   |
+LL | #[cfg(feature = "unk")]
+   |       ^^^^^^^^^^^^^^^
+   |
+   = note: expected values for `feature` are: `std`
+
+warning: unexpected condition value `` for condition name `feature`
+   |
+   = help: was set with `--cfg` but isn't in the `--check-cfg` expected values
+
+warning: 4 warnings emitted
+
diff --git a/tests/ui/check-cfg/exhaustive-names-values.full.stderr b/tests/ui/check-cfg/exhaustive-names-values.full.stderr
new file mode 100644
index 00000000000..f0224a2e33c
--- /dev/null
+++ b/tests/ui/check-cfg/exhaustive-names-values.full.stderr
@@ -0,0 +1,33 @@
+warning: unexpected `cfg` condition name: `unknown_key`
+  --> $DIR/exhaustive-names-values.rs:12:7
+   |
+LL | #[cfg(unknown_key = "value")]
+   |       ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expected names are: `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
+   = note: `#[warn(unexpected_cfgs)]` on by default
+
+warning: unexpected `cfg` condition value: `value`
+  --> $DIR/exhaustive-names-values.rs:16:7
+   |
+LL | #[cfg(test = "value")]
+   |       ^^^^----------
+   |           |
+   |           help: remove the value
+   |
+   = note: no expected value for `test`
+
+warning: unexpected `cfg` condition value: `unk`
+  --> $DIR/exhaustive-names-values.rs:20:7
+   |
+LL | #[cfg(feature = "unk")]
+   |       ^^^^^^^^^^^^^^^
+   |
+   = note: expected values for `feature` are: `std`
+
+warning: unexpected `full` as condition name
+   |
+   = help: was set with `--cfg` but isn't in the `--check-cfg` expected names
+
+warning: 4 warnings emitted
+
diff --git a/tests/ui/check-cfg/exhaustive-names-values.rs b/tests/ui/check-cfg/exhaustive-names-values.rs
new file mode 100644
index 00000000000..f553d93cae2
--- /dev/null
+++ b/tests/ui/check-cfg/exhaustive-names-values.rs
@@ -0,0 +1,34 @@
+// Check warning for unexpected cfg in the code and in the CLI
+// arguments (here the revision cfg).
+//
+// check-pass
+// revisions: empty_names_values empty_cfg feature full
+// compile-flags: -Z unstable-options
+// [empty_names_values]compile-flags: --check-cfg=names() --check-cfg=values()
+// [empty_cfg]compile-flags: --check-cfg=cfg()
+// [feature]compile-flags: --check-cfg=cfg(feature,values("std"))
+// [full]compile-flags: --check-cfg=cfg(feature,values("std")) --check-cfg=cfg()
+
+#[cfg(unknown_key = "value")]
+//~^ WARNING unexpected `cfg` condition name
+pub fn f() {}
+
+#[cfg(test = "value")]
+//~^ WARNING unexpected `cfg` condition value
+pub fn f() {}
+
+#[cfg(feature = "unk")]
+//[feature]~^ WARNING unexpected `cfg` condition value
+//[full]~^^ WARNING unexpected `cfg` condition value
+pub fn feat() {}
+
+#[cfg(feature = "std")]
+pub fn feat() {}
+
+#[cfg(windows)]
+pub fn win() {}
+
+#[cfg(unix)]
+pub fn unix() {}
+
+fn main() {}
diff --git a/tests/ui/check-cfg/empty-names.stderr b/tests/ui/check-cfg/exhaustive-names.empty_names.stderr
index 9bba852c6de..6190ff71464 100644
--- a/tests/ui/check-cfg/empty-names.stderr
+++ b/tests/ui/check-cfg/exhaustive-names.empty_names.stderr
@@ -1,5 +1,5 @@
 warning: unexpected `cfg` condition name: `unknown_key`
-  --> $DIR/empty-names.rs:6:7
+  --> $DIR/exhaustive-names.rs:8:7
    |
 LL | #[cfg(unknown_key = "value")]
    |       ^^^^^^^^^^^^^^^^^^^^^
@@ -7,5 +7,9 @@ LL | #[cfg(unknown_key = "value")]
    = help: expected names are: `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
    = note: `#[warn(unexpected_cfgs)]` on by default
 
-warning: 1 warning emitted
+warning: unexpected `empty_names` as condition name
+   |
+   = help: was set with `--cfg` but isn't in the `--check-cfg` expected names
+
+warning: 2 warnings emitted
 
diff --git a/tests/ui/check-cfg/exhaustive-names.exhaustive_names.stderr b/tests/ui/check-cfg/exhaustive-names.exhaustive_names.stderr
new file mode 100644
index 00000000000..f338434cd29
--- /dev/null
+++ b/tests/ui/check-cfg/exhaustive-names.exhaustive_names.stderr
@@ -0,0 +1,15 @@
+warning: unexpected `cfg` condition name: `unknown_key`
+  --> $DIR/exhaustive-names.rs:8:7
+   |
+LL | #[cfg(unknown_key = "value")]
+   |       ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expected names are: `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
+   = note: `#[warn(unexpected_cfgs)]` on by default
+
+warning: unexpected `exhaustive_names` as condition name
+   |
+   = help: was set with `--cfg` but isn't in the `--check-cfg` expected names
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/check-cfg/exhaustive-names.rs b/tests/ui/check-cfg/exhaustive-names.rs
new file mode 100644
index 00000000000..b86a7f84eb4
--- /dev/null
+++ b/tests/ui/check-cfg/exhaustive-names.rs
@@ -0,0 +1,12 @@
+// Check warning for unexpected cfg
+//
+// check-pass
+// revisions: empty_names exhaustive_names
+// [empty_names]compile-flags: --check-cfg=names() -Z unstable-options
+// [exhaustive_names]compile-flags: --check-cfg=cfg() -Z unstable-options
+
+#[cfg(unknown_key = "value")]
+//~^ WARNING unexpected `cfg` condition name
+pub fn f() {}
+
+fn main() {}
diff --git a/tests/ui/check-cfg/exhaustive-values.empty_cfg.stderr b/tests/ui/check-cfg/exhaustive-values.empty_cfg.stderr
new file mode 100644
index 00000000000..999b2702849
--- /dev/null
+++ b/tests/ui/check-cfg/exhaustive-values.empty_cfg.stderr
@@ -0,0 +1,17 @@
+warning: unexpected `cfg` condition value: `value`
+  --> $DIR/exhaustive-values.rs:9:7
+   |
+LL | #[cfg(test = "value")]
+   |       ^^^^----------
+   |           |
+   |           help: remove the value
+   |
+   = note: no expected value for `test`
+   = note: `#[warn(unexpected_cfgs)]` on by default
+
+warning: unexpected `empty_cfg` as condition name
+   |
+   = help: was set with `--cfg` but isn't in the `--check-cfg` expected names
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/check-cfg/empty-values.stderr b/tests/ui/check-cfg/exhaustive-values.empty_values.stderr
index 932651c5bfe..77ddc35100a 100644
--- a/tests/ui/check-cfg/empty-values.stderr
+++ b/tests/ui/check-cfg/exhaustive-values.empty_values.stderr
@@ -1,5 +1,5 @@
 warning: unexpected `cfg` condition value: `value`
-  --> $DIR/empty-values.rs:6:7
+  --> $DIR/exhaustive-values.rs:9:7
    |
 LL | #[cfg(test = "value")]
    |       ^^^^----------
diff --git a/tests/ui/check-cfg/exhaustive-values.rs b/tests/ui/check-cfg/exhaustive-values.rs
new file mode 100644
index 00000000000..8a1689ba86b
--- /dev/null
+++ b/tests/ui/check-cfg/exhaustive-values.rs
@@ -0,0 +1,13 @@
+// Check warning for unexpected cfg value
+//
+// check-pass
+// revisions: empty_values empty_cfg without_names
+// [empty_values]compile-flags: --check-cfg=values() -Z unstable-options
+// [empty_cfg]compile-flags: --check-cfg=cfg() -Z unstable-options
+// [without_names]compile-flags: --check-cfg=cfg(any()) -Z unstable-options
+
+#[cfg(test = "value")]
+//~^ WARNING unexpected `cfg` condition value
+pub fn f() {}
+
+fn main() {}
diff --git a/tests/ui/check-cfg/exhaustive-values.without_names.stderr b/tests/ui/check-cfg/exhaustive-values.without_names.stderr
new file mode 100644
index 00000000000..77ddc35100a
--- /dev/null
+++ b/tests/ui/check-cfg/exhaustive-values.without_names.stderr
@@ -0,0 +1,13 @@
+warning: unexpected `cfg` condition value: `value`
+  --> $DIR/exhaustive-values.rs:9:7
+   |
+LL | #[cfg(test = "value")]
+   |       ^^^^----------
+   |           |
+   |           help: remove the value
+   |
+   = note: no expected value for `test`
+   = note: `#[warn(unexpected_cfgs)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/check-cfg/invalid-arguments.anything_else.stderr b/tests/ui/check-cfg/invalid-arguments.anything_else.stderr
index 850924d993a..925664bb3fc 100644
--- a/tests/ui/check-cfg/invalid-arguments.anything_else.stderr
+++ b/tests/ui/check-cfg/invalid-arguments.anything_else.stderr
@@ -1,2 +1,2 @@
-error: invalid `--check-cfg` argument: `anything_else(...)` (expected `names(name1, name2, ... nameN)` or `values(name, "value1", "value2", ... "valueN")`)
+error: invalid `--check-cfg` argument: `anything_else(...)` (expected `cfg(name, values("value1", "value2", ... "valueN"))`)
 
diff --git a/tests/ui/check-cfg/invalid-arguments.giberich.stderr b/tests/ui/check-cfg/invalid-arguments.giberich.stderr
new file mode 100644
index 00000000000..d427033fcc2
--- /dev/null
+++ b/tests/ui/check-cfg/invalid-arguments.giberich.stderr
@@ -0,0 +1,2 @@
+error: invalid `--check-cfg` argument: `cfg(...)` (expected `cfg(name, values("value1", "value2", ... "valueN"))`)
+
diff --git a/tests/ui/check-cfg/invalid-arguments.ident_in_values_1.stderr b/tests/ui/check-cfg/invalid-arguments.ident_in_values_1.stderr
new file mode 100644
index 00000000000..0dc44d9ac76
--- /dev/null
+++ b/tests/ui/check-cfg/invalid-arguments.ident_in_values_1.stderr
@@ -0,0 +1,2 @@
+error: invalid `--check-cfg` argument: `cfg(foo,values(bar))` (`values()` arguments must be string literals or `any()`)
+
diff --git a/tests/ui/check-cfg/invalid-arguments.ident_in_values_2.stderr b/tests/ui/check-cfg/invalid-arguments.ident_in_values_2.stderr
new file mode 100644
index 00000000000..d0a1453e3c4
--- /dev/null
+++ b/tests/ui/check-cfg/invalid-arguments.ident_in_values_2.stderr
@@ -0,0 +1,2 @@
+error: invalid `--check-cfg` argument: `cfg(foo,values("bar",bar,"bar"))` (`values()` arguments must be string literals or `any()`)
+
diff --git a/tests/ui/check-cfg/invalid-arguments.mixed_any.stderr b/tests/ui/check-cfg/invalid-arguments.mixed_any.stderr
new file mode 100644
index 00000000000..9239f8cce94
--- /dev/null
+++ b/tests/ui/check-cfg/invalid-arguments.mixed_any.stderr
@@ -0,0 +1,2 @@
+error: invalid `--check-cfg` argument: `cfg(any(),values(any()))` (`values()` cannot be specified before the names)
+
diff --git a/tests/ui/check-cfg/invalid-arguments.mixed_values_any.stderr b/tests/ui/check-cfg/invalid-arguments.mixed_values_any.stderr
new file mode 100644
index 00000000000..4c406143d08
--- /dev/null
+++ b/tests/ui/check-cfg/invalid-arguments.mixed_values_any.stderr
@@ -0,0 +1,2 @@
+error: invalid `--check-cfg` argument: `cfg(foo,values("bar",any()))` (`values()` arguments cannot specify string literals and `any()` at the same time)
+
diff --git a/tests/ui/check-cfg/invalid-arguments.multiple_any.stderr b/tests/ui/check-cfg/invalid-arguments.multiple_any.stderr
new file mode 100644
index 00000000000..6f1db1b13c3
--- /dev/null
+++ b/tests/ui/check-cfg/invalid-arguments.multiple_any.stderr
@@ -0,0 +1,2 @@
+error: invalid `--check-cfg` argument: `cfg(any(),any())` (`any()` cannot be specified multiple times)
+
diff --git a/tests/ui/check-cfg/invalid-arguments.multiple_values.stderr b/tests/ui/check-cfg/invalid-arguments.multiple_values.stderr
new file mode 100644
index 00000000000..bce305b09c3
--- /dev/null
+++ b/tests/ui/check-cfg/invalid-arguments.multiple_values.stderr
@@ -0,0 +1,2 @@
+error: invalid `--check-cfg` argument: `cfg(foo,values(),values())` (`values()` cannot be specified multiple times)
+
diff --git a/tests/ui/check-cfg/invalid-arguments.multiple_values_any.stderr b/tests/ui/check-cfg/invalid-arguments.multiple_values_any.stderr
new file mode 100644
index 00000000000..748ce231af7
--- /dev/null
+++ b/tests/ui/check-cfg/invalid-arguments.multiple_values_any.stderr
@@ -0,0 +1,2 @@
+error: invalid `--check-cfg` argument: `cfg(foo,values(any(),any()))` (`any()` in `values()` cannot be specified multiple times)
+
diff --git a/tests/ui/check-cfg/invalid-arguments.not_empty_any.stderr b/tests/ui/check-cfg/invalid-arguments.not_empty_any.stderr
new file mode 100644
index 00000000000..daf38147fe5
--- /dev/null
+++ b/tests/ui/check-cfg/invalid-arguments.not_empty_any.stderr
@@ -0,0 +1,2 @@
+error: invalid `--check-cfg` argument: `cfg(any(foo))` (`any()` must be empty)
+
diff --git a/tests/ui/check-cfg/invalid-arguments.not_empty_values_any.stderr b/tests/ui/check-cfg/invalid-arguments.not_empty_values_any.stderr
new file mode 100644
index 00000000000..79f83e802ca
--- /dev/null
+++ b/tests/ui/check-cfg/invalid-arguments.not_empty_values_any.stderr
@@ -0,0 +1,2 @@
+error: invalid `--check-cfg` argument: `cfg(foo,values(any(bar)))` (`any()` must be empty)
+
diff --git a/tests/ui/check-cfg/invalid-arguments.rs b/tests/ui/check-cfg/invalid-arguments.rs
index 5090ce3e845..79bef89c957 100644
--- a/tests/ui/check-cfg/invalid-arguments.rs
+++ b/tests/ui/check-cfg/invalid-arguments.rs
@@ -2,9 +2,33 @@
 //
 // check-fail
 // revisions: anything_else names_simple_ident values_simple_ident values_string_literals
-// [anything_else]compile-flags: -Z unstable-options --check-cfg=anything_else(...)
-// [names_simple_ident]compile-flags: -Z unstable-options --check-cfg=names("NOT_IDENT")
-// [values_simple_ident]compile-flags: -Z unstable-options --check-cfg=values("NOT_IDENT")
-// [values_string_literals]compile-flags: -Z unstable-options --check-cfg=values(test,12)
+// revisions: string_for_name_1 string_for_name_2 multiple_any multiple_values
+// revisions: multiple_values_any not_empty_any not_empty_values_any
+// revisions: values_any_missing_values values_any_before_ident ident_in_values_1
+// revisions: ident_in_values_2 unknown_meta_item_1 unknown_meta_item_2 unknown_meta_item_3
+// revisions: mixed_values_any mixed_any giberich
+//
+// compile-flags: -Z unstable-options
+// [anything_else]compile-flags: --check-cfg=anything_else(...)
+// [names_simple_ident]compile-flags: --check-cfg=names("NOT_IDENT")
+// [values_simple_ident]compile-flags: --check-cfg=values("NOT_IDENT")
+// [values_string_literals]compile-flags: --check-cfg=values(test,12)
+// [string_for_name_1]compile-flags: --check-cfg=cfg("NOT_IDENT")
+// [string_for_name_2]compile-flags: --check-cfg=cfg(foo,"NOT_IDENT",bar)
+// [multiple_any]compile-flags: --check-cfg=cfg(any(),any())
+// [multiple_values]compile-flags: --check-cfg=cfg(foo,values(),values())
+// [multiple_values_any]compile-flags: --check-cfg=cfg(foo,values(any(),any()))
+// [not_empty_any]compile-flags: --check-cfg=cfg(any(foo))
+// [not_empty_values_any]compile-flags: --check-cfg=cfg(foo,values(any(bar)))
+// [values_any_missing_values]compile-flags: --check-cfg=cfg(foo,any())
+// [values_any_before_ident]compile-flags: --check-cfg=cfg(values(any()),foo)
+// [ident_in_values_1]compile-flags: --check-cfg=cfg(foo,values(bar))
+// [ident_in_values_2]compile-flags: --check-cfg=cfg(foo,values("bar",bar,"bar"))
+// [unknown_meta_item_1]compile-flags: --check-cfg=abc()
+// [unknown_meta_item_2]compile-flags: --check-cfg=cfg(foo,test())
+// [unknown_meta_item_3]compile-flags: --check-cfg=cfg(foo,values(test()))
+// [mixed_values_any]compile-flags: --check-cfg=cfg(foo,values("bar",any()))
+// [mixed_any]compile-flags: --check-cfg=cfg(any(),values(any()))
+// [giberich]compile-flags: --check-cfg=cfg(...)
 
 fn main() {}
diff --git a/tests/ui/check-cfg/invalid-arguments.string_for_name_1.stderr b/tests/ui/check-cfg/invalid-arguments.string_for_name_1.stderr
new file mode 100644
index 00000000000..c6f6834ffd3
--- /dev/null
+++ b/tests/ui/check-cfg/invalid-arguments.string_for_name_1.stderr
@@ -0,0 +1,2 @@
+error: invalid `--check-cfg` argument: `cfg("NOT_IDENT")` (`cfg()` arguments must be simple identifiers, `any()` or `values(...)`)
+
diff --git a/tests/ui/check-cfg/invalid-arguments.string_for_name_2.stderr b/tests/ui/check-cfg/invalid-arguments.string_for_name_2.stderr
new file mode 100644
index 00000000000..ab3dc86cd1a
--- /dev/null
+++ b/tests/ui/check-cfg/invalid-arguments.string_for_name_2.stderr
@@ -0,0 +1,2 @@
+error: invalid `--check-cfg` argument: `cfg(foo,"NOT_IDENT",bar)` (`cfg()` arguments must be simple identifiers, `any()` or `values(...)`)
+
diff --git a/tests/ui/check-cfg/invalid-arguments.unknown_meta_item_1.stderr b/tests/ui/check-cfg/invalid-arguments.unknown_meta_item_1.stderr
new file mode 100644
index 00000000000..c04b15ec265
--- /dev/null
+++ b/tests/ui/check-cfg/invalid-arguments.unknown_meta_item_1.stderr
@@ -0,0 +1,2 @@
+error: invalid `--check-cfg` argument: `abc()` (expected `cfg(name, values("value1", "value2", ... "valueN"))`)
+
diff --git a/tests/ui/check-cfg/invalid-arguments.unknown_meta_item_2.stderr b/tests/ui/check-cfg/invalid-arguments.unknown_meta_item_2.stderr
new file mode 100644
index 00000000000..cee65f9887b
--- /dev/null
+++ b/tests/ui/check-cfg/invalid-arguments.unknown_meta_item_2.stderr
@@ -0,0 +1,2 @@
+error: invalid `--check-cfg` argument: `cfg(foo,test())` (`cfg()` arguments must be simple identifiers, `any()` or `values(...)`)
+
diff --git a/tests/ui/check-cfg/invalid-arguments.unknown_meta_item_3.stderr b/tests/ui/check-cfg/invalid-arguments.unknown_meta_item_3.stderr
new file mode 100644
index 00000000000..2441e2537b7
--- /dev/null
+++ b/tests/ui/check-cfg/invalid-arguments.unknown_meta_item_3.stderr
@@ -0,0 +1,2 @@
+error: invalid `--check-cfg` argument: `cfg(foo,values(test()))` (`values()` arguments must be string literals or `any()`)
+
diff --git a/tests/ui/check-cfg/invalid-arguments.values_any_before_ident.stderr b/tests/ui/check-cfg/invalid-arguments.values_any_before_ident.stderr
new file mode 100644
index 00000000000..fc93ec8fbdf
--- /dev/null
+++ b/tests/ui/check-cfg/invalid-arguments.values_any_before_ident.stderr
@@ -0,0 +1,2 @@
+error: invalid `--check-cfg` argument: `cfg(values(any()),foo)` (`values()` cannot be specified before the names)
+
diff --git a/tests/ui/check-cfg/invalid-arguments.values_any_missing_values.stderr b/tests/ui/check-cfg/invalid-arguments.values_any_missing_values.stderr
new file mode 100644
index 00000000000..f41672fcbdb
--- /dev/null
+++ b/tests/ui/check-cfg/invalid-arguments.values_any_missing_values.stderr
@@ -0,0 +1,2 @@
+error: invalid `--check-cfg` argument: `cfg(foo,any())` (`cfg(any())` can only be provided in isolation)
+
diff --git a/tests/ui/check-cfg/invalid-cfg-name.rs b/tests/ui/check-cfg/invalid-cfg-name.rs
deleted file mode 100644
index 8499d3d4448..00000000000
--- a/tests/ui/check-cfg/invalid-cfg-name.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-// Check warning for invalid configuration name
-//
-// edition:2018
-// check-pass
-// compile-flags: --check-cfg=names() -Z unstable-options
-
-#[cfg(widnows)]
-//~^ WARNING unexpected `cfg` condition name
-pub fn f() {}
-
-#[cfg(windows)]
-pub fn g() {}
-
-pub fn main() {}
diff --git a/tests/ui/check-cfg/invalid-cfg-value.rs b/tests/ui/check-cfg/invalid-cfg-value.rs
deleted file mode 100644
index 9e428d367fd..00000000000
--- a/tests/ui/check-cfg/invalid-cfg-value.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Check warning for invalid configuration value
-//
-// edition:2018
-// check-pass
-// compile-flags: --check-cfg=values(feature,"serde","full") --cfg=feature="rand" -Z unstable-options
-
-#[cfg(feature = "sedre")]
-//~^ WARNING unexpected `cfg` condition value
-pub fn f() {}
-
-#[cfg(feature = "serde")]
-pub fn g() {}
-
-#[cfg(feature = "rand")]
-//~^ WARNING unexpected `cfg` condition value
-pub fn h() {}
-
-pub fn main() {}
diff --git a/tests/ui/check-cfg/mix.stderr b/tests/ui/check-cfg/mix.cfg.stderr
index 23da9f22a72..daa200440cc 100644
--- a/tests/ui/check-cfg/mix.stderr
+++ b/tests/ui/check-cfg/mix.cfg.stderr
@@ -1,5 +1,5 @@
 warning: unexpected `cfg` condition name: `widnows`
-  --> $DIR/mix.rs:11:7
+  --> $DIR/mix.rs:15:7
    |
 LL | #[cfg(widnows)]
    |       ^^^^^^^ help: there is a config with a similar name: `windows`
@@ -7,7 +7,7 @@ LL | #[cfg(widnows)]
    = note: `#[warn(unexpected_cfgs)]` on by default
 
 warning: unexpected `cfg` condition value: (none)
-  --> $DIR/mix.rs:15:7
+  --> $DIR/mix.rs:19:7
    |
 LL | #[cfg(feature)]
    |       ^^^^^^^- help: specify a config value: `= "foo"`
@@ -15,7 +15,7 @@ LL | #[cfg(feature)]
    = note: expected values for `feature` are: `foo`
 
 warning: unexpected `cfg` condition value: `bar`
-  --> $DIR/mix.rs:22:7
+  --> $DIR/mix.rs:26:7
    |
 LL | #[cfg(feature = "bar")]
    |       ^^^^^^^^^^^^^^^
@@ -23,7 +23,7 @@ LL | #[cfg(feature = "bar")]
    = note: expected values for `feature` are: `foo`
 
 warning: unexpected `cfg` condition value: `zebra`
-  --> $DIR/mix.rs:26:7
+  --> $DIR/mix.rs:30:7
    |
 LL | #[cfg(feature = "zebra")]
    |       ^^^^^^^^^^^^^^^^^
@@ -31,12 +31,12 @@ LL | #[cfg(feature = "zebra")]
    = note: expected values for `feature` are: `foo`
 
 warning: unexpected `cfg` condition name: `uu`
-  --> $DIR/mix.rs:30:12
+  --> $DIR/mix.rs:34:12
    |
 LL | #[cfg_attr(uu, test)]
    |            ^^
    |
-   = help: expected names are: `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
+   = help: expected names are: `cfg`, `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `names_values`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
 
 warning: unexpected condition value `bar` for condition name `feature`
    |
@@ -47,13 +47,13 @@ warning: unexpected `unknown_name` as condition name
    = help: was set with `--cfg` but isn't in the `--check-cfg` expected names
 
 warning: unexpected `cfg` condition name: `widnows`
-  --> $DIR/mix.rs:39:10
+  --> $DIR/mix.rs:43:10
    |
 LL |     cfg!(widnows);
    |          ^^^^^^^ help: there is a config with a similar name: `windows`
 
 warning: unexpected `cfg` condition value: `bar`
-  --> $DIR/mix.rs:42:10
+  --> $DIR/mix.rs:46:10
    |
 LL |     cfg!(feature = "bar");
    |          ^^^^^^^^^^^^^^^
@@ -61,7 +61,7 @@ LL |     cfg!(feature = "bar");
    = note: expected values for `feature` are: `foo`
 
 warning: unexpected `cfg` condition value: `zebra`
-  --> $DIR/mix.rs:44:10
+  --> $DIR/mix.rs:48:10
    |
 LL |     cfg!(feature = "zebra");
    |          ^^^^^^^^^^^^^^^^^
@@ -69,25 +69,25 @@ LL |     cfg!(feature = "zebra");
    = note: expected values for `feature` are: `foo`
 
 warning: unexpected `cfg` condition name: `xxx`
-  --> $DIR/mix.rs:46:10
+  --> $DIR/mix.rs:50:10
    |
 LL |     cfg!(xxx = "foo");
    |          ^^^^^^^^^^^
 
 warning: unexpected `cfg` condition name: `xxx`
-  --> $DIR/mix.rs:48:10
+  --> $DIR/mix.rs:52:10
    |
 LL |     cfg!(xxx);
    |          ^^^
 
 warning: unexpected `cfg` condition name: `xxx`
-  --> $DIR/mix.rs:50:14
+  --> $DIR/mix.rs:54:14
    |
 LL |     cfg!(any(xxx, windows));
    |              ^^^
 
 warning: unexpected `cfg` condition value: `bad`
-  --> $DIR/mix.rs:52:14
+  --> $DIR/mix.rs:56:14
    |
 LL |     cfg!(any(feature = "bad", windows));
    |              ^^^^^^^^^^^^^^^
@@ -95,43 +95,43 @@ LL |     cfg!(any(feature = "bad", windows));
    = note: expected values for `feature` are: `foo`
 
 warning: unexpected `cfg` condition name: `xxx`
-  --> $DIR/mix.rs:54:23
+  --> $DIR/mix.rs:58:23
    |
 LL |     cfg!(any(windows, xxx));
    |                       ^^^
 
 warning: unexpected `cfg` condition name: `xxx`
-  --> $DIR/mix.rs:56:20
+  --> $DIR/mix.rs:60:20
    |
 LL |     cfg!(all(unix, xxx));
    |                    ^^^
 
 warning: unexpected `cfg` condition name: `aa`
-  --> $DIR/mix.rs:58:14
+  --> $DIR/mix.rs:62:14
    |
 LL |     cfg!(all(aa, bb));
    |              ^^
 
 warning: unexpected `cfg` condition name: `bb`
-  --> $DIR/mix.rs:58:18
+  --> $DIR/mix.rs:62:18
    |
 LL |     cfg!(all(aa, bb));
    |                  ^^
 
 warning: unexpected `cfg` condition name: `aa`
-  --> $DIR/mix.rs:61:14
+  --> $DIR/mix.rs:65:14
    |
 LL |     cfg!(any(aa, bb));
    |              ^^
 
 warning: unexpected `cfg` condition name: `bb`
-  --> $DIR/mix.rs:61:18
+  --> $DIR/mix.rs:65:18
    |
 LL |     cfg!(any(aa, bb));
    |                  ^^
 
 warning: unexpected `cfg` condition value: `zebra`
-  --> $DIR/mix.rs:64:20
+  --> $DIR/mix.rs:68:20
    |
 LL |     cfg!(any(unix, feature = "zebra"));
    |                    ^^^^^^^^^^^^^^^^^
@@ -139,13 +139,13 @@ LL |     cfg!(any(unix, feature = "zebra"));
    = note: expected values for `feature` are: `foo`
 
 warning: unexpected `cfg` condition name: `xxx`
-  --> $DIR/mix.rs:66:14
+  --> $DIR/mix.rs:70:14
    |
 LL |     cfg!(any(xxx, feature = "zebra"));
    |              ^^^
 
 warning: unexpected `cfg` condition value: `zebra`
-  --> $DIR/mix.rs:66:19
+  --> $DIR/mix.rs:70:19
    |
 LL |     cfg!(any(xxx, feature = "zebra"));
    |                   ^^^^^^^^^^^^^^^^^
@@ -153,19 +153,19 @@ LL |     cfg!(any(xxx, feature = "zebra"));
    = note: expected values for `feature` are: `foo`
 
 warning: unexpected `cfg` condition name: `xxx`
-  --> $DIR/mix.rs:69:14
+  --> $DIR/mix.rs:73:14
    |
 LL |     cfg!(any(xxx, unix, xxx));
    |              ^^^
 
 warning: unexpected `cfg` condition name: `xxx`
-  --> $DIR/mix.rs:69:25
+  --> $DIR/mix.rs:73:25
    |
 LL |     cfg!(any(xxx, unix, xxx));
    |                         ^^^
 
 warning: unexpected `cfg` condition value: `zebra`
-  --> $DIR/mix.rs:72:14
+  --> $DIR/mix.rs:76:14
    |
 LL |     cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra"));
    |              ^^^^^^^^^^^^^^^^^
@@ -173,7 +173,7 @@ LL |     cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra"));
    = note: expected values for `feature` are: `foo`
 
 warning: unexpected `cfg` condition value: `zebra`
-  --> $DIR/mix.rs:72:33
+  --> $DIR/mix.rs:76:33
    |
 LL |     cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra"));
    |                                 ^^^^^^^^^^^^^^^^^
@@ -181,7 +181,7 @@ LL |     cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra"));
    = note: expected values for `feature` are: `foo`
 
 warning: unexpected `cfg` condition value: `zebra`
-  --> $DIR/mix.rs:72:52
+  --> $DIR/mix.rs:76:52
    |
 LL |     cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra"));
    |                                                    ^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/check-cfg/mix.names_values.stderr b/tests/ui/check-cfg/mix.names_values.stderr
new file mode 100644
index 00000000000..daa200440cc
--- /dev/null
+++ b/tests/ui/check-cfg/mix.names_values.stderr
@@ -0,0 +1,192 @@
+warning: unexpected `cfg` condition name: `widnows`
+  --> $DIR/mix.rs:15:7
+   |
+LL | #[cfg(widnows)]
+   |       ^^^^^^^ help: there is a config with a similar name: `windows`
+   |
+   = note: `#[warn(unexpected_cfgs)]` on by default
+
+warning: unexpected `cfg` condition value: (none)
+  --> $DIR/mix.rs:19:7
+   |
+LL | #[cfg(feature)]
+   |       ^^^^^^^- help: specify a config value: `= "foo"`
+   |
+   = note: expected values for `feature` are: `foo`
+
+warning: unexpected `cfg` condition value: `bar`
+  --> $DIR/mix.rs:26:7
+   |
+LL | #[cfg(feature = "bar")]
+   |       ^^^^^^^^^^^^^^^
+   |
+   = note: expected values for `feature` are: `foo`
+
+warning: unexpected `cfg` condition value: `zebra`
+  --> $DIR/mix.rs:30:7
+   |
+LL | #[cfg(feature = "zebra")]
+   |       ^^^^^^^^^^^^^^^^^
+   |
+   = note: expected values for `feature` are: `foo`
+
+warning: unexpected `cfg` condition name: `uu`
+  --> $DIR/mix.rs:34:12
+   |
+LL | #[cfg_attr(uu, test)]
+   |            ^^
+   |
+   = help: expected names are: `cfg`, `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `names_values`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
+
+warning: unexpected condition value `bar` for condition name `feature`
+   |
+   = help: was set with `--cfg` but isn't in the `--check-cfg` expected values
+
+warning: unexpected `unknown_name` as condition name
+   |
+   = help: was set with `--cfg` but isn't in the `--check-cfg` expected names
+
+warning: unexpected `cfg` condition name: `widnows`
+  --> $DIR/mix.rs:43:10
+   |
+LL |     cfg!(widnows);
+   |          ^^^^^^^ help: there is a config with a similar name: `windows`
+
+warning: unexpected `cfg` condition value: `bar`
+  --> $DIR/mix.rs:46:10
+   |
+LL |     cfg!(feature = "bar");
+   |          ^^^^^^^^^^^^^^^
+   |
+   = note: expected values for `feature` are: `foo`
+
+warning: unexpected `cfg` condition value: `zebra`
+  --> $DIR/mix.rs:48:10
+   |
+LL |     cfg!(feature = "zebra");
+   |          ^^^^^^^^^^^^^^^^^
+   |
+   = note: expected values for `feature` are: `foo`
+
+warning: unexpected `cfg` condition name: `xxx`
+  --> $DIR/mix.rs:50:10
+   |
+LL |     cfg!(xxx = "foo");
+   |          ^^^^^^^^^^^
+
+warning: unexpected `cfg` condition name: `xxx`
+  --> $DIR/mix.rs:52:10
+   |
+LL |     cfg!(xxx);
+   |          ^^^
+
+warning: unexpected `cfg` condition name: `xxx`
+  --> $DIR/mix.rs:54:14
+   |
+LL |     cfg!(any(xxx, windows));
+   |              ^^^
+
+warning: unexpected `cfg` condition value: `bad`
+  --> $DIR/mix.rs:56:14
+   |
+LL |     cfg!(any(feature = "bad", windows));
+   |              ^^^^^^^^^^^^^^^
+   |
+   = note: expected values for `feature` are: `foo`
+
+warning: unexpected `cfg` condition name: `xxx`
+  --> $DIR/mix.rs:58:23
+   |
+LL |     cfg!(any(windows, xxx));
+   |                       ^^^
+
+warning: unexpected `cfg` condition name: `xxx`
+  --> $DIR/mix.rs:60:20
+   |
+LL |     cfg!(all(unix, xxx));
+   |                    ^^^
+
+warning: unexpected `cfg` condition name: `aa`
+  --> $DIR/mix.rs:62:14
+   |
+LL |     cfg!(all(aa, bb));
+   |              ^^
+
+warning: unexpected `cfg` condition name: `bb`
+  --> $DIR/mix.rs:62:18
+   |
+LL |     cfg!(all(aa, bb));
+   |                  ^^
+
+warning: unexpected `cfg` condition name: `aa`
+  --> $DIR/mix.rs:65:14
+   |
+LL |     cfg!(any(aa, bb));
+   |              ^^
+
+warning: unexpected `cfg` condition name: `bb`
+  --> $DIR/mix.rs:65:18
+   |
+LL |     cfg!(any(aa, bb));
+   |                  ^^
+
+warning: unexpected `cfg` condition value: `zebra`
+  --> $DIR/mix.rs:68:20
+   |
+LL |     cfg!(any(unix, feature = "zebra"));
+   |                    ^^^^^^^^^^^^^^^^^
+   |
+   = note: expected values for `feature` are: `foo`
+
+warning: unexpected `cfg` condition name: `xxx`
+  --> $DIR/mix.rs:70:14
+   |
+LL |     cfg!(any(xxx, feature = "zebra"));
+   |              ^^^
+
+warning: unexpected `cfg` condition value: `zebra`
+  --> $DIR/mix.rs:70:19
+   |
+LL |     cfg!(any(xxx, feature = "zebra"));
+   |                   ^^^^^^^^^^^^^^^^^
+   |
+   = note: expected values for `feature` are: `foo`
+
+warning: unexpected `cfg` condition name: `xxx`
+  --> $DIR/mix.rs:73:14
+   |
+LL |     cfg!(any(xxx, unix, xxx));
+   |              ^^^
+
+warning: unexpected `cfg` condition name: `xxx`
+  --> $DIR/mix.rs:73:25
+   |
+LL |     cfg!(any(xxx, unix, xxx));
+   |                         ^^^
+
+warning: unexpected `cfg` condition value: `zebra`
+  --> $DIR/mix.rs:76:14
+   |
+LL |     cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra"));
+   |              ^^^^^^^^^^^^^^^^^
+   |
+   = note: expected values for `feature` are: `foo`
+
+warning: unexpected `cfg` condition value: `zebra`
+  --> $DIR/mix.rs:76:33
+   |
+LL |     cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra"));
+   |                                 ^^^^^^^^^^^^^^^^^
+   |
+   = note: expected values for `feature` are: `foo`
+
+warning: unexpected `cfg` condition value: `zebra`
+  --> $DIR/mix.rs:76:52
+   |
+LL |     cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra"));
+   |                                                    ^^^^^^^^^^^^^^^^^
+   |
+   = note: expected values for `feature` are: `foo`
+
+warning: 28 warnings emitted
+
diff --git a/tests/ui/check-cfg/mix.rs b/tests/ui/check-cfg/mix.rs
index 9adf5c46e43..d7b3b4953b7 100644
--- a/tests/ui/check-cfg/mix.rs
+++ b/tests/ui/check-cfg/mix.rs
@@ -3,7 +3,11 @@
 // we correctly lint on the `cfg!` macro and `cfg_attr` attribute.
 //
 // check-pass
-// compile-flags: --check-cfg=names() --check-cfg=values(feature,"foo") --cfg feature="bar" --cfg unknown_name -Z unstable-options
+// revisions: names_values cfg
+// compile-flags: --cfg feature="bar" --cfg unknown_name -Z unstable-options
+// compile-flags: --check-cfg=cfg(names_values,cfg)
+// [names_values]compile-flags: --check-cfg=names() --check-cfg=values(feature,"foo")
+// [cfg]compile-flags: --check-cfg=cfg(feature,values("foo"))
 
 #[cfg(windows)]
 fn do_windows_stuff() {}
diff --git a/tests/ui/check-cfg/no-values.stderr b/tests/ui/check-cfg/no-expected-values.empty.stderr
index b05a569dd01..5d261b2a5e6 100644
--- a/tests/ui/check-cfg/no-values.stderr
+++ b/tests/ui/check-cfg/no-expected-values.empty.stderr
@@ -1,5 +1,5 @@
 warning: unexpected `cfg` condition value: `foo`
-  --> $DIR/no-values.rs:6:7
+  --> $DIR/no-expected-values.rs:12:7
    |
 LL | #[cfg(feature = "foo")]
    |       ^^^^^^^--------
@@ -10,7 +10,7 @@ LL | #[cfg(feature = "foo")]
    = note: `#[warn(unexpected_cfgs)]` on by default
 
 warning: unexpected `cfg` condition value: `foo`
-  --> $DIR/no-values.rs:10:7
+  --> $DIR/no-expected-values.rs:16:7
    |
 LL | #[cfg(test = "foo")]
    |       ^^^^--------
diff --git a/tests/ui/check-cfg/no-expected-values.mixed.stderr b/tests/ui/check-cfg/no-expected-values.mixed.stderr
new file mode 100644
index 00000000000..5d261b2a5e6
--- /dev/null
+++ b/tests/ui/check-cfg/no-expected-values.mixed.stderr
@@ -0,0 +1,23 @@
+warning: unexpected `cfg` condition value: `foo`
+  --> $DIR/no-expected-values.rs:12:7
+   |
+LL | #[cfg(feature = "foo")]
+   |       ^^^^^^^--------
+   |              |
+   |              help: remove the value
+   |
+   = note: no expected value for `feature`
+   = note: `#[warn(unexpected_cfgs)]` on by default
+
+warning: unexpected `cfg` condition value: `foo`
+  --> $DIR/no-expected-values.rs:16:7
+   |
+LL | #[cfg(test = "foo")]
+   |       ^^^^--------
+   |           |
+   |           help: remove the value
+   |
+   = note: no expected value for `test`
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/check-cfg/no-expected-values.rs b/tests/ui/check-cfg/no-expected-values.rs
new file mode 100644
index 00000000000..9e2a9f09aed
--- /dev/null
+++ b/tests/ui/check-cfg/no-expected-values.rs
@@ -0,0 +1,20 @@
+// Check that we detect unexpected value when none are allowed
+//
+// check-pass
+// revisions: values simple mixed empty
+// compile-flags: -Z unstable-options
+// compile-flags: --check-cfg=cfg(values,simple,mixed,empty)
+// [values]compile-flags: --check-cfg=values(test) --check-cfg=values(feature)
+// [simple]compile-flags: --check-cfg=cfg(test) --check-cfg=cfg(feature)
+// [mixed]compile-flags: --check-cfg=cfg(test,feature)
+// [empty]compile-flags: --check-cfg=cfg(test,feature,values())
+
+#[cfg(feature = "foo")]
+//~^ WARNING unexpected `cfg` condition value
+fn do_foo() {}
+
+#[cfg(test = "foo")]
+//~^ WARNING unexpected `cfg` condition value
+fn do_foo() {}
+
+fn main() {}
diff --git a/tests/ui/check-cfg/no-expected-values.simple.stderr b/tests/ui/check-cfg/no-expected-values.simple.stderr
new file mode 100644
index 00000000000..5d261b2a5e6
--- /dev/null
+++ b/tests/ui/check-cfg/no-expected-values.simple.stderr
@@ -0,0 +1,23 @@
+warning: unexpected `cfg` condition value: `foo`
+  --> $DIR/no-expected-values.rs:12:7
+   |
+LL | #[cfg(feature = "foo")]
+   |       ^^^^^^^--------
+   |              |
+   |              help: remove the value
+   |
+   = note: no expected value for `feature`
+   = note: `#[warn(unexpected_cfgs)]` on by default
+
+warning: unexpected `cfg` condition value: `foo`
+  --> $DIR/no-expected-values.rs:16:7
+   |
+LL | #[cfg(test = "foo")]
+   |       ^^^^--------
+   |           |
+   |           help: remove the value
+   |
+   = note: no expected value for `test`
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/check-cfg/no-expected-values.values.stderr b/tests/ui/check-cfg/no-expected-values.values.stderr
new file mode 100644
index 00000000000..5d261b2a5e6
--- /dev/null
+++ b/tests/ui/check-cfg/no-expected-values.values.stderr
@@ -0,0 +1,23 @@
+warning: unexpected `cfg` condition value: `foo`
+  --> $DIR/no-expected-values.rs:12:7
+   |
+LL | #[cfg(feature = "foo")]
+   |       ^^^^^^^--------
+   |              |
+   |              help: remove the value
+   |
+   = note: no expected value for `feature`
+   = note: `#[warn(unexpected_cfgs)]` on by default
+
+warning: unexpected `cfg` condition value: `foo`
+  --> $DIR/no-expected-values.rs:16:7
+   |
+LL | #[cfg(test = "foo")]
+   |       ^^^^--------
+   |           |
+   |           help: remove the value
+   |
+   = note: no expected value for `test`
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/check-cfg/no-values.rs b/tests/ui/check-cfg/no-values.rs
deleted file mode 100644
index 8c80f56cb5a..00000000000
--- a/tests/ui/check-cfg/no-values.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-// Check that we detect unexpected value when none are allowed
-//
-// check-pass
-// compile-flags: --check-cfg=values(test) --check-cfg=values(feature) -Z unstable-options
-
-#[cfg(feature = "foo")]
-//~^ WARNING unexpected `cfg` condition value
-fn do_foo() {}
-
-#[cfg(test = "foo")]
-//~^ WARNING unexpected `cfg` condition value
-fn do_foo() {}
-
-fn main() {}
diff --git a/tests/ui/check-cfg/stmt-no-ice.rs b/tests/ui/check-cfg/stmt-no-ice.rs
index cf76487ed46..383e830a1b2 100644
--- a/tests/ui/check-cfg/stmt-no-ice.rs
+++ b/tests/ui/check-cfg/stmt-no-ice.rs
@@ -1,7 +1,7 @@
 // This test checks that there is no ICE with this code
 //
 // check-pass
-// compile-flags:--check-cfg=names() -Z unstable-options
+// compile-flags:--check-cfg=cfg() -Z unstable-options
 
 fn main() {
     #[cfg(crossbeam_loom)]
diff --git a/tests/ui/check-cfg/invalid-cfg-name.stderr b/tests/ui/check-cfg/unexpected-cfg-name.exhaustive.stderr
index 8c3c72c9667..513f7ac7fd1 100644
--- a/tests/ui/check-cfg/invalid-cfg-name.stderr
+++ b/tests/ui/check-cfg/unexpected-cfg-name.exhaustive.stderr
@@ -1,5 +1,5 @@
 warning: unexpected `cfg` condition name: `widnows`
-  --> $DIR/invalid-cfg-name.rs:7:7
+  --> $DIR/unexpected-cfg-name.rs:9:7
    |
 LL | #[cfg(widnows)]
    |       ^^^^^^^ help: there is a config with a similar name: `windows`
diff --git a/tests/ui/check-cfg/unexpected-cfg-name.names.stderr b/tests/ui/check-cfg/unexpected-cfg-name.names.stderr
new file mode 100644
index 00000000000..513f7ac7fd1
--- /dev/null
+++ b/tests/ui/check-cfg/unexpected-cfg-name.names.stderr
@@ -0,0 +1,10 @@
+warning: unexpected `cfg` condition name: `widnows`
+  --> $DIR/unexpected-cfg-name.rs:9:7
+   |
+LL | #[cfg(widnows)]
+   |       ^^^^^^^ help: there is a config with a similar name: `windows`
+   |
+   = note: `#[warn(unexpected_cfgs)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/check-cfg/unexpected-cfg-name.rs b/tests/ui/check-cfg/unexpected-cfg-name.rs
new file mode 100644
index 00000000000..15c3aa6e081
--- /dev/null
+++ b/tests/ui/check-cfg/unexpected-cfg-name.rs
@@ -0,0 +1,16 @@
+// Check warning for unexpected configuration name
+//
+// check-pass
+// revisions: names exhaustive
+// compile-flags: --check-cfg=cfg(names,exhaustive)
+// [names]compile-flags: --check-cfg=names() -Z unstable-options
+// [exhaustive]compile-flags: --check-cfg=cfg() -Z unstable-options
+
+#[cfg(widnows)]
+//~^ WARNING unexpected `cfg` condition name
+pub fn f() {}
+
+#[cfg(windows)]
+pub fn g() {}
+
+pub fn main() {}
diff --git a/tests/ui/check-cfg/invalid-cfg-value.stderr b/tests/ui/check-cfg/unexpected-cfg-value.cfg.stderr
index 947beac6ffd..2ed7f900557 100644
--- a/tests/ui/check-cfg/invalid-cfg-value.stderr
+++ b/tests/ui/check-cfg/unexpected-cfg-value.cfg.stderr
@@ -1,5 +1,5 @@
 warning: unexpected `cfg` condition value: `sedre`
-  --> $DIR/invalid-cfg-value.rs:7:7
+  --> $DIR/unexpected-cfg-value.rs:11:7
    |
 LL | #[cfg(feature = "sedre")]
    |       ^^^^^^^^^^-------
@@ -10,7 +10,7 @@ LL | #[cfg(feature = "sedre")]
    = note: `#[warn(unexpected_cfgs)]` on by default
 
 warning: unexpected `cfg` condition value: `rand`
-  --> $DIR/invalid-cfg-value.rs:14:7
+  --> $DIR/unexpected-cfg-value.rs:18:7
    |
 LL | #[cfg(feature = "rand")]
    |       ^^^^^^^^^^^^^^^^
diff --git a/tests/ui/check-cfg/unexpected-cfg-value.rs b/tests/ui/check-cfg/unexpected-cfg-value.rs
new file mode 100644
index 00000000000..a84458071de
--- /dev/null
+++ b/tests/ui/check-cfg/unexpected-cfg-value.rs
@@ -0,0 +1,22 @@
+// Check warning for invalid configuration value in the code and
+// in the cli
+//
+// check-pass
+// revisions: values cfg
+// compile-flags: --cfg=feature="rand" -Z unstable-options
+// compile-flags: --check-cfg=cfg(values,cfg)
+// [values]compile-flags: --check-cfg=values(feature,"serde","full")
+// [cfg]compile-flags: --check-cfg=cfg(feature,values("serde","full"))
+
+#[cfg(feature = "sedre")]
+//~^ WARNING unexpected `cfg` condition value
+pub fn f() {}
+
+#[cfg(feature = "serde")]
+pub fn g() {}
+
+#[cfg(feature = "rand")]
+//~^ WARNING unexpected `cfg` condition value
+pub fn h() {}
+
+pub fn main() {}
diff --git a/tests/ui/check-cfg/unexpected-cfg-value.values.stderr b/tests/ui/check-cfg/unexpected-cfg-value.values.stderr
new file mode 100644
index 00000000000..2ed7f900557
--- /dev/null
+++ b/tests/ui/check-cfg/unexpected-cfg-value.values.stderr
@@ -0,0 +1,25 @@
+warning: unexpected `cfg` condition value: `sedre`
+  --> $DIR/unexpected-cfg-value.rs:11:7
+   |
+LL | #[cfg(feature = "sedre")]
+   |       ^^^^^^^^^^-------
+   |                 |
+   |                 help: there is a expected value with a similar name: `"serde"`
+   |
+   = note: expected values for `feature` are: `full`, `serde`
+   = note: `#[warn(unexpected_cfgs)]` on by default
+
+warning: unexpected `cfg` condition value: `rand`
+  --> $DIR/unexpected-cfg-value.rs:18:7
+   |
+LL | #[cfg(feature = "rand")]
+   |       ^^^^^^^^^^^^^^^^
+   |
+   = note: expected values for `feature` are: `full`, `serde`
+
+warning: unexpected condition value `rand` for condition name `feature`
+   |
+   = help: was set with `--cfg` but isn't in the `--check-cfg` expected values
+
+warning: 3 warnings emitted
+
diff --git a/tests/ui/check-cfg/unknown-values.rs b/tests/ui/check-cfg/unknown-values.rs
new file mode 100644
index 00000000000..c082a2f25ac
--- /dev/null
+++ b/tests/ui/check-cfg/unknown-values.rs
@@ -0,0 +1,17 @@
+// Check that no warning is emitted for unknown cfg value
+//
+// check-pass
+// revisions: simple mixed with_values
+// compile-flags: -Z unstable-options
+// compile-flags: --check-cfg=cfg(simple,mixed,with_values)
+// [simple]compile-flags: --check-cfg=cfg(foo,values(any()))
+// [mixed]compile-flags: --check-cfg=cfg(foo) --check-cfg=cfg(foo,values(any()))
+// [with_values]compile-flags:--check-cfg=cfg(foo,values(any())) --check-cfg=cfg(foo,values("aa"))
+
+#[cfg(foo = "value")]
+pub fn f() {}
+
+#[cfg(foo)]
+pub fn f() {}
+
+fn main() {}
diff --git a/tests/ui/check-cfg/well-known-names.rs b/tests/ui/check-cfg/well-known-names.rs
index e57fb69a1e0..1dcb419b4a7 100644
--- a/tests/ui/check-cfg/well-known-names.rs
+++ b/tests/ui/check-cfg/well-known-names.rs
@@ -1,7 +1,7 @@
 // This test checks that we lint on non well known names and that we don't lint on well known names
 //
 // check-pass
-// compile-flags: --check-cfg=names() -Z unstable-options
+// compile-flags: --check-cfg=cfg() -Z unstable-options
 
 #[cfg(target_oz = "linux")]
 //~^ WARNING unexpected `cfg` condition name
diff --git a/tests/ui/check-cfg/well-known-names.stderr b/tests/ui/check-cfg/well-known-names.stderr
index a5d38a99eee..3001289b7e0 100644
--- a/tests/ui/check-cfg/well-known-names.stderr
+++ b/tests/ui/check-cfg/well-known-names.stderr
@@ -2,11 +2,13 @@ warning: unexpected `cfg` condition name: `target_oz`
   --> $DIR/well-known-names.rs:6:7
    |
 LL | #[cfg(target_oz = "linux")]
-   |       ---------^^^^^^^^^^
-   |       |
-   |       help: there is a config with a similar name: `target_os`
+   |       ^^^^^^^^^^^^^^^^^^^
    |
    = note: `#[warn(unexpected_cfgs)]` on by default
+help: there is a config with a similar name and value
+   |
+LL | #[cfg(target_os = "linux")]
+   |       ~~~~~~~~~
 
 warning: unexpected `cfg` condition name: `features`
   --> $DIR/well-known-names.rs:13:7
diff --git a/tests/ui/check-cfg/well-known-values.rs b/tests/ui/check-cfg/well-known-values.rs
index 96375dc8d31..8b56c8729d8 100644
--- a/tests/ui/check-cfg/well-known-values.rs
+++ b/tests/ui/check-cfg/well-known-values.rs
@@ -2,7 +2,7 @@
 // values
 //
 // check-pass
-// compile-flags: --check-cfg=values() -Z unstable-options
+// compile-flags: --check-cfg=cfg() -Z unstable-options
 
 #[cfg(target_os = "linuz")]
 //~^ WARNING unexpected `cfg` condition value
diff --git a/tests/ui/iterators/invalid-iterator-chain-fixable.fixed b/tests/ui/iterators/invalid-iterator-chain-fixable.fixed
new file mode 100644
index 00000000000..513b5bd13d8
--- /dev/null
+++ b/tests/ui/iterators/invalid-iterator-chain-fixable.fixed
@@ -0,0 +1,42 @@
+// run-rustfix
+use std::collections::hash_set::Iter;
+use std::collections::HashSet;
+
+fn iter_to_vec<'b, X>(i: Iter<'b, X>) -> Vec<X> where X: Clone {
+    let i = i.map(|x| x.clone());
+    i.collect() //~ ERROR E0277
+}
+
+fn main() {
+    let v = vec![(0, 0)];
+    let scores = v
+        .iter()
+        .map(|(a, b)| {
+            a + b
+        });
+    println!("{}", scores.sum::<i32>()); //~ ERROR E0277
+    println!(
+        "{}",
+        vec![0, 1]
+            .iter()
+            .map(|x| x * 2)
+            .map(|x| { x })
+            .map(|x| { x })
+            .sum::<i32>(), //~ ERROR E0277
+    );
+    println!("{}", vec![0, 1].iter().map(|x| { x }).sum::<i32>()); //~ ERROR E0277
+    let a = vec![0];
+    let b = a.into_iter();
+    let c = b.map(|x| x + 1);
+    let d = c.filter(|x| *x > 10 );
+    let e = d.map(|x| {
+        x + 1
+    });
+    let f = e.filter(|_| false);
+    let g: Vec<i32> = f.collect(); //~ ERROR E0277
+    println!("{g:?}");
+
+    let mut s = HashSet::new();
+    s.insert(1u8);
+    println!("{:?}", iter_to_vec(s.iter()));
+}
diff --git a/tests/ui/iterators/invalid-iterator-chain-fixable.rs b/tests/ui/iterators/invalid-iterator-chain-fixable.rs
new file mode 100644
index 00000000000..79b861702c7
--- /dev/null
+++ b/tests/ui/iterators/invalid-iterator-chain-fixable.rs
@@ -0,0 +1,42 @@
+// run-rustfix
+use std::collections::hash_set::Iter;
+use std::collections::HashSet;
+
+fn iter_to_vec<'b, X>(i: Iter<'b, X>) -> Vec<X> {
+    let i = i.map(|x| x.clone());
+    i.collect() //~ ERROR E0277
+}
+
+fn main() {
+    let v = vec![(0, 0)];
+    let scores = v
+        .iter()
+        .map(|(a, b)| {
+            a + b;
+        });
+    println!("{}", scores.sum::<i32>()); //~ ERROR E0277
+    println!(
+        "{}",
+        vec![0, 1]
+            .iter()
+            .map(|x| x * 2)
+            .map(|x| { x; })
+            .map(|x| { x })
+            .sum::<i32>(), //~ ERROR E0277
+    );
+    println!("{}", vec![0, 1].iter().map(|x| { x; }).sum::<i32>()); //~ ERROR E0277
+    let a = vec![0];
+    let b = a.into_iter();
+    let c = b.map(|x| x + 1);
+    let d = c.filter(|x| *x > 10 );
+    let e = d.map(|x| {
+        x + 1;
+    });
+    let f = e.filter(|_| false);
+    let g: Vec<i32> = f.collect(); //~ ERROR E0277
+    println!("{g:?}");
+
+    let mut s = HashSet::new();
+    s.insert(1u8);
+    println!("{:?}", iter_to_vec(s.iter()));
+}
diff --git a/tests/ui/iterators/invalid-iterator-chain-fixable.stderr b/tests/ui/iterators/invalid-iterator-chain-fixable.stderr
new file mode 100644
index 00000000000..1bfe765e78a
--- /dev/null
+++ b/tests/ui/iterators/invalid-iterator-chain-fixable.stderr
@@ -0,0 +1,157 @@
+error[E0277]: a value of type `Vec<X>` cannot be built from an iterator over elements of type `&X`
+  --> $DIR/invalid-iterator-chain-fixable.rs:7:7
+   |
+LL |     let i = i.map(|x| x.clone());
+   |                         ------- this method call is cloning the reference `&X`, not `X` which doesn't implement `Clone`
+LL |     i.collect()
+   |       ^^^^^^^ value of type `Vec<X>` cannot be built from `std::iter::Iterator<Item=&X>`
+   |
+   = help: the trait `FromIterator<&X>` is not implemented for `Vec<X>`
+   = help: the trait `FromIterator<X>` is implemented for `Vec<X>`
+   = help: for that trait implementation, expected `X`, found `&X`
+note: the method call chain might not have had the expected associated types
+  --> $DIR/invalid-iterator-chain-fixable.rs:5:26
+   |
+LL | fn iter_to_vec<'b, X>(i: Iter<'b, X>) -> Vec<X> {
+   |                          ^^^^^^^^^^^ `Iterator::Item` is `&X` here
+LL |     let i = i.map(|x| x.clone());
+   |               ------------------ `Iterator::Item` remains `&X` here
+note: required by a bound in `collect`
+  --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+help: consider further restricting type parameter `X`
+   |
+LL | fn iter_to_vec<'b, X>(i: Iter<'b, X>) -> Vec<X> where X: Clone {
+   |                                                 ++++++++++++++
+
+error[E0277]: a value of type `i32` cannot be made by summing an iterator over elements of type `()`
+  --> $DIR/invalid-iterator-chain-fixable.rs:17:33
+   |
+LL |     println!("{}", scores.sum::<i32>());
+   |                           ---   ^^^ value of type `i32` cannot be made by summing a `std::iter::Iterator<Item=()>`
+   |                           |
+   |                           required by a bound introduced by this call
+   |
+   = help: the trait `Sum<()>` is not implemented for `i32`
+   = help: the following other types implement trait `Sum<A>`:
+             <i32 as Sum>
+             <i32 as Sum<&'a i32>>
+note: the method call chain might not have had the expected associated types
+  --> $DIR/invalid-iterator-chain-fixable.rs:14:10
+   |
+LL |       let v = vec![(0, 0)];
+   |               ------------ this expression has type `Vec<({integer}, {integer})>`
+LL |       let scores = v
+LL |           .iter()
+   |            ------ `Iterator::Item` is `&({integer}, {integer})` here
+LL |           .map(|(a, b)| {
+   |  __________^
+LL | |             a + b;
+LL | |         });
+   | |__________^ `Iterator::Item` changed to `()` here
+note: required by a bound in `std::iter::Iterator::sum`
+  --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+help: consider removing this semicolon
+   |
+LL -             a + b;
+LL +             a + b
+   |
+
+error[E0277]: a value of type `i32` cannot be made by summing an iterator over elements of type `()`
+  --> $DIR/invalid-iterator-chain-fixable.rs:25:20
+   |
+LL |             .sum::<i32>(),
+   |              ---   ^^^ value of type `i32` cannot be made by summing a `std::iter::Iterator<Item=()>`
+   |              |
+   |              required by a bound introduced by this call
+   |
+   = help: the trait `Sum<()>` is not implemented for `i32`
+   = help: the following other types implement trait `Sum<A>`:
+             <i32 as Sum>
+             <i32 as Sum<&'a i32>>
+note: the method call chain might not have had the expected associated types
+  --> $DIR/invalid-iterator-chain-fixable.rs:23:14
+   |
+LL |         vec![0, 1]
+   |         ---------- this expression has type `Vec<{integer}>`
+LL |             .iter()
+   |              ------ `Iterator::Item` is `&{integer}` here
+LL |             .map(|x| x * 2)
+   |              -------------- `Iterator::Item` changed to `{integer}` here
+LL |             .map(|x| { x; })
+   |              ^^^^^^^^^^^^^^^ `Iterator::Item` changed to `()` here
+LL |             .map(|x| { x })
+   |              -------------- `Iterator::Item` remains `()` here
+note: required by a bound in `std::iter::Iterator::sum`
+  --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+help: consider removing this semicolon
+   |
+LL -             .map(|x| { x; })
+LL +             .map(|x| { x })
+   |
+
+error[E0277]: a value of type `i32` cannot be made by summing an iterator over elements of type `()`
+  --> $DIR/invalid-iterator-chain-fixable.rs:27:60
+   |
+LL |     println!("{}", vec![0, 1].iter().map(|x| { x; }).sum::<i32>());
+   |                                                      ---   ^^^ value of type `i32` cannot be made by summing a `std::iter::Iterator<Item=()>`
+   |                                                      |
+   |                                                      required by a bound introduced by this call
+   |
+   = help: the trait `Sum<()>` is not implemented for `i32`
+   = help: the following other types implement trait `Sum<A>`:
+             <i32 as Sum>
+             <i32 as Sum<&'a i32>>
+note: the method call chain might not have had the expected associated types
+  --> $DIR/invalid-iterator-chain-fixable.rs:27:38
+   |
+LL |     println!("{}", vec![0, 1].iter().map(|x| { x; }).sum::<i32>());
+   |                    ---------- ------ ^^^^^^^^^^^^^^^ `Iterator::Item` changed to `()` here
+   |                    |          |
+   |                    |          `Iterator::Item` is `&{integer}` here
+   |                    this expression has type `Vec<{integer}>`
+note: required by a bound in `std::iter::Iterator::sum`
+  --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+help: consider removing this semicolon
+   |
+LL -     println!("{}", vec![0, 1].iter().map(|x| { x; }).sum::<i32>());
+LL +     println!("{}", vec![0, 1].iter().map(|x| { x }).sum::<i32>());
+   |
+
+error[E0277]: a value of type `Vec<i32>` cannot be built from an iterator over elements of type `()`
+  --> $DIR/invalid-iterator-chain-fixable.rs:36:25
+   |
+LL |     let g: Vec<i32> = f.collect();
+   |                         ^^^^^^^ value of type `Vec<i32>` cannot be built from `std::iter::Iterator<Item=()>`
+   |
+   = help: the trait `FromIterator<()>` is not implemented for `Vec<i32>`
+   = help: the trait `FromIterator<i32>` is implemented for `Vec<i32>`
+   = help: for that trait implementation, expected `i32`, found `()`
+note: the method call chain might not have had the expected associated types
+  --> $DIR/invalid-iterator-chain-fixable.rs:32:15
+   |
+LL |       let a = vec![0];
+   |               ------- this expression has type `Vec<{integer}>`
+LL |       let b = a.into_iter();
+   |                 ----------- `Iterator::Item` is `{integer}` here
+LL |       let c = b.map(|x| x + 1);
+   |                 -------------- `Iterator::Item` remains `{integer}` here
+LL |       let d = c.filter(|x| *x > 10 );
+   |                 -------------------- `Iterator::Item` remains `{integer}` here
+LL |       let e = d.map(|x| {
+   |  _______________^
+LL | |         x + 1;
+LL | |     });
+   | |______^ `Iterator::Item` changed to `()` here
+LL |       let f = e.filter(|_| false);
+   |                 ----------------- `Iterator::Item` remains `()` here
+note: required by a bound in `collect`
+  --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+help: consider removing this semicolon
+   |
+LL -         x + 1;
+LL +         x + 1
+   |
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/iterators/invalid-iterator-chain.stderr b/tests/ui/iterators/invalid-iterator-chain.stderr
index 2601c9c0d69..4dc13086912 100644
--- a/tests/ui/iterators/invalid-iterator-chain.stderr
+++ b/tests/ui/iterators/invalid-iterator-chain.stderr
@@ -1,6 +1,8 @@
 error[E0277]: a value of type `Vec<X>` cannot be built from an iterator over elements of type `&X`
   --> $DIR/invalid-iterator-chain.rs:6:7
    |
+LL |     let i = i.map(|x| x.clone());
+   |                         ------- this method call is cloning the reference `&X`, not `X` which doesn't implement `Clone`
 LL |     i.collect()
    |       ^^^^^^^ value of type `Vec<X>` cannot be built from `std::iter::Iterator<Item=&X>`
    |
@@ -16,6 +18,10 @@ LL |     let i = i.map(|x| x.clone());
    |               ------------------ `Iterator::Item` remains `&X` here
 note: required by a bound in `collect`
   --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+help: consider further restricting type parameter `X`
+   |
+LL | fn iter_to_vec<'b, X>(i: Iter<'b, X>) -> Vec<X> where X: Clone {
+   |                                                 ++++++++++++++
 
 error[E0277]: a value of type `i32` cannot be made by summing an iterator over elements of type `()`
   --> $DIR/invalid-iterator-chain.rs:15:33
@@ -43,6 +49,11 @@ LL | |         });
    | |__________^ `Iterator::Item` changed to `()` here
 note: required by a bound in `std::iter::Iterator::sum`
   --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+help: consider removing this semicolon
+   |
+LL -             a + b;
+LL +             a + b
+   |
 
 error[E0277]: a value of type `i32` cannot be made by summing an iterator over elements of type `()`
   --> $DIR/invalid-iterator-chain.rs:26:20
@@ -77,6 +88,11 @@ LL |             .map(|x| { x; })
    |              ^^^^^^^^^^^^^^^ `Iterator::Item` changed to `()` here
 note: required by a bound in `std::iter::Iterator::sum`
   --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+help: consider removing this semicolon
+   |
+LL -             .map(|x| { x; })
+LL +             .map(|x| { x })
+   |
 
 error[E0277]: a value of type `i32` cannot be made by summing an iterator over elements of type `f64`
   --> $DIR/invalid-iterator-chain.rs:36:20
@@ -130,6 +146,11 @@ LL |     println!("{}", vec![0, 1].iter().map(|x| { x; }).sum::<i32>());
    |                    this expression has type `Vec<{integer}>`
 note: required by a bound in `std::iter::Iterator::sum`
   --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+help: consider removing this semicolon
+   |
+LL -     println!("{}", vec![0, 1].iter().map(|x| { x; }).sum::<i32>());
+LL +     println!("{}", vec![0, 1].iter().map(|x| { x }).sum::<i32>());
+   |
 
 error[E0277]: a value of type `i32` cannot be made by summing an iterator over elements of type `&()`
   --> $DIR/invalid-iterator-chain.rs:39:46
@@ -182,6 +203,11 @@ LL |       let f = e.filter(|_| false);
    |                 ----------------- `Iterator::Item` remains `()` here
 note: required by a bound in `collect`
   --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+help: consider removing this semicolon
+   |
+LL -         x + 1;
+LL +         x + 1
+   |
 
 error: aborting due to 7 previous errors