-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #355 from StefanMaron/prerelease
Merge branch 'prerelease' into master
- Loading branch information
Showing
8 changed files
with
301 additions
and
131 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create<DiagnosticDescriptor>(DiagnosticDescriptors.Rule0027RunPageImplementPageManagement); | ||
|
||
public override void Initialize(AnalysisContext context) => context.RegisterOperationAction(new Action<OperationAnalysisContext>(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<int, string> GetSupportedRecords() | ||
{ | ||
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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create<DiagnosticDescriptor>(DiagnosticDescriptors.Rule0031RecordInstanceIsolationLevel); | ||
|
||
public override void Initialize(AnalysisContext context) => context.RegisterOperationAction(new Action<OperationAnalysisContext>(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())); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create<DiagnosticDescriptor>(DiagnosticDescriptors.Rule0032ClearCodeunitSingleInstance); | ||
|
||
public override void Initialize(AnalysisContext context) | ||
{ | ||
context.RegisterOperationAction(new Action<OperationAnalysisContext>(this.ClearCodeunit), OperationKind.InvocationExpression); | ||
context.RegisterOperationAction(new Action<OperationAnalysisContext>(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<ISymbol> 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<ISymbol> 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<ISymbol> 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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.