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

Add new Rule0080: Analyze JsonToken JPath #832

Merged
merged 1 commit into from
Dec 13, 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
35 changes: 35 additions & 0 deletions BusinessCentral.LinterCop.Test/Rule0080.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
namespace BusinessCentral.LinterCop.Test;

public class Rule0080
{
private string _testCaseDir = "";

[SetUp]
public void Setup()
{
_testCaseDir = Path.Combine(Directory.GetParent(Environment.CurrentDirectory)!.Parent!.Parent!.FullName,
"TestCases", "Rule0080");
}

[Test]
[TestCase("SelectToken")]
public async Task HasDiagnostic(string testCase)
{
var code = await File.ReadAllTextAsync(Path.Combine(_testCaseDir, "HasDiagnostic", $"{testCase}.al"))
.ConfigureAwait(false);

var fixture = RoslynFixtureFactory.Create<Rule0080AnalyzeJsonTokenJPath>();
fixture.HasDiagnostic(code, Rule0080AnalyzeJsonTokenJPath.DiagnosticDescriptors.Rule0080AnalyzeJsonTokenJPath.Id);
}

[Test]
[TestCase("SelectToken")]
public async Task NoDiagnostic(string testCase)
{
var code = await File.ReadAllTextAsync(Path.Combine(_testCaseDir, "NoDiagnostic", $"{testCase}.al"))
.ConfigureAwait(false);

var fixture = RoslynFixtureFactory.Create<Rule0080AnalyzeJsonTokenJPath>();
fixture.NoDiagnosticAtMarker(code, Rule0080AnalyzeJsonTokenJPath.DiagnosticDescriptors.Rule0080AnalyzeJsonTokenJPath.Id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
codeunit 50100 MyCodeunit
{
procedure MyProcedure()
var
MyJsonToken: JsonToken;
Result: JsonToken;
begin
MyJsonToken.SelectToken([|'$.custom_attributes[?(@.attribute_code == "activation_status")].value'|], Result);
end;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
codeunit 50100 MyCodeunit
{
procedure MyProcedure()
var
MyJsonToken: JsonToken;
Result: JsonToken;
begin
MyJsonToken.SelectToken([|'$.custom_attributes[?(@.attribute_code == ''activation_status'')].value'|], Result);
end;
}
58 changes: 58 additions & 0 deletions BusinessCentral.LinterCop/Design/Rule0080AnalyzeJsonTokenJPath.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using BusinessCentral.LinterCop.AnalysisContextExtension;
using Microsoft.Dynamics.Nav.CodeAnalysis;
using Microsoft.Dynamics.Nav.CodeAnalysis.Diagnostics;
using Microsoft.Dynamics.Nav.CodeAnalysis.Syntax;
using System.Collections.Immutable;

namespace BusinessCentral.LinterCop.Design
{
[DiagnosticAnalyzer]
public class Rule0080AnalyzeJsonTokenJPath : DiagnosticAnalyzer
{
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(DiagnosticDescriptors.Rule0080AnalyzeJsonTokenJPath);

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

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

if (ctx.Operation is not IInvocationExpression operation)
return;

if (operation.TargetMethod.MethodKind != MethodKind.BuiltInMethod ||
operation.TargetMethod.Name != "SelectToken" ||
operation.TargetMethod.ContainingSymbol?.Name != "JsonToken")
return;

var stringLiterals = operation.Arguments
.Where(p => p.Parameter?.Name == "Path")
.Select(p => p.Syntax)
.OfType<LiteralExpressionSyntax>()
.Select(l => l.Literal)
.OfType<StringLiteralValueSyntax>()
.Where(l => l.Value.ValueText?.Contains('"') ?? false);

foreach (var stringLiteral in stringLiterals)
{
ctx.ReportDiagnostic(Diagnostic.Create(
DiagnosticDescriptors.Rule0080AnalyzeJsonTokenJPath,
stringLiteral.GetLocation()));
}
}

public static class DiagnosticDescriptors
{
public static readonly DiagnosticDescriptor Rule0080AnalyzeJsonTokenJPath = new(
id: LinterCopAnalyzers.AnalyzerPrefix + "0080",
title: LinterCopAnalyzers.GetLocalizableString("Rule0080AnalyzeJsonTokenJPathTitle"),
messageFormat: LinterCopAnalyzers.GetLocalizableString("Rule0080AnalyzePathOnJsonTokenFormat"),
category: "Design",
defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true,
description: LinterCopAnalyzers.GetLocalizableString("Rule0080AnalyzePathOnJsonTokenDescription"),
helpLinkUri: "https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0080");
}
}
}
7 changes: 6 additions & 1 deletion BusinessCentral.LinterCop/LinterCop.ruleset.json
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,11 @@
"id": "LC0079",
"action": "Info",
"justification": "Event publishers should not be public."
}
},
{
"id": "LC0080",
"action": "Warning",
"justification": "Replace double quotes in JPath expressions with two single quotes."
}
]
}
9 changes: 9 additions & 0 deletions BusinessCentral.LinterCop/LinterCopAnalyzers.resx
Original file line number Diff line number Diff line change
Expand Up @@ -831,4 +831,13 @@
<data name="Rule0079NonPublicEventPublisherDescription" xml:space="preserve">
<value>Event Publishers should be local or internal to allow for future parameter extensions.</value>
</data>
<data name="Rule0080AnalyzeJsonTokenJPathTitle" xml:space="preserve">
<value>Replace double quotes in JPath expressions with two single quotes.</value>
</data>
<data name="Rule0080AnalyzeJsonTokenJPathFormat" xml:space="preserve">
<value>Double quote character detected in JPath expression. Replace all double quotes (") with two single quotes ('').</value>
</data>
<data name="Rule0080AnalyzeJsonTokenJPathDescription" xml:space="preserve">
<value>Detects and warns against the use of double-quote (") characters in JPath expressions.</value>
</data>
</root>
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,4 +232,5 @@ For an example and the default values see: [LinterCop.ruleset.json](./BusinessCe
|[LC0076](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0076)|The field with table relation should have at least the same length as the referenced field.|Warning|
|[LC0077](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0077)|Methods should always be called with parenthesis.|Info|
|[LC0078](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0078)|Temporary records should not trigger table triggers.|Info|
|[LC0079](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0079)|Event publishers should not be public.|Info|
|[LC0079](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0079)|Event publishers should not be public.|Info|
|[LC0080](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0080)|Replace double quotes in JPath expressions with two single quotes.|Warning|
Loading