Skip to content

Commit 355a4bd

Browse files
bors[bot]matklad
andauthored
Merge #11134
11134: internal: tighten up parser API r=matklad a=matklad It's tempting to expose things like `Expr::parse`, but they'll necessary have somewhat ad-hoc semantics. Instead, we narrow down the parser's interface strictly to what's needed for MBE. For everything else (eg, parsing imports), the proper way is enclose the input string into some context, parse the whole as a file, and then verify that the input was parsed as intended. bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
2 parents 4d3ad04 + ea96c37 commit 355a4bd

File tree

81 files changed

+331
-651
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+331
-651
lines changed

crates/hir/src/attrs.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use hir_def::{
1111
};
1212
use hir_expand::{hygiene::Hygiene, MacroDefId};
1313
use hir_ty::db::HirDatabase;
14-
use syntax::ast;
14+
use syntax::{ast, AstNode};
1515

1616
use crate::{
1717
Adt, AssocItem, Const, ConstParam, Enum, Field, Function, GenericParam, Impl, LifetimeParam,
@@ -147,8 +147,18 @@ fn resolve_doc_path(
147147
// FIXME
148148
AttrDefId::MacroDefId(_) => return None,
149149
};
150-
let path = ast::Path::parse(link).ok()?;
151-
let modpath = ModPath::from_src(db.upcast(), path, &Hygiene::new_unhygienic())?;
150+
151+
let modpath = {
152+
let ast_path = ast::SourceFile::parse(&format!("type T = {};", link))
153+
.syntax_node()
154+
.descendants()
155+
.find_map(ast::Path::cast)?;
156+
if ast_path.to_string() != link {
157+
return None;
158+
}
159+
ModPath::from_src(db.upcast(), ast_path, &Hygiene::new_unhygienic())?
160+
};
161+
152162
let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath);
153163
let resolved = if resolved == PerNs::none() {
154164
resolver.resolve_module_path_in_trait_assoc_items(db.upcast(), &modpath)?

crates/hir_def/src/attr.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -714,8 +714,7 @@ impl Attr {
714714
hygiene: &Hygiene,
715715
id: AttrId,
716716
) -> Option<Attr> {
717-
let (parse, _) =
718-
mbe::token_tree_to_syntax_node(tt, mbe::ParserEntryPoint::MetaItem).ok()?;
717+
let (parse, _) = mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MetaItem).ok()?;
719718
let ast = ast::Meta::cast(parse.syntax_node())?;
720719

721720
Self::from_src(db, ast, hygiene, id)

crates/hir_expand/src/builtin_derive_macro.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ struct BasicAdtInfo {
7272
}
7373

7474
fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, mbe::ExpandError> {
75-
let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, mbe::ParserEntryPoint::Items)?; // FragmentKind::Items doesn't parse attrs?
75+
let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MacroItems)?; // FragmentKind::Items doesn't parse attrs?
7676
let macro_items = ast::MacroItems::cast(parsed.syntax_node()).ok_or_else(|| {
7777
debug!("derive node didn't parse");
7878
mbe::ExpandError::UnexpectedToken

crates/hir_expand/src/db.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -497,11 +497,11 @@ fn token_tree_to_syntax_node(
497497
expand_to: ExpandTo,
498498
) -> Result<(Parse<SyntaxNode>, mbe::TokenMap), ExpandError> {
499499
let entry_point = match expand_to {
500-
ExpandTo::Statements => mbe::ParserEntryPoint::Statements,
501-
ExpandTo::Items => mbe::ParserEntryPoint::Items,
502-
ExpandTo::Pattern => mbe::ParserEntryPoint::Pattern,
503-
ExpandTo::Type => mbe::ParserEntryPoint::Type,
504-
ExpandTo::Expr => mbe::ParserEntryPoint::Expr,
500+
ExpandTo::Statements => mbe::TopEntryPoint::MacroStmts,
501+
ExpandTo::Items => mbe::TopEntryPoint::MacroItems,
502+
ExpandTo::Pattern => mbe::TopEntryPoint::Pattern,
503+
ExpandTo::Type => mbe::TopEntryPoint::Type,
504+
ExpandTo::Expr => mbe::TopEntryPoint::Expr,
505505
};
506506
mbe::token_tree_to_syntax_node(tt, entry_point)
507507
}

crates/hir_expand/src/eager.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ pub fn expand_eager_macro(
131131
let arg_file_id = arg_id;
132132

133133
let parsed_args = diagnostic_sink
134-
.result(mbe::token_tree_to_syntax_node(&parsed_args, mbe::ParserEntryPoint::Expr))?
134+
.result(mbe::token_tree_to_syntax_node(&parsed_args, mbe::TopEntryPoint::Expr))?
135135
.0;
136136
let result = eager_macro_recur(
137137
db,

crates/ide_assists/src/handlers/remove_dbg.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,8 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
3636
let input_expressions = input_expressions
3737
.into_iter()
3838
.filter_map(|(is_sep, group)| (!is_sep).then(|| group))
39-
.map(|mut tokens| ast::Expr::parse(&tokens.join("")))
40-
.collect::<Result<Vec<ast::Expr>, _>>()
41-
.ok()?;
39+
.map(|mut tokens| syntax::hacks::parse_expr_from_str(&tokens.join("")))
40+
.collect::<Option<Vec<ast::Expr>>>()?;
4241

4342
let parent = macro_call.syntax().parent()?;
4443
let (range, text) = match &*input_expressions {

crates/ide_completion/src/completions/attribute.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ fn parse_comma_sep_expr(input: ast::TokenTree) -> Option<Vec<ast::Expr>> {
309309
input_expressions
310310
.into_iter()
311311
.filter_map(|(is_sep, group)| (!is_sep).then(|| group))
312-
.filter_map(|mut tokens| ast::Expr::parse(&tokens.join("")).ok())
312+
.filter_map(|mut tokens| syntax::hacks::parse_expr_from_str(&tokens.join("")))
313313
.collect::<Vec<ast::Expr>>(),
314314
)
315315
}

crates/ide_completion/src/snippet.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -212,15 +212,14 @@ fn validate_snippet(
212212
) -> Option<(Box<[GreenNode]>, String, Option<Box<str>>)> {
213213
let mut imports = Vec::with_capacity(requires.len());
214214
for path in requires.iter() {
215-
let path = ast::Path::parse(path).ok()?;
216-
let valid_use_path = path.segments().all(|seg| {
217-
matches!(seg.kind(), Some(ast::PathSegmentKind::Name(_)))
218-
|| seg.generic_arg_list().is_none()
219-
});
220-
if !valid_use_path {
215+
let use_path = ast::SourceFile::parse(&format!("use {};", path))
216+
.syntax_node()
217+
.descendants()
218+
.find_map(ast::Path::cast)?;
219+
if use_path.syntax().text() != path.as_str() {
221220
return None;
222221
}
223-
let green = path.syntax().green().into_owned();
222+
let green = use_path.syntax().green().into_owned();
224223
imports.push(green);
225224
}
226225
let snippet = snippet.iter().join("\n");

crates/ide_db/src/helpers.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,11 @@ pub fn get_path_at_cursor_in_tt(cursor: &ast::Ident) -> Option<ast::Path> {
6767
.filter_map(SyntaxElement::into_token)
6868
.take_while(|tok| tok != cursor);
6969

70-
ast::Path::parse(&path_tokens.chain(iter::once(cursor.clone())).join("")).ok()
70+
syntax::hacks::parse_expr_from_str(&path_tokens.chain(iter::once(cursor.clone())).join(""))
71+
.and_then(|expr| match expr {
72+
ast::Expr::PathExpr(it) => it.path(),
73+
_ => None,
74+
})
7175
}
7276

7377
/// Parses and resolves the path at the cursor position in the given attribute, if it is a derive.
@@ -323,7 +327,12 @@ pub fn parse_tt_as_comma_sep_paths(input: ast::TokenTree) -> Option<Vec<ast::Pat
323327
let paths = input_expressions
324328
.into_iter()
325329
.filter_map(|(is_sep, group)| (!is_sep).then(|| group))
326-
.filter_map(|mut tokens| ast::Path::parse(&tokens.join("")).ok())
330+
.filter_map(|mut tokens| {
331+
syntax::hacks::parse_expr_from_str(&tokens.join("")).and_then(|expr| match expr {
332+
ast::Expr::PathExpr(it) => it.path(),
333+
_ => None,
334+
})
335+
})
327336
.collect();
328337
Some(paths)
329338
}

crates/ide_ssr/src/fragments.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//! When specifying SSR rule, you generally want to map one *kind* of thing to
2+
//! the same kind of thing: path to path, expression to expression, type to
3+
//! type.
4+
//!
5+
//! The problem is, while this *kind* is generally obvious to the human, the ide
6+
//! needs to determine it somehow. We do this in a stupid way -- by pasting SSR
7+
//! rule into different contexts and checking what works.
8+
9+
use syntax::{ast, AstNode, SyntaxNode};
10+
11+
pub(crate) fn ty(s: &str) -> Result<SyntaxNode, ()> {
12+
fragment::<ast::Type>("type T = {};", s)
13+
}
14+
15+
pub(crate) fn item(s: &str) -> Result<SyntaxNode, ()> {
16+
fragment::<ast::Item>("{}", s)
17+
}
18+
19+
pub(crate) fn pat(s: &str) -> Result<SyntaxNode, ()> {
20+
fragment::<ast::Pat>("const _: () = {let {} = ();};", s)
21+
}
22+
23+
pub(crate) fn expr(s: &str) -> Result<SyntaxNode, ()> {
24+
fragment::<ast::Expr>("const _: () = {};", s)
25+
}
26+
27+
pub(crate) fn stmt(s: &str) -> Result<SyntaxNode, ()> {
28+
let template = "const _: () = { {}; };";
29+
let input = template.replace("{}", s);
30+
let parse = syntax::SourceFile::parse(&input);
31+
if !parse.errors().is_empty() {
32+
return Err(());
33+
}
34+
let mut node =
35+
parse.tree().syntax().descendants().skip(2).find_map(ast::Stmt::cast).ok_or(())?;
36+
if !s.ends_with(';') && node.to_string().ends_with(';') {
37+
node = node.clone_for_update();
38+
node.syntax().last_token().map(|it| it.detach());
39+
}
40+
if node.to_string() != s {
41+
return Err(());
42+
}
43+
Ok(node.syntax().clone_subtree())
44+
}
45+
46+
fn fragment<T: AstNode>(template: &str, s: &str) -> Result<SyntaxNode, ()> {
47+
let s = s.trim();
48+
let input = template.replace("{}", s);
49+
let parse = syntax::SourceFile::parse(&input);
50+
if !parse.errors().is_empty() {
51+
return Err(());
52+
}
53+
let node = parse.tree().syntax().descendants().find_map(T::cast).ok_or(())?;
54+
if node.syntax().text() != s {
55+
return Err(());
56+
}
57+
Ok(node.syntax().clone_subtree())
58+
}

0 commit comments

Comments
 (0)