Skip to content

Commit 4ce9de6

Browse files
committed
Fix ifdef attribute state leaking across scopes accepting orphan else/elsif
1 parent 34c9a03 commit 4ce9de6

2 files changed

Lines changed: 88 additions & 0 deletions

File tree

crates/analyzer/src/handlers/check_attribute.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,15 @@ impl CheckAttribute {
165165
self.check_ifdef(&mut attrs, last);
166166
self.set_attrs(attrs, range);
167167
}
168+
169+
/// An elsif/else can only chain to an ifdef/ifndef in the same group
170+
/// list; clear the state left by a previous list or an enclosing scope,
171+
/// which would otherwise let an orphan #[else] emit an unmatched `else.
172+
fn reset_ifdef(&mut self) {
173+
self.ifdef_state = IfdefState::None;
174+
self.ifdef_pos.clear();
175+
self.ifdef_neg.clear();
176+
}
168177
}
169178

170179
impl Handler for CheckAttribute {
@@ -176,6 +185,7 @@ impl Handler for CheckAttribute {
176185
impl VerylGrammarTrait for CheckAttribute {
177186
fn statement_block(&mut self, arg: &StatementBlock) -> Result<(), ParolError> {
178187
if let HandlerPoint::Before = self.point {
188+
self.reset_ifdef();
179189
for x in &arg.statement_block_list {
180190
let x = x.statement_block_group.as_ref();
181191
let attrs: Vec<_> = x
@@ -191,6 +201,7 @@ impl VerylGrammarTrait for CheckAttribute {
191201

192202
fn modport_list(&mut self, arg: &ModportList) -> Result<(), ParolError> {
193203
if let HandlerPoint::Before = self.point {
204+
self.reset_ifdef();
194205
let mut groups = vec![arg.modport_group.as_ref()];
195206
groups.extend(
196207
arg.modport_list_list
@@ -213,6 +224,7 @@ impl VerylGrammarTrait for CheckAttribute {
213224

214225
fn enum_list(&mut self, arg: &EnumList) -> Result<(), ParolError> {
215226
if let HandlerPoint::Before = self.point {
227+
self.reset_ifdef();
216228
let mut groups = vec![arg.enum_group.as_ref()];
217229
groups.extend(arg.enum_list_list.iter().map(|x| x.enum_group.as_ref()));
218230
let len = groups.len();
@@ -231,6 +243,7 @@ impl VerylGrammarTrait for CheckAttribute {
231243

232244
fn struct_union_list(&mut self, arg: &StructUnionList) -> Result<(), ParolError> {
233245
if let HandlerPoint::Before = self.point {
246+
self.reset_ifdef();
234247
let mut groups = vec![arg.struct_union_group.as_ref()];
235248
groups.extend(
236249
arg.struct_union_list_list
@@ -253,6 +266,7 @@ impl VerylGrammarTrait for CheckAttribute {
253266

254267
fn inst_parameter_list(&mut self, arg: &InstParameterList) -> Result<(), ParolError> {
255268
if let HandlerPoint::Before = self.point {
269+
self.reset_ifdef();
256270
let mut groups = vec![arg.inst_parameter_group.as_ref()];
257271
groups.extend(
258272
arg.inst_parameter_list_list
@@ -275,6 +289,7 @@ impl VerylGrammarTrait for CheckAttribute {
275289

276290
fn inst_port_list(&mut self, arg: &InstPortList) -> Result<(), ParolError> {
277291
if let HandlerPoint::Before = self.point {
292+
self.reset_ifdef();
278293
let mut groups = vec![arg.inst_port_group.as_ref()];
279294
groups.extend(
280295
arg.inst_port_list_list
@@ -297,6 +312,7 @@ impl VerylGrammarTrait for CheckAttribute {
297312

298313
fn with_parameter_list(&mut self, arg: &WithParameterList) -> Result<(), ParolError> {
299314
if let HandlerPoint::Before = self.point {
315+
self.reset_ifdef();
300316
let mut groups = vec![arg.with_parameter_group.as_ref()];
301317
groups.extend(
302318
arg.with_parameter_list_list
@@ -319,6 +335,7 @@ impl VerylGrammarTrait for CheckAttribute {
319335

320336
fn port_declaration_list(&mut self, arg: &PortDeclarationList) -> Result<(), ParolError> {
321337
if let HandlerPoint::Before = self.point {
338+
self.reset_ifdef();
322339
let mut groups = vec![arg.port_declaration_group.as_ref()];
323340
groups.extend(
324341
arg.port_declaration_list_list
@@ -341,6 +358,7 @@ impl VerylGrammarTrait for CheckAttribute {
341358

342359
fn module_declaration(&mut self, arg: &ModuleDeclaration) -> Result<(), ParolError> {
343360
if let HandlerPoint::Before = self.point {
361+
self.reset_ifdef();
344362
for x in &arg.module_declaration_list {
345363
let x = x.module_group.as_ref();
346364
let attrs: Vec<_> = x
@@ -356,6 +374,7 @@ impl VerylGrammarTrait for CheckAttribute {
356374

357375
fn interface_declaration(&mut self, arg: &InterfaceDeclaration) -> Result<(), ParolError> {
358376
if let HandlerPoint::Before = self.point {
377+
self.reset_ifdef();
359378
for x in &arg.interface_declaration_list {
360379
let x = x.interface_group.as_ref();
361380
let attrs: Vec<_> = x
@@ -371,6 +390,7 @@ impl VerylGrammarTrait for CheckAttribute {
371390

372391
fn generate_named_block(&mut self, arg: &GenerateNamedBlock) -> Result<(), ParolError> {
373392
if let HandlerPoint::Before = self.point {
393+
self.reset_ifdef();
374394
for x in &arg.generate_named_block_list {
375395
let x = x.generate_group.as_ref();
376396
let attrs: Vec<_> = x
@@ -389,6 +409,7 @@ impl VerylGrammarTrait for CheckAttribute {
389409
arg: &GenerateOptionalNamedBlock,
390410
) -> Result<(), ParolError> {
391411
if let HandlerPoint::Before = self.point {
412+
self.reset_ifdef();
392413
for x in &arg.generate_optional_named_block_list {
393414
let x = x.generate_group.as_ref();
394415
let attrs: Vec<_> = x
@@ -404,6 +425,7 @@ impl VerylGrammarTrait for CheckAttribute {
404425

405426
fn package_declaration(&mut self, arg: &PackageDeclaration) -> Result<(), ParolError> {
406427
if let HandlerPoint::Before = self.point {
428+
self.reset_ifdef();
407429
for x in &arg.package_declaration_list {
408430
let x = x.package_group.as_ref();
409431
let attrs: Vec<_> = x
@@ -419,6 +441,7 @@ impl VerylGrammarTrait for CheckAttribute {
419441

420442
fn veryl(&mut self, arg: &Veryl) -> Result<(), ParolError> {
421443
if let HandlerPoint::Before = self.point {
444+
self.reset_ifdef();
422445
for x in &arg.veryl_list {
423446
let x = x.description_group.as_ref();
424447
let attrs: Vec<_> = x

crates/analyzer/src/tests.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18618,3 +18618,68 @@ module M {
1861818618
let errors = analyze();
1861918619
assert!(errors.is_empty(), "{errors:?}");
1862018620
}
18621+
18622+
#[test]
18623+
fn orphan_else_across_scopes() {
18624+
// The ifdef/elsif/else attribute state persisted across attribute-group
18625+
// lists and nesting levels, so an orphan #[else] chained to an #[ifdef]
18626+
// in a previous module or an enclosing scope and emitted an unmatched
18627+
// `else into the SV output.
18628+
let code = r#"
18629+
module ModuleA {
18630+
#[ifdef(A)]
18631+
let _x: logic = 1'b0;
18632+
let _z: logic = 1'b0;
18633+
}
18634+
module ModuleB {
18635+
#[else]
18636+
let _y: logic = 1'b0;
18637+
let _w: logic = 1'b0;
18638+
}
18639+
"#;
18640+
18641+
let errors = analyze(code);
18642+
assert!(
18643+
errors
18644+
.iter()
18645+
.any(|e| matches!(e, AnalyzerError::AmbiguousElsif { .. })),
18646+
"{errors:?}"
18647+
);
18648+
18649+
// Nested scope: an outer #[ifdef] must not legitimize an inner #[else].
18650+
let code = r#"
18651+
#[ifdef(DEF_X)]
18652+
module ModuleA {
18653+
#[else]
18654+
let _a: logic = 1;
18655+
let _b: logic = 1;
18656+
}
18657+
"#;
18658+
18659+
let errors = analyze(code);
18660+
assert!(
18661+
errors
18662+
.iter()
18663+
.any(|e| matches!(e, AnalyzerError::AmbiguousElsif { .. })),
18664+
"{errors:?}"
18665+
);
18666+
18667+
// A well-formed chain in one list stays accepted.
18668+
let code = r#"
18669+
module ModuleA {
18670+
#[ifdef(A)]
18671+
let _x: logic = 1'b0;
18672+
#[else]
18673+
let _y: logic = 1'b0;
18674+
let _z: logic = 1'b0;
18675+
}
18676+
"#;
18677+
18678+
let errors = analyze(code);
18679+
assert!(
18680+
!errors
18681+
.iter()
18682+
.any(|e| matches!(e, AnalyzerError::AmbiguousElsif { .. })),
18683+
"{errors:?}"
18684+
);
18685+
}

0 commit comments

Comments
 (0)