linter
Logtalk provides a built-in linter tool that runs automatically when compiling and loading source files. The lint warnings are controlled by a set of flags. The default values for these flags are defined in the backend Prolog compiler adapter files and can be overridden from a settings file, from a source file (e.g., a loader file), or from an entity. These flags can be set globally using the set_logtalk_flag/2 built-in predicate. For (source file or entity) local scope, use instead the set_logtalk_flag/2 directive.
The linter flags can be managed as a group using the linter meta-flag. See the documentation for details.
Some lint checks are turned off by default, specially when computationally expensive. Still, it’s a good idea to turn them on to check your code on a regular basis (e.g., in CI/CD pipelines).
Note that, in some cases, the linter may generate false warnings due to source code analysis limitations or special cases that, while valid when intended, usually result from programming issues. When a code rewrite is not a sensible solution to avoid the warning, the workaround is to turn off as locally as possible the flag that controls the warning.
Main linter checks
Lint checks include:
Missing directives (including scope, meta-predicate, dynamic, discontiguous, and multifile directives)
Duplicated directives, clauses, and grammar rules
Missing predicates (unknown messages plus calls to non-declared and non-defined predicates)
Calls to declared but not defined static predicates
Non-terminals called as predicates (instead of via the
phrase/2-3
built-in methods)Predicates called as non-terminals (instead of via the
call//1
built-in method)Non-portable predicate calls, predicate options, arithmetic function calls, directives, flags, and flag values
Missing arithmetic functions (with selected backends)
Suspicious calls (syntactically valid calls that are likely semantic errors; e.g. float comparisons using the standard arithmetic comparison operators or comparing numbers using unification)
Deprecated directives, predicates, arithmetic functions, control constructs, and flags
References to unknown entities (objects, protocols, categories, or modules)
Top-level shortcuts used as directives
Unification goals that will succeed without binding any variables
Unification goals that will succeed by creating a cyclic term
Goals that are always true or always false
Trivial goal failures (due to no matching predicate clause)
Redefined built-in predicates
Redefined standard operators
Lambda expression unclassified variables and mixed up variables
Lambda expression with parameter variables used elsewhere in a clause
Singleton variables
If-then-else and soft cut control constructs without an else part
If-then-else and soft cut control constructs where the test is a unification between a variable and a ground term
Missing parentheses around if-then-else and disjunction control constructs in the presence of cuts in the first argument
Cuts in clauses for multifile predicates
Missing cut in repeat loops
Possible non-steadfast predicate definitions
Non-tail recursive predicate definitions
Redundant calls to control constructs and built-in predicates
Calls to all-solutions predicates with existentially qualified variables not occurring in the qualified goal
Calls to all-solutions predicates with no shared variables between template and goal
Calls to
bagof/3
andsetof/3
where the goal argument contains singleton variablesCalls to
findall/3
used to backtrack over all solutions of a goal without collecting themCalls to
catch/3
that catch all exceptionsCalls to standard predicates that have more efficient alternatives
Unsound calls in grammar rules
File, entity, predicate, and variable names not following official coding guidelines
Variable names that differ only on case
Clauses whose body is a disjunction (and that can be rewritten as multiple clauses per coding guidelines)
Naked meta-variables in cut-transparent control constructs
Left-recursion in clauses and grammar rules
Additional lint checks are provided by the lgtunit
, lgtdoc
,
make
, and dead_code_scanner
tools. For large projects, the data
generated by the code_metrics
tool may also be relevant in accessing
code quality and suggesting code refactoring candidates.
Help on linter warnings
By loading the tutor
tool, most lint warnings are expanded with
explanations and suggestions on how to fix the reported issues. See also
the coding
guidelines for
additional explanations.
Extending the linter
Experimental support for extending the linter with user-defined warnings
is available using the
logtalk_linter_hook/7
multifile hook predicate. For example, the format
and list
library objects define this hook predicate to lint calls to the
format/2-3
and append/3
predicates for common errors and
misuses.
Linting Prolog modules
This tool can also be applied to Prolog modules that Logtalk is able to
compile as objects. For example, if the Prolog module file is named
module.pl
, try:
| ?- logtalk_load(module, [source_data(on)]).
Due to the lack of standardization of module systems and the abundance of proprietary extensions, this solution is not expected to work for all cases.
Linting plain Prolog files
This tool can also be applied to plain Prolog code. For example, if the
Prolog file is named code.pl
, simply define an object including its
code:
:- object(code).
:- include('code.pl').
:- end_object.
Save the object to an e.g. code.lgt
file in the same directory as
the Prolog file and then load it:
| ?- logtalk_load(code, [source_data(on)]).
In alternative, use the object_wrapper_hook
provided by the
hook_objects
library:
| ?- logtalk_load(hook_objects(loader)).
...
| ?- logtalk_load(code, [hook(object_wrapper_hook), source_data(on)]).
With either wrapping solution, pay special attention to any compilation warnings that may signal issues that could prevent the plain Prolog from being fully checked when wrapped by an object.