Skip to content

Commit 8dea8a2

Browse files
committed
Extract Spaces refactor / cleanup
Have extract_spaces method of ExtractSpaces trait take an arena so that: 1. We can remove a lot of `todo!()`s. 2. Recursively collapse several layers of SpaceBefore/SpaceAfter into a single Spaces. 3. Avoid some fuzzer panics (but not all). Rename new snapshot
1 parent 8fa9791 commit 8dea8a2

24 files changed

+355
-151
lines changed

crates/compiler/can/src/annotation.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,7 +1086,7 @@ fn canonicalize_has_clause(
10861086
} = clause;
10871087
let region = *region;
10881088

1089-
let var_name = var.extract_spaces().item;
1089+
let var_name = var.extract_spaces(env.arena).item;
10901090
debug_assert!(
10911091
var_name.starts_with(char::is_lowercase),
10921092
"Vars should have been parsed as lowercase"
@@ -1192,8 +1192,10 @@ fn can_extension_type(
11921192
);
11931193

11941194
if valid_extension_type(shallow_dealias_with_scope(scope, &ext_type)) {
1195-
if matches!(loc_ann.extract_spaces().item, TypeAnnotation::Wildcard)
1196-
&& matches!(ext_problem_kind, ExtensionTypeKind::TagUnion)
1195+
if matches!(
1196+
loc_ann.extract_spaces(env.arena).item,
1197+
TypeAnnotation::Wildcard
1198+
) && matches!(ext_problem_kind, ExtensionTypeKind::TagUnion)
11971199
&& pol == CanPolarity::Pos
11981200
{
11991201
// Wildcards are redundant in positive positions, since they will always be

crates/compiler/can/src/def.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ fn canonicalize_claimed_ability_impl<'a>(
542542
) -> Result<(Symbol, Symbol), ()> {
543543
let ability_home = ability.module_id();
544544

545-
match loc_impl.extract_spaces().item {
545+
match loc_impl.extract_spaces(env.arena).item {
546546
AssignedField::LabelOnly(label) => {
547547
let label_str = label.value;
548548
let region = label.region;
@@ -788,7 +788,7 @@ fn canonicalize_opaque<'a>(
788788

789789
for has_ability in has_abilities.value.items {
790790
let region = has_ability.region;
791-
let (ability, opt_impls) = match has_ability.value.extract_spaces().item {
791+
let (ability, opt_impls) = match has_ability.value.extract_spaces(env.arena).item {
792792
ast::ImplementsAbility::ImplementsAbility { ability, impls } => (ability, impls),
793793
_ => internal_error!("spaces not extracted"),
794794
};
@@ -835,7 +835,7 @@ fn canonicalize_opaque<'a>(
835835

836836
// First up canonicalize all the claimed implementations, building a map of ability
837837
// member -> implementation.
838-
for loc_impl in impls.extract_spaces().item.items {
838+
for loc_impl in impls.extract_spaces(env.arena).item.items {
839839
let (member, impl_symbol) =
840840
match canonicalize_claimed_ability_impl(env, scope, ability, loc_impl) {
841841
Ok((member, impl_symbol)) => (member, impl_symbol),
@@ -3005,7 +3005,7 @@ fn to_pending_type_def<'a>(
30053005

30063006
for member in *members {
30073007
let name_region = member.name.region;
3008-
let member_name = member.name.extract_spaces().item;
3008+
let member_name = member.name.extract_spaces(env.arena).item;
30093009

30103010
let member_sym = match scope.introduce(member_name.into(), name_region) {
30113011
Ok(sym) => sym,

crates/compiler/can/src/pattern.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -856,7 +856,7 @@ pub fn canonicalize_record_destructs<'a>(
856856
let mut opt_erroneous = None;
857857

858858
for loc_pattern in patterns.iter() {
859-
match loc_pattern.value.extract_spaces().item {
859+
match loc_pattern.value.extract_spaces(env.arena).item {
860860
Identifier { ident: label } => {
861861
match scope.introduce(label.into(), region) {
862862
Ok(symbol) => {

crates/compiler/fmt/src/annotation.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,12 @@ use bumpalo::{
1313
collections::{String, Vec},
1414
Bump,
1515
};
16-
use roc_parse::{ast::Spaced, ident::UppercaseIdent};
17-
use roc_parse::{
18-
ast::{
19-
AbilityImpls, AssignedField, Collection, CommentOrNewline, Expr, ExtractSpaces,
20-
FunctionArrow, ImplementsAbilities, ImplementsAbility, ImplementsClause, Spaceable, Spaces,
21-
SpacesAfter, SpacesBefore, Tag, TypeAnnotation, TypeHeader,
22-
},
23-
expr::merge_spaces,
16+
use roc_parse::ast::{
17+
merge_spaces, AbilityImpls, AssignedField, Collection, CommentOrNewline, Expr, ExtractSpaces,
18+
FunctionArrow, ImplementsAbilities, ImplementsAbility, ImplementsClause, Spaceable, Spaces,
19+
SpacesAfter, SpacesBefore, Tag, TypeAnnotation, TypeHeader,
2420
};
21+
use roc_parse::{ast::Spaced, ident::UppercaseIdent};
2522
use roc_region::all::Loc;
2623

2724
/// Does an AST node need parens around it?
@@ -618,7 +615,7 @@ impl<'a> Formattable for ImplementsClause<'a> {
618615
}
619616

620617
fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) {
621-
buf.push_str(self.var.value.extract_spaces().item);
618+
buf.push_str(self.var.value.extract_spaces(buf.bump()).item);
622619
buf.spaces(1);
623620
buf.push_str(roc_parse::keyword::IMPLEMENTS);
624621
buf.spaces(1);
@@ -1447,7 +1444,7 @@ pub struct NodeSpaces<'a, T> {
14471444
impl<'a, T: Copy> ExtractSpaces<'a> for NodeSpaces<'a, T> {
14481445
type Item = T;
14491446

1450-
fn extract_spaces(&self) -> Spaces<'a, T> {
1447+
fn extract_spaces(&self, _arena: &'a Bump) -> Spaces<'a, T> {
14511448
Spaces {
14521449
before: self.before,
14531450
item: self.item,

crates/compiler/fmt/src/collection.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
use roc_parse::{
2-
ast::{Collection, CommentOrNewline, ExtractSpaces},
3-
expr::merge_spaces,
4-
};
1+
use roc_parse::ast::{merge_spaces, Collection, CommentOrNewline, ExtractSpaces};
52

63
use crate::{
74
annotation::{is_collection_multiline, Formattable, Newlines},
@@ -33,7 +30,7 @@ impl Braces {
3330
}
3431
}
3532

36-
pub fn fmt_collection<'a, 'buf, T: ExtractSpaces<'a> + Formattable + std::fmt::Debug>(
33+
pub fn fmt_collection<'a, 'buf: 'a, T: ExtractSpaces<'a> + Formattable + std::fmt::Debug>(
3734
buf: &mut Buf<'buf>,
3835

3936
indent: u16,
@@ -59,7 +56,7 @@ pub fn fmt_collection<'a, 'buf, T: ExtractSpaces<'a> + Formattable + std::fmt::D
5956

6057
for (index, item) in items.iter().enumerate() {
6158
let is_first_item = index == 0;
62-
let item = item.extract_spaces();
59+
let item = item.extract_spaces(buf.arena);
6360
let is_only_newlines = item.before.iter().all(|s| s.is_newline());
6461
let last_after_was_only_newlines = last_after.iter().all(|s| s.is_newline());
6562

crates/compiler/fmt/src/def.rs

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,11 @@ use crate::{Buf, MigrationFlags};
1717
use bumpalo::Bump;
1818
use roc_error_macros::internal_error;
1919
use roc_parse::ast::{
20-
AbilityMember, Defs, Expr, ExtractSpaces, ImportAlias, ImportAsKeyword, ImportExposingKeyword,
21-
ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport,
22-
ModuleImportParams, Pattern, Spaces, SpacesBefore, StrLiteral, TypeAnnotation, TypeDef,
23-
TypeHeader, ValueDef,
20+
merge_spaces, AbilityMember, Defs, Expr, ExtractSpaces, ImportAlias, ImportAsKeyword,
21+
ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation, IngestedFileImport,
22+
ModuleImport, ModuleImportParams, Pattern, Spaces, SpacesBefore, StrLiteral, TypeAnnotation,
23+
TypeDef, TypeHeader, ValueDef,
2424
};
25-
use roc_parse::expr::merge_spaces;
2625
use roc_parse::header::Keyword;
2726
use roc_region::all::Loc;
2827

@@ -435,8 +434,10 @@ impl<'a> Formattable for TypeDef<'a> {
435434
typ: ann,
436435
derived: has_abilities,
437436
} => {
438-
let ann_is_where_clause =
439-
matches!(ann.extract_spaces().item, TypeAnnotation::Where(..));
437+
let ann_is_where_clause = matches!(
438+
ann.extract_spaces(buf.arena).item,
439+
TypeAnnotation::Where(..)
440+
);
440441

441442
// Always put the has-abilities clause on a newline if the opaque annotation
442443
// contains a where-has clause.
@@ -476,7 +477,7 @@ impl<'a> Formattable for TypeDef<'a> {
476477
} => {
477478
let header_lifted = type_head_lift_spaces(buf.text.bump(), *header);
478479
header_lifted.item.format(buf, indent);
479-
let implements = loc_implements.value.extract_spaces();
480+
let implements = loc_implements.value.extract_spaces(buf.arena);
480481
let before_implements = merge_spaces_conservative(
481482
buf.text.bump(),
482483
header_lifted.after,
@@ -513,7 +514,7 @@ impl<'a> Formattable for TypeDef<'a> {
513514
before,
514515
item,
515516
after,
516-
} = member.name.value.extract_spaces();
517+
} = member.name.value.extract_spaces(buf.arena);
517518
fmt_spaces(buf, before.iter(), indent + INDENT);
518519
buf.ensure_ends_with_newline();
519520

@@ -833,7 +834,7 @@ impl<'a> Formattable for ValueDef<'a> {
833834
use roc_parse::ast::ValueDef::*;
834835
match self {
835836
Annotation(loc_pattern, loc_annotation) => {
836-
let pat_parens = if ann_pattern_needs_parens(&loc_pattern.value) {
837+
let pat_parens = if ann_pattern_needs_parens(buf.arena, &loc_pattern.value) {
837838
Parens::InApply
838839
} else {
839840
Parens::NotNeeded
@@ -860,7 +861,7 @@ impl<'a> Formattable for ValueDef<'a> {
860861
body_pattern,
861862
body_expr,
862863
} => {
863-
let pat_parens = if ann_pattern_needs_parens(&ann_pattern.value) {
864+
let pat_parens = if ann_pattern_needs_parens(buf.arena, &ann_pattern.value) {
864865
Parens::InApply
865866
} else {
866867
Parens::NotNeeded
@@ -888,10 +889,12 @@ impl<'a> Formattable for ValueDef<'a> {
888889
}
889890
}
890891

891-
fn ann_pattern_needs_parens(value: &Pattern<'_>) -> bool {
892-
match value.extract_spaces().item {
892+
fn ann_pattern_needs_parens(arena: &'_ Bump, value: &Pattern<'_>) -> bool {
893+
match value.extract_spaces(arena).item {
893894
Pattern::Tag(_) => true,
894-
Pattern::Apply(func, _args) if matches!(func.extract_spaces().item, Pattern::Tag(..)) => {
895+
Pattern::Apply(func, _args)
896+
if matches!(func.extract_spaces(arena).item, Pattern::Tag(..)) =>
897+
{
895898
true
896899
}
897900
_ => false,
@@ -1016,17 +1019,17 @@ pub fn fmt_body<'a>(
10161019
body: &'a Expr<'a>,
10171020
indent: u16,
10181021
) {
1019-
let pattern_extracted = pattern.extract_spaces();
1022+
let pattern_extracted = pattern.extract_spaces(buf.arena);
10201023
// Check if this is an assignment into the unit value
10211024
let is_unit_assignment = if let Pattern::RecordDestructure(collection) = pattern_extracted.item
10221025
{
10231026
allow_simplify_empty_record_destructure
10241027
&& collection.is_empty()
10251028
&& pattern_extracted.before.iter().all(|s| s.is_newline())
10261029
&& pattern_extracted.after.iter().all(|s| s.is_newline())
1027-
&& !matches!(body.extract_spaces().item, Expr::Defs(..))
1028-
&& !matches!(body.extract_spaces().item, Expr::Return(..))
1029-
&& !matches!(body.extract_spaces().item, Expr::DbgStmt { .. })
1030+
&& !matches!(body.extract_spaces(buf.arena).item, Expr::Defs(..))
1031+
&& !matches!(body.extract_spaces(buf.arena).item, Expr::Return(..))
1032+
&& !matches!(body.extract_spaces(buf.arena).item, Expr::DbgStmt { .. })
10301033
&& !starts_with_expect_ident(body)
10311034
} else {
10321035
false
@@ -1186,7 +1189,7 @@ impl<'a> Formattable for AbilityMember<'a> {
11861189
_newlines: Newlines,
11871190
indent: u16,
11881191
) {
1189-
let Spaces { before, item, .. } = self.name.value.extract_spaces();
1192+
let Spaces { before, item, .. } = self.name.value.extract_spaces(buf.arena);
11901193
fmt_spaces(buf, before.iter(), indent);
11911194

11921195
buf.indent(indent);

crates/compiler/fmt/src/expr.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,9 @@ use bumpalo::collections::Vec;
1313
use bumpalo::Bump;
1414
use roc_module::called_via::{self, BinOp, UnaryOp};
1515
use roc_parse::ast::{
16-
AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces, Pattern, Spaceable,
17-
Spaces, SpacesAfter, SpacesBefore, WhenBranch,
16+
merge_spaces, AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces, Pattern,
17+
Spaceable, Spaces, SpacesAfter, SpacesBefore, StrLiteral, StrSegment, WhenBranch,
1818
};
19-
use roc_parse::ast::{StrLiteral, StrSegment};
20-
use roc_parse::expr::merge_spaces;
2119
use roc_parse::ident::Accessor;
2220
use roc_parse::keyword;
2321
use roc_region::all::Loc;
@@ -674,6 +672,7 @@ fn fmt_apply(
674672
indent: u16,
675673
buf: &mut Buf<'_>,
676674
) {
675+
let arena = buf.arena;
677676
// should_reflow_outdentable, aka should we transform this:
678677
//
679678
// ```
@@ -692,27 +691,28 @@ fn fmt_apply(
692691
// 2,
693692
// ]
694693
// ```
695-
let should_reflow_outdentable = loc_expr.extract_spaces().after.is_empty()
694+
let should_reflow_outdentable = loc_expr.extract_spaces(buf.arena).after.is_empty()
696695
&& except_last(loc_args).all(|a| !a.is_multiline())
697696
&& loc_args
698697
.last()
699698
.map(|a| {
700-
a.extract_spaces().item.is_multiline()
701-
&& is_outdentable_collection(&a.value.extract_spaces().item)
702-
&& (a.extract_spaces().before == [CommentOrNewline::Newline]
703-
|| a.extract_spaces().before.is_empty())
699+
a.extract_spaces(arena).item.is_multiline()
700+
&& is_outdentable_collection(&a.value.extract_spaces(arena).item)
701+
&& (a.extract_spaces(arena).before == [CommentOrNewline::Newline]
702+
|| a.extract_spaces(arena).before.is_empty())
704703
})
705704
.unwrap_or_default();
706705

707706
let needs_indent = !should_reflow_outdentable
708-
&& (!loc_expr.extract_spaces().after.is_empty()
707+
&& (!loc_expr.extract_spaces(arena).after.is_empty()
709708
|| except_last(loc_args).any(|a| a.is_multiline())
710709
|| loc_expr.is_multiline()
711710
|| loc_args
712711
.last()
713712
.map(|a| {
714713
a.is_multiline()
715-
&& (!a.extract_spaces().before.is_empty() || !is_outdentable(&a.value))
714+
&& (!a.extract_spaces(arena).before.is_empty()
715+
|| !is_outdentable(arena, &a.value))
716716
})
717717
.unwrap_or_default());
718718

@@ -868,9 +868,9 @@ pub(crate) fn format_sq_literal(buf: &mut Buf, s: &str) {
868868
buf.push('\'');
869869
}
870870

871-
fn is_outdentable(expr: &Expr) -> bool {
871+
fn is_outdentable(arena: &'_ Bump, expr: &Expr) -> bool {
872872
matches!(
873-
expr.extract_spaces().item,
873+
expr.extract_spaces(arena).item,
874874
Expr::Tuple(_) | Expr::List(_) | Expr::Record(_) | Expr::Closure(..)
875875
)
876876
}
@@ -1554,15 +1554,15 @@ pub fn format_spaces(buf: &mut Buf, spaces: &[CommentOrNewline], newlines: Newli
15541554
}
15551555
}
15561556

1557-
fn is_when_patterns_multiline(when_branch: &WhenBranch) -> bool {
1557+
fn is_when_patterns_multiline(arena: &'_ Bump, when_branch: &WhenBranch) -> bool {
15581558
let patterns = when_branch.patterns;
15591559
let (first_pattern, rest) = patterns.split_first().unwrap();
15601560

15611561
let is_multiline_patterns = if let Some((last_pattern, inner_patterns)) = rest.split_last() {
1562-
!first_pattern.value.extract_spaces().after.is_empty()
1563-
|| !last_pattern.value.extract_spaces().before.is_empty()
1562+
!first_pattern.value.extract_spaces(arena).after.is_empty()
1563+
|| !last_pattern.value.extract_spaces(arena).before.is_empty()
15641564
|| inner_patterns.iter().any(|p| {
1565-
let spaces = p.value.extract_spaces();
1565+
let spaces = p.value.extract_spaces(arena);
15661566
!spaces.before.is_empty() || !spaces.after.is_empty()
15671567
})
15681568
} else {
@@ -1624,7 +1624,7 @@ fn fmt_when<'a>(
16241624
let expr = &branch.value;
16251625
let patterns = &branch.patterns;
16261626
let is_multiline_expr = expr.is_multiline();
1627-
let is_multiline_patterns = is_when_patterns_multiline(branch);
1627+
let is_multiline_patterns = is_when_patterns_multiline(buf.bump(), branch);
16281628

16291629
for (pattern_index, pattern) in patterns.iter().enumerate() {
16301630
if pattern_index == 0 {

crates/compiler/fmt/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub struct Buf<'a> {
2121
beginning_of_line: bool,
2222
line_indent: u16,
2323
flags: MigrationFlags,
24+
pub arena: &'a Bump,
2425
}
2526

2627
#[derive(Debug, Copy, Clone)]
@@ -44,6 +45,7 @@ impl<'a> Buf<'a> {
4445
newlines_to_flush: 0,
4546
beginning_of_line: true,
4647
flags,
48+
arena,
4749
}
4850
}
4951

@@ -59,6 +61,10 @@ impl<'a> Buf<'a> {
5961
self.text.into_bump_str()
6062
}
6163

64+
pub fn bump(&'a self) -> &'a Bump {
65+
self.text.bump()
66+
}
67+
6268
pub fn indent(&mut self, indent: u16) {
6369
if self.beginning_of_line {
6470
self.line_indent = indent;

crates/compiler/fmt/src/pattern.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ use crate::spaces::{fmt_comments_only, fmt_spaces, NewlineAt, INDENT};
77
use crate::Buf;
88
use bumpalo::Bump;
99
use roc_parse::ast::{
10-
Base, CommentOrNewline, Pattern, PatternAs, Spaceable, Spaces, SpacesAfter, SpacesBefore,
10+
merge_spaces, Base, CommentOrNewline, Pattern, PatternAs, Spaceable, Spaces, SpacesAfter,
11+
SpacesBefore,
1112
};
12-
use roc_parse::expr::merge_spaces;
1313
use roc_region::all::Loc;
1414

1515
pub fn fmt_pattern<'a>(buf: &mut Buf, pattern: &'a Pattern<'a>, indent: u16, parens: Parens) {

0 commit comments

Comments
 (0)