From 30b78983d093203d62d0c6bcfda539bf6a0f4f48 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Tue, 2 Jul 2019 20:22:24 -0700 Subject: [PATCH] Rewrite DreamChecker's readme --- src/dreamchecker/README.md | 100 ++++++++++++++++++++++++++----------- 1 file changed, 72 insertions(+), 28 deletions(-) diff --git a/src/dreamchecker/README.md b/src/dreamchecker/README.md index 85713f04..de22d016 100644 --- a/src/dreamchecker/README.md +++ b/src/dreamchecker/README.md @@ -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.