docs: add docs about diagnostics

This commit is contained in:
Pig Fang 2025-05-13 21:27:45 +08:00
parent 553c37ba1d
commit 22c52d108e
No known key found for this signature in database
GPG key ID: A8198F548DADA9E2
16 changed files with 574 additions and 11 deletions

View file

@ -43,6 +43,29 @@ export default defineConfig({
{ text: 'Format', link: '/config/format' },
],
},
{
text: 'Diagnostics',
items: [
{
text: 'Overview',
link: '/diagnostics/overview',
docFooterText: 'Diagnostics Overview',
},
{ text: 'Undefined', link: '/diagnostics/undef' },
{ text: 'Unused', link: '/diagnostics/unused' },
{ text: 'Unreachable', link: '/diagnostics/unreachable' },
{ text: 'Uninitialized', link: '/diagnostics/uninit' },
{ text: 'Mutated Immutable', link: '/diagnostics/mutated-immutable' },
{ text: 'Needless Mutable', link: '/diagnostics/needless-mut' },
{ text: 'Type Checking', link: '/diagnostics/type-check' },
{ text: 'Subtyping', link: '/diagnostics/subtyping' },
{ text: 'Type Misuse', link: '/diagnostics/type-misuse' },
{ text: 'Shadowing', link: '/diagnostics/shadow' },
{ text: 'Constant Expression', link: '/diagnostics/const-expr' },
{ text: 'Duplicated Names', link: '/diagnostics/duplicated-names' },
{ text: 'New Non-defaultable', link: '/diagnostics/new-non-defaultable' },
],
},
],
socialLinks: [
{ icon: 'github', link: 'https://github.com/g-plane/wasm-language-tools' },

View file

@ -54,17 +54,6 @@ WebAssembly allows shadowing identifiers in block labels. For example:
However, it's confusing that you may want to jump to the label at line 3 or line 4.
This lint reports such cases when you accidentally give the same name to different block labels.
Same name but not being as inner block is not reported, such as:
```wasm
(module
(func
(block $label
br $label)
(block $label
br $label)))
```
## `implicitModule`
> default: `"allow"`

View file

@ -0,0 +1,36 @@
# Constant Expression
For the initialization expressions of globals, tables, offsets and element segments, they must be constant.
To be a constant expression, it must only contain the following instructions:
- `*.const`
- `global.get`
- `ref.null`
- `ref.i31`
- `ref.func`
- `struct.new`
- `struct.new_default`
- `array.new`
- `array.new_default`
- `array.new_fixed`
- `any.convert_extern`
- `extern.convert_any`
Constant expression example:
```wasm
(module
(global i32
i32.const 2))
```
Non-constant expression example:
```wasm error-5-5-5-12
(module
(global i32
i32.const 1
i32.const 2
i32.add))
```

View file

@ -0,0 +1,27 @@
# Duplicated Names
This checks duplicated identifiers in their same scope:
```wasm error-2-9-2-11 error-3-9-3-11
(module
(func $f)
(func $f))
```
Same identifier in different scopes is allowed:
```wasm
(module
(func (param $p i32))
(func (param $p i32)))
```
Though in the same scope, if two identifiers have same name but different kinds, it is allowed:
```wasm
(module
(func $f)
(type $f (func)))
```
In the example above, the first `$f` is funcidx while the second `$f` is typeidx.

View file

@ -0,0 +1,47 @@
# Mutated Immutable
This checks for globals, WasmGC struct fields and arrays that are not declared mutable,
but are mutated unexpectedly.
Specifically:
- `global.set` will mutate a global
- `struct.set` will mutate a WasmGC struct field
- `array.set`, `array.fill`, `array.init_data` and `array.init_elem` will mutate a WasmGC array
Global types and field types aren't defined with a `mut` keyword are immutable.
It's invalid to mutate them.
## Globals
```wasm error-5-17-5-18
(module
(global i32
i32.const 0)
(func
(global.set 0
(i32.const 0))))
```
## Struct Fields
```wasm error-6-18-6-19
(module
(type (struct (field i32)))
(func (param (ref 0))
local.get 0
i32.const 0
struct.set 0 0))
```
## Arrays
```wasm error-7-15-7-16
(module
(type (array i32))
(func (param (ref 0))
local.get 0
i32.const 0
i32.const 0
array.set 0))
```

View file

@ -0,0 +1,56 @@
# Needless Mutable
> [!TIP]
> This can be configured. Read more about [configuration](../config/lint.md#needlessmut).
This checks for globals, WasmGC struct fields and arrays that are declared mutable but are never mutated.
Specifically:
- `global.set` can mutate a global
- `struct.set` can mutate a WasmGC struct field
- `array.set`, `array.fill`, `array.init_data` and `array.init_elem` can mutate a WasmGC array
For those global types and field types that are defined with a `mut` keyword
but are never mutated by the instructions above, "mutable" is needless.
## Globals
```wasm warning-5-20-5-23
(module
(func
global.get 0
drop)
(global $global (mut i32)
i32.const 0))
```
It won't report exported globals, since they may be mutated by the host.
```wasm
(module
(export "globalValue" (global 0))
(global (mut i32)
i32.const 0))
```
## Struct Fields
```wasm warning-2-25-2-28
(module
(type (struct (field (mut i32))))
(func (param (ref 0)) (result i32)
local.get 0
struct.get 0 0))
```
## Arrays
```wasm warning-2-17-2-20
(module
(type (array (mut i32)))
(func (param (ref 0)) (result i32)
local.get 0
i32.const 0
array.get 0))
```

View file

@ -0,0 +1,34 @@
# New Non-defaultable
When using `array.new_default` or `struct.new_default`, the given type must be defaultable.
This checks if that type is defaultable or not.
```wasm error-10-24-10-46 error-13-25-13-48
(module
(type $defaultable-array (array i32))
(type $non-defaultable-array (array (ref any)))
(type $defaultable-struct (struct (field i32)))
(type $non-defaultable-struct (struct (field (ref any))))
(func
(result (ref $defaultable-array) (ref $non-defaultable-array) (ref $defaultable-struct) (ref $non-defaultable-struct))
(array.new_default $defaultable-array
(i32.const 0))
(array.new_default $non-defaultable-array
(i32.const 0))
(struct.new_default $defaultable-struct)
(struct.new_default $non-defaultable-struct)))
```
For struct type, only when all fields are defaultable, the struct is defaultable:
```wasm error-7-25-7-26
(module
(type
(struct
(field $defaultable-field i32)
(field $non-defaultable-field (ref any))))
(func (result (ref 0))
(struct.new_default 0)))
```
Empty struct is defaultable.

View file

@ -0,0 +1,6 @@
# Diagnostics
WebAssembly Language Tools provides not only some basic validations that [WABT](https://github.com/WebAssembly/wabt) does,
but also some additional lints such as detecting unused items, unreachable code and needless mutable items.
This documentation introduces most kinds of diagnostics.

View file

@ -0,0 +1,29 @@
# Shadowing
> [!TIP]
> This can be configured. Read more about [configuration](../config/lint.md#shadow).
WebAssembly allows shadowing identifiers in block labels. For example:
```wasm :line-numbers warning-3-12-3-18
(module
(func
(block $label
(block $label
br $label))))
```
However, it's confusing that you may want to jump to the label at line 3 or line 4.
(Actually it jumps to the inner block label, which is line 4 in this case.)
This checks such cases when you accidentally give the same name to different block labels.
Same name but not being as inner block won't be reported, such as:
```wasm
(module
(func
(block $label
br $label)
(block $label
br $label)))
```

View file

@ -0,0 +1,42 @@
# Subtyping
This checks subtyping relationships when defining types.
## Super Type's Typeidx
Super type's typeidx must be smaller than the sub type's typeidx:
```wasm error-2-14-2-15 error-5-17-5-19
(module
(type (sub 1 (func)))
(type (sub (func)))
(type $z (sub $a (func)))
(type $a (sub (func))))
```
## Super Type Can't Be Final
Super type can't be implicitly or explicitly final:
```wasm error-3-17-3-19
(module
(type $t (func)) ;; implicitly final
(type $s (sub $t (func))))
```
```wasm error-3-17-3-19
(module
(type $t (sub final (func))) ;; explicitly final
(type $s (sub $t (func))))
```
## Type Matching
The sub type must match the super type:
```wasm error-3-18-3-21
(module
(type $a0 (sub (array i32)))
(type $s0 (sub $a0 (struct))))
```

View file

@ -0,0 +1,28 @@
# Type Checking
Type checking will happen when using instructions and at the end of a block (including functions, global expressions, etc).
For instructions:
```wasm error-5-5-5-12 error-7-5-9-21
(module
(func (result i32)
i32.const 0
i64.const 0
i32.add)
(func (result i32)
(i32.add
(i32.const 0)
(i64.const 0))))
```
For the end of a block:
```wasm error-3-16-3-17 error-6-20-6-21
(module
(func (param i64) (result i32)
local.get 0)
(func (result i32)
(block (result i32)
(i64.const 0))))
```

View file

@ -0,0 +1,133 @@
# Type Misuse
> [!NOTE]
> Please pay attention to distinguish it from [type checking](./type-check.md).
> Type checking ensures operand types that passed to instructions are correct,
> while this checks immediates.
WasmGC introduces struct types and array types, and also introduces new instructions like `array.*` and `struct.*`.
This checks if types that used in these instructions match the instruction or not.
## `array.*` and `struct.*`
For example, a struct type used in `array.*` instructions:
```wasm error-5-23-5-30
(module
(type $struct (struct))
(func
i32.const 0
array.new_default $struct
drop))
```
Or, an array type used in `struct.*` instructions:
```wasm error-4-24-4-30
(module
(type $array (array i32))
(func
struct.new_default $array
drop))
```
## `array.copy`
Beside the checks above, `array.copy` also checks the source and destination types.
The source type must match the destination type.
```wasm error-10-5-10-37
(module
(type $dst_array (array (mut i32)))
(type $src_array (array i64))
(func (param (ref $dst_array) (ref $src_array))
local.get 0
i32.const 0
local.get 1
i32.const 0
i32.const 0
array.copy $dst_array $src_array))
```
## `call_ref` and `return_call_ref`
These instructions require the referenced type must be a function type.
```wasm error-12-14-12-21 error-14-21-14-27
(module
(type $func (func))
(type $struct (struct (field i32)))
(type $array (array (mut i32)))
(func (param (ref $func))
local.get 0
call_ref $func
local.get 0
return_call_ref $func)
(func (param (ref $func))
local.get 0
call_ref $struct
local.get 0
return_call_ref $array))
```
## `br_on_cast`
This instruction requires the last type of the label's types must be a ref type:
```wasm error-3-17-3-18
(module
(func
(br_on_cast 0 structref structref
(unreachable))))
```
It requires the second ref type must match the first ref type:
```wasm error-3-29-3-37
(module
(func (result anyref)
(br_on_cast 0 structref arrayref
(unreachable))))
```
It also requires the second ref type must match the last type of the label's types:
```wasm error-4-36-4-49
(module
(func (param (ref any)) (result (ref $t))
(block (result (ref any))
(br_on_cast 1 (ref null any) (ref null $t)
(local.get 0)))
(unreachable)))
```
## `br_on_cast_fail`
This instruction requires the last type of the label's types must be a ref type:
```wasm error-3-22-3-23
(module
(func
(br_on_cast_fail 0 structref structref
(unreachable))))
```
It requires the second ref type must match the first ref type:
```wasm error-3-34-3-42
(module
(func (result anyref)
(br_on_cast_fail 0 structref arrayref
(unreachable))))
```
It also requires the [type difference](https://webassembly.github.io/gc/core/valid/conventions.html#aux-reftypediff)
between the first ref type and the second ref type must match the last type of the label's types:
```wasm error-4-7-5-23
(module
(func (param (ref null any)) (result (ref any))
(block (result (ref $t))
(br_on_cast_fail 1 (ref null any) (ref $t)
(local.get 0)))))
```

27
docs/diagnostics/undef.md Normal file
View file

@ -0,0 +1,27 @@
# Undefined
This checks items that are used but haven't been defined. It checks for:
- function calls
- parameter usage
- function local usage
- type use
- global usage
- memory usage
- table usage
- block label usage
- WasmGC struct field usage
Examples:
```wasm error-3-8-3-9 error-5-15-5-16 error-6-10-6-22 error-9-16-9-28
(module
(func
br 1)
(func
local.get 0
call $not-defined)
(func
i32.const 0
global.set $not-defined))
```

View file

@ -0,0 +1,14 @@
# Uninitialized
This checks when accessing function locals, if they are initialized or not.
Number types, vector types and nullable reference types are considered "defaultable", so they don't require manual initialization.
For non-nullable reference types, they must be initialized by `local.set` or `local.tee` instructions.
```wasm error-5-15-5-16
(module
(func (result i32) (local i32)
local.get 0)
(func (result (ref any)) (local (ref any))
local.get 0))
```

View file

@ -0,0 +1,30 @@
# Unreachable
> [!TIP]
> This can be configured. Read more about [configuration](../config/lint.md#unreachable).
This detects unreachable code, for example those code after `br`, `br_table`, `return` and `unreachable` instructions or infinite loops:
```wasm faded-5-5-5-8 faded-8-5-8-8
(module
(func
(loop ;; infinite loop
br 0)
nop)
(func
return
nop))
```
```wasm faded-4-6-4-13 faded-8-7-10-10
(module
(func
(nop)
(i32.add
(nop)
(unreachable
(i32.const 1))
(i32.const 0))
(drop)
(nop)))
```

View file

@ -0,0 +1,42 @@
# Unused
> [!TIP]
> This can be configured. Read more about [configuration](../config/lint.md#unused).
This checks items that are defined but never used. It checks for:
- functions
- parameters
- function locals
- type definitions
- globals
- memories
- tables
- WasmGC struct fields
Here is an example of unused functions and globals with or without identifiers:
```wasm warning-2-4-2-8 warning-3-9-3-11 warning-4-4-4-10 warning-6-11-6-13
(module
(func)
(func $f)
(global i32
i32.const 0)
(global $g i32
i32.const 0))
```
To intentionally ignore, you can add an underscore prefix to the identifier:
```wasm
(module
(func $_)
(func $_f))
```
If they're exported, they won't be reported as unused:
```wasm
(module
(func (export "main")))
```