///|
pub(all) struct SignalUse {
module_name : String
signal_name : String
context : String
span : @source.Span
} derive(Eq, Debug)
///|
fn collect_expression_uses(
module_name : String,
expression : @syntax.Expression,
context : String,
result : Array[SignalUse],
) -> Unit {
match expression {
@syntax.Name(name, span) =>
result.push({ module_name, signal_name: name, context, span })
@syntax.Index(base, index, _) => {
collect_expression_uses(module_name, base, context, result)
collect_expression_uses(module_name, index, context, result)
}
@syntax.Unary(_, operand, _) =>
collect_expression_uses(module_name, operand, context, result)
@syntax.Binary(left, _, right, _) => {
collect_expression_uses(module_name, left, context, result)
collect_expression_uses(module_name, right, context, result)
}
@syntax.Number(_, _) | @syntax.StringLiteral(_, _) | @syntax.Unknown(_) =>
()
}
}
///|
fn collect_statement_uses(
module_name : String,
statement : @syntax.Statement,
result : Array[SignalUse],
) -> Unit {
match statement {
@syntax.Assignment(target, value, _, span) => {
collect_expression_uses(module_name, target, "assignment-target", result)
collect_expression_uses(module_name, value, "assignment-value", result)
if target.name_text() is None {
result.push({
module_name,
signal_name: "",
context: "assignment-target",
span,
})
}
}
@syntax.Block(statements, _) =>
for child in statements {
collect_statement_uses(module_name, child, result)
}
@syntax.If(condition, then_branch, else_branch, _) => {
collect_expression_uses(module_name, condition, "condition", result)
collect_statement_uses(module_name, then_branch, result)
match else_branch {
Some(branch) => collect_statement_uses(module_name, branch, result)
None => ()
}
}
@syntax.Case(expression, arms, _) => {
collect_expression_uses(
module_name, expression, "case-expression", result,
)
for arm in arms {
for choice in arm.choices {
collect_expression_uses(module_name, choice, "case-choice", result)
}
collect_statement_uses(module_name, arm.body, result)
}
}
@syntax.Empty(_) | @syntax.Unknown(_) => ()
}
}
///|
pub fn SemanticIndex::uses(self : SemanticIndex) -> Array[SignalUse] {
let result : Array[SignalUse] = []
for mod in self.syntax_modules {
for item in mod.items {
match item {
@syntax.Assign(assign) => {
collect_expression_uses(
mod.name,
assign.target,
"continuous-target",
result,
)
collect_expression_uses(
mod.name,
assign.value,
"continuous-value",
result,
)
}
@syntax.Always(always) =>
collect_statement_uses(mod.name, always.body, result)
@syntax.Statement(statement) =>
collect_statement_uses(mod.name, statement, result)
@syntax.Instance(instance) =>
for connection in instance.connections {
collect_expression_uses(
mod.name,
connection.expression,
"instance-connection",
result,
)
}
@syntax.Unknown(_) => ()
}
}
}
result
}
///|
pub fn SemanticIndex::uses_of(
self : SemanticIndex,
module_name : String,
signal_name : String,
) -> Array[SignalUse] {
self
.uses()
.filter(fn(usage) {
usage.module_name == module_name && usage.signal_name == signal_name
})
}
///|
pub fn SemanticIndex::consumer_count(
self : SemanticIndex,
module_name : String,
signal_name : String,
) -> Int {
self.uses_of(module_name, signal_name).length()
}
///|
pub fn SemanticIndex::port_direction(
self : SemanticIndex,
module_name : String,
port_name : String,
) -> @syntax.Direction? {
match self.find_module(module_name) {
None => None
Some(mod) => {
let mut result : @syntax.Direction? = None
for port in mod.ports {
if port.name == port_name {
result = Some(port.direction)
}
}
result
}
}
}
///|
pub fn SemanticIndex::connection_count(
self : SemanticIndex,
module_name : String,
) -> Int {
let mut count = 0
for mod in self.modules {
if mod.name == module_name {
for instance in mod.instances {
for syntax_mod in self.syntax_modules {
if syntax_mod.name == module_name {
for item in syntax_mod.items {
match item {
@syntax.Instance(value) =>
if value.instance_name == instance.instance_name {
count += value.connections.length()
}
_ => ()
}
}
}
}
}
}
}
count
}
///|
pub fn SemanticIndex::unresolved_names(self : SemanticIndex) -> Array[String] {
let names : Array[String] = []
for issue in self.issues {
if issue.code == "RTL102" {
if !names.contains(issue.message) {
names.push(issue.message)
}
}
}
names
}
///|
pub fn SemanticIndex::issue_summary(self : SemanticIndex) -> String {
let codes = self.issue_codes()
if codes.length() == 0 {
return "no semantic issues"
}
let builder = StringBuilder()
for index, code in codes {
if index > 0 {
builder.write_string(", ")
}
builder.write_string(code)
builder.write_char('=')
builder.write_object(self.issues_for_code(code).length())
}
builder.to_string()
}