Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions content/docs/analyzers/LinterCop/LC0100.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
+++
title = 'Global variable could be local'
linkTitle = 'LC0100'

[params]
id = 'LC0100'
severity = 'Hidden'
category = 'Design'
codeAction = false
ignoreObsolete = true
+++

An AL global variable lives as long as its object instance and is visible to every procedure in that object. When only one procedure uses it and replaces its value before every read, that lifetime and visibility serve no purpose. A later edit can otherwise turn an isolated calculation into shared state without changing the declaration.

Declare the variable inside the procedure so its lifetime and visibility match its actual use.

### Example

{{< highlight al "hl_lines=2" >}}
var
MyGlobalVariable: Integer; // Global variable 'MyGlobalVariable' (Integer) is only used in 'ShowValue' and appears to be reinitialized before every read. Consider moving it to local scope. [LC0100]

local procedure ShowValue()
begin
MyGlobalVariable := 1;
Message('%1', MyGlobalVariable);
end;
{{< /highlight >}}

Move the declaration into the procedure:

{{< highlight al "hl_lines=3" >}}
local procedure ShowValue()
var
MyLocalVariable: Integer;
begin
MyLocalVariable := 1;
Message('%1', MyLocalVariable);
end;
{{< /highlight >}}

### When the diagnostic is reported

- The global variable is declared in a normal codeunit.
- Every reference binds to the same global variable and occurs in one procedure or trigger.
- Every reachable read is preceded by an unconditional assignment, `Clear`, or equivalent initialization on that path.
- A normal record field is read after a successful `Record.Get(...)` with key arguments or a whole-record assignment.
- The variable is a value-semantic scalar, label, or normal non-temporary record.

The analysis follows branches and guards. A variable initialized in both sides of an `if` statement qualifies; a variable initialized in only one side does not.

For immutable labels, the message omits the reinitialization wording: `Global variable 'MyLabel' (Label) is only used in 'ShowMessage'. Consider moving it to local scope.`

### Exception

LC0100 deliberately stays silent when object state can affect behavior. This includes variables outside normal codeunits, variables passed through a `var` parameter, recursive paths that read state retained by an inner call, temporary records, handle-like types such as `List`, `SingleInstance` or manual-subscriber codeunits, non-normal codeunit subtypes, `ClearAll()`, integration/business/internal event publishers that expose the sender, integration events that expose global variables, collectible errors, and record operations whose retained view or company context is not fully modeled.

Any non-modeled invocation between initialization and a later read also suppresses the diagnostic. Called code and callback-capable built-ins may reenter the same codeunit instance, so the analysis requires a fresh initialization after such a call.

The rule is disabled by default. Enable it in a ruleset when the project is ready to review localization suggestions.

### See also

- [Discussion #499: Detect global variables which can be moved to a local scope](https://github.com/ALCops/Analyzers/discussions/499)
1 change: 1 addition & 0 deletions content/docs/analyzers/LinterCop/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ The LinterCop is a code linter for AL, comparable to widely used static analysis
| [LC0097](lc0097/) | Avoid mixing exit() and named return variable assignments | Info | — | |
| [LC0098](lc0098/) | Event subscriber name does not match the configured template | Info | ✓ | ✓ |
| [LC0099](lc0099/) | Event subscriber parameter is not referenced | Info | ✓ | ✓ |
| [LC0100](lc0100/) | Global variable could be local | Hidden | | |