diff --git a/vhdl_lang/src/analysis/declarative.rs b/vhdl_lang/src/analysis/declarative.rs index c84be55b..2a7718ff 100644 --- a/vhdl_lang/src/analysis/declarative.rs +++ b/vhdl_lang/src/analysis/declarative.rs @@ -41,7 +41,10 @@ impl Declaration { | SubprogramBody(_) | Use(_) | Package(_) + | PackageDeclaration(_) + | PackageBody(_) | Configuration(_) + | Disconnection(_) | View(_) ), // LRM: configuration_declarative_item @@ -61,6 +64,9 @@ impl Declaration { | SubprogramBody(_) | Use(_) | Package(_) + | PackageDeclaration(_) + | PackageBody(_) + | Disconnection(_) | View(_) ), // LRM: package_body_declarative_item @@ -72,6 +78,7 @@ impl Declaration { | Overloaded::UninstSubprogram(..), ) | AnyEntKind::Concurrent(Some(Concurrent::Process)) + // LRM protected type body declarative item | AnyEntKind::Type(named_entity::Type::Protected(..)) => matches!( self, Object(ObjectDeclaration { @@ -86,6 +93,8 @@ impl Declaration { | SubprogramBody(_) | Use(_) | Package(_) + | PackageDeclaration(_) + | PackageBody(_) ), // LRM: package_declarative_item AnyEntKind::Design(Design::Package(..)) => matches!( @@ -100,6 +109,8 @@ impl Declaration { | SubprogramInstantiation(_) | Use(_) | Package(_) + | PackageDeclaration(_) + | Disconnection(_) | View(_) ), _ => { @@ -606,7 +617,43 @@ impl<'a, 't> AnalyzeContext<'a, 't> { scope.add(ent, diagnostics); } } + Declaration::PackageDeclaration(ref mut unit) => { + self.analyze_package(unit, diagnostics)?; + } + Declaration::PackageBody(ref mut unit) => { + self.analyze_package_body(unit, diagnostics)?; + } Declaration::Configuration(..) => {} + Declaration::Disconnection(ref mut disc) => { + let DisconnectionSpecification { + ident, + subtype_indication, + expression, + } = disc; + self.expr_with_ttyp(scope, self.time(), expression, diagnostics)?; + self.analyze_subtype_indication(scope, subtype_indication, diagnostics)?; + + let subtype = as_fatal(self.resolve_subtype_indication( + scope, + subtype_indication, + diagnostics, + )); + if let Ok(Some(subtype)) = subtype { + if let GuardedSignalList::Ident(ref mut ident) = ident { + scope.add( + self.arena.define( + self.ctx, + ident, + parent, + AnyEntKind::Disconnection(subtype), + src_span, + Some(self.source()), + ), + diagnostics, + ); + } + } + } Declaration::View(view) => { if let Some(view) = as_fatal(self.analyze_view_declaration( scope, @@ -1184,6 +1231,7 @@ fn get_entity_class(ent: EntRef) -> Option { Design::Context(_) => None, }, AnyEntKind::View(_) => None, + AnyEntKind::Disconnection(_) => None, } } diff --git a/vhdl_lang/src/analysis/design_unit.rs b/vhdl_lang/src/analysis/design_unit.rs index f06664d8..925f1bb9 100644 --- a/vhdl_lang/src/analysis/design_unit.rs +++ b/vhdl_lang/src/analysis/design_unit.rs @@ -132,7 +132,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> { Ok(()) } - fn analyze_package( + pub fn analyze_package( &self, unit: &mut PackageDeclaration, diagnostics: &mut dyn DiagnosticHandler, @@ -297,7 +297,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> { Ok(()) } - fn analyze_package_body( + pub fn analyze_package_body( &self, unit: &mut PackageBody, diagnostics: &mut dyn DiagnosticHandler, diff --git a/vhdl_lang/src/analysis/names.rs b/vhdl_lang/src/analysis/names.rs index b6d398e3..3343103d 100644 --- a/vhdl_lang/src/analysis/names.rs +++ b/vhdl_lang/src/analysis/names.rs @@ -188,6 +188,7 @@ impl<'a> ResolvedName<'a> { }), AnyEntKind::Type(_) => ResolvedName::Type(TypeEnt::from_any(ent).unwrap()), AnyEntKind::View(_) => ResolvedName::Final(ent), + AnyEntKind::Disconnection(_) => ResolvedName::Final(ent), AnyEntKind::Overloaded(_) => { return Err(( "Internal error. Unreachable as overloaded is handled outside".to_owned(), @@ -258,6 +259,7 @@ impl<'a> ResolvedName<'a> { } AnyEntKind::File(_) | AnyEntKind::View(_) + | AnyEntKind::Disconnection(_) | AnyEntKind::InterfaceFile(_) | AnyEntKind::Component(_) | AnyEntKind::Concurrent(_) @@ -1845,7 +1847,10 @@ impl Declaration { Declaration::SubprogramBody(_) => "subprogram body", Declaration::Use(_) => "use", Declaration::Package(_) => "package instantiation", + Declaration::PackageDeclaration(_) => "package", + Declaration::PackageBody(_) => "package body", Declaration::Configuration(_) => "configuration", + Declaration::Disconnection(_) => "disconnection", Declaration::View(_) => "view", } } diff --git a/vhdl_lang/src/analysis/package_instance.rs b/vhdl_lang/src/analysis/package_instance.rs index b16c8022..8d820fd5 100644 --- a/vhdl_lang/src/analysis/package_instance.rs +++ b/vhdl_lang/src/analysis/package_instance.rs @@ -386,6 +386,9 @@ impl<'a, 't> AnalyzeContext<'a, 't> { } }, AnyEntKind::View(typ) => AnyEntKind::View(self.map_subtype(mapping, *typ)), + AnyEntKind::Disconnection(typ) => { + AnyEntKind::Disconnection(self.map_subtype(mapping, *typ)) + } }) } diff --git a/vhdl_lang/src/analysis/tests/declarations.rs b/vhdl_lang/src/analysis/tests/declarations.rs index fbca954a..1c8231b1 100644 --- a/vhdl_lang/src/analysis/tests/declarations.rs +++ b/vhdl_lang/src/analysis/tests/declarations.rs @@ -3,7 +3,7 @@ // You can obtain one at http://mozilla.org/MPL/2.0/. // // Copyright (c) 2023, Olof Kraigher olof.kraigher@gmail.com -use crate::analysis::tests::{check_diagnostics, LibraryBuilder}; +use crate::analysis::tests::{check_diagnostics, check_no_diagnostics, LibraryBuilder}; use crate::data::error_codes::ErrorCode; use crate::Diagnostic; @@ -100,3 +100,103 @@ end entity test; )], ) } + +#[test] +pub fn disconnect_no_analyze_error() { + let mut builder = LibraryBuilder::new(); + let _ = builder.code( + "libname", + "\ +entity ent is +end entity; +architecture arch of ent is + constant foo : time := 10 ns; + disconnect all : integer after foo; +begin +end arch; + ", + ); + check_no_diagnostics(&builder.analyze()) +} + +#[test] +pub fn disconnect_expression_error_1() { + let mut builder = LibraryBuilder::new(); + let code = builder.code( + "libname", + "\ +entity ent is +end entity; +architecture arch of ent is + constant foo : time := 10 ns; + disconnect all : integer after bar; +begin +end arch; + ", + ); + check_diagnostics( + builder.analyze(), + vec![Diagnostic::new( + code.s1("bar"), + "No declaration of 'bar'", + ErrorCode::Unresolved, + )], + ) +} + +#[test] +pub fn disconnect_expression_error_2() { + let mut builder = LibraryBuilder::new(); + let code = builder.code( + "libname", + "\ +entity ent is +end entity; +architecture arch of ent is + signal foo : integer; + disconnect all : integer after foo; +begin +end arch; + ", + ); + check_diagnostics( + builder.analyze(), + vec![Diagnostic::new( + code.s1("after foo").s1("foo"), + "signal 'foo' of integer type 'INTEGER' does not match physical type 'TIME'", + ErrorCode::TypeMismatch, + )], + ) +} + +#[test] +pub fn disconnect_type_error_1() { + let mut builder = LibraryBuilder::new(); + let code = builder.code( + "libname", + "\ +entity ent is +end entity; +architecture arch of ent is + signal foo : integer; + disconnect all : bar after 10 ns; +begin +end arch; + ", + ); + check_diagnostics( + builder.analyze(), + vec![ + Diagnostic::new( + code.s1("bar"), + "No declaration of 'bar'", + ErrorCode::Unresolved, + ), + Diagnostic::new( + code.s1("bar"), + "No declaration of 'bar'", + ErrorCode::Unresolved, + ), + ], + ) +} diff --git a/vhdl_lang/src/ast.rs b/vhdl_lang/src/ast.rs index 110863be..e7a2118a 100644 --- a/vhdl_lang/src/ast.rs +++ b/vhdl_lang/src/ast.rs @@ -852,7 +852,10 @@ pub enum Declaration { SubprogramBody(SubprogramBody), Use(UseClause), Package(PackageInstantiation), + PackageDeclaration(PackageDeclaration), + PackageBody(PackageBody), Configuration(ConfigurationSpecification), + Disconnection(DisconnectionSpecification), View(ModeViewDeclaration), } @@ -1375,6 +1378,22 @@ pub struct ConfigurationSpecification { pub vunit_bind_inds: Vec, } +/// LRM 7.4 Guarded Signal List +#[derive(PartialEq, Debug, Clone)] +pub enum GuardedSignalList { + All, + Others, + Ident(WithDecl), +} + +/// LRM 7.4 Disconnection specification +#[derive(PartialEq, Debug, Clone)] +pub struct DisconnectionSpecification { + pub ident: GuardedSignalList, + pub subtype_indication: SubtypeIndication, + pub expression: WithTokenSpan, +} + /// LRM 3.4 Configuration declarations #[derive(PartialEq, Debug, Clone)] pub enum ConfigurationDeclarativeItem { diff --git a/vhdl_lang/src/ast/display.rs b/vhdl_lang/src/ast/display.rs index c0ff64a8..0837d58c 100644 --- a/vhdl_lang/src/ast/display.rs +++ b/vhdl_lang/src/ast/display.rs @@ -1140,6 +1140,12 @@ impl Display for ConfigurationDeclaration { } } +impl Display for DisconnectionSpecification { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "disconnection") + } +} + impl Display for EntityDeclaration { fn fmt(&self, f: &mut Formatter<'_>) -> Result { // Not used: context_clause, decl, statements diff --git a/vhdl_lang/src/ast/search.rs b/vhdl_lang/src/ast/search.rs index daaeba8c..5d93a8d3 100644 --- a/vhdl_lang/src/ast/search.rs +++ b/vhdl_lang/src/ast/search.rs @@ -67,6 +67,7 @@ pub enum FoundDeclaration<'a> { ConcurrentStatement(&'a LabeledConcurrentStatement), SequentialStatement(&'a LabeledSequentialStatement), View(&'a ModeViewDeclaration), + GuardedSignalListWithIdent(&'a WithDecl), } pub trait Searcher { @@ -1075,14 +1076,33 @@ impl Search for Declaration { return_if_found!(open_info.search(ctx, searcher)); return_if_found!(file_name.search(ctx, searcher)); } - Declaration::Package(ref package_instance) => { return_if_found!(package_instance.search(ctx, searcher)); } - + Declaration::PackageDeclaration(ref decl) => { + return_if_found!(decl.search(ctx, searcher)); + } + Declaration::PackageBody(ref body) => { + return_if_found!(body.search(ctx, searcher)); + } Declaration::Configuration(_) => { // @TODO } + Declaration::Disconnection(disconnect) => { + let DisconnectionSpecification { + ident, + subtype_indication, + expression, + } = disconnect; + if let GuardedSignalList::Ident(ident) = ident { + return_if_found!(searcher + .search_decl(ctx, FoundDeclaration::GuardedSignalListWithIdent(ident)) + .or_not_found()); + } + return_if_found!(subtype_indication.search(ctx, searcher)); + return_if_found!(expression.search(ctx, searcher)); + } + Declaration::View(view) => { return_if_found!(searcher .search_decl(ctx, FoundDeclaration::View(view)) @@ -1697,6 +1717,7 @@ impl<'a> FoundDeclaration<'a> { FoundDeclaration::SequentialStatement(..) => None, FoundDeclaration::SubprogramInstantiation(_) => None, FoundDeclaration::View(view) => view.end_ident_pos, + FoundDeclaration::GuardedSignalListWithIdent(..) => None, } } @@ -1732,6 +1753,7 @@ impl<'a> FoundDeclaration<'a> { FoundDeclaration::ConcurrentStatement(value) => &value.label.decl, FoundDeclaration::SequentialStatement(value) => &value.label.decl, FoundDeclaration::View(value) => &value.ident.decl, + FoundDeclaration::GuardedSignalListWithIdent(value) => &value.decl, } } } @@ -1839,6 +1861,9 @@ impl std::fmt::Display for FoundDeclaration<'_> { FoundDeclaration::GenerateBody(value) => { write!(f, "{value}") } + FoundDeclaration::GuardedSignalListWithIdent(value) => { + write!(f, "{value}") + } FoundDeclaration::ConcurrentStatement(value) => { if let Some(ref label) = value.label.tree { write!(f, "{label}") diff --git a/vhdl_lang/src/named_entity.rs b/vhdl_lang/src/named_entity.rs index b8c7159a..2cf44e8d 100644 --- a/vhdl_lang/src/named_entity.rs +++ b/vhdl_lang/src/named_entity.rs @@ -7,8 +7,9 @@ use crate::ast::{ AliasDeclaration, AnyDesignUnit, AnyPrimaryUnit, AnySecondaryUnit, Attribute, AttributeDeclaration, AttributeSpecification, ComponentDeclaration, Declaration, Designator, - FileDeclaration, HasIdent, Ident, InterfaceFileDeclaration, InterfacePackageDeclaration, - ModeViewDeclaration, ObjectClass, ObjectDeclaration, PackageInstantiation, SubprogramBody, + DisconnectionSpecification, FileDeclaration, GuardedSignalList, HasIdent, Ident, + InterfaceFileDeclaration, InterfacePackageDeclaration, ModeViewDeclaration, ObjectClass, + ObjectDeclaration, PackageBody, PackageDeclaration, PackageInstantiation, SubprogramBody, SubprogramInstantiation, SubprogramSpecification, TypeDeclaration, WithDecl, }; use crate::ast::{ExternalObjectClass, InterfaceDeclaration, InterfaceObjectDeclaration}; @@ -65,6 +66,7 @@ pub enum AnyEntKind<'a> { Library, Design(Design<'a>), View(Subtype<'a>), + Disconnection(Subtype<'a>), } impl<'a> AnyEntKind<'a> { @@ -128,6 +130,7 @@ impl<'a> AnyEntKind<'a> { Design(design) => design.describe(), Type(typ) => typ.describe(), View(..) => "view", + Disconnection(..) => "disconnection", } } } @@ -631,8 +634,11 @@ impl HasEntityId for Declaration { Declaration::SubprogramBody(body) => body.ent_id(), Declaration::SubprogramInstantiation(decl) => decl.ent_id(), Declaration::Package(pkg) => pkg.ent_id(), + Declaration::PackageDeclaration(pkg) => pkg.ent_id(), + Declaration::PackageBody(pkg) => pkg.ent_id(), Declaration::Use(_) => None, Declaration::Configuration(_) => None, + Declaration::Disconnection(pkg) => pkg.ent_id(), Declaration::View(decl) => decl.ent_id(), } } @@ -650,6 +656,27 @@ impl HasEntityId for PackageInstantiation { } } +impl HasEntityId for PackageDeclaration { + fn ent_id(&self) -> Option { + self.ident.decl.get() + } +} + +impl HasEntityId for DisconnectionSpecification { + fn ent_id(&self) -> Option { + match &self.ident { + GuardedSignalList::Ident(with_decl) => with_decl.decl.get(), + _ => None, + } + } +} + +impl HasEntityId for PackageBody { + fn ent_id(&self) -> Option { + self.ident.decl.get() + } +} + impl HasEntityId for SubprogramBody { fn ent_id(&self) -> Option { self.specification.ent_id() diff --git a/vhdl_lang/src/syntax.rs b/vhdl_lang/src/syntax.rs index f8b20df6..be48a4c9 100644 --- a/vhdl_lang/src/syntax.rs +++ b/vhdl_lang/src/syntax.rs @@ -16,6 +16,7 @@ mod configuration; mod context; mod declarative_part; mod design_unit; +mod disconnection; mod expression; mod interface_declaration; mod names; diff --git a/vhdl_lang/src/syntax/declarative_part.rs b/vhdl_lang/src/syntax/declarative_part.rs index 49144074..919d80ab 100644 --- a/vhdl_lang/src/syntax/declarative_part.rs +++ b/vhdl_lang/src/syntax/declarative_part.rs @@ -10,6 +10,7 @@ use super::common::ParseResult; use super::component_declaration::parse_component_declaration; use super::configuration::parse_configuration_specification; use super::context::parse_use_clause; +use super::design_unit::{parse_package_body, parse_package_declaration}; use super::names::parse_selected_name; use super::object_declaration::{parse_file_declaration, parse_object_declaration}; use super::subprogram::parse_subprogram; @@ -18,6 +19,7 @@ use super::type_declaration::parse_type_declaration; use crate::ast::token_range::WithTokenSpan; use crate::ast::{ContextClause, Declaration, PackageInstantiation}; use crate::syntax::concurrent_statement::parse_map_aspect; +use crate::syntax::disconnection::parse_disconnection_specification; use crate::syntax::view::parse_mode_view_declaration; use vhdl_lang::syntax::parser::ParsingContext; @@ -62,39 +64,41 @@ pub fn is_declarative_part(ctx: &mut ParsingContext) -> ParseResult { | For | View | Begin + | Disconnect )) } +pub fn is_recover_token(kind: Kind) -> bool { + matches!( + kind, + Type | Subtype + | Component + | Impure + | Pure + | Function + | Procedure + | Package + | For + | File + | Shared + | Constant + | Signal + | Variable + | Attribute + | View + | Use + | Alias + | Begin + | End + | Disconnect + ) +} + pub fn parse_declarative_part( ctx: &mut ParsingContext<'_>, ) -> ParseResult>> { let mut declarations: Vec> = Vec::new(); - fn is_recover_token(kind: Kind) -> bool { - matches!( - kind, - Type | Subtype - | Component - | Impure - | Pure - | Function - | Procedure - | Package - | For - | File - | Shared - | Constant - | Signal - | Variable - | Attribute - | View - | Use - | Alias - | Begin - | End - ) - } - while let Some(token) = ctx.stream.peek() { let start_token = ctx.stream.get_current_token_id(); match token.kind { @@ -104,7 +108,15 @@ pub fn parse_declarative_part( Type | Subtype => parse_type_declaration(ctx).map(Declaration::Type)?, Component => parse_component_declaration(ctx).map(Declaration::Component)?, Impure | Pure | Function | Procedure => parse_subprogram(ctx)?, - Package => parse_package_instantiation(ctx).map(Declaration::Package)?, + Package => { + if ctx.stream.next_kinds_are(&[Package, Identifier, Is, New]) { + parse_package_instantiation(ctx).map(Declaration::Package)? + } else if ctx.stream.next_kinds_are(&[Package, Body]) { + parse_package_body(ctx).map(Declaration::PackageBody)? + } else { + parse_package_declaration(ctx).map(Declaration::PackageDeclaration)? + } + } For => { parse_configuration_specification(ctx).map(Declaration::Configuration)? } @@ -177,16 +189,35 @@ pub fn parse_declarative_part( } } + Disconnect => { + let decls: ParseResult>> = + parse_disconnection_specification(ctx).map(|decls| { + decls + .into_iter() + .map(|decl| decl.map_into(Declaration::Disconnection)) + .collect() + }); + match decls.or_recover_until(ctx, is_recover_token) { + Ok(ref mut decls) => declarations.append(decls), + Err(err) => { + ctx.diagnostics.push(err); + continue; + } + } + } + _ => { use crate::VHDLStandard::*; let expected: &[Kind] = match ctx.standard { VHDL2008 | VHDL1993 => &[ Type, Subtype, Component, Impure, Pure, Function, Procedure, Package, For, File, Shared, Constant, Signal, Variable, Attribute, Use, Alias, + Disconnect, ], VHDL2019 => &[ Type, Subtype, Component, Impure, Pure, Function, Procedure, Package, For, File, Shared, Constant, Signal, Variable, Attribute, Use, Alias, View, + Disconnect, ], }; ctx.diagnostics.push(token.kinds_error(expected)); @@ -282,7 +313,7 @@ constant x: natural := 5; "Expected 'type', 'subtype', 'component', 'impure', 'pure', \ 'function', 'procedure', 'package', 'for', 'file', \ 'shared', 'constant', 'signal', 'variable', 'attribute', \ - 'use' or 'alias'" + 'use', 'alias' or 'disconnect'" )] ); } @@ -310,7 +341,7 @@ var not_a_var: broken; "Expected 'type', 'subtype', 'component', 'impure', 'pure', \ 'function', 'procedure', 'package', 'for', 'file', \ 'shared', 'constant', 'signal', 'variable', 'attribute', \ - 'use' or 'alias'", + 'use', 'alias' or 'disconnect'", )], ); @@ -328,7 +359,7 @@ var not_a_var: broken; "Expected 'type', 'subtype', 'component', 'impure', 'pure', \ 'function', 'procedure', 'package', 'for', 'file', \ 'shared', 'constant', 'signal', 'variable', 'attribute', \ - 'use', 'alias' or 'view'", + 'use', 'alias', 'view' or 'disconnect'", )], ) } diff --git a/vhdl_lang/src/syntax/design_unit.rs b/vhdl_lang/src/syntax/design_unit.rs index 1d0ea17c..9a1a6c6a 100644 --- a/vhdl_lang/src/syntax/design_unit.rs +++ b/vhdl_lang/src/syntax/design_unit.rs @@ -933,4 +933,34 @@ end entity y; assert_eq!(tok.kind, Context); assert_eq!(tok.pos, code.s1("context").pos()); } + + // #[test] + // fn parse_architecture_body_with_package_decl_and_body() { + // let (code, design_file) = parse_ok( + // " + //architecture arch of ent is + // package my_pkg is + // -- ... + // end my_pkg; + // package body my_pkg is + // -- ... + // end package body; + //begin + //end arch; + //", + // ); + // + //assert_eq!( + // code.with_stream_no_diagnostics(parse_architecture_body()), + // ArchitectureBody { + // span: code.token_span(), + // context_clause: ContextClause::default(), + // ident: code.s1("arch").decl_ident(), + // entity_name: None, // TODO + // begin_token : code.s1("architecture").token(), + // decl : None, + // statements : None, + // end_ident_pos : None, + // }) + // } } diff --git a/vhdl_lang/src/syntax/disconnection.rs b/vhdl_lang/src/syntax/disconnection.rs new file mode 100644 index 00000000..e5536f03 --- /dev/null +++ b/vhdl_lang/src/syntax/disconnection.rs @@ -0,0 +1,215 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com + +use super::common::ParseResult; +use super::expression::parse_expression; +use super::tokens::{Kind::*, TokenSpan}; +use crate::ast::token_range::WithTokenSpan; +use crate::ast::*; +use crate::syntax::subtype_indication::parse_subtype_indication; +use vhdl_lang::syntax::parser::ParsingContext; + +/// LRM 7.4 Disconnection Specification +pub fn parse_disconnection_specification( + ctx: &mut ParsingContext<'_>, +) -> ParseResult>> { + let start_token = ctx.stream.expect_kind(Disconnect)?; + let mut idents: Vec = Vec::new(); + if let Some(token) = ctx.stream.peek() { + match token.kind { + All => { + idents.push(GuardedSignalList::All); + ctx.stream.skip(); + } + Others => { + idents.push(GuardedSignalList::Others); + ctx.stream.skip(); + } + _ => loop { + idents.push(GuardedSignalList::Ident(WithDecl::new( + ctx.stream.expect_ident()?, + ))); + if ctx.stream.next_kind_is(Comma) { + ctx.stream.skip(); + } else { + break; + } + }, + } + } + + ctx.stream.expect_kind(Colon)?; + let subtype_indication = parse_subtype_indication(ctx)?; + ctx.stream.expect_kind(After)?; + let expression = parse_expression(ctx)?; + let end_token = ctx.stream.expect_kind(SemiColon)?; + + Ok(idents + .into_iter() + .map(|ident| { + WithTokenSpan::new( + DisconnectionSpecification { + ident, + subtype_indication: subtype_indication.clone(), + expression: expression.clone(), + }, + TokenSpan::new(start_token, end_token), + ) + }) + .collect()) +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::syntax::test::Code; + #[test] + fn disconnect_spec_with_scalar_or_composite_signal() { + let code = Code::new( + "\ + disconnect S: T after 42 ms; + ", + ); + assert_eq!( + code.with_stream_no_diagnostics(parse_disconnection_specification)[0], + WithTokenSpan::new( + DisconnectionSpecification { + ident: GuardedSignalList::Ident(WithDecl::new(code.s1("S").ident())), + subtype_indication: code.s1("T").subtype_indication(), + expression: code.s1("42 ms").expr(), + }, + code.token_span() + ) + ) + } + + #[test] + fn disconnect_spec_with_scalar_or_composite_signal_variant() { + let code = Code::new( + "\ + disconnect foobar : integer after 100*CLK_PERIOD; + ", + ); + assert_eq!( + code.with_stream_no_diagnostics(parse_disconnection_specification)[0], + WithTokenSpan::new( + DisconnectionSpecification { + ident: GuardedSignalList::Ident(WithDecl::new(code.s1("foobar").ident())), + subtype_indication: code.s1("integer").subtype_indication(), + expression: code.s1("100*CLK_PERIOD").expr(), + }, + code.token_span() + ) + ) + } + + #[test] + fn disconnect_spec_explicit_with_others() { + let code = Code::new( + "\ + disconnect others: std_logic after bar; + ", + ); + assert_eq!( + code.with_stream_no_diagnostics(parse_disconnection_specification)[0], + WithTokenSpan::new( + DisconnectionSpecification { + ident: GuardedSignalList::Others, + subtype_indication: code.s1("std_logic").subtype_indication(), + expression: code.s1("bar").expr(), + }, + code.token_span() + ) + ) + } + + #[test] + fn disconnect_spec_explicit_with_all() { + let code = Code::new( + "\ + disconnect all : unsigned(3 downto 0) after bar; + ", + ); + assert_eq!( + code.with_stream_no_diagnostics(parse_disconnection_specification)[0], + WithTokenSpan::new( + DisconnectionSpecification { + ident: GuardedSignalList::All, + subtype_indication: code.s1("unsigned(3 downto 0)").subtype_indication(), + expression: code.s1("bar").expr(), + }, + code.token_span() + ) + ) + } + + #[test] + fn disconnect_spec_syntax_error_1() { + let code = Code::new( + "\ +disconnect ; T after 42 ms; +", + ); + let (decl, _) = code.with_partial_stream_diagnostics(parse_disconnection_specification); + assert!(decl.is_err()); + } + + #[test] + fn disconnect_spec_syntax_error_2() { + let code = Code::new( + "\ +disconnect 7 after 42 ms; +", + ); + let (decl, _) = code.with_partial_stream_diagnostics(parse_disconnection_specification); + assert!(decl.is_err()); + } + + #[test] + fn disconnect_spec_syntax_error_3() { + let code = Code::new( + "\ +disconnect foo after 42 ms; +", + ); + let (decl, _) = code.with_partial_stream_diagnostics(parse_disconnection_specification); + assert!(decl.is_err()); + } + + #[test] + fn disconnect_spec_syntax_error_4() { + let code = Code::new( + "\ +disconnect foo : std_logic afterrr 42 ms; +", + ); + let (decl, _) = code.with_partial_stream_diagnostics(parse_disconnection_specification); + assert!(decl.is_err()); + } + + #[test] + fn disconnect_spec_syntax_error_5() { + let code = Code::new( + "\ +disconnect foo : after 42 ms bar; +", + ); + let (decl, _) = code.with_partial_stream_diagnostics(parse_disconnection_specification); + assert!(decl.is_err()); + } + + #[test] + fn disconnect_spec_syntax_error_6() { + let code = Code::new( + "\ +diconnect foo : after 42 ms; +", + ); + let (decl, _) = code.with_partial_stream_diagnostics(parse_disconnection_specification); + assert!(decl.is_err()); + } +} diff --git a/vhdl_ls/src/vhdl_server.rs b/vhdl_ls/src/vhdl_server.rs index 38877cf8..788ca5f8 100644 --- a/vhdl_ls/src/vhdl_server.rs +++ b/vhdl_ls/src/vhdl_server.rs @@ -900,6 +900,7 @@ fn entity_kind_to_completion_kind(kind: &AnyEntKind) -> CompletionItemKind { AnyEntKind::Library => CompletionItemKind::MODULE, AnyEntKind::Design(_) => CompletionItemKind::MODULE, AnyEntKind::View(_) => CompletionItemKind::INTERFACE, + AnyEntKind::Disconnection(_) => CompletionItemKind::FIELD, } } @@ -1125,6 +1126,7 @@ fn to_symbol_kind(kind: &AnyEntKind) -> SymbolKind { AnyEntKind::Concurrent(_) => SymbolKind::NAMESPACE, AnyEntKind::Library => SymbolKind::NAMESPACE, AnyEntKind::View(_) => SymbolKind::INTERFACE, + AnyEntKind::Disconnection(_) => SymbolKind::FIELD, AnyEntKind::Design(d) => match d { vhdl_lang::Design::Entity(_, _) => SymbolKind::MODULE, vhdl_lang::Design::Architecture(_) => SymbolKind::MODULE,