Skip to content

fix(AC0032): DeleteAll and ModifyAll require Read - #582

Merged
Arthurvdv merged 5 commits into
mainfrom
fix/ac0032-deleteall-modifyall-require-read
Oct 11, 2026
Merged

Arthurvdv merged 5 commits into
mainfrom
fix/ac0032-deleteall-modifyall-require-read

Conversation

@Arthurvdv

Copy link
Copy Markdown
Member

Summary

DeleteAll and ModifyAll need Read in addition to Delete and Modify at runtime, but MethodOperationMap mapped them one-to-one. AC0032 therefore reported the r in rd next to a DeleteAll as unused (and its CodeFix removed it), and AC0031 never reported a missing r.

Runtime probe on a BC sandbox (user with only indirect rimd on a custom table; each scenario codeunit's Permissions was the only grant):

Call i / d / m alone With r
Insert works n/a
Delete(false/true) works (also on a never-read record and one passed in) n/a
Modify(false/true) works (same conditions) n/a
DeleteAll(false/true), filtered or not fails with a Read permission error rd works
ModifyAll(false/true), filtered or not fails with a Read permission error rm works
FindFirst needs r (already modelled)

Design: MethodOperationMap.ImpliesRead(methodName) flags the two methods and RequiredPermissionDetector.TryGetImpliedRead builds the extra Read RequiredPermission. AC0031 reports/checks it next to the primary permission; AC0032 adds it at its single collection point, so the variable-map fast path, symbol-info fallback and bare/implicit-self paths all get it. Insert, Modify and Delete stay single-letter. RecordRef receivers keep their existing handling.

Behaviour change

AC0031 now reports a missing r on objects declaring only d (or only m) while calling DeleteAll (ModifyAll). This includes patterns Microsoft ships in the Base App, e.g. codeunit 382 BankAccStmtLines-Delete (Permissions = TableData "Bank Account Statement Line" = d; with DeleteAll()), which the runtime evidence shows is genuinely missing r. AC0032 stops flagging r next to these calls and still flags i/m.

Companion docs PR: ALCops/alcops.dev#202

Test plan

  • Fixtures committed first and verified failing for the right reason (see commits): AC0032 NoDiagnostic rd+DeleteAll, rm+ModifyAll (filtered/unfiltered, run-trigger true), bare self call in a table; AC0032 HasDiagnostic and HasFix rimd+DeleteAll/ModifyAll keep r; AC0031 HasDiagnostic d+DeleteAll, m+ModifyAll; AC0031 HasFix adds r; AC0031 NoDiagnostic regression for d+Delete, m+Modify, i+Insert.
  • dotnet test ALCops.sln green, dotnet format --verify-no-changes clean, Release all-TFM builds of Common, ApplicationCop, LinterCop, PlatformCop, FormattingCop with ContinuousIntegrationBuild=true, Validate-Rules.ps1 OK.
  • /code-review high, 1 round: no confirmed correctness findings. Justified: (1) two AC0031 diagnostics on one DeleteAll when neither letter is covered, intended and consistent with DataTransfer (one per missing letter; the fixtures pass); (2) syntactic method name on the fast path is a pre-existing imprecision shared with the primary operation; (3)/(4) single helper pair kept for a minimal diff; the "unrelated commits" findings were a wrong base in the review and do not apply (3 commits ahead of origin/main).

Refs #574

🤖 Generated with Claude Code

Arthurvdv and others added 5 commits October 11, 2026 10:35
DeleteAll and ModifyAll need Read in addition to Delete and Modify at runtime.
Add AC0032 NoDiagnostic/HasFix fixtures and AC0031 HasDiagnostic/HasFix fixtures
for the implied Read, plus regression fixtures showing Delete, Modify and Insert
stay single-letter. The implied-read fixtures fail against the current one-to-one map.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
At runtime DeleteAll needs Read plus Delete and ModifyAll needs Read plus Modify;
the single-record Delete, Modify and Insert need only their own permission.
MethodOperationMap mapped them one-to-one, so AC0032 reported the r in rd next to
a DeleteAll as unused (and its CodeFix removed it) and AC0031 never reported a
missing r.

Add MethodOperationMap.ImpliesRead and RequiredPermissionDetector.TryGetImpliedRead,
and have both consumers add the implied Read permission next to the primary one.
AC0032 adds it at the single collection point, so every receiver path (variable
map, symbol-info fallback, implicit self) is covered. RecordRef receivers keep
their existing bailout.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Add the design decision with the runtime evidence to the AC0031 and AC0032 rule
docs and note the permission-only nature of the implied Read in the
RecordMethodClassification guide.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…eses

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Arthurvdv
Arthurvdv force-pushed the fix/ac0032-deleteall-modifyall-require-read branch from 3dfbb3a to 61accd9 Compare October 11, 2026 08:40
@Arthurvdv
Arthurvdv merged commit b076040 into main Oct 11, 2026
46 checks passed
@Arthurvdv
Arthurvdv deleted the fix/ac0032-deleteall-modifyall-require-read branch October 11, 2026 08:50
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