about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeón Orell Valerian Liehr <me@fmease.dev>2024-01-02 00:57:30 +0100
committerLeón Orell Valerian Liehr <me@fmease.dev>2024-01-02 13:49:48 +0100
commitaa799049d731f2922c237b27211d4e19b4db918b (patch)
tree142010aeb8d07dec4dc7f0370072a279be4cbbec
parentae8e401c9f8b51dc8ff3ac5914e0203ea95e26d2 (diff)
downloadrust-aa799049d731f2922c237b27211d4e19b4db918b.tar.gz
rust-aa799049d731f2922c237b27211d4e19b4db918b.zip
E0379: Provide suggestions
-rw-r--r--compiler/rustc_ast_passes/messages.ftl7
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs130
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs19
-rw-r--r--tests/ui/consts/const-fn-mismatch.stderr5
-rw-r--r--tests/ui/consts/const-fn-not-in-trait.stderr10
-rw-r--r--tests/ui/consts/issue-54954.stderr5
-rw-r--r--tests/ui/feature-gates/feature-gate-min_const_fn.stderr15
-rw-r--r--tests/ui/mismatched_types/const-fn-in-trait.stderr10
-rw-r--r--tests/ui/parser/fn-header-semantic-fail.stderr20
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/effects/trait-fn-const.rs10
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/effects/trait-fn-const.stderr48
11 files changed, 228 insertions, 51 deletions
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index b7274db029f..feea02c679c 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -241,6 +241,13 @@ ast_passes_trait_fn_const =
         [true] trait impls
         *[false] traits
     } cannot be const
+    .const_context_label = this declares all associated functions implicitly const
+    .remove_const_sugg = remove the `const`{$requires_multiple_changes ->
+        [true] {" ..."}
+        *[false] {""}
+    }
+    .make_impl_const_sugg = ... and declare the impl to be const instead
+    .make_trait_const_sugg = ... and declare the trait to be a `#[const_trait]` instead
 
 ast_passes_trait_object_single_bound = only a single explicit lifetime bound is permitted
 
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 39ea684087e..b69d4cccaf0 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -46,6 +46,21 @@ enum DisallowTildeConstContext<'a> {
     Item,
 }
 
+enum TraitOrTraitImpl<'a> {
+    Trait { span: Span, constness: Option<Span> },
+    TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref: &'a TraitRef },
+}
+
+impl<'a> TraitOrTraitImpl<'a> {
+    fn constness(&self) -> Option<Span> {
+        match self {
+            Self::Trait { constness: Some(span), .. }
+            | Self::TraitImpl { constness: Const::Yes(span), .. } => Some(*span),
+            _ => None,
+        }
+    }
+}
+
 struct AstValidator<'a> {
     session: &'a Session,
     features: &'a Features,
@@ -53,11 +68,7 @@ struct AstValidator<'a> {
     /// The span of the `extern` in an `extern { ... }` block, if any.
     extern_mod: Option<&'a Item>,
 
-    /// Are we inside a trait impl?
-    in_trait_impl: bool,
-
-    /// Are we inside a const trait defn or impl?
-    in_const_trait_or_impl: bool,
+    outer_trait_or_trait_impl: Option<TraitOrTraitImpl<'a>>,
 
     has_proc_macro_decls: bool,
 
@@ -78,24 +89,28 @@ struct AstValidator<'a> {
 impl<'a> AstValidator<'a> {
     fn with_in_trait_impl(
         &mut self,
-        is_in: bool,
-        constness: Option<Const>,
+        trait_: Option<(Const, ImplPolarity, &'a TraitRef)>,
         f: impl FnOnce(&mut Self),
     ) {
-        let old = mem::replace(&mut self.in_trait_impl, is_in);
-        let old_const = mem::replace(
-            &mut self.in_const_trait_or_impl,
-            matches!(constness, Some(Const::Yes(_))),
+        let old = mem::replace(
+            &mut self.outer_trait_or_trait_impl,
+            trait_.map(|(constness, polarity, trait_ref)| TraitOrTraitImpl::TraitImpl {
+                constness,
+                polarity,
+                trait_ref,
+            }),
         );
         f(self);
-        self.in_trait_impl = old;
-        self.in_const_trait_or_impl = old_const;
+        self.outer_trait_or_trait_impl = old;
     }
 
-    fn with_in_trait(&mut self, is_const: bool, f: impl FnOnce(&mut Self)) {
-        let old = mem::replace(&mut self.in_const_trait_or_impl, is_const);
+    fn with_in_trait(&mut self, span: Span, constness: Option<Span>, f: impl FnOnce(&mut Self)) {
+        let old = mem::replace(
+            &mut self.outer_trait_or_trait_impl,
+            Some(TraitOrTraitImpl::Trait { span, constness }),
+        );
         f(self);
-        self.in_const_trait_or_impl = old;
+        self.outer_trait_or_trait_impl = old;
     }
 
     fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) {
@@ -291,10 +306,48 @@ impl<'a> AstValidator<'a> {
         }
     }
 
-    fn check_trait_fn_not_const(&self, constness: Const) {
-        if let Const::Yes(span) = constness {
-            self.dcx().emit_err(errors::TraitFnConst { span, in_impl: self.in_trait_impl });
-        }
+    fn check_trait_fn_not_const(&self, constness: Const, parent: &TraitOrTraitImpl<'a>) {
+        let Const::Yes(span) = constness else {
+            return;
+        };
+
+        let make_impl_const_sugg = if self.features.const_trait_impl
+            && let TraitOrTraitImpl::TraitImpl {
+                constness: Const::No,
+                polarity: ImplPolarity::Positive,
+                trait_ref,
+            } = parent
+        {
+            Some(trait_ref.path.span.shrink_to_lo())
+        } else {
+            None
+        };
+
+        let make_trait_const_sugg = if self.features.const_trait_impl
+            && let TraitOrTraitImpl::Trait { span, constness: None } = parent
+        {
+            Some(span.shrink_to_lo())
+        } else {
+            None
+        };
+
+        let parent_constness = parent.constness();
+        self.dcx().emit_err(errors::TraitFnConst {
+            span,
+            in_impl: matches!(parent, TraitOrTraitImpl::TraitImpl { .. }),
+            const_context_label: parent_constness,
+            remove_const_sugg: (
+                self.session.source_map().span_extend_while(span, |c| c == ' ').unwrap_or(span),
+                match parent_constness {
+                    Some(_) => rustc_errors::Applicability::MachineApplicable,
+                    None => rustc_errors::Applicability::MaybeIncorrect,
+                },
+            ),
+            requires_multiple_changes: make_impl_const_sugg.is_some()
+                || make_trait_const_sugg.is_some(),
+            make_impl_const_sugg,
+            make_trait_const_sugg,
+        });
     }
 
     fn check_fn_decl(&self, fn_decl: &FnDecl, self_semantic: SelfSemantic) {
@@ -817,7 +870,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 self_ty,
                 items,
             }) => {
-                self.with_in_trait_impl(true, Some(*constness), |this| {
+                self.with_in_trait_impl(Some((*constness, *polarity, t)), |this| {
                     this.visibility_not_permitted(
                         &item.vis,
                         errors::VisibilityNotPermittedNote::TraitImpl,
@@ -963,8 +1016,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 }
             }
             ItemKind::Trait(box Trait { is_auto, generics, bounds, items, .. }) => {
-                let is_const_trait = attr::contains_name(&item.attrs, sym::const_trait);
-                self.with_in_trait(is_const_trait, |this| {
+                let is_const_trait =
+                    attr::find_by_name(&item.attrs, sym::const_trait).map(|attr| attr.span);
+                self.with_in_trait(item.span, is_const_trait, |this| {
                     if *is_auto == IsAuto::Yes {
                         // Auto traits cannot have generics, super traits nor contain items.
                         this.deny_generic_params(generics, item.ident.span);
@@ -977,8 +1031,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     // context for the supertraits.
                     this.visit_vis(&item.vis);
                     this.visit_ident(item.ident);
-                    let disallowed =
-                        (!is_const_trait).then(|| DisallowTildeConstContext::Trait(item.span));
+                    let disallowed = is_const_trait
+                        .is_none()
+                        .then(|| DisallowTildeConstContext::Trait(item.span));
                     this.with_tilde_const(disallowed, |this| {
                         this.visit_generics(generics);
                         walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
@@ -1342,7 +1397,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
 
         let tilde_const_allowed =
             matches!(fk.header(), Some(FnHeader { constness: ast::Const::Yes(_), .. }))
-                || matches!(fk.ctxt(), Some(FnCtxt::Assoc(_)) if self.in_const_trait_or_impl);
+                || matches!(fk.ctxt(), Some(FnCtxt::Assoc(_)))
+                    && self
+                        .outer_trait_or_trait_impl
+                        .as_ref()
+                        .and_then(TraitOrTraitImpl::constness)
+                        .is_some();
 
         let disallowed = (!tilde_const_allowed).then(|| DisallowTildeConstContext::Fn(fk));
         self.with_tilde_const(disallowed, |this| visit::walk_fn(this, fk));
@@ -1353,7 +1413,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             self.check_nomangle_item_asciionly(item.ident, item.span);
         }
 
-        if ctxt == AssocCtxt::Trait || !self.in_trait_impl {
+        if ctxt == AssocCtxt::Trait || self.outer_trait_or_trait_impl.is_none() {
             self.check_defaultness(item.span, item.kind.defaultness());
         }
 
@@ -1401,10 +1461,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             );
         }
 
-        if ctxt == AssocCtxt::Trait || self.in_trait_impl {
+        if let Some(parent) = &self.outer_trait_or_trait_impl {
             self.visibility_not_permitted(&item.vis, errors::VisibilityNotPermittedNote::TraitImpl);
             if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind {
-                self.check_trait_fn_not_const(sig.header.constness);
+                self.check_trait_fn_not_const(sig.header.constness, parent);
             }
         }
 
@@ -1414,7 +1474,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
 
         match &item.kind {
             AssocItemKind::Fn(box Fn { sig, generics, body, .. })
-                if self.in_const_trait_or_impl
+                if self
+                    .outer_trait_or_trait_impl
+                    .as_ref()
+                    .and_then(TraitOrTraitImpl::constness)
+                    .is_some()
                     || ctxt == AssocCtxt::Trait
                     || matches!(sig.header.constness, Const::Yes(_)) =>
             {
@@ -1430,8 +1494,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 );
                 self.visit_fn(kind, item.span, item.id);
             }
-            _ => self
-                .with_in_trait_impl(false, None, |this| visit::walk_assoc_item(this, item, ctxt)),
+            _ => self.with_in_trait_impl(None, |this| visit::walk_assoc_item(this, item, ctxt)),
         }
     }
 }
@@ -1547,8 +1610,7 @@ pub fn check_crate(
         session,
         features,
         extern_mod: None,
-        in_trait_impl: false,
-        in_const_trait_or_impl: false,
+        outer_trait_or_trait_impl: None,
         has_proc_macro_decls: false,
         outer_impl_trait: None,
         disallow_tilde_const: Some(DisallowTildeConstContext::Item),
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index ebaa7ccecc9..42ada39f515 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -1,7 +1,7 @@
 //! Errors emitted by ast_passes.
 
 use rustc_ast::ParamKindOrd;
-use rustc_errors::AddToDiagnostic;
+use rustc_errors::{AddToDiagnostic, Applicability};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::{symbol::Ident, Span, Symbol};
 
@@ -50,6 +50,23 @@ pub struct TraitFnConst {
     #[label]
     pub span: Span,
     pub in_impl: bool,
+    #[label(ast_passes_const_context_label)]
+    pub const_context_label: Option<Span>,
+    #[suggestion(ast_passes_remove_const_sugg, code = "")]
+    pub remove_const_sugg: (Span, Applicability),
+    pub requires_multiple_changes: bool,
+    #[suggestion(
+        ast_passes_make_impl_const_sugg,
+        code = "const ",
+        applicability = "maybe-incorrect"
+    )]
+    pub make_impl_const_sugg: Option<Span>,
+    #[suggestion(
+        ast_passes_make_trait_const_sugg,
+        code = "#[const_trait]\n",
+        applicability = "maybe-incorrect"
+    )]
+    pub make_trait_const_sugg: Option<Span>,
 }
 
 #[derive(Diagnostic)]
diff --git a/tests/ui/consts/const-fn-mismatch.stderr b/tests/ui/consts/const-fn-mismatch.stderr
index 7d681c212b9..9e7d93b0c97 100644
--- a/tests/ui/consts/const-fn-mismatch.stderr
+++ b/tests/ui/consts/const-fn-mismatch.stderr
@@ -2,7 +2,10 @@ error[E0379]: functions in trait impls cannot be declared const
   --> $DIR/const-fn-mismatch.rs:11:5
    |
 LL |     const fn f() -> u32 {
-   |     ^^^^^ functions in trait impls cannot be const
+   |     ^^^^^-
+   |     |
+   |     functions in trait impls cannot be const
+   |     help: remove the `const`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/consts/const-fn-not-in-trait.stderr b/tests/ui/consts/const-fn-not-in-trait.stderr
index 5d364eb882d..04430610ad0 100644
--- a/tests/ui/consts/const-fn-not-in-trait.stderr
+++ b/tests/ui/consts/const-fn-not-in-trait.stderr
@@ -2,13 +2,19 @@ error[E0379]: functions in traits cannot be declared const
   --> $DIR/const-fn-not-in-trait.rs:5:5
    |
 LL |     const fn f() -> u32;
-   |     ^^^^^ functions in traits cannot be const
+   |     ^^^^^-
+   |     |
+   |     functions in traits cannot be const
+   |     help: remove the `const`
 
 error[E0379]: functions in traits cannot be declared const
   --> $DIR/const-fn-not-in-trait.rs:7:5
    |
 LL |     const fn g() -> u32 {
-   |     ^^^^^ functions in traits cannot be const
+   |     ^^^^^-
+   |     |
+   |     functions in traits cannot be const
+   |     help: remove the `const`
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/consts/issue-54954.stderr b/tests/ui/consts/issue-54954.stderr
index b0701bab793..03c47030c0e 100644
--- a/tests/ui/consts/issue-54954.stderr
+++ b/tests/ui/consts/issue-54954.stderr
@@ -2,7 +2,10 @@ error[E0379]: functions in traits cannot be declared const
   --> $DIR/issue-54954.rs:5:5
    |
 LL |     const fn const_val<T: Sized>() -> usize {
-   |     ^^^^^ functions in traits cannot be const
+   |     ^^^^^-
+   |     |
+   |     functions in traits cannot be const
+   |     help: remove the `const`
 
 error[E0790]: cannot call associated function on trait without specifying the corresponding `impl` type
   --> $DIR/issue-54954.rs:1:24
diff --git a/tests/ui/feature-gates/feature-gate-min_const_fn.stderr b/tests/ui/feature-gates/feature-gate-min_const_fn.stderr
index abc9ddabd51..0b16f9abb70 100644
--- a/tests/ui/feature-gates/feature-gate-min_const_fn.stderr
+++ b/tests/ui/feature-gates/feature-gate-min_const_fn.stderr
@@ -2,19 +2,28 @@ error[E0379]: functions in traits cannot be declared const
   --> $DIR/feature-gate-min_const_fn.rs:6:5
    |
 LL |     const fn foo() -> u32;
-   |     ^^^^^ functions in traits cannot be const
+   |     ^^^^^-
+   |     |
+   |     functions in traits cannot be const
+   |     help: remove the `const`
 
 error[E0379]: functions in traits cannot be declared const
   --> $DIR/feature-gate-min_const_fn.rs:7:5
    |
 LL |     const fn bar() -> u32 { 0 }
-   |     ^^^^^ functions in traits cannot be const
+   |     ^^^^^-
+   |     |
+   |     functions in traits cannot be const
+   |     help: remove the `const`
 
 error[E0379]: functions in trait impls cannot be declared const
   --> $DIR/feature-gate-min_const_fn.rs:11:5
    |
 LL |     const fn foo() -> u32 { 0 }
-   |     ^^^^^ functions in trait impls cannot be const
+   |     ^^^^^-
+   |     |
+   |     functions in trait impls cannot be const
+   |     help: remove the `const`
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/mismatched_types/const-fn-in-trait.stderr b/tests/ui/mismatched_types/const-fn-in-trait.stderr
index 75122237bf5..06976933b2f 100644
--- a/tests/ui/mismatched_types/const-fn-in-trait.stderr
+++ b/tests/ui/mismatched_types/const-fn-in-trait.stderr
@@ -2,13 +2,19 @@ error[E0379]: functions in traits cannot be declared const
   --> $DIR/const-fn-in-trait.rs:3:5
    |
 LL |     const fn g();
-   |     ^^^^^ functions in traits cannot be const
+   |     ^^^^^-
+   |     |
+   |     functions in traits cannot be const
+   |     help: remove the `const`
 
 error[E0379]: functions in trait impls cannot be declared const
   --> $DIR/const-fn-in-trait.rs:7:5
    |
 LL |     const fn f() -> u32 { 22 }
-   |     ^^^^^ functions in trait impls cannot be const
+   |     ^^^^^-
+   |     |
+   |     functions in trait impls cannot be const
+   |     help: remove the `const`
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/parser/fn-header-semantic-fail.stderr b/tests/ui/parser/fn-header-semantic-fail.stderr
index 1f280424cf3..696d8e01b63 100644
--- a/tests/ui/parser/fn-header-semantic-fail.stderr
+++ b/tests/ui/parser/fn-header-semantic-fail.stderr
@@ -11,13 +11,19 @@ error[E0379]: functions in traits cannot be declared const
   --> $DIR/fn-header-semantic-fail.rs:18:9
    |
 LL |         const fn ft3();
-   |         ^^^^^ functions in traits cannot be const
+   |         ^^^^^-
+   |         |
+   |         functions in traits cannot be const
+   |         help: remove the `const`
 
 error[E0379]: functions in traits cannot be declared const
   --> $DIR/fn-header-semantic-fail.rs:20:9
    |
 LL |         const async unsafe extern "C" fn ft5();
-   |         ^^^^^ functions in traits cannot be const
+   |         ^^^^^-
+   |         |
+   |         functions in traits cannot be const
+   |         help: remove the `const`
 
 error: functions cannot be both `const` and `async`
   --> $DIR/fn-header-semantic-fail.rs:20:9
@@ -32,13 +38,19 @@ error[E0379]: functions in trait impls cannot be declared const
   --> $DIR/fn-header-semantic-fail.rs:29:9
    |
 LL |         const fn ft3() {}
-   |         ^^^^^ functions in trait impls cannot be const
+   |         ^^^^^-
+   |         |
+   |         functions in trait impls cannot be const
+   |         help: remove the `const`
 
 error[E0379]: functions in trait impls cannot be declared const
   --> $DIR/fn-header-semantic-fail.rs:31:9
    |
 LL |         const async unsafe extern "C" fn ft5() {}
-   |         ^^^^^ functions in trait impls cannot be const
+   |         ^^^^^-
+   |         |
+   |         functions in trait impls cannot be const
+   |         help: remove the `const`
 
 error: functions cannot be both `const` and `async`
   --> $DIR/fn-header-semantic-fail.rs:31:9
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/trait-fn-const.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/trait-fn-const.rs
index 0d1c926fef5..891e87d3b97 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/trait-fn-const.rs
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/trait-fn-const.rs
@@ -7,7 +7,15 @@ trait Trait {
 }
 
 impl const Trait for () {
-    const fn fun() {}  //~ ERROR functions in trait impls cannot be declared const
+    const fn fun() {} //~ ERROR functions in trait impls cannot be declared const
+}
+
+impl Trait for u32 {
+    const fn fun() {} //~ ERROR functions in trait impls cannot be declared const
+}
+
+trait NonConst {
+    const fn fun(); //~ ERROR functions in traits cannot be declared const
 }
 
 fn main() {}
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/trait-fn-const.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/trait-fn-const.stderr
index cf8a2772d89..4d0b03046d2 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/trait-fn-const.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/trait-fn-const.stderr
@@ -1,15 +1,59 @@
 error[E0379]: functions in traits cannot be declared const
   --> $DIR/trait-fn-const.rs:6:5
    |
+LL | #[const_trait]
+   | -------------- this declares all associated functions implicitly const
+LL | trait Trait {
 LL |     const fn fun();
-   |     ^^^^^ functions in traits cannot be const
+   |     ^^^^^-
+   |     |
+   |     functions in traits cannot be const
+   |     help: remove the `const`
 
 error[E0379]: functions in trait impls cannot be declared const
   --> $DIR/trait-fn-const.rs:10:5
    |
+LL | impl const Trait for () {
+   |      ----- this declares all associated functions implicitly const
+LL |     const fn fun() {}
+   |     ^^^^^-
+   |     |
+   |     functions in trait impls cannot be const
+   |     help: remove the `const`
+
+error[E0379]: functions in trait impls cannot be declared const
+  --> $DIR/trait-fn-const.rs:14:5
+   |
 LL |     const fn fun() {}
    |     ^^^^^ functions in trait impls cannot be const
+   |
+help: remove the `const` ...
+   |
+LL -     const fn fun() {}
+LL +     fn fun() {}
+   |
+help: ... and declare the impl to be const instead
+   |
+LL | impl const Trait for u32 {
+   |      +++++
+
+error[E0379]: functions in traits cannot be declared const
+  --> $DIR/trait-fn-const.rs:18:5
+   |
+LL |     const fn fun();
+   |     ^^^^^ functions in traits cannot be const
+   |
+help: remove the `const` ...
+   |
+LL -     const fn fun();
+LL +     fn fun();
+   |
+help: ... and declare the trait to be a `#[const_trait]` instead
+   |
+LL + #[const_trait]
+LL | trait NonConst {
+   |
 
-error: aborting due to 2 previous errors
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0379`.