Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 59dd642

Browse files
authored
Merge pull request rust-lang#18417 from ChayimFriedman2/hash-string
fix: Correctly handle `#""` in edition <2024
2 parents ed670e0 + d6b2658 commit 59dd642

File tree

4 files changed

+30
-7
lines changed

4 files changed

+30
-7
lines changed

src/tools/rust-analyzer/crates/parser/src/lexed_str.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ impl<'a> LexedStr<'a> {
3939
conv.offset = shebang_len;
4040
};
4141

42-
for token in rustc_lexer::tokenize(&text[conv.offset..]) {
42+
// Re-create the tokenizer from scratch every token because `GuardedStrPrefix` is one token in the lexer
43+
// but we want to split it to two in edition <2024.
44+
while let Some(token) = rustc_lexer::tokenize(&text[conv.offset..]).next() {
4345
let token_text = &text[conv.offset..][..token.len as usize];
4446

4547
conv.extend_token(&token.kind, token_text);
@@ -158,7 +160,7 @@ impl<'a> Converter<'a> {
158160
}
159161
}
160162

161-
fn extend_token(&mut self, kind: &rustc_lexer::TokenKind, token_text: &str) {
163+
fn extend_token(&mut self, kind: &rustc_lexer::TokenKind, mut token_text: &str) {
162164
// A note on an intended tradeoff:
163165
// We drop some useful information here (see patterns with double dots `..`)
164166
// Storing that info in `SyntaxKind` is not possible due to its layout requirements of
@@ -189,10 +191,15 @@ impl<'a> Converter<'a> {
189191
rustc_lexer::TokenKind::RawIdent => IDENT,
190192

191193
rustc_lexer::TokenKind::GuardedStrPrefix if self.edition.at_least_2024() => {
194+
// FIXME: rustc does something better for recovery.
192195
err = "Invalid string literal (reserved syntax)";
193196
ERROR
194197
}
195-
rustc_lexer::TokenKind::GuardedStrPrefix => POUND,
198+
rustc_lexer::TokenKind::GuardedStrPrefix => {
199+
// The token is `#"` or `##`, split it into two.
200+
token_text = &token_text[1..];
201+
POUND
202+
}
196203

197204
rustc_lexer::TokenKind::Literal { kind, .. } => {
198205
self.extend_literal(token_text.len(), kind);

src/tools/rust-analyzer/crates/parser/src/tests.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,20 @@ use crate::{Edition, LexedStr, TopEntryPoint};
1515
#[path = "../test_data/generated/runner.rs"]
1616
mod runner;
1717

18+
fn infer_edition(file_path: &Path) -> Edition {
19+
let file_content = std::fs::read_to_string(file_path).unwrap();
20+
if let Some(edition) = file_content.strip_prefix("//@ edition: ") {
21+
edition[..4].parse().expect("invalid edition directive")
22+
} else {
23+
Edition::CURRENT
24+
}
25+
}
26+
1827
#[test]
1928
fn lex_ok() {
2029
for case in TestCase::list("lexer/ok") {
2130
let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
22-
let actual = lex(&case.text);
31+
let actual = lex(&case.text, infer_edition(&case.rs));
2332
expect_file![case.rast].assert_eq(&actual)
2433
}
2534
}
@@ -28,13 +37,13 @@ fn lex_ok() {
2837
fn lex_err() {
2938
for case in TestCase::list("lexer/err") {
3039
let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
31-
let actual = lex(&case.text);
40+
let actual = lex(&case.text, infer_edition(&case.rs));
3241
expect_file![case.rast].assert_eq(&actual)
3342
}
3443
}
3544

36-
fn lex(text: &str) -> String {
37-
let lexed = LexedStr::new(Edition::CURRENT, text);
45+
fn lex(text: &str, edition: Edition) -> String {
46+
let lexed = LexedStr::new(edition, text);
3847

3948
let mut res = String::new();
4049
for i in 0..lexed.len() {
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
COMMENT "//@ edition: 2021"
2+
WHITESPACE "\n\n"
3+
POUND "#"
4+
STRING "\"foo\""
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
//@ edition: 2021
2+
3+
#"foo"

0 commit comments

Comments
 (0)