From 58ec78859cf2016ec326f4acc00f8b45c7f66787 Mon Sep 17 00:00:00 2001 From: James Onnen Date: Wed, 22 May 2024 12:20:52 -0700 Subject: [PATCH 1/4] feat: Support linting complex udim2 constructors that can be simplified This is a common PR comment and one that reduces readability. A selene lint to check for this usage will speed up development. --- docs/src/lints/roblox_complex_udim2_new.md | 14 ++ selene-lib/src/lib.rs | 1 + selene-lib/src/lints.rs | 3 + .../src/lints/roblox_complex_udim2_new.rs | 176 ++++++++++++++++++ .../roblox_complex_udim2_new.lua | 23 +++ .../roblox_complex_udim2_new.std.toml | 2 + .../roblox_complex_udim2_new.stderr | 40 ++++ 7 files changed, 259 insertions(+) create mode 100644 docs/src/lints/roblox_complex_udim2_new.md create mode 100644 selene-lib/src/lints/roblox_complex_udim2_new.rs create mode 100644 selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.lua create mode 100644 selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.std.toml create mode 100644 selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.stderr diff --git a/docs/src/lints/roblox_complex_udim2_new.md b/docs/src/lints/roblox_complex_udim2_new.md new file mode 100644 index 00000000..839584b7 --- /dev/null +++ b/docs/src/lints/roblox_complex_udim2_new.md @@ -0,0 +1,14 @@ +# roblox_complex_udim2_new +## What it does +Checks for uses of `UDim2.new` where the arguments could be simplified to `UDim2.fromScale` or `UDim2.fromOffset`. + +## Why this is bad +This reduces readability of `UDim2.new()` construction. + +## Example +```lua +UDim2.new(1, 0, 1, 0) +``` + +## Remarks +This lint is only active if you are using the Roblox standard library. diff --git a/selene-lib/src/lib.rs b/selene-lib/src/lib.rs index 77bb09c3..6cfbe81d 100644 --- a/selene-lib/src/lib.rs +++ b/selene-lib/src/lib.rs @@ -326,6 +326,7 @@ use_lints! { #[cfg(feature = "roblox")] { + roblox_complex_udim2_new: lints::roblox_complex_udim2_new::ComplexUDim2NewLint, roblox_incorrect_color3_new_bounds: lints::roblox_incorrect_color3_new_bounds::Color3BoundsLint, roblox_incorrect_roact_usage: lints::roblox_incorrect_roact_usage::IncorrectRoactUsageLint, roblox_suspicious_udim2_new: lints::roblox_suspicious_udim2_new::SuspiciousUDim2NewLint, diff --git a/selene-lib/src/lints.rs b/selene-lib/src/lints.rs index fa42553c..b6c00d26 100644 --- a/selene-lib/src/lints.rs +++ b/selene-lib/src/lints.rs @@ -36,6 +36,9 @@ pub mod undefined_variable; pub mod unscoped_variables; pub mod unused_variable; +#[cfg(feature = "roblox")] +pub mod roblox_complex_udim2_new; + #[cfg(feature = "roblox")] pub mod roblox_incorrect_color3_new_bounds; diff --git a/selene-lib/src/lints/roblox_complex_udim2_new.rs b/selene-lib/src/lints/roblox_complex_udim2_new.rs new file mode 100644 index 00000000..9b5ebf12 --- /dev/null +++ b/selene-lib/src/lints/roblox_complex_udim2_new.rs @@ -0,0 +1,176 @@ +use super::*; +use crate::ast_util::range; +use std::convert::Infallible; + +use full_moon::{ + ast::{self, Ast}, + visitors::Visitor, +}; + +pub struct ComplexUDim2NewLint; + +fn create_diagnostic(mismatch: &UDim2ComplexArgs) -> Diagnostic { + let code = "roblox_complex_udim2_new"; + let primary_label = Label::new(mismatch.call_range); + + if mismatch.no_scale { + Diagnostic::new_complete( + code, + "this UDim2.new call only sets offset, and can be simplified using UDim2.fromOffset" + .to_owned(), + primary_label, + vec![format!( + "try: UDim2.fromOffset({}, {})", + mismatch.arg_0, mismatch.arg_1 + )], + Vec::new(), + ) + } else { + Diagnostic::new_complete( + code, + "this UDim2.new call only sets scale, and can be simplified using UDim2.fromScale" + .to_owned(), + primary_label, + vec![format!( + "try: UDim2.fromScale({}, {})", + mismatch.arg_0, mismatch.arg_1 + )], + Vec::new(), + ) + } +} + +impl Lint for ComplexUDim2NewLint { + type Config = (); + type Error = Infallible; + + const SEVERITY: Severity = Severity::Warning; + const LINT_TYPE: LintType = LintType::Style; + + fn new(_: Self::Config) -> Result { + Ok(ComplexUDim2NewLint) + } + + fn pass(&self, ast: &Ast, context: &Context, _: &AstContext) -> Vec { + if !context.is_roblox() { + return Vec::new(); + } + + let mut visitor = UDim2NewVisitor::default(); + + visitor.visit_ast(ast); + + visitor.args.iter().map(create_diagnostic).collect() + } +} + +#[derive(Default)] +struct UDim2NewVisitor { + args: Vec, +} + +struct UDim2ComplexArgs { + call_range: (usize, usize), + arg_0: f32, + arg_1: f32, + no_scale: bool, +} + +impl Visitor for UDim2NewVisitor { + fn visit_function_call(&mut self, call: &ast::FunctionCall) { + if_chain::if_chain! { + if let ast::Prefix::Name(token) = call.prefix(); + if token.token().to_string() == "UDim2"; + let mut suffixes = call.suffixes().collect::>(); + + if suffixes.len() == 2; // .new and () + let call_suffix = suffixes.pop().unwrap(); + let index_suffix = suffixes.pop().unwrap(); + + if let ast::Suffix::Index(ast::Index::Dot { name, .. }) = index_suffix; + if name.token().to_string() == "new"; + + if let ast::Suffix::Call(ast::Call::AnonymousCall( + ast::FunctionArgs::Parentheses { arguments, .. } + )) = call_suffix; + + then { + let args_provided = arguments.len(); + if args_provided != 4 { + return; + } + + + let numbers_passed = arguments.iter().filter(|expression| { + matches!(expression, ast::Expression::Number(_)) + }).count(); + if numbers_passed != 4 { + return; + } + + let mut iter = arguments.iter(); + let x_scale = match iter.next().unwrap().to_string().parse::() { + Ok(value) => value, + Err(_) => return, + }; + let x_offset = match iter.next().unwrap().to_string().parse::() { + Ok(value) => value, + Err(_) => return, + }; + let y_scale = match iter.next().unwrap().to_string().parse::() { + Ok(value) => value, + Err(_) => return, + }; + let y_offset = match iter.next().unwrap().to_string().parse::() { + Ok(value) => value, + Err(_) => return, + }; + + let no_scale = x_scale == 0.0 && y_scale == 0.0; + let no_offset = x_offset == 0.0 && y_offset == 0.0; + + let arg_0; + let arg_1; + if no_scale && no_offset + { + // Skip any lint + return; + } + else if no_scale + { + arg_0 = x_offset; + arg_1 = y_offset; + } + else if no_offset { + arg_0 = x_scale; + arg_1 = y_scale; + } + else + { + return; + } + + self.args.push(UDim2ComplexArgs { + call_range: range(call), + arg_0: arg_0, + arg_1: arg_1, + no_scale: no_scale, + }); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::{super::test_util::test_lint, *}; + + #[test] + fn test_roblox_complex_udim2_new() { + test_lint( + ComplexUDim2NewLint::new(()).unwrap(), + "roblox_complex_udim2_new", + "roblox_complex_udim2_new", + ); + } +} diff --git a/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.lua b/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.lua new file mode 100644 index 00000000..cdbcbcae --- /dev/null +++ b/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.lua @@ -0,0 +1,23 @@ +UDim2.new(0) +UDim2.new(0.5) +UDim2.new(1) +UDim2.new(2) +UDim2.new(a) +UDim2.new(1, 1) +UDim2.new(1, 2) +UDim2.new(a, b) +UDim2.new(1, a) +UDim2.new(1, 1, 1) +UDim2.new(a, b, c) +UDim2.new(1, 1, 1, 1) +UDim2.new(a, b, c, d) +UDim2.fromOffset(1, 1) +UDim2.fromScale(1, 1) +UDim2.new() +UDim2.new(1, 0, 1, 0) +UDim2.new(0, 0, 1, 0) +UDim2.new(0, 5, 1, 5) +UDim2.new(0, 5, 0, 1) +UDim2.new(0, 0, 0, 1) +UDim2.new(0, 1, 0, 1) +UDim2.new(0, a, 0, 1) diff --git a/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.std.toml b/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.std.toml new file mode 100644 index 00000000..98886350 --- /dev/null +++ b/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.std.toml @@ -0,0 +1,2 @@ +[selene] +name = "roblox" diff --git a/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.stderr b/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.stderr new file mode 100644 index 00000000..07cca377 --- /dev/null +++ b/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.stderr @@ -0,0 +1,40 @@ +error[roblox_complex_udim2_new]: this UDim2.new call only sets scale, and can be simplified using UDim2.fromScale + ┌─ roblox_complex_udim2_new.lua:17:1 + │ +17 │ UDim2.new(1, 0, 1, 0) + │ ^^^^^^^^^^^^^^^^^^^^^ + │ + = try: UDim2.fromScale(1, 1) + +error[roblox_complex_udim2_new]: this UDim2.new call only sets scale, and can be simplified using UDim2.fromScale + ┌─ roblox_complex_udim2_new.lua:18:1 + │ +18 │ UDim2.new(0, 0, 1, 0) + │ ^^^^^^^^^^^^^^^^^^^^^ + │ + = try: UDim2.fromScale(0, 1) + +error[roblox_complex_udim2_new]: this UDim2.new call only sets offset, and can be simplified using UDim2.fromOffset + ┌─ roblox_complex_udim2_new.lua:20:1 + │ +20 │ UDim2.new(0, 5, 0, 1) + │ ^^^^^^^^^^^^^^^^^^^^^ + │ + = try: UDim2.fromOffset(5, 1) + +error[roblox_complex_udim2_new]: this UDim2.new call only sets offset, and can be simplified using UDim2.fromOffset + ┌─ roblox_complex_udim2_new.lua:21:1 + │ +21 │ UDim2.new(0, 0, 0, 1) + │ ^^^^^^^^^^^^^^^^^^^^^ + │ + = try: UDim2.fromOffset(0, 1) + +error[roblox_complex_udim2_new]: this UDim2.new call only sets offset, and can be simplified using UDim2.fromOffset + ┌─ roblox_complex_udim2_new.lua:22:1 + │ +22 │ UDim2.new(0, 1, 0, 1) + │ ^^^^^^^^^^^^^^^^^^^^^ + │ + = try: UDim2.fromOffset(1, 1) + From 8e9d51601b6e9fcc752b892d40df1a62dd66b70d Mon Sep 17 00:00:00 2001 From: James Onnen Date: Mon, 3 Jun 2024 14:05:50 -0700 Subject: [PATCH 2/4] fix: Address PR comments and refactor code to support UDim2.new(a, 0, b, 0) --- .../src/lints/roblox_complex_udim2_new.rs | 104 +++++++----------- .../roblox_complex_udim2_new.lua | 4 + .../roblox_complex_udim2_new.stderr | 32 ++++++ 3 files changed, 78 insertions(+), 62 deletions(-) diff --git a/selene-lib/src/lints/roblox_complex_udim2_new.rs b/selene-lib/src/lints/roblox_complex_udim2_new.rs index 9b5ebf12..679459c9 100644 --- a/selene-lib/src/lints/roblox_complex_udim2_new.rs +++ b/selene-lib/src/lints/roblox_complex_udim2_new.rs @@ -9,34 +9,33 @@ use full_moon::{ pub struct ComplexUDim2NewLint; -fn create_diagnostic(mismatch: &UDim2ComplexArgs) -> Diagnostic { +fn create_diagnostic(args: &UDim2ComplexArgs) -> Diagnostic { let code = "roblox_complex_udim2_new"; - let primary_label = Label::new(mismatch.call_range); + let primary_label = Label::new(args.call_range); - if mismatch.no_scale { - Diagnostic::new_complete( + match args.complexity_type { + UDim2ConstructorType::OffsetOnly => Diagnostic::new_complete( code, "this UDim2.new call only sets offset, and can be simplified using UDim2.fromOffset" .to_owned(), primary_label, vec![format!( "try: UDim2.fromOffset({}, {})", - mismatch.arg_0, mismatch.arg_1 + args.arg_0, args.arg_1 )], Vec::new(), - ) - } else { - Diagnostic::new_complete( + ), + UDim2ConstructorType::ScaleOnly => Diagnostic::new_complete( code, "this UDim2.new call only sets scale, and can be simplified using UDim2.fromScale" .to_owned(), primary_label, vec![format!( "try: UDim2.fromScale({}, {})", - mismatch.arg_0, mismatch.arg_1 + args.arg_0, args.arg_1 )], Vec::new(), - ) + ), } } @@ -69,11 +68,17 @@ struct UDim2NewVisitor { args: Vec, } +#[derive(PartialEq)] +enum UDim2ConstructorType { + ScaleOnly, + OffsetOnly, +} + struct UDim2ComplexArgs { + complexity_type: UDim2ConstructorType, call_range: (usize, usize), - arg_0: f32, - arg_1: f32, - no_scale: bool, + arg_0: String, + arg_1: String, } impl Visitor for UDim2NewVisitor { @@ -95,67 +100,42 @@ impl Visitor for UDim2NewVisitor { )) = call_suffix; then { - let args_provided = arguments.len(); - if args_provided != 4 { + if arguments.len() != 4 { return; } + let mut iter = arguments.iter(); + let x_scale = iter.next().unwrap().to_string(); + let x_offset = iter.next().unwrap().to_string(); + let y_scale = iter.next().unwrap().to_string(); + let y_offset = iter.next().unwrap().to_string(); - let numbers_passed = arguments.iter().filter(|expression| { - matches!(expression, ast::Expression::Number(_)) - }).count(); - if numbers_passed != 4 { - return; - } + let only_offset = x_scale.parse::() == Ok(0.0) && y_scale.parse::() == Ok(0.0); + let only_scale = x_offset.parse::() == Ok(0.0) && y_offset.parse::() == Ok(0.0); - let mut iter = arguments.iter(); - let x_scale = match iter.next().unwrap().to_string().parse::() { - Ok(value) => value, - Err(_) => return, - }; - let x_offset = match iter.next().unwrap().to_string().parse::() { - Ok(value) => value, - Err(_) => return, - }; - let y_scale = match iter.next().unwrap().to_string().parse::() { - Ok(value) => value, - Err(_) => return, - }; - let y_offset = match iter.next().unwrap().to_string().parse::() { - Ok(value) => value, - Err(_) => return, - }; - - let no_scale = x_scale == 0.0 && y_scale == 0.0; - let no_offset = x_offset == 0.0 && y_offset == 0.0; - - let arg_0; - let arg_1; - if no_scale && no_offset + if only_offset && only_scale { - // Skip any lint + // Skip linting if all are zero return; } - else if no_scale + else if only_offset { - arg_0 = x_offset; - arg_1 = y_offset; - } - else if no_offset { - arg_0 = x_scale; - arg_1 = y_scale; + self.args.push(UDim2ComplexArgs { + call_range: range(call), + arg_0: x_offset, + arg_1: y_offset, + complexity_type: UDim2ConstructorType::OffsetOnly, + }); } - else + else if only_scale { - return; + self.args.push(UDim2ComplexArgs { + call_range: range(call), + arg_0: x_scale, + arg_1: y_scale, + complexity_type: UDim2ConstructorType::ScaleOnly, + }); } - - self.args.push(UDim2ComplexArgs { - call_range: range(call), - arg_0: arg_0, - arg_1: arg_1, - no_scale: no_scale, - }); } } } diff --git a/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.lua b/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.lua index cdbcbcae..0dc4db84 100644 --- a/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.lua +++ b/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.lua @@ -21,3 +21,7 @@ UDim2.new(0, 5, 0, 1) UDim2.new(0, 0, 0, 1) UDim2.new(0, 1, 0, 1) UDim2.new(0, a, 0, 1) +UDim2.new(0, a, 0, b) +UDim2.new(1, a, 0, b) +UDim2.new(a, 0, 0, 0) +UDim2.new(a, 0.0, 0, 0.0) diff --git a/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.stderr b/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.stderr index 07cca377..287a36ad 100644 --- a/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.stderr +++ b/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.stderr @@ -38,3 +38,35 @@ error[roblox_complex_udim2_new]: this UDim2.new call only sets offset, and can b │ = try: UDim2.fromOffset(1, 1) +error[roblox_complex_udim2_new]: this UDim2.new call only sets offset, and can be simplified using UDim2.fromOffset + ┌─ roblox_complex_udim2_new.lua:23:1 + │ +23 │ UDim2.new(0, a, 0, 1) + │ ^^^^^^^^^^^^^^^^^^^^^ + │ + = try: UDim2.fromOffset(a, 1) + +error[roblox_complex_udim2_new]: this UDim2.new call only sets offset, and can be simplified using UDim2.fromOffset + ┌─ roblox_complex_udim2_new.lua:24:1 + │ +24 │ UDim2.new(0, a, 0, b) + │ ^^^^^^^^^^^^^^^^^^^^^ + │ + = try: UDim2.fromOffset(a, b) + +error[roblox_complex_udim2_new]: this UDim2.new call only sets scale, and can be simplified using UDim2.fromScale + ┌─ roblox_complex_udim2_new.lua:26:1 + │ +26 │ UDim2.new(a, 0, 0, 0) + │ ^^^^^^^^^^^^^^^^^^^^^ + │ + = try: UDim2.fromScale(a, 0) + +error[roblox_complex_udim2_new]: this UDim2.new call only sets scale, and can be simplified using UDim2.fromScale + ┌─ roblox_complex_udim2_new.lua:27:1 + │ +27 │ UDim2.new(a, 0.0, 0, 0.0) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = try: UDim2.fromScale(a, 0) + From 1ba6d91e9764e401a961451ca7a42c4b026c35da Mon Sep 17 00:00:00 2001 From: James Onnen Date: Tue, 13 Aug 2024 13:07:42 -0700 Subject: [PATCH 3/4] refactor: Rename to ManualFromScaleOrFromOffsetLint --- ... roblox_manual_fromscale_or_fromoffset.md} | 2 +- selene-lib/src/lib.rs | 2 +- selene-lib/src/lints.rs | 2 +- ... roblox_manual_fromscale_or_fromoffset.rs} | 16 ++--- .../roblox_complex_udim2_new.stderr | 72 ------------------- ...roblox_manual_fromscale_or_fromoffset.lua} | 0 ...x_manual_fromscale_or_fromoffset.std.toml} | 0 ...blox_manual_fromscale_or_fromoffset.stderr | 72 +++++++++++++++++++ 8 files changed, 83 insertions(+), 83 deletions(-) rename docs/src/lints/{roblox_complex_udim2_new.md => roblox_manual_fromscale_or_fromoffset.md} (89%) rename selene-lib/src/lints/{roblox_complex_udim2_new.rs => roblox_manual_fromscale_or_fromoffset.rs} (91%) delete mode 100644 selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.stderr rename selene-lib/tests/lints/{roblox_complex_udim2_new/roblox_complex_udim2_new.lua => roblox_manual_fromscale_or_fromoffset/roblox_manual_fromscale_or_fromoffset.lua} (100%) rename selene-lib/tests/lints/{roblox_complex_udim2_new/roblox_complex_udim2_new.std.toml => roblox_manual_fromscale_or_fromoffset/roblox_manual_fromscale_or_fromoffset.std.toml} (100%) create mode 100644 selene-lib/tests/lints/roblox_manual_fromscale_or_fromoffset/roblox_manual_fromscale_or_fromoffset.stderr diff --git a/docs/src/lints/roblox_complex_udim2_new.md b/docs/src/lints/roblox_manual_fromscale_or_fromoffset.md similarity index 89% rename from docs/src/lints/roblox_complex_udim2_new.md rename to docs/src/lints/roblox_manual_fromscale_or_fromoffset.md index 839584b7..38dce0c5 100644 --- a/docs/src/lints/roblox_complex_udim2_new.md +++ b/docs/src/lints/roblox_manual_fromscale_or_fromoffset.md @@ -1,4 +1,4 @@ -# roblox_complex_udim2_new +# roblox_manual_fromscale_or_fromoffset ## What it does Checks for uses of `UDim2.new` where the arguments could be simplified to `UDim2.fromScale` or `UDim2.fromOffset`. diff --git a/selene-lib/src/lib.rs b/selene-lib/src/lib.rs index 6cfbe81d..8c743f96 100644 --- a/selene-lib/src/lib.rs +++ b/selene-lib/src/lib.rs @@ -326,7 +326,7 @@ use_lints! { #[cfg(feature = "roblox")] { - roblox_complex_udim2_new: lints::roblox_complex_udim2_new::ComplexUDim2NewLint, + roblox_manual_fromscale_or_fromoffset: lints::roblox_manual_fromscale_or_fromoffset::ManualFromScaleOrFromOffsetLint, roblox_incorrect_color3_new_bounds: lints::roblox_incorrect_color3_new_bounds::Color3BoundsLint, roblox_incorrect_roact_usage: lints::roblox_incorrect_roact_usage::IncorrectRoactUsageLint, roblox_suspicious_udim2_new: lints::roblox_suspicious_udim2_new::SuspiciousUDim2NewLint, diff --git a/selene-lib/src/lints.rs b/selene-lib/src/lints.rs index b6c00d26..4d6b5bb2 100644 --- a/selene-lib/src/lints.rs +++ b/selene-lib/src/lints.rs @@ -37,7 +37,7 @@ pub mod unscoped_variables; pub mod unused_variable; #[cfg(feature = "roblox")] -pub mod roblox_complex_udim2_new; +pub mod roblox_manual_fromscale_or_fromoffset; #[cfg(feature = "roblox")] pub mod roblox_incorrect_color3_new_bounds; diff --git a/selene-lib/src/lints/roblox_complex_udim2_new.rs b/selene-lib/src/lints/roblox_manual_fromscale_or_fromoffset.rs similarity index 91% rename from selene-lib/src/lints/roblox_complex_udim2_new.rs rename to selene-lib/src/lints/roblox_manual_fromscale_or_fromoffset.rs index 679459c9..b9fb8cfa 100644 --- a/selene-lib/src/lints/roblox_complex_udim2_new.rs +++ b/selene-lib/src/lints/roblox_manual_fromscale_or_fromoffset.rs @@ -7,10 +7,10 @@ use full_moon::{ visitors::Visitor, }; -pub struct ComplexUDim2NewLint; +pub struct ManualFromScaleOrFromOffsetLint; fn create_diagnostic(args: &UDim2ComplexArgs) -> Diagnostic { - let code = "roblox_complex_udim2_new"; + let code = "roblox_manual_fromscale_or_fromoffset"; let primary_label = Label::new(args.call_range); match args.complexity_type { @@ -39,7 +39,7 @@ fn create_diagnostic(args: &UDim2ComplexArgs) -> Diagnostic { } } -impl Lint for ComplexUDim2NewLint { +impl Lint for ManualFromScaleOrFromOffsetLint { type Config = (); type Error = Infallible; @@ -47,7 +47,7 @@ impl Lint for ComplexUDim2NewLint { const LINT_TYPE: LintType = LintType::Style; fn new(_: Self::Config) -> Result { - Ok(ComplexUDim2NewLint) + Ok(ManualFromScaleOrFromOffsetLint) } fn pass(&self, ast: &Ast, context: &Context, _: &AstContext) -> Vec { @@ -146,11 +146,11 @@ mod tests { use super::{super::test_util::test_lint, *}; #[test] - fn test_roblox_complex_udim2_new() { + fn test_manual_fromscale_or_fromoffset() { test_lint( - ComplexUDim2NewLint::new(()).unwrap(), - "roblox_complex_udim2_new", - "roblox_complex_udim2_new", + ManualFromScaleOrFromOffsetLint::new(()).unwrap(), + "roblox_manual_fromscale_or_fromoffset", + "roblox_manual_fromscale_or_fromoffset", ); } } diff --git a/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.stderr b/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.stderr deleted file mode 100644 index 287a36ad..00000000 --- a/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.stderr +++ /dev/null @@ -1,72 +0,0 @@ -error[roblox_complex_udim2_new]: this UDim2.new call only sets scale, and can be simplified using UDim2.fromScale - ┌─ roblox_complex_udim2_new.lua:17:1 - │ -17 │ UDim2.new(1, 0, 1, 0) - │ ^^^^^^^^^^^^^^^^^^^^^ - │ - = try: UDim2.fromScale(1, 1) - -error[roblox_complex_udim2_new]: this UDim2.new call only sets scale, and can be simplified using UDim2.fromScale - ┌─ roblox_complex_udim2_new.lua:18:1 - │ -18 │ UDim2.new(0, 0, 1, 0) - │ ^^^^^^^^^^^^^^^^^^^^^ - │ - = try: UDim2.fromScale(0, 1) - -error[roblox_complex_udim2_new]: this UDim2.new call only sets offset, and can be simplified using UDim2.fromOffset - ┌─ roblox_complex_udim2_new.lua:20:1 - │ -20 │ UDim2.new(0, 5, 0, 1) - │ ^^^^^^^^^^^^^^^^^^^^^ - │ - = try: UDim2.fromOffset(5, 1) - -error[roblox_complex_udim2_new]: this UDim2.new call only sets offset, and can be simplified using UDim2.fromOffset - ┌─ roblox_complex_udim2_new.lua:21:1 - │ -21 │ UDim2.new(0, 0, 0, 1) - │ ^^^^^^^^^^^^^^^^^^^^^ - │ - = try: UDim2.fromOffset(0, 1) - -error[roblox_complex_udim2_new]: this UDim2.new call only sets offset, and can be simplified using UDim2.fromOffset - ┌─ roblox_complex_udim2_new.lua:22:1 - │ -22 │ UDim2.new(0, 1, 0, 1) - │ ^^^^^^^^^^^^^^^^^^^^^ - │ - = try: UDim2.fromOffset(1, 1) - -error[roblox_complex_udim2_new]: this UDim2.new call only sets offset, and can be simplified using UDim2.fromOffset - ┌─ roblox_complex_udim2_new.lua:23:1 - │ -23 │ UDim2.new(0, a, 0, 1) - │ ^^^^^^^^^^^^^^^^^^^^^ - │ - = try: UDim2.fromOffset(a, 1) - -error[roblox_complex_udim2_new]: this UDim2.new call only sets offset, and can be simplified using UDim2.fromOffset - ┌─ roblox_complex_udim2_new.lua:24:1 - │ -24 │ UDim2.new(0, a, 0, b) - │ ^^^^^^^^^^^^^^^^^^^^^ - │ - = try: UDim2.fromOffset(a, b) - -error[roblox_complex_udim2_new]: this UDim2.new call only sets scale, and can be simplified using UDim2.fromScale - ┌─ roblox_complex_udim2_new.lua:26:1 - │ -26 │ UDim2.new(a, 0, 0, 0) - │ ^^^^^^^^^^^^^^^^^^^^^ - │ - = try: UDim2.fromScale(a, 0) - -error[roblox_complex_udim2_new]: this UDim2.new call only sets scale, and can be simplified using UDim2.fromScale - ┌─ roblox_complex_udim2_new.lua:27:1 - │ -27 │ UDim2.new(a, 0.0, 0, 0.0) - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ - = try: UDim2.fromScale(a, 0) - diff --git a/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.lua b/selene-lib/tests/lints/roblox_manual_fromscale_or_fromoffset/roblox_manual_fromscale_or_fromoffset.lua similarity index 100% rename from selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.lua rename to selene-lib/tests/lints/roblox_manual_fromscale_or_fromoffset/roblox_manual_fromscale_or_fromoffset.lua diff --git a/selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.std.toml b/selene-lib/tests/lints/roblox_manual_fromscale_or_fromoffset/roblox_manual_fromscale_or_fromoffset.std.toml similarity index 100% rename from selene-lib/tests/lints/roblox_complex_udim2_new/roblox_complex_udim2_new.std.toml rename to selene-lib/tests/lints/roblox_manual_fromscale_or_fromoffset/roblox_manual_fromscale_or_fromoffset.std.toml diff --git a/selene-lib/tests/lints/roblox_manual_fromscale_or_fromoffset/roblox_manual_fromscale_or_fromoffset.stderr b/selene-lib/tests/lints/roblox_manual_fromscale_or_fromoffset/roblox_manual_fromscale_or_fromoffset.stderr new file mode 100644 index 00000000..a30fdf32 --- /dev/null +++ b/selene-lib/tests/lints/roblox_manual_fromscale_or_fromoffset/roblox_manual_fromscale_or_fromoffset.stderr @@ -0,0 +1,72 @@ +error[roblox_manual_fromscale_or_fromoffset]: this UDim2.new call only sets scale, and can be simplified using UDim2.fromScale + ┌─ roblox_manual_fromscale_or_fromoffset.lua:17:1 + │ +17 │ UDim2.new(1, 0, 1, 0) + │ ^^^^^^^^^^^^^^^^^^^^^ + │ + = try: UDim2.fromScale(1, 1) + +error[roblox_manual_fromscale_or_fromoffset]: this UDim2.new call only sets scale, and can be simplified using UDim2.fromScale + ┌─ roblox_manual_fromscale_or_fromoffset.lua:18:1 + │ +18 │ UDim2.new(0, 0, 1, 0) + │ ^^^^^^^^^^^^^^^^^^^^^ + │ + = try: UDim2.fromScale(0, 1) + +error[roblox_manual_fromscale_or_fromoffset]: this UDim2.new call only sets offset, and can be simplified using UDim2.fromOffset + ┌─ roblox_manual_fromscale_or_fromoffset.lua:20:1 + │ +20 │ UDim2.new(0, 5, 0, 1) + │ ^^^^^^^^^^^^^^^^^^^^^ + │ + = try: UDim2.fromOffset(5, 1) + +error[roblox_manual_fromscale_or_fromoffset]: this UDim2.new call only sets offset, and can be simplified using UDim2.fromOffset + ┌─ roblox_manual_fromscale_or_fromoffset.lua:21:1 + │ +21 │ UDim2.new(0, 0, 0, 1) + │ ^^^^^^^^^^^^^^^^^^^^^ + │ + = try: UDim2.fromOffset(0, 1) + +error[roblox_manual_fromscale_or_fromoffset]: this UDim2.new call only sets offset, and can be simplified using UDim2.fromOffset + ┌─ roblox_manual_fromscale_or_fromoffset.lua:22:1 + │ +22 │ UDim2.new(0, 1, 0, 1) + │ ^^^^^^^^^^^^^^^^^^^^^ + │ + = try: UDim2.fromOffset(1, 1) + +error[roblox_manual_fromscale_or_fromoffset]: this UDim2.new call only sets offset, and can be simplified using UDim2.fromOffset + ┌─ roblox_manual_fromscale_or_fromoffset.lua:23:1 + │ +23 │ UDim2.new(0, a, 0, 1) + │ ^^^^^^^^^^^^^^^^^^^^^ + │ + = try: UDim2.fromOffset(a, 1) + +error[roblox_manual_fromscale_or_fromoffset]: this UDim2.new call only sets offset, and can be simplified using UDim2.fromOffset + ┌─ roblox_manual_fromscale_or_fromoffset.lua:24:1 + │ +24 │ UDim2.new(0, a, 0, b) + │ ^^^^^^^^^^^^^^^^^^^^^ + │ + = try: UDim2.fromOffset(a, b) + +error[roblox_manual_fromscale_or_fromoffset]: this UDim2.new call only sets scale, and can be simplified using UDim2.fromScale + ┌─ roblox_manual_fromscale_or_fromoffset.lua:26:1 + │ +26 │ UDim2.new(a, 0, 0, 0) + │ ^^^^^^^^^^^^^^^^^^^^^ + │ + = try: UDim2.fromScale(a, 0) + +error[roblox_manual_fromscale_or_fromoffset]: this UDim2.new call only sets scale, and can be simplified using UDim2.fromScale + ┌─ roblox_manual_fromscale_or_fromoffset.lua:27:1 + │ +27 │ UDim2.new(a, 0.0, 0, 0.0) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = try: UDim2.fromScale(a, 0) + From a87e682feb3723967d42a2a529d0eeffb6a98113 Mon Sep 17 00:00:00 2001 From: James Onnen Date: Tue, 13 Aug 2024 13:10:49 -0700 Subject: [PATCH 4/4] docs: Update changelog.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e6a62ce..f0fb656a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/Kampfkarren/selene/compare/0.27.1...HEAD) ### Added - Added `Path2DControlPoint.new` to the Roblox standard library +- Added new [`roblox_manual_fromscale_or_fromoffset` lint](https://kampfkarren.github.io/selene/lints/roblox_manual_fromscale_or_fromoffset.html), which will warn when the arguments could be simplified to `UDim2.fromScale` or `UDim2.fromOffset`. ## [0.27.1](https://github.com/Kampfkarren/selene/releases/tag/0.27.1) - 2024-04-28 ### Fixed