Add tree_print for a whole module
Some checks are pending
ELP CI / ci (26, 26.2.5.13, linux, 26.2, ubuntu-22.04-arm, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, true, linux-arm64) (push) Blocked by required conditions
ELP CI / ci (26, 26.2.5.13, macos, 26.2, macos-13, macos-13-x64, x86_64-apple-darwin, true, darwin-x64) (push) Blocked by required conditions
ELP CI / ci (26, 26.2.5.13, macos, 26.2, macos-latest, macos-latest-arm, aarch64-apple-darwin, true, darwin-arm64) (push) Blocked by required conditions
ELP CI / ci (26, 26.2.5.13, windows, 26.2, windows-2022, windows-2022-x64, x86_64-pc-windows-msvc, true, win32-x64) (push) Blocked by required conditions
ELP CI / ci (27, 27.3.4, linux, 27.3, ubuntu-22.04, ubuntu-22.04-x64, x86_64-unknown-linux-gnu, false, linux-x64) (push) Blocked by required conditions
ELP CI / ci (27, 27.3.4, linux, 27.3, ubuntu-22.04-arm, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, false, linux-arm64) (push) Blocked by required conditions
ELP CI / ci (27, 27.3.4, macos, 27.3, macos-13, macos-13-x64, x86_64-apple-darwin, false, darwin-x64) (push) Blocked by required conditions
ELP CI / ci (27, 27.3.4, macos, 27.3, macos-latest, macos-latest-arm, aarch64-apple-darwin, false, darwin-arm64) (push) Blocked by required conditions
ELP CI / ci (27, 27.3.4, windows, 27.3, windows-2022, windows-2022-x64, x86_64-pc-windows-msvc, false, win32-x64) (push) Blocked by required conditions
ELP CI / ci (28, 28.0.1, linux, 28, ubuntu-22.04, ubuntu-22.04-x64, x86_64-unknown-linux-gnu, false, linux-x64) (push) Blocked by required conditions
ELP CI / ci (28, 28.0.1, linux, 28, ubuntu-22.04-arm, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, false, linux-arm64) (push) Blocked by required conditions
ELP CI / ci (28, 28.0.1, macos, 28, macos-13, macos-13-x64, x86_64-apple-darwin, false, darwin-x64) (push) Blocked by required conditions
ELP CI / ci (28, 28.0.1, macos, 28, macos-latest, macos-latest-arm, aarch64-apple-darwin, false, darwin-arm64) (push) Blocked by required conditions
ELP CI / ci (28, 28.0.1, windows, 28, windows-2022, windows-2022-x64, x86_64-pc-windows-msvc, false, win32-x64) (push) Blocked by required conditions
ELP CI / edb (push) Waiting to run
ELP CI / ci (26, 26.2.5.13, linux, 26.2, ubuntu-22.04, ubuntu-22.04-x64, x86_64-unknown-linux-gnu, true, linux-x64) (push) Blocked by required conditions
Deploy Website to GitHub Pages / Deploy Website to GitHub Pages (push) Waiting to run

Summary:
As per title.

We use tree_print as a development diagnostic tool.
The test runner for tree_print itself had code to tree_print an entire module via its forms.

This diff extracts this code and makes it available for use elsewhere.

Reviewed By: TD5

Differential Revision: D88159685

fbshipit-source-id: daf1bd1c25d22b9d9f48863b428538efe37a267c
This commit is contained in:
Alan Zimmerman 2025-12-02 08:11:47 -08:00 committed by meta-codesync[bot]
parent cbe9a058cc
commit 8558c9d5dc

View file

@ -15,6 +15,8 @@ use std::fmt;
use std::fmt::Write as _;
use std::str;
use elp_base_db::FileId;
use super::DefineBody;
use super::FoldBody;
use super::RecordBody;
@ -33,11 +35,15 @@ use crate::ComprehensionExpr;
use crate::Define;
use crate::Expr;
use crate::ExprId;
use crate::FormIdx;
use crate::FunType;
use crate::FunctionBody;
use crate::FunctionClauseBody;
use crate::FunctionDefId;
use crate::InFile;
use crate::ListType;
use crate::Literal;
use crate::PPDirective;
use crate::Pat;
use crate::PatId;
use crate::Record;
@ -50,6 +56,7 @@ use crate::TermId;
use crate::TypeAlias;
use crate::TypeExpr;
use crate::TypeExprId;
use crate::db::DefDatabase;
use crate::db::InternDatabase;
use crate::expr::Guards;
use crate::expr::MaybeExpr;
@ -332,6 +339,63 @@ pub(crate) fn print_ssr(db: &dyn InternDatabase, body: &SsrBody) -> String {
printer.result()
}
#[allow(dead_code)] // This is used for debugging
pub fn print_form_list(db: &dyn DefDatabase, file_id: FileId, strategy: Strategy) -> String {
let form_list = db.file_form_list(file_id);
let dbi: &dyn InternDatabase = db;
form_list
.forms()
.iter()
.flat_map(|&form_idx| -> Option<String> {
match form_idx {
FormIdx::FunctionClause(function_id) => {
let body =
db.function_body(InFile::new(file_id, FunctionDefId::new(function_id)));
Some(body.tree_print(dbi, strategy))
}
FormIdx::TypeAlias(type_alias_id) => {
let type_alias = &form_list[type_alias_id];
let body = db.type_body(InFile::new(file_id, type_alias_id));
Some(body.tree_print(dbi, type_alias))
}
FormIdx::Spec(spec_id) => {
let spec = SpecOrCallback::Spec(form_list[spec_id].clone());
let body = db.spec_body(InFile::new(file_id, spec_id));
Some(body.tree_print(dbi, spec))
}
FormIdx::Callback(callback_id) => {
let spec = SpecOrCallback::Callback(form_list[callback_id].clone());
let body = db.callback_body(InFile::new(file_id, callback_id));
Some(body.tree_print(dbi, spec))
}
FormIdx::Record(record_id) => {
let body = db.record_body(InFile::new(file_id, record_id));
Some(body.print(dbi, &form_list, record_id))
}
FormIdx::Attribute(attribute_id) => {
let attribute = AnyAttribute::Attribute(form_list[attribute_id].clone());
let body = db.attribute_body(InFile::new(file_id, attribute_id));
Some(body.print(dbi, attribute))
}
FormIdx::CompileOption(attribute_id) => {
let attribute = AnyAttribute::CompileOption(form_list[attribute_id].clone());
let body = db.compile_body(InFile::new(file_id, attribute_id));
Some(body.tree_print(dbi, attribute))
}
FormIdx::PPDirective(pp) => match form_list[pp] {
PPDirective::Define(define) => {
let body = db.define_body(InFile::new(file_id, define));
Some(body.tree_print(dbi, &form_list[define]))
}
_ => None,
},
_ => None,
}
})
.collect::<Vec<_>>()
.join("")
}
struct Printer<'a> {
db: &'a dyn InternDatabase,
body: &'a FoldBody<'a>,
@ -1523,13 +1587,8 @@ mod tests {
use expect_test::Expect;
use expect_test::expect;
use crate::AnyAttribute;
use crate::FormIdx;
use crate::FunctionDefId;
use crate::InFile;
use crate::SpecOrCallback;
use crate::Strategy;
use crate::db::DefDatabase;
use crate::body::tree_print::print_form_list;
use crate::fold::MacroStrategy;
use crate::fold::ParenStrategy;
use crate::test_db::TestDB;
@ -1546,52 +1605,7 @@ mod tests {
#[track_caller]
fn check_with_strategy(strategy: Strategy, fixture: &str, expect: Expect) {
let (db, file_id) = TestDB::with_single_file(fixture);
let form_list = db.file_form_list(file_id);
let pretty = form_list
.forms()
.iter()
.flat_map(|&form_idx| -> Option<String> {
match form_idx {
FormIdx::FunctionClause(function_id) => {
let body =
db.function_body(InFile::new(file_id, FunctionDefId::new(function_id)));
Some(body.tree_print(&db, strategy))
}
FormIdx::TypeAlias(type_alias_id) => {
let type_alias = &form_list[type_alias_id];
let body = db.type_body(InFile::new(file_id, type_alias_id));
Some(body.tree_print(&db, type_alias))
}
FormIdx::Spec(spec_id) => {
let spec = SpecOrCallback::Spec(form_list[spec_id].clone());
let body = db.spec_body(InFile::new(file_id, spec_id));
Some(body.tree_print(&db, spec))
}
FormIdx::Callback(callback_id) => {
let spec = SpecOrCallback::Callback(form_list[callback_id].clone());
let body = db.callback_body(InFile::new(file_id, callback_id));
Some(body.tree_print(&db, spec))
}
FormIdx::Record(record_id) => {
let body = db.record_body(InFile::new(file_id, record_id));
Some(body.print(&db, &form_list, record_id))
}
FormIdx::Attribute(attribute_id) => {
let attribute = AnyAttribute::Attribute(form_list[attribute_id].clone());
let body = db.attribute_body(InFile::new(file_id, attribute_id));
Some(body.print(&db, attribute))
}
FormIdx::CompileOption(attribute_id) => {
let attribute =
AnyAttribute::CompileOption(form_list[attribute_id].clone());
let body = db.compile_body(InFile::new(file_id, attribute_id));
Some(body.tree_print(&db, attribute))
}
_ => None,
}
})
.collect::<Vec<_>>()
.join("");
let pretty = print_form_list(&db, file_id, strategy);
expect.assert_eq(pretty.trim_start());
}
@ -1999,6 +2013,16 @@ mod tests {
foo() -> ?EXPR(2).
"#,
expect![[r#"
-define(EXPR/1,
Expr<2>:Expr::BinaryOp {
lhs
Expr<0>:Literal(Integer(1))
rhs
Expr<1>:Expr::Var(X)
op
ArithOp(Add),
}
).
function: foo/0
Clause {
pats
@ -3420,4 +3444,102 @@ mod tests {
"#]],
);
}
#[test]
fn top_level_macro() {
// Note: we currently lower a macro as an Expr only.
// We have special processing in lower_clause_or_macro_body
// to deal with top level macros, at the ast level.
check(
r#"
-define(FOO(X), baz() -> X).
-define(BAR(X), {X}).
?FOO(42).
foo() -> ?BAR(42).
"#,
expect![[r#"
-define(FOO/1,
Expr<0>:Expr::Missing
).
-define(BAR/1,
Expr<1>:Expr::Tuple {
Expr<0>:Expr::Var(X),
}
).
function: baz/0
Clause {
pats
guards
exprs
Expr<2>:Literal(Integer(42)),
}.
function: foo/0
Clause {
pats
guards
exprs
Expr<4>:Expr::MacroCall {
args
Expr<3>:Literal(Integer(42)),
macro_def
Some(InFile { file_id: FileId(0), value: Idx::<Define>(1) })
expansion
Expr<2>:Expr::Tuple {
Expr<1>:Literal(Integer(42)),
}
},
}.
"#]],
);
}
#[test]
fn top_level_forms() {
check(
r#"
-module(main).
bug
-compile([export_all]).
-wild('foo').
-type foo() :: ok.
-spec bar() -> ok.
bar() -> ok.
-callback baz() -> ok.
-record(rec, {f}).
"#,
expect![[r#"
-compile(
Term::List {
exprs
Literal(Atom('export_all')),
tail
}
).
-wild(foo).
-type foo() :: Literal(Atom('ok')).
-spec bar
() ->
Literal(Atom('ok')).
function: bar/0
Clause {
pats
guards
exprs
Expr<1>:Literal(Atom('ok')),
}.
-callback baz
() ->
Literal(Atom('ok')).
-record(rec, {
f
}).
"#]],
);
}
}