From f4199f786eb9dcdaf8e95038d129fe9a13f7a568 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 19 Jul 2024 20:20:00 +0200 Subject: [PATCH 1/2] Parse contextual dyn keyword properly in edition 2015 --- crates/mbe/src/to_parser_input.rs | 5 +- crates/parser/src/grammar/paths.rs | 2 + crates/parser/src/grammar/types.rs | 21 ++++ crates/parser/src/parser.rs | 8 +- crates/parser/src/shortcuts.rs | 11 +- crates/parser/src/tests.rs | 2 +- crates/parser/src/tests/prefix_entries.rs | 2 +- crates/parser/test_data/generated/runner.rs | 7 ++ .../parser/inline/ok/dyn_trait_type_weak.rast | 113 ++++++++++++++++++ .../parser/inline/ok/dyn_trait_type_weak.rs | 4 + 10 files changed, 164 insertions(+), 11 deletions(-) create mode 100644 crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast create mode 100644 crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs diff --git a/crates/mbe/src/to_parser_input.rs b/crates/mbe/src/to_parser_input.rs index 66db525362..c35b28527a 100644 --- a/crates/mbe/src/to_parser_input.rs +++ b/crates/mbe/src/to_parser_input.rs @@ -65,9 +65,8 @@ pub(crate) fn to_parser_input( i if i.starts_with('\'') => res.push(LIFETIME_IDENT), _ if ident.is_raw.yes() => res.push(IDENT), "gen" if !edition.at_least_2024() => res.push(IDENT), - "async" | "await" | "dyn" | "try" if !edition.at_least_2018() => { - res.push(IDENT) - } + "dyn" if !edition.at_least_2018() => res.push_ident(DYN_KW), + "async" | "await" | "try" if !edition.at_least_2018() => res.push(IDENT), text => match SyntaxKind::from_keyword(text) { Some(kind) => res.push(kind), None => { diff --git a/crates/parser/src/grammar/paths.rs b/crates/parser/src/grammar/paths.rs index 01b8f9e918..86e19fbe5f 100644 --- a/crates/parser/src/grammar/paths.rs +++ b/crates/parser/src/grammar/paths.rs @@ -2,6 +2,8 @@ use super::*; pub(super) const PATH_FIRST: TokenSet = TokenSet::new(&[IDENT, T![self], T![super], T![crate], T![Self], T![:], T![<]]); +pub(super) const WEAK_DYN_PATH_FIRST: TokenSet = + TokenSet::new(&[IDENT, T![self], T![super], T![crate], T![Self]]); pub(super) fn is_path_start(p: &Parser<'_>) -> bool { is_use_path_start(p) || p.at(T![<]) || p.at(T![Self]) diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs index f95425824a..9a1c6dfdf7 100644 --- a/crates/parser/src/grammar/types.rs +++ b/crates/parser/src/grammar/types.rs @@ -1,3 +1,5 @@ +use crate::grammar::paths::WEAK_DYN_PATH_FIRST; + use super::*; pub(super) const TYPE_FIRST: TokenSet = paths::PATH_FIRST.union(TokenSet::new(&[ @@ -49,6 +51,13 @@ fn type_with_bounds_cond(p: &mut Parser<'_>, allow_bounds: bool) { T![dyn] => dyn_trait_type(p), // Some path types are not allowed to have bounds (no plus) T![<] => path_type_bounds(p, allow_bounds), + T![ident] + if !p.edition().at_least_2018() + && p.at_contextual_kw(T![dyn]) + && WEAK_DYN_PATH_FIRST.contains(p.nth(1)) => + { + dyn_trait_type_weak(p) + } _ if paths::is_path_start(p) => path_or_macro_type_(p, allow_bounds), LIFETIME_IDENT if p.nth_at(1, T![+]) => bare_dyn_trait_type(p), _ => { @@ -279,6 +288,18 @@ fn dyn_trait_type(p: &mut Parser<'_>) { m.complete(p, DYN_TRAIT_TYPE); } +// test dyn_trait_type_weak 2015 +// type A = dyn Iterator> + 'a; +// type A = &dyn Iterator> + 'a; +// type A = dyn::Path; +fn dyn_trait_type_weak(p: &mut Parser<'_>) { + assert!(p.at_contextual_kw(T![dyn])); + let m = p.start(); + p.bump_remap(T![dyn]); + generic_params::bounds_without_colon(p); + m.complete(p, DYN_TRAIT_TYPE); +} + // test bare_dyn_types_with_leading_lifetime // type A = 'static + Trait; // type B = S<'static + Trait>; diff --git a/crates/parser/src/parser.rs b/crates/parser/src/parser.rs index 5b901f911d..40e3c11a1d 100644 --- a/crates/parser/src/parser.rs +++ b/crates/parser/src/parser.rs @@ -27,14 +27,14 @@ pub(crate) struct Parser<'t> { pos: usize, events: Vec, steps: Cell, - _edition: Edition, + edition: Edition, } static PARSER_STEP_LIMIT: Limit = Limit::new(15_000_000); impl<'t> Parser<'t> { pub(super) fn new(inp: &'t Input, edition: Edition) -> Parser<'t> { - Parser { inp, pos: 0, events: Vec::new(), steps: Cell::new(0), _edition: edition } + Parser { inp, pos: 0, events: Vec::new(), steps: Cell::new(0), edition: edition } } pub(crate) fn finish(self) -> Vec { @@ -277,6 +277,10 @@ impl<'t> Parser<'t> { fn push_event(&mut self, event: Event) { self.events.push(event); } + + pub(crate) fn edition(&self) -> Edition { + self.edition + } } /// See [`Parser::start`]. diff --git a/crates/parser/src/shortcuts.rs b/crates/parser/src/shortcuts.rs index 7f49cc087a..1cf81e79b0 100644 --- a/crates/parser/src/shortcuts.rs +++ b/crates/parser/src/shortcuts.rs @@ -12,7 +12,7 @@ use std::mem; use crate::{ - LexedStr, Step, + Edition, LexedStr, Step, SyntaxKind::{self, *}, }; @@ -25,7 +25,7 @@ pub enum StrStep<'a> { } impl LexedStr<'_> { - pub fn to_input(&self) -> crate::Input { + pub fn to_input(&self, edition: Edition) -> crate::Input { let _p = tracing::info_span!("LexedStr::to_input").entered(); let mut res = crate::Input::default(); let mut was_joint = false; @@ -35,8 +35,11 @@ impl LexedStr<'_> { was_joint = false } else if kind == SyntaxKind::IDENT { let token_text = self.text(i); - let contextual_kw = - SyntaxKind::from_contextual_keyword(token_text).unwrap_or(SyntaxKind::IDENT); + let contextual_kw = if !edition.at_least_2018() && token_text == "dyn" { + SyntaxKind::DYN_KW + } else { + SyntaxKind::from_contextual_keyword(token_text).unwrap_or(SyntaxKind::IDENT) + }; res.push_ident(contextual_kw); } else { if was_joint { diff --git a/crates/parser/src/tests.rs b/crates/parser/src/tests.rs index d35d2d3b03..e7bccb6685 100644 --- a/crates/parser/src/tests.rs +++ b/crates/parser/src/tests.rs @@ -70,7 +70,7 @@ fn parse_err() { fn parse(entry: TopEntryPoint, text: &str, edition: Edition) -> (String, bool) { let lexed = LexedStr::new(edition, text); - let input = lexed.to_input(); + let input = lexed.to_input(edition); let output = entry.parse(&input, edition); let mut buf = String::new(); diff --git a/crates/parser/src/tests/prefix_entries.rs b/crates/parser/src/tests/prefix_entries.rs index 4d4ab345d9..e2268eed60 100644 --- a/crates/parser/src/tests/prefix_entries.rs +++ b/crates/parser/src/tests/prefix_entries.rs @@ -83,7 +83,7 @@ fn meta_item() { #[track_caller] fn check(entry: PrefixEntryPoint, input: &str, prefix: &str) { let lexed = LexedStr::new(Edition::CURRENT, input); - let input = lexed.to_input(); + let input = lexed.to_input(Edition::CURRENT); let mut n_tokens = 0; for step in entry.parse(&input, Edition::CURRENT).iter() { diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs index d0e6b3f6c9..a935fe4ee3 100644 --- a/crates/parser/test_data/generated/runner.rs +++ b/crates/parser/test_data/generated/runner.rs @@ -195,6 +195,13 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/dyn_trait_type.rs"); } #[test] + fn dyn_trait_type_weak() { + run_and_expect_no_errors_with_edition( + "test_data/parser/inline/ok/dyn_trait_type_weak.rs", + crate::Edition::Edition2015, + ); + } + #[test] fn effect_blocks() { run_and_expect_no_errors("test_data/parser/inline/ok/effect_blocks.rs"); } #[test] fn exclusive_range_pat() { diff --git a/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast b/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast new file mode 100644 index 0000000000..00ca92402c --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast @@ -0,0 +1,113 @@ +SOURCE_FILE + TYPE_ALIAS + COMMENT "// 2015" + WHITESPACE "\n" + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "A" + WHITESPACE " " + EQ "=" + WHITESPACE " " + DYN_TRAIT_TYPE + DYN_KW "dyn" + WHITESPACE " " + TYPE_BOUND_LIST + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Iterator" + GENERIC_ARG_LIST + L_ANGLE "<" + ASSOC_TYPE_ARG + NAME_REF + IDENT "Item" + EQ "=" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Foo" + GENERIC_ARG_LIST + L_ANGLE "<" + LIFETIME_ARG + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" + R_ANGLE ">" + WHITESPACE " " + PLUS "+" + WHITESPACE " " + TYPE_BOUND + LIFETIME + LIFETIME_IDENT "'a" + SEMICOLON ";" + WHITESPACE "\n" + TYPE_ALIAS + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "A" + WHITESPACE " " + EQ "=" + WHITESPACE " " + REF_TYPE + AMP "&" + DYN_TRAIT_TYPE + DYN_KW "dyn" + WHITESPACE " " + TYPE_BOUND_LIST + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Iterator" + GENERIC_ARG_LIST + L_ANGLE "<" + ASSOC_TYPE_ARG + NAME_REF + IDENT "Item" + EQ "=" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Foo" + GENERIC_ARG_LIST + L_ANGLE "<" + LIFETIME_ARG + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" + R_ANGLE ">" + WHITESPACE " " + PLUS "+" + WHITESPACE " " + TYPE_BOUND + LIFETIME + LIFETIME_IDENT "'a" + SEMICOLON ";" + WHITESPACE "\n" + TYPE_ALIAS + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "A" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATH_TYPE + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "dyn" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "Path" + SEMICOLON ";" + WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs b/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs new file mode 100644 index 0000000000..c4941e65bf --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs @@ -0,0 +1,4 @@ +// 2015 +type A = dyn Iterator> + 'a; +type A = &dyn Iterator> + 'a; +type A = dyn::Path; From 92f5e806f18c4f613a4ef568e942e2e2cd917824 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 19 Jul 2024 20:04:38 +0200 Subject: [PATCH 2/2] Fix edition used for include macro parsing --- crates/hir-expand/src/builtin_fn_macro.rs | 8 ++++---- crates/parser/src/parser.rs | 2 +- crates/rust-analyzer/src/cli/diagnostics.rs | 10 +++++++--- crates/syntax/src/parsing.rs | 4 ++-- crates/syntax/src/parsing/reparsing.rs | 2 +- xtask/src/metrics.rs | 4 ++-- 6 files changed, 17 insertions(+), 13 deletions(-) diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index 7f25b7dc03..6272e1df7d 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -18,7 +18,7 @@ use crate::{ name, quote, quote::dollar_crate, tt::{self, DelimSpan}, - ExpandError, ExpandResult, HirFileIdExt, MacroCallId, MacroFileIdExt, + ExpandError, ExpandResult, HirFileIdExt, Lookup as _, MacroCallId, }; macro_rules! register_builtin { @@ -687,8 +687,8 @@ fn relative_file( path_str: &str, allow_recursion: bool, ) -> Result { - let call_site = - call_id.as_macro_file().parent(db).original_file_respecting_includes(db).file_id(); + let lookup = call_id.lookup(db); + let call_site = lookup.kind.file_id().original_file_respecting_includes(db).file_id(); let path = AnchoredPath { anchor: call_site, path: path_str }; let res = db .resolve_path(path) @@ -697,7 +697,7 @@ fn relative_file( if res == call_site && !allow_recursion { Err(ExpandError::other(format!("recursive inclusion of `{path_str}`"))) } else { - Ok(EditionedFileId::new(res, Edition::CURRENT_FIXME)) + Ok(EditionedFileId::new(res, db.crate_graph()[lookup.krate].edition)) } } diff --git a/crates/parser/src/parser.rs b/crates/parser/src/parser.rs index 40e3c11a1d..7d3eb5de25 100644 --- a/crates/parser/src/parser.rs +++ b/crates/parser/src/parser.rs @@ -34,7 +34,7 @@ static PARSER_STEP_LIMIT: Limit = Limit::new(15_000_000); impl<'t> Parser<'t> { pub(super) fn new(inp: &'t Input, edition: Edition) -> Parser<'t> { - Parser { inp, pos: 0, events: Vec::new(), steps: Cell::new(0), edition: edition } + Parser { inp, pos: 0, events: Vec::new(), steps: Cell::new(0), edition } } pub(crate) fn finish(self) -> Vec { diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs index 489bb42eec..4ddeb4ab1b 100644 --- a/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/crates/rust-analyzer/src/cli/diagnostics.rs @@ -5,8 +5,8 @@ use project_model::{CargoConfig, RustLibSource}; use rustc_hash::FxHashSet; use hir::{db::HirDatabase, Crate, HirFileIdExt, Module}; -use ide::{AnalysisHost, AssistResolveStrategy, DiagnosticsConfig, Severity}; -use ide_db::base_db::SourceDatabaseExt; +use ide::{AnalysisHost, AssistResolveStrategy, Diagnostic, DiagnosticsConfig, Severity}; +use ide_db::{base_db::SourceDatabaseExt, LineIndexDatabase}; use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice}; use crate::cli::flags; @@ -74,7 +74,11 @@ impl flags::Diagnostics { found_error = true; } - println!("{diagnostic:?}"); + let Diagnostic { code, message, range, severity, .. } = diagnostic; + let line_index = db.line_index(range.file_id); + let start = line_index.line_col(range.range.start()); + let end = line_index.line_col(range.range.end()); + println!("{severity:?} {code:?} from {start:?} to {end:?}: {message}"); } visited_files.insert(file_id); diff --git a/crates/syntax/src/parsing.rs b/crates/syntax/src/parsing.rs index e52daa42f1..2c7828c052 100644 --- a/crates/syntax/src/parsing.rs +++ b/crates/syntax/src/parsing.rs @@ -12,7 +12,7 @@ pub(crate) use crate::parsing::reparsing::incremental_reparse; pub(crate) fn parse_text(text: &str, edition: parser::Edition) -> (GreenNode, Vec) { let _p = tracing::info_span!("parse_text").entered(); let lexed = parser::LexedStr::new(edition, text); - let parser_input = lexed.to_input(); + let parser_input = lexed.to_input(edition); let parser_output = parser::TopEntryPoint::SourceFile.parse(&parser_input, edition); let (node, errors, _eof) = build_tree(lexed, parser_output); (node, errors) @@ -25,7 +25,7 @@ pub(crate) fn parse_text_at( ) -> (GreenNode, Vec) { let _p = tracing::info_span!("parse_text_at").entered(); let lexed = parser::LexedStr::new(edition, text); - let parser_input = lexed.to_input(); + let parser_input = lexed.to_input(edition); let parser_output = entry.parse(&parser_input, edition); let (node, errors, _eof) = build_tree(lexed, parser_output); (node, errors) diff --git a/crates/syntax/src/parsing/reparsing.rs b/crates/syntax/src/parsing/reparsing.rs index a895d9e274..a5cc4e90df 100644 --- a/crates/syntax/src/parsing/reparsing.rs +++ b/crates/syntax/src/parsing/reparsing.rs @@ -92,7 +92,7 @@ fn reparse_block( let text = get_text_after_edit(node.clone().into(), edit); let lexed = parser::LexedStr::new(edition, text.as_str()); - let parser_input = lexed.to_input(); + let parser_input = lexed.to_input(edition); if !is_balanced(&lexed) { return None; } diff --git a/xtask/src/metrics.rs b/xtask/src/metrics.rs index 9a7785dd43..21001c28da 100644 --- a/xtask/src/metrics.rs +++ b/xtask/src/metrics.rs @@ -6,7 +6,7 @@ use std::{ time::{Instant, SystemTime, UNIX_EPOCH}, }; -use anyhow::{bail, format_err}; +use anyhow::format_err; use xshell::{cmd, Shell}; use crate::flags::{self, MeasurementType}; @@ -193,7 +193,7 @@ impl Metrics { impl Host { fn new(sh: &Shell) -> anyhow::Result { if cfg!(not(target_os = "linux")) { - bail!("can only collect metrics on Linux "); + return Ok(Host { os: "unknown".into(), cpu: "unknown".into(), mem: "unknown".into() }); } let os = read_field(sh, "/etc/os-release", "PRETTY_NAME=")?.trim_matches('"').to_owned();