ability to set suggestions on lints (#6535)

* fix the lint tests which were not compiling

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* apply sugggestions for offsetplanes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* diagnostics and suggestions for offset planes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
Jess Frazelle
2025-04-28 12:07:10 -07:00
committed by GitHub
parent 2e754f2a11
commit 94452cce88
10 changed files with 339 additions and 266 deletions

View File

@ -3,7 +3,7 @@ use schemars::JsonSchema;
use serde::Serialize;
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity};
use crate::{lsp::IntoDiagnostic, walk::Node, SourceRange};
use crate::{errors::Suggestion, lsp::IntoDiagnostic, walk::Node, SourceRange};
/// Check the provided AST for any found rule violations.
///
@ -40,6 +40,22 @@ pub struct Discovered {
/// Is this discovered issue overridden by the programmer?
pub overridden: bool,
/// Suggestion to fix the issue.
pub suggestion: Option<Suggestion>,
}
impl Discovered {
#[cfg(test)]
pub fn apply_suggestion(&self, src: &str) -> Option<String> {
let suggestion = self.suggestion.as_ref()?;
Some(format!(
"{}{}{}",
&src[0..suggestion.source_range.start()],
suggestion.insert,
&src[suggestion.source_range.end()..]
))
}
}
#[cfg(feature = "pyo3")]
@ -80,6 +96,7 @@ impl IntoDiagnostic for &Discovered {
fn to_lsp_diagnostics(&self, code: &str) -> Vec<Diagnostic> {
let message = self.finding.title.to_owned();
let source_range = self.pos;
let edit = self.suggestion.as_ref().map(|s| s.to_lsp_edit(code));
vec![Diagnostic {
range: source_range.to_lsp_range(code),
@ -91,7 +108,7 @@ impl IntoDiagnostic for &Discovered {
message,
related_information: None,
tags: None,
data: None,
data: edit.map(|e| serde_json::to_value(e).unwrap()),
}]
}
@ -121,12 +138,13 @@ pub struct Finding {
impl Finding {
/// Create a new Discovered finding at the specific Position.
pub fn at(&self, description: String, pos: SourceRange) -> Discovered {
pub fn at(&self, description: String, pos: SourceRange, suggestion: Option<Suggestion>) -> Discovered {
Discovered {
description,
finding: self.clone(),
pos,
overridden: false,
suggestion,
}
}
}
@ -182,7 +200,11 @@ mod test {
macro_rules! assert_no_finding {
( $check:expr, $finding:expr, $kcl:expr ) => {
let prog = $crate::parsing::top_level_parse($kcl).unwrap();
let prog = $crate::Program::parse_no_errs($kcl).unwrap();
// Ensure the code still works.
$crate::execution::parse_execute($kcl).await.unwrap();
for discovered_finding in prog.lint($check).unwrap() {
if discovered_finding.finding == $finding {
assert!(false, "Finding {:?} was emitted", $finding.code);
@ -192,11 +214,28 @@ mod test {
}
macro_rules! assert_finding {
( $check:expr, $finding:expr, $kcl:expr ) => {
let prog = $crate::parsing::top_level_parse($kcl).unwrap();
( $check:expr, $finding:expr, $kcl:expr, $output:expr, $suggestion:expr ) => {
let prog = $crate::Program::parse_no_errs($kcl).unwrap();
// Ensure the code still works.
$crate::execution::parse_execute($kcl).await.unwrap();
for discovered_finding in prog.lint($check).unwrap() {
pretty_assertions::assert_eq!(discovered_finding.description, $output,);
if discovered_finding.finding == $finding {
pretty_assertions::assert_eq!(
discovered_finding.suggestion.clone().map(|s| s.insert),
$suggestion,
);
if discovered_finding.suggestion.is_some() {
// Apply the suggestion to the source code.
let code = discovered_finding.apply_suggestion($kcl).unwrap();
// Ensure the code still works.
$crate::execution::parse_execute(&code).await.unwrap();
}
return;
}
}
@ -205,18 +244,18 @@ mod test {
}
macro_rules! test_finding {
( $name:ident, $check:expr, $finding:expr, $kcl:expr ) => {
#[test]
fn $name() {
$crate::lint::rule::assert_finding!($check, $finding, $kcl);
( $name:ident, $check:expr, $finding:expr, $kcl:expr, $output:expr, $suggestion:expr ) => {
#[tokio::test]
async fn $name() {
$crate::lint::rule::assert_finding!($check, $finding, $kcl, $output, $suggestion);
}
};
}
macro_rules! test_no_finding {
( $name:ident, $check:expr, $finding:expr, $kcl:expr ) => {
#[test]
fn $name() {
#[tokio::test]
async fn $name() {
$crate::lint::rule::assert_no_finding!($check, $finding, $kcl);
}
};