Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve InvalidCastException #695

Merged
merged 2 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,46 @@ namespace BusinessCentral.LinterCop.Design
[DiagnosticAnalyzer]
public class Rule0027RunPageImplementPageManagement : DiagnosticAnalyzer
{
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create<DiagnosticDescriptor>(DiagnosticDescriptors.Rule0027RunPageImplementPageManagement, DiagnosticDescriptors.Rule0000ErrorInRule);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create<DiagnosticDescriptor>(DiagnosticDescriptors.Rule0027RunPageImplementPageManagement);

public override void Initialize(AnalysisContext context) => context.RegisterOperationAction(new Action<OperationAnalysisContext>(this.CheckRunPageImplementPageManagement), OperationKind.InvocationExpression);
private static readonly Dictionary<int, string> _supportedRecords = new Dictionary<int, string>
{
{ 36, "Sales Header" },
{ 38, "Purchase Header" },
{ 79, "Company Information" },
{ 80, "Gen. Journal Template" },
{ 81, "Gen. Journal Line" },
{ 91, "User Setup" },
{ 98, "General Ledger Setup" },
{ 112, "Sales Invoice Header" },
{ 131, "Incoming Documents Setup" },
{ 207, "Res. Journal Line" },
{ 210, "Job Journal Line" },
{ 232, "Gen. Journal Batch" },
{ 312, "Purchases & Payables Setup" },
{ 454, "Approval Entry" },
{ 843, "Cash Flow Setup" },
{ 1251, "Text-to-Account Mapping" },
{ 1275, "Doc. Exch. Service Setup" },
{ 5107, "Sales Header Archive" },
{ 5109, "Purchase Header Archive" },
{ 5200, "Employee" },
{ 5405, "Production Order" },
{ 5900, "Service Header" },
{ 5965, "Service Contract Header" },
{ 7152, "Item Analysis View" },
{ 2000000120, "User" }
};

public override void Initialize(AnalysisContext context)
=> context.RegisterOperationAction(new Action<OperationAnalysisContext>(this.CheckRunPageImplementPageManagement), OperationKind.InvocationExpression);

private void CheckRunPageImplementPageManagement(OperationAnalysisContext ctx)
{
if (ctx.IsObsoletePendingOrRemoved()) return;

IInvocationExpression operation = (IInvocationExpression)ctx.Operation;
if (operation.TargetMethod.MethodKind != MethodKind.BuiltInMethod) return;

if (operation.TargetMethod.ContainingType.GetTypeSymbol().GetNavTypeKindSafe() != NavTypeKind.Page) return;
if (operation.Arguments.Count() < 2) return;

Expand All @@ -38,15 +67,8 @@ private void CheckRunPageImplementPageManagement(OperationAnalysisContext ctx)
break;

case SyntaxKind.OptionAccessExpression:
try // Investigate https://github.com/StefanMaron/BusinessCentral.LinterCop/issues/682
{
if (IsSupportedRecord(((IConversionExpression)operation.Arguments[1].Value).Operand))
ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0027RunPageImplementPageManagement, ctx.Operation.Syntax.GetLocation()));
}
catch
{
ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0000ErrorInRule, ctx.Operation.Syntax.GetLocation(), new Object[] { "Rule0027", "IsSupportedRecord", "" }));
}
if (IsSupportedRecord(((IConversionExpression)operation.Arguments[1].Value).Operand))
ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0027RunPageImplementPageManagement, ctx.Operation.Syntax.GetLocation()));
break;

default:
Expand All @@ -57,52 +79,37 @@ private void CheckRunPageImplementPageManagement(OperationAnalysisContext ctx)
private static bool IsSupportedRecord(IOperation operation)
{
IRecordTypeSymbol recordTypeSymbol = null;

if (operation.Kind == OperationKind.GlobalReferenceExpression || operation.Kind == OperationKind.LocalReferenceExpression)
recordTypeSymbol = (IRecordTypeSymbol)operation.GetSymbol().GetTypeSymbol();

if (operation.Kind == OperationKind.InvocationExpression)
recordTypeSymbol = (IRecordTypeSymbol)operation.Type.GetTypeSymbol();
switch (operation.Kind)
{
case OperationKind.GlobalReferenceExpression:
case OperationKind.LocalReferenceExpression:
recordTypeSymbol = operation.GetSymbol().GetTypeSymbol() as IRecordTypeSymbol;
break;
case OperationKind.InvocationExpression:
recordTypeSymbol = operation.Type.GetTypeSymbol() as IRecordTypeSymbol;
break;
default:
return false;
}

if (recordTypeSymbol == null || recordTypeSymbol.Temporary) return false;

if (GetSupportedRecords().ContainsKey(recordTypeSymbol.Id))
return SemanticFacts.IsSameName(recordTypeSymbol.Name, GetSupportedRecords()[recordTypeSymbol.Id]);
if (_supportedRecords.ContainsKey(recordTypeSymbol.Id))
return SemanticFacts.IsSameName(recordTypeSymbol.Name, _supportedRecords[recordTypeSymbol.Id]);

return false;
}

private static Dictionary<int, string> GetSupportedRecords()
public static class DiagnosticDescriptors
{
Dictionary<int, string> SupportedRecords = new Dictionary<int, string>
{
{ 36, "Sales Header" },
{ 38, "Purchase Header" },
{ 79, "Company Information" },
{ 80, "Gen. Journal Template" },
{ 81, "Gen. Journal Line" },
{ 91, "User Setup" },
{ 98, "General Ledger Setup" },
{ 112, "Sales Invoice Header" },
{ 131, "Incoming Documents Setup" },
{ 207, "Res. Journal Line" },
{ 210, "Job Journal Line" },
{ 232, "Gen. Journal Batch" },
{ 312, "Purchases & Payables Setup" },
{ 454, "Approval Entry" },
{ 843, "Cash Flow Setup" },
{ 1251, "Text-to-Account Mapping" },
{ 1275, "Doc. Exch. Service Setup" },
{ 5107, "Sales Header Archive" },
{ 5109, "Purchase Header Archive" },
{ 5200, "Employee" },
{ 5405, "Production Order" },
{ 5900, "Service Header" },
{ 5965, "Service Contract Header" },
{ 7152, "Item Analysis View" },
{ 2000000120, "User" }
};
return SupportedRecords;
public static readonly DiagnosticDescriptor Rule0027RunPageImplementPageManagement = new(
id: LinterCopAnalyzers.AnalyzerPrefix + "0027",
title: LinterCopAnalyzers.GetLocalizableString("Rule0027RunPageImplementPageManagementTitle"),
messageFormat: LinterCopAnalyzers.GetLocalizableString("Rule0027RunPageImplementPageManagementFormat"),
category: "Design",
defaultSeverity: DiagnosticSeverity.Info, isEnabledByDefault: true,
description: LinterCopAnalyzers.GetLocalizableString("Rule0027RunPageImplementPageManagementDescription"),
helpLinkUri: "https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0027");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ private void AnalyzeRunPageArguments(OperationAnalysisContext ctx)
if (pageSourceTable == null) return;

IOperation operand = ((IConversionExpression)operation.Arguments[1].Value).Operand;
ITableTypeSymbol recordArgument = ((IRecordTypeSymbol)operand.GetSymbol().GetTypeSymbol()).BaseTable;
if (operand.GetSymbol().GetTypeSymbol() is not IRecordTypeSymbol recordTypeSymbol) return;
ITableTypeSymbol recordArgument = recordTypeSymbol.BaseTable;

if (!AreTheSameNavObjects(recordArgument, pageSourceTable))
ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0039ArgumentDifferentTypeThenExpected, ctx.Operation.Syntax.GetLocation(), new object[] { 2, operand.GetSymbol().GetTypeSymbol().ToString(), pageSourceTable.GetNavTypeKindSafe() + pageSourceTable.Name.QuoteIdentifierIfNeeded() }));
Expand Down
1 change: 0 additions & 1 deletion BusinessCentral.LinterCop/LinterCopAnalyzers.Generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ public static class DiagnosticDescriptors
public static readonly DiagnosticDescriptor Rule0024SemicolonAfterMethodOrTriggerDeclaration = new DiagnosticDescriptor(LinterCopAnalyzers.AnalyzerPrefix + "0024", (LocalizableString)new LocalizableResourceString("Rule0024SemicolonAfterMethodOrTriggerDeclarationTitle", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), (LocalizableString)new LocalizableResourceString("Rule0024SemicolonAfterMethodOrTriggerDeclarationFormat", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "Design", DiagnosticSeverity.Info, true, (LocalizableString)new LocalizableResourceString("Rule0024SemicolonAfterMethodOrTriggerDeclarationDescription", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0024");
public static readonly DiagnosticDescriptor Rule0025InternalProcedureModifier = new DiagnosticDescriptor(LinterCopAnalyzers.AnalyzerPrefix + "0025", (LocalizableString)new LocalizableResourceString("Rule0025InternalProcedureModifierTitle", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), (LocalizableString)new LocalizableResourceString("Rule0025InternalProcedureModifierFormat", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "Design", DiagnosticSeverity.Hidden, true, (LocalizableString)new LocalizableResourceString("Rule0025InternalProcedureModifierDescription", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0025");
public static readonly DiagnosticDescriptor Rule0026ToolTipMustEndWithDot = new DiagnosticDescriptor(LinterCopAnalyzers.AnalyzerPrefix + "0026", (LocalizableString)new LocalizableResourceString("Rule0026ToolTipMustEndWithDotTitle", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), (LocalizableString)new LocalizableResourceString("Rule0026ToolTipMustEndWithDotFormat", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "Design", DiagnosticSeverity.Info, true, (LocalizableString)new LocalizableResourceString("Rule0026ToolTipMustEndWithDotDescription", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0026");
public static readonly DiagnosticDescriptor Rule0027RunPageImplementPageManagement = new DiagnosticDescriptor(LinterCopAnalyzers.AnalyzerPrefix + "0027", (LocalizableString)new LocalizableResourceString("Rule0027RunPageImplementPageManagement", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), (LocalizableString)new LocalizableResourceString("Rule0027RunPageImplementPageManagement", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "Design", DiagnosticSeverity.Info, true, (LocalizableString)new LocalizableResourceString("Rule0027RunPageImplementPageManagement", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0027");
public static readonly DiagnosticDescriptor Rule0028IdentifiersInEventSubscribers = new DiagnosticDescriptor(LinterCopAnalyzers.AnalyzerPrefix + "0028", (LocalizableString)new LocalizableResourceString("Rule0028IdentifiersInEventSubscribersTitle", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), (LocalizableString)new LocalizableResourceString("Rule0028IdentifiersInEventSubscribersFormat", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "Design", DiagnosticSeverity.Info, true, (LocalizableString)new LocalizableResourceString("Rule0028IdentifiersInEventSubscribersDescription", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0028");
public static readonly DiagnosticDescriptor Rule0029CompareDateTimeThroughCodeunit = new DiagnosticDescriptor(LinterCopAnalyzers.AnalyzerPrefix + "0029", (LocalizableString)new LocalizableResourceString("Rule0029CompareDateTimeThroughCodeunitTitle", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), (LocalizableString)new LocalizableResourceString("Rule0029CompareDateTimeThroughCodeunitFormat", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "Design", DiagnosticSeverity.Info, true, (LocalizableString)new LocalizableResourceString("Rule0029CompareDateTimeThroughCodeunitDescription", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0029");
public static readonly DiagnosticDescriptor Rule0030AccessInternalForInstallAndUpgradeCodeunits = new DiagnosticDescriptor(LinterCopAnalyzers.AnalyzerPrefix + "0030", (LocalizableString)new LocalizableResourceString("Rule0030AccessInternalForInstallAndUpgradeCodeunitsTitle", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), (LocalizableString)new LocalizableResourceString("Rule0030AccessInternalForInstallAndUpgradeCodeunitsFormat", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "Design", DiagnosticSeverity.Info, true, (LocalizableString)new LocalizableResourceString("Rule0030AccessInternalForInstallAndUpgradeCodeunitsDescription", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0030");
Expand Down
8 changes: 7 additions & 1 deletion BusinessCentral.LinterCop/LinterCopAnalyzers.resx
Original file line number Diff line number Diff line change
Expand Up @@ -345,9 +345,15 @@
<data name="Rule0026ToolTipMustEndWithDotTitle" xml:space="preserve">
<value>ToolTip must end with a dot.</value>
</data>
<data name="Rule0027RunPageImplementPageManagement" xml:space="preserve">
<data name="Rule0027RunPageImplementPageManagementTitle" xml:space="preserve">
<value>Utilize Page Management codeunit for launching page.</value>
</data>
<data name="Rule0027RunPageImplementPageManagementFormat" xml:space="preserve">
<value>Utilize the "Page Management" codeunit for launching page.</value>
</data>
<data name="Rule0027RunPageImplementPageManagementDescription" xml:space="preserve">
<value>Utilizing the Page Management codeunit rather than invoking Page.Run directly.</value>
</data>
<data name="Rule0028IdentifiersInEventSubscribersDescription" xml:space="preserve">
<value>Event subscriber arguments now use identifier syntax instead of string literals.</value>
</data>
Expand Down