fix(fc0007,pc0038,lc0089,lc0090) Inconsistent handling of AL built-in methods that unconditionally end execution - #464
Conversation
Resolve Error and FieldError calls semantically across PC0038, FC0007, and LC0089/LC0090. This preserves cognitive-complexity increments for user-defined methods with the same names, supports case-insensitive AL method names, and removes the obsolete ThrowError terminator. Add focused regression coverage and document the LC0089/LC0090 design. Fixes ALCops#463
…ail to bind PR ALCops#464 moved guard-clause detection for Error/FieldError from the lexical guardClauseExitCommands set to FlowTerminatingBuiltIns, which requires MethodKind.BuiltInMethod. When an argument does not bind (undefined variable, wrong arity, mid-edit), Binder.CreateBadCall takes the methods.Length != 1 branch for the two-overload built-ins Dialog.Error, Table.FieldError and FieldRef.FieldError and returns an ErrorMethodSymbol with MethodKind.Method. The classifier rejected it, the guard scored +1 and LC0089/LC0090 flickered while typing. IsGuardExpression now runs in four stages: lexical exit commands, a syntactic callee-name pre-filter (only Error/FieldError reach the binder), semantic resolution, and an unbound fallback that keeps the guard unless the call positively binds to a user-defined procedure (DeclaringSyntaxReference != null) or the receiver is typed as a user application object. - Add error-tolerant fixtures proving Error(UndefinedVar) and Rec.FieldError(UndefinedField) stay guards, and Handler.Error(UndefinedVar) on a user codeunit still does not. - Fix stale comment in IsGuardInvocation. - Sync lc0089-cognitive-complexity instructions (design decisions, known issues, roadmap, test coverage). Refs ALCops#464, ALCops#463 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ail to bind PR ALCops#464 moved guard-clause detection for Error/FieldError from the lexical guardClauseExitCommands set to FlowTerminatingBuiltIns, which requires MethodKind.BuiltInMethod. When an argument does not bind (undefined variable, wrong arity, mid-edit), Binder.CreateBadCall takes the methods.Length != 1 branch for the two-overload built-ins Dialog.Error, Table.FieldError and FieldRef.FieldError and returns an ErrorMethodSymbol with MethodKind.Method. The classifier rejected it, the guard scored +1 and LC0089/LC0090 flickered while typing. IsGuardExpression now accepts an invocation target whose name is in FlowTerminatingBuiltIns.MethodNames and has no DeclaringSyntaxReference. Built-ins and the synthesized ErrorMethodSymbol both satisfy this; only user-defined procedures carry a DeclaringSyntaxReference, and since AL has no user overloads a user procedure with bad arguments still binds to its own symbol and stays demoted. - Add error-tolerant fixtures proving Error(UndefinedVar) and Rec.FieldError(UndefinedField) stay guards, and Handler.Error(UndefinedVar) on a user codeunit still does not. - Fix stale comment in IsGuardInvocation. - Sync lc0089-cognitive-complexity instructions (design decisions, known issues, roadmap, test coverage). Refs ALCops#464, ALCops#463 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…anticModel The AL SDK's Compilation.GetSemanticModel is not cached: it returns new SyntaxTreeSemanticModel(this, syntaxTree) on every call (Compilation.cs:957). Calling it once per analyzed code block therefore re-bound each procedure from scratch. CodeBlockAnalysisContext already carries the semantic model for its tree, and the analyzer uses context.SemanticModel elsewhere in the same file. - Drop the Compilation parameter from AnalyzeCognitiveComplexity. - Sync lc0089-cognitive-complexity instructions (design decision, architecture, remove the roadmap entry). Refs ALCops#464, ALCops#463 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Thank you for creating a PR for this. Instead of letting Claude write a review comment I wanted to experiment with letting it create a PR, so it created four (stacked) PR's based on the code review. Would love to hear if this works for you. |
perf(LC0089): use context.SemanticModel instead of Compilation.GetSemanticModel
…n.GetSemanticModel"
…-context-semantic-model Revert "perf(LC0089): use context.SemanticModel instead of Compilation.GetSemanticModel"
|
Thanks for trying this out, Arthur. I’m sorry, but I’m going to close the PR for now. My own review didn’t confirm all of the findings and found gaps, and I also accidentally merged the PRs in the wrong order. Unfortunately, I don’t have enough time at the moment to explore this workflow further, but I appreciate the experiment and the effort you put into it. |
|
I’m sorry, this wasn’t the outcome I was expecting. I’ll reach out to you directly so we can align and make sure we’re on the same page for future PRs. |
|
Looking forward to it. But the only issue here is that directly the first PR was wrong in my opinion. Like an AI suggesting the following 10 steps where step 1 already contains errors and changes all steps after. |
Fixes the inconsistent handling of AL built-in methods that unconditionally end execution.
FlowTerminatingBuiltInsas the shared semantic classifier for built-inErrorandFieldError.Record.FieldErrorandFieldRef.FieldErroras terminating return paths.FieldErrorconsistently withError.ErrororFieldError.FieldRef.FieldError.ThrowErrorentry.Validation
CognitiveComplexity: 18 passednetstandard2.1,net8.0,net10.0): 0 errorsFixes #463