
________________________________________________________________________

This file is part of Logtalk <https://logtalk.org/>  
SPDX-FileCopyrightText: 1998-2023 Paulo Moura <pmoura@logtalk.org>  
SPDX-License-Identifier: Apache-2.0

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
________________________________________________________________________


`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](../userman/programming.html#programming-flags-lint).
The default values for these flags are defined in the backend Prolog
compiler adapter files and can be overriden 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](../refman/predicates/set_logtalk_flag_2.html)
built-in predicate. For (source file or entity) local scope, use instead
the [set_logtalk_flag/2](../refman/directives/set_logtalk_flag_2.html)
directive.

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 creating a cyclic term
- Goals that are always true or always false
- Trivial goal fails (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 parenthesis 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` and `setof/3` where the goal argument contains singleton variables
- Calls to `findall/3` used to backtrack over all solutions of a goal without collecting them
- Calls to `catch/3` that catch all exceptions
- Calls 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](https://logtalk.org/coding_style_guidelines.html) for
additional explanations.


Extending the linter
--------------------

Experimental support for extending the linter with user-defined warnings is
available using the [logtalk_linter_hook/7](../refman/predicates/logtalk_linter_hook_7.html)
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.
