QLint is a static q/kdb+ code analysis tool. The software detects a wide variety of standard q rules, and provides the capability for users to configure and add rules for their code.
Running the linter
The linter can be run in a number of ways. The linter can be used directly in function editors, on files, or even on entire repositories.
Files are linted by right-clicking on an artifact in the artifact tree, clicking Code, then clicking Lint, then Lint again. This will display the linter output to the console.
Linting will yield different results according to the type of artifact:
|artifact||target of linting|
|repository||applicable files in the repository|
|module||applicable files in the module|
|applicable file||that file|
Multiple files, modules, and repositories may be linted simultaneously by highlighting each of the artifacts and following the above instructions.
It is also possible to lint a file open in an Editor window by picking
Lint from its context menu.
The linter will also be automatically run whenever an artifact is saved and will update the gutter icons. Automatic linting may be toggled off or on by clicking the File > Settings menu item, then Lint on save.
The linter is capable of validating functions, data artifacts, and files with either the .q or .quke extensions.
The default rules fall into three categories, ordered by significance.
|error||significant problems with the code that will very likely lead to runtime or compile errors|
|warning||code may lead to unexpected situations, not necessarily fatal|
|info||problems with the documentation of files|
Any applicable Analyst artifacts that are open in editors will display icons in their left hand gutter to identify any broken linter rules. These icons are updated whenever the linter for a given artifact is run, or when an editor is saved.
The colour of each lint icon corresponds to the significance of each linter category, with
info. Hovering over the lint icon will display a help tip that
provides a description of all the broken rules for the given line and, in quotes, the text
that broke the rule.
The linter tests code for violations of one or more rules from one of the above mentioned categories.
The output of the linter reports broken rules, the code that violated the rule, the location in the code, and any additional messages to clarify the violation.
Each qLint defined rule category (info, warning, and error) is coloured a unique color.
Within the output each category is presented as a series of dropdown lists. Clicking the arrow next to a category will display all the broken linter rules for that category. Clicking the arrow next to each broken rule will display a description of the broken rule along with all of the files (with associated line numbers) where a rule was broken. Clicking on a line number will open the file at the line where the rule was violated.
In the below example, three files were linted, each with one broken linter rule. The error category is closed, while the warning category only shows the list of broken rules. The info category shows the details of each instance of a broken rule including file name and line number.
In the above example the
UNDOCUMENTED_PARAM rule shows only the positions where the rule
was violated and the code that violated the rule. Some rules also report error
messages to provide more detail as to the broken rule.
For example, if a test file with the suffix
.quke is linted, then the details of all
formatting errors will be reported by the linter.
The linter may be configured at workspace, module, and file level. File-level configuration takes precedence over module-level configuration, and module-level configuration takes precedence over workspace-level configuration.
At the workspace level, it is possible to choose which rules should be run by calling
.qlint.ws.setRules with a list of rule labels (q symbols) that should
be used for linting. These labels are the names of the rules to run, for example,
ASSIGN_RESERVED_WORD. This function must be called every time a workspace is loaded
to produce the desired rules.
At the module or repository level, it is possible to configure the rules used when running the
linter using a
UNUSED_PARAM : true UNDOCUMENTED_PARAM : true, error ASSIGN_RESERVED_WORD : false
lint.config file affects only the other artifacts in the module or repository under which
it is located. The first word on each line should be the rule to configure,
followed by a colon and any configurations.
lint.config file specifies which rules should be tested when linting. A rule can be
false to indicate that it should be ignored or to
true to indicate that it
should be included. By default, all rules are included, however setting a rule to
will take precedence if a rule was ommitted when setting the list of rules using
.qlint.ws.setRules. Rules configured at the module level take precedence over rules
configured at the repository level.
It is also possible to change the priority (rule category) af a rule. This will change where the rule is displayed in the output for the file to allow the user to group rules of similar significance together. Changing the priority of the rule allows a user to control how a rule is interpreted if she disagrees with the default priority.
The Analyst workspace provides a dialog to help create configuration files. This is done by right-clicking on a module or repository in the artifact tree, picking Code, then Lint, then Config.
The dialog allows users to set the activation state and priority of the rule. It also provides a description of the rule, which can be found by hovering over the '?'.
Below is a potential result from running the above
lint.config on a set of files. A file
that previously had an
ASSIGN_RESERVED_WORD error now passes because that rule has been
UNDOCUMENTED_PARAM rule now has the priority of an error (as opposed to an info)
since the user has decided that it is a very serious issue to have undocumented parameters.
At the file level, it is possible to configure the linter to either ignore or include specific
rules using the
@qlintinclude tags respectively.
@qlintsuppress tag is a comment tag that can be used to ignore specific rules on that file.
@qlintsuppress tag must be followed by a list of rules labels to ignore.
With the above syntax, the rule will be ignored for the entire file. It is also possible to
ignore rules for only a specified region of the file using the
@qlintsuppress tag. To do so,
the rule label must be followed by a pair of brackets that enclose the number of lines
after the line with the
@qlintsuppress comment to ignore the rule. For example, the comment
//@qlintsuppress UNUSED_PARAM(2) will cause the
UNUSED_PARAM rule to be ignored on the line
with the comment and for two subsequent lines.
It is possible to ignore all rules with a
@qlintsuppress tag with the keyword
all as one of the
rules to ignore.
It is also possible to force certain rules to be run on a file using the
@qlintinclude tag is followed by a list of rules that must be run
on the file, even if they have been ignored at the workspace or module level. In the below
@qlintinclude tag overrules the ignored
ASSIGN_RESERVED_WORD rule due to
@qlintinclude tag only forces inclusion of a rule if it was removed from the set of rules to test.
It does not overrule any change of rule category as set by a
lint.config file. Like
it is possible to force inclusion of all rules using the keyword
all on the same line as
@qlintsuppress, there is no ability to only include a rule for a subsection of a file.
|ASSIGN_RESERVED_WORD||error||Assignment to a reserved word|
|COND_EVENARGS||error||Conditional $ should not be used with an even number of arguments|
|DECLARED_AFTER_USE||error||The variable was declared after being used|
|GLOBAL_PEACH||error||Modifying globals inside a peach statement is not allowed|
|INVALID_ADVERB||error||A binary adverb cannot be applied to a unary function|
|INVALID_ASSIGN||error||Attempt to assign to a string, symbol, or number|
|INVALID_ESCAPE||error||Invalid Escape Sequence: Valid escape sequences are: \n,\r,\t,/,\\,\/ and three digit octal sequences \377 or smaller|
|INVALID_QUKE||error||A quke file was improperly formatted|
|OVERWRITE_ARTIFACT||error||Variable assignment overwrites namespace or artifact|
|STATEMENT_IN_EXPR||error||If, while, or do statement used in expression, possible missing semicolon|
|RESERVED_NAME||error||File has reserved name|
|TOO_MANY_CONSTANTS||error||Too many constants in a function|
|TOO_MANY_GLOBALS||error||Too many globals in a function|
|TOO_MANY_LOCALS||error||Too many locals in a function|
|UNINDENTED_CODE||error||Any multiline expression must be indented after the first line|
|BACKWARD_COMPATIBILITY||warning||This function has backward compatability issues with kdb versions less than 3.6|
|CAST_TYPE_NUMERICAL||warning||Casting using a short to indicate cast type is unnecesarily unclear. Another form is advised|
|CONDITIONALLY_DECLARED||warning||This variable may be undefined at this point, as it was only declared conditionally|
|DEBUG_FUNCTION||warning||Calling a debug function. Likely should not be in release version|
|DEPRECATED_DATETIME||warning||Datetime has been deprecated|
|DEPRECATED_FUNCTION||warning||This file uses a deprecated function|
|EMPTY_IF||warning||If statement lacks code to execute|
|FIXED_SEED||warning||Inputting a positive number into ?0Ng will result in the same seed every run|
|FUNCTION_START||warning||Function artifact must start with a function|
|INSUFFICIENT_INDENT||warning||There should be one tab more indentation on every other line of a function than the first|
|INTERNAL||warning||Reference to an internal api of another module|
|INVALID_FUNCTION||warning||Function artifacts must be lambda definitions, rather than projections, immediately invoked functions, or functions in expressions|
|MALFORMED_SUPPRESSION||warning||Malformed @qlintsuppress tag|
|MISSING_DEPENDENCY||warning||Any reference to another namespace should be listed in the dependency list|
|NAME_COLLISION||warning||Executing statement in editor could overwrite global variable|
|NEED_EXPLICIT_RETURN||warning||Explicit return needed. Otherwise will return generic null|
|POSSIBLE_RETURN||warning||Assignment statement looks like return|
|UNDECLARED_VAR||warning||Undeclared variable in function will be treated as global|
|UNUSED_INTERNAL||warning||This function is marked as internal (is part of a sub-namespace i) but was never used within the namespace|
|UNUSED_PARAM||warning||This param was declared then never used|
|UNUSED_VAR||warning||This variable was declared then never used|
|RANDOM_GUIDS||warning||Multiple calls to ?0ng in quick succession, with negative numbers, can produce the same output|
|UNREACHABLE_CODE||warning||A preceding return prevents this statement from being reached|
|UNEXPECTED_COND_NEWLINE||warning||Condition should begin on same line as loop or if statement|
|UNPARENTHESIZED_JOIN||warning||A potential join in this QSQL statement will be interpreted as separate statements unless wrapped in parentheses|
|VAR_Q_ERROR||warning||Variable name the same as q error message. This can cause ambiguous error messages|
|DEFAULT_QDOC||info||The file has the default documentation|
|INVALID_TYPEDEF||info||Invalid typedef tag|
|INVALID_TAG||info||Tag not recognized as valid qDoc tag|
|MISSING_OVERVIEW||info||Missing @fileOverview tag with associated description|
|MISSING_RETURNS||info||Missing @returns tag|
|MISSING_TYPE||info||Missing type in returns or param tag|
|MULTIPLE_RETURNS||info||Multiple @returns tags|
|OUT_OF_ORDER_PARAM||info||Parameters out of order|
|PARAM_NOT_IN_CODE||info||This param is not in the function|
|QDOC_TYPE||info||Invalid type in tag|
|REDUNDANT_GLOBAL_ASSIGN||info||Using the global ammend operator on a fully qualified name is redundant|