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

View File

@@ -34,23 +34,27 @@ impl Translator<'_> {
{{#nodes}}
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}}
if self.should_be_excluded_attrs(node) { return None; }
if self.should_be_excluded(node) { return None; }
{{/has_attrs}}
{{#fields}}
{{#predicate}}
let {{name}} = node.{{method}}().is_some();
{{/predicate}}
{{#string}}
let {{name}} = node.try_get_text();
{{/string}}
{{#list}}
let {{name}} = node.{{method}}().filter_map(|x| self.emit_{{snake_case_ty}}(&x)).collect();
{{/list}}
{{#optional}}
let {{name}} = node.{{method}}().and_then(|x| self.emit_{{snake_case_ty}}(&x));
{{/optional}}
let {{name}} =
{{#predicate}}
node.{{method}}().is_some()
{{/predicate}}
{{#string}}
node.try_get_text()
{{/string}}
{{#list}}
node.{{method}}().filter_map(|x| self.emit_{{snake_case_ty}}(&x)).collect()
{{/list}}
{{#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}}
let label = self.trap.emit(generated::{{name}} {
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_parser::SyntaxKind;
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::{
AstNode, NodeOrToken, SyntaxElementChildren, SyntaxError, SyntaxNode, SyntaxToken, TextRange,
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| {
item.attrs().any(|attr| {
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 {
if 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 should_skip_bodies(&self) -> bool {
self.source_kind == SourceKind::Library
}
pub(crate) fn extract_types_from_path_segment(

File diff suppressed because it is too large Load Diff