mod require_test_for_mutation;
pub use require_test_for_mutation::RequireTestForMutation;
use crate::{
LintSeverity, OpportunityContext, OpportunityId, Suggest, SuggestCategory, SuggestLocation,
SuggestOpportunity,
};
use ryo_analysis::SymbolId;
pub struct LintDetails {
pub suggestion: Option<String>,
pub expected: Option<String>,
pub actual: Option<String>,
}
pub trait LintSuggest: Suggest {
fn code(&self) -> &'static str;
fn default_severity(&self) -> LintSeverity;
fn create_lint_opportunity(
&self,
id: OpportunityId,
targets: Vec<SymbolId>,
location: SuggestLocation,
message: impl Into<String>,
details: LintDetails,
) -> SuggestOpportunity {
SuggestOpportunity::new(
id,
targets,
location,
message,
1.0, OpportunityContext::Lint {
code: self.code().to_string(),
rule: self.name().to_string(),
severity: self.default_severity(),
suggestion: details.suggestion,
expected: details.expected,
actual: details.actual,
},
)
}
}
pub fn is_lint_suggest(suggest: &dyn Suggest) -> bool {
suggest.category() == SuggestCategory::Lint
}
pub fn format_clippy(opp: &SuggestOpportunity) -> String {
match &opp.context {
OpportunityContext::Lint {
code,
severity,
suggestion,
..
} => {
let mut output = format!(
"{} ({}): {} [{}] {}\n",
opp.location.file, opp.location.symbol_path, severity, code, opp.message
);
if let Some(sugg) = suggestion {
output.push_str(&format!(" = help: {}\n", sugg));
}
output
}
_ => format!("{}: {}\n", opp.location, opp.message),
}
}
pub fn format_clippy_all(opportunities: &[SuggestOpportunity]) -> String {
let mut output = String::new();
for opp in opportunities {
output.push_str(&format_clippy(opp));
}
let (errors, warnings, infos) = count_by_severity(opportunities);
output.push_str(&format!(
"\nFound {} error(s), {} warning(s), {} info(s)\n",
errors, warnings, infos
));
output
}
pub fn format_json(opp: &SuggestOpportunity) -> String {
serde_json::to_string(opp).unwrap_or_else(|_| "{}".to_string())
}
pub fn format_json_all(opportunities: &[SuggestOpportunity]) -> String {
serde_json::to_string_pretty(opportunities).unwrap_or_else(|_| "[]".to_string())
}
pub fn count_by_severity(opportunities: &[SuggestOpportunity]) -> (usize, usize, usize) {
let mut errors = 0;
let mut warnings = 0;
let mut infos = 0;
for opp in opportunities {
if let OpportunityContext::Lint { severity, .. } = &opp.context {
match severity {
LintSeverity::Error => errors += 1,
LintSeverity::Warning => warnings += 1,
LintSeverity::Info => infos += 1,
}
}
}
(errors, warnings, infos)
}
pub fn has_errors(opportunities: &[SuggestOpportunity]) -> bool {
opportunities.iter().any(|opp| {
matches!(
&opp.context,
OpportunityContext::Lint {
severity: LintSeverity::Error,
..
}
)
})
}
#[derive(Debug, Clone, Default)]
pub struct LintResult {
pub violations: Vec<SuggestOpportunity>,
pub files_checked: usize,
}
impl LintResult {
pub fn new() -> Self {
Self::default()
}
pub fn has_errors(&self) -> bool {
has_errors(&self.violations)
}
pub fn count_by_severity(&self) -> (usize, usize, usize) {
count_by_severity(&self.violations)
}
pub fn format_clippy(&self) -> String {
format_clippy_all(&self.violations)
}
pub fn format_json(&self) -> String {
format_json_all(&self.violations)
}
}