use crate::julia_version::Version;
use crate::linter::diagnostic::{Diagnostic, Severity};
use crate::linter::rules::{Example, Rule, RuleContext};
use crate::syntax::{SyntaxElement, SyntaxKind};
struct Feature {
kind: SyntaxKind,
introduced: Version,
label: &'static str,
}
const FEATURES: &[Feature] = &[
Feature {
kind: SyntaxKind::PUBLIC_STMT,
introduced: Version::new(1, 11, 0),
label: "the `public` keyword",
},
Feature {
kind: SyntaxKind::IMPORT_ALIAS,
introduced: Version::new(1, 6, 0),
label: "renaming with `as` in `import`/`using`",
},
];
pub struct JuliaVersionCompat;
impl Rule for JuliaVersionCompat {
fn id(&self) -> &'static str {
"julia-version-compat"
}
fn default_severity(&self) -> Severity {
Severity::Error
}
fn description(&self) -> &'static str {
"Flag syntax newer than the project's declared Julia support range. \
Fatou parses the full superset of Julia syntax, so a construct from a \
newer release parses cleanly even when the project targets an older \
version; this rule reports when a supported version predates the \
construct (e.g. `public` needs 1.11, `import ... as` needs 1.6). The \
target range is taken from `--julia-version`, `[julia] version`, or the \
project's `Project.toml` `[compat]` / `Manifest.toml`; with no target \
known the rule stays silent."
}
fn examples(&self) -> &'static [Example] {
&[Example {
caption: "Targeting Julia 1.0, but `public` needs 1.11 and `as` needs 1.6:",
source: "module M\npublic foo\nimport A as B\nend\n",
}]
}
fn example_julia_target(&self) -> Option<crate::julia_version::VersionRange> {
Some(crate::julia_version::VersionRange {
min: Version::new(1, 0, 0),
max: Some(Version::new(2, 0, 0)),
})
}
fn interests(&self) -> &'static [SyntaxKind] {
&[SyntaxKind::PUBLIC_STMT, SyntaxKind::IMPORT_ALIAS]
}
fn check(&self, el: &SyntaxElement, ctx: &RuleContext<'_>, sink: &mut Vec<Diagnostic>) {
let Some(target) = ctx.julia_target else {
return;
};
let Some(node) = el.as_node() else { return };
let kind = node.kind();
let Some(feature) = FEATURES.iter().find(|f| f.kind == kind) else {
return;
};
if target.covers_feature(feature.introduced) {
return;
}
sink.push(Diagnostic::new(
self.id(),
node.text_range(),
format!(
"{} requires Julia {}, but the project supports {} and up",
feature.label, feature.introduced, target.min
),
));
}
}