diff --git a/vhdl_lang/src/analysis/declarative.rs b/vhdl_lang/src/analysis/declarative.rs index 4beaa086..2a7718ff 100644 --- a/vhdl_lang/src/analysis/declarative.rs +++ b/vhdl_lang/src/analysis/declarative.rs @@ -626,12 +626,33 @@ impl<'a, 't> AnalyzeContext<'a, 't> { Declaration::Configuration(..) => {} Declaration::Disconnection(ref mut disc) => { let DisconnectionSpecification { - ident: _, + 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( @@ -1210,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/names.rs b/vhdl_lang/src/analysis/names.rs index f1b7bd68..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(_) 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 fafd3165..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; @@ -116,7 +116,7 @@ begin end arch; ", ); - check_diagnostics(builder.analyze(), vec![]) + check_no_diagnostics(&builder.analyze()) } #[test] @@ -186,10 +186,17 @@ end arch; ); check_diagnostics( builder.analyze(), - vec![Diagnostic::new( - code.s1("bar"), - "No declaration of 'bar'", - ErrorCode::Unresolved, - )], + 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 fbb131b7..e7a2118a 100644 --- a/vhdl_lang/src/ast.rs +++ b/vhdl_lang/src/ast.rs @@ -1383,7 +1383,7 @@ pub struct ConfigurationSpecification { pub enum GuardedSignalList { All, Others, - Ident(WithDecl) + Ident(WithDecl), } /// LRM 7.4 Disconnection specification diff --git a/vhdl_lang/src/ast/display.rs b/vhdl_lang/src/ast/display.rs index b827290a..0837d58c 100644 --- a/vhdl_lang/src/ast/display.rs +++ b/vhdl_lang/src/ast/display.rs @@ -1142,11 +1142,7 @@ impl Display for ConfigurationDeclaration { impl Display for DisconnectionSpecification { fn fmt(&self, f: &mut Formatter<'_>) -> Result { - if let Some(ident) = &self.ident { - write!(f, "disconnection {}", ident) - } else { - write!(f, "disconnection") - } + write!(f, "disconnection") } } diff --git a/vhdl_lang/src/ast/search.rs b/vhdl_lang/src/ast/search.rs index e033724b..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 { @@ -1089,13 +1090,19 @@ impl Search for Declaration { } Declaration::Disconnection(disconnect) => { let DisconnectionSpecification { - ident: _, + 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)) @@ -1710,6 +1717,7 @@ impl<'a> FoundDeclaration<'a> { FoundDeclaration::SequentialStatement(..) => None, FoundDeclaration::SubprogramInstantiation(_) => None, FoundDeclaration::View(view) => view.end_ident_pos, + FoundDeclaration::GuardedSignalListWithIdent(..) => None, } } @@ -1745,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, } } } @@ -1852,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 35bc38f1..2cf44e8d 100644 --- a/vhdl_lang/src/named_entity.rs +++ b/vhdl_lang/src/named_entity.rs @@ -7,10 +7,10 @@ use crate::ast::{ AliasDeclaration, AnyDesignUnit, AnyPrimaryUnit, AnySecondaryUnit, Attribute, AttributeDeclaration, AttributeSpecification, ComponentDeclaration, Declaration, Designator, - DisconnectionSpecification, FileDeclaration, HasIdent, Ident, InterfaceFileDeclaration, - InterfacePackageDeclaration, ModeViewDeclaration, ObjectClass, ObjectDeclaration, PackageBody, - PackageDeclaration, PackageInstantiation, SubprogramBody, SubprogramInstantiation, - SubprogramSpecification, TypeDeclaration, WithDecl, + DisconnectionSpecification, FileDeclaration, GuardedSignalList, HasIdent, Ident, + InterfaceFileDeclaration, InterfacePackageDeclaration, ModeViewDeclaration, ObjectClass, + ObjectDeclaration, PackageBody, PackageDeclaration, PackageInstantiation, SubprogramBody, + SubprogramInstantiation, SubprogramSpecification, TypeDeclaration, WithDecl, }; use crate::ast::{ExternalObjectClass, InterfaceDeclaration, InterfaceObjectDeclaration}; use crate::data::*; @@ -66,6 +66,7 @@ pub enum AnyEntKind<'a> { Library, Design(Design<'a>), View(Subtype<'a>), + Disconnection(Subtype<'a>), } impl<'a> AnyEntKind<'a> { @@ -129,6 +130,7 @@ impl<'a> AnyEntKind<'a> { Design(design) => design.describe(), Type(typ) => typ.describe(), View(..) => "view", + Disconnection(..) => "disconnection", } } } @@ -662,7 +664,10 @@ impl HasEntityId for PackageDeclaration { impl HasEntityId for DisconnectionSpecification { fn ent_id(&self) -> Option { - self.ident.as_ref().and_then(|ident| ident.decl.get()) + match &self.ident { + GuardedSignalList::Ident(with_decl) => with_decl.decl.get(), + _ => None, + } } } diff --git a/vhdl_lang/src/syntax/declarative_part.rs b/vhdl_lang/src/syntax/declarative_part.rs index 8c9dfd00..919d80ab 100644 --- a/vhdl_lang/src/syntax/declarative_part.rs +++ b/vhdl_lang/src/syntax/declarative_part.rs @@ -190,9 +190,15 @@ pub fn parse_declarative_part( } Disconnect => { - match parse_disconnection_specification(ctx).or_recover_until(ctx, is_recover_token) - { - Ok(decl) => declarations.push(decl.map_into(Declaration::Disconnection)), + 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; diff --git a/vhdl_lang/src/syntax/disconnection.rs b/vhdl_lang/src/syntax/disconnection.rs index 03f4c02b..e5536f03 100644 --- a/vhdl_lang/src/syntax/disconnection.rs +++ b/vhdl_lang/src/syntax/disconnection.rs @@ -6,34 +6,38 @@ use super::common::ParseResult; use super::expression::parse_expression; -//use super::separated_list::parse_ident_list; use super::tokens::{Kind::*, TokenSpan}; use crate::ast::token_range::WithTokenSpan; use crate::ast::*; -use crate::syntax::declarative_part::is_recover_token; use crate::syntax::subtype_indication::parse_subtype_indication; -use crate::syntax::Kind; use vhdl_lang::syntax::parser::ParsingContext; /// LRM 7.4 Disconnection Specification pub fn parse_disconnection_specification( ctx: &mut ParsingContext<'_>, -) -> ParseResult> { +) -> ParseResult>> { let start_token = ctx.stream.expect_kind(Disconnect)?; - - let ident = GuardedSignalList::All; + 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 => { - let ident = GuardedSignalList::Others; + idents.push(GuardedSignalList::Others); ctx.stream.skip(); } - _ => { // Identifier - let ident = WithDecl::new(ctx.stream.expect_ident()?); - } + _ => loop { + idents.push(GuardedSignalList::Ident(WithDecl::new( + ctx.stream.expect_ident()?, + ))); + if ctx.stream.next_kind_is(Comma) { + ctx.stream.skip(); + } else { + break; + } + }, } } @@ -42,177 +46,170 @@ pub fn parse_disconnection_specification( ctx.stream.expect_kind(After)?; let expression = parse_expression(ctx)?; let end_token = ctx.stream.expect_kind(SemiColon)?; - Ok(WithTokenSpan::new( - DisconnectionSpecification { - ident, - subtype_indication, - expression, - }, - TokenSpan::new(start_token, end_token), - )) + + 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::data::Diagnostic; -// use crate::syntax::test::{check_diagnostics, 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), -// WithTokenSpan::new( -// DisconnectionSpecification { -// ident: code.s1("S").decl_ident(), -// subtype_indication: Some(code.s1("T").subtype_indication()), -// expression: Some(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), -// WithTokenSpan::new( -// DisconnectionSpecification { -// ident: Some(code.s1("foobar").decl_ident()), -// subtype_indication: Some(code.s1("integer").subtype_indication()), -// expression: Some(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), -// WithTokenSpan::new( -// DisconnectionSpecification { -// ident: None, -// subtype_indication: Some(code.s1("std_logic").subtype_indication()), -// expression: Some(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), -// WithTokenSpan::new( -// DisconnectionSpecification { -// ident: None, -// subtype_indication: Some(code.s1("unsigned(3 downto 0)").subtype_indication()), -// expression: Some(code.s1("bar").expr()), -// }, -// code.token_span() -// ) -// ) -// } -// -// #[test] -// fn disconnect_spec_syntax_error_1() { -// let code = Code::new( -// "\ -//disconnect ; T after 42 ms; -//", -// ); -// let (_, diag) = code.with_partial_stream_diagnostics(parse_disconnection_specification); -// check_diagnostics( -// diag, -// vec![Diagnostic::syntax_error( -// code.s1(";").pos(), -// "Expected '{identifier}', 'all' or 'others'", -// )], -// ); -// } -// -// #[test] -// fn disconnect_spec_syntax_error_2() { -// let code = Code::new( -// "\ -//disconnect 7 after 42 ms; -//", -// ); -// let (_, diag) = code.with_partial_stream_diagnostics(parse_disconnection_specification); -// check_diagnostics( -// diag, -// vec![Diagnostic::syntax_error( -// code.s1("7").pos(), -// "Expected '{identifier}', 'all' or 'others'", -// )], -// ); -// } -// -// #[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()); -// } -//} +#[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,