1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//! Source-level checks on constraint declarations that slang does not make.
//!
//! IEEE 1800-2023 18.5.1: "it shall be an error if more than one external
//! constraint block is provided for any given prototype". slang binds the
//! first external block it finds and never looks at the rest, so a second one
//! would be silently ignored; this pass rejects it.
use std::collections::HashSet;
use std::ops::Range;
use sv_lang::nodes::ConstraintDeclarationSyntax;
use sv_lang::{AstNode, SyntaxTree};
/// Rejects a prototype that more than one `constraint C::name { ... }` block
/// in the same scope claims. The error text names the prototype.
pub(crate) fn check_single_external_blocks(trees: &[SyntaxTree]) -> Result<(), String> {
let mut seen: HashSet<(usize, Option<Range<usize>>, String)> = HashSet::new();
for (index, tree) in trees.iter().enumerate() {
for decl in tree.root().find_all::<ConstraintDeclarationSyntax>() {
// Only the out-of-block form `C::name` is an external block.
let name: String = decl
.name()
.syntax()
.text()
.chars()
.filter(|c| !c.is_whitespace())
.collect();
if !name.contains("::") {
continue;
}
let scope = decl.syntax().parent().map(|p| p.byte_range());
if !seen.insert((index, scope, name.clone())) {
return Err(format!(
"more than one external constraint block for the prototype `{name}` \
(IEEE 1800-2023 18.5.1)"
));
}
}
}
Ok(())
}