You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
While fixing PC0029 (#544) three false negatives turned out to be gaps in how rules reach a record: a bare-self field write inside a table trigger, a namespaced fully qualified Record variable, and Rec."Field" inside the OnRun trigger of a codeunit with TableNo. Only PC0029 was fixed. Every rule that touches record fields, record methods or user procedures can have the same gaps, and #509 audited only the method receiver axis on 18 rules. This issue asks for a second, broader audit.
The constructs every rule must handle
AL reaches a receiver in four forms, and they apply to fields (IFieldAccess.Instance) and methods (IInvocationExpression.Instance, built-in record methods and user procedures alike):
Form
Example
Bound Instance
Named variable
MyTable."No.", MyTable.Modify(), Helper.MyProc()
variable or parameter reference
Implicit Rec
Rec."No.", Rec.Modify()
the synthesized Rec (see below)
Bare self
"No.", Modify(), MyProc()
null inside tables and tableextensions; a Rec reference elsewhere
this (runtime 14.0+)
this."No.", this.Modify(), this.MyProc()
OperationKind.ThisReference
Rec itself has several origins (verified in the decompiled SDK, Microsoft.Dynamics.Nav.CodeAnalysis):
Object
Rec
Bare access binds as
xRec
this
table, tableextension
synthesized global flagged as the object's own instance (TableObjectMembers, TableExtensionObjectMembers)
Instance == null, triggers and procedures alike
yes
the record type
page, pageextension, requestpage, report, xmlport
synthesized global of the SourceTable (report/xmlport: the request page's) (PageObjectMembers and siblings)
binder inserts Rec: IGlobalReferenceExpression, gated on #pragma implicitwith
yes
the object
codeunit with TableNo, only in trigger OnRun
synthesized local variable of the trigger (SourceMethodOrTriggerSymbol)
ILocalReferenceExpression over that local (InMethodBinder)
no
the codeunit; this."Field" does not compile
report / query dataitem triggers
none
IReportDataItemAccess / query dataitem access
no
the object
TableNo is a codeunit-only property (ObjectParser.GetCodeunitProperties). A namespaced Record Pub.Ext.Domain.Table binds to the same RecordTypeSymbol as the unqualified spelling, but a misspelled namespace yields an error type that still reports NavTypeKind.Record while not being an IRecordTypeSymbol.
Full write-up with SDK file references: .claude/rules/receiver-forms.md (renamed and expanded from record-receiver-forms.md in #544); fixture conventions in .claude/rules/testing.md rule 6 and 7; reviewer rows in REVIEW.md.
What to do
For every rule that reads a record receiver, a field access or a method invocation (in all six cops, analyzers and code fixes):
Add the fixture set from testing.md rule 6: {Scenario}NamedVariable, RecSelf, BareSelf, ThisSelf (gated on 14.0), the InTableExtension variants, {Scenario}OnRunRec for a TableNo codeunit, {Scenario}PageRec where the rule can apply to pages or request pages, and one namespaced fixture with a fully qualified object reference (rule 7). Where a code fix exists, add HasFix cases for bare self and this.
Fix what fails. Prefer GetReceiverTableType (Common) for table resolution; a Rec check must accept both the global and the OnRun local (test IsSynthesized and the name, not SymbolKind.GlobalVariable).
Why
While fixing PC0029 (#544) three false negatives turned out to be gaps in how rules reach a record: a bare-self field write inside a table trigger, a namespaced fully qualified
Recordvariable, andRec."Field"inside theOnRuntrigger of a codeunit withTableNo. Only PC0029 was fixed. Every rule that touches record fields, record methods or user procedures can have the same gaps, and #509 audited only the method receiver axis on 18 rules. This issue asks for a second, broader audit.The constructs every rule must handle
AL reaches a receiver in four forms, and they apply to fields (
IFieldAccess.Instance) and methods (IInvocationExpression.Instance, built-in record methods and user procedures alike):InstanceMyTable."No.",MyTable.Modify(),Helper.MyProc()RecRec."No.",Rec.Modify()Rec(see below)"No.",Modify(),MyProc()Recreference elsewherethis(runtime 14.0+)this."No.",this.Modify(),this.MyProc()OperationKind.ThisReferenceRecitself has several origins (verified in the decompiled SDK,Microsoft.Dynamics.Nav.CodeAnalysis):RecxRecthisTableObjectMembers,TableExtensionObjectMembers)Instance == null, triggers and procedures alikeSourceTable(report/xmlport: the request page's) (PageObjectMembersand siblings)Rec:IGlobalReferenceExpression, gated on#pragma implicitwithTableNo, only intrigger OnRunSourceMethodOrTriggerSymbol)ILocalReferenceExpressionover that local (InMethodBinder)this."Field"does not compileIReportDataItemAccess/ query dataitem accessTableNois a codeunit-only property (ObjectParser.GetCodeunitProperties). A namespacedRecord Pub.Ext.Domain.Tablebinds to the sameRecordTypeSymbolas the unqualified spelling, but a misspelled namespace yields an error type that still reportsNavTypeKind.Recordwhile not being anIRecordTypeSymbol.Full write-up with SDK file references:
.claude/rules/receiver-forms.md(renamed and expanded fromrecord-receiver-forms.mdin #544); fixture conventions in.claude/rules/testing.mdrule 6 and 7; reviewer rows inREVIEW.md.What to do
For every rule that reads a record receiver, a field access or a method invocation (in all six cops, analyzers and code fixes):
testing.mdrule 6:{Scenario}NamedVariable,RecSelf,BareSelf,ThisSelf(gated on 14.0), theInTableExtensionvariants,{Scenario}OnRunRecfor aTableNocodeunit,{Scenario}PageRecwhere the rule can apply to pages or request pages, and one namespaced fixture with a fully qualified object reference (rule 7). Where a code fix exists, addHasFixcases for bare self andthis.GetReceiverTableType(Common) for table resolution; aReccheck must accept both the global and theOnRunlocal (testIsSynthesizedand the name, notSymbolKind.GlobalVariable)..claude/rules/diagnostics/{id}-{slug}.md(fixed / by design / known limitation), as fix: handle all four record-method receiver forms across analyzers (#348) #509 did for the method axis; pin every deliberate non-report with aNoDiagnosticfixture.Related: #509 (method receiver audit), #544 (PC0029 fixes and the guide), and #546 (implicit primary key audit).