///|
fn severity(
config : LintConfig,
default : @diagnostic.Severity,
) -> @diagnostic.Severity {
if config.warnings_as_errors && default is @diagnostic.Warning {
@diagnostic.Error
} else {
default
}
}
///|
fn make_diagnostic(
config : LintConfig,
code : String,
default : @diagnostic.Severity,
message : String,
span : @source.Span,
) -> @diagnostic.Diagnostic? {
if config.enabled(code) {
Some(
@diagnostic.Diagnostic::new(
code,
severity(config, default),
message,
span,
),
)
} else {
None
}
}
///|
fn diagnostics_for_signal(
config : LintConfig,
mod : @semantic.ModuleInfo,
signal : @semantic.SignalInfo,
) -> Array[@diagnostic.Diagnostic] {
let diagnostics : Array[@diagnostic.Diagnostic] = []
if !signal.from_port && !signal.has_driver() {
match
make_diagnostic(
config,
"RTL201",
@diagnostic.Warning,
"signal is never driven",
signal.declaration_span,
) {
Some(value) => diagnostics.push(value)
None => ()
}
}
if signal.drivers.length() > 1 {
match
make_diagnostic(
config,
"RTL202",
@diagnostic.Error,
"signal has multiple drivers",
signal.declaration_span,
) {
Some(value) => diagnostics.push(value)
None => ()
}
}
if signal.from_port &&
mod.ports.any(fn(port) {
port.name == signal.name && port.direction is @syntax.Output
}) &&
!signal.has_driver() {
match
make_diagnostic(
config,
"RTL203",
@diagnostic.Warning,
"output port is never assigned",
signal.declaration_span,
) {
Some(value) => diagnostics.push(value)
None => ()
}
}
diagnostics
}
///|
fn diagnostics_for_issue(
config : LintConfig,
issue : @semantic.SemanticIssue,
) -> @diagnostic.Diagnostic? {
let default = if issue.code == "RTL102" {
@diagnostic.Error
} else {
@diagnostic.Warning
}
make_diagnostic(config, issue.code, default, issue.message, issue.span)
}
///|
fn diagnostics_for_contract(
config : LintConfig,
contract : @semantic.PortContract,
) -> @diagnostic.Diagnostic? {
match contract.status {
@semantic.WidthMismatch =>
make_diagnostic(
config,
"RTL205",
@diagnostic.Error,
"instance port width does not match the connected expression",
contract.span,
)
@semantic.UnknownModule | @semantic.MissingPort =>
make_diagnostic(
config,
"RTL206",
@diagnostic.Warning,
"instance port contract cannot be resolved",
contract.span,
)
@semantic.DirectionMismatch =>
make_diagnostic(
config,
"RTL205",
@diagnostic.Error,
"instance port direction does not match the connection",
contract.span,
)
@semantic.Match | @semantic.UnknownWidth => None
}
}
///|
fn diagnostics_for_control_flow(
config : LintConfig,
index : @semantic.SemanticIndex,
module_name : String,
) -> Array[@diagnostic.Diagnostic] {
let diagnostics : Array[@diagnostic.Diagnostic] = []
match index.control_flow(module_name) {
None => ()
Some(summary) =>
for name in summary.conditionally_assigned {
match index.find_module(module_name) {
Some(mod) =>
match mod.signal(name) {
Some(signal) =>
match
make_diagnostic(
config,
"RTL207",
@diagnostic.Info,
"procedural assignment is conditional on control flow",
signal.declaration_span,
) {
Some(value) => diagnostics.push(value)
None => ()
}
None => ()
}
None => ()
}
}
}
diagnostics
}
///|
pub fn default_rules() -> Array[Rule] {
[
UndrivenSignal,
MultipleDrivers,
UnresolvedInstance,
DuplicateDeclaration,
OutputNotAssigned,
WidthMismatch,
ContractWidthMismatch,
UnknownContract,
ConditionalAssignment,
]
}
///|
pub fn run(
index : @semantic.SemanticIndex,
config : LintConfig,
) -> Array[@diagnostic.Diagnostic] {
let diagnostics : Array[@diagnostic.Diagnostic] = []
for issue in index.issues {
match diagnostics_for_issue(config, issue) {
Some(value) => diagnostics.push(value)
None => ()
}
}
for mod in index.modules {
for signal in mod.signals {
for diagnostic in diagnostics_for_signal(config, mod, signal) {
diagnostics.push(diagnostic)
}
}
for diagnostic in diagnostics_for_control_flow(config, index, mod.name) {
diagnostics.push(diagnostic)
}
}
for contract in index.port_contracts() {
match diagnostics_for_contract(config, contract) {
Some(value) => diagnostics.push(value)
None => ()
}
}
diagnostics
}