Skip to content

feat(FC0002): check casing of generic type arguments and subtyped object references - #502

Merged
Arthurvdv merged 6 commits into
mainfrom
feat/fc0002-generic-type-arguments
Sep 1, 2026
Merged

feat(FC0002): check casing of generic type arguments and subtyped object references#502
Arthurvdv merged 6 commits into
mainfrom
feat/fc0002-generic-type-arguments

Conversation

@Arthurvdv

@Arthurvdv Arthurvdv commented Sep 1, 2026

Copy link
Copy Markdown
Member

Closes #255

Problem

FC0002 raised no diagnostic for wrong casing inside List of [...] / Dictionary of [...] type declarations:

var
    MyList: List of [INTERFACE "IMYINTERFACE"];
    MyDict: Dictionary of [TEXT, INTERFACE "IMYINTERFACE"];

Two gaps in CasingMismatchIdentifier.WalkNode:

  • GenericNamedDataTypeSyntax (SyntaxKind.GenericDataType) was never pushed onto the walk stack, so its TypeArguments were never visited — TEXT/INTERFACE inside the brackets went unchecked.
  • The SubtypedDataTypeSyntax branch compared only the TypeName; the object name after Record/Codeunit/Interface/… was never compared against the declared symbol — anywhere, including top-level declarations.

Fix

  • Push GenericDataType nodes onto the existing explicit stack (alongside EnumDataType/LabelDataType). ChildNodes() yields only the type-argument nodes, so the outer type name is not double-reported and nested generics recurse naturally.
  • Collect IdentifierNameSyntax object references from subtyped data types and resolve them in a new batched ResolveObjectReferences pass — grouped by (TypeName, name) (separate from the identifiers list to avoid canonical-text cross-contamination with same-named variables), one GetSymbolInfo per group.
  • New EnumProvider.SyntaxKind.GenericDataType member (nameof-based; the kind exists on the netstandard2.1 floor SDK).
  • The existing CasingMismatchCodeFix (CanonicalText + QuoteIdentifierIfNeededWithReflection) applies to the new diagnostics unchanged — covered by new HasFix fixtures, including quote-dropping ("IMYINTERFACE"IMyInterface) and re-quoting ("MY TABLE""My Table").
  • Namespace-qualified references (Record MYPUBLISHER.MYEXTENSION.MYAPPDOMAIN.MYTABLE) are checked too, in a dedicated ResolveQualifiedObjectReferences pass: the object name is compared against symbol.Name, and each namespace part right-aligned against the declared namespace casing via GetContainingNamespaceQualifiedNameWithReflection (all-TFM safe).

Deliberately not checked: object references by numeric ID (Record 18).

Note: object-reference casing is now checked in all subtyped declarations (globals, locals, parameters, return values), not only inside generic brackets — fixing it brackets-only would have left an odd inconsistency. Severity stays Info.

Tests

  • HasDiagnostic/NoDiagnostic fixtures GenericDataType.al, SubtypedObjectReference.al, and NamespacedObjectReference.al (regression-first: each HasDiagnostic case failed before its analyzer change). The Interface-in-generic fixtures are skipped below AL 14.0 (AL0408).
  • HasFix/GenericTypeArgument, HasFix/QuotedObjectReference, and HasFix/QualifiedObjectReference.
  • Full FormattingCop suite: 191/191 passing. CI builds of ALCops.Common and ALCops.FormattingCop green on netstandard2.1/net8.0/net10.0. dotnet format clean.

Companion docs PR in alcops.dev follows.

🤖 Generated with Claude Code

Arthurvdv and others added 2 commits September 1, 2026 15:43
…ect references

Walk GenericNamedDataTypeSyntax (List of [...], Dictionary of [...]) type
arguments by pushing the node onto the existing explicit stack. Collect
IdentifierNameSyntax nodes from SubtypedDataTypeSyntax object references
and resolve via GetSymbolInfo in a new ResolveObjectReferences batch pass,
grouped by (TypeName, ObjectName) to avoid cross-contamination with the
identifiers list.

The existing CasingMismatchCodeFix (CanonicalText + QuoteIdentifierIfNeeded)
applies automatically to the new diagnostic locations.

Closes #255

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Arthurvdv and others added 4 commits September 1, 2026 17:19
…low AL 14.0

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the parent-traversal cast and "\0"-separated composite group key
in ResolveObjectReferences with a (TypeName, Node) tuple collected in
WalkNode, grouped by the default tuple comparer like ResolveIdentifiers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Collect QualifiedNameSyntax subtypes into a dedicated batched pass.
GetSymbolInfo on the qualified name resolves via the SDK's
GetSymbolFromObjectReference QualifiedName case; the object name is
compared against symbol.Name and namespace parts right-aligned against
GetContainingNamespaceQualifiedNameWithReflection (all-TFM safe).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Arthurvdv
Arthurvdv merged commit 4ab1542 into main Sep 1, 2026
53 checks passed
@Arthurvdv
Arthurvdv deleted the feat/fc0002-generic-type-arguments branch September 1, 2026 16:19
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.

FC0002: Add support for List/Dictionary

1 participant