about summary refs log tree commit diff
path: root/compiler/rustc_parse/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_parse/src')
-rw-r--r--compiler/rustc_parse/src/errors.rs30
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs11
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs123
-rw-r--r--compiler/rustc_parse/src/parser/item.rs18
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs7
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs60
6 files changed, 170 insertions, 79 deletions
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 109d36fe689..2e81d2a876b 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -3212,3 +3212,33 @@ pub struct UnsafeAttrOutsideUnsafeSuggestion {
     #[suggestion_part(code = ")")]
     pub right: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(parse_binder_before_modifiers)]
+pub struct BinderBeforeModifiers {
+    #[primary_span]
+    pub binder_span: Span,
+    #[label]
+    pub modifiers_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_binder_and_polarity)]
+pub struct BinderAndPolarity {
+    #[primary_span]
+    pub polarity_span: Span,
+    #[label]
+    pub binder_span: Span,
+    pub polarity: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_modifiers_and_polarity)]
+pub struct PolarityAndModifiers {
+    #[primary_span]
+    pub polarity_span: Span,
+    #[label]
+    pub modifiers_span: Span,
+    pub polarity: &'static str,
+    pub modifiers_concatenated: String,
+}
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index 0b2c3044039..535b53a836e 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -457,14 +457,3 @@ impl<'a> Parser<'a> {
         Err(self.dcx().create_err(err))
     }
 }
-
-/// The attributes are complete if all attributes are either a doc comment or a
-/// builtin attribute other than `cfg_attr`.
-pub fn is_complete(attrs: &[ast::Attribute]) -> bool {
-    attrs.iter().all(|attr| {
-        attr.is_doc_comment()
-            || attr.ident().is_some_and(|ident| {
-                ident.name != sym::cfg_attr && rustc_feature::is_builtin_attr_name(ident.name)
-            })
-    })
-}
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index dc5f98f7be8..5dc49ea51d1 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -60,10 +60,6 @@ impl AttrWrapper {
     pub fn is_empty(&self) -> bool {
         self.attrs.is_empty()
     }
-
-    pub fn is_complete(&self) -> bool {
-        crate::parser::attr::is_complete(&self.attrs)
-    }
 }
 
 /// Returns `true` if `attrs` contains a `cfg` or `cfg_attr` attribute
@@ -114,17 +110,15 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
             replace_ranges.sort_by_key(|(range, _)| range.start);
 
             #[cfg(debug_assertions)]
-            {
-                for [(range, tokens), (next_range, next_tokens)] in replace_ranges.array_windows() {
-                    assert!(
-                        range.end <= next_range.start || range.end >= next_range.end,
-                        "Replace ranges should either be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})",
-                        range,
-                        tokens,
-                        next_range,
-                        next_tokens,
-                    );
-                }
+            for [(range, tokens), (next_range, next_tokens)] in replace_ranges.array_windows() {
+                assert!(
+                    range.end <= next_range.start || range.end >= next_range.end,
+                    "Replace ranges should either be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})",
+                    range,
+                    tokens,
+                    next_range,
+                    next_tokens,
+                );
             }
 
             // Process the replace ranges, starting from the highest start
@@ -137,9 +131,9 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
             // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }`
             //
             // By starting processing from the replace range with the greatest
-            // start position, we ensure that any replace range which encloses
-            // another replace range will capture the *replaced* tokens for the inner
-            // range, not the original tokens.
+            // start position, we ensure that any (outer) replace range which
+            // encloses another (inner) replace range will fully overwrite the
+            // inner range's replacement.
             for (range, target) in replace_ranges.into_iter().rev() {
                 assert!(!range.is_empty(), "Cannot replace an empty range: {range:?}");
 
@@ -199,20 +193,20 @@ impl<'a> Parser<'a> {
         force_collect: ForceCollect,
         f: impl FnOnce(&mut Self, ast::AttrVec) -> PResult<'a, (R, bool)>,
     ) -> PResult<'a, R> {
-        // Skip collection when nothing could observe the collected tokens, i.e.
-        // all of the following conditions hold.
-        // - We are not force collecting tokens (because force collection
-        //   requires tokens by definition).
-        if matches!(force_collect, ForceCollect::No)
-            // - None of our outer attributes require tokens.
-            && attrs.is_complete()
-            // - Our target doesn't support custom inner attributes (custom
+        // We must collect if anything could observe the collected tokens, i.e.
+        // if any of the following conditions hold.
+        // - We are force collecting tokens (because force collection requires
+        //   tokens by definition).
+        let needs_collection = matches!(force_collect, ForceCollect::Yes)
+            // - Any of our outer attributes require tokens.
+            || needs_tokens(&attrs.attrs)
+            // - Our target supports custom inner attributes (custom
             //   inner attribute invocation might require token capturing).
-            && !R::SUPPORTS_CUSTOM_INNER_ATTRS
-            // - We are not in `capture_cfg` mode (which requires tokens if
+            || R::SUPPORTS_CUSTOM_INNER_ATTRS
+            // - We are in `capture_cfg` mode (which requires tokens if
             //   the parsed node has `#[cfg]` or `#[cfg_attr]` attributes).
-            && !self.capture_cfg
-        {
+            || self.capture_cfg;
+        if !needs_collection {
             return Ok(f(self, attrs.attrs)?.0);
         }
 
@@ -250,28 +244,28 @@ impl<'a> Parser<'a> {
             return Ok(ret);
         }
 
-        // This is similar to the "skip collection" check at the start of this
-        // function, but now that we've parsed an AST node we have more
+        // This is similar to the `needs_collection` check at the start of this
+        // function, but now that we've parsed an AST node we have complete
         // information available. (If we return early here that means the
         // setup, such as cloning the token cursor, was unnecessary. That's
         // hard to avoid.)
         //
-        // Skip collection when nothing could observe the collected tokens, i.e.
-        // all of the following conditions hold.
-        // - We are not force collecting tokens.
-        if matches!(force_collect, ForceCollect::No)
-            // - None of our outer *or* inner attributes require tokens.
-            //   (`attrs` was just outer attributes, but `ret.attrs()` is outer
-            //   and inner attributes. That makes this check more precise than
-            //   `attrs.is_complete()` at the start of the function, and we can
-            //   skip the subsequent check on `R::SUPPORTS_CUSTOM_INNER_ATTRS`.
-            && crate::parser::attr::is_complete(ret.attrs())
-            // - We are not in `capture_cfg` mode, or we are but there are no
-            //   `#[cfg]` or `#[cfg_attr]` attributes. (During normal
-            //   non-`capture_cfg` parsing, we don't need any special capturing
-            //   for those attributes, because they're builtin.)
-            && (!self.capture_cfg || !has_cfg_or_cfg_attr(ret.attrs()))
-        {
+        // We must collect if anything could observe the collected tokens, i.e.
+        // if any of the following conditions hold.
+        // - We are force collecting tokens.
+        let needs_collection = matches!(force_collect, ForceCollect::Yes)
+            // - Any of our outer *or* inner attributes require tokens.
+            //   (`attr.attrs` was just outer attributes, but `ret.attrs()` is
+            //   outer and inner attributes. So this check is more precise than
+            //   the earlier `needs_tokens` check, and we don't need to
+            //   check `R::SUPPORTS_CUSTOM_INNER_ATTRS`.)
+            || needs_tokens(ret.attrs())
+            // - We are in `capture_cfg` mode and there are `#[cfg]` or
+            //   `#[cfg_attr]` attributes. (During normal non-`capture_cfg`
+            //   parsing, we don't need any special capturing for those
+            //   attributes, because they're builtin.)
+            || (self.capture_cfg && has_cfg_or_cfg_attr(ret.attrs()));
+        if !needs_collection {
             return Ok(ret);
         }
 
@@ -297,11 +291,13 @@ impl<'a> Parser<'a> {
         // with `None`, which means the relevant tokens will be removed. (More
         // details below.)
         let mut inner_attr_replace_ranges = Vec::new();
-        for inner_attr in ret.attrs().iter().filter(|a| a.style == ast::AttrStyle::Inner) {
-            if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&inner_attr.id) {
-                inner_attr_replace_ranges.push((attr_range, None));
-            } else {
-                self.dcx().span_delayed_bug(inner_attr.span, "Missing token range for attribute");
+        for attr in ret.attrs() {
+            if attr.style == ast::AttrStyle::Inner {
+                if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&attr.id) {
+                    inner_attr_replace_ranges.push((attr_range, None));
+                } else {
+                    self.dcx().span_delayed_bug(attr.span, "Missing token range for attribute");
+                }
             }
         }
 
@@ -337,8 +333,7 @@ impl<'a> Parser<'a> {
         // When parsing `m`:
         // - `start_pos..end_pos` is `0..34` (`mod m`, excluding the `#[cfg_eval]` attribute).
         // - `inner_attr_replace_ranges` is empty.
-        // - `replace_range_start..replace_ranges_end` has two entries.
-        //   - One to delete the inner attribute (`17..27`), obtained when parsing `g` (see above).
+        // - `replace_range_start..replace_ranges_end` has one entry.
         //   - One `AttrsTarget` (added below when parsing `g`) to replace all of `g` (`3..33`,
         //     including its outer attribute), with:
         //     - `attrs`: includes the outer and the inner attr.
@@ -369,12 +364,10 @@ impl<'a> Parser<'a> {
 
             // What is the status here when parsing the example code at the top of this method?
             //
-            // When parsing `g`, we add two entries:
+            // When parsing `g`, we add one entry:
             // - The `start_pos..end_pos` (`3..33`) entry has a new `AttrsTarget` with:
             //   - `attrs`: includes the outer and the inner attr.
             //   - `tokens`: lazy tokens for `g` (with its inner attr deleted).
-            // - `inner_attr_replace_ranges` contains the one entry to delete the inner attr's
-            //   tokens (`17..27`).
             //
             // When parsing `m`, we do nothing here.
 
@@ -384,7 +377,6 @@ impl<'a> Parser<'a> {
             let start_pos = if has_outer_attrs { attrs.start_pos } else { start_pos };
             let target = AttrsTarget { attrs: ret.attrs().iter().cloned().collect(), tokens };
             self.capture_state.replace_ranges.push((start_pos..end_pos, Some(target)));
-            self.capture_state.replace_ranges.extend(inner_attr_replace_ranges);
         } else if matches!(self.capture_state.capturing, Capturing::No) {
             // Only clear the ranges once we've finished capturing entirely, i.e. we've finished
             // the outermost call to this method.
@@ -461,6 +453,19 @@ fn make_attr_token_stream(
     AttrTokenStream::new(stack_top.inner)
 }
 
+/// Tokens are needed if:
+/// - any non-single-segment attributes (other than doc comments) are present; or
+/// - any `cfg_attr` attributes are present;
+/// - any single-segment, non-builtin attributes are present.
+fn needs_tokens(attrs: &[ast::Attribute]) -> bool {
+    attrs.iter().any(|attr| match attr.ident() {
+        None => !attr.is_doc_comment(),
+        Some(ident) => {
+            ident.name == sym::cfg_attr || !rustc_feature::is_builtin_attr_name(ident.name)
+        }
+    })
+}
+
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
 #[cfg(target_pointer_width = "64")]
 mod size_asserts {
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index fbc5b914600..112855e6d1f 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -2483,12 +2483,15 @@ impl<'a> Parser<'a> {
     /// `check_pub` adds additional `pub` to the checks in case users place it
     /// wrongly, can be used to ensure `pub` never comes after `default`.
     pub(super) fn check_fn_front_matter(&mut self, check_pub: bool, case: Case) -> bool {
+        const ALL_QUALS: &[Symbol] =
+            &[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern];
+
         // We use an over-approximation here.
         // `const const`, `fn const` won't parse, but we're not stepping over other syntax either.
         // `pub` is added in case users got confused with the ordering like `async pub fn`,
         // only if it wasn't preceded by `default` as `default pub` is invalid.
         let quals: &[Symbol] = if check_pub {
-            &[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern]
+            ALL_QUALS
         } else {
             &[kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern]
         };
@@ -2518,9 +2521,9 @@ impl<'a> Parser<'a> {
             || self.check_keyword_case(kw::Extern, case)
                 && self.look_ahead(1, |t| t.can_begin_string_literal())
                 && (self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, case)) ||
-                    // this branch is only for better diagnostic in later, `pub` is not allowed here
+                    // this branch is only for better diagnostics; `pub`, `unsafe`, etc. are not allowed here
                     (self.may_recover()
-                        && self.look_ahead(2, |t| t.is_keyword(kw::Pub))
+                        && self.look_ahead(2, |t| ALL_QUALS.iter().any(|&kw| t.is_keyword(kw)))
                         && self.look_ahead(3, |t| t.is_keyword_case(kw::Fn, case))))
     }
 
@@ -2773,7 +2776,14 @@ impl<'a> Parser<'a> {
             let snapshot = p.create_snapshot_for_diagnostic();
             let param = p.parse_param_general(req_name, first_param).or_else(|e| {
                 let guar = e.emit();
-                let lo = p.prev_token.span;
+                // When parsing a param failed, we should check to make the span of the param
+                // not contain '(' before it.
+                // For example when parsing `*mut Self` in function `fn oof(*mut Self)`.
+                let lo = if let TokenKind::OpenDelim(Delimiter::Parenthesis) = p.prev_token.kind {
+                    p.prev_token.span.shrink_to_hi()
+                } else {
+                    p.prev_token.span
+                };
                 p.restore_snapshot(snapshot);
                 // Skip every token until next possible arg or end.
                 p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(Delimiter::Parenthesis)]);
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 7326b9ec51f..e7240869a39 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -599,7 +599,7 @@ impl<'a> Parser<'a> {
 
     /// If the next token is the given keyword, eats it and returns `true`.
     /// Otherwise, returns `false`. An expectation is also added for diagnostics purposes.
-    // Public for rustfmt usage.
+    // Public for rustc_builtin_macros and rustfmt usage.
     #[inline]
     pub fn eat_keyword(&mut self, kw: Symbol) -> bool {
         if self.check_keyword(kw) {
@@ -631,8 +631,11 @@ impl<'a> Parser<'a> {
         false
     }
 
+    /// If the next token is the given keyword, eats it and returns `true`.
+    /// Otherwise, returns `false`. No expectation is added.
+    // Public for rustc_builtin_macros usage.
     #[inline]
-    fn eat_keyword_noexpect(&mut self, kw: Symbol) -> bool {
+    pub fn eat_keyword_noexpect(&mut self, kw: Symbol) -> bool {
         if self.token.is_keyword(kw) {
             self.bump();
             true
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index a8134110010..f95ecd254ce 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -935,9 +935,14 @@ impl<'a> Parser<'a> {
     /// If no modifiers are present, this does not consume any tokens.
     ///
     /// ```ebnf
-    /// TRAIT_BOUND_MODIFIERS = [["~"] "const"] ["async"] ["?" | "!"]
+    /// CONSTNESS = [["~"] "const"]
+    /// ASYNCNESS = ["async"]
+    /// POLARITY = ["?" | "!"]
     /// ```
+    ///
+    /// See `parse_generic_ty_bound` for the complete grammar of trait bound modifiers.
     fn parse_trait_bound_modifiers(&mut self) -> PResult<'a, TraitBoundModifiers> {
+        let modifier_lo = self.token.span;
         let constness = if self.eat(&token::Tilde) {
             let tilde = self.prev_token.span;
             self.expect_keyword(kw::Const)?;
@@ -970,6 +975,7 @@ impl<'a> Parser<'a> {
         } else {
             BoundAsyncness::Normal
         };
+        let modifier_hi = self.prev_token.span;
 
         let polarity = if self.eat(&token::Question) {
             BoundPolarity::Maybe(self.prev_token.span)
@@ -980,13 +986,40 @@ impl<'a> Parser<'a> {
             BoundPolarity::Positive
         };
 
+        // Enforce the mutual-exclusivity of `const`/`async` and `?`/`!`.
+        match polarity {
+            BoundPolarity::Positive => {
+                // All trait bound modifiers allowed to combine with positive polarity
+            }
+            BoundPolarity::Maybe(polarity_span) | BoundPolarity::Negative(polarity_span) => {
+                match (asyncness, constness) {
+                    (BoundAsyncness::Normal, BoundConstness::Never) => {
+                        // Ok, no modifiers.
+                    }
+                    (_, _) => {
+                        let constness = constness.as_str();
+                        let asyncness = asyncness.as_str();
+                        let glue =
+                            if !constness.is_empty() && !asyncness.is_empty() { " " } else { "" };
+                        let modifiers_concatenated = format!("{constness}{glue}{asyncness}");
+                        self.dcx().emit_err(errors::PolarityAndModifiers {
+                            polarity_span,
+                            polarity: polarity.as_str(),
+                            modifiers_span: modifier_lo.to(modifier_hi),
+                            modifiers_concatenated,
+                        });
+                    }
+                }
+            }
+        }
+
         Ok(TraitBoundModifiers { constness, asyncness, polarity })
     }
 
     /// Parses a type bound according to:
     /// ```ebnf
     /// TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN)
-    /// TY_BOUND_NOPAREN = [TRAIT_BOUND_MODIFIERS] [for<LT_PARAM_DEFS>] SIMPLE_PATH
+    /// TY_BOUND_NOPAREN = [for<GENERIC_PARAMS> CONSTNESS ASYNCNESS | POLARITY] SIMPLE_PATH
     /// ```
     ///
     /// For example, this grammar accepts `for<'a: 'b> ~const ?m::Trait<'a>`.
@@ -996,9 +1029,25 @@ impl<'a> Parser<'a> {
         has_parens: bool,
         leading_token: &Token,
     ) -> PResult<'a, GenericBound> {
-        let modifiers = self.parse_trait_bound_modifiers()?;
         let (mut lifetime_defs, binder_span) = self.parse_late_bound_lifetime_defs()?;
 
+        let modifiers_lo = self.token.span;
+        let modifiers = self.parse_trait_bound_modifiers()?;
+        let modifiers_span = modifiers_lo.to(self.prev_token.span);
+
+        if let Some(binder_span) = binder_span {
+            match modifiers.polarity {
+                BoundPolarity::Negative(polarity_span) | BoundPolarity::Maybe(polarity_span) => {
+                    self.dcx().emit_err(errors::BinderAndPolarity {
+                        binder_span,
+                        polarity_span,
+                        polarity: modifiers.polarity.as_str(),
+                    });
+                }
+                BoundPolarity::Positive => {}
+            }
+        }
+
         // Recover erroneous lifetime bound with modifiers or binder.
         // e.g. `T: for<'a> 'a` or `T: ~const 'a`.
         if self.token.is_lifetime() {
@@ -1006,6 +1055,11 @@ impl<'a> Parser<'a> {
             return self.parse_generic_lt_bound(lo, has_parens);
         }
 
+        if let (more_lifetime_defs, Some(binder_span)) = self.parse_late_bound_lifetime_defs()? {
+            lifetime_defs.extend(more_lifetime_defs);
+            self.dcx().emit_err(errors::BinderBeforeModifiers { binder_span, modifiers_span });
+        }
+
         let mut path = if self.token.is_keyword(kw::Fn)
             && self.look_ahead(1, |tok| tok.kind == TokenKind::OpenDelim(Delimiter::Parenthesis))
             && let Some(path) = self.recover_path_from_fn()