use lsp_types::DiagnosticTag;
use nu_protocol::ast::{Expr, Expression};
use crate::{
ast::call::CallExt,
config::LintLevel,
context::LintContext,
rule::{DetectFix, Rule},
violation::Detection,
};
struct UseOverSource;
impl DetectFix for UseOverSource {
type FixInput<'a> = ();
fn id(&self) -> &'static str {
"source_to_use"
}
fn short_description(&self) -> &'static str {
"`source` replaceable with `use`"
}
fn long_description(&self) -> Option<&'static str> {
Some(
"The 'use' command imports exported module definitions with namespace control and \
selective imports. The 'source' command executes scripts directly, causing namespace \
pollution. Only use 'source' for scripts with side effects, not for importing \
definitions.",
)
}
fn source_link(&self) -> Option<&'static str> {
Some("https://www.nushell.sh/book/modules/using_modules.html")
}
fn level(&self) -> LintLevel {
LintLevel::Hint
}
fn diagnostic_tags(&self) -> &'static [DiagnosticTag] {
&[DiagnosticTag::DEPRECATED]
}
fn detect<'a>(&self, context: &'a LintContext) -> Vec<(Detection, Self::FixInput<'a>)> {
let detections = context.detect(|expr, ctx| {
if Self::is_source_command(expr, ctx) {
vec![Self::create_detection(expr)]
} else {
vec![]
}
});
Self::no_fix(detections)
}
}
impl UseOverSource {
fn is_source_command(expr: &Expression, ctx: &LintContext) -> bool {
let Expr::Call(call) = &expr.expr else {
return false;
};
call.get_call_name(ctx) == "source"
}
fn create_detection(expr: &Expression) -> Detection {
Detection::from_global_span(
"Use 'use' instead of 'source' for importing modules",
expr.span,
)
.with_primary_label("'source' command used here")
}
}
pub static RULE: &dyn Rule = &UseOverSource;
#[cfg(test)]
mod detect_bad;
#[cfg(test)]
mod ignore_good;