use std::path::Path;
use crate::project::{ExternalResolution, FileScope};
use crate::rindex::provider::CompositeProvider;
use crate::semantic::{SemanticModel, SymbolProvider};
use crate::syntax::SyntaxNode;
use super::diagnostic::{Diagnostic, Severity};
pub mod correctness;
pub mod suspicious;
pub fn all_rules() -> Vec<Box<dyn Rule>> {
vec![
Box::new(correctness::UndefinedSymbol),
Box::new(correctness::UnusedBinding),
Box::new(correctness::DuplicateFormal),
Box::new(suspicious::AssignmentInCondition),
Box::new(suspicious::ShadowedBuiltin),
]
}
pub const ALL_RULE_IDS: &[&str] = &[
"undefined-symbol",
"unused-binding",
"duplicate-formal",
"assignment-in-condition",
"shadowed-builtin",
];
pub trait Rule: Send + Sync {
fn id(&self) -> &'static str;
fn default_severity(&self) -> Severity {
Severity::Warning
}
fn default_enabled(&self) -> bool {
true
}
fn run(&self, ctx: &RuleContext<'_>) -> Vec<Diagnostic>;
}
pub struct RuleContext<'a> {
pub path: &'a Path,
pub root: &'a SyntaxNode,
pub model: &'a SemanticModel,
pub symbols: &'a dyn SymbolProvider,
pub project: Option<&'a FileScope<'a>>,
pub resolution: Option<&'a ExternalResolution>,
}
pub struct ResolvedRules {
pub rules: Vec<Box<dyn Rule>>,
}
impl ResolvedRules {
pub fn resolve(select: Option<&[String]>, ignore: &[String]) -> (Self, Vec<String>) {
let mut unknown = Vec::new();
for id in select.iter().flat_map(|v| v.iter()).chain(ignore.iter()) {
if !ALL_RULE_IDS.contains(&id.as_str()) {
unknown.push(id.clone());
}
}
let mut chosen: Vec<Box<dyn Rule>> = match select {
Some(picks) => all_rules()
.into_iter()
.filter(|r| picks.iter().any(|p| p == r.id()))
.collect(),
None => all_rules()
.into_iter()
.filter(|r| r.default_enabled())
.collect(),
};
chosen.retain(|r| !ignore.iter().any(|i| i == r.id()));
(Self { rules: chosen }, unknown)
}
pub fn default_set() -> Self {
let (set, _) = Self::resolve(None, &[]);
set
}
}
pub fn run_rules(
rules: &[Box<dyn Rule>],
path: &Path,
root: &SyntaxNode,
model: &SemanticModel,
symbols: &dyn SymbolProvider,
project: Option<&FileScope<'_>>,
resolution: Option<&ExternalResolution>,
) -> Vec<Diagnostic> {
let ctx = RuleContext {
path,
root,
model,
symbols,
project,
resolution,
};
let mut all = Vec::new();
for rule in rules {
all.extend(rule.run(&ctx));
}
all.sort_by(|a, b| {
(u32::from(a.range.start()), u32::from(a.range.end()), a.rule).cmp(&(
u32::from(b.range.start()),
u32::from(b.range.end()),
b.rule,
))
});
all
}
pub fn default_symbol_provider() -> CompositeProvider {
CompositeProvider::base_only()
}