mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-10-31 12:04:43 +00:00 
			
		
		
		
	parser: fix parsing of trait bound polarity and for-binders
The rustc AST allows both `for<>` binders and `?` polarity
modifiers in trait bounds, but they are parsed in a specific
order and validated for correctness:
  1. `for<>` binder is parsed first.
  2. Polarity modifiers (`?`, `!`) are parsed second.
  3. The parser validates that binders and polarity modifiers
     do not conflict:
```rust
if let Some(binder_span) = binder_span {
    match modifiers.polarity {
        BoundPolarity::Maybe(polarity_span) => {
            // Error: "for<...> binder not allowed with ? polarity"
        }
    }
}
```
This implies:
- `for<> ?Sized` → Valid syntax. Invalid semantics.
- `?for<> Sized` → Invalid syntax.
However, rust-analyzer incorrectly had special-case logic that
allowed `?for<>` as valid syntax. This fix removes that incorrect
special case, making rust-analyzer reject `?for<> Sized` as a
syntax error, matching rustc behavior.
This has caused confusion in other crates (such as syn) which
rely on these files to implement correct syntax evaluation.
			
			
This commit is contained in:
		
							parent
							
								
									31db5b5be9
								
							
						
					
					
						commit
						943b42f743
					
				
					 6 changed files with 73 additions and 19 deletions
				
			
		|  | @ -182,12 +182,6 @@ fn type_bound(p: &mut Parser<'_>) -> bool { | |||
|             ); | ||||
|             m.complete(p, USE_BOUND_GENERIC_ARGS); | ||||
|         } | ||||
|         T![?] if p.nth_at(1, T![for]) => { | ||||
|             // test question_for_type_trait_bound
 | ||||
|             // fn f<T>() where T: ?for<> Sized {}
 | ||||
|             p.bump_any(); | ||||
|             types::for_type(p, false) | ||||
|         } | ||||
|         _ => { | ||||
|             if path_type_bound(p).is_err() { | ||||
|                 m.abandon(p); | ||||
|  | @ -219,8 +213,13 @@ fn path_type_bound(p: &mut Parser<'_>) -> Result<(), ()> { | |||
|     // test async_trait_bound
 | ||||
|     // fn async_foo(_: impl async Fn(&i32)) {}
 | ||||
|     p.eat(T![async]); | ||||
|     // test question_for_type_trait_bound
 | ||||
|     // fn f<T>() where T: for<> ?Sized {}
 | ||||
|     p.eat(T![?]); | ||||
| 
 | ||||
|     // test_err invalid_question_for_type_trait_bound
 | ||||
|     // fn f<T>() where T: ?for<> Sized {}
 | ||||
| 
 | ||||
|     if paths::is_use_path_start(p) { | ||||
|         types::path_type_bounds(p, false); | ||||
|         // test_err type_bounds_macro_call_recovery
 | ||||
|  |  | |||
|  | @ -796,6 +796,12 @@ mod err { | |||
|     #[test] | ||||
|     fn impl_type() { run_and_expect_errors("test_data/parser/inline/err/impl_type.rs"); } | ||||
|     #[test] | ||||
|     fn invalid_question_for_type_trait_bound() { | ||||
|         run_and_expect_errors( | ||||
|             "test_data/parser/inline/err/invalid_question_for_type_trait_bound.rs", | ||||
|         ); | ||||
|     } | ||||
|     #[test] | ||||
|     fn let_else_right_curly_brace() { | ||||
|         run_and_expect_errors("test_data/parser/inline/err/let_else_right_curly_brace.rs"); | ||||
|     } | ||||
|  |  | |||
|  | @ -0,0 +1,49 @@ | |||
| SOURCE_FILE | ||||
|   FN | ||||
|     FN_KW "fn" | ||||
|     WHITESPACE " " | ||||
|     NAME | ||||
|       IDENT "f" | ||||
|     GENERIC_PARAM_LIST | ||||
|       L_ANGLE "<" | ||||
|       TYPE_PARAM | ||||
|         NAME | ||||
|           IDENT "T" | ||||
|       R_ANGLE ">" | ||||
|     PARAM_LIST | ||||
|       L_PAREN "(" | ||||
|       R_PAREN ")" | ||||
|     WHITESPACE " " | ||||
|     WHERE_CLAUSE | ||||
|       WHERE_KW "where" | ||||
|       WHITESPACE " " | ||||
|       WHERE_PRED | ||||
|         PATH_TYPE | ||||
|           PATH | ||||
|             PATH_SEGMENT | ||||
|               NAME_REF | ||||
|                 IDENT "T" | ||||
|         COLON ":" | ||||
|         WHITESPACE " " | ||||
|         TYPE_BOUND_LIST | ||||
|           QUESTION "?" | ||||
|       WHERE_PRED | ||||
|         FOR_BINDER | ||||
|           FOR_KW "for" | ||||
|           GENERIC_PARAM_LIST | ||||
|             L_ANGLE "<" | ||||
|             R_ANGLE ">" | ||||
|         WHITESPACE " " | ||||
|         PATH_TYPE | ||||
|           PATH | ||||
|             PATH_SEGMENT | ||||
|               NAME_REF | ||||
|                 IDENT "Sized" | ||||
|     WHITESPACE " " | ||||
|     BLOCK_EXPR | ||||
|       STMT_LIST | ||||
|         L_CURLY "{" | ||||
|         R_CURLY "}" | ||||
|   WHITESPACE "\n" | ||||
| error 20: expected comma | ||||
| error 31: expected colon | ||||
|  | @ -0,0 +1 @@ | |||
| fn f<T>() where T: ?for<> Sized {} | ||||
|  | @ -27,14 +27,13 @@ SOURCE_FILE | |||
|         WHITESPACE " " | ||||
|         TYPE_BOUND_LIST | ||||
|           TYPE_BOUND | ||||
|             QUESTION "?" | ||||
|             FOR_TYPE | ||||
|             FOR_BINDER | ||||
|               FOR_KW "for" | ||||
|               GENERIC_PARAM_LIST | ||||
|                 L_ANGLE "<" | ||||
|                 R_ANGLE ">" | ||||
|             WHITESPACE " " | ||||
|             QUESTION "?" | ||||
|             PATH_TYPE | ||||
|               PATH | ||||
|                 PATH_SEGMENT | ||||
|  |  | |||
|  | @ -1 +1 @@ | |||
| fn f<T>() where T: ?for<> Sized {} | ||||
| fn f<T>() where T: for<> ?Sized {} | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Nathaniel McCallum
						Nathaniel McCallum