feat(pc0038,fc0007,lc0089,lc0090) Improve flow-terminating built-in analysis - #505
Conversation
- Recognize potential named-return assignments through var parameters and receiver calls in conditions, case selectors, and loop expressions. - Handle short-circuit and/or conditions in evaluation order, consider exhaustive enum and option cases in the current compilation, and add regression coverage for case true, TextEncoding, and control-flow paths.
- Add FlowTerminatingBuiltIns to ALCops.Common as the shared semantic classification for AL built-ins that unconditionally end execution. - PC0038 now uses this classifier instead of its local Error/ThrowError name check. It recognizes built-in Error, Record.FieldError, and FieldRef.FieldError calls as terminating paths, while user-defined methods with the same names remain regular calls. - Support invalid editor-time invocations when the binder synthesizes an Error or FieldError target on Dialog, Record, or FieldRef. This prevents PC0038 diagnostics from flickering while an argument is unresolved or a call is still being edited. - Remove the unverified ThrowError special case from PC0038 and cover the new behavior with regression fixtures for: - unnamed and named return values - if/else, guard-clause, and case-else paths - Record.FieldError and FieldRef.FieldError - user-defined FieldError and Error methods - invalid calls with unbound arguments - Document the precise terminator semantics and validate the change with the focused PC0038 suite, the PlatformCop test project, the full solution test suite, and multi-target builds for netstandard2.1, net8.0, and net10.0. Fixes ALCops#463
- Fixes ALCops#468 - Match exact Dialog.Error, Table.FieldError, and FieldRef.FieldError bindings - Add clean and invalid binding regressions and document the collectible ErrorInfo limitation
- Fixes ALCops#463 - Reuse shared terminator classification in FC0007 and LC0089/LC0090 - Preserve CurrReport and CurrXMLport guards and exclude user-defined methods
|
Thanks for this one, the shared
|
Fixes ALCops#471 Track repeat-loop break states, parenthesized conditions, ternary branches, and compile-time enum extension values in PC0038. Expose canonical flow-terminating built-in names for stable FC0007 diagnostics and avoid unnecessary LC0089 semantic binding. Replace proxy-only classifier tests with a binder-driven FieldRef invalid-binding fixture. Update the FC0007 setting schema and PC0038 regression documentation.
# Conflicts: # .claude/skills/fix-false-positive/references/regression-catalog.md
…er runtimes Same-module enumextension declarations are rejected with AL0334 by compilers below 13.0, and the ternary conditional expression only parses from 14.0, so the corresponding fixtures are skipped on older SDK versions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
I’ve taken the liberty of making a small commit to fix the failing tests (version-gate the tests that fail on older AL versions), so we can get this into the main branch. @MODUSCarstenScholling, are you okay with me merging this? |
Resolves the four documentation conflicts against the restructured .claude rules: common-library.md takes main's version (it already describes FlowTerminatingBuiltIns), and the PC0038, LC0089 and FC0007 rule docs are rewritten to the new template while carrying over this branch's content (shared flow-terminator classifier, short-circuit and exhaustive-case handling, collectible Error(ErrorInfo) limitation, error-tolerant fixtures). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
That's great @Arthurvdv |
Summary
This PR strengthens PC0038's return-value flow analysis and establishes a shared, semantic definition of AL calls that terminate control flow.
It fixes the
FieldErrorfalse positive reported in #463, prevents future false negatives caused by overly broad built-in name matching (#468), and migrates the related FC0007 and LC0089/LC0090 flow-aware behavior to the same shared classifier.Issues addressed
Fixes #463
Fixes #468
PC0038: return-value flow analysis
vararguments in conditionscaseselectorswhileandrepeat untilconditionsforstart/end boundsforeachcollection expressionsand/oris always evaluatedxorevaluates both operands and therefore does not use short-circuit handlingcasestatements withoutelsewhen all enum or option values visible in the current compilation are covered.exit(<value>), a bareexit, and a fallthrough path with a definitely assigned named return.ThrowErrorspecial case because it is not part of the verified shared flow-termination contract.Shared flow-terminator classification
Introduces
ALCops.Common.FlowTerminatingBuiltInsas the single semantic classifier for built-in calls that unconditionally terminate normal flow.The classifier accepts only these exact clean bindings:
Dialog.ErrorTable.FieldErrorFieldRef.FieldErrorThe implementation intentionally validates both the built-in method and its containing built-in class. Checking only
MethodKind.BuiltInMethodplus a method name would incorrectly classify a future unrelated built-in namedErrororFieldErroras terminating.For incomplete code in the editor, the classifier supports invalid bindings only when the synthesized receiver has the matching semantic type:
Dialog.ErrorRecord.FieldErrorFieldRef.FieldErrorThis prevents diagnostic flicker while an argument or expression is incomplete, without treating user-defined methods named
ErrororFieldErroras terminators.The implementation uses centralized SDK name constants and direct comparer-based checks. The NAV SDK does not expose a stable public enum or identifier for individual built-in methods; direct comparisons avoid reflection and collection lookup overhead in analyzer hot paths.
FC0007: statement spacing
ErrorOnlyandExitAndErrorclassification toFlowTerminatingBuiltIns.Record.FieldError(...)andFieldRef.FieldError(...)as scope-leaving statements.ErrorandFieldErrorprocedures out of scope-leaving detection.FieldErrorbindings.FieldError().LC0089 / LC0090: cognitive complexity
ErrorandFieldErrorguard-clause detection toFlowTerminatingBuiltIns.Record.FieldError(...)andFieldRef.FieldError(...)guards as complexity-neutral flow simplifications.ErrorandFieldErrorcalls as normal calls that continue to add cognitive complexity.CurrReport.Break(),CurrReport.Skip(), andCurrReport.Quit()CurrXMLportcallsbreakandcontinueCurrReportandCurrXMLportcalls are deliberately not part ofFlowTerminatingBuiltIns: they may be valid guard exits for cognitive complexity, but they are not general procedure terminators.Tests
Adds regression coverage for:
if/else, guard-clause, andcase elsepathsRecord.FieldErrorandFieldRef.FieldErrorErrorandFieldErrorcalls in error-tolerant editor fixturesFieldErrorFieldError()FieldErrorguard clausesErrorandFieldErrorname collisionsCurrReportandCurrXMLportguard behaviorKnown limitation
Error(ErrorInfo)is currently classified as flow-terminating even whenErrorInfo.Collectible = trueand execution occurs in anErrorBehavior::Collectscope. AL can continue after such a call.Correctly distinguishing that behavior requires data-flow analysis of the
ErrorInfovalue and enclosing error-collection context, which is outside the current invocation-level classifier. This limitation is documented in the affected internal rule documentation.