mirror of
https://github.com/g-plane/wasm-language-tools.git
synced 2025-08-04 16:39:38 +00:00
docs: add docs about diagnostics
This commit is contained in:
parent
553c37ba1d
commit
22c52d108e
16 changed files with 574 additions and 11 deletions
|
@ -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' },
|
||||
|
|
|
@ -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"`
|
||||
|
|
36
docs/diagnostics/const-expr.md
Normal file
36
docs/diagnostics/const-expr.md
Normal 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))
|
||||
```
|
27
docs/diagnostics/duplicated-names.md
Normal file
27
docs/diagnostics/duplicated-names.md
Normal 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.
|
47
docs/diagnostics/mutated-immutable.md
Normal file
47
docs/diagnostics/mutated-immutable.md
Normal 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))
|
||||
```
|
56
docs/diagnostics/needless-mut.md
Normal file
56
docs/diagnostics/needless-mut.md
Normal 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))
|
||||
```
|
34
docs/diagnostics/new-non-defaultable.md
Normal file
34
docs/diagnostics/new-non-defaultable.md
Normal 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.
|
6
docs/diagnostics/overview.md
Normal file
6
docs/diagnostics/overview.md
Normal 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.
|
29
docs/diagnostics/shadow.md
Normal file
29
docs/diagnostics/shadow.md
Normal 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)))
|
||||
```
|
42
docs/diagnostics/subtyping.md
Normal file
42
docs/diagnostics/subtyping.md
Normal 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))))
|
||||
```
|
28
docs/diagnostics/type-check.md
Normal file
28
docs/diagnostics/type-check.md
Normal 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))))
|
||||
```
|
133
docs/diagnostics/type-misuse.md
Normal file
133
docs/diagnostics/type-misuse.md
Normal 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
27
docs/diagnostics/undef.md
Normal 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))
|
||||
```
|
14
docs/diagnostics/uninit.md
Normal file
14
docs/diagnostics/uninit.md
Normal 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))
|
||||
```
|
30
docs/diagnostics/unreachable.md
Normal file
30
docs/diagnostics/unreachable.md
Normal 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)))
|
||||
```
|
42
docs/diagnostics/unused.md
Normal file
42
docs/diagnostics/unused.md
Normal 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")))
|
||||
```
|
Loading…
Add table
Add a link
Reference in a new issue