Skip to content

Commit

Permalink
Merge pull request #511 from StefanMaron/prerelease
Browse files Browse the repository at this point in the history
Merge from 'Prerelease' into master
  • Loading branch information
Arthurvdv authored Jan 16, 2024
2 parents 091f1c3 + b9fe905 commit fe8dc2c
Show file tree
Hide file tree
Showing 12 changed files with 429 additions and 99 deletions.
51 changes: 19 additions & 32 deletions Design/Rule0003DoNotUseObjectIDsInVariablesOrProperties.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
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
Expand All @@ -15,21 +16,20 @@ public override void Initialize(AnalysisContext context)
SyntaxKind.ObjectReference,
SyntaxKind.PermissionValue
});

}

private void CheckForObjectIDsInVariablesOrProperties(SyntaxNodeAnalysisContext ctx)
{
if (ctx.ContainingSymbol.IsObsoletePending || ctx.ContainingSymbol.IsObsoleteRemoved) return;
if (ctx.ContainingSymbol.GetContainingObjectTypeSymbol().IsObsoletePending || ctx.ContainingSymbol.GetContainingObjectTypeSymbol().IsObsoleteRemoved) return;

var correctName = "";

string correctName;
if (ctx.ContainingSymbol.Kind == SymbolKind.LocalVariable || ctx.ContainingSymbol.Kind == SymbolKind.GlobalVariable)
{
IVariableSymbol variable = (IVariableSymbol)ctx.ContainingSymbol;
if (variable.Type.NavTypeKind == NavTypeKind.DotNet) return;
if (variable.Type.GetNavTypeKindSafe() == NavTypeKind.DotNet) return;

if (variable.Type.NavTypeKind == NavTypeKind.Array)
if (variable.Type.GetNavTypeKindSafe() == NavTypeKind.Array)
correctName = ((IArrayTypeSymbol)variable.Type).ElementType.Name.ToString();
else
correctName = variable.Type.Name;
Expand All @@ -43,7 +43,6 @@ private void CheckForObjectIDsInVariablesOrProperties(SyntaxNodeAnalysisContext
if (ctx.ContainingSymbol.Kind == SymbolKind.Property)
{
IPropertySymbol property = (IPropertySymbol)ctx.ContainingSymbol;

if (ctx.Node.Kind == SyntaxKind.PermissionValue)
{
var nodes = ctx.Node.ChildNodesAndTokens().GetEnumerator();
Expand All @@ -60,7 +59,6 @@ private void CheckForObjectIDsInVariablesOrProperties(SyntaxNodeAnalysisContext
ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0003DoNotUseObjectIDsInVariablesOrProperties, nodes.Current.GetLocation(), new object[] { "", "the object name" }));
};
}

};
}

Expand All @@ -80,15 +78,12 @@ private void CheckForObjectIDsInVariablesOrProperties(SyntaxNodeAnalysisContext

foreach (IParameterSymbol parameter in method.Parameters)
{
if (parameter.ParameterType.NavTypeKind == NavTypeKind.DotNet)
{
continue;
}
if (parameter.ParameterType.GetNavTypeKindSafe() == NavTypeKind.DotNet) continue;

if (ctx.Node.GetLocation().SourceSpan.End == parameter.DeclaringSyntaxReference.GetSyntax(CancellationToken.None).Span.End)
if (ctx.Node.GetLocation().SourceSpan.End == parameter.DeclaringSyntaxReference.GetSyntax(ctx.CancellationToken).Span.End)
{
if (parameter.ParameterType.NavTypeKind == NavTypeKind.Array)
correctName = ((IArrayTypeSymbol)(parameter.ParameterType)).ElementType.Name.ToString();
if (parameter.ParameterType.GetNavTypeKindSafe() == NavTypeKind.Array)
correctName = ((IArrayTypeSymbol)parameter.ParameterType).ElementType.Name.ToString();
else
correctName = parameter.ParameterType.Name;

Expand All @@ -99,28 +94,20 @@ private void CheckForObjectIDsInVariablesOrProperties(SyntaxNodeAnalysisContext
ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0005VariableCasingShouldNotDifferFromDeclaration, ctx.Node.GetLocation(), new object[] { correctName, "" }));
}
}
try
{
IReturnValueSymbol returnValue = method.ReturnValueSymbol;
if (returnValue.ReturnType.NavTypeKind == NavTypeKind.DotNet)
{
return;
}
IReturnValueSymbol returnValue = method.ReturnValueSymbol;
if (returnValue?.DeclaringSyntaxReference == null || returnValue.ReturnType.GetNavTypeKindSafe() == NavTypeKind.DotNet) return;

if (ctx.Node.GetLocation().SourceSpan.End == returnValue.DeclaringSyntaxReference.GetSyntax(CancellationToken.None).Span.End)
{
correctName = returnValue.ReturnType.Name;
if (ctx.Node.GetLocation().SourceSpan.End == returnValue.DeclaringSyntaxReference.GetSyntax(ctx.CancellationToken).Span.End)
{
correctName = returnValue.ReturnType.Name;

if (ctx.Node.GetLastToken().ToString().Trim('"').ToUpper() != correctName.ToUpper())
ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0003DoNotUseObjectIDsInVariablesOrProperties, ctx.Node.GetLocation(), new object[] { ctx.Node.ToString().Trim('"'), correctName }));
if (ctx.Node.GetLastToken().ToString().Trim('"').ToUpper() != correctName.ToUpper())
ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0003DoNotUseObjectIDsInVariablesOrProperties, ctx.Node.GetLocation(), new object[] { ctx.Node.ToString().Trim('"'), correctName }));

if (ctx.Node.GetLastToken().ToString().Trim('"') != correctName)
ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0005VariableCasingShouldNotDifferFromDeclaration, ctx.Node.GetLocation(), new object[] { correctName, "" }));
}
if (ctx.Node.GetLastToken().ToString().Trim('"') != correctName)
ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0005VariableCasingShouldNotDifferFromDeclaration, ctx.Node.GetLocation(), new object[] { correctName, "" }));
}
catch (System.NullReferenceException)
{ }
}
}
}
}
}
22 changes: 11 additions & 11 deletions Design/Rule0004LookupPageIdAndDrillDownPageId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace BusinessCentral.LinterCop.Design
[DiagnosticAnalyzer]
public class Rule0004LookupPageIdAndDrillDownPageId : DiagnosticAnalyzer
{
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create<DiagnosticDescriptor>(DiagnosticDescriptors.Rule0004LookupPageIdAndDrillDownPageId);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create<DiagnosticDescriptor>(DiagnosticDescriptors.Rule0004LookupPageIdAndDrillDownPageId, DiagnosticDescriptors.Rule0000ErrorInRule);

public override void Initialize(AnalysisContext context)
=> context.RegisterSymbolAction(new Action<SymbolAnalysisContext>(this.CheckForLookupPageIdAndDrillDownPageId), SymbolKind.Page);
Expand All @@ -18,13 +18,13 @@ private void CheckForLookupPageIdAndDrillDownPageId(SymbolAnalysisContext contex
IPageTypeSymbol pageTypeSymbol = (IPageTypeSymbol)context.Symbol;
if (pageTypeSymbol.PageType != PageTypeKind.List || pageTypeSymbol.RelatedTable == null) return;
if (pageTypeSymbol.RelatedTable.ContainingModule != context.Symbol.ContainingModule) return;
CheckTable(pageTypeSymbol.RelatedTable, ref context);
CheckTable(pageTypeSymbol.RelatedTable, context);
}

private void CheckTable(ITableTypeSymbol table, ref SymbolAnalysisContext context)
private void CheckTable(ITableTypeSymbol table, SymbolAnalysisContext context)
{
if (table.IsObsoletePending || table.IsObsoleteRemoved) return;
if (!IsSymbolAccessible(table)) return;
if (!IsSymbolAccessible(table, context)) return;
if (table.TableType == TableTypeKind.Temporary) return;

bool exists = table.Properties.Where(e => e.PropertyKind == PropertyKind.DrillDownPageId || e.PropertyKind == PropertyKind.LookupPageId).Count() == 2;
Expand All @@ -34,24 +34,24 @@ private void CheckTable(ITableTypeSymbol table, ref SymbolAnalysisContext contex
Diagnostic.Create(
DiagnosticDescriptors.Rule0004LookupPageIdAndDrillDownPageId,
table.GetLocation(),
new object[] { GetDeclaration(table), table.Name, context.Symbol.Name }));
new object[] { GetDeclaration(table, context), table.Name, context.Symbol.Name }));
}

private static string GetDeclaration(ISymbol symbol)
=> symbol.Location.SourceTree.GetText(CancellationToken.None).GetSubText(symbol.DeclaringSyntaxReference.Span).ToString();
private static string GetDeclaration(ISymbol symbol, SymbolAnalysisContext context)
=> symbol.Location.SourceTree.GetText(context.CancellationToken).GetSubText(symbol.DeclaringSyntaxReference.Span).ToString();

private static bool IsSymbolAccessible(ISymbol symbol)
private static bool IsSymbolAccessible(ISymbol symbol, SymbolAnalysisContext context)
{
try
{
GetDeclaration(symbol);
GetDeclaration(symbol, context);
return true;
}
catch (Exception)
{
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0000ErrorInRule, context.Symbol.GetLocation(), new Object[] { "Rule0004", "Exception", "at Line 47" }));
return false;
}
}
}

}
}
20 changes: 8 additions & 12 deletions Design/Rule0012DoNotUseObjectIdInSystemFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,30 +38,26 @@ private void CheckForObjectIdsInFunctionInvocations(OperationAnalysisContext con
{
if (context.ContainingSymbol.GetContainingObjectTypeSymbol().IsObsoletePending || context.ContainingSymbol.GetContainingObjectTypeSymbol().IsObsoleteRemoved) return;
if (context.ContainingSymbol.IsObsoletePending || context.ContainingSymbol.IsObsoleteRemoved) return;

IInvocationExpression operation = (IInvocationExpression)context.Operation;
RelevantFuntion CurrentFunction = null;
try
{
CurrentFunction = FunctionCallsWithIDParamaters.RelevantFunctions.First(o => (o.ObjectType.ToString().ToUpper() == operation.TargetMethod.ContainingSymbol.Name.ToUpper() && o.FunctionName == operation.TargetMethod.Name));
}
catch (System.InvalidOperationException)
{ }
if (operation.TargetMethod.Parameters.Length == 0) return;
if (operation.Arguments.Length == 0) return;

SyntaxKind[] AllowedParameterKinds = { SyntaxKind.MemberAccessExpression, SyntaxKind.IdentifierName, SyntaxKind.InvocationExpression, SyntaxKind.QualifiedName };
RelevantFuntion CurrentFunction = FunctionCallsWithIDParamaters.RelevantFunctions.FirstOrDefault(o => (o.ObjectType.ToString().ToUpper() == operation.TargetMethod.ContainingSymbol.Name.ToUpper() && o.FunctionName == operation.TargetMethod.Name));
if (CurrentFunction == null) return;

if (CurrentFunction != null && operation.TargetMethod.Parameters.Length != 0 && !AllowedParameterKinds.Contains(operation.Arguments[0].Syntax.Kind) && (operation.Arguments[0].Syntax.ToString() != "0" || !CurrentFunction.ZeroIDAllowed))
SyntaxKind[] AllowedParameterKinds = { SyntaxKind.MemberAccessExpression, SyntaxKind.IdentifierName, SyntaxKind.InvocationExpression, SyntaxKind.QualifiedName };
if (!AllowedParameterKinds.Contains(operation.Arguments[0].Syntax.Kind) && (operation.Arguments[0].Syntax.ToString() != "0" || !CurrentFunction.ZeroIDAllowed))
{
if (operation.TargetMethod.Parameters[0].ParameterType.NavTypeKind == NavTypeKind.Integer)
{
int tempint = 0;
if (int.TryParse(operation.Arguments[0].Syntax.ToString(), out tempint))
if (int.TryParse(operation.Arguments[0].Syntax.ToString(), out int tempint))
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0012DoNotUseObjectIdInSystemFunctions, context.Operation.Syntax.GetLocation(), new object[] { CurrentFunction.CorrectAccessSymbol, "" }));
else
if (!operation.Arguments[0].Syntax.ToString().ToUpper().StartsWith(CurrentFunction.CorrectAccessSymbol.ToUpper()))
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0012DoNotUseObjectIdInSystemFunctions, context.Operation.Syntax.GetLocation(), new object[] { CurrentFunction.CorrectAccessSymbol, "" }));
}
}

}
}

Expand Down
44 changes: 33 additions & 11 deletions Design/Rule0016CheckForMissingCaptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,30 @@ class Rule0016CheckForMissingCaptions : DiagnosticAnalyzer
{
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create<DiagnosticDescriptor>(DiagnosticDescriptors.Rule0016CheckForMissingCaptions);

private static readonly List<string> PromotedGroupNames = new List<string>
{
"category_new",
"category_process",
"category_report",
"category_category4",
"category_category5",
"category_category6",
"category_category7",
"category_category8",
"category_category9",
"category_category10",
"category_category11",
"category_category12",
"category_category13",
"category_category14",
"category_category15",
"category_category16",
"category_category17",
"category_category18",
"category_category19",
"category_category20",
};

public override void Initialize(AnalysisContext context)
=> context.RegisterSymbolAction(new Action<SymbolAnalysisContext>(this.CheckForMissingCaptions),
SymbolKind.Page,
Expand All @@ -30,7 +54,7 @@ private void CheckForMissingCaptions(SymbolAnalysisContext context)

if (context.Symbol.Kind == SymbolKind.Control)
{
var Control = ((IControlSymbol)context.Symbol);
var Control = (IControlSymbol)context.Symbol;
switch (Control.ControlKind)
{
case ControlKind.Field:
Expand Down Expand Up @@ -110,22 +134,20 @@ private void CheckForMissingCaptions(SymbolAnalysisContext context)

private bool CaptionIsMissing(ISymbol Symbol, SymbolAnalysisContext context)
{
try
if (Symbol.ContainingType?.Kind == SymbolKind.Table)
{
if (Symbol.ContainingType.Kind == SymbolKind.Table)
{
if (((ITableTypeSymbol)Symbol.ContainingType).Id >= 2000000000)
return false;
if (((IFieldSymbol)Symbol).Id >= 2000000000)
return false;
}
if (((ITableTypeSymbol)Symbol.ContainingType).Id >= 2000000000)
return false;
if (((IFieldSymbol)Symbol).Id >= 2000000000)
return false;
}
catch (NullReferenceException)
{ }

if (Symbol.GetEnumPropertyValue<ShowAsKind>(PropertyKind.ShowAs) == ShowAsKind.SplitButton)
return false;

if (SemanticFacts.IsSameName(Symbol.MostSpecificKind, "Group") && PromotedGroupNames.Contains(Symbol.Name.ToLowerInvariant()))
return false;

if (Symbol.GetBooleanPropertyValue(PropertyKind.ShowCaption) != false)
if (Symbol.GetProperty(PropertyKind.Caption) == null && Symbol.GetProperty(PropertyKind.CaptionClass) == null)
return true;
Expand Down
51 changes: 49 additions & 2 deletions Design/Rule0043SecretText.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,56 @@ public class Rule0043SecretText : DiagnosticAnalyzer
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create<DiagnosticDescriptor>(DiagnosticDescriptors.Rule0043SecretText);

private static readonly string authorization = "Authorization";

private static readonly List<string> buildInMethodNames = new List<string>
{
"add",
"getvalues",
"tryaddwithoutvalidation"
};

public override void Initialize(AnalysisContext context) => context.RegisterOperationAction(new Action<OperationAnalysisContext>(this.AnalyzeHttpObjects), OperationKind.InvocationExpression);
public override void Initialize(AnalysisContext context)
{
context.RegisterOperationAction(new Action<OperationAnalysisContext>(this.AnalyzeHttpObjects), OperationKind.InvocationExpression);
// TODO: enable after Spring2024OrGreater release
// context.RegisterOperationAction(new Action<OperationAnalysisContext>(this.AnalyzeIsolatedStorage), OperationKind.InvocationExpression);
}

private void AnalyzeIsolatedStorage(OperationAnalysisContext ctx)
{
// TODO: enable after Spring2024OrGreater release
// if (!VersionChecker.IsSupported(ctx.ContainingSymbol, VersionCompatibility.Spring2024OrGreater)) 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.Arguments.Count() < 3) return;

IMethodSymbol targetMethod = operation.TargetMethod;
if (targetMethod == null || targetMethod.ContainingSymbol.Kind != SymbolKind.Class) return;
if (!SemanticFacts.IsSameName(targetMethod.ContainingSymbol.Name, "IsolatedStorage")) return;

int argumentIndex;
switch (operation.TargetMethod.Name.ToLowerInvariant())
{
case "get":
argumentIndex = 2;
break;
case "set":
case "setencrypted":
argumentIndex = 1;
break;
default:
argumentIndex = -1;
break;
}

if (argumentIndex == -1 || operation.Arguments[argumentIndex].Parameter == null) return;

if (!IsArgumentOfTypeSecretText(operation.Arguments[argumentIndex]))
ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0043SecretText, ctx.Operation.Syntax.GetLocation()));
}

private void AnalyzeHttpObjects(OperationAnalysisContext ctx)
{
Expand Down Expand Up @@ -51,10 +93,15 @@ private void AnalyzeHttpObjects(OperationAnalysisContext ctx)

if (!IsAuthorizationArgument(operation.Arguments[0])) return;

if (operation.Arguments[1].Parameter.OriginalDefinition.GetTypeSymbol().GetNavTypeKindSafe() != NavTypeKind.SecretText)
if (!IsArgumentOfTypeSecretText(operation.Arguments[1]))
ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0043SecretText, ctx.Operation.Syntax.GetLocation()));
}

private bool IsArgumentOfTypeSecretText(IArgument argument)
{
return argument.Parameter.OriginalDefinition.GetTypeSymbol().GetNavTypeKindSafe() == NavTypeKind.SecretText;
}

private static bool IsAuthorizationArgument(IArgument argument)
{
switch (argument.Syntax.Kind)
Expand Down
Loading

0 comments on commit fe8dc2c

Please sign in to comment.