Skip to content

fix(AC0032): detect this. self-access via the operation tree (#343)#349

Merged
Arthurvdv merged 4 commits into
release/v1.0.0from
fix/ac0032-this-keyword-self-access
Jun 18, 2026
Merged

fix(AC0032): detect this. self-access via the operation tree (#343)#349
Arthurvdv merged 4 commits into
release/v1.0.0from
fix/ac0032-this-keyword-self-access

Conversation

@Arthurvdv

@Arthurvdv Arthurvdv commented Jun 18, 2026

Copy link
Copy Markdown
Member

What

Fixes the AC0032 (unused permissions) false positive from #343, where a table that grants a tabledata permission on itself and accesses itself via the this keyword (this.Get/Insert/Modify) had its self-permission incorrectly flagged as unused.

Root cause

AC0032 resolves table data access at the syntax level and handled only variable receivers. Two self-access forms were missed:

  • this.Modify() — the AL this self-reference keyword; it matched neither the identifier fast path nor the GetSymbolInfo fallback.
  • Modify() — bare implicit self; the implicit-self branch checked containingObject is IRecordTypeSymbol, which is always false because a table object's declared symbol is an ITableTypeSymbol, not an IRecordTypeSymbol.

Changes

  • Resolve any non-identifier receiver (the this self-reference and expression receivers) via the operation tree: SemanticModel.GetOperation(receiver)?.Type. In a table, this binds to the record; non-record objects (e.g. a codeunit, where this is the codeunit instance) yield a non-record type and are ignored.
  • Resolve bare implicit self directly from the containing ITableTypeSymbol.
  • TryGetPermissionForType helper accepts a record type (via OriginalDefinition) or a table type directly.

Why the operation tree (not ThisExpressionSyntax)

The earlier iteration pattern-matched ThisExpressionSyntax behind #if !NETSTANDARD2_1. That type — plus SyntaxKind.ThisExpression and IInstanceReferenceOperation — is absent at the netstandard2.1 compile floor (AL 12.0.13, predating the Fall 2024 this feature), so the fix only reached AL 16.0+ (net8.0/net10.0 binaries). AL 14.0–15.2 ship a netstandard2.0 SDK and run ALCops's netstandard2.1 binary, where the guarded branch was excluded → the false positive persisted there.

IOperation, SemanticModel.GetOperation and IOperation.Type all exist at the floor. Resolving the receiver type off the base IOperation therefore needs no type reference and no #if guard, so the fix now works on AL 14.0+ (every TFM). This mirrors AC0031 (RequiredPermissionDetector), which already resolves this this way and passes on 14.0–15.2.

All four receiver forms now handled

MyTable.Modify(), Rec.Modify(), Modify(), this.Modify().

Tests

  • AC0032 ThisKeywordSelfAccess, ImplicitSelfBareCall (NoDiagnostic); ThisKeywordPartialUnused (HasDiagnostic).
  • AC0031 future-proof ThisKeywordSelfCallInTable (HasDiagnostic) — confirms this. access is detected as requiring a permission.
  • this cases guarded at runtime 14.0 (SkipTestIfVersionIsTooLow).
  • Full ApplicationCop suite: 290 passing locally (net10.0). All three TFMs build clean (netstandard2.1 included).

Docs

  • AC0031 / AC0032 instruction files.
  • analyzer-development guide: "Record method receiver forms" section (resolve this/expression receivers via IOperation.Type, do not reference ThisExpressionSyntax).
  • netstandard21-compatibility guide: ThisExpressionSyntax row rewritten to recommend the operation-tree approach.

Follow-up

Created #348 to audit all other syntax-level analyzers for the same four receiver forms (triage only).

Fixes #343

Arthurvdv and others added 4 commits June 18, 2026 11:23
…self

AC0032 (unused permissions) resolves table data access at the syntax level.
It recognised variable receivers (`MyTable.Modify()`, `Rec.Modify()`) but
missed two self-access forms:

- `this.Modify()` — the AL `this` self-reference keyword (BC 2024 wave 2)
- `Modify()` — bare implicit self

Both made a table's self-permission appear unused, producing a false positive
(issue #343). Root cause: the `this` receiver is a `ThisExpressionSyntax`
(neither the identifier fast path nor the `GetSymbolInfo` fallback resolved it),
and the implicit-self branch checked `containingObject is IRecordTypeSymbol` —
always false, since a table object's symbol is an `ITableTypeSymbol`, not a record.

Changes:
- Resolve `this` via `SemanticModel.GetOperation(thisNode)?.Type` (binds to the
  record type); guarded with `#if !NETSTANDARD2_1` (type absent in that SDK).
- Resolve bare implicit self from the containing `ITableTypeSymbol`.
- New `TryGetSelfPermission` accepts either a record type or a table type.
- Tests: AC0032 `ThisKeywordSelfAccess`, `ImplicitSelfBareCall` (NoDiagnostic),
  `ThisKeywordPartialUnused` (HasDiagnostic); AC0031 future-proof
  `ThisKeywordSelfCallInTable` (HasDiagnostic). `this` cases guarded at runtime 14.0.
- Docs: AC0031/AC0032 instruction files, analyzer-development guide (four receiver
  forms), netstandard21-compatibility guide (`ThisExpressionSyntax`).
- Follow-up audit tracked in #348.

Fixes #343

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The 'this' keyword parses from runtime 14.0, but the SDK only binds a table's
`this` to its backing record type (via GetRecord) from runtime 16.0 (BC 2025
wave 2). On 14.0-15.2, GetOperation(this).Type does not resolve to a record, so
`this.Modify()` self-access is not detected and the NoDiagnostic test fails.

Bump the ThisKeywordSelfAccess guard from 14.0 to 16.0 and document the
limitation in the AC0032 instruction file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… 16.0+

The previous note attributed the 16.0 boundary to an SDK runtime binding
difference. That is wrong: the binder resolves a table's `this` to its record
identically on AL 14.0/15.2/16.0 (verified against the tagged NAV SDK source).

The real cause is a netstandard2.1 compile-time limitation. The public
`ThisExpressionSyntax` type (and `SyntaxKind.ThisExpression` /
`IInstanceReferenceOperation`) is absent from the netstandard2.1 build floor
(AL 12.0.13); these were added in AL 14.0. The `this` branch is therefore
`#if !NETSTANDARD2_1`-excluded with no reflection/operation-tree fallback. AL
14.0-15.2 ship a netstandard2.0 SDK and run ALCops's netstandard2.1 binary, so
`this.` self-access is undetected there and the #343 false positive persists on
those versions; it is fixed only on the net8.0/net10.0 binaries (AL 16.0+).

- Correct the analyzer comment, AC0032 known-limitation, analyzer-development
  guide, and netstandard21-compatibility guide.
- Keep the `ThisKeywordSelfAccess` guard at 16.0 with an accurate reason.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve non-identifier method receivers (the AL `this` self-reference and
expression receivers) through the receiver's IOperation.Type instead of
pattern-matching ThisExpressionSyntax. This mirrors AC0031
(RequiredPermissionDetector) and never references the ThisExpressionSyntax
type, which is absent at the netstandard2.1 compile floor (AL 12.0.13).

Because IOperation, SemanticModel.GetOperation and IOperation.Type all exist
at that floor, the fix works on every TFM and AL version (14.0+), not just the
net8.0/net10.0 builds (AL 16.0+). Removes the prior #if !NETSTANDARD2_1 guard
and the associated known-limitation, and reverts the ThisKeywordSelfAccess
test guard from 16.0 back to 14.0.

Renames TryGetSelfPermission to TryGetPermissionForType to reflect that it now
serves any resolved receiver/self type. Updates the AC0032, analyzer-development
and netstandard21-compatibility instruction files accordingly.

Fixes #343

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Arthurvdv Arthurvdv changed the title fix(AC0032): detect self-access via this keyword and bare implicit self (#343) fix(AC0032): detect this. self-access via the operation tree (#343) Jun 18, 2026
@Arthurvdv Arthurvdv merged commit 739fff7 into release/v1.0.0 Jun 18, 2026
48 checks passed
@Arthurvdv Arthurvdv deleted the fix/ac0032-this-keyword-self-access branch June 18, 2026 10:39
Arthurvdv added a commit that referenced this pull request Jun 18, 2026
fix(AC0032): detect this. self-access via the operation tree (#349)
MODUSCarstenScholling pushed a commit to MODUSCarstenScholling/ALCops-Analyzers that referenced this pull request Jun 19, 2026
) (ALCops#349)

 fix(AC0032): detect self-access via `this` keyword and bare implicit self
 
 AC0032 (unused permissions) resolves table data access at the syntax level.
 It recognised variable receivers (`MyTable.Modify()`, `Rec.Modify()`) but
 missed two self-access forms, both of which made a table's self-permission
 appear unused and produced a false positive (issue ALCops#343):
 
 - `this.Modify()` — the AL `this` self-reference keyword (BC 2024 wave 2)
 - `Modify()` — bare implicit self
 
 Root cause: the `this` receiver matched neither the identifier fast path nor
 the `GetSymbolInfo` fallback, and the implicit-self branch checked
 `containingObject is IRecordTypeSymbol` — always false, since a table object's
 symbol is an `ITableTypeSymbol`, not a record.
 
 Changes:
 - Resolve any non-identifier receiver (the `this` self-reference and expression
   receivers) via the operation tree: `SemanticModel.GetOperation(receiver)?.Type`.
   In a table, `this` binds to the record; non-record objects (e.g. a codeunit,
   where `this` is the instance) yield a non-record type and are ignored. This
   mirrors AC0031 (`RequiredPermissionDetector`) and never references
   `ThisExpressionSyntax`, which is absent at the netstandard2.1 compile floor
   (AL 12.0.13). Because `IOperation`, `GetOperation` and `IOperation.Type` all
   exist at that floor, the fix needs no `#if` guard and works on AL 14.0+ across
   every TFM — not just the net8.0/net10.0 builds (AL 16.0+).
 - Resolve bare implicit self from the containing `ITableTypeSymbol`.
 - `TryGetPermissionForType` accepts either a record type (via `OriginalDefinition`)
   or a table type directly.
 
 Fixes ALCops#343
 
 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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