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
The public OperationExtensions.GetSymbol() method in Microsoft.Dynamics.Nav.CodeAnalysis.dll throws an InvalidCastException when called on an IOperation that represents a DATABASE::<ObjectName>, CODEUNIT::<ObjectName>, or similar application object reference expression.
This occurs because two internal bound types (BoundApplicationObjectAccess and BoundObjectAccess) both set ExpressionKind => OperationKind.FieldAccess, but implement IApplicationObjectAccess and IObjectAccess respectively, not IFieldAccess. The GetSymbol() switch statement unconditionally casts FieldAccess operations to IFieldAccess:
// OperationExtensions.cs line 44-45 (decompiled from SDK 16.0.27.57058)OperationKind.FieldAccess=>((IFieldAccess)operation).FieldSymbol,// InvalidCastException here
This is the same class of issue as #7977 (GetSymbolInfo NullReferenceException), where internal SDK types produce unexpected exceptions when accessed through public API methods. As with #7977, this issue surfaces in custom code analyzers. We have not encountered it in any of the CodeCops shipped with Business Central (CodeCop, AppSourceCop, UICop, PerTenantExtensionCop).
2. To Reproduce
Minimal custom analyzer (C#)
usingMicrosoft.Dynamics.Nav.CodeAnalysis;usingMicrosoft.Dynamics.Nav.CodeAnalysis.Diagnostics;usingSystem.Collections.Immutable;namespaceMyCustomCodeAnalyzer;[DiagnosticAnalyzer]publicclassMyDiagnosticAnalyzer:DiagnosticAnalyzer{privatestaticreadonlyDiagnosticDescriptorRule=new(id:"TEST0001",title:"Test rule",messageFormat:"Found symbol: {0}",category:"Test",defaultSeverity:DiagnosticSeverity.Warning,isEnabledByDefault:true);publicoverrideImmutableArray<DiagnosticDescriptor>SupportedDiagnostics{get;}=ImmutableArray.Create(Rule);publicoverridevoidInitialize(AnalysisContextcontext)=>context.RegisterOperationAction(AnalyzeOperation,OperationKind.InvocationExpression);privatestaticvoidAnalyzeOperation(OperationAnalysisContextctx){if(ctx.Operationis not IInvocationExpressioninvocation)return;foreach(varargininvocation.Arguments){// This line throws InvalidCastException when the argument// is a DATABASE::X or CODEUNIT::X expressionvarsymbol=arg.Value.GetSymbol();}}}
AL code that triggers the crash
codeunit50000 "My Codeunit"
{
procedure MyProcedure()var
MyTable: Record "My Table";
begin
MyTable.MyMethod(DATABASE::"My Table");
end;
}
table50000 "My Table"
{
fields
{
field(1; "Entry No."; Integer) { }
}
keys
{
key(PK; "Entry No.") { }
}
procedure MyMethod(SourceType: Integer)beginend;
}
Error message
Analyzer 'MyCustomCodeAnalyzer.MyDiagnosticAnalyzer' threw an exception of type 'System.InvalidCastException' with message 'System.InvalidCastException: Unable to cast object of type 'Microsoft.Dynamics.Nav.CodeAnalysis.BoundApplicationObjectAccess' to type 'Microsoft.Dynamics.Nav.CodeAnalysis.IFieldAccess'.
at Microsoft.Dynamics.Nav.CodeAnalysis.OperationExtensions.GetSymbol(IOperation operation) in X:\source\Prod\Microsoft.Dynamics.Nav.CodeAnalysis\Compilation\OperationExtensions.cs:line 37'
The same crash occurs with CODEUNIT::, XMLPORT::, QUERY::, and REPORT:: expressions, as they all produce BoundApplicationObjectAccess operations internally.
A related but distinct type, BoundObjectAccess (implementing internal IObjectAccess), exhibits the same problem: it also sets ExpressionKind => OperationKind.FieldAccess without implementing IFieldAccess.
3. Expected behavior
GetSymbol() should handle BoundApplicationObjectAccess and BoundObjectAccess gracefully. For example:
Return the ApplicationObjectTypeSymbol for IApplicationObjectAccess operations (the meaningful symbol)
Return the ObjectTypeSymbol for IObjectAccess operations
Or at minimum, return null instead of throwing
A possible fix in OperationExtensions.GetSymbol():
GetSymbol() throws System.InvalidCastException because BoundApplicationObjectAccess reports OperationKind.FieldAccess but does not implement IFieldAccess.
5. Versions
AL Language: 16.0.27.57058 (Microsoft.Dynamics.Nav.CodeAnalysis.dll)
Visual Studio Code: 1.116.0 (system setup), Commit: 560a9dba96f961efea7b1612916f89e5d5d4d679
Business Central: not directly applicable (analyzer-side issue)
Operating System: Windows_NT x64 10.0.20348
Final Checklist
Search the issue repository to ensure you are reporting a new issue
Reproduce the issue after disabling all extensions except the AL Language extension
Simplify your code around the issue to better isolate the problem
Note
This issue was created with help from AI using GitHub Copilot. The analysis, reproduction code, and suggested fix were developed collaboratively.
1. Describe the bug
The public
OperationExtensions.GetSymbol()method inMicrosoft.Dynamics.Nav.CodeAnalysis.dllthrows anInvalidCastExceptionwhen called on anIOperationthat represents aDATABASE::<ObjectName>,CODEUNIT::<ObjectName>, or similar application object reference expression.This occurs because two internal bound types (
BoundApplicationObjectAccessandBoundObjectAccess) both setExpressionKind => OperationKind.FieldAccess, but implementIApplicationObjectAccessandIObjectAccessrespectively, notIFieldAccess. TheGetSymbol()switch statement unconditionally castsFieldAccessoperations toIFieldAccess:This is the same class of issue as #7977 (
GetSymbolInfoNullReferenceException), where internal SDK types produce unexpected exceptions when accessed through public API methods. As with #7977, this issue surfaces in custom code analyzers. We have not encountered it in any of the CodeCops shipped with Business Central (CodeCop, AppSourceCop, UICop, PerTenantExtensionCop).2. To Reproduce
Minimal custom analyzer (C#)
AL code that triggers the crash
Error message
The same crash occurs with
CODEUNIT::,XMLPORT::,QUERY::, andREPORT::expressions, as they all produceBoundApplicationObjectAccessoperations internally.A related but distinct type,
BoundObjectAccess(implementing internalIObjectAccess), exhibits the same problem: it also setsExpressionKind => OperationKind.FieldAccesswithout implementingIFieldAccess.3. Expected behavior
GetSymbol()should handleBoundApplicationObjectAccessandBoundObjectAccessgracefully. For example:ApplicationObjectTypeSymbolforIApplicationObjectAccessoperations (the meaningful symbol)ObjectTypeSymbolforIObjectAccessoperationsnullinstead of throwingA possible fix in
OperationExtensions.GetSymbol():4. Actual behavior
GetSymbol()throwsSystem.InvalidCastExceptionbecauseBoundApplicationObjectAccessreportsOperationKind.FieldAccessbut does not implementIFieldAccess.5. Versions
Final Checklist
Note
This issue was created with help from AI using GitHub Copilot. The analysis, reproduction code, and suggested fix were developed collaboratively.