From c6275db2a87893ff65679289c2cc428013a0ca80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Gir=C3=A1ldez?= Date: Fri, 20 Sep 2024 01:19:38 -0400 Subject: [PATCH] Refactor: split apart `render_bindings` for readability ... and to silence clippy :) --- .../tests/src/bindings_output/renderer.rs | 215 ++++++++++-------- 1 file changed, 120 insertions(+), 95 deletions(-) diff --git a/crates/solidity/outputs/cargo/tests/src/bindings_output/renderer.rs b/crates/solidity/outputs/cargo/tests/src/bindings_output/renderer.rs index 5dfe0b51f..32ec049d3 100644 --- a/crates/solidity/outputs/cargo/tests/src/bindings_output/renderer.rs +++ b/crates/solidity/outputs/cargo/tests/src/bindings_output/renderer.rs @@ -1,131 +1,156 @@ +use std::iter::once; use std::ops::Range; use anyhow::Result; use ariadne::{Color, Config, FnCache, Label, Report, ReportBuilder, ReportKind, Source}; use metaslang_bindings::ResolutionError; -use slang_solidity::bindings::{Bindings, Definition}; +use slang_solidity::bindings::{Bindings, Definition, Reference}; use slang_solidity::diagnostic; use super::runner::ParsedPart; +type ReportSpan<'a> = (&'a str, Range); + pub(crate) fn render_bindings( bindings: &Bindings, parsed_parts: &[ParsedPart<'_>], ) -> Result { let mut buffer: Vec = Vec::new(); - let mut definitions: Vec> = Vec::new(); - for definition in bindings.all_definitions() { - if !definition.is_built_in() && definition.get_cursor().is_some() { - definitions.push(definition); - }; - } + let all_definitions = collect_all_definitions(bindings); for part in parsed_parts { - let mut builder: ReportBuilder<'_, (&str, Range)> = Report::build( - ReportKind::Custom("References and definitions", Color::Unset), - part.path, - 0, - ) - .with_config(Config::default().with_color(false)); - if !part.parse_output.is_valid() { - let mut report = part - .parse_output - .errors() - .iter() - .map(|error| diagnostic::render(error, part.path, part.contents, false)) - .collect::>() - .join("\n") - .into_bytes(); + let mut parse_errors_report = part.parse_errors_report().into_bytes(); buffer.extend_from_slice("Parse errors:\n".as_bytes()); - buffer.append(&mut report); + buffer.append(&mut parse_errors_report); buffer.push(b'\n'); } - for (index, definition) in definitions.iter().enumerate() { - let Some(cursor) = definition.get_cursor() else { - continue; - }; - let def_file = definition - .get_file() - .expect("definition should be in a file"); - if def_file != part.path { - continue; - } - - let range = { - let range = cursor.text_range(); - let start = part.contents[..range.start.utf8].chars().count(); - let end = part.contents[..range.end.utf8].chars().count(); - start..end - }; - - let message = format!("def: {}", index + 1); - builder = builder.with_label(Label::new((part.path, range)).with_message(message)); - } - - for reference in bindings.all_references() { - let Some(cursor) = reference.get_cursor() else { - continue; - }; + let part_references = bindings.all_references().filter(|reference| { let ref_file = reference.get_file().expect("reference should be in a file"); - if ref_file != part.path { - continue; - } - - let range = { - let range = cursor.text_range(); - let start = part.contents[..range.start.utf8].chars().count(); - let end = part.contents[..range.end.utf8].chars().count(); - start..end - }; - - let definition = reference.jump_to_definition(); - let message = match definition { - Ok(definition) => { - if definition.is_built_in() { - "ref: built-in".to_string() - } else { - let def_id = definitions.iter().position(|d| *d == definition).unwrap(); - format!("ref: {}", def_id + 1) - } - } - Err(ResolutionError::Unresolved) => "unresolved".to_string(), - Err(ResolutionError::AmbiguousDefinitions(ambiguous_definitions)) => { - let ref_labels = ambiguous_definitions - .iter() - .filter_map(|ambiguous_definition| { - if ambiguous_definition.is_built_in() { - Some("built-in".to_string()) - } else { - definitions - .iter() - .position(|d| d == ambiguous_definition) - .map(|index| format!("ref: {}", index + 1)) - } - }) - .collect::>(); - format!("ambiguous: {}", ref_labels.join(", ")) - } - }; - - builder = builder.with_label(Label::new((part.path, range)).with_message(message)); - } + ref_file == part.path + }); + let bindings_report = build_report_for_part(part, &all_definitions, part_references); - let report = builder.finish(); let file_cache = FnCache::new( (move |id| Err(Box::new(format!("Failed to fetch source '{id}'")) as _)) as fn(&_) -> _, ) .with_sources( - parsed_parts - .iter() + once(part) .map(|part| (part.path, Source::from(part.contents))) .collect(), ); - report.write(file_cache, &mut buffer)?; + bindings_report.write(file_cache, &mut buffer)?; } let result = String::from_utf8(buffer)?; Ok(result) } + +// We collect all non built-in definitions in a vector to be able to identify +// them by a numeric index +fn collect_all_definitions(bindings: &Bindings) -> Vec> { + let mut definitions: Vec> = Vec::new(); + for definition in bindings.all_definitions() { + if !definition.is_built_in() && definition.get_cursor().is_some() { + definitions.push(definition); + } + } + definitions +} + +impl ParsedPart<'_> { + fn parse_errors_report(&self) -> String { + self.parse_output + .errors() + .iter() + .map(|error| diagnostic::render(error, self.path, self.contents, false)) + .collect::>() + .join("\n") + } +} + +fn build_report_for_part<'a>( + part: &'a ParsedPart<'a>, + all_definitions: &'a [Definition<'a>], + part_references: impl Iterator> + 'a, +) -> Report<'a, ReportSpan<'a>> { + let mut builder: ReportBuilder<'_, ReportSpan<'_>> = Report::build( + ReportKind::Custom("References and definitions", Color::Unset), + part.path, + 0, + ) + .with_config(Config::default().with_color(false)); + + for (index, definition) in all_definitions.iter().enumerate() { + let Some(cursor) = definition.get_cursor() else { + continue; + }; + let def_file = definition + .get_file() + .expect("definition should be in a file"); + if def_file != part.path { + continue; + } + + let range = { + let range = cursor.text_range(); + let start = part.contents[..range.start.utf8].chars().count(); + let end = part.contents[..range.end.utf8].chars().count(); + start..end + }; + + let message = format!("def: {}", index + 1); + builder = builder.with_label(Label::new((part.path, range)).with_message(message)); + } + + for reference in part_references { + let Some(cursor) = reference.get_cursor() else { + continue; + }; + + let range = { + let range = cursor.text_range(); + let start = part.contents[..range.start.utf8].chars().count(); + let end = part.contents[..range.end.utf8].chars().count(); + start..end + }; + + let definition = reference.jump_to_definition(); + let message = match definition { + Ok(definition) => { + if definition.is_built_in() { + "ref: built-in".to_string() + } else { + let def_id = all_definitions + .iter() + .position(|d| *d == definition) + .unwrap(); + format!("ref: {}", def_id + 1) + } + } + Err(ResolutionError::Unresolved) => "unresolved".to_string(), + Err(ResolutionError::AmbiguousDefinitions(ambiguous_definitions)) => { + let ref_labels = ambiguous_definitions + .iter() + .filter_map(|ambiguous_definition| { + if ambiguous_definition.is_built_in() { + Some("built-in".to_string()) + } else { + all_definitions + .iter() + .position(|d| d == ambiguous_definition) + .map(|index| format!("ref: {}", index + 1)) + } + }) + .collect::>(); + format!("ambiguous: {}", ref_labels.join(", ")) + } + }; + + builder = builder.with_label(Label::new((part.path, range)).with_message(message)); + } + + builder.finish() +}