Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: use "rule" instead of "lint" #444

Merged
merged 5 commits into from
Oct 15, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Added the ability to allow specific patterns in the `deprecated` lint.
- Added exclude option to selene.toml for excluding files from lints
- Adds support for `.yaml` extensions to be used for standard libraries alongside `.yml`.
- Normalized "lint" terminology over "rule" throughout codebase. "rules" in `selene.toml` should now be "lints", but "rules" will still be supported for backwards compatibility.

### Changed
- Updated internal parser, giving substantial parsing speed increases.
Expand Down
36 changes: 18 additions & 18 deletions docs/src/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ selene uses [Full Moon](https://github.com/Kampfkarren/full-moon) to parse the L
TODO: Upload selene-lib on crates.io and link the docs.rs page for that as well as throughout the rest of this article.

## Writing a lint
In selene, lints are created in isolated modules. To start, create a file in `selene-lib/src/rules` with the name of your lint. In this example, we're going to call the lint `cool_lint.rs`.
In selene, lints are created in isolated modules. To start, create a file in `selene-lib/src/lints` with the name of your lint. In this example, we're going to call the lint `cool_lint.rs`.

Let's now understand what a lint consists of. selene takes lints in the form of structs that implement the `Rule` trait. The `Rule` trait expects:
Let's now understand what a lint consists of. selene takes lints in the form of structs that implement the `Lint` trait. The `Lint` trait expects:

- A `Config` associated type that defines what the configuration format is expected to be. Whatever you pass must be [deserializable](https://serde.rs/).
- An `Error` associated type that implements [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html). This is used if configurations can be invalid (such as a parameter only being a number within a range). Most of the time, configurations cannot be invalid (other than deserializing errors, which are handled by selene), and so you can set this to [`std::convert::Infallible`](https://doc.rust-lang.org/std/convert/enum.Infallible.html).
- A `SEVERITY` constant which is either `Severity::Error` or `Severity::Warning`. Use `Error` if the code is positively impossible to be correct.
- A `RULE_TYPE` constant which is either `Complexity`, `Correctness`, `Performance`, or `Style`. So far not used for anything.
- A `LINT_TYPE` constant which is either `Complexity`, `Correctness`, `Performance`, or `Style`. So far not used for anything.
- A `new` function with the signature `fn new(config: Self::Config) -> Result<Self, Self::Error>`. With the selene CLI, this is called once.
- A `pass` function with the signature `fn pass(&self, ast: &full_moon::ast::Ast, context: &Context, ast_context: &AstContext) -> Vec<Diagnostic>`. The `ast` argument is the full-moon representation of the code. The `context` argument provides optional additional information, such as the standard library being used. The `ast_context` argument provides context specific to that AST, such as its scopes. Any `Diagnostic` structs returned here are displayed to the user.

Expand All @@ -25,12 +25,12 @@ use std::convert::Infallible;

struct CoolLint;

impl Rule for CoolLint {
impl Lint for CoolLint {
type Config = ();
type Error = Infallible;

const SEVERITY: Severity = Severity::Warning;
const RULE_TYPE: RuleType = RuleType::Style;
const LINT_TYPE: LintType = LintType::Style;

fn new(_: Self::Config) -> Result<Self, Self::Error> {
Ok(CoolLint)
Expand All @@ -44,37 +44,37 @@ impl Rule for CoolLint {

The implementation of `pass` is completely up to you, but there are a few common patterns.

- Creating a visitor over the ast provided and creating diagnostics based off of that. See [`divide_by_zero`](https://github.com/Kampfkarren/selene/blob/master/selene-lib/src/rules/divide_by_zero.rs) and [`suspicious_reverse_loop`](https://github.com/Kampfkarren/selene/blob/master/selene-lib/src/rules/suspicious_reverse_loop.rs) for straight forward examples.
- Using the `ScopeManager` struct to lint based off of usage of variables and references. See [`shadowing`](https://github.com/Kampfkarren/selene/blob/master/selene-lib/src/rules/shadowing.rs) and [`global_usage`](https://github.com/Kampfkarren/selene/blob/master/selene-lib/src/rules/global_usage.rs).
- Creating a visitor over the ast provided and creating diagnostics based off of that. See [`divide_by_zero`](https://github.com/Kampfkarren/selene/blob/master/selene-lib/src/lints/divide_by_zero.rs) and [`suspicious_reverse_loop`](https://github.com/Kampfkarren/selene/blob/master/selene-lib/src/lints/suspicious_reverse_loop.rs) for straight forward examples.
- Using the `ScopeManager` struct to lint based off of usage of variables and references. See [`shadowing`](https://github.com/Kampfkarren/selene/blob/master/selene-lib/src/lints/shadowing.rs) and [`global_usage`](https://github.com/Kampfkarren/selene/blob/master/selene-lib/src/lints/global_usage.rs).

### Getting selene to recognize the new lint

Now that we have our lint, we have to make sure selene actually knows to use it. There are two places you need to update.

In selene-lib/src/lib.rs, search for `use_rules!`. You will see something such as:
In selene-lib/src/lib.rs, search for `use_lints!`. You will see something such as:

```rs
use_rules! {
almost_swapped: rules::almost_swapped::AlmostSwappedLint,
divide_by_zero: rules::divide_by_zero::DivideByZeroLint,
empty_if: rules::empty_if::EmptyIfLint,
use_lints! {
almost_swapped: lints::almost_swapped::AlmostSwappedLint,
divide_by_zero: lints::divide_by_zero::DivideByZeroLint,
empty_if: lints::empty_if::EmptyIfLint,
...
}
```

Put your lint in this list (alphabetical order) as the following format:

```
lint_name: rules::module_name::LintObject,
lint_name: lints::module_name::LintObject,
```

For us, this would be:

```
cool_lint: rules::cool_lint::CoolLint,
cool_lint: lints::cool_lint::CoolLint,
```

Next, in `selene-lib/src/rules.rs`, search for `pub mod`, and you will see:
Next, in `selene-lib/src/lints.rs`, search for `pub mod`, and you will see:

```rs
pub mod almost_swapped;
Expand Down Expand Up @@ -128,7 +128,7 @@ The `.unwrap()` is just because `CoolLint::new` returns a `Result`. If you want

The first `"cool_lint"` is the name of the folder we created. The second `"cool_lint"` is the name of the *Lua file* we created.

Now, just run `cargo test`, and a `.stderr` file will be automatically generated! You can manipulate it however you see fit as well as modifying your rule, and so long as the file is there, selene will make sure that its accurate.
Now, just run `cargo test`, and a `.stderr` file will be automatically generated! You can manipulate it however you see fit as well as modifying your lint, and so long as the file is there, selene will make sure that its accurate.

Optionally, you can add a `.std.toml` with the same name as the test next to the lua file, where you can specify a custom [standard library](./usage/std.html) to use. If you do not, the Lua 5.1 standard library will be used.

Expand All @@ -141,7 +141,7 @@ To document a new lint, edit `docs/src/SUMMARY.md`, and add your lint to the tab
Then, edit the markdown file it creates (if you have `mdbook serve` on, it'll create it for you), and write it in this format:

````
# rule_name
# lint_name
## What it does
Explain what your lint does, simply.

Expand All @@ -166,4 +166,4 @@ Specify any configuration if it exists.
If there's anything else a user should know when using this lint, write it here.
````

This isn't a strict format, and you can mess with it as appropriate. For example, `standard_library` does not have a "Why this is bad" section as not only is it a very encompassing rule, but it should be fairly obvious. Many rules don't specify a "...should be written as..." as it is either something with various potential fixes (such as [`global_usage`](./lints/global_usage.md)) or because the "good code" is just removing parts entirely (such as [`unbalanced_assignments`](./lints/unbalanced_assignments.md)).
This isn't a strict format, and you can mess with it as appropriate. For example, `standard_library` does not have a "Why this is bad" section as not only is it a very encompassing lint, but it should be fairly obvious. Many lints don't specify a "...should be written as..." as it is either something with various potential fixes (such as [`global_usage`](./lints/global_usage.md)) or because the "good code" is just removing parts entirely (such as [`unbalanced_assignments`](./lints/unbalanced_assignments.md)).
2 changes: 1 addition & 1 deletion docs/src/lints/global_usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Prohibits use of `_G`.
`_G` is global mutable state, which is heavily regarded as harmful. You should instead refactor your code to be more modular in nature.

## Remarks
If you are using the Roblox standard library, use of `shared` is prohibited under this rule.
If you are using the Roblox standard library, use of `shared` is prohibited under this lint.

## Example
```lua
Expand Down
2 changes: 1 addition & 1 deletion docs/src/lints/high_cyclomatic_complexity.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ end
This lint is off by default. In order to enable it, add this to your selene.toml:

```toml
[rules]
[lints]
high_cyclomatic_complexity = "warn" # Or "deny"
```
12 changes: 6 additions & 6 deletions docs/src/usage/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Configuration files are placed in the directory you are running selene in and ar
You can change the severity of lints by entering the following into selene.toml:

```toml
[rules]
[lints]
lint_1 = "severity"
lint_2 = "severity"
...
Expand All @@ -21,17 +21,17 @@ Where "severity" is one of the following:

Note that "deny" and "warn" are effectively the same, only warn will give orange text while error gives red text, and they both have different counters.

## Configuring specific rules
You can configure specific rules by entering the following into selene.toml:
## Configuring specific lints
You can configure specific lints by entering the following into selene.toml:

```toml
[config]
rule1 = ...
rule2 = ...
lint1 = ...
lint2 = ...
...
```

Where the value is whatever the special configuration of that rule is. You can learn these on the lints specific page in the [list of lints](../lints/index.md). For example, if we wanted to allow empty if branches if the contents contain comments, then we would write:
Where the value is whatever the special configuration of that lint is. You can learn these on the lints specific page in the [list of lints](../lints/index.md). For example, if we wanted to allow empty if branches if the contents contain comments, then we would write:

```toml
[config]
Expand Down
2 changes: 1 addition & 1 deletion docs/src/usage/filtering.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ local something = 1
This also works with settings other than `allow`--you can `warn` or `deny` lints in the same fashion. For example, you can have a project with the following `selene.toml` [configuration](./configuration.md):

```toml
[rules]
[lints]
unused_variable = "allow" # I'm fine with unused variables in code
```

Expand Down
Loading