use crate::linter::diagnostic::{Diagnostic, Severity};
use crate::linter::rules::correctness::const_decl;
use crate::linter::rules::{Example, Rule, RuleContext};
use crate::syntax::{SyntaxElement, SyntaxKind};
pub struct LocalConst;
impl Rule for LocalConst {
fn id(&self) -> &'static str {
"local-const"
}
fn default_severity(&self) -> Severity {
Severity::Error
}
fn description(&self) -> &'static str {
"Flag a `const` declaration carrying a `local` modifier, in either \
order. Julia has no `local const` construct: the code parses but \
always fails at lowering with \"expected assignment after `const`\", \
everywhere — the file top level, a `module` or `struct` body, a loop \
or `let`, and inside a function alike. Write `local z = 1` for a \
local binding or `const z = 1` for a constant. A declaration inside \
quoted code or a macro argument is left alone, since it may never be \
lowered as written."
}
fn examples(&self) -> &'static [Example] {
&[
Example {
caption: "`local` and `const` cannot be combined:",
source: "local const LIMIT = 10\n",
},
Example {
caption: "The other order is the same construct, and a function body is no \
different from the top level:",
source: "function scale(x)\n const local factor = 2\n factor * x\nend\n",
},
]
}
fn interests(&self) -> &'static [SyntaxKind] {
&[SyntaxKind::CONST_STMT]
}
fn check(&self, el: &SyntaxElement, _ctx: &RuleContext<'_>, sink: &mut Vec<Diagnostic>) {
let Some(node) = el.as_node() else {
return;
};
let Some(modifier) = const_decl::scope_modifier(node) else {
return;
};
if modifier.kind != SyntaxKind::LOCAL_STMT {
return;
}
if modifier
.outer
.ancestors()
.skip(1)
.any(|a| const_decl::is_unlowered_context(&a))
{
return;
}
sink.push(Diagnostic::new(
self.id(),
modifier.outer.text_range(),
"`local const` declaration is not supported",
));
}
}