Skip to content

Commit

Permalink
Aptos Move Linter Documentation Page (#688)
Browse files Browse the repository at this point in the history
  • Loading branch information
vineethk authored Nov 4, 2024
1 parent 18d1ac9 commit ddafc6d
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 0 deletions.
3 changes: 3 additions & 0 deletions apps/nextra/pages/en/build/smart-contracts/_meta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ export default {
compiler_v2: {
title: "Compiler V2",
},
linter: {
title: "Linter",
},
"learn-move": {
display: "hidden",
},
Expand Down
122 changes: 122 additions & 0 deletions apps/nextra/pages/en/build/smart-contracts/linter.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
---
title: "Aptos Move Lint"
---

# Aptos Move Lint

The "Aptos Move Lint" tool runs on a Move package to find and warn about common issues in Move programs, helping improve your Move code.

You can run it with the aptos CLI command: `aptos move lint`.

This tool is currently in beta, so please try it out and submit your [feedback](https://github.com/aptos-labs/aptos-core/issues/new?title=%5Blinter%5D%20%3CDescriptive%20Title%3E&body=%3CDetailed%20description%20of%20the%20issue%20or%20feature%20request%3E&labels=move-linter&projects=aptos-labs/16), including ideas and requests for new lint rules.


## Lint Checks
### `avoid_copy_on_identity_comparison`

Checks for identity comparisons (`==` or `!=`) between copied values of type `vector` or `struct` (i.e., types for which copy can be expensive). It instead suggests to use reference-based identity comparison instead (i.e., use `&x == &y` instead of `x == y`, when the above mentioned conditions meet).

[This recommendation](book/equality.mdx#avoid-extra-copies) is also given in the Move book. Due to automatic copy inference, it may not be obvious when a copy is being made while using `==` or `!=` on values with types that have the `copy` ability. This lint identifies cases where extra copies on vectors or structs could be skipped by using reference-based identity comparisons.

### `blocks_in_conditions`

Checks for use of blocks in conditions (e.g., in `if`, `match`, and `while` conditions), which can make code hard to read. An example coding pattern caught by this lint is:

```move
if ({let x = foo(); !x}) { // uses a block in condition
bar();
}
```

Such code can usually be rewritten to hoist the block out and above the condition, usually making it more readable.

It is a common Move pattern to provide inline specifications in conditions, especially loop invariants, which requires creating blocks in conditions. We exclude this pattern in the lint check to continue to allow for this specification pattern.

Note that an `assert!` is translated to a conditional abort, so blocks in `assert!` condition also are reported by this lint.

### `needless_bool`

Checks for patterns of the form (where `x` is any arbitrary boolean expression):

- `if (x) true else false`, which can be replaced with just `x`.
- `if (x) false else true`, which can be replaced with just `!x`.
- `if (x) { return true } else { return false }`, which can be replaced with just `return x`.
- `if (x) { return false } else { return true }`, which can be replaced with just `return !x`.
- `if (x) true else true` or `if (x) false else false`, which should be rewritten to remove the redundant branch.

### `needless_deref_ref`

Checks for patterns where references taken are immediately dereferenced, and suggests removing the pair of dereference-reference operators:

- `*&x.f` can be simplified to `x.f`
- `*&mut x.f` can be simplified to `x.f`
- `*&mut x.f = 5;` can be simplified to `x.f = 5;`

### `needless_mutable_reference`

Checks for mutable references or borrows (currently: mutable reference parameters, mutable borrow of locals, `borrow_global_mut`) that are not used mutably, and suggests to use the immutable reference or borrow instead.

For example, in the function `foo` below, `&mut` can be replaced by `&` because the reference is not mutably used.

```move
fun foo(x: u64): u64 {
let y = &mut x;
*y
}
```

### `needless_ref_deref`

Checks for patterns where immutable reference are taken for a dereference, and suggests removing the pair of reference-dereference operators: `&*x` can be simplified to `x`.

### `needless_ref_in_field_access`

Checks for patterns where there are needless references taken when accessing a field of a struct or an enum, and suggests removing the explicit reference taken:

- `(&s).f` can be simplified to `s.f`
- `(&mut s).f = 42;` can be simplified to `s.f = 42;`

### `simpler_numeric_expression`

Checks for various patterns where a simpler numeric expression can be used instead. In all these cases, the code must already type check, and `x` can be any numeric expression.

- `x & 0`, `x * 0`, `0 & x`, `0 * x`, `0 << x`, `0 >> x`, `x % 1` can all be replaced with just `0`.
- `x | 0`, `x ^ 0`, `x >> 0`, `x << 0`, `x + 0`, `x - 0`, `x / 1`, `x * 1`, `0 | x`, `0 ^ x`, `0 + x`, `1 * x` can all be replaced with just `x`.

### `unnecessary_boolean_identity_comparison`

Checks for boolean identity comparisons of the form:

- `x == true`, `true == x`, which can be replaced with just `x`.
- `x == false`, `false == x`, which can be replaced with just `!x`.

In all these cases, `x` can be any arbitrary boolean expression.

### `unnecessary_numerical_extreme_comparison`

Checks if there are any numerical comparisons with extreme values (i.e., min and max value representable by that numeric type) that are unnecessary or can be made more precise and clear. Depending on the comparison, various recommendations are made.

Consider the following example expressions that are caught by the lint, and the corresponding recommendations made (in all these cases, `x` is a place holder for a numerical expression of type `u8`, `u16`, `u32`, `u64`, `u128`, or `u256`, and `MAX` is a place holder for the max value representable for that numeric type):

- `x < 0`, `0 > x`, `x > MAX`, `MAX < x`, are always false, rewrite code to remove this comparison
- `x >= 0`, `0 <= x`, `x <= MAX`, `MAX >= x`, are always true, rewrite code to remove this comparison
- `x <= 0`, `0 >= x`, `x >= MAX`, `MAX <= x`, can all be simplified to use `==` instead
- `x > 0`, `0 < x`, `x < MAX`, `MAX > x`, can all be clarified to use `!=` instead

### `while_true`

Checks for `while (true) { .... }` patterns and suggests using the more explicit `loop { .... }` construct instead.

## Suppressing Lint Warnings

To suppress one or more lint checks named `check1`, `check2`, ... (and so on), you can add the attribute `#[lint::skip(check1, check2, ...)]` to a function or a module. The linter will then not perform the checks named `check1`, `check2`, ... (and so on) for that function or module.

For example, the function below would usually get a warning from the linter about a `needless_bool`, but due to the attribute on the function, the linter does not emit a warning.

```move
#[lint::skip(needless_bool)]
fun violation(): bool {
if (foo()) true else false
}
```

0 comments on commit ddafc6d

Please sign in to comment.