Skip to content

Commit

Permalink
Add Formatting Capabilities (#321)
Browse files Browse the repository at this point in the history
This PR enables the initial support for formatting VHDL language elements and files. The formatter is transforms the AST representation into a string. It is capable of formatting any AST element and preserving comments as well as newlines.
  • Loading branch information
Schottkyc137 authored Jul 28, 2024
1 parent f5546fa commit 86eb38e
Show file tree
Hide file tree
Showing 78 changed files with 7,746 additions and 993 deletions.
1 change: 1 addition & 0 deletions clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ignore-interior-mutability = ["vhdl_lang::data::source::UniqueSource"]
20 changes: 15 additions & 5 deletions vhdl_lang/src/analysis/assignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
self.analyze_expression_for_target(scope, ttyp, item, diagnostics)?;
self.boolean_expr(scope, condition, diagnostics)?;
}
if let Some(expr) = else_item {
if let Some((expr, _)) = else_item {
self.analyze_expression_for_target(scope, ttyp, expr, diagnostics)?;
}
}
Expand All @@ -48,7 +48,12 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
alternatives,
} = selection;
let ctyp = as_fatal(self.expr_unambiguous_type(scope, expression, diagnostics))?;
for Alternative { choices, item } in alternatives.iter_mut() {
for Alternative {
choices,
item,
span: _,
} in alternatives.iter_mut()
{
self.analyze_expression_for_target(scope, ttyp, item, diagnostics)?;
self.choice_with_ttyp(scope, ctyp, choices, diagnostics)?;
}
Expand Down Expand Up @@ -83,7 +88,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
self.analyze_waveform(scope, ttyp, item, diagnostics)?;
self.boolean_expr(scope, condition, diagnostics)?;
}
if let Some(wavf) = else_item {
if let Some((wavf, _)) = else_item {
self.analyze_waveform(scope, ttyp, wavf, diagnostics)?;
}
}
Expand All @@ -93,7 +98,12 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
alternatives,
} = selection;
let ctyp = as_fatal(self.expr_unambiguous_type(scope, expression, diagnostics))?;
for Alternative { choices, item } in alternatives.iter_mut() {
for Alternative {
choices,
item,
span: _,
} in alternatives.iter_mut()
{
self.analyze_waveform(scope, ttyp, item, diagnostics)?;
self.choice_with_ttyp(scope, ctyp, choices, diagnostics)?;
}
Expand All @@ -119,7 +129,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
}
}
}
Waveform::Unaffected => {}
Waveform::Unaffected(_) => {}
}
Ok(())
}
Expand Down
6 changes: 3 additions & 3 deletions vhdl_lang/src/analysis/association.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
// it must be a type conversion or a single parameter function call

let (pos, resolved_formal) = if let Some((inner_pos, inner_name)) =
to_formal_conversion_argument(&mut fcall.parameters)
to_formal_conversion_argument(&mut fcall.parameters.items)
{
(
inner_pos,
Expand Down Expand Up @@ -250,7 +250,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
bail!(
diagnostics,
Diagnostic::new(
&fcall.name.pos(self.ctx),
fcall.name.pos(self.ctx),
format!(
"No function '{}' accepting {}",
fcall.name,
Expand Down Expand Up @@ -372,7 +372,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
result.push((actual.span, Some(formal)));
} else {
diagnostics.add(
&actual.pos(self.ctx),
actual.pos(self.ctx),
"Unexpected extra argument",
ErrorCode::TooManyArguments,
);
Expand Down
11 changes: 6 additions & 5 deletions vhdl_lang/src/analysis/concurrent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
..
} = process;
if let Some(sensitivity_list) = sensitivity_list {
match sensitivity_list {
match &mut sensitivity_list.item {
SensitivityList::Names(names) => {
self.sensitivity_list_check(scope, names, diagnostics)?;
}
Expand Down Expand Up @@ -165,7 +165,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
let nested = scope.nested();
self.analyze_generate_body(&nested, parent, item, src_span, diagnostics)?;
}
if let Some(ref mut else_item) = else_item {
if let Some((ref mut else_item, _)) = else_item {
let nested = scope.nested();
self.analyze_generate_body(&nested, parent, else_item, src_span, diagnostics)?;
}
Expand All @@ -186,6 +186,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
let Alternative {
ref mut choices,
ref mut item,
span: _,
} = alternative;
self.choice_with_ttyp(scope, ctyp, choices, diagnostics)?;
let nested = scope.nested();
Expand Down Expand Up @@ -258,7 +259,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
// Pre-declare labels
self.define_labels_for_concurrent_part(scope, parent, statements, diagnostics)?;

if let Some(ref mut decl) = decl {
if let Some((ref mut decl, _)) = decl {
self.analyze_declarative_part(scope, parent, decl, diagnostics)?;
}
self.analyze_concurrent_part(scope, inner_parent, statements, diagnostics)?;
Expand Down Expand Up @@ -456,7 +457,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
))? {
if object_name.base.class() != ObjectClass::Signal {
diagnostics.add(
&name.pos(self.ctx),
name.pos(self.ctx),
format!(
"{} is not a signal and cannot be in a sensitivity list",
object_name.base.describe_class()
Expand All @@ -467,7 +468,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
&& !object_name.base.is_port()
{
diagnostics.add(
&name.pos(self.ctx),
name.pos(self.ctx),
format!(
"{} cannot be in a sensitivity list",
object_name.base.describe_class()
Expand Down
43 changes: 25 additions & 18 deletions vhdl_lang/src/analysis/declarative.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
name,
subtype_indication,
signature,
is_token: _,
} = alias;

let resolved_name = self.name_resolve(scope, name.span, &mut name.item, diagnostics);
Expand Down Expand Up @@ -283,7 +284,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
));
}
diagnostics.add(
&name.pos(self.ctx),
name.pos(self.ctx),
format!("{} cannot be aliased", resolved_name.describe_type()),
ErrorCode::MismatchedKinds,
);
Expand Down Expand Up @@ -447,7 +448,8 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
}
Declaration::File(ref mut file) => {
let FileDeclaration {
ident,
idents,
colon_token: _,
subtype_indication,
open_info,
file_name,
Expand All @@ -459,18 +461,20 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
diagnostics,
))?;

if let Some(ref mut expr) = open_info {
if let Some((_, ref mut expr)) = open_info {
self.expr_unknown_ttyp(scope, expr, diagnostics)?;
}
if let Some(ref mut expr) = file_name {
if let Some((_, ref mut expr)) = file_name {
self.expr_unknown_ttyp(scope, expr, diagnostics)?;
}

if let Some(subtype) = subtype {
scope.add(
self.define(ident, parent, AnyEntKind::File(subtype), src_span),
diagnostics,
);
for ident in idents {
scope.add(
self.define(ident, parent, AnyEntKind::File(subtype), src_span),
diagnostics,
);
}
}
}
Declaration::Component(ref mut component) => {
Expand Down Expand Up @@ -625,7 +629,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
Type::Record(region) => region,
_ => {
let diag = Diagnostic::new(
&view.typ.type_mark.pos(self.ctx),
view.typ.type_mark.pos(self.ctx),
format!(
"The type of a view must be a record type, not {}",
typ.type_mark().describe()
Expand All @@ -641,17 +645,17 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
};
let mut unassociated: HashSet<_> = record_region.elems.iter().collect();
for element in view.elements.iter_mut() {
for name in element.names.items.iter_mut() {
let desi = Designator::Identifier(name.item.item.clone());
for name in element.names.iter_mut() {
let desi = Designator::Identifier(name.tree.item.clone());
let Some(record_element) = record_region.lookup(&desi) else {
diagnostics.push(Diagnostic::new(
name.item.pos(self.ctx),
name.pos(self.ctx),
format!("Not a part of {}", typ.type_mark().describe()),
ErrorCode::Unresolved,
));
continue;
};
name.set_unique_reference(&record_element);
name.decl.set_unique_reference(&record_element);
unassociated.remove(&record_element);
}
}
Expand Down Expand Up @@ -690,6 +694,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
entity_name,
entity_class,
expr,
colon_token: _,
} = attr_spec;

let attr_ent = match scope.lookup(
Expand Down Expand Up @@ -744,7 +749,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
if let Some(signature) = signature {
diagnostics.push(Diagnostic::should_not_have_signature(
"Attribute specification",
&signature.pos(self.ctx),
signature.pos(self.ctx),
));
}
ent
Expand Down Expand Up @@ -954,7 +959,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
class,
iface: Some(ObjectInterface::simple(
object_decl.list_type,
mode.mode.unwrap_or_default(),
mode.mode.as_ref().map(|mode| mode.item).unwrap_or_default(),
)),
subtype,
has_default: mode.expression.is_some(),
Expand All @@ -968,14 +973,14 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
let resolved =
self.name_resolve(scope, view.name.span, &mut view.name.item, diagnostics)?;
let view_ent = self.resolve_view_ent(&resolved, diagnostics, view.name.span)?;
if let Some(ast_declared_subtype) = &mut view.subtype_indication {
if let Some((_, ast_declared_subtype)) = &mut view.subtype_indication {
let declared_subtype =
self.resolve_subtype_indication(scope, ast_declared_subtype, diagnostics)?;
if declared_subtype.type_mark() != view_ent.subtype().type_mark() {
bail!(
diagnostics,
Diagnostic::new(
&ast_declared_subtype.type_mark.pos(self.ctx),
ast_declared_subtype.type_mark.pos(self.ctx),
"Specified subtype must match the subtype declared for the view",
ErrorCode::TypeMismatch
)
Expand Down Expand Up @@ -1061,7 +1066,9 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
ArrayIndex::IndexSubtypeDefintion(ref mut type_mark) => self
.type_name(scope, type_mark.span, &mut type_mark.item, diagnostics)
.map(|typ| typ.base()),
ArrayIndex::Discrete(ref mut drange) => self.drange_type(scope, drange, diagnostics),
ArrayIndex::Discrete(ref mut drange) => {
self.drange_type(scope, &mut drange.item, diagnostics)
}
}
}
}
Expand Down
24 changes: 12 additions & 12 deletions vhdl_lang/src/analysis/design_unit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
ResolvedName::Library(ref library_name) => {
if library_name != self.work_library_name() {
diagnostics.add(
&prefix.pos(self.ctx),
prefix.pos(self.ctx),
format!("Configuration must be within the same library '{}' as the corresponding entity", self.work_library_name()), ErrorCode::ConfigNotInSameLibrary);
Err(EvalError::Unknown)
} else {
Expand Down Expand Up @@ -451,14 +451,14 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
}
}
other => {
diagnostics.push(other.kind_error(&prefix.pos(self.ctx), "library"));
diagnostics.push(other.kind_error(prefix.pos(self.ctx), "library"));
Err(EvalError::Unknown)
}
}
}
_ => {
diagnostics.add(
&ent_name.pos(self.ctx),
ent_name.pos(self.ctx),
"Expected selected name",
ErrorCode::MismatchedKinds,
);
Expand All @@ -480,7 +480,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
bail!(
diagnostics,
Diagnostic::mismatched_kinds(
&prefix.pos(self.ctx),
prefix.pos(self.ctx),
"Invalid prefix of a selected name",
)
);
Expand All @@ -490,7 +490,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
bail!(
diagnostics,
Diagnostic::mismatched_kinds(
&prefix.pos(self.ctx),
prefix.pos(self.ctx),
"'.all' may not be the prefix of a selected name",
)
);
Expand Down Expand Up @@ -531,7 +531,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
| Name::External(..) => {
bail!(
diagnostics,
Diagnostic::mismatched_kinds(&name.pos(self.ctx), "Invalid selected name",)
Diagnostic::mismatched_kinds(name.pos(self.ctx), "Invalid selected name",)
);
}
}
Expand All @@ -548,7 +548,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
ContextItem::Library(LibraryClause {
ref mut name_list, ..
}) => {
for library_name in name_list.items.iter_mut() {
for library_name in name_list.iter_mut() {
if self.work_sym == library_name.item.item {
library_name.set_unique_reference(self.work_library());
diagnostics.add(
Expand Down Expand Up @@ -577,12 +577,12 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
ContextItem::Context(ContextReference {
ref mut name_list, ..
}) => {
for name in name_list.items.iter_mut() {
for name in name_list.iter_mut() {
match name.item {
Name::Selected(..) => {}
_ => {
diagnostics.add(
&name.pos(self.ctx),
name.pos(self.ctx),
"Context reference must be a selected name",
ErrorCode::MismatchedKinds,
);
Expand Down Expand Up @@ -639,13 +639,13 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
use_clause: &mut UseClause,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult {
for name in use_clause.name_list.items.iter_mut() {
for name in use_clause.name_list.iter_mut() {
match name.item {
Name::Selected(..) => {}
Name::SelectedAll(..) => {}
_ => {
diagnostics.add(
&name.pos(self.ctx),
name.pos(self.ctx),
"Use clause must be a selected name",
ErrorCode::MismatchedKinds,
);
Expand Down Expand Up @@ -729,7 +729,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
}
}
diagnostics.add(
&package_name.pos(self.ctx),
package_name.pos(self.ctx),
format!("'{package_name}' is not an uninstantiated generic package"),
ErrorCode::MismatchedKinds,
);
Expand Down
Loading

0 comments on commit 86eb38e

Please sign in to comment.