Skip to content

fix(PC0013): ignore static built-in calls when resolving a bare record receiver - #551

Merged
Arthurvdv merged 4 commits into
release/v1.3.1from
fix/pc0013-static-builtin-receiver
Sep 14, 2026
Merged

Arthurvdv merged 4 commits into
release/v1.3.1from
fix/pc0013-static-builtin-receiver

Conversation

@Arthurvdv

@Arthurvdv Arthurvdv commented Sep 14, 2026

Copy link
Copy Markdown
Member

Root cause

IInvocationExpression.Instance returns null whenever TargetMethod.IsStatic is true — not only for bare implicit-self calls inside a table. The shared resolver GetReceiverTableType (introduced in 1.3.0 by the receiver-form audit) treated every null instance as "bare self" and fell back to the containing table. Inside a table, IsolatedStorage.Get(...) therefore resolved to the enclosing table and its 2 or 3 arguments were compared with the primary key — triggering PC0013.

Fix

GetReceiverTableType in OperationExtensions.cs now takes the member operation itself (the IInvocationExpression or IFieldAccess) instead of its Instance, so a caller can no longer hand it the ambiguous null receiver. For an invocation with a null instance it first rejects static targets:

  • net8.0 / net10.0: TargetMethod.IsStatic (available from AL 16.0 / net8.0 SDK)
  • netstandard2.1: TargetMethod.ContainingSymbol is IClassTypeSymbol whose name is not "Table" — static built-ins are declared on language classes (IsolatedStorage, NumberSequence, Dialog, System, …), while record built-ins live on the Table class

All 11 callers across AC, DC, LC, PC and Common pass the operation, which fixes the same latent false positive in:

  • RequiredPermissionDetector (AC0031 / AC0032): Get, Insert, Delete, Next, Rename, Count are in MethodOperationMap
  • UseReturnValueForDatabaseReadMethods (AC0030)
  • WriteToFlowFieldRequiresComment (DC0002)
  • AnalyzeCountMethod (LC0081 / LC0082)
  • ExplicitlySetRunTrigger (LC0040)
  • PossibleOverflowAssigning (PC0022): its Get path had the same exposure
  • RecordGetProcedureArguments (PC0013)
  • SetRangeWithFilterOperators (PC0003)
  • TransferFieldsSchemaCompatibility (PC0021 / PC0020)
  • UseSequentialGuid (PC0029, field-access and Validate paths)

Fixtures

Three NoDiagnostic fixtures for PC0013 (the directly reported rule):

Fixture Scenario
IsolatedStorageGetInTableProcedure.al 3-arg and 2-arg IsolatedStorage.Get + Delete inside a table procedure (issue repro)
IsolatedStorageGetInTableTrigger.al Same inside trigger OnInsert() with a 2-field PK
IsolatedStorageGetInTableExtension.al Same inside a tableextension procedure (gated on AL 13.0)

All three fail before the fix with "Too many arguments provided" at the markers and pass after.

Test results

  • Red (before fix): 3 new NoDiagnostic fixtures fail with PC0013 at markers
  • Green (after fix): RecordGetProcedureArguments — 45/45 pass (including GetBareSelf, GetRecSelf, GetThisSelf); dotnet test ALCops.sln — 1768 pass, 0 fail; three-TFM Release build — all 7 cops build 0 warnings 0 errors; dotnet format --verify-no-changes — clean

Fixes #550

🤖 Generated with Claude Code

Arthurvdv and others added 4 commits September 14, 2026 10:57
…d receiver

IInvocationExpression.Instance is null for static built-in class calls as well as for bare
implicit-self calls, so GetReceiverTableType resolved IsolatedStorage.Get inside a table to the
enclosing table. The resolver now takes the invocation and returns no table for static targets.

Fixes #550

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
IInvocationExpression.Instance is null for static built-in class calls as well as for bare
self calls. Note the two overloads of GetReceiverTableType, the AL 12 class anchor that stands
in for IMethodSymbol.IsStatic, and the regression cause for the fix-false-positive catalog.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The static-target test is only meaningful when the invocation has no receiver operation, so
compute it inside that branch instead of on every invocation the ten callers pass through.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tself

GetReceiverTableType now takes the invocation or field access rather than its Instance, so a
caller can no longer hand it the ambiguous null receiver of a static built-in call. The
static-target check and the receiver fallback live in the one remaining method.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Arthurvdv
Arthurvdv merged commit bc6ae5a into release/v1.3.1 Sep 14, 2026
39 checks passed
@Arthurvdv
Arthurvdv deleted the fix/pc0013-static-builtin-receiver branch September 14, 2026 09:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant