Skip to content

Commit

Permalink
passing tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dicko2 committed Nov 18, 2024
1 parent ee15456 commit 53aef96
Show file tree
Hide file tree
Showing 36 changed files with 249 additions and 110 deletions.
81 changes: 36 additions & 45 deletions src/Agoda.Analyzers.Test/AllAnalyzersUnitTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using Agoda.Analyzers.Helpers;
Expand Down Expand Up @@ -44,50 +45,27 @@ private static IEnumerable<TestCaseData> GetAnalyzerTestCases()
}

[TestCaseSource(nameof(GetAnalyzerTestCases))]
public void Analyzer_Should_Have_Required_Properties_For_Diagnostic_Create(Type analyzerType)
public void Analyzer_Should_Pass_Properties_To_Diagnostic_Create(Type analyzerType)
{
var methods = analyzerType.GetMethods(BindingFlags.Instance |
BindingFlags.NonPublic |
BindingFlags.Public |
BindingFlags.DeclaredOnly |
BindingFlags.Static |
BindingFlags.FlattenHierarchy);
BindingFlags.NonPublic |
BindingFlags.Public |
BindingFlags.DeclaredOnly |
BindingFlags.Static |
BindingFlags.FlattenHierarchy);

var violations = new List<string>();

foreach (var method in methods)
{
try
{
// Get the IL bytes of the method
var methodBody = method.GetMethodBody();
if (methodBody == null) continue;

var instructions = methodBody.GetILAsByteArray();
if (instructions == null) continue;

// Check if the method contains a call to Diagnostic.Create
if (ContainsDiagnosticCreate(method))
foreach (var diagnosticCreateCall in FindDiagnosticCreateCalls(method))
{
// Create instance of analyzer to check properties
var analyzer = Activator.CreateInstance(analyzerType);

// Get the Properties dictionary/field
var propertiesInfo = analyzerType.GetProperty("Properties",
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

propertiesInfo.ShouldNotBeNull($"Analyzer uses Diagnostic.Create in method {method.Name} " +
"but doesn't have a Properties property");

var properties = propertiesInfo.GetValue(analyzer) as IDictionary<string, string>;

properties.ShouldNotBeNull($"Properties is null or not a dictionary");

properties.ContainsKey(AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES)
.ShouldBeTrue($"Method {method.Name} doesn't contain required '{AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES}' property When calling Diagnostic.Create");

properties["MyKey"].ShouldNotBeNullOrWhiteSpace(
$"MyKey property in method {method.Name} should have a value");
if (!diagnosticCreateCall.HasPropertiesParameter)
{
violations.Add($"Method {method.Name} in {analyzerType.Name} calls Diagnostic.Create without properties parameter");
}
}
}
catch (Exception ex)
Expand All @@ -99,21 +77,24 @@ public void Analyzer_Should_Have_Required_Properties_For_Diagnostic_Create(Type
violations.ShouldBeEmpty();
}

private bool ContainsDiagnosticCreate(MethodInfo method)
private class DiagnosticCreateCall
{
public bool HasPropertiesParameter { get; set; }
public int ParameterCount { get; set; }
}

private IEnumerable<DiagnosticCreateCall> FindDiagnosticCreateCalls(MethodInfo method)
{
var calls = new List<DiagnosticCreateCall>();
try
{
// Get all method calls within the method
var body = method.GetMethodBody();
if (body == null) return false;
if (body == null) return calls;

// Decompile IL to find calls to Diagnostic.Create
var instructions = body.GetILAsByteArray();
if (instructions == null) return false;
if (instructions == null) return calls;

// Get all method calls from the IL
var moduleHandle = method.Module.ModuleHandle;
var methodCalls = new List<MethodInfo>();

for (int i = 0; i < instructions.Length; i++)
{
Expand All @@ -129,7 +110,18 @@ private bool ContainsDiagnosticCreate(MethodInfo method)
if (calledMethod?.DeclaringType?.FullName == "Microsoft.CodeAnalysis.Diagnostic" &&
calledMethod.Name == "Create")
{
return true;
var parameters = calledMethod.GetParameters();
var hasPropertiesParam = parameters.Any(p =>
p.ParameterType.IsGenericType &&
p.ParameterType.GetGenericTypeDefinition() == typeof(ImmutableDictionary<,>) &&
p.ParameterType.GetGenericArguments()[0] == typeof(string) &&
p.ParameterType.GetGenericArguments()[1] == typeof(string));

calls.Add(new DiagnosticCreateCall
{
HasPropertiesParameter = hasPropertiesParam,
ParameterCount = parameters.Length
});
}
}
catch
Expand All @@ -142,10 +134,9 @@ private bool ContainsDiagnosticCreate(MethodInfo method)
}
catch
{
// If we can't analyze the method, assume it might contain Diagnostic.Create
return true;
// If we can't analyze the method, we'll skip it
}

return false;
return calls;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace Agoda.Analyzers.AgodaCustom
public class AG0001DependencyResolverMustNotBeUsed : PropertyInvocationAnalyzerBase
{
internal override Dictionary<string, string> Properties => new Dictionary<string, string>()
{ { Const.KEY_TECH_DEBT_IN_MINUTES, "10" } };
{ { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } };

Check warning on line 17 in src/Agoda.Analyzers/AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs

View check run for this annotation

Codecov / codecov/patch

src/Agoda.Analyzers/AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs#L16-L17

Added lines #L16 - L17 were not covered by tests

public const string DIAGNOSTIC_ID = "AG0001";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Agoda.Analyzers.Helpers;
using System.Collections.Generic;
using Agoda.Analyzers.Helpers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
Expand Down Expand Up @@ -71,7 +72,12 @@ So right not not possible to filter out unreferenced errors in the specific proj
//var references = SymbolFinder.FindReferencesAsync(methodSymbol, null).Result;
//if (references.Count() > 1)

context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation()));
context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation(),properties: _props.ToImmutableDictionary()));
}

private static Dictionary<string, string> _props = new Dictionary<string, string>()
{
{ AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" }
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;

namespace Agoda.Analyzers.AgodaCustom
Expand Down Expand Up @@ -60,11 +61,14 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context)

if (context.SemanticModel.GetTypeInfo(simpleType).Type.ToDisplayString() == "Microsoft.AspNetCore.Http.HttpContext")
{
context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation()));
context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation(), properties: _props.ToImmutableDictionary()));
}
}



private static Dictionary<string, string> _props = new Dictionary<string, string>()
{
{ AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" }
};

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Agoda.Analyzers.Helpers;
using System.Collections.Generic;

namespace Agoda.Analyzers.AgodaCustom
{
Expand Down Expand Up @@ -59,7 +60,7 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
// Type.GetType("") - this method is completely banned as all its overloads take types as strings
if (!GetTypeRule.Verify(methodSymbol))
{
context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation()));
context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation(), properties: _props.ToImmutableDictionary()));
return;
}

Expand All @@ -78,8 +79,12 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context)

if (firstParameter.Type.Name.ToLower() == "string")
{
context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation()));
context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation(), properties: _props.ToImmutableDictionary()));
}
}
private static Dictionary<string, string> _props = new Dictionary<string, string>()
{
{ AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" }
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Agoda.Analyzers.Helpers;
using System.Collections.Generic;

namespace Agoda.Analyzers.AgodaCustom
{
Expand Down Expand Up @@ -69,7 +70,11 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context)

// report error at position of method name
var methodNameToken = methodDeclaration.ChildTokens().First(t => t.IsKind(SyntaxKind.IdentifierToken));
context.ReportDiagnostic(Diagnostic.Create(Descriptor, methodNameToken.GetLocation()));
context.ReportDiagnostic(Diagnostic.Create(Descriptor, methodNameToken.GetLocation(), properties: _props.ToImmutableDictionary()));
}
private static Dictionary<string, string> _props = new Dictionary<string, string>()
{
{ AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" }
};
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Immutable;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text.RegularExpressions;
using Agoda.Analyzers.Helpers;
Expand Down Expand Up @@ -74,7 +75,11 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context)

if (publicConstructorsCount == 1) return;

context.ReportDiagnostic(Diagnostic.Create(Descriptor, classDeclaration.GetLocation()));
context.ReportDiagnostic(Diagnostic.Create(Descriptor, classDeclaration.GetLocation(), properties: _props.ToImmutableDictionary()));
}
private static Dictionary<string, string> _props = new Dictionary<string, string>()
{
{ AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" }
};
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Agoda.Analyzers.Helpers;
using Microsoft.CodeAnalysis;
Expand Down Expand Up @@ -45,7 +46,7 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
if ("Microsoft.AspNetCore.Http.IHttpContextAccessor" == paramTypeName
|| "Microsoft.AspNetCore.Http.HttpContextAccessor" == paramTypeName)
{
context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation()));
context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation(), properties: _props.ToImmutableDictionary()));
}
}

Expand All @@ -57,5 +58,9 @@ public override void Initialize(AnalysisContext context)

context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.Parameter);
}
private static Dictionary<string, string> _props = new Dictionary<string, string>()
{
{ AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" }
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Agoda.Analyzers.Helpers;
using System.Collections.Generic;

namespace Agoda.Analyzers.AgodaCustom
{
Expand Down Expand Up @@ -58,7 +59,11 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
.Any(x => TestMethodHelpers.IsTestCase(x, context));
if (!hasTestMethod) return;

context.ReportDiagnostic(Diagnostic.Create(Descriptor, classDeclaration.GetLocation()));
context.ReportDiagnostic(Diagnostic.Create(Descriptor, classDeclaration.GetLocation(), properties: _props.ToImmutableDictionary()));
}
private static Dictionary<string, string> _props = new Dictionary<string, string>()
{
{ AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" }
};
}
}
32 changes: 17 additions & 15 deletions src/Agoda.Analyzers/AgodaCustom/AG0011NoDirectQueryStringAccess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,26 @@ namespace Agoda.Analyzers.AgodaCustom
public class AG0011NoDirectQueryStringAccess : PropertyInvocationAnalyzerBase
{
public const string DIAGNOSTIC_ID = "AG0011";

private static readonly LocalizableString Title = new LocalizableResourceString(
nameof(CustomRulesResources.AG0011Title),
CustomRulesResources.ResourceManager,
nameof(CustomRulesResources.AG0011Title),
CustomRulesResources.ResourceManager,
typeof(CustomRulesResources));

private static readonly LocalizableString MessageFormat = new LocalizableResourceString(
nameof(CustomRulesResources.AG0011Title),
CustomRulesResources.ResourceManager,
nameof(CustomRulesResources.AG0011Title),
CustomRulesResources.ResourceManager,
typeof(CustomRulesResources));

protected override DiagnosticDescriptor Descriptor => new DiagnosticDescriptor(
DIAGNOSTIC_ID,
Title,
MessageFormat,
DIAGNOSTIC_ID,
Title,
MessageFormat,
AnalyzerCategory.CustomQualityRules,
DiagnosticSeverity.Error,
AnalyzerConstants.EnabledByDefault,
DiagnosticSeverity.Error,
AnalyzerConstants.EnabledByDefault,
DescriptionContentLoader.GetAnalyzerDescription(nameof(AG0011NoDirectQueryStringAccess)),
"https://agoda-com.github.io/standards-c-sharp/services/framework-abstractions.html",
"https://agoda-com.github.io/standards-c-sharp/services/framework-abstractions.html",
WellKnownDiagnosticTags.EditAndContinue);

protected override IEnumerable<InvocationRule> Rules => new[]
Expand All @@ -42,6 +42,8 @@ public class AG0011NoDirectQueryStringAccess : PropertyInvocationAnalyzerBase
};

internal override Dictionary<string, string> Properties => new Dictionary<string, string>()
{ { Const.KEY_TECH_DEBT_IN_MINUTES, "10" } };
}
{
{ AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" }
};

Check warning on line 47 in src/Agoda.Analyzers/AgodaCustom/AG0011NoDirectQueryStringAccess.cs

View check run for this annotation

Codecov / codecov/patch

src/Agoda.Analyzers/AgodaCustom/AG0011NoDirectQueryStringAccess.cs#L44-L47

Added lines #L44 - L47 were not covered by tests
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Net;
Expand Down Expand Up @@ -67,7 +68,7 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
return;
}

context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation()));
context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation(), properties: _props.ToImmutableDictionary()));
}

private static bool HasInvokedAssertStaticMethod(MethodDeclarationSyntax methodDeclaration, SyntaxNodeAnalysisContext context)
Expand Down Expand Up @@ -128,5 +129,9 @@ public AssertLibraryInfo(string namespaceTitle, string module, string name, stri
HasExtenstionMethods = type != null;
}
}
private static Dictionary<string, string> _props = new Dictionary<string, string>()
{
{ AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" }
};
}
}
Loading

0 comments on commit 53aef96

Please sign in to comment.