about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_parse/messages.ftl2
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs25
-rw-r--r--compiler/rustc_span/src/source_map.rs15
-rw-r--r--tests/ui/parser/issues/issue-111692.rs32
-rw-r--r--tests/ui/parser/issues/issue-111692.stderr46
-rw-r--r--tests/ui/parser/method-call-on-struct-literal-in-if-condition.stderr2
6 files changed, 113 insertions, 9 deletions
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 9263394508e..35eec2c8e1b 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -695,7 +695,7 @@ parse_struct_literal_body_without_path =
 
 parse_struct_literal_needing_parens =
     invalid struct literal
-    .suggestion = you might need to surround the struct literal in parentheses
+    .suggestion = you might need to surround the struct literal with parentheses
 
 parse_struct_literal_not_allowed_here = struct literals are not allowed here
     .suggestion = surround the struct literal with parentheses
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index c1454039685..228eff1269f 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -751,13 +751,24 @@ impl<'a> Parser<'a> {
                     tail.could_be_bare_literal = true;
                     if maybe_struct_name.is_ident() && can_be_struct_literal {
                         // Account for `if Example { a: one(), }.is_pos() {}`.
-                        Err(self.sess.create_err(StructLiteralNeedingParens {
-                            span: maybe_struct_name.span.to(expr.span),
-                            sugg: StructLiteralNeedingParensSugg {
-                                before: maybe_struct_name.span.shrink_to_lo(),
-                                after: expr.span.shrink_to_hi(),
-                            },
-                        }))
+                        // expand `before` so that we take care of module path such as:
+                        // `foo::Bar { ... } `
+                        // we expect to suggest `(foo::Bar { ... })` instead of `foo::(Bar { ... })`
+                        let sm = self.sess.source_map();
+                        let before = maybe_struct_name.span.shrink_to_lo();
+                        if let Ok(extend_before) = sm.span_extend_prev_while(before, |t| {
+                            t.is_alphanumeric() || t == ':' || t == '_'
+                        }) {
+                            Err(self.sess.create_err(StructLiteralNeedingParens {
+                                span: maybe_struct_name.span.to(expr.span),
+                                sugg: StructLiteralNeedingParensSugg {
+                                    before: extend_before.shrink_to_lo(),
+                                    after: expr.span.shrink_to_hi(),
+                                },
+                            }))
+                        } else {
+                            return None;
+                        }
                     } else {
                         self.sess.emit_err(StructLiteralBodyWithoutPath {
                             span: expr.span,
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index 1824510a974..c53fe084c4d 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -744,6 +744,21 @@ impl SourceMap {
         })
     }
 
+    /// Extends the given `Span` to previous character while the previous character matches the predicate
+    pub fn span_extend_prev_while(
+        &self,
+        span: Span,
+        f: impl Fn(char) -> bool,
+    ) -> Result<Span, SpanSnippetError> {
+        self.span_to_source(span, |s, start, _end| {
+            let n = s[..start]
+                .char_indices()
+                .rfind(|&(_, c)| !f(c))
+                .map_or(start, |(i, _)| start - i - 1);
+            Ok(span.with_lo(span.lo() - BytePos(n as u32)))
+        })
+    }
+
     /// Extends the given `Span` to just before the next occurrence of `c`.
     pub fn span_extend_to_next_char(&self, sp: Span, c: char, accept_newlines: bool) -> Span {
         if let Ok(next_source) = self.span_to_next_source(sp) {
diff --git a/tests/ui/parser/issues/issue-111692.rs b/tests/ui/parser/issues/issue-111692.rs
new file mode 100644
index 00000000000..56096f706a8
--- /dev/null
+++ b/tests/ui/parser/issues/issue-111692.rs
@@ -0,0 +1,32 @@
+mod module {
+    #[derive(Eq, PartialEq)]
+    pub struct Type {
+        pub x: u8,
+        pub y: u8,
+    }
+
+    pub const C: u8 = 32u8;
+}
+
+fn test(x: module::Type) {
+    if x == module::Type { x: module::C, y: 1 } { //~ ERROR invalid struct literal
+    }
+}
+
+fn test2(x: module::Type) {
+    if x ==module::Type { x: module::C, y: 1 } { //~ ERROR invalid struct literal
+    }
+}
+
+
+fn test3(x: module::Type) {
+    if x == Type { x: module::C, y: 1 } { //~ ERROR invalid struct literal
+    }
+}
+
+fn test4(x: module::Type) {
+    if x == demo_module::Type { x: module::C, y: 1 } { //~ ERROR invalid struct literal
+    }
+}
+
+fn main() { }
diff --git a/tests/ui/parser/issues/issue-111692.stderr b/tests/ui/parser/issues/issue-111692.stderr
new file mode 100644
index 00000000000..068b0483b0f
--- /dev/null
+++ b/tests/ui/parser/issues/issue-111692.stderr
@@ -0,0 +1,46 @@
+error: invalid struct literal
+  --> $DIR/issue-111692.rs:12:21
+   |
+LL |     if x == module::Type { x: module::C, y: 1 } {
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: you might need to surround the struct literal with parentheses
+   |
+LL |     if x == (module::Type { x: module::C, y: 1 }) {
+   |             +                                   +
+
+error: invalid struct literal
+  --> $DIR/issue-111692.rs:17:20
+   |
+LL |     if x ==module::Type { x: module::C, y: 1 } {
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: you might need to surround the struct literal with parentheses
+   |
+LL |     if x ==(module::Type { x: module::C, y: 1 }) {
+   |            +                                   +
+
+error: invalid struct literal
+  --> $DIR/issue-111692.rs:23:13
+   |
+LL |     if x == Type { x: module::C, y: 1 } {
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: you might need to surround the struct literal with parentheses
+   |
+LL |     if x == (Type { x: module::C, y: 1 }) {
+   |             +                           +
+
+error: invalid struct literal
+  --> $DIR/issue-111692.rs:28:26
+   |
+LL |     if x == demo_module::Type { x: module::C, y: 1 } {
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: you might need to surround the struct literal with parentheses
+   |
+LL |     if x == (demo_module::Type { x: module::C, y: 1 }) {
+   |             +                                        +
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/parser/method-call-on-struct-literal-in-if-condition.stderr b/tests/ui/parser/method-call-on-struct-literal-in-if-condition.stderr
index 7fd7ffc94a5..dedbad90945 100644
--- a/tests/ui/parser/method-call-on-struct-literal-in-if-condition.stderr
+++ b/tests/ui/parser/method-call-on-struct-literal-in-if-condition.stderr
@@ -4,7 +4,7 @@ error: invalid struct literal
 LL |     if Example { a: one(), }.is_pos() {
    |        ^^^^^^^^^^^^^^^^^^^^^
    |
-help: you might need to surround the struct literal in parentheses
+help: you might need to surround the struct literal with parentheses
    |
 LL |     if (Example { a: one(), }).is_pos() {
    |        +                     +