Rust: move body skipping logic to code generation

This commit is contained in:
Paolo Tranquilli
2025-05-22 14:44:42 +02:00
parent a4788fd816
commit 7e917c9c35
4 changed files with 145 additions and 700 deletions

View File

@@ -114,7 +114,9 @@ fn node_src_to_schema_class(
let (ty, child) = match &f.ty { let (ty, child) = match &f.ty {
FieldType::String => ("optional[string]".to_string(), false), FieldType::String => ("optional[string]".to_string(), false),
FieldType::Predicate => ("predicate".to_string(), false), FieldType::Predicate => ("predicate".to_string(), false),
FieldType::Optional(ty) => (format!("optional[\"{}\"]", class_name(ty)), true), FieldType::Optional(ty) | FieldType::Body(ty) => {
(format!("optional[\"{}\"]", class_name(ty)), true)
}
FieldType::List(ty) => (format!("list[\"{}\"]", class_name(ty)), true), FieldType::List(ty) => (format!("list[\"{}\"]", class_name(ty)), true),
}; };
SchemaField { SchemaField {
@@ -169,6 +171,7 @@ enum FieldType {
String, String,
Predicate, Predicate,
Optional(String), Optional(String),
Body(String),
List(String), List(String),
} }
@@ -177,158 +180,93 @@ struct FieldInfo {
ty: FieldType, ty: FieldType,
} }
impl FieldInfo {
pub fn optional(name: &str, ty: &str) -> FieldInfo {
FieldInfo {
name: name.to_string(),
ty: FieldType::Optional(ty.to_string()),
}
}
pub fn body(name: &str, ty: &str) -> FieldInfo {
FieldInfo {
name: name.to_string(),
ty: FieldType::Body(ty.to_string()),
}
}
pub fn string(name: &str) -> FieldInfo {
FieldInfo {
name: name.to_string(),
ty: FieldType::String,
}
}
pub fn predicate(name: &str) -> FieldInfo {
FieldInfo {
name: name.to_string(),
ty: FieldType::Predicate,
}
}
pub fn list(name: &str, ty: &str) -> FieldInfo {
FieldInfo {
name: name.to_string(),
ty: FieldType::List(ty.to_string()),
}
}
}
fn get_additional_fields(node: &AstNodeSrc) -> Vec<FieldInfo> { fn get_additional_fields(node: &AstNodeSrc) -> Vec<FieldInfo> {
match node.name.as_str() { match node.name.as_str() {
"Name" | "NameRef" | "Lifetime" => vec![FieldInfo { "Name" | "NameRef" | "Lifetime" => vec![FieldInfo::string("text")],
name: "text".to_string(), "Abi" => vec![FieldInfo::string("abi_string")],
ty: FieldType::String, "Literal" => vec![FieldInfo::string("text_value")],
}], "PrefixExpr" => vec![FieldInfo::string("operator_name")],
"Abi" => vec![FieldInfo {
name: "abi_string".to_string(),
ty: FieldType::String,
}],
"Literal" => vec![FieldInfo {
name: "text_value".to_string(),
ty: FieldType::String,
}],
"PrefixExpr" => vec![FieldInfo {
name: "operator_name".to_string(),
ty: FieldType::String,
}],
"BinExpr" => vec![ "BinExpr" => vec![
FieldInfo { FieldInfo::optional("lhs", "Expr"),
name: "lhs".to_string(), FieldInfo::optional("rhs", "Expr"),
ty: FieldType::Optional("Expr".to_string()), FieldInfo::string("operator_name"),
},
FieldInfo {
name: "rhs".to_string(),
ty: FieldType::Optional("Expr".to_string()),
},
FieldInfo {
name: "operator_name".to_string(),
ty: FieldType::String,
},
], ],
"IfExpr" => vec![ "IfExpr" => vec![
FieldInfo { FieldInfo::optional("then_branch", "BlockExpr"),
name: "then_branch".to_string(), FieldInfo::optional("else_branch", "ElseBranch"),
ty: FieldType::Optional("BlockExpr".to_string()), FieldInfo::optional("condition", "Expr"),
},
FieldInfo {
name: "else_branch".to_string(),
ty: FieldType::Optional("ElseBranch".to_string()),
},
FieldInfo {
name: "condition".to_string(),
ty: FieldType::Optional("Expr".to_string()),
},
], ],
"RangeExpr" => vec![ "RangeExpr" => vec![
FieldInfo { FieldInfo::optional("start", "Expr"),
name: "start".to_string(), FieldInfo::optional("end", "Expr"),
ty: FieldType::Optional("Expr".to_string()), FieldInfo::string("operator_name"),
},
FieldInfo {
name: "end".to_string(),
ty: FieldType::Optional("Expr".to_string()),
},
FieldInfo {
name: "operator_name".to_string(),
ty: FieldType::String,
},
], ],
"RangePat" => vec![ "RangePat" => vec![
FieldInfo { FieldInfo::optional("start", "Pat"),
name: "start".to_string(), FieldInfo::optional("end", "Pat"),
ty: FieldType::Optional("Pat".to_string()), FieldInfo::string("operator_name"),
},
FieldInfo {
name: "end".to_string(),
ty: FieldType::Optional("Pat".to_string()),
},
FieldInfo {
name: "operator_name".to_string(),
ty: FieldType::String,
},
], ],
"IndexExpr" => vec![ "IndexExpr" => vec![
FieldInfo { FieldInfo::optional("index", "Expr"),
name: "index".to_string(), FieldInfo::optional("base", "Expr"),
ty: FieldType::Optional("Expr".to_string()),
},
FieldInfo {
name: "base".to_string(),
ty: FieldType::Optional("Expr".to_string()),
},
], ],
"Impl" => vec![ "Impl" => vec![
FieldInfo { FieldInfo::optional("trait_", "Type"),
name: "trait_".to_string(), FieldInfo::optional("self_ty", "Type"),
ty: FieldType::Optional("Type".to_string()),
},
FieldInfo {
name: "self_ty".to_string(),
ty: FieldType::Optional("Type".to_string()),
},
], ],
"ForExpr" => vec![FieldInfo { "ForExpr" => vec![FieldInfo::optional("iterable", "Expr")],
name: "iterable".to_string(), "WhileExpr" => vec![FieldInfo::optional("condition", "Expr")],
ty: FieldType::Optional("Expr".to_string()), "MatchGuard" => vec![FieldInfo::optional("condition", "Expr")],
}],
"WhileExpr" => vec![FieldInfo {
name: "condition".to_string(),
ty: FieldType::Optional("Expr".to_string()),
}],
"MatchGuard" => vec![FieldInfo {
name: "condition".to_string(),
ty: FieldType::Optional("Expr".to_string()),
}],
"MacroDef" => vec![ "MacroDef" => vec![
FieldInfo { FieldInfo::optional("args", "TokenTree"),
name: "args".to_string(), FieldInfo::optional("body", "TokenTree"),
ty: FieldType::Optional("TokenTree".to_string()),
},
FieldInfo {
name: "body".to_string(),
ty: FieldType::Optional("TokenTree".to_string()),
},
], ],
"FormatArgsExpr" => vec![FieldInfo { "FormatArgsExpr" => vec![FieldInfo::list("args", "FormatArgsArg")],
name: "args".to_string(), "ArgList" => vec![FieldInfo::list("args", "Expr")],
ty: FieldType::List("FormatArgsArg".to_string()), "Fn" => vec![FieldInfo::body("body", "BlockExpr")],
}], "Const" => vec![FieldInfo::body("body", "Expr")],
"ArgList" => vec![FieldInfo { "Static" => vec![FieldInfo::body("body", "Expr")],
name: "args".to_string(), "ClosureExpr" => vec![FieldInfo::optional("body", "Expr")],
ty: FieldType::List("Expr".to_string()), "ArrayExpr" => vec![FieldInfo::predicate("is_semicolon")],
}], "SelfParam" => vec![FieldInfo::predicate("is_amp")],
"Fn" => vec![FieldInfo { "UseTree" => vec![FieldInfo::predicate("is_star")],
name: "body".to_string(),
ty: FieldType::Optional("BlockExpr".to_string()),
}],
"Const" => vec![FieldInfo {
name: "body".to_string(),
ty: FieldType::Optional("Expr".to_string()),
}],
"Static" => vec![FieldInfo {
name: "body".to_string(),
ty: FieldType::Optional("Expr".to_string()),
}],
"ClosureExpr" => vec![FieldInfo {
name: "body".to_string(),
ty: FieldType::Optional("Expr".to_string()),
}],
"ArrayExpr" => vec![FieldInfo {
name: "is_semicolon".to_string(),
ty: FieldType::Predicate,
}],
"SelfParam" => vec![FieldInfo {
name: "is_amp".to_string(),
ty: FieldType::Predicate,
}],
"UseTree" => vec![FieldInfo {
name: "is_star".to_string(),
ty: FieldType::Predicate,
}],
_ => vec![], _ => vec![],
} }
} }
@@ -356,6 +294,10 @@ fn get_fields(node: &AstNodeSrc) -> Vec<FieldInfo> {
("ArrayExpr", "expr") // The ArrayExpr type also has an 'exprs' field ("ArrayExpr", "expr") // The ArrayExpr type also has an 'exprs' field
| ("PathSegment", "ty" | "path_type") // these are broken, handling them manually | ("PathSegment", "ty" | "path_type") // these are broken, handling them manually
=> continue, => continue,
("Param", "pat") => {
result.push(FieldInfo::body("pat", "Pat"));
continue;
},
_ => {} _ => {}
} }
let ty = match field { let ty = match field {
@@ -374,54 +316,26 @@ fn get_fields(node: &AstNodeSrc) -> Vec<FieldInfo> {
} }
for trait_ in &node.traits { for trait_ in &node.traits {
match trait_.as_str() { match trait_.as_str() {
"HasAttrs" => result.push(FieldInfo { "HasAttrs" => result.push(FieldInfo::list("attrs", "Attr")),
name: "attrs".to_owned(), "HasName" => result.push(FieldInfo::optional("name", "Name")),
ty: FieldType::List("Attr".to_owned()), "HasVisibility" => result.push(FieldInfo::optional("visibility", "Visibility")),
}),
"HasName" => result.push(FieldInfo {
name: "name".to_owned(),
ty: FieldType::Optional("Name".to_owned()),
}),
"HasVisibility" => result.push(FieldInfo {
name: "visibility".to_owned(),
ty: FieldType::Optional("Visibility".to_owned()),
}),
"HasGenericParams" => { "HasGenericParams" => {
result.push(FieldInfo { result.push(FieldInfo::optional(
name: "generic_param_list".to_owned(), "generic_param_list",
ty: FieldType::Optional("GenericParamList".to_owned()), "GenericParamList",
}); ));
result.push(FieldInfo { result.push(FieldInfo::optional("where_clause", "WhereClause"))
name: "where_clause".to_owned(),
ty: FieldType::Optional("WhereClause".to_owned()),
})
} }
"HasGenericArgs" => result.push(FieldInfo { "HasGenericArgs" => {
name: "generic_arg_list".to_owned(), result.push(FieldInfo::optional("generic_arg_list", "GenericArgList"))
ty: FieldType::Optional("GenericArgList".to_owned()), }
}), "HasTypeBounds" => result.push(FieldInfo::optional("type_bound_list", "TypeBoundList")),
"HasTypeBounds" => result.push(FieldInfo { "HasModuleItem" => result.push(FieldInfo::list("items", "Item")),
name: "type_bound_list".to_owned(),
ty: FieldType::Optional("TypeBoundList".to_owned()),
}),
"HasModuleItem" => result.push(FieldInfo {
name: "items".to_owned(),
ty: FieldType::List("Item".to_owned()),
}),
"HasLoopBody" => { "HasLoopBody" => {
result.push(FieldInfo { result.push(FieldInfo::optional("label", "Label"));
name: "label".to_owned(), result.push(FieldInfo::optional("loop_body", "BlockExpr"))
ty: FieldType::Optional("Label".to_owned()),
});
result.push(FieldInfo {
name: "loop_body".to_owned(),
ty: FieldType::Optional("BlockExpr".to_owned()),
})
} }
"HasArgList" => result.push(FieldInfo { "HasArgList" => result.push(FieldInfo::optional("arg_list", "ArgList")),
name: "arg_list".to_owned(),
ty: FieldType::Optional("ArgList".to_owned()),
}),
"HasDocComments" => {} "HasDocComments" => {}
_ => panic!("Unknown trait {}", trait_), _ => panic!("Unknown trait {}", trait_),
@@ -455,6 +369,7 @@ struct ExtractorNodeFieldInfo {
predicate: bool, predicate: bool,
optional: bool, optional: bool,
list: bool, list: bool,
body: bool,
} }
#[derive(Serialize)] #[derive(Serialize)]
@@ -518,6 +433,13 @@ fn field_info_to_extractor_info(name: &str, field: &FieldInfo) -> ExtractorNodeF
optional: true, optional: true,
..Default::default() ..Default::default()
}, },
FieldType::Body(ty) => ExtractorNodeFieldInfo {
name,
method: field.name.clone(),
snake_case_ty: to_lower_snake_case(ty),
body: true,
..Default::default()
},
FieldType::List(ty) => ExtractorNodeFieldInfo { FieldType::List(ty) => ExtractorNodeFieldInfo {
name, name,
method: field.name.clone(), method: field.name.clone(),

View File

@@ -34,23 +34,27 @@ impl Translator<'_> {
{{#nodes}} {{#nodes}}
pub(crate) fn emit_{{snake_case_name}}(&mut self, node: &ast::{{ast_name}}) -> Option<Label<generated::{{name}}>> { pub(crate) fn emit_{{snake_case_name}}(&mut self, node: &ast::{{ast_name}}) -> Option<Label<generated::{{name}}>> {
if self.should_be_excluded(node) { return None; }
{{#has_attrs}} {{#has_attrs}}
if self.should_be_excluded_attrs(node) { return None; } if self.should_be_excluded(node) { return None; }
{{/has_attrs}} {{/has_attrs}}
{{#fields}} {{#fields}}
{{#predicate}} let {{name}} =
let {{name}} = node.{{method}}().is_some(); {{#predicate}}
{{/predicate}} node.{{method}}().is_some()
{{#string}} {{/predicate}}
let {{name}} = node.try_get_text(); {{#string}}
{{/string}} node.try_get_text()
{{#list}} {{/string}}
let {{name}} = node.{{method}}().filter_map(|x| self.emit_{{snake_case_ty}}(&x)).collect(); {{#list}}
{{/list}} node.{{method}}().filter_map(|x| self.emit_{{snake_case_ty}}(&x)).collect()
{{#optional}} {{/list}}
let {{name}} = node.{{method}}().and_then(|x| self.emit_{{snake_case_ty}}(&x)); {{#optional}}
{{/optional}} node.{{method}}().and_then(|x| self.emit_{{snake_case_ty}}(&x))
{{/optional}}
{{#body}}
if self.should_skip_bodies() { None } else { node.{{method}}().and_then(|x| self.emit_{{snake_case_ty}}(&x)) }
{{/body}}
;
{{/fields}} {{/fields}}
let label = self.trap.emit(generated::{{name}} { let label = self.trap.emit(generated::{{name}} {
id: TrapId::Star, id: TrapId::Star,

View File

@@ -16,7 +16,7 @@ use ra_ap_ide_db::RootDatabase;
use ra_ap_ide_db::line_index::{LineCol, LineIndex}; use ra_ap_ide_db::line_index::{LineCol, LineIndex};
use ra_ap_parser::SyntaxKind; use ra_ap_parser::SyntaxKind;
use ra_ap_span::TextSize; use ra_ap_span::TextSize;
use ra_ap_syntax::ast::{Const, Fn, HasName, Param, Static}; use ra_ap_syntax::ast::HasName;
use ra_ap_syntax::{ use ra_ap_syntax::{
AstNode, NodeOrToken, SyntaxElementChildren, SyntaxError, SyntaxNode, SyntaxToken, TextRange, AstNode, NodeOrToken, SyntaxElementChildren, SyntaxError, SyntaxNode, SyntaxToken, TextRange,
ast, ast,
@@ -627,7 +627,7 @@ impl<'a> Translator<'a> {
})(); })();
} }
pub(crate) fn should_be_excluded_attrs(&self, item: &impl ast::HasAttrs) -> bool { pub(crate) fn should_be_excluded(&self, item: &impl ast::HasAttrs) -> bool {
self.semantics.is_some_and(|sema| { self.semantics.is_some_and(|sema| {
item.attrs().any(|attr| { item.attrs().any(|attr| {
attr.as_simple_call().is_some_and(|(name, tokens)| { attr.as_simple_call().is_some_and(|(name, tokens)| {
@@ -637,43 +637,8 @@ impl<'a> Translator<'a> {
}) })
} }
pub(crate) fn should_be_excluded(&self, item: &impl ast::AstNode) -> bool { pub(crate) fn should_skip_bodies(&self) -> bool {
if self.source_kind == SourceKind::Library { self.source_kind == SourceKind::Library
let syntax = item.syntax();
if syntax
.parent()
.and_then(Fn::cast)
.and_then(|x| x.body())
.is_some_and(|body| body.syntax() == syntax)
{
return true;
}
if syntax
.parent()
.and_then(Const::cast)
.and_then(|x| x.body())
.is_some_and(|body| body.syntax() == syntax)
{
return true;
}
if syntax
.parent()
.and_then(Static::cast)
.and_then(|x| x.body())
.is_some_and(|body| body.syntax() == syntax)
{
return true;
}
if syntax
.parent()
.and_then(Param::cast)
.and_then(|x| x.pat())
.is_some_and(|pat| pat.syntax() == syntax)
{
return true;
}
}
false
} }
pub(crate) fn extract_types_from_path_segment( pub(crate) fn extract_types_from_path_segment(

File diff suppressed because it is too large Load Diff