Rewrite DreamChecker's readme

This commit is contained in:
Tad Hardesty 2019-07-02 20:22:24 -07:00
parent fba51ebb9a
commit 30b78983d0

View file

@ -1,37 +1,81 @@
# DreamChecker
**DreamChecker** is a robust static analysis and typechecking engine for
DreamMaker, the scripting language of the [BYOND] game engine.
**DreamChecker** is a robust whole-program static analysis and type checking
engine for DreamMaker, the scripting language of the [BYOND] game engine.
[BYOND]: https://secure.byond.com/
DreamMaker supports approximately four typechecking features:
## Running DreamChecker
* Field and method use with `.` will compile error if the annotated type of the
left-hand side does not have a field or method matching the name on the
right-hand side. Replacing `.` with `:` bypasses this.
* `for` loops where the automatically filter the input list using `istype` with
the declared type of the loop variable, if present.
* `new()` calls which omit the type to construct use the declared type of the
left-hand side of an assignment in which they are involved.
* `istype()` calls which omit the type to check against use the declared type
of the variable passed to be checked.
DreamChecker can be obtained with `cargo build -p dreamchecker` or from the
[releases] page.
However, even this limited support has problems:
DreamChecker should be run from within the DM project directory. It will
automatically detect the `.dme` file, parse it, and issue diagnostics.
DreamChecker will exit with a non-zero status code if it discovers any
diagnostics, making it suitable for use in continuous integration environments.
* No way to specify the "value" type of associated lists, only the "key".
* Typecasts of all kinds (up, down, sideways) have no syntax whatsoever and are
wholly unchecked.
* It is impossible to declare a return type for a procedure.
* Various apparently typelike annotations have no compile- or run-time effects
despite being accepted (and ignored) by the compiler.
* There is no difference in type annotation between a variable intended to
refer to object instances and typepaths. This leads to confusion, such as
attempting to reference fields of a typepath as if it were an instance, which
runtime errors.
* There is no notion of nullable or non-nullable types.
* There are no type annotations to represent primitives.
[releases]: https://github.com/SpaceManiac/SpacemanDMM/releases
DreamChecker intends to address some or all of these shortcomings, integrating
ideas from TypeScript and family, including flow typing and automatically
determining appropriate type annotations in places where they are missing.
## Diagnostics
In addition to the simple inline diagnostics discovered by the [parsing suite],
DreamCheckers's whole-program analysis can find problems such as:
[parsing suite]: ../dreammaker/
* Unknown `set` setting names.
* Undefined types on unused variables.
* Keyword arguments being passed to procs which do not accept them.
* Calling procs with non-keyword arguments following keyword arguments.
* Proc overrides which are missing keyword arguments that their parents are
called with.
* Declaring vars with `/list` in unusual positions, e.g.
`var/atom/list/movable/L`.
* Use of `src` in global `/proc`s where it is guaranteed to be `null`.
* Calling the parent proc `..()` when no such parent exists.
* Accesses like `L[1].foo` and `foo().bar` wherein `.` acts like `:` instead.
* List accesses perform lookups according to the type appended to `/list`,
e.g. with `var/list/obj/L`, the type of `L[1]` will be `/obj` and a lookup
of `L[1].name` will not generate a warning.
* Proc calls will obey the [return type](#return-type) annotation if present.
## Extensions
DreamChecker also adds additional typing features to the language through a
series of extensions, described below.
Because DreamMaker does not recognize these extensions, it is recommended that
you use the preprocessor flag `SPACEMAN_DMM` to determine whether they should
be enabled:
```dm
#ifdef SPACEMAN_DMM
#define RETURN_TYPE(X) set SpacemanDMM_return_type = X
#define SHOULD_CALL_PARENT(X) set SpacemanDMM_should_call_parent = X
#else
#define RETURN_TYPE(X)
#define SHOULD_CALL_PARENT(X)
#endif
```
### Return type
Use `set SpacemanDMM_return_type = expression` to set a return type expression
for a proc. The return type can take the forms:
* `/typepath` - a raw typepath. The return type of the proc is the type named.
* `param` - a typepath given as a parameter, for procs which return an instance
of the passed-in type.
* `param.type` - the static type of a passed-in parameter, for procs which
return their input or otherwise another value of the same type.
* `param[_].type` - the static type of a passed-in parameter, with one level
of `/list` stripped, for procs which select one item from a list. The `[_]`
may be repeated to strip more levels of `/list`.
### Should call parent
Use `set SpacemanDMM_should_call_parent = 1` to enable a diagnostic on children
of the proc it is set on which do not contain any `..()` parent calls. This can
help with finding situations where a signal or other important handling in the
parent proc is being skipped. Child procs may set this setting to `0` instead
to override the check.