Skip to content

Commit

Permalink
Add param deprecation and deprecate instance.new parent param
Browse files Browse the repository at this point in the history
  • Loading branch information
chriscerie committed Mar 27, 2024
1 parent e73cccc commit e635d28
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 12 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
## [Unreleased](https://github.com/Kampfkarren/selene/compare/0.26.1...HEAD)
### Added
- Added `CFrame.lookAlong` to the Roblox standard library
- Added `deprecated` config field to standard library function parameters

### Changed
- Updated the warning message for the `mixed_table` lint to include why mixed tables should be avoided
- Officially deprecated `Instance.new`'s parent argument

## [0.26.1](https://github.com/Kampfkarren/selene/releases/tag/0.26.1) - 2023-11-11
### Fixed
Expand Down
2 changes: 1 addition & 1 deletion docs/src/usage/std.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ globals:
A field is understood as a table if it has fields of its own. Notice that `math` is not defined anywhere, but its fields are. This will create an implicit `math` with the property writability of `read-only`.

### Deprecated
Any field can have a deprecation notice added to it, which will then be read by [the deprecated lint](../lints/deprecated.md).
Any field or arg can have a deprecation notice added to it, which will then be read by [the deprecated lint](../lints/deprecated.md).

```yaml
---
Expand Down
5 changes: 5 additions & 0 deletions selene-lib/default_std/roblox_base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,11 @@ globals:
Instance.new:
args:
- type: string
- required: false
type:
display: Instance
deprecated:
message: set the instance's parent separately
# This is only must_use because we don't allow the second parameter
must_use: true
NumberRange.new:
Expand Down
67 changes: 61 additions & 6 deletions selene-lib/src/lints/deprecated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::convert::Infallible;
use full_moon::{ast, visitors::Visitor};
use serde::Deserialize;

use crate::ast_util::{name_paths::*, scopes::ScopeManager};
use crate::ast_util::{name_paths::*, range, scopes::ScopeManager};

use super::{super::standard_library::*, *};

Expand Down Expand Up @@ -48,6 +48,11 @@ struct DeprecatedVisitor<'a> {
standard_library: &'a StandardLibrary,
}

struct Argument {
display: String,
range: (usize, usize),
}

impl<'a> DeprecatedVisitor<'a> {
fn new(
config: &DeprecatedLintConfig,
Expand Down Expand Up @@ -194,21 +199,62 @@ impl Visitor for DeprecatedVisitor<'_> {
feature = "force_exhaustive_checks",
deny(non_exhaustive_omitted_patterns)
)]
let argument_displays = match function_args {
let arguments = match function_args {
ast::FunctionArgs::Parentheses { arguments, .. } => arguments
.iter()
.map(|argument| argument.to_string())
.map(|argument| Argument {
display: argument.to_string().trim_end().to_string(),
range: range(argument),
})
.collect(),

ast::FunctionArgs::String(token) => vec![token.to_string()],
ast::FunctionArgs::String(token) => vec![
(Argument {
display: token.to_string(),
range: range(token),
}),
],
ast::FunctionArgs::TableConstructor(table_constructor) => {
vec![table_constructor.to_string()]
vec![Argument {
display: table_constructor.to_string(),
range: range(table_constructor),
}]
}

_ => Vec::new(),
};

self.check_name_path(call, "function", &name_path, &argument_displays);
if let Some(Field {
field_kind: FieldKind::Function(function),
..
}) = self.standard_library.find_global(&name_path)
{
for (arg, arg_std) in arguments.iter().zip(&function.arguments) {
if arg.display == "nil" {
continue;
}

if let Some(deprecated) = &arg_std.deprecated {
self.diagnostics.push(Diagnostic::new_complete(
"deprecated",
"this parameter is deprecated".to_string(),
Label::new(arg.range),
vec![deprecated.message.clone()],
Vec::new(),
));
};
}
}

self.check_name_path(
call,
"function",
&name_path,
&arguments
.iter()
.map(|arg| arg.display.clone())
.collect::<Vec<_>>(),
);
}
}

Expand All @@ -234,6 +280,15 @@ mod tests {
);
}

#[test]
fn test_deprecated_params() {
test_lint(
DeprecatedLint::new(DeprecatedLintConfig::default()).unwrap(),
"deprecated",
"deprecated_params",
);
}

#[test]
fn test_specific_allow() {
test_lint(
Expand Down
4 changes: 4 additions & 0 deletions selene-lib/src/standard_library/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,10 @@ pub struct Argument {
#[serde(default)]
#[serde(skip_serializing_if = "is_default")]
pub observes: Observes,

#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub deprecated: Option<Deprecated>,
}

#[derive(Clone, Debug, Hash, PartialEq, Eq)]
Expand Down
1 change: 1 addition & 0 deletions selene-lib/src/standard_library/v1_upgrade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ impl From<v1::Argument> for Argument {
required: v1_argument.required.into(),
argument_type: v1_argument.argument_type.into(),
observes: Observes::ReadWrite,
deprecated: None,
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions selene-lib/tests/lints/deprecated/deprecated_params.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
local _ = Instance.new(a)
local _ = Instance.new(a, b)
local _ = Instance.new(a, nil )
local _ = Instance.new(a, "nil")

a(1)
a ""
a {}
17 changes: 17 additions & 0 deletions selene-lib/tests/lints/deprecated/deprecated_params.std.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
globals:
Instance.new:
args:
- type: string
- required: false
type:
display: Instance
deprecated:
message: set the instance's parent separately
a:
args:
- required: false
type:
display: any
deprecated:
message: this is deprecated
40 changes: 40 additions & 0 deletions selene-lib/tests/lints/deprecated/deprecated_params.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
error[deprecated]: this parameter is deprecated
┌─ deprecated_params.lua:2:27
2 │ local _ = Instance.new(a, b)
│ ^
= set the instance's parent separately

error[deprecated]: this parameter is deprecated
┌─ deprecated_params.lua:4:27
4 │ local _ = Instance.new(a, "nil")
│ ^^^^^
= set the instance's parent separately

error[deprecated]: this parameter is deprecated
┌─ deprecated_params.lua:6:3
6 │ a(1)
│ ^
= this is deprecated

error[deprecated]: this parameter is deprecated
┌─ deprecated_params.lua:7:3
7 │ a ""
│ ^^
= this is deprecated

error[deprecated]: this parameter is deprecated
┌─ deprecated_params.lua:8:3
8 │ a {}
│ ^^
= this is deprecated

24 changes: 19 additions & 5 deletions selene/src/roblox/generate_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ impl RobloxGenerator {
argument_type: ArgumentType::Any,
required: Required::NotRequired,
observes: Observes::ReadWrite,
deprecated: None,
})
.collect(),
method: true,
Expand Down Expand Up @@ -260,11 +261,23 @@ impl RobloxGenerator {
self.std.globals.insert(
"Instance.new".to_owned(),
Field::from_field_kind(FieldKind::Function(FunctionBehavior {
arguments: vec![Argument {
argument_type: ArgumentType::Constant(instance_names),
required: Required::Required(None),
observes: Observes::ReadWrite,
}],
arguments: vec![
Argument {
argument_type: ArgumentType::Constant(instance_names),
required: Required::Required(None),
observes: Observes::ReadWrite,
deprecated: None,
},
Argument {
argument_type: ArgumentType::Display("Instance".to_string()),
required: Required::Required(None),
observes: Observes::ReadWrite,
deprecated: Some(Deprecated {
message: "set the instance's parent separately".to_owned(),
replace: vec![],
}),
},
],
method: false,

// Only true because we don't allow the second parameter
Expand Down Expand Up @@ -294,6 +307,7 @@ impl RobloxGenerator {
argument_type: ArgumentType::Constant(service_names),
required: Required::Required(None),
observes: Observes::ReadWrite,
deprecated: None,
}],
method: true,
must_use: true,
Expand Down

0 comments on commit e635d28

Please sign in to comment.