Skip to content

Commit b079956

Browse files
committed
Improve analysis test snapshot output
1 parent 861a97e commit b079956

94 files changed

Lines changed: 9625 additions & 15149 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/analyzer/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ salsa = "0.16.1"
1919
parking_lot_core = { version = "=0.8.0" } # used by salsa; version pinned for wasm compatibility
2020
indexmap = "1.6.2"
2121
if_chain = "1.0.1"
22+
smallvec = { version = "1.6.1", features = ["union"] }
2223

2324
[dev-dependencies]
2425
insta = "1.7.1"

crates/analyzer/tests/analysis.rs

Lines changed: 137 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
use fe_analyzer::context;
21
use fe_analyzer::namespace::items::{self, Item, TypeDef};
3-
use fe_analyzer::namespace::types::{Event, FixedSize, FunctionSignature, Type};
2+
use fe_analyzer::namespace::types::{Event, FixedSize};
43
use fe_analyzer::{AnalyzerDb, Db};
54
use fe_common::diagnostics::{diagnostics_string, print_diagnostics, Diagnostic, Label, Severity};
65
use fe_common::files::FileStore;
76
use fe_parser::node::NodeId;
87
use fe_parser::node::Span;
98
use indexmap::IndexMap;
109
use insta::assert_snapshot;
10+
use smallvec::SmallVec;
1111
use std::collections::hash_map::DefaultHasher;
1212
use std::collections::HashMap;
1313
use std::fmt::Debug;
@@ -46,10 +46,10 @@ macro_rules! test_analysis {
4646
// for larger diffs. I recommend commenting out all tests but one.
4747
fe_common::assert_snapshot_wasm!(
4848
concat!("snapshots/analysis__", stringify!($name), ".snap"),
49-
build_snapshot(files, module, &db)
49+
build_snapshot(&files, module, &db)
5050
);
5151
} else {
52-
assert_snapshot!(build_snapshot(files, module, &db));
52+
assert_snapshot!(build_snapshot(&files, module, &db));
5353
}
5454
}
5555
};
@@ -162,128 +162,155 @@ test_analysis! { data_copying_stress, "stress/data_copying_stress.fe"}
162162
test_analysis! { tuple_stress, "stress/tuple_stress.fe"}
163163
test_analysis! { type_aliases, "features/type_aliases.fe"}
164164

165-
fn build_snapshot(file_store: FileStore, module: items::ModuleId, db: &dyn AnalyzerDb) -> String {
166-
// contract and struct types aren't worth printing
167-
let type_aliases = module
165+
fn build_snapshot(file_store: &FileStore, module: items::ModuleId, db: &dyn AnalyzerDb) -> String {
166+
let diagnostics = module
168167
.all_items(db)
169168
.iter()
170-
.filter_map(|def| match def {
171-
Item::Type(TypeDef::Alias(alias)) => {
172-
Some((alias.data(db).ast.span, alias.typ(db).unwrap()))
173-
}
174-
_ => None,
175-
})
176-
.collect::<Vec<(Span, Type)>>();
169+
.map(|item| match item {
170+
Item::Type(TypeDef::Alias(alias)) => vec![build_display_diagnostic(
171+
alias.data(db).ast.span,
172+
&alias.typ(db).unwrap(),
173+
)],
174+
Item::Type(TypeDef::Struct(struct_)) => [label_in_non_overlapping_groups(
175+
&struct_
176+
.all_fields(db)
177+
.iter()
178+
.map(|field| (field.data(db).ast.span, field.typ(db).unwrap()))
179+
.collect::<Vec<_>>(),
180+
)]
181+
.concat(),
182+
Item::Type(TypeDef::Contract(contract)) => [
183+
label_in_non_overlapping_groups(
184+
&contract
185+
.all_fields(db)
186+
.iter()
187+
.map(|field| (field.data(db).ast.span, field.typ(db).unwrap()))
188+
.collect::<Vec<_>>(),
189+
),
190+
contract
191+
.events(db)
192+
.values()
193+
.map(|id| event_diagnostics(*id, db))
194+
.flatten()
195+
.collect(),
196+
contract
197+
.functions(db)
198+
.values()
199+
.map(|id| function_diagnostics(*id, db))
200+
.flatten()
201+
.collect(),
202+
]
203+
.concat(),
177204

178-
let struct_fields: Vec<(Span, Type)> = module
179-
.all_structs(db)
180-
.iter()
181-
.map(|struc| {
182-
struc
183-
.all_fields(db)
184-
.iter()
185-
.map(|field| (field.data(db).ast.span, field.typ(db).unwrap().into()))
186-
.collect::<Vec<_>>()
187-
})
188-
.flatten()
189-
.collect();
205+
Item::Function(id) => function_diagnostics(*id, db),
206+
Item::Constant(id) => vec![build_display_diagnostic(id.span(db), &id.typ(db).unwrap())],
190207

191-
let contract_ids = module.all_contracts(db);
192-
let contract_fields: Vec<(Span, Type)> = contract_ids
193-
.iter()
194-
.map(|contract| {
195-
contract
196-
.all_fields(db)
197-
.iter()
198-
.map(|field| (field.data(db).ast.span, field.typ(db).unwrap()))
199-
.collect::<Vec<_>>()
200-
})
201-
.flatten()
202-
.collect();
203-
let event_fields = contract_ids
204-
.iter()
205-
.map(|contract| {
206-
contract
207-
.all_events(db)
208-
.iter()
209-
.map(|event| {
210-
// Event field spans are a bit of a hassle right now
211-
event
212-
.data(db)
213-
.ast
214-
.kind
215-
.fields
216-
.iter()
217-
.map(|node| node.span)
218-
.zip(
219-
event
220-
.typ(db)
221-
.fields
222-
.iter()
223-
.map(|field| field.typ.clone().unwrap()),
224-
)
225-
.collect::<Vec<(Span, FixedSize)>>()
226-
})
227-
.flatten()
228-
.collect::<Vec<(Span, FixedSize)>>()
208+
// Events can't be defined at the module level yet.
209+
Item::Event(_) => vec![],
210+
211+
// Built-in stuff
212+
Item::Type(TypeDef::Primitive(_))
213+
| Item::GenericType(_)
214+
| Item::BuiltinFunction(_)
215+
| Item::Object(_) => vec![],
229216
})
230217
.flatten()
231-
.collect::<Vec<(Span, FixedSize)>>();
218+
.collect::<Vec<_>>();
232219

233-
let all_function_ids: Vec<items::FunctionId> = contract_ids
234-
.iter()
235-
.map(|contract| contract.all_functions(db).as_ref().clone())
236-
.flatten()
237-
.collect();
220+
diagnostics_string(&diagnostics, &file_store)
221+
}
238222

239-
let function_sigs = all_function_ids
240-
.iter()
241-
.map(|fun| (fun.data(db).ast.span, fun.signature(db)))
242-
.collect::<Vec<(Span, Rc<FunctionSignature>)>>();
223+
fn new_diagnostic(labels: Vec<Label>) -> Diagnostic {
224+
Diagnostic {
225+
severity: Severity::Note,
226+
message: String::new(),
227+
labels: labels.to_vec(),
228+
notes: vec![],
229+
}
230+
}
243231

244-
let all_function_bodies: Vec<Rc<context::FunctionBody>> =
245-
all_function_ids.iter().map(|func| func.body(db)).collect();
232+
fn label_in_non_overlapping_groups(spans: &[(Span, impl Display)]) -> Vec<Diagnostic> {
233+
// Accumulate labels in a vec until we reach a span that overlaps
234+
// the labeled range, then emit a Diagnostic with the accumulated labels
235+
// and begin again. This assumes that all spans are within the same file.
236+
let file_id = if let Some((span, _)) = spans.first() {
237+
span.file_id
238+
} else {
239+
return vec![];
240+
};
246241

247-
let expressions = all_function_bodies
242+
spans
248243
.iter()
249-
.map(|body| lookup_spans(&body.expressions, &body.spans))
244+
.enumerate()
245+
.scan(
246+
(Span::zero(file_id), vec![]),
247+
|(labeled, labels), (idx, (span, attr))| {
248+
let mut diags = SmallVec::<[Diagnostic; 2]>::new();
249+
250+
let overlaps = span.start < labeled.end && span.end > labeled.start;
251+
252+
// If the current span overlaps with the union of the current set of labels,
253+
// emit a diagnostic, and clear the set of labels.
254+
if overlaps {
255+
diags.push(new_diagnostic(labels.to_vec()));
256+
labels.clear();
257+
*labeled = *span;
258+
}
259+
labels.push(Label::primary(*span, format!("{}", attr)));
260+
*labeled += *span;
261+
262+
// If this is the last thing to label, emit a diagnostic.
263+
if idx == spans.len() - 1 {
264+
diags.push(new_diagnostic(labels.to_vec()));
265+
}
266+
Some(diags)
267+
},
268+
)
250269
.flatten()
251-
.collect::<Vec<_>>();
252-
let emits = all_function_bodies
253-
.iter()
254-
.map(|body| {
255-
lookup_spans(&body.emits, &body.spans)
270+
.collect()
271+
}
272+
273+
fn function_diagnostics(fun: items::FunctionId, db: &dyn AnalyzerDb) -> Vec<Diagnostic> {
274+
let body = fun.body(db);
275+
[
276+
// signature
277+
build_debug_diagnostics(&[(fun.data(db).ast.span, &fun.signature(db))]),
278+
// declarations
279+
label_in_non_overlapping_groups(&lookup_spans(&body.var_decl_types, &body.spans)),
280+
// expressions
281+
label_in_non_overlapping_groups(&lookup_spans(&body.expressions, &body.spans)),
282+
// emits
283+
build_debug_diagnostics(
284+
&lookup_spans(&body.emits, &body.spans)
256285
.into_iter()
257286
.map(|(span, eventid)| (span, eventid.typ(db)))
258-
.collect::<Vec<(Span, Rc<Event>)>>()
259-
})
260-
.flatten()
261-
.collect::<Vec<_>>();
262-
let declarations = all_function_bodies
263-
.iter()
264-
.map(|body| lookup_spans(&body.var_decl_types, &body.spans))
265-
.flatten()
266-
.collect::<Vec<_>>();
267-
let calls = all_function_bodies
268-
.iter()
269-
.map(|body| lookup_spans(&body.calls, &body.spans))
270-
.flatten()
271-
.collect::<Vec<_>>();
272-
273-
let diagnostics = [
274-
build_display_diagnostics(&type_aliases),
275-
build_display_diagnostics(&struct_fields),
276-
build_display_diagnostics(&contract_fields),
277-
build_display_diagnostics(&event_fields),
278-
build_debug_diagnostics(&function_sigs),
279-
build_display_diagnostics(&expressions),
280-
build_debug_diagnostics(&emits),
281-
build_display_diagnostics(&declarations),
282-
build_debug_diagnostics(&calls),
287+
.collect::<Vec<(Span, Rc<Event>)>>(),
288+
),
289+
// calls
290+
build_debug_diagnostics(&lookup_spans(&body.calls, &body.spans)),
283291
]
284-
.concat();
292+
.concat()
293+
}
285294

286-
diagnostics_string(&diagnostics, &file_store)
295+
fn event_diagnostics(event: items::EventId, db: &dyn AnalyzerDb) -> Vec<Diagnostic> {
296+
// Event field spans are a bit of a hassle right now
297+
label_in_non_overlapping_groups(
298+
&event
299+
.data(db)
300+
.ast
301+
.kind
302+
.fields
303+
.iter()
304+
.map(|node| node.span)
305+
.zip(
306+
event
307+
.typ(db)
308+
.fields
309+
.iter()
310+
.map(|field| field.typ.clone().unwrap()),
311+
)
312+
.collect::<Vec<(Span, FixedSize)>>(),
313+
)
287314
}
288315

289316
fn lookup_spans<T: Clone>(
@@ -314,15 +341,7 @@ fn build_debug_diagnostic<T: Debug>(span: Span, attributes: &T) -> Diagnostic {
314341
}
315342
}
316343

317-
fn build_display_diagnostics<T: Display>(spanned_attributes: &[(Span, T)]) -> Vec<Diagnostic> {
318-
spanned_attributes
319-
.iter()
320-
.map(|(span, attributes)| build_display_diagnostic(*span, attributes))
321-
.collect::<Vec<_>>()
322-
}
323-
324344
fn build_display_diagnostic<T: Display>(span: Span, attributes: &T) -> Diagnostic {
325-
// Hash the attributes and label the span with it.
326345
let label = Label::primary(span, format!("{}", attributes));
327346
Diagnostic {
328347
severity: Severity::Note,

0 commit comments

Comments
 (0)