1. Describe the bug
CodeCop rule AA0136 (Do not write code that will never be hit) detects unreachable code after a built-in Error(...) call, but not after Record.FieldError(...) or FieldRef.FieldError(...), even though both unconditionally stop the execution of AL code.
The official documentation describes both methods with the same terminal semantics:
Dialog.Error: "Displays an error message and ends the execution of AL code."
Record.FieldError / FieldRef.FieldError: "Stops the execution of the code, causing a run-time error, and creates an error message for a field."
Since FieldError is the idiomatic way to raise a field-specific validation error, dead code following it goes undetected, while the semantically identical Error(...) pattern is flagged.
2. To Reproduce
Steps to reproduce the behavior:
- Create an AL project with CodeCop enabled (
"al.codeAnalyzers": ["${CodeCop}"]).
- Add the codeunit below and observe the AA0136 diagnostics.
codeunit 50100 "AA0136 FieldError Repro"
{
local procedure DemoUnreachable(SalesLine: Record "Sales Line")
begin
SalesLine.FieldError(Type);
Message('Unreachable'); // NOT flagged by AA0136 - but this code can never run
Error('Stop');
Message('Unreachable'); // IS flagged by AA0136
end;
}
3. Expected behavior
AA0136 is reported on both Message(...) statements: a statement directly following an unconditional FieldError(...) call can never be hit, exactly like one following Error(...).
4. Actual behavior
AA0136 is reported only on the statement following Error(...). The statement following SalesLine.FieldError(Type) produces no diagnostic.
Additional analysis: the rule's unreachable-after-throw detection matches an expression statement whose target method is named error and whose containing symbol is a built-in language class symbol. That second condition already holds for FieldError ΓÇö its containers (the Table and FieldRef language classes) are built-in class symbols, exactly like Dialog for Error. Only the literal name comparison excludes it, so extending the check to also accept FieldError (all four overloads: Record.FieldError(Field[, Text]), Record.FieldError(Field, ErrorInfo), FieldRef.FieldError([Text]), FieldRef.FieldError(ErrorInfo)) would be consistent with the existing design. TestField should not be added, as it only errors conditionally.
Found while investigating the inverse gap in a third-party analyzer: ALCops/Analyzers#463.
5. Versions:
- AL Language: 18.0.2668733
- Visual Studio Code: 1.134.0 (commit 110a328ea54b42367b803ec53ee0bf52ef26b419)
- Business Central: N/A (compile-time CodeCop diagnostic; no server involved)
- List of Visual Studio Code extensions that you have installed: AL Language extension only (all other extensions disabled while reproducing)
- Operating System:
Final Checklist
Please remember to do the following:
Generated with Claude Fable 5
Internal work item: AB#647929
1. Describe the bug
CodeCop rule AA0136 (Do not write code that will never be hit) detects unreachable code after a built-in
Error(...)call, but not afterRecord.FieldError(...)orFieldRef.FieldError(...), even though both unconditionally stop the execution of AL code.The official documentation describes both methods with the same terminal semantics:
Dialog.Error: "Displays an error message and ends the execution of AL code."Record.FieldError/FieldRef.FieldError: "Stops the execution of the code, causing a run-time error, and creates an error message for a field."Since
FieldErroris the idiomatic way to raise a field-specific validation error, dead code following it goes undetected, while the semantically identicalError(...)pattern is flagged.2. To Reproduce
Steps to reproduce the behavior:
"al.codeAnalyzers": ["${CodeCop}"]).3. Expected behavior
AA0136 is reported on both
Message(...)statements: a statement directly following an unconditionalFieldError(...)call can never be hit, exactly like one followingError(...).4. Actual behavior
AA0136 is reported only on the statement following
Error(...). The statement followingSalesLine.FieldError(Type)produces no diagnostic.Additional analysis: the rule's unreachable-after-throw detection matches an expression statement whose target method is named
errorand whose containing symbol is a built-in language class symbol. That second condition already holds forFieldErrorΓÇö its containers (theTableandFieldReflanguage classes) are built-in class symbols, exactly likeDialogforError. Only the literal name comparison excludes it, so extending the check to also acceptFieldError(all four overloads:Record.FieldError(Field[, Text]),Record.FieldError(Field, ErrorInfo),FieldRef.FieldError([Text]),FieldRef.FieldError(ErrorInfo)) would be consistent with the existing design.TestFieldshould not be added, as it only errors conditionally.Found while investigating the inverse gap in a third-party analyzer: ALCops/Analyzers#463.
5. Versions:
Final Checklist
Please remember to do the following:
Search the issue repository to ensure you are reporting a new issue
Reproduce the issue after disabling all extensions except the AL Language extension
Simplify your code around the issue to better isolate the problem
Generated with Claude Fable 5
Internal work item: AB#647929