Skip to content

Names, scope and unused declarations

These rules find hidden dependencies and names that are unused or conceal other names. Select them with --rules preset:scope. See linting for configuration and suppressions.

Rule Family Severity Default Automatic fix
suspicious-shadowing suspicious Warning Yes No
unused-declaration suspicious Informational Yes No
unused-generator-binding style Warning No Sometimes
global-variable-in-function style Warning No No

Reports a local name that hides a visible enclosing value binding. The diagnostic identifies both declarations.

int: required = 4;
constraint forall (required in 1..3) (required > 0);

Inside the forall, required means the generator. A reader looking at the outer declaration might expect it to mean the model’s requirement. Choose a name such as k for the generator, or rename the outer declaration to express its role more precisely.

Function parameters, let declarations and generators are checked. A function or predicate parameter may share the name of a top-level declaration, as in predicate p(array[int] of var int: x) for a global x: passing a global into a callable under its own name is common practice, as global-variable-in-function recommends. Names in disjoint scopes and underscore-prefixed names are also exempt. To allow common loop names, configure exact exceptions:

[lint.options.suspicious-shadowing]
ignore-names = ["i", "j"]

Reports top-level declarations that a complete model does not use in its constraints, solve item, output or annotations, even indirectly through other declarations.

int: old_staff_limit = 12;
var 0 .. 10: staff;
constraint staff >= 4;
solve minimize staff;

old_staff_limit has no effect on this model. Check whether it is a leftover or whether a constraint was supposed to use it. Removing it is a modelling decision; Zincite does not delete declarations.

Uses through other declarations count. Explicit and default output also matter, so a variable printed as part of the solution is not unused just because no constraint refers to it. let declarations are checked too. Generator names are left to unused-generator-binding, so each unused generator name is reported once. Underscore-prefixed names are exempt. Fragments without a solve item are skipped; unknown reachable dependencies can prevent an unused claim.

Finds a generator name that is never used by the remaining generators, filters, body or annotations.

array[1 .. 7] of int: daily_capacity = [8 | day in 1 .. 7];

If seven copies of 8 are intended, make the unused name explicit:

array[1 .. 7] of int: daily_capacity = [8 | _ in 1 .. 7];

If capacity should depend on the day, the warning may instead reveal a missing array access in the body. Removing the generator would change the number of elements. Zincite’s conditional Safe fix replaces only the name with _ in supported array, set and indexed comprehensions. Generator-call shorthand and assignment generators receive advice without a fix. Names already starting with _ are exempt.

Reports a user function or predicate that reads a top-level decision variable without receiving it as an argument.

var 0 .. 10: staff;
function var int: shortfall(int: required) = max([0, required - staff]);

The result depends on staff, although the call only mentions required. Passing both values makes the dependency visible and lets the function be reused for another shift:

function var int: shortfall(int: required, var int: available) = max([0, required - available]);

Parameter-valued globals do not trigger this rule. Locals or arguments that happen to share a global’s name are resolved to their actual declaration.