diff --git a/Design/Rule0021BuiltInMethodImplementThroughCodeunit.cs b/Design/Rule0021BuiltInMethodImplementThroughCodeunit.cs index 3adc0530..1a3cc68e 100644 --- a/Design/Rule0021BuiltInMethodImplementThroughCodeunit.cs +++ b/Design/Rule0021BuiltInMethodImplementThroughCodeunit.cs @@ -1,18 +1,15 @@ using Microsoft.Dynamics.Nav.CodeAnalysis; using Microsoft.Dynamics.Nav.CodeAnalysis.Diagnostics; -using Microsoft.Dynamics.Nav.CodeAnalysis.Symbols; -using Microsoft.Dynamics.Nav.CodeAnalysis.Syntax; using System.Collections.Immutable; namespace BusinessCentral.LinterCop.Design { [DiagnosticAnalyzer] - public class Rule0021BuiltInMethodImplementThroughCodeunit : DiagnosticAnalyzer + public class BuiltInMethodImplementThroughCodeunit : DiagnosticAnalyzer { public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create( DiagnosticDescriptors.Rule0021ConfirmImplementConfirmManagement, DiagnosticDescriptors.Rule0022GlobalLanguageImplementTranslationHelper, - DiagnosticDescriptors.Rule0027RunPageImplementPageManagement, DiagnosticDescriptors.Rule0000ErrorInRule); public override void Initialize(AnalysisContext context) => context.RegisterOperationAction(new Action(this.CheckBuiltInMethod), OperationKind.InvocationExpression); @@ -25,16 +22,6 @@ private void CheckBuiltInMethod(OperationAnalysisContext ctx) IInvocationExpression operation = (IInvocationExpression)ctx.Operation; if (operation.TargetMethod.MethodKind != MethodKind.BuiltInMethod) return; - if (operation.TargetMethod.ContainingType.GetTypeSymbol().GetNavTypeKindSafe() == NavTypeKind.Page && operation.Arguments.Count() > 1) - { - if (operation.TargetMethod.ReturnValueSymbol.ReturnType.NavTypeKind == NavTypeKind.Action) return; // Page Management Codeunit doesn't support returntype Action - if (operation.Arguments[0].Syntax.GetIdentifierOrLiteralValue() == "0") return; // Allow zero as input for Page.Run(0, ) - if (operation.Arguments[0].Syntax.IsKind(SyntaxKind.IdentifierName)) return; // In case the PageID is set by a field from a (setup) record, do not raise diagnostic - if (operation.TargetMethod.Name.ToUpper() == "ENQUEUEBACKGROUNDTASK") return; // do not execute on CurrPage.EnqueueBackgroundTask - ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0027RunPageImplementPageManagement, ctx.Operation.Syntax.GetLocation())); - return; - } - switch (operation.TargetMethod.Name.ToUpper()) { case "CONFIRM": diff --git a/Design/Rule0027RunPageImplementPageManagement.cs b/Design/Rule0027RunPageImplementPageManagement.cs new file mode 100644 index 00000000..90a1437e --- /dev/null +++ b/Design/Rule0027RunPageImplementPageManagement.cs @@ -0,0 +1,101 @@ +using Microsoft.Dynamics.Nav.CodeAnalysis; +using Microsoft.Dynamics.Nav.CodeAnalysis.Diagnostics; +using Microsoft.Dynamics.Nav.CodeAnalysis.Symbols; +using Microsoft.Dynamics.Nav.CodeAnalysis.Syntax; +using System.Collections.Immutable; + +namespace BusinessCentral.LinterCop.Design +{ + [DiagnosticAnalyzer] + public class Rule0027RunPageImplementPageManagement : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(DiagnosticDescriptors.Rule0027RunPageImplementPageManagement); + + public override void Initialize(AnalysisContext context) => context.RegisterOperationAction(new Action(this.CheckRunPageImplementPageManagement), OperationKind.InvocationExpression); + + private void CheckRunPageImplementPageManagement(OperationAnalysisContext ctx) + { + if (ctx.ContainingSymbol.GetContainingObjectTypeSymbol().IsObsoletePending || ctx.ContainingSymbol.GetContainingObjectTypeSymbol().IsObsoleteRemoved) return; + if (ctx.ContainingSymbol.IsObsoletePending || ctx.ContainingSymbol.IsObsoleteRemoved) 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; + + // do not execute on CurrPage.EnqueueBackgroundTask + if (SemanticFacts.IsSameName(operation.TargetMethod.Name, "EnqueueBackgroundTask")) return; + + // Page Management Codeunit doesn't support returntype Action + if (operation.TargetMethod.ReturnValueSymbol.ReturnType.GetNavTypeKindSafe() == NavTypeKind.Action) return; + + switch (operation.Arguments[0].Syntax.Kind) + { + case SyntaxKind.LiteralExpression: + if (operation.Arguments[0].Syntax.GetIdentifierOrLiteralValue() == "0") + ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0027RunPageImplementPageManagement, ctx.Operation.Syntax.GetLocation())); + break; + + case SyntaxKind.OptionAccessExpression: + if (IsSupportedRecord(((IConversionExpression)operation.Arguments[1].Value).Operand)) + ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0027RunPageImplementPageManagement, ctx.Operation.Syntax.GetLocation())); + break; + + default: + return; + } + } + + 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(); + + if (recordTypeSymbol == null || recordTypeSymbol.Temporary) return false; + + if (GetSupportedRecords().ContainsKey(recordTypeSymbol.Id)) + return SemanticFacts.IsSameName(recordTypeSymbol.Name, GetSupportedRecords()[recordTypeSymbol.Id]); + + return false; + } + + private static Dictionary GetSupportedRecords() + { + Dictionary SupportedRecords = new Dictionary + { + { 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; + } + } +} \ No newline at end of file diff --git a/Design/Rule0031RecordInstanceIsolationLevel.cs b/Design/Rule0031RecordInstanceIsolationLevel.cs new file mode 100644 index 00000000..e18e0d88 --- /dev/null +++ b/Design/Rule0031RecordInstanceIsolationLevel.cs @@ -0,0 +1,31 @@ +using Microsoft.Dynamics.Nav.Analyzers.Common.AppSourceCopConfiguration; +using Microsoft.Dynamics.Nav.CodeAnalysis; +using Microsoft.Dynamics.Nav.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; + +namespace BusinessCentral.LinterCop.Design +{ + [DiagnosticAnalyzer] + public class Rule0031RecordInstanceIsolationLevel : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(DiagnosticDescriptors.Rule0031RecordInstanceIsolationLevel); + + public override void Initialize(AnalysisContext context) => context.RegisterOperationAction(new Action(this.CheckLockTable), OperationKind.InvocationExpression); + + private void CheckLockTable(OperationAnalysisContext ctx) + { + // ReadIsolation is supported from runtime versions 11.0 or greater. + var manifest = AppSourceCopConfigurationProvider.GetManifest(ctx.Compilation); + if (manifest.Runtime < RuntimeVersion.Spring2023) return; + + if (ctx.ContainingSymbol.GetContainingObjectTypeSymbol().IsObsoletePending || ctx.ContainingSymbol.GetContainingObjectTypeSymbol().IsObsoleteRemoved) return; + if (ctx.ContainingSymbol.IsObsoletePending || ctx.ContainingSymbol.IsObsoleteRemoved) return; + + IInvocationExpression operation = (IInvocationExpression)ctx.Operation; + if (operation.TargetMethod.MethodKind != MethodKind.BuiltInMethod) return; + + if (SemanticFacts.IsSameName(operation.TargetMethod.Name, "LockTable")) + ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0031RecordInstanceIsolationLevel, ctx.Operation.Syntax.GetLocation())); + } + } +} \ No newline at end of file diff --git a/Design/Rule0032ClearCodeunitSingleInstance.cs b/Design/Rule0032ClearCodeunitSingleInstance.cs new file mode 100644 index 00000000..9733ba0b --- /dev/null +++ b/Design/Rule0032ClearCodeunitSingleInstance.cs @@ -0,0 +1,86 @@ +using Microsoft.Dynamics.Nav.CodeAnalysis; +using Microsoft.Dynamics.Nav.CodeAnalysis.Diagnostics; +using Microsoft.Dynamics.Nav.CodeAnalysis.Symbols; +using System.Collections.Immutable; + +namespace BusinessCentral.LinterCop.Design +{ + [DiagnosticAnalyzer] + public class Rule0032ClearCodeunitSingleInstance : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(DiagnosticDescriptors.Rule0032ClearCodeunitSingleInstance); + + public override void Initialize(AnalysisContext context) + { + context.RegisterOperationAction(new Action(this.ClearCodeunit), OperationKind.InvocationExpression); + context.RegisterOperationAction(new Action(this.ClearAllCodeunit), OperationKind.InvocationExpression); + } + + private void ClearCodeunit(OperationAnalysisContext ctx) + { + if (ctx.ContainingSymbol.GetContainingObjectTypeSymbol().IsObsoletePending || ctx.ContainingSymbol.GetContainingObjectTypeSymbol().IsObsoleteRemoved) return; + if (ctx.ContainingSymbol.IsObsoletePending || ctx.ContainingSymbol.IsObsoleteRemoved) return; + + IInvocationExpression operation = (IInvocationExpression)ctx.Operation; + if (operation.TargetMethod.MethodKind != MethodKind.BuiltInMethod) return; + + if (!SemanticFacts.IsSameName(operation.TargetMethod.Name, "Clear")) return; + if (operation.Arguments.Count() < 1) return; + + IOperation operand = ((IConversionExpression)operation.Arguments[0].Value).Operand; + if (operand.GetSymbol().GetTypeSymbol().GetNavTypeKindSafe() != NavTypeKind.Codeunit) return; + + if (IsSingleInstanceCodeunitWithGlobalVars((ICodeunitTypeSymbol)operand.GetSymbol().GetTypeSymbol())) + ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0032ClearCodeunitSingleInstance, ctx.Operation.Syntax.GetLocation(), new Object[] { operand.GetSymbol().Name, operand.GetSymbol().GetTypeSymbol().Name })); + } + + private void ClearAllCodeunit(OperationAnalysisContext ctx) + { + if (ctx.ContainingSymbol.GetContainingObjectTypeSymbol().IsObsoletePending || ctx.ContainingSymbol.GetContainingObjectTypeSymbol().IsObsoleteRemoved) return; + if (ctx.ContainingSymbol.IsObsoletePending || ctx.ContainingSymbol.IsObsoleteRemoved) return; + + if (ctx.ContainingSymbol.GetContainingObjectTypeSymbol().GetNavTypeKindSafe() != NavTypeKind.Codeunit) return; + + IInvocationExpression operation = (IInvocationExpression)ctx.Operation; + if (operation.TargetMethod.MethodKind != MethodKind.BuiltInMethod) return; + if (!SemanticFacts.IsSameName(operation.TargetMethod.Name, "ClearAll")) return; + + IEnumerable localVariables = ((IMethodSymbol)ctx.ContainingSymbol.OriginalDefinition).LocalVariables + .Where(var => var.OriginalDefinition.GetTypeSymbol().GetNavTypeKindSafe() == NavTypeKind.Codeunit) + .Where(var => var.OriginalDefinition.GetTypeSymbol().OriginalDefinition != ctx.ContainingSymbol.GetContainingObjectTypeSymbol().OriginalDefinition); + IEnumerable globalVariables = ctx.ContainingSymbol.GetContainingObjectTypeSymbol() + .GetMembers() + .Where(members => members.Kind == SymbolKind.GlobalVariable) + .Where(var => var.OriginalDefinition.GetTypeSymbol().GetNavTypeKindSafe() == NavTypeKind.Codeunit) + .Where(var => var.OriginalDefinition.GetTypeSymbol().OriginalDefinition != ctx.ContainingSymbol.GetContainingObjectTypeSymbol().OriginalDefinition); + + if (HasSingleInstanceCodeunitWithGlobalVars(localVariables, out ISymbol codeunit) || HasSingleInstanceCodeunitWithGlobalVars(globalVariables, out codeunit)) + ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0032ClearCodeunitSingleInstance, ctx.Operation.Syntax.GetLocation(), new Object[] { codeunit.Name, codeunit.GetTypeSymbol().Name })); + } + + private static bool HasSingleInstanceCodeunitWithGlobalVars(IEnumerable variables, out ISymbol codeunit) + { + foreach (ISymbol variable in variables.Where(var => var.OriginalDefinition.ContainingType.GetNavTypeKindSafe() == NavTypeKind.Codeunit)) + if (IsSingleInstanceCodeunitWithGlobalVars((ICodeunitTypeSymbol)variable.OriginalDefinition.GetTypeSymbol())) + { + codeunit = variable; + return true; + } + + codeunit = null; + return false; + } + + private static bool IsSingleInstanceCodeunitWithGlobalVars(ICodeunitTypeSymbol codeunitTypeSymbol) + { + IPropertySymbol singleInstanceProperty = codeunitTypeSymbol.GetProperty(PropertyKind.SingleInstance); + if (singleInstanceProperty == null || !(bool)singleInstanceProperty.Value) return false; + + var globalVariables = codeunitTypeSymbol.GetMembers().Where(members => members.Kind == SymbolKind.GlobalVariable); + var globalVariablesNonRecordTypes = globalVariables.Where(vars => vars.GetTypeSymbol().GetNavTypeKindSafe() != NavTypeKind.Record); + + bool globalVariablesExists = globalVariablesNonRecordTypes.Count() != 0; + return globalVariablesExists; + } + } +} \ No newline at end of file diff --git a/LinterCop.ruleset.json b/LinterCop.ruleset.json index 345e7a72..f55b4340 100644 --- a/LinterCop.ruleset.json +++ b/LinterCop.ruleset.json @@ -135,7 +135,7 @@ { "id": "LC0027", "action": "Info", - "justification": "Page.Run(Modal) must be implemented through the Page Management codeunit from the Base Application." + "justification": "Utilize the Page Management codeunit for launching page." }, { "id": "LC0028", @@ -146,6 +146,21 @@ "id": "LC0029", "action": "Info", "justification": "Use CompareDateTime method in Type Helper codeunit for DateTime variable comparisons." + }, + { + "id": "LC0030", + "action": "Info", + "justification": "Set Access property to Internal for Install/Upgrade codeunits." + }, + { + "id": "LC0031", + "action": "Info", + "justification": "Set ReadIsolation property instead of LockTable method." + }, + { + "id": "LC0032", + "action": "Warning", + "justification": "Clear(All) does not affect or change values for global variables in single instance codeunits." } ] } \ No newline at end of file diff --git a/LinterCopAnalyzers.Generated.cs b/LinterCopAnalyzers.Generated.cs index a4ae3feb..4f04db12 100644 --- a/LinterCopAnalyzers.Generated.cs +++ b/LinterCopAnalyzers.Generated.cs @@ -37,5 +37,7 @@ public static class DiagnosticDescriptors public static readonly DiagnosticDescriptor Rule0028CodeNavigabilityOnEventSubscribers = new DiagnosticDescriptor(LinterCopAnalyzers.AnalyzerPrefix + "0028", (LocalizableString)new LocalizableResourceString("Rule0028CodeNavigabilityOnEventSubscribersTitle", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), (LocalizableString)new LocalizableResourceString("Rule0028CodeNavigabilityOnEventSubscribersFormat", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "Design", DiagnosticSeverity.Info, true, (LocalizableString)new LocalizableResourceString("Rule0028CodeNavigabilityOnEventSubscribersDescription", 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"); + public static readonly DiagnosticDescriptor Rule0031RecordInstanceIsolationLevel = new DiagnosticDescriptor(LinterCopAnalyzers.AnalyzerPrefix + "0031", (LocalizableString)new LocalizableResourceString("Rule0031RecordInstanceIsolationLevelTitle", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), (LocalizableString)new LocalizableResourceString("Rule0031RecordInstanceIsolationLevelFormat", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "Design", DiagnosticSeverity.Info, true, (LocalizableString)new LocalizableResourceString("Rule0031RecordInstanceIsolationLevelDescription", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0031"); + public static readonly DiagnosticDescriptor Rule0032ClearCodeunitSingleInstance = new DiagnosticDescriptor(LinterCopAnalyzers.AnalyzerPrefix + "0032", (LocalizableString)new LocalizableResourceString("Rule0032ClearCodeunitSingleInstanceTitle", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), (LocalizableString)new LocalizableResourceString("Rule0032ClearCodeunitSingleInstanceFormat", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "Design", DiagnosticSeverity.Warning, true, (LocalizableString)new LocalizableResourceString("Rule0032ClearCodeunitSingleInstanceDescription", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0032"); } } \ No newline at end of file diff --git a/LinterCopAnalyzers.resx b/LinterCopAnalyzers.resx index 6e7db730..d3232fdb 100644 --- a/LinterCopAnalyzers.resx +++ b/LinterCopAnalyzers.resx @@ -119,295 +119,204 @@ LC - + + + There was an error in a Linter Rule + + + There was an Error in Rule "{0}" of type "{1}" {2} + + + There was an error in a Linter Rule FlowFields should not be editable. - FlowFields should not be editable. - FlowFields should not be editable. - Commit() needs a comment to justify its existence. Either a leading or a trailing comment. - Commit() needs a comment to justify its existence. Either a leading or a trailing comment. - Commit() needs a comment to justify its existence. Either a leading or a trailing comment. - Do not use an Object ID for properties or variables declaration. - Do not use an Object ID for properties or variables declaration. Use {1} instead. - Do not use an Object ID for properties or variables declaration. - Property "LookupPageID" and "DrilldownPageID" must be filled in table because it is used in list page - Property "LookupPageID" and "DrilldownPageID" must be filled in table "{1}" because it is used in page "{2}" (list) - Property "LookupPageID" and "DrilldownPageID" must be filled in table because it is used in list page - Wrong casing detected! - Wrong casing detected! Use {0} instead. - Wrong casing detected! - AutoIncrement fields are not possible in temporary tables! - AutoIncrement fields are not possible in temporary tables! - AutoIncrement fields are not possible in temporary tables! - DataPerCompany is missing! - DataPerCompany is missing! - DataPerCompany is missing! - Filter operators should not be used in SetRange. Use SetFilter instead. - Filter operators should not be used in SetRange. Use SetFilter instead. - Filter operators should not be used in SetRange. Use SetFilter instead. - Cyclomatic complexity and Maintainability index - Cyclomatic complexity: {0}/({1}), Maintainability index: {2}/({3}) - Cyclomatic complexity and Maintainability index - Access property is missing! - Access property is missing! - Access property is missing! - Wrong Parameter detected. - Wrong Parameter detected. Select the correct object with "{0}::" instead. - Wrong Parameter detected. - NotBlank should be set explicitly for tables with a single-field primary key. - - + NotBlank should be set explicitly for tables with a single-field primary key. - - + NotBlank should be set explicitly for tables with a single-field primary key. - The Caption of permissionset objects should not exceed the maximum length. - The Caption of permissionset objects should not exceed {0} characters. Use MaxLength={0} or Locked=true to ensure there are no translations that exceed this limit. - The Caption of permissionset objects should not exceed the maximum length. - All application objects should be covered by at least one permission set in the extension. - The application object {0} "{1}" is not covered by any permission set in the extension. - All application objects should be covered by at least one permission set in the extension. - Caption is missing. - Caption is missing. - Caption is missing. - - + Writing to a FlowField is not common. Add a comment to explain this. - - + Writing to a FlowField is not common. Add a comment to explain this. - Writing to a FlowField is not common. Add a comment to explain this. - - - - There was an error in a Linter Rule - - - - There was an error in a Linter Rule - - - - There was an Error in Rule "{0}" of type "{1}" {2} - Events in internal codeunits are not accessible to extensions and should therefore be avoided. - The event {0} is declared in an internal codeunit {1}. Avoid events in internal codeunits as they are not accessible to extensions. - Events in internal codeunits are not accessible to extensions and should therefore be avoided. - - - - Data Classification is equal to the Table. Remove to reduce redundancy. - Data Classification is equal to the Table. Remove to reduce redundancy. - Data Classification is equal to the Table. Remove to reduce redundancy. - - + + Data Classification is equal to the Table. Remove to reduce redundancy. + + Applicatication Area is equal to the Page. Remove to reduce redundancy. - Applicatication Area is equal to the Page. Remove to reduce redundancy. - - + Applicatication Area is equal to the Page. Remove to reduce redundancy. - Confirm() must be implemented through the "Confirm Management" codeunit from the System Application. - - - - Fieldgroup "{0}" is missing on table "{1}". - - + Refactor to Confirm Management - GlobalLanguage() must be implemented through the "Translation Helper" codeunit from the Base Application. - GlobalLanguage() must be implemented through the "Translation Helper" codeunit from the Base Application. - GlobalLanguage() must be implemented through the "Translation Helper" codeunit from the Base Application. - - - - Event subscriber arguments now use identifier syntax instead of string literals. - - - - Event subscriber arguments now use identifier syntax instead of string literals. - - - - Event subscriber arguments now use identifier syntax instead of string literals. - - - - Use CompareDateTime method in Type Helper codeunit for DateTime variable comparisons. - - - - Use CompareDateTime method in Type Helper codeunit for DateTime variable comparisons. - - - Use CompareDateTime method in Type Helper codeunit for DateTime variable comparisons. - + + Fieldgroup "{0}" is missing on table "{1}". Procedure declaration should not end with semicolon. @@ -437,15 +346,51 @@ ToolTip must end with a dot. - Page.Run(Modal) must be implemented through the "Page Management" codeunit from the Base Application. + Utilize the "Page Management" codeunit for launching page. - + + Event subscriber arguments now use identifier syntax instead of string literals. + + + Event subscriber arguments now use identifier syntax instead of string literals. + + + Event subscriber arguments now use identifier syntax instead of string literals. + + + Use CompareDateTime method in Type Helper codeunit for DateTime variable comparisons. + + + Use CompareDateTime method in Type Helper codeunit for DateTime variable comparisons. + + + Use CompareDateTime method in Type Helper codeunit for DateTime variable comparisons. + + Set Access property to Internal for Install/Upgrade codeunits. Set Access property to Internal for Install/Upgrade codeunits. - + Set Access property to Internal for Install/Upgrade codeunits. + + Set ReadIsolation property instead of LockTable method. + + + Set ReadIsolation property instead of LockTable method. + + + Set ReadIsolation property instead of LockTable method. + + + Clear(All) does not affect or change values for global variables in '{0}: Codeunit {1}'. + + + Clear(All) does not affect or change values for global variables in '{0}: Codeunit {1}'. + + + Clear(All) does not affect or change values for global variables in '{0}: Codeunit {1}'. + \ No newline at end of file diff --git a/README.md b/README.md index 432bdca6..5b7b41e5 100644 --- a/README.md +++ b/README.md @@ -68,9 +68,12 @@ Further note that you should have BcContainerHelper version 2.0.16 (or newer) in |[LC0024](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0024)|Procedure declaration should not end with semicolon.|Info| |[LC0025](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0025)|Procedure must be either local or internal.|Info| |[LC0026](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0026)|ToolTip must end with a dot.|Info| -|[LC0027](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0027)|`Page.Run()` and `Page.RunModal()` must be implemented through the `Page Management` codeunit from the Base Application.|Info| +|[LC0027](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0027)|Utilize the `Page Management` codeunit for launching page.|Info| |[LC0028](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0028)|Event subscriber arguments now use identifier syntax instead of string literals.|Info| |[LC0029](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0029)|Use `CompareDateTime` method in `Type Helper` codeunit for DateTime variable comparisons.|Info| +|[LC0030](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0030)|Set Access property to Internal for Install/Upgrade codeunits.|Info| +|[LC0031](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0031)|Set ReadIsolation property instead of LockTable method.|Info| +|[LC0032](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0032)|Clear(All) does not affect or change values for global variables in single instance codeunits.|Warning| ## Configuration