Skip to content

Commit f9d0ee9

Browse files
authored
Feat: Emit diagnostic when a declaration is not allowed in the region it resides (#224)
* Feat: Emit diagnostic when a declaration is not allowed in the region it resides * Disallow shared variables in block contexts; allow them elsewhere * Allow signals in packages * Refactor: Use AnyEntKind enum instead of DeclarativeContext * Add copyright header
1 parent c86d370 commit f9d0ee9

File tree

8 files changed

+185
-11
lines changed

8 files changed

+185
-11
lines changed

vhdl_lang/src/analysis/declarative.rs

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,95 @@
66

77
use super::names::*;
88
use super::*;
9-
use crate::ast;
109
use crate::ast::*;
1110
use crate::data::*;
1211
use crate::named_entity::{Signature, *};
12+
use crate::{ast, named_entity, HasTokenSpan};
1313
use analyze::*;
1414
use fnv::FnvHashMap;
1515
use std::collections::hash_map::Entry;
1616

17+
impl Declaration {
18+
pub fn is_allowed_in_context(&self, parent: &AnyEntKind) -> bool {
19+
use Declaration::*;
20+
use ObjectClass::*;
21+
match parent {
22+
AnyEntKind::Design(Design::Architecture(..))
23+
| AnyEntKind::Concurrent(Some(Concurrent::Block | Concurrent::Generate)) => matches!(
24+
self,
25+
Object(ObjectDeclaration {
26+
class: Constant | Signal | SharedVariable,
27+
..
28+
}) | File(_)
29+
| Type(_)
30+
| Component(_)
31+
| Attribute(_)
32+
| Alias(_)
33+
| SubprogramDeclaration(_)
34+
| SubprogramInstantiation(_)
35+
| SubprogramBody(_)
36+
| Use(_)
37+
| Package(_)
38+
| Configuration(_)
39+
),
40+
AnyEntKind::Design(Design::Configuration) => {
41+
matches!(self, Use(_) | Attribute(ast::Attribute::Specification(_)))
42+
}
43+
AnyEntKind::Design(Design::Entity(..)) => matches!(
44+
self,
45+
Object(_)
46+
| File(_)
47+
| Type(_)
48+
| Attribute(_)
49+
| Alias(_)
50+
| SubprogramDeclaration(_)
51+
| SubprogramInstantiation(_)
52+
| SubprogramBody(_)
53+
| Use(_)
54+
| Package(_)
55+
),
56+
AnyEntKind::Design(Design::PackageBody | Design::UninstPackage(..))
57+
| AnyEntKind::Overloaded(Overloaded::SubprogramDecl(_) | Overloaded::Subprogram(_))
58+
| AnyEntKind::Concurrent(Some(Concurrent::Process))
59+
| AnyEntKind::Type(named_entity::Type::Protected(..)) => matches!(
60+
self,
61+
Object(ObjectDeclaration {
62+
class: Constant | Variable | SharedVariable,
63+
..
64+
}) | File(_)
65+
| Type(_)
66+
| Attribute(_)
67+
| Alias(_)
68+
| SubprogramDeclaration(_)
69+
| SubprogramInstantiation(_)
70+
| SubprogramBody(_)
71+
| Use(_)
72+
| Package(_)
73+
),
74+
AnyEntKind::Design(Design::Package(..)) => matches!(
75+
self,
76+
Object(_)
77+
| File(_)
78+
| Type(_)
79+
| Component(_)
80+
| Attribute(_)
81+
| Alias(_)
82+
| SubprogramDeclaration(_)
83+
| SubprogramInstantiation(_)
84+
| Use(_)
85+
| Package(_)
86+
),
87+
_ => {
88+
// AnyEntKind::Library is used in tests for a generic declarative region
89+
if !(cfg!(test) && matches!(parent, AnyEntKind::Library)) {
90+
debug_assert!(false, "Parent should be a declarative region");
91+
}
92+
true
93+
}
94+
}
95+
}
96+
}
97+
1798
impl<'a> AnalyzeContext<'a> {
1899
pub fn analyze_declarative_part(
19100
&self,
@@ -29,6 +110,13 @@ impl<'a> AnalyzeContext<'a> {
29110

30111
let (decl, remaining) = declarations[i..].split_first_mut().unwrap();
31112

113+
if !decl.is_allowed_in_context(parent.kind()) {
114+
diagnostics.error(
115+
decl.get_pos(self.ctx),
116+
format!("{} declaration not allowed here", decl.describe(),),
117+
)
118+
}
119+
32120
match decl {
33121
Declaration::Type(type_decl) => match type_decl.def {
34122
TypeDefinition::Incomplete(ref mut reference) => {

vhdl_lang/src/analysis/names.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1645,6 +1645,36 @@ fn plural(singular: &'static str, plural: &'static str, count: usize) -> &'stati
16451645
}
16461646
}
16471647

1648+
impl Declaration {
1649+
pub fn describe(&self) -> &'static str {
1650+
match self {
1651+
Declaration::Object(ObjectDeclaration { class, .. }) => match class {
1652+
ObjectClass::Constant => "constant",
1653+
ObjectClass::Signal => "signal",
1654+
ObjectClass::Variable => "variable",
1655+
ObjectClass::SharedVariable => "shared variable",
1656+
},
1657+
Declaration::File(_) => "file",
1658+
Declaration::Type(TypeDeclaration { def, .. }) => match def {
1659+
TypeDefinition::Subtype(_) => "subtype",
1660+
_ => "type",
1661+
},
1662+
Declaration::Component(_) => "component",
1663+
Declaration::Attribute(attribute) => match attribute {
1664+
Attribute::Specification(_) => "attribute specification",
1665+
Attribute::Declaration(_) => "attribute",
1666+
},
1667+
Declaration::Alias(_) => "alias",
1668+
Declaration::SubprogramDeclaration(_) => "subprogram",
1669+
Declaration::SubprogramInstantiation(_) => "subprogram instantiation",
1670+
Declaration::SubprogramBody(_) => "subprogram body",
1671+
Declaration::Use(_) => "use",
1672+
Declaration::Package(_) => "package instantiation",
1673+
Declaration::Configuration(_) => "configuration",
1674+
}
1675+
}
1676+
}
1677+
16481678
impl Diagnostic {
16491679
fn cannot_be_prefix(prefix_pos: &SrcPos, resolved: ResolvedName, suffix: Suffix) -> Diagnostic {
16501680
let suffix_desc = match suffix {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
3+
// You can obtain one at http://mozilla.org/MPL/2.0/.
4+
//
5+
// Copyright (c) 2023, Olof Kraigher [email protected]
6+
use crate::analysis::tests::{check_diagnostics, LibraryBuilder};
7+
use crate::Diagnostic;
8+
9+
#[test]
10+
pub fn declaration_not_allowed_everywhere() {
11+
let mut builder = LibraryBuilder::new();
12+
let code = builder.code(
13+
"libname",
14+
"\
15+
entity ent is
16+
end entity;
17+
18+
architecture arch of ent is
19+
20+
function my_func return natural is
21+
signal x : bit;
22+
begin
23+
24+
end my_func;
25+
begin
26+
27+
my_block : block
28+
variable y: natural;
29+
begin
30+
end block my_block;
31+
32+
end architecture;
33+
",
34+
);
35+
check_diagnostics(
36+
builder.analyze(),
37+
vec![
38+
Diagnostic::error(
39+
code.s1("signal x : bit;"),
40+
"signal declaration not allowed here",
41+
),
42+
Diagnostic::error(
43+
code.s1("variable y: natural;"),
44+
"variable declaration not allowed here",
45+
),
46+
],
47+
)
48+
}

vhdl_lang/src/analysis/tests/implicit.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,15 @@ fn deallocate_is_defined_for_access_type() {
116116
package pkg is
117117
type arr_t is array (natural range <>) of character;
118118
type ptr_t is access arr_t;
119+
end package;
119120
121+
package body pkg is
120122
procedure theproc is
121123
variable theptr: ptr_t;
122124
begin
123125
deallocate(theptr);
124126
end procedure;
125-
end package;
127+
end package body;
126128
",
127129
);
128130
}

vhdl_lang/src/analysis/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod association_formal;
99
mod circular_dependencies;
1010
mod context_clause;
1111
mod custom_attributes;
12+
mod declarations;
1213
mod deferred_constant;
1314
mod hierarchy;
1415
mod homographs;

vhdl_lang/src/analysis/tests/protected_type.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,15 +249,19 @@ fn protected_type_body_reference() {
249249
let code = builder.code(
250250
"libname",
251251
"
252-
package pkg1 is
252+
entity ent1 is
253+
end ent1;
254+
255+
architecture arch of ent1 is
253256
type prot_t is protected
254257
end protected;
255258
256259
type prot_t is protected body
257260
end protected body;
258261
259262
shared variable var : prot_t;
260-
end package;",
263+
begin
264+
end architecture;",
261265
);
262266

263267
let (root, diagnostics) = builder.get_analyzed_root();

vhdl_lang/src/analysis/tests/resolves_type_mark.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,7 @@ signal sig2 : rec.field'subtype;
428428
fn check_good_type_marks() {
429429
check_code_with_no_diagnostics(
430430
"
431+
431432
package gpkg is
432433
-- Interface type
433434
generic (type type_t);
@@ -437,7 +438,10 @@ package gpkg is
437438
end record;
438439
end package;
439440
440-
package pkg is
441+
entity ent is
442+
end entity ent;
443+
444+
architecture arch of ent is
441445
type incomplete;
442446
-- Incomplete type
443447
type access_t is access incomplete;
@@ -461,11 +465,8 @@ package pkg is
461465
-- Alias of type
462466
alias alias_t is enum_t;
463467
constant const2 : alias_t := alpha;
464-
end package;
465-
466-
package body pkg is
467-
end package body;
468-
468+
begin
469+
end architecture;
469470
",
470471
);
471472
}

vhdl_lang/src/analysis/tests/visibility.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,7 @@ fn generics_are_visible_in_procedures_but_not_outside() {
772772
a := b;
773773
b := temp;
774774
end procedure swap;
775-
variable temp2: T;
775+
shared variable temp2: T;
776776
",
777777
);
778778
let (_, diagnostics) = builder.get_analyzed_root();

0 commit comments

Comments
 (0)