Skip to content

Commit

Permalink
Introduce start of fold_file function
Browse files Browse the repository at this point in the history
Summary:
This is the start of a diff stack.
It introduces the `fold_file` function which does a fold over every form in a file, with a callback for each visited element.
The callback includes the `FormId`, so the details of the relevent form can be accessed in the callback.

Reviewed By: perehonchuk

Differential Revision: D49777940

fbshipit-source-id: b5a317f95a7b8153732ba3cc1baae0743aa2149a
  • Loading branch information
alanz authored and facebook-github-bot committed Oct 2, 2023
1 parent f1438eb commit 486c3fb
Showing 1 changed file with 108 additions and 0 deletions.
108 changes: 108 additions & 0 deletions crates/hir/src/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

use std::ops::Index;

use elp_base_db::FileId;

use crate::body::UnexpandedIndex;
use crate::expr::AnyExpr;
use crate::expr::MaybeExpr;
Expand All @@ -26,14 +28,64 @@ use crate::ExprId;
use crate::FormIdx;
use crate::FunType;
use crate::HirIdx;
use crate::InFile;
use crate::ListType;
use crate::Pat;
use crate::PatId;
use crate::Semantic;
use crate::Term;
use crate::TermId;
use crate::TypeExpr;
use crate::TypeExprId;

// ---------------------------------------------------------------------

/// Fold over the contents of a file.
#[allow(dead_code)] // Until the balance of the stack lands and it gets used
pub fn fold_file<'a, T>(
sema: &Semantic,
file_id: FileId,
initial: T,
callback: AnyCallBack<'a, T>,
) -> T {
let form_list = sema.form_list(file_id);
let r = form_list
.forms()
.iter()
.fold(initial, |r, &form_idx| match form_idx {
FormIdx::Function(function_id) => sema.fold_function(
InFile::new(file_id, function_id),
r,
&mut |acc, _clause, ctx| callback(acc, ctx),
),
FormIdx::TypeAlias(_type_alias_id) => {
todo!()
}
FormIdx::Spec(_spec_id) => {
todo!()
}
FormIdx::Callback(_callback_id) => {
todo!()
}
FormIdx::Record(_record_id) => {
todo!()
}
FormIdx::Attribute(_attribute_id) => {
todo!()
}
FormIdx::CompileOption(_attribute_id) => {
todo!()
}
_ => {
// Will have to do some time?
r
}
});
r
}

// ---------------------------------------------------------------------

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum On {
Entry,
Expand Down Expand Up @@ -743,6 +795,7 @@ mod tests {
use la_arena::Idx;
use la_arena::RawIdx;

use super::fold_file;
use super::FoldBody;
use crate::body::UnexpandedIndex;
use crate::expr::AnyExpr;
Expand Down Expand Up @@ -1403,4 +1456,59 @@ bar() ->

// end of testing type expression traversals
// -----------------------------------------------------------------

#[track_caller]
fn count_atom_foo(fixture_str: &str, n: u32) {
let (db, file_id, range_or_offset) = TestDB::with_range_or_offset(fixture_str);
let sema = Semantic::new(&db);
let offset = match range_or_offset {
elp_base_db::fixture::RangeOrOffset::Range(_) => panic!(),
elp_base_db::fixture::RangeOrOffset::Offset(o) => o,
};
let in_file = sema.parse(file_id);
let source_file = in_file.value;

let ast_atom =
algo::find_node_at_offset::<ast::Atom>(source_file.syntax(), offset).unwrap();
expect![[r#"foo"#]].assert_eq(&ast_atom.raw_text());
let hir_atom = to_atom(&sema, InFile::new(file_id, &ast_atom)).unwrap();

let r: u32 = fold_file(&sema, file_id, 0, &mut |acc, ctx| match ctx.item {
AnyExpr::Expr(Expr::Literal(Literal::Atom(atom))) => {
if atom == hir_atom {
acc + 1
} else {
acc
}
}
AnyExpr::Pat(Pat::Literal(Literal::Atom(atom))) => {
if atom == hir_atom {
acc + 1
} else {
acc
}
}
_ => acc,
});

// Count of the occurrences of the atom 'foo' in the code example
assert_eq!(r, n);
}

#[test]
fn traverse_file_function_1() {
let fixture_str = r#"
-module(foo).
-export([bar/1]).
bar(0) ->
foo;
bar(X) ->
case X of
foo -> bar;
baz -> 'foo';
_ -> f~oo
end.
"#;
count_atom_foo(fixture_str, 4);
}
}

0 comments on commit 486c3fb

Please sign in to comment.