]> git.lizzy.rs Git - rust.git/commitdiff
Detect `::` -> `:` typo in type argument
authorEsteban Kuber <esteban@kuber.com.ar>
Tue, 11 Jan 2022 21:56:10 +0000 (21:56 +0000)
committerEsteban Kuber <esteban@kuber.com.ar>
Wed, 2 Feb 2022 01:19:24 +0000 (01:19 +0000)
When writing `Vec<A:B>`, suggest `Vec<A::B>`.

compiler/rustc_resolve/src/late.rs
compiler/rustc_resolve/src/late/diagnostics.rs
src/test/ui/suggestions/type-ascription-instead-of-path-in-type.rs [new file with mode: 0644]
src/test/ui/suggestions/type-ascription-instead-of-path-in-type.stderr [new file with mode: 0644]

index 2c678e71ae179de0defee914d8f3e454bd9f319a..6aed3223480f1a93645fb95f00ddeec878a0a0f0 100644 (file)
@@ -400,6 +400,8 @@ struct DiagnosticMetadata<'ast> {
 
     /// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`.
     current_where_predicate: Option<&'ast WherePredicate>,
+
+    current_type_path: Option<&'ast Ty>,
 }
 
 struct LateResolutionVisitor<'a, 'b, 'ast> {
@@ -472,8 +474,10 @@ fn visit_local(&mut self, local: &'ast Local) {
     }
     fn visit_ty(&mut self, ty: &'ast Ty) {
         let prev = self.diagnostic_metadata.current_trait_object;
+        let prev_ty = self.diagnostic_metadata.current_type_path;
         match ty.kind {
             TyKind::Path(ref qself, ref path) => {
+                self.diagnostic_metadata.current_type_path = Some(ty);
                 self.smart_resolve_path(ty.id, qself.as_ref(), path, PathSource::Type);
             }
             TyKind::ImplicitSelf => {
@@ -490,6 +494,7 @@ fn visit_ty(&mut self, ty: &'ast Ty) {
         }
         visit::walk_ty(self, ty);
         self.diagnostic_metadata.current_trait_object = prev;
+        self.diagnostic_metadata.current_type_path = prev_ty;
     }
     fn visit_poly_trait_ref(&mut self, tref: &'ast PolyTraitRef, m: &'ast TraitBoundModifier) {
         self.smart_resolve_path(
@@ -1936,7 +1941,6 @@ fn smart_resolve_path_fragment(
                 let instead = res.is_some();
                 let suggestion =
                     if res.is_none() { this.report_missing_type_error(path) } else { None };
-                // get_from_node_id
 
                 this.r.use_injections.push(UseError {
                     err,
index 7e1e5c788052bfb2f342f3abef5b7b745a25955e..d05f139e3bf5aaf9876a78708a614b766b967146 100644 (file)
@@ -26,6 +26,7 @@
 use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP};
 
 use std::iter;
+use std::ops::Deref;
 
 use tracing::debug;
 
@@ -265,6 +266,8 @@ pub(crate) fn smart_resolve_report_errors(
             }
         }
 
+        self.detect_assoct_type_constraint_meant_as_path(base_span, &mut err);
+
         // Emit special messages for unresolved `Self` and `self`.
         if is_self_type(path, ns) {
             err.code(rustc_errors::error_code!(E0411));
@@ -603,6 +606,40 @@ pub(crate) fn smart_resolve_report_errors(
         (err, candidates)
     }
 
+    fn detect_assoct_type_constraint_meant_as_path(
+        &self,
+        base_span: Span,
+        err: &mut DiagnosticBuilder<'_>,
+    ) {
+        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 {
+                        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",
+                            "::".to_string(),
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                }
+            }
+        }
+    }
+
     fn get_single_associated_item(
         &mut self,
         path: &[Segment],
diff --git a/src/test/ui/suggestions/type-ascription-instead-of-path-in-type.rs b/src/test/ui/suggestions/type-ascription-instead-of-path-in-type.rs
new file mode 100644 (file)
index 0000000..48d19f6
--- /dev/null
@@ -0,0 +1,14 @@
+enum A {
+    B,
+}
+
+fn main() {
+    let _: Vec<A:B> = A::B;
+    //~^ ERROR cannot find trait `B` in this scope
+    //~| HELP you might have meant to write a path instead of an associated type bound
+    //~| ERROR associated type bounds are unstable
+    //~| HELP add `#![feature(associated_type_bounds)]` to the crate attributes to enable
+    //~| ERROR struct takes at least 1 generic argument but 0 generic arguments were supplied
+    //~| HELP add missing generic argument
+    //~| ERROR associated type bindings are not allowed here
+}
diff --git a/src/test/ui/suggestions/type-ascription-instead-of-path-in-type.stderr b/src/test/ui/suggestions/type-ascription-instead-of-path-in-type.stderr
new file mode 100644 (file)
index 0000000..951ff23
--- /dev/null
@@ -0,0 +1,46 @@
+error[E0405]: cannot find trait `B` in this scope
+  --> $DIR/type-ascription-instead-of-path-in-type.rs:6:18
+   |
+LL |     let _: Vec<A:B> = A::B;
+   |                  ^ not found in this scope
+   |
+help: you might have meant to write a path instead of an associated type bound
+   |
+LL |     let _: Vec<A::B> = A::B;
+   |                 ~~
+
+error[E0658]: associated type bounds are unstable
+  --> $DIR/type-ascription-instead-of-path-in-type.rs:6:16
+   |
+LL |     let _: Vec<A:B> = A::B;
+   |                ^^^
+   |
+   = note: see issue #52662 <https://github.com/rust-lang/rust/issues/52662> for more information
+   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
+
+error[E0107]: this struct takes at least 1 generic argument but 0 generic arguments were supplied
+  --> $DIR/type-ascription-instead-of-path-in-type.rs:6:12
+   |
+LL |     let _: Vec<A:B> = A::B;
+   |            ^^^ expected at least 1 generic argument
+   |
+note: struct defined here, with at least 1 generic parameter: `T`
+  --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+   |
+LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
+   |            ^^^ -
+help: add missing generic argument
+   |
+LL |     let _: Vec<T, A:B> = A::B;
+   |                ++
+
+error[E0229]: associated type bindings are not allowed here
+  --> $DIR/type-ascription-instead-of-path-in-type.rs:6:16
+   |
+LL |     let _: Vec<A:B> = A::B;
+   |                ^^^ associated type not allowed here
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0107, E0229, E0405, E0658.
+For more information about an error, try `rustc --explain E0107`.