Skip to content

Latest commit

 

History

History
311 lines (267 loc) · 15.6 KB

index.rst

File metadata and controls

311 lines (267 loc) · 15.6 KB

Nimalyzer documentation

Nimalyzer is a static code analyzer for Nim programming language. It allows checking a Nim source code against predefined rules. Its design is inspired by AdaControl. Nimalyzer can be used to enforce some design patterns or ensure that some language constructs are present in a code, or not. For example, it can check if all procedures have defined proper pragmas. Additionally, it can be used as an advanced search through a code tool, for example find all public variables type of int with name which starts with newVar. It is controlled by configuration files containing a set of rules, their parameters and options related to the program behavior. At this moment, the project is in early alpha stage, it doesn't offer too much, its configuration syntax can change and should have a nice amount of bugs.

The project released under 3-Clause BSD license.

  1. To use Nimalyzer with your project, first you have to create a configuration file for it. Please check documentation for configuration syntax and list of available rules for more details. In your configuration file you have to set at least one source file to check and at least one rule to use.
  2. Run Nimanalyzer with path to your configuration file as the argument. For example: nimalyzer config/nimalyzer.cfg and read its output.

It is possible to disable a selected rule for a part of a module with the pragma ruleOff: [name of pragma] and re-enable it later with the pragma ruleOn: [name of pragma]. To do it you have to:

  1. Import the module nimalyzer : import nimalyzer, if you have installed nimalyzer with Nimble, otherwise, point to the module nimalyzer where it is installed.
  2. Insert in the desired place in the code the pragma to disable rule. For example: {.ruleOff: "hasDoc".}. Usually, the pragma can be placed before or in the fragment of the code in which it should be disabled, but some rules can enter restrictions about it. Please refer to the rules' documentation where to place the pragma.
  3. If you want to re-enable a rule in a code later, insert pragma ruleOn. For example: {.ruleOn: "hasDoc".}. Same as with disabling, the pragma can be placed before or in the fragment of the code in which it should be disabled, but some rules can enter restrictions about it. Please refer to the rules' documentation where to place the pragma. Usually, rules require it in the same location where pragma ruleOff should be placed.
  • Names of the rules used for pragmas are case-insensitive, thus "hasDoc" can be also "hasdoc", "HasDoc" or "HASDOC".
  • Names of the rules used for pragmas are strings, compared to the value of ruleName constants defined in the rules.
  • Disabling the rule in the code cause to disable all checks of that type in the code. For example if you used {.ruleOff: "hasEntity".} and your configuration file contains several settings for that rule, all of them will be disabled from this point in the code.

The program allows to automatically or semi-automatically fix some problems reported by its rules with fix type of the rule. In most cases, this type of rules will only open the source code file with the default text editor or execute another command set in the program's configuration file. But several rules allow to automatically fix the problems, for example, hasPragma rule can add or remove a selected pragmas from declarations. In that case the changes will be made on the same file. Sometimes it can produce an invalid code, for example adding pragma contractual may produce an invalid code as the pragma requires enclosing a code in special blocks. Thus, it is strongly recommended to have some kind of backup of the code, for example a version control system like Git, Fossil, etc. More information on how the selected program's rule react with fix type of the rule can be found in the available rules documentation. Additionally, because the program works on AST representation of the checked code, there is a big chance that the code will be reformatted after changes. It is a good idea to have set auto-formatting after executing fix type of rules.

All the program's rules are stored in rules directory inside src directory. To add a new rule it is recommended to use tool genrule (or genrule.exe on Windows). To create the tool, execute in the main project's directory, where Nimble file is: nimble tools. Then execute command: bin/genrule. The program will ask a couple of questions and create a new rule from the template file located in tools directory. Now you can start working on your new rule.

To delete an existing rule, it is enough to remove the Nim file which contains the rule code.

Each module which contains code of the program's rules is split on several parts.

ruleConfig contains configuration of the rule. Available settings are:

  • ruleName - the name of the rule. Required. String value.
  • ruleFoundMessage - the message shown when the rule return positive result of analyzing the code. Required. String value.
  • ruleNotFoundMessage - the message shown when the rule returns negative result of analyzing the code. Required. String value.
  • rulePositiveMessage - the message shown when the rule meets the code's element which follows the rule's requirements, for example, a procedure with documentation, etc. Required. String value.
  • ruleNegativeMessage - the message shown when the rule meets the code's element which doesn't follow the rule's requirements, for example, a procedure without documentation, etc. Required. String value.
  • ruleOptions - the list of options which the rule accepts. If not set, default value, the rule will not accept any arguments in a configuration file. It is a Nim sequence with possible values: node for AST Node, str for string values, integer for integer values, natural for integer values greater than -1, positive for integer values greater than 0, and custom for string values which can contain only the selected values, similar to enumerations. In the last case, the setting ruleOptionValues must be set too. At the moment, a rule can have only one custom option type. The setting is optional. Enumeration.
  • ruleOptionValues - the list of values for the custom type of the rule's options. It is a Nim sequence of strings. The setting is required only when setting ruleOptions contains custom type of the options.
  • ruleMinOptions - the minimal amount of options required by the rule. Default value is 0, which means the rule requires zero or more options. The setting is optional. Natural value.
  • ruleShowForCheck - if true, show the rule summary message for check type of the rule. By default, it is disabled, default value false. The setting is optional. Boolean value.

Each rule has available the following constants to use in its code:

  • showForCheck - Boolean value, set by the configuration's option ruleShowForCheck.
  • foundMessage - String value, set by the configuration's option ruleFoundMessage.
  • notFoundMessage - String value, set by the configuration's option ruleNotFoundMessage.
  • positiveMessage - String value, set by the configuration's option rulePositiveMessage.
  • negativeMessage - String value, set by the configuration's option ruleNegativeMessage.

checkRule is the macro which is runs to check the Nim code. It is split on several parts. Each part must have at least discard statement. The checkRule is a recursive statement, it executes itself from the main AST node of the code to each its child. Additionally, the statement can raise only the ValueError exception, all other exceptions must be caught in the code. All the checking parts are:

  • initCheck - the initialization of checking the Nim code with the rule. This part of code is run only once. It is a good place to initialize some global variables, etc.
  • startCheck - the fragment which will be executed each time, before check any AST node of the Nim code.
  • checking - the part in which the Nim code is checked. Executed for each AST node of the Nim code.
  • endCheck - the part executed at the end of checking, same as initCheck, executed only once. It shows the rule's summary, etc.

checkRule has access to the following variables:

  • astNode - the currently checked Nim code as AST node as pointer. While the pointer can't be changed, the node (and Nim code itself) can be modified.

  • parentNode - the parent AST node of the currently checked Nim code. Same as astNode, the pointer can't be changed, but the Nim code is modifiable.

  • rule - the rule data structure as an object. All its content can be modified. It contains fields: * options - the list of the rule options entered by the user in the

    configuration file. It is a sequence of strings.

    • parent - if true, the currently checked Nim code is the main AST node of the code to check. Boolean value.
    • fileName - the name of the file which contains the checked Nim code. String value.
    • negation - if true, the rule is configured as a negation (with word not in the configuration file). Boolean value.
    • ruleType - the type of the rule: check, fix, search or count. Enumeration.
    • amount - the amount of results found in the previous iterations of checking the Nim code. Integer value.
    • enabled - if true, the rule is enabled for the currently checked Nim code. Boolean value.
    • fixCommand - the command executed by fix type of the rule. Sets by the user in the configuration file. String value.
    • identsCache - the Nim idents cache needed for some internal rule code. It is recommended to not change it.
    • forceFixCommand - if true, the rule should use fixCommand for fix type of the rule instead of its own code. Sets by the user in the configuration file. Boolean value.
  • isParent - if true, the rule is in the main AST node of the currently checked Nim code. Boolean, read only value.

  • messagePrefix - the prefix added to each log's message. Its content depends on the level of the program's messages set in the configuration file. String, read only value.

checkRule can use the following procedures and templates:

  • message(text: string; returnValue: var int; level: Level = lvlError; decrease: bool = true) - prints the selected text as the program's log's message and modify the rule results amount rule.amount via returnValue parameter. If decrease parameter is set to true, the returnValue will be decreased, otherwise increased. level is the level of the log message.
  • errorMessage(text: string; e: ref Exception = nil): int - prints the selected text as the program's error message. If parameter e isn't nil, it also shows the message and stack trace, in debug builds only, for the current exception.
  • setRuleState(node: PNode; ruleName: string; oldState: var bool) - checks and sets the state, enabled or disabled, of the rule, based on the program's pragmas in the code. node is the AST node of the Nim code currently checked, ruleName is usually set to the configuration variable ruleName and oldState is the modified state of the rule, usually set to rule.state, it can be modified by setRuleState call.
  • setResult(checkResult: bool; positiveMessage, negativeMessage: string; node: PNode; ruleData: string = ""; params: varargs[string]) - sets the result of checking the Nim code as the AST node. checkResult is the result of checking of the Nim code, for example, true if the code's documentation found or if procedure has the selected pragma. positiveMessage will be shown when checkResult fulfills the rule's settings, like negation, type, etc. negativeMessage will be shown when checkResilt not fulfils the rule's settings. Both are usually set to the rule's configuration options like positiveMessage and negativeMessage. ruleData is an additional data used by fix type of the rule. params contains list of additional data, used in the program's messages, positiveMessage and negativeMessage. To use any of params, use template {params[number]} in messages, where [number] is the number of the parameter on the list, starting from zero.
  • getNodesToCheck(parentNode, node: PNode): PNode - get the flattened into one list, the list of AST nodes, starting from currently checked node of the Nim code.

fixRule is the macro which will be executed for fix type of the rule. It must contain at least discard statement. If it is set to discard only statement, then the command set by the configuration fixCommand setting will be executed. Otherwise, the code inside the macro will be used, unless the program's configuration option forceFixCommand is set. The macro returns true if the Nim code was modified, so the program can save the new version of the Nim code to the file, otherwise false. If fixCommand executed, the macro always returns false. Additionally, the statement can't raise any exception, all must be caught in the code.

fixRule has access to the following variables:

  • astNode - the currently checked Nim code as AST node as pointer. While the pointer can't be changed, the node (and Nim code itself) can be modified.

  • parentNode - the parent AST node of the currently checked Nim code. Same as astNode, the pointer can't be changed, but the Nim code is modifiable.

  • rule - the rule data structure as an object. It contains fields: * options - the list of the rule options entered by the user in the

    configuration file. It is a sequence of strings.

    • parent - if true, the currently checked Nim code is the main AST node of the code to check. Boolean value.
    • fileName - the name of the file which contains the checked Nim code. String value.
    • negation - if true, the rule is configured as a negation (with word not in the configuration file). Boolean value.
    • ruleType - the type of the rule: check, fix, search or count. Enumeration.
    • amount - the amount of results found in the previous iterations of checking the Nim code. Integer value.
    • enabled - if true, the rule is enabled for the currently checked Nim code. Boolean value.
    • fixCommand - the command executed by fix type of the rule. Sets by the user in the configuration file. String value.
    • identsCache - the Nim idents cache needed for some internal rule code. It is recommended to not change it.
    • forceFixCommand - if true, the rule should use fixCommand for fix type of the rule instead of its own code. Sets by the user in the configuration file. Boolean value.
  • data - additional data sent to the fixRule macro, usually via setResult call. String value.

Additional procedures to use in the program's rules' code.

  • getNodesToCheck(parentNode, node: PNode): PNode - get flattened list of AST child nodes of node from parentNode.

The documentation of module rules contains information about all available data structures and subprograms in the program's rules' code.