about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs434
1 files changed, 261 insertions, 173 deletions
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index c3c9b0b5617..ac115360257 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -130,6 +130,15 @@ pub(super) enum LifetimeElisionCandidate {
     Missing(MissingLifetime),
 }
 
+struct BaseError<'a> {
+    msg: String,
+    fallback_label: String,
+    span: Span,
+    span_label: Option<(Span, &'a str)>,
+    could_be_expr: bool,
+    suggestion: Option<(Span, &'a str, String)>,
+}
+
 impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
     fn def_span(&self, def_id: DefId) -> Option<Span> {
         match def_id.krate {
@@ -138,35 +147,18 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
         }
     }
 
-    /// Handles error reporting for `smart_resolve_path_fragment` function.
-    /// Creates base error and amends it with one short label and possibly some longer helps/notes.
-    pub(crate) fn smart_resolve_report_errors(
+    fn make_base_error(
         &mut self,
         path: &[Segment],
         span: Span,
         source: PathSource<'_>,
         res: Option<Res>,
-    ) -> (DiagnosticBuilder<'a, ErrorGuaranteed>, Vec<ImportSuggestion>) {
-        let ident_span = path.last().map_or(span, |ident| ident.ident.span);
-        let ns = source.namespace();
-        let is_expected = &|res| source.is_expected(res);
-        let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _));
-
-        debug!(?res, ?source);
-
+    ) -> BaseError<'static> {
         // Make the base error.
-        struct BaseError<'a> {
-            msg: String,
-            fallback_label: String,
-            span: Span,
-            span_label: Option<(Span, &'a str)>,
-            could_be_expr: bool,
-            suggestion: Option<(Span, &'a str, String)>,
-        }
         let mut expected = source.descr_expected();
         let path_str = Segment::names_to_string(path);
         let item_str = path.last().unwrap().ident;
-        let base_error = if let Some(res) = res {
+        if let Some(res) = res {
             BaseError {
                 msg: format!("expected {}, found {} `{}`", expected, res.descr(), path_str),
                 fallback_label: format!("not a {expected}"),
@@ -277,53 +269,92 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                 could_be_expr: false,
                 suggestion,
             }
-        };
+        }
+    }
 
+    /// Handles error reporting for `smart_resolve_path_fragment` function.
+    /// Creates base error and amends it with one short label and possibly some longer helps/notes.
+    pub(crate) fn smart_resolve_report_errors(
+        &mut self,
+        path: &[Segment],
+        span: Span,
+        source: PathSource<'_>,
+        res: Option<Res>,
+    ) -> (DiagnosticBuilder<'a, ErrorGuaranteed>, Vec<ImportSuggestion>) {
+        debug!(?res, ?source);
+        let base_error = self.make_base_error(path, span, source, res);
         let code = source.error_code(res.is_some());
         let mut err =
             self.r.session.struct_span_err_with_code(base_error.span, &base_error.msg, code);
 
         self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span);
 
-        if let Some((span, label)) = base_error.span_label {
+        if let Some((span, label)) = base_error.span_label.clone() {
             err.span_label(span, label);
         }
 
-        if let Some(sugg) = base_error.suggestion {
-            err.span_suggestion_verbose(sugg.0, sugg.1, sugg.2, Applicability::MaybeIncorrect);
+        if let Some(ref sugg) = base_error.suggestion {
+            err.span_suggestion_verbose(sugg.0, sugg.1, &sugg.2, Applicability::MaybeIncorrect);
         }
 
-        if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal {
-            err.multipart_suggestion(
-                "you might have meant to write a `struct` literal",
-                vec![
-                    (span.shrink_to_lo(), "{ SomeStruct ".to_string()),
-                    (span.shrink_to_hi(), "}".to_string()),
-                ],
-                Applicability::HasPlaceholders,
-            );
+        self.suggest_bare_struct_literal(&mut err);
+        self.suggest_pattern_match_with_let(&mut err, source, span);
+
+        self.suggest_self_or_self_ref(&mut err, path, span);
+        self.detect_assoct_type_constraint_meant_as_path(&mut err, &base_error);
+        if self.suggest_self_ty_or_self_value(&mut err, source, path, span) {
+            return (err, Vec::new());
         }
-        match (source, self.diagnostic_metadata.in_if_condition) {
-            (
-                PathSource::Expr(_),
-                Some(Expr { span: expr_span, kind: ExprKind::Assign(lhs, _, _), .. }),
-            ) => {
-                // Icky heuristic so we don't suggest:
-                // `if (i + 2) = 2` => `if let (i + 2) = 2` (approximately pattern)
-                // `if 2 = i` => `if let 2 = i` (lhs needs to contain error span)
-                if lhs.is_approximately_pattern() && lhs.span.contains(span) {
-                    err.span_suggestion_verbose(
-                        expr_span.shrink_to_lo(),
-                        "you might have meant to use pattern matching",
-                        "let ",
-                        Applicability::MaybeIncorrect,
-                    );
+
+        let (found, candidates) =
+            self.try_lookup_name_with_relex_fashion(&mut err, source, path, span, res, &base_error);
+        if found {
+            return (err, candidates);
+        }
+
+        self.suggest_type_ascription(&mut err, source, path, res, span, &base_error);
+        self.add_err_code_cases(&mut err, source, path, span);
+
+        (err, candidates)
+    }
+
+    fn detect_assoct_type_constraint_meant_as_path(
+        &self,
+        err: &mut Diagnostic,
+        base_error: &BaseError<'static>,
+    ) {
+        let Some(ty) = self.diagnostic_metadata.current_type_path else { return; };
+        let TyKind::Path(_, path) = &ty.kind else { return; };
+        for segment in &path.segments {
+            let Some(params) = &segment.args else { continue; };
+            let ast::GenericArgs::AngleBracketed(ref params) = params.deref() else { continue; };
+            for param in &params.args {
+                let ast::AngleBracketedArg::Constraint(constraint) = param else { continue; };
+                let ast::AssocConstraintKind::Bound { bounds } = &constraint.kind else {
+                    continue;
+                };
+                for bound in bounds {
+                    let ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifier::None)
+                        = bound else
+                    {
+                        continue;
+                    };
+                    if base_error.span == trait_ref.span {
+                        err.span_suggestion_verbose(
+                            constraint.ident.span.between(trait_ref.span),
+                            "you might have meant to write a path instead of an associated type bound",
+                            "::",
+                            Applicability::MachineApplicable,
+                        );
+                    }
                 }
             }
-            _ => {}
         }
+    }
 
+    fn suggest_self_or_self_ref(&mut self, err: &mut Diagnostic, path: &[Segment], span: Span) {
         let is_assoc_fn = self.self_type_is_available();
+        let item_str = path.last().unwrap().ident;
         // Emit help message for fake-self from other languages (e.g., `this` in Javascript).
         if ["this", "my"].contains(&item_str.as_str()) && is_assoc_fn {
             err.span_suggestion_short(
@@ -358,96 +389,25 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                 }
             }
         }
+    }
 
-        self.detect_assoct_type_constraint_meant_as_path(base_error.span, &mut err);
-
-        // Emit special messages for unresolved `Self` and `self`.
-        if is_self_type(path, ns) {
-            err.code(rustc_errors::error_code!(E0411));
-            err.span_label(
-                span,
-                "`Self` is only available in impls, traits, and type definitions".to_string(),
-            );
-            if let Some(item_kind) = self.diagnostic_metadata.current_item {
-                err.span_label(
-                    item_kind.ident.span,
-                    format!(
-                        "`Self` not allowed in {} {}",
-                        item_kind.kind.article(),
-                        item_kind.kind.descr()
-                    ),
-                );
-            }
-            return (err, Vec::new());
-        }
-        if is_self_value(path, ns) {
-            debug!("smart_resolve_path_fragment: E0424, source={:?}", source);
-
-            err.code(rustc_errors::error_code!(E0424));
-            err.span_label(span, match source {
-                PathSource::Pat => "`self` value is a keyword and may not be bound to variables or shadowed",
-                _ => "`self` value is a keyword only available in methods with a `self` parameter",
-            });
-            if let Some((fn_kind, span)) = &self.diagnostic_metadata.current_function {
-                // The current function has a `self' parameter, but we were unable to resolve
-                // a reference to `self`. This can only happen if the `self` identifier we
-                // are resolving came from a different hygiene context.
-                if fn_kind.decl().inputs.get(0).map_or(false, |p| p.is_self()) {
-                    err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters");
-                } else {
-                    let doesnt = if is_assoc_fn {
-                        let (span, sugg) = fn_kind
-                            .decl()
-                            .inputs
-                            .get(0)
-                            .map(|p| (p.span.shrink_to_lo(), "&self, "))
-                            .unwrap_or_else(|| {
-                                // Try to look for the "(" after the function name, if possible.
-                                // This avoids placing the suggestion into the visibility specifier.
-                                let span = fn_kind
-                                    .ident()
-                                    .map_or(*span, |ident| span.with_lo(ident.span.hi()));
-                                (
-                                    self.r
-                                        .session
-                                        .source_map()
-                                        .span_through_char(span, '(')
-                                        .shrink_to_hi(),
-                                    "&self",
-                                )
-                            });
-                        err.span_suggestion_verbose(
-                            span,
-                            "add a `self` receiver parameter to make the associated `fn` a method",
-                            sugg,
-                            Applicability::MaybeIncorrect,
-                        );
-                        "doesn't"
-                    } else {
-                        "can't"
-                    };
-                    if let Some(ident) = fn_kind.ident() {
-                        err.span_label(
-                            ident.span,
-                            &format!("this function {} have a `self` parameter", doesnt),
-                        );
-                    }
-                }
-            } else if let Some(item_kind) = self.diagnostic_metadata.current_item {
-                err.span_label(
-                    item_kind.ident.span,
-                    format!(
-                        "`self` not allowed in {} {}",
-                        item_kind.kind.article(),
-                        item_kind.kind.descr()
-                    ),
-                );
-            }
-            return (err, Vec::new());
-        }
-
+    fn try_lookup_name_with_relex_fashion(
+        &mut self,
+        err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+        source: PathSource<'_>,
+        path: &[Segment],
+        span: Span,
+        res: Option<Res>,
+        base_error: &BaseError<'static>,
+    ) -> (bool, Vec<ImportSuggestion>) {
         // Try to lookup name in more relaxed fashion for better error reporting.
         let ident = path.last().unwrap().ident;
+        let is_expected = &|res| source.is_expected(res);
+        let ns = source.namespace();
+        let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _));
+        let path_str = Segment::names_to_string(path);
+        let ident_span = path.last().map_or(span, |ident| ident.ident.span);
+
         let mut candidates = self
             .r
             .lookup_import_candidates(ident, ns, &self.parent_scope, is_expected)
@@ -494,7 +454,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                     {
                         // Already reported this issue on the lhs of the type ascription.
                         err.delay_as_bug();
-                        return (err, candidates);
+                        return (true, candidates);
                     }
                 }
 
@@ -522,8 +482,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                 );
             }
         }
+
         // Try Levenshtein algorithm.
-        let typo_sugg = self.lookup_typo_candidate(path, ns, is_expected);
+        let typo_sugg = self.lookup_typo_candidate(path, source.namespace(), is_expected);
         if path.len() == 1 && self.self_type_is_available() {
             if let Some(candidate) = self.lookup_assoc_candidate(ident, ns, is_expected) {
                 let self_is_available = self.self_value_is_available(path[0].ident.span);
@@ -560,8 +521,8 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                         );
                     }
                 }
-                self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span);
-                return (err, candidates);
+                self.r.add_typo_suggestion(err, typo_sugg, ident_span);
+                return (true, candidates);
             }
 
             // If the first argument in call is `self` suggest calling a method.
@@ -579,14 +540,14 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                     format!("self.{path_str}({args_snippet})"),
                     Applicability::MachineApplicable,
                 );
-                return (err, candidates);
+                return (true, candidates);
             }
         }
 
         // Try context-dependent help if relaxed lookup didn't work.
         if let Some(res) = res {
             if self.smart_resolve_context_dependent_help(
-                &mut err,
+                err,
                 span,
                 source,
                 res,
@@ -594,14 +555,25 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                 &base_error.fallback_label,
             ) {
                 // We do this to avoid losing a secondary span when we override the main error span.
-                self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span);
-                return (err, candidates);
+                self.r.add_typo_suggestion(err, typo_sugg, ident_span);
+                return (true, candidates);
             }
         }
+        return (false, candidates);
+    }
 
+    fn suggest_type_ascription(
+        &mut self,
+        err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+        source: PathSource<'_>,
+        path: &[Segment],
+        res: Option<Res>,
+        span: Span,
+        base_error: &BaseError<'static>,
+    ) {
         let is_macro =
             base_error.span.from_expansion() && base_error.span.desugaring_kind().is_none();
-        if !self.type_ascription_suggestion(&mut err, base_error.span) {
+        if !self.type_ascription_suggestion(err, base_error.span) {
             let mut fallback = false;
             if let (
                 PathSource::Trait(AliasPossibility::Maybe),
@@ -663,9 +635,12 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                 }
             }
 
-            fallback |= self.restrict_assoc_type_in_where_clause(span, &mut err);
+            let is_expected = &|res| source.is_expected(res);
+            let ident_span = path.last().map_or(span, |ident| ident.ident.span);
+            fallback |= self.restrict_assoc_type_in_where_clause(span, err);
 
-            if !self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span) {
+            let typo_sugg = self.lookup_typo_candidate(path, source.namespace(), is_expected);
+            if !self.r.add_typo_suggestion(err, typo_sugg, ident_span) {
                 fallback = true;
                 match self.diagnostic_metadata.current_let_binding {
                     Some((pat_sp, Some(ty_sp), None))
@@ -683,17 +658,27 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
 
                 // If the trait has a single item (which wasn't matched by Levenshtein), suggest it
                 let suggestion = self.get_single_associated_item(&path, &source, is_expected);
-                self.r.add_typo_suggestion(&mut err, suggestion, ident_span);
+                self.r.add_typo_suggestion(err, suggestion, ident_span);
             }
             if fallback {
                 // Fallback label.
-                err.span_label(base_error.span, base_error.fallback_label);
+                err.span_label(base_error.span, &base_error.fallback_label);
             }
         }
+    }
+
+    fn add_err_code_cases(
+        &mut self,
+        err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+        source: PathSource<'_>,
+        path: &[Segment],
+        span: Span,
+    ) {
         if let Some(err_code) = &err.code {
             if err_code == &rustc_errors::error_code!(E0425) {
                 for label_rib in &self.label_ribs {
                     for (label_ident, node_id) in &label_rib.bindings {
+                        let ident = path.last().unwrap().ident;
                         if format!("'{}", ident) == label_ident.to_string() {
                             err.span_label(label_ident.span, "a label with a similar name exists");
                             if let PathSource::Expr(Some(Expr {
@@ -724,38 +709,106 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                 }
             }
         }
-
-        (err, candidates)
     }
 
-    fn detect_assoct_type_constraint_meant_as_path(&self, base_span: Span, err: &mut Diagnostic) {
-        let Some(ty) = self.diagnostic_metadata.current_type_path else { return; };
-        let TyKind::Path(_, path) = &ty.kind else { return; };
-        for segment in &path.segments {
-            let Some(params) = &segment.args else { continue; };
-            let ast::GenericArgs::AngleBracketed(ref params) = params.deref() else { continue; };
-            for param in &params.args {
-                let ast::AngleBracketedArg::Constraint(constraint) = param else { continue; };
-                let ast::AssocConstraintKind::Bound { bounds } = &constraint.kind else {
-                    continue;
-                };
-                for bound in bounds {
-                    let ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifier::None)
-                        = bound else
-                    {
-                        continue;
-                    };
-                    if base_span == trait_ref.span {
+    /// Emit special messages for unresolved `Self` and `self`.
+    fn suggest_self_ty_or_self_value(
+        &mut self,
+        err: &mut Diagnostic,
+        source: PathSource<'_>,
+        path: &[Segment],
+        span: Span,
+    ) -> bool {
+        if is_self_type(path, source.namespace()) {
+            err.code(rustc_errors::error_code!(E0411));
+            err.span_label(
+                span,
+                "`Self` is only available in impls, traits, and type definitions".to_string(),
+            );
+            if let Some(item_kind) = self.diagnostic_metadata.current_item {
+                err.span_label(
+                    item_kind.ident.span,
+                    format!(
+                        "`Self` not allowed in {} {}",
+                        item_kind.kind.article(),
+                        item_kind.kind.descr()
+                    ),
+                );
+            }
+            return true;
+        }
+
+        if is_self_value(path, source.namespace()) {
+            debug!("smart_resolve_path_fragment: E0424, source={:?}", source);
+            err.code(rustc_errors::error_code!(E0424));
+            err.span_label(
+            span,
+            match source {
+                PathSource::Pat => {
+                    "`self` value is a keyword and may not be bound to variables or shadowed"
+                }
+                _ => "`self` value is a keyword only available in methods with a `self` parameter",
+            },
+            );
+            let is_assoc_fn = self.self_type_is_available();
+            if let Some((fn_kind, span)) = &self.diagnostic_metadata.current_function {
+                // The current function has a `self' parameter, but we were unable to resolve
+                // a reference to `self`. This can only happen if the `self` identifier we
+                // are resolving came from a different hygiene context.
+                if fn_kind.decl().inputs.get(0).map_or(false, |p| p.is_self()) {
+                    err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters");
+                } else {
+                    let doesnt = if is_assoc_fn {
+                        let (span, sugg) = fn_kind
+                            .decl()
+                            .inputs
+                            .get(0)
+                            .map(|p| (p.span.shrink_to_lo(), "&self, "))
+                            .unwrap_or_else(|| {
+                                // Try to look for the "(" after the function name, if possible.
+                                // This avoids placing the suggestion into the visibility specifier.
+                                let span = fn_kind
+                                    .ident()
+                                    .map_or(*span, |ident| span.with_lo(ident.span.hi()));
+                                (
+                                    self.r
+                                        .session
+                                        .source_map()
+                                        .span_through_char(span, '(')
+                                        .shrink_to_hi(),
+                                    "&self",
+                                )
+                            });
                         err.span_suggestion_verbose(
-                            constraint.ident.span.between(trait_ref.span),
-                            "you might have meant to write a path instead of an associated type bound",
-                            "::",
-                            Applicability::MachineApplicable,
+                            span,
+                            "add a `self` receiver parameter to make the associated `fn` a method",
+                            sugg,
+                            Applicability::MaybeIncorrect,
+                        );
+                        "doesn't"
+                    } else {
+                        "can't"
+                    };
+                    if let Some(ident) = fn_kind.ident() {
+                        err.span_label(
+                            ident.span,
+                            &format!("this function {} have a `self` parameter", doesnt),
                         );
                     }
                 }
+            } else if let Some(item_kind) = self.diagnostic_metadata.current_item {
+                err.span_label(
+                    item_kind.ident.span,
+                    format!(
+                        "`self` not allowed in {} {}",
+                        item_kind.kind.article(),
+                        item_kind.kind.descr()
+                    ),
+                );
             }
+            return true;
         }
+        false
     }
 
     fn suggest_swapping_misplaced_self_ty_and_trait(
@@ -787,6 +840,41 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
         }
     }
 
+    fn suggest_bare_struct_literal(&mut self, err: &mut Diagnostic) {
+        if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal {
+            err.multipart_suggestion(
+                "you might have meant to write a `struct` literal",
+                vec![
+                    (span.shrink_to_lo(), "{ SomeStruct ".to_string()),
+                    (span.shrink_to_hi(), "}".to_string()),
+                ],
+                Applicability::HasPlaceholders,
+            );
+        }
+    }
+
+    fn suggest_pattern_match_with_let(
+        &mut self,
+        err: &mut Diagnostic,
+        source: PathSource<'_>,
+        span: Span,
+    ) {
+        if let PathSource::Expr(_) = source &&
+        let Some(Expr { span: expr_span, kind: ExprKind::Assign(lhs, _, _), .. } ) = self.diagnostic_metadata.in_if_condition {
+            // Icky heuristic so we don't suggest:
+            // `if (i + 2) = 2` => `if let (i + 2) = 2` (approximately pattern)
+            // `if 2 = i` => `if let 2 = i` (lhs needs to contain error span)
+            if lhs.is_approximately_pattern() && lhs.span.contains(span) {
+                err.span_suggestion_verbose(
+                    expr_span.shrink_to_lo(),
+                    "you might have meant to use pattern matching",
+                    "let ",
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        }
+    }
+
     fn get_single_associated_item(
         &mut self,
         path: &[Segment],