diff --git a/CHANGELOG.md b/CHANGELOG.md index cff1241e..20904890 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Added `bit32.byteswap` to Luau standard library - Added `buffer` library to Luau standard library - Added `SharedTable` to Roblox standard library +- Added new [`--fix`](https://kampfkarren.github.io/selene/cli/usage.html#fix) flag, which will automatically apply lint suggestions. ### Changed - Updated internal parser, which includes floor division (`//`), more correct parsing of string interpolation with double braces, and better parsing of `\z` escapes. diff --git a/Cargo.lock b/Cargo.lock index 93740fe7..0debea82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -992,6 +992,7 @@ dependencies = [ "serde", "serde_json", "serde_yaml", + "similar", "termcolor", "toml", ] @@ -1064,6 +1065,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "similar" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aeaf503862c419d66959f5d7ca015337d864e9c49485d771b732e2a20453597" + [[package]] name = "smallvec" version = "1.10.0" diff --git a/Cargo.toml b/Cargo.toml index 383ee1ae..6e20337b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,18 @@ -[workspace] -members = ["selene", "selene-lib"] -resolver = "2" - -[workspace.package] -version = "0.25.0" -authors = ["Kampfkarren "] -edition = "2021" -homepage = "https://kampfkarren.github.io/selene/" -license = "MPL-2.0" -repository = "https://github.com/Kampfkarren/selene" - -[workspace.dependencies] -full_moon = "0.19.0" -toml = "0.7.2" - -# Do not update this without confirming profiling uses the same version of tracy-client as selene -profiling = "1.0.7" +[workspace] +members = ["selene", "selene-lib"] +resolver = "2" + +[workspace.package] +version = "0.25.0" +authors = ["Kampfkarren "] +edition = "2021" +homepage = "https://kampfkarren.github.io/selene/" +license = "MPL-2.0" +repository = "https://github.com/Kampfkarren/selene" + +[workspace.dependencies] +full_moon = "0.19.0" +toml = "0.7.2" + +# Do not update this without confirming profiling uses the same version of tracy-client as selene +profiling = "1.0.7" diff --git a/docs/src/cli/usage.md b/docs/src/cli/usage.md index 25eb5855..770f0325 100644 --- a/docs/src/cli/usage.md +++ b/docs/src/cli/usage.md @@ -9,6 +9,7 @@ USAGE: FLAGS: --allow-warnings Pass when only warnings occur --no-exclude Ignore excludes defined in config + --fix Automatically fix applicable lint warnings -h, --help Prints help information -n, --no-summary Suppress summary information -q, --quiet Display only the necessary information. Equivalent to --display-style="quiet" @@ -76,6 +77,10 @@ Results: 0 parse errors ``` +**--fix** *fix* + +Automatically applies lint suggestions. Since this can be potentially destructive, it's not allowed when there are uncommitted changes. This safety mechanism can be ignored by including `--allow-dirty` to allow unstaged changes or `--allow-staged` to allow only staged changes. + **--num-threads** *num-threads* Specifies the number of threads for selene to use. Defaults to however many cores your CPU has. If you type `selene --help`, you can see this number because it will show as the default for you. diff --git a/selene-lib/Cargo.toml b/selene-lib/Cargo.toml index 96f8ce6e..9811f638 100644 --- a/selene-lib/Cargo.toml +++ b/selene-lib/Cargo.toml @@ -22,6 +22,7 @@ profiling.workspace = true regex = "1.7.1" serde = "1.0.152" serde_yaml = "0.9.16" +similar = "2.3.0" toml.workspace = true [dev-dependencies] diff --git a/selene-lib/src/lib.rs b/selene-lib/src/lib.rs index 77bb09c3..56948be8 100644 --- a/selene-lib/src/lib.rs +++ b/selene-lib/src/lib.rs @@ -229,10 +229,10 @@ macro_rules! use_lints { }) } - pub fn test_on(&self, ast: &Ast) -> Vec { + pub fn test_on(&self, ast: &Ast, code: &String) -> Vec { let mut diagnostics = Vec::new(); - let ast_context = AstContext::from_ast(ast); + let ast_context = AstContext::from_ast(ast, &code.to_string()); macro_rules! check_lint { ($name:ident) => { diff --git a/selene-lib/src/lint_filtering.rs b/selene-lib/src/lint_filtering.rs index 447437fa..e814e9d3 100644 --- a/selene-lib/src/lint_filtering.rs +++ b/selene-lib/src/lint_filtering.rs @@ -4,7 +4,7 @@ use crate::{ visit_nodes::{NodeVisitor, VisitorType}, }, lint_exists, - lints::{Diagnostic, Label, Severity}, + lints::{Applicability, Diagnostic, Label, Severity}, CheckerDiagnostic, LintVariation, }; use full_moon::{ast::Ast, node::Node, tokenizer::TokenType}; @@ -149,6 +149,8 @@ impl NodeVisitor for FilterVisitor { trivia_start_position.bytes(), trivia_end_position.bytes(), )), + None, + Applicability::Unspecified, )) } })); @@ -223,6 +225,8 @@ pub fn filter_diagnostics( (first_code.0.bytes(), first_code.1.bytes()), "global filter must be before this".to_owned(), )], + None, + Applicability::Unspecified, )); continue; @@ -244,6 +248,8 @@ pub fn filter_diagnostics( possibly_conflicting.comment_range, "conflicts with this".to_owned(), )], + None, + Applicability::Unspecified, )); } } diff --git a/selene-lib/src/lints.rs b/selene-lib/src/lints.rs index fa42553c..78e951a9 100644 --- a/selene-lib/src/lints.rs +++ b/selene-lib/src/lints.rs @@ -89,6 +89,27 @@ pub enum Severity { Warning, } +/// Indicates the confidence in the correctness of a suggestion +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Applicability { + /// The suggestion is definitely what the user intended, or maintains the exact meaning of the code + /// + /// The suggestion is safe to be automatically applied + MachineApplicable, + + /// The suggestion is probably what the user intended, but may not maintain the exact meaning of the code + /// + /// The suggestion generates valid code when applied + MaybeIncorrect, + + /// The suggestion is probably what the user intended, but may not maintain the exact meaning of the code + /// + /// The suggestion does not generate valid code when applied + HasPlaceholders, + + Unspecified, +} + #[derive(Debug)] pub struct Diagnostic { pub code: &'static str, @@ -96,14 +117,38 @@ pub struct Diagnostic { pub notes: Vec, pub primary_label: Label, pub secondary_labels: Vec