From 2b79b133d05a45cb42ed6fe586d483b0538beb3c Mon Sep 17 00:00:00 2001 From: jdickson Date: Mon, 18 Nov 2024 13:38:15 +0700 Subject: [PATCH 1/7] Revise rules and being to add meta data --- .../AG0001.md | 4 +- doc/AG0002.md | 51 +++++ doc/AG0003.md | 65 ++++++ doc/AG0004.md | 33 ++++ doc/AG0005.md | 44 +++++ doc/AG0006.md | 41 ++++ doc/AG0009.md | 64 ++++++ doc/AG0010.md | 47 +++++ doc/AG0011.md | 49 +++++ doc/AG0012.md | 68 +++++++ .../AG0013.md | 4 +- doc/AG0018.md | 52 +++++ doc/AG0019.md | 61 ++++++ doc/AG0020.md | 74 +++++++ doc/AG0021.md | 68 +++++++ doc/AG0022.md | 66 +++++++ .../AG0023.md | 6 +- doc/AG0024.md | 62 ++++++ .../AG0025.md | 4 +- .../AG0026.md | 6 +- doc/AG0027.md | 64 ++++++ doc/AG0030.md | 58 ++++++ doc/AG0032.md | 74 +++++++ doc/AG0033.md | 72 +++++++ .../AG0035.md | 6 +- .../AG0037.md | 6 +- .../AG0038.md | 6 +- doc/AG0039.md | 113 +++++++++++ doc/AG0040.md | 3 + doc/AG0041.md | 87 ++++---- doc/AG0042.md | 120 +++++------- doc/AG0043.md | 138 +++++-------- doc/AG0044.md | 141 +++++-------- doc/prompt.md | 185 ++++++++++++++++++ .../AllAnalyzersUnitTests.cs | 119 +++++++++++ src/Agoda.Analyzers/Agoda.Analyzers.csproj | 83 -------- .../AG0001DependencyResolverMustNotBeUsed.cs | 3 + .../AG0011NoDirectQueryStringAccess.cs | 3 + .../AG0032PreventUseOfBlockingTaskMethods.cs | 3 + .../AG0033PreventUseOfTaskResult.cs | 3 + .../AG0035PreventUseOfMachineName.cs | 3 + ...0WaitUntilStateNetworkIdleMustNotBeUsed.cs | 3 + .../PropertyInvocationAnalyzerBase.cs | 1 + src/Agoda.Analyzers/Const.cs | 11 ++ ...AG0002PrivateMethodsShouldNotBeTested.html | 39 ---- ...ContextCannotBePassedAsMethodArgument.html | 52 ----- ...NotUseHardCodedStringsToIdentifyTypes.html | 22 --- ...05TestMethodNamesMustFollowConvention.html | 46 ----- ...ShouldHaveExactlyOnePublicConstructor.html | 9 - ...ccessorCannotBePassedAsMethodArgument.html | 57 ------ .../AG0010PreventTestFixtureInheritance.html | 21 -- .../AG0011NoDirectQueryStringAccess.html | 20 -- ...tMethodMustContainAtLeastOneAssertion.html | 55 ------ ...OnlyCertainPubliclyExposedEnumerables.html | 24 --- ...eventUseOfInternalsVisibleToAttribute.html | 31 --- .../AG0020AvoidReturningNullEnumerables.html | 48 ----- .../RuleContent/AG0021PreferAsyncMethods.html | 8 - ...poseBothSyncAndAsyncVersionsOfMethods.html | 32 --- ...AG0024PreventUseOfTaskFactoryStartNew.html | 20 -- ...eOnlyDataSeleniumIsUsedToFindElements.html | 30 --- .../AG0030PreventUseOfDynamics.html | 9 - ...AG0032PreventUseOfBlockingTaskMethods.html | 22 --- .../AG0033PreventUseOfTaskResult.html | 17 -- .../AG0039MethodLineLengthAnalyzer.html | 71 ------- src/AgodaAnalyzers.sln | 37 ++++ 65 files changed, 1822 insertions(+), 1022 deletions(-) rename src/Agoda.Analyzers/RuleContent/AG0001DependencyResolverMustNotBeUsed.html => doc/AG0001.md (95%) create mode 100644 doc/AG0002.md create mode 100644 doc/AG0003.md create mode 100644 doc/AG0004.md create mode 100644 doc/AG0005.md create mode 100644 doc/AG0006.md create mode 100644 doc/AG0009.md create mode 100644 doc/AG0010.md create mode 100644 doc/AG0011.md create mode 100644 doc/AG0012.md rename src/Agoda.Analyzers/RuleContent/AG0013LimitNumberOfTestMethodParametersTo5.html => doc/AG0013.md (96%) create mode 100644 doc/AG0018.md create mode 100644 doc/AG0019.md create mode 100644 doc/AG0020.md create mode 100644 doc/AG0021.md create mode 100644 doc/AG0022.md rename src/Agoda.Analyzers/RuleContent/AG0023PreventTheUseOfThreadSleep.html => doc/AG0023.md (93%) create mode 100644 doc/AG0024.md rename src/Agoda.Analyzers/RuleContent/AG0025PreventUseOfTaskContinue.html => doc/AG0025.md (96%) rename src/Agoda.Analyzers/RuleContent/AG0026EnsureOnlyCssSelectorIsUsedToFindElements.html => doc/AG0026.md (95%) create mode 100644 doc/AG0027.md create mode 100644 doc/AG0030.md create mode 100644 doc/AG0032.md create mode 100644 doc/AG0033.md rename src/Agoda.Analyzers/RuleContent/AG0035PreventUseOfMachineName.html => doc/AG0035.md (96%) rename src/Agoda.Analyzers/RuleContent/AG0037EnsureSeleniumTestHasOwnedByAttribute.html => doc/AG0037.md (97%) rename src/Agoda.Analyzers/RuleContent/AG0038PreventUseOfRegionPreprocessorDirective.html => doc/AG0038.md (95%) create mode 100644 doc/AG0039.md create mode 100644 doc/AG0040.md create mode 100644 doc/prompt.md create mode 100644 src/Agoda.Analyzers/Const.cs delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0002PrivateMethodsShouldNotBeTested.html delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0003HttpContextCannotBePassedAsMethodArgument.html delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0004DoNotUseHardCodedStringsToIdentifyTypes.html delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0005TestMethodNamesMustFollowConvention.html delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0006RegisteredComponentShouldHaveExactlyOnePublicConstructor.html delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0009IHttpContextAccessorCannotBePassedAsMethodArgument.html delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0010PreventTestFixtureInheritance.html delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0011NoDirectQueryStringAccess.html delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0012TestMethodMustContainAtLeastOneAssertion.html delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0018PermitOnlyCertainPubliclyExposedEnumerables.html delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0019PreventUseOfInternalsVisibleToAttribute.html delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0020AvoidReturningNullEnumerables.html delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0021PreferAsyncMethods.html delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0022DoNotExposeBothSyncAndAsyncVersionsOfMethods.html delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0024PreventUseOfTaskFactoryStartNew.html delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0027EnsureOnlyDataSeleniumIsUsedToFindElements.html delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0030PreventUseOfDynamics.html delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0032PreventUseOfBlockingTaskMethods.html delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0033PreventUseOfTaskResult.html delete mode 100644 src/Agoda.Analyzers/RuleContent/AG0039MethodLineLengthAnalyzer.html diff --git a/src/Agoda.Analyzers/RuleContent/AG0001DependencyResolverMustNotBeUsed.html b/doc/AG0001.md similarity index 95% rename from src/Agoda.Analyzers/RuleContent/AG0001DependencyResolverMustNotBeUsed.html rename to doc/AG0001.md index 74f6897..b1a4350 100644 --- a/src/Agoda.Analyzers/RuleContent/AG0001DependencyResolverMustNotBeUsed.html +++ b/doc/AG0001.md @@ -1,4 +1,6 @@ -

+# Rule AG0001 + +

The DependencyResolver should not be used directly in the codebase. We should expose our dependencies as a constructor parameter.

diff --git a/doc/AG0002.md b/doc/AG0002.md new file mode 100644 index 0000000..0fec37c --- /dev/null +++ b/doc/AG0002.md @@ -0,0 +1,51 @@ +# Classes should only expose functionality used in their public interface + +ID: AG0002 + +Type: Code Smell + +When implementing an interface, classes should only expose methods that are part of their public contract. Additional public or internal methods that aren't part of the implemented interfaces create confusion about the class's responsibilities and violate the Interface Segregation Principle. + +#### Don't + +```csharp +interface ISomething +{ + void DoSomething(); +} + +class TestClass : ISomething +{ + public void DoSomething() + { + } + internal void DoSomething2() // Noncompliant - not part of interface + { + } +} +``` + +#### Do + +```csharp +interface ISomething +{ + void DoSomething(); +} + +class TestClass : ISomething +{ + public void DoSomething() + { + } +} +``` + +Adding methods that aren't part of the interface creates several problems: + +- Violates Interface Segregation Principle by potentially forcing clients to depend on methods they don't use +- Makes the code harder to understand and maintain by obscuring the class's true responsibilities +- Can lead to tight coupling if these additional methods are used by other classes +- Makes testing more difficult as you need to consider methods outside the interface contract + +If additional functionality is needed, consider creating a new interface that better represents the complete contract of the class. diff --git a/doc/AG0003.md b/doc/AG0003.md new file mode 100644 index 0000000..b9bfca1 --- /dev/null +++ b/doc/AG0003.md @@ -0,0 +1,65 @@ +# Avoid passing HttpContext as a parameter + +ID: AG0003 + +Type: Code Smell + +Passing `System.Web.HttpContext` as a parameter creates tight coupling to the web infrastructure and makes unit testing significantly more difficult. Instead, pass only the specific HttpContext properties that your code actually needs. + +#### Don't + +```csharp +using System.Web; + +interface ISomething +{ + void SomeMethod(HttpContext c, string sampleString); // Noncompliant +} + +class TestClass: ISomething +{ + public void SomeMethod(HttpContext context, string sampleString) + { + // Hard to test due to HttpContext dependency + } + + public TestClass(System.Web.HttpContext context) + { + // Constructor with HttpContext dependency + } +} +``` + +#### Do + +```csharp +using System.Web; + +interface ISomething +{ + void SomeMethod(string userAgent, string sampleString); +} + +class TestClass: ISomething +{ + public void SomeMethod(string userAgent, string sampleString) + { + // Only depends on what it needs + } + + public TestClass(string userAgent) + { + // Clean constructor with specific dependencies + } +} +``` + +Passing HttpContext creates several problems: + +- Makes unit testing difficult since HttpContext is complex to mock +- Violates Single Responsibility Principle by potentially accessing many different context properties +- Creates tight coupling to ASP.NET infrastructure +- Obscures the actual dependencies of your code +- Makes it harder to port code to other platforms or frameworks + +Instead, identify and pass only the specific HttpContext properties your code needs (like UserAgent, Request.Path, etc). This improves testability and makes dependencies explicit. diff --git a/doc/AG0004.md b/doc/AG0004.md new file mode 100644 index 0000000..706c81b --- /dev/null +++ b/doc/AG0004.md @@ -0,0 +1,33 @@ +# Avoid hard-coded type strings + +ID: AG0004 + +Type: Bug + +Hard-coded strings to identify namespaces and types make refactoring risky and move type resolution errors from compile-time to runtime. Always use the `typeof` operator to reference types, which provides compile-time safety. + +#### Don't + +```csharp +// Both fail at runtime after namespace changes +var instance = Activator.CreateInstance("Agoda", "Agoda.MyType"); +var type = Type.GetType("Agoda.MyType"); +``` + +#### Do + +```csharp +// Caught at compile time after namespace changes +var instance = Activator.CreateInstance(typeof(Agoda.MyType)); +var type = typeof(Agoda.MyType); +``` + +Using string literals for type identification creates several problems: + +- Refactoring operations like namespace moves or type renames won't update the strings +- Type resolution failures only surface at runtime instead of compile time +- No IntelliSense or IDE support for type names +- More difficult to maintain as type references aren't tracked by development tools +- Can lead to runtime exceptions in production that could have been caught during development + +Always use language features like `typeof()` that provide compile-time type safety and refactoring support. diff --git a/doc/AG0005.md b/doc/AG0005.md new file mode 100644 index 0000000..fe1367d --- /dev/null +++ b/doc/AG0005.md @@ -0,0 +1,44 @@ +# Test methods should have descriptive names + +ID: AG0005 + +Type: Code Smell + +Test method names should clearly communicate what is being tested, under what conditions, and the expected outcome. This makes tests serve as documentation and helps quickly identify what failed when tests break. + +Test names should follow this pattern: + +- Class: `Tests` +- Methods: `__` or `_` + +#### Don't + +```csharp +[Test] +public void HazardLightsTest() // Noncompliant - unclear what aspect is being tested +{...} +``` + +#### Do + +```csharp +[Test] +public void HazardLightsToggleButton_WhenPushedWithLightsBlinking_StopsLightsBlinking() +{...} +``` + +Poor test names create several problems: + +- Makes it harder to understand test failures without reading the implementation +- Reduces test suite's value as documentation +- Makes it difficult to identify missing test cases +- Complicates test maintenance and refactoring +- Makes it harder for team members to understand test coverage + +If naming a test is difficult, it might indicate the test is doing too much and should be split into more focused tests. Good test names help ensure: + +- Clear test purpose +- Easy identification of failures +- Documentation of behavior +- Coverage visibility +- Maintainable test suite \ No newline at end of file diff --git a/doc/AG0006.md b/doc/AG0006.md new file mode 100644 index 0000000..8fe5a55 --- /dev/null +++ b/doc/AG0006.md @@ -0,0 +1,41 @@ +# Components should have a single public constructor + +ID: AG0006 + +Type: Code Smell + +Each component registered with the dependency injection container should have exactly one public constructor. Having multiple public constructors leads to ambiguity in dependency resolution and makes the codebase harder to maintain. + +This is a blocker rule - any component with multiple public constructors should be refactored to have only one. + +Reasons to avoid multiple public constructors: + +- Creates ambiguity for the DI container when resolving dependencies +- Makes it harder to track and manage dependencies +- Increases complexity of dependency resolution +- Makes code harder to test and maintain +- Can lead to runtime errors if wrong constructor is chosen +- Violates the Single Responsibility Principle + +If a dependency is truly optional, use the null object pattern instead of multiple constructors: + +```csharp +// Don't do this +public class Service +{ + public Service() { } // Noncompliant - multiple constructors + + public Service(ILogger logger) { } +} + +// Do this instead +public class Service +{ + public Service(ILogger logger = null) // Single constructor with optional dependency + { + _logger = logger ?? NullLogger.Instance; + } +} +``` + +Always prefer explicit dependency declaration through a single constructor for clearer and more maintainable code. diff --git a/doc/AG0009.md b/doc/AG0009.md new file mode 100644 index 0000000..727eee1 --- /dev/null +++ b/doc/AG0009.md @@ -0,0 +1,64 @@ +# Avoid passing HttpContextAccessor as a parameter + +ID: AG0009 + +Type: Code Smell + +Passing `IHttpContextAccessor` or `HttpContextAccessor` as a parameter creates tight coupling to ASP.NET Core infrastructure and makes testing difficult. Instead, pass only the specific HTTP context properties that your code actually needs. + +#### Don't + +```csharp +using Microsoft.AspNetCore.Http; + +interface ISomething +{ + void SomeMethod(IHttpContextAccessor c, string sampleString); // Noncompliant + void SomeMethod(HttpContextAccessor c, string sampleString); // Noncompliant +} + +class TestClass : ISomething +{ + public void SomeMethod(IHttpContextAccessor context, string sampleString) + { + // Hard to test due to IHttpContextAccessor dependency + } + + public TestClass(IHttpContextAccessor context) + { + // Constructor with unnecessary dependency + } +} +``` + +#### Do + +```csharp +interface ISomething +{ + void SomeMethod(string userAgent, string sampleString); +} + +class TestClass : ISomething +{ + public void SomeMethod(string userAgent, string sampleString) + { + // Only depends on what it needs + } + + public TestClass(string userAgent) + { + // Clean constructor with specific dependencies + } +} +``` + +Passing HttpContextAccessor creates several problems: + +- Makes unit testing difficult since context is complex to mock +- Violates Single Responsibility Principle by potentially accessing many different context properties +- Creates tight coupling to ASP.NET Core infrastructure +- Obscures the actual dependencies of your code +- Makes it harder to port code to other platforms or frameworks + +Instead, identify and pass only the specific context properties your code needs (like UserAgent, Request.Path, etc). This improves testability and makes dependencies explicit. \ No newline at end of file diff --git a/doc/AG0010.md b/doc/AG0010.md new file mode 100644 index 0000000..3bce3cf --- /dev/null +++ b/doc/AG0010.md @@ -0,0 +1,47 @@ +# Avoid test fixture inheritance + +ID: AG0010 + +Type: Code Smell + +Test fixture inheritance should be avoided as it creates hidden dependencies, makes tests harder to understand, and violates the test isolation principle. Each test class should be independent and self-contained. + +#### Don't + +```csharp +[TestFixture] +public class TestClass : BaseTest // Noncompliant - inheriting from base test class +{ + // Code +} +``` + +#### Do + +```csharp +[TestFixture] +public class TestClass +{ + // Code +} +``` + +Test fixture inheritance creates several problems: + +- Makes test dependencies and setup harder to understand +- Complicates test maintenance due to hidden inherited behavior +- Can lead to unexpected test interactions +- Violates test isolation principle +- Makes it difficult to refactor tests +- Can cause setup/teardown ordering issues +- Reduces test clarity and readability + +Instead of inheritance, prefer: + +- Composition through helper methods +- Test utility classes +- Builder patterns +- Shared test data factories +- Clear, explicit test setup within each test class + +This keeps tests more maintainable and easier to understand at a glance. \ No newline at end of file diff --git a/doc/AG0011.md b/doc/AG0011.md new file mode 100644 index 0000000..4e193d3 --- /dev/null +++ b/doc/AG0011.md @@ -0,0 +1,49 @@ +# Avoid direct QueryString access + +ID: AG0011 + +Type: Code Smell + +Direct access to `QueryString` collection bypasses ASP.NET model binding, which provides validation, type conversion, and security features. Always use model binding with action method parameters instead. + +#### Don't + +```csharp +public ActionResult Index() +{ + var id = HttpContext.Current.Request.QueryString["id"]; // Noncompliant + // Use id here +} +``` + +#### Do + +```csharp +public ActionResult Index([FromQuery] int id) +{ + // Use id here +} +``` + +Direct QueryString access creates several problems: + +- Bypasses model validation +- No automatic type conversion +- Potential security vulnerabilities from unvalidated input +- Makes testing harder as it depends on HttpContext +- Code is more verbose and error-prone +- No automatic null handling +- No automatic binding to complex types +- Harder to maintain and modify + +Model binding provides: + +- Automatic type conversion +- Input validation +- Security features +- Cleaner, more maintainable code +- Better testability +- Support for complex object binding +- Clear parameter intent through attributes + +Always use model binding to handle query string parameters in a safe and maintainable way. diff --git a/doc/AG0012.md b/doc/AG0012.md new file mode 100644 index 0000000..bdf5858 --- /dev/null +++ b/doc/AG0012.md @@ -0,0 +1,68 @@ +# Test methods must contain assertions + +ID: AG0012 + +Type: Bug + +Each test method should contain at least one assertion to verify expected behavior. Tests without assertions don't validate anything and provide a false sense of security. + +#### Don't + +```csharp +using NUnit.Framework; + +public class TestClass +{ + [Test] + public void This_Is_Not_Valid() // Noncompliant - no assertions + { + int[] arrayToAssert = { 1, 2, 3 }; + } +} +``` + +#### Do + +With NUnit: + +```csharp +using NUnit.Framework; + +public class TestClass +{ + [Test] + public void This_Is_Valid() + { + int[] arrayToAssert = { 1, 2, 3 }; + Assert.That(arrayToAssert, Has.Exactly(1).EqualTo(3)); + } +} +``` + +With Shouldly: + +```csharp +using NUnit.Framework; +using Shouldly; + +public class TestClass +{ + [Test] + public void This_Is_Valid() + { + int[] arrayForShouldBe = { 1, 2, 3 }; + arrayForShouldBe.Length.ShouldBe(3); + } +} +``` + +Tests without assertions create problems: + +- Don't actually verify any behavior +- Give false confidence in code quality +- May pass even when the code is broken +- Make test intent unclear +- Hard to maintain as requirements aren't explicit +- Can hide bugs by appearing to test functionality + +Always include explicit assertions to verify expected behavior in your tests. diff --git a/src/Agoda.Analyzers/RuleContent/AG0013LimitNumberOfTestMethodParametersTo5.html b/doc/AG0013.md similarity index 96% rename from src/Agoda.Analyzers/RuleContent/AG0013LimitNumberOfTestMethodParametersTo5.html rename to doc/AG0013.md index fec3107..e8fc0b9 100644 --- a/src/Agoda.Analyzers/RuleContent/AG0013LimitNumberOfTestMethodParametersTo5.html +++ b/doc/AG0013.md @@ -1,4 +1,6 @@ -

+# Rule AG0013 + +

Limit number of test method input parameters to 5

diff --git a/doc/AG0018.md b/doc/AG0018.md new file mode 100644 index 0000000..db4610b --- /dev/null +++ b/doc/AG0018.md @@ -0,0 +1,52 @@ +# Return types implementing IEnumerable should use interface types + +ID: AG0018 + +Type: Code Smell + +## Summary + +Public methods and properties that return IEnumerable-implementing types must be declared using specific interface types rather than concrete implementations. + +## Explanation + +When exposing collections through public APIs, you should return interface types rather than concrete implementations. This provides better encapsulation, flexibility for future changes, and clearer contracts with consumers of your API. + +### Allowed Return Types + +- `IEnumerable` +- `ISet` +- `IList` +- `IDictionary` +- `IReadOnlyDictionary` +- `KeyedCollection` +- `byte[]` (special case for raw binary data) +- `string` (which implements `IEnumerable`) + +### Don't ❌ + +```csharp +public List GetStrings() { ... } +public List StringProperty { get; set; } +``` + +### Do ✅ + +```csharp +public IList GetStrings() { ... } +public IList StringProperty { get; set; } +``` + +## Why Avoid Concrete Types? + +Returning concrete types like `List` creates several problems: + +- Tightly couples your API to specific implementations +- Makes it harder to change implementations without breaking consumers +- Exposes unnecessary implementation details +- May allow consumers to depend on implementation-specific behaviors +- Reduces flexibility for performance optimization or behavior changes + +By using interface types, you maintain better control over your API's contract while giving yourself freedom to modify the underlying implementation as needed. + +This rule helps ensure consistent and maintainable APIs by enforcing the use of proper abstraction levels when exposing collections. \ No newline at end of file diff --git a/doc/AG0019.md b/doc/AG0019.md new file mode 100644 index 0000000..78dfecf --- /dev/null +++ b/doc/AG0019.md @@ -0,0 +1,61 @@ +# Prevent use of InternalsVisibleTo Attribute + +ID: AG0019 + +Type: Code Smell + +## Summary + +The `InternalsVisibleTo` attribute should not be used as it violates encapsulation principles and creates tight coupling between test code and implementation details. + +## Explanation + +Making internal members visible to test assemblies is a common but problematic practice that: + +- Violates encapsulation principles +- Creates tight coupling between tests and implementation +- Makes tests brittle and harder to maintain +- Makes refactoring more difficult +- Encourages testing implementation details instead of behavior + +You should only test the public surface of your classes. If you find yourself needing to test internal methods, consider: + +- Making the method public if it represents actual API surface +- Refactoring the code to better expose the behavior through public methods +- Rethinking the design to avoid the need to test internals + +### Don't ❌ + +```csharp +using System; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Agoda.Website.UnitTestFramework")] +namespace RoslynTest +{ + // ... +} +``` + +### Do ✅ + +```csharp +using System; + +namespace RoslynTest +{ + // Design your public API to be testable without exposing internals +} +``` + +## Why Avoid InternalsVisibleTo? + +Using `InternalsVisibleTo` leads to several problems: + +- Tests become coupled to implementation details rather than behavior +- Changes to internal implementation can break tests +- Makes refactoring more difficult as tests depend on internal structure +- Violates encapsulation principles +- Can hide poor API design by allowing tests to bypass public interfaces + +Focus on testing behavior through public interfaces rather than implementation details through internal members. \ No newline at end of file diff --git a/doc/AG0020.md b/doc/AG0020.md new file mode 100644 index 0000000..0889ab1 --- /dev/null +++ b/doc/AG0020.md @@ -0,0 +1,74 @@ +# Never Return Null From IEnumerable Methods/Properties + +ID: AG0020 + +Type: Bug / Code Smell + +## Summary + +Methods or properties returning `IEnumerable` types should never return null. Instead, return an empty collection. + +## Explanation + +Returning null from enumerable methods forces callers to handle special cases and can lead to `NullReferenceException`s in production. This creates unnecessary complexity and potential runtime errors. + +Instead: + +- Return an empty collection (`Enumerable.Empty()`) for no results +- Fix upstream services to never return null collections +- Use empty collections to represent the absence of items + +### Don't ❌ + +```csharp +public IEnumerable GetProductIds(int brandId) +{ + var products = productService.GetProductsForBrand(brandId); + + if (products == null || !products.Any()) + { + return null; // Dangerous! Forces null checks on caller + } + + return products.Select(p => p.Id); +} +``` + +### Do ✅ + +```csharp +public IEnumerable GetProductIds(int brandId) +{ + var products = productService.GetProductsForBrand(brandId); + + if (products == null) + { + return Enumerable.Empty(); + } + + return products.Select(p => p.Id); +} +``` + +### Best Practice ✅✅ + +```csharp +public IEnumerable GetProductIds(int brandId) +{ + // Ensure upstream services never return null + return productService + .GetProductsForBrand(brandId) + .Select(p => p.Id); +} +``` + +## Why Avoid Returning Null? + +- Prevents `NullReferenceException`s in production +- Reduces complexity by eliminating special case handling +- Makes code more predictable and easier to use +- Follows the principle of least surprise +- Makes LINQ operations work seamlessly +- Simplifies error handling and flow control + +Remember: Empty collections are the proper way to represent "no items" in enumerable results. diff --git a/doc/AG0021.md b/doc/AG0021.md new file mode 100644 index 0000000..819f008 --- /dev/null +++ b/doc/AG0021.md @@ -0,0 +1,68 @@ +# Use Async Methods When Available + +ID: AG0021 + +Type: Code Smell / Performance + +## Summary + +Always use the asynchronous version of a method when available. Using synchronous methods when async alternatives exist can impact performance and scalability. + +## Explanation + +Synchronous operations block threads while waiting for I/O or other operations to complete. This wastes resources and reduces application scalability. Modern .NET provides async versions for most I/O operations and external service calls. You should always prefer these async versions. + +Common patterns to avoid: + +- Using `File.ReadAllText()` instead of `File.ReadAllTextAsync()` +- Using `HttpClient.GetString()` instead of `HttpClient.GetStringAsync()` +- Using `Stream.Read()` instead of `Stream.ReadAsync()` +- Using `SqlCommand.ExecuteReader()` instead of `SqlCommand.ExecuteReaderAsync()` + +### Don't ❌ + +```csharp +public string GetData() +{ + using var client = new HttpClient(); + return client.GetString("https://api.example.com/data"); // Blocks thread +} + +public void SaveFile(string content) +{ + File.WriteAllText("file.txt", content); // Blocks thread +} +``` + +### Do ✅ + +```csharp +public async Task GetDataAsync() +{ + using var client = new HttpClient(); + return await client.GetStringAsync("https://api.example.com/data"); +} + +public async Task SaveFileAsync(string content) +{ + await File.WriteAllTextAsync("file.txt", content); +} +``` + +## Benefits of Async Methods + +- Improved application scalability +- Better resource utilization +- Increased responsiveness +- Higher throughput +- No thread blocking during I/O operations +- Better user experience in UI applications + +Always check for async alternatives when working with: + +- File operations +- Network calls +- Database operations +- External service calls +- Stream operations +- Task-based operations \ No newline at end of file diff --git a/doc/AG0022.md b/doc/AG0022.md new file mode 100644 index 0000000..4cb84f3 --- /dev/null +++ b/doc/AG0022.md @@ -0,0 +1,66 @@ +# Do Not Expose Both Sync and Async Versions of Methods + +ID: AG0022 + +Type: Code Smell / API Design + +## Summary + +When designing APIs that perform I/O or CPU-intensive work, expose only the asynchronous version of the method. Never provide both synchronous and asynchronous versions of the same functionality. + +## Explanation + +Providing both sync and async versions of methods: + +- Encourages poor practices and sub-optimal choices +- Creates confusion about which method to use +- Leads to performance issues when sync versions are chosen +- Violates the principle of least surprise + +Key rules: + +- If an operation can be async, it must only be async +- Always suffix async methods with "Async" +- Never provide a synchronous alternative +- Make the decision at design time: sync or async, not both + +### Don't ❌ + +```csharp +interface IFileDownloader +{ + byte[] DownloadFile(string url); // Don't expose sync version + Task DownloadFileAsync(string url); // alongside async +} + +interface IDataProcessor +{ + Result ProcessData(Data data); // Don't provide + Task ProcessDataAsync(Data data); // both options +} +``` + +### Do ✅ + +```csharp +interface IFileDownloader +{ + Task DownloadFileAsync(string url); // Async only +} + +interface IDataProcessor +{ + Task ProcessDataAsync(Data data); // Single, clear choice +} +``` + +## Why Avoid Dual Versions? + +- Prevents confusion about which method to use +- Forces good async practices by default +- Avoids accidental use of blocking operations +- Maintains consistency across the codebase +- Reduces maintenance burden +- Prevents performance issues from choosing sync version + +Remember: If an operation could be async, it should only be exposed as async. Never provide a "convenient" sync alternative. diff --git a/src/Agoda.Analyzers/RuleContent/AG0023PreventTheUseOfThreadSleep.html b/doc/AG0023.md similarity index 93% rename from src/Agoda.Analyzers/RuleContent/AG0023PreventTheUseOfThreadSleep.html rename to doc/AG0023.md index 7c19a47..d611d28 100644 --- a/src/Agoda.Analyzers/RuleContent/AG0023PreventTheUseOfThreadSleep.html +++ b/doc/AG0023.md @@ -1,4 +1,6 @@ -

+# Rule AG0023 + +

Use Task.Delay instead of Thread.Sleep

@@ -18,4 +20,4 @@

Noncompliant Code Examples

Compliant Code Example

     await Task.Delay(500);
-
\ No newline at end of file + diff --git a/doc/AG0024.md b/doc/AG0024.md new file mode 100644 index 0000000..bc075d9 --- /dev/null +++ b/doc/AG0024.md @@ -0,0 +1,62 @@ +# Use Task.Run Instead of Task.Factory.StartNew + +ID: AG0024 + +Type: Code Smell / Best Practice + +## Summary + +Avoid using `Task.Factory.StartNew` as it's complex and error-prone. Use `Task.Run` for most scenarios as it provides safer defaults and clearer semantics. + +## Explanation + +`Task.Factory.StartNew` is a low-level API with many pitfalls: + +- Complex configuration options that are easy to misuse +- Doesn't handle async delegates properly by default +- Can lead to unexpected behavior with task scheduling +- Requires careful consideration of TaskCreationOptions +- Can cause issues with task continuations + +Only use `Task.Factory.StartNew` in very specific scenarios, such as when `TaskCreationOptions.LongRunning` is needed (and even then, consider alternatives). + +### Don't ❌ + +```csharp +// Complex and error-prone +Task.Factory.StartNew(MyMethod, + CancellationToken.None, + TaskCreationOptions.DenyChildAttach, + TaskScheduler.Default); + +// Doesn't handle async delegates correctly +Task.Factory.StartNew(async () => await DoWorkAsync()); + +// Unnecessarily complex +Task.Factory.StartNew(() => DoWork(), + TaskCreationOptions.None); +``` + +### Do ✅ + +```csharp +// Simple and safe +Task.Run(() => MyMethod()); + +// Properly handles async delegates +Task.Run(async () => await DoWorkAsync()); + +// Clear intent, proper defaults +Task.Run(() => DoWork()); +``` + +## Why Avoid Task.Factory.StartNew? + +- More complex than necessary for most use cases +- Easy to misconfigure +- Can cause unexpected behaviors +- Poor handling of async delegates by default +- Requires deep understanding of TPL internals +- `Task.Run` provides better defaults and safer behavior + +Remember: Use `Task.Run` by default. Only consider `Task.Factory.StartNew` if you have a specific need that's been verified through profiling. diff --git a/src/Agoda.Analyzers/RuleContent/AG0025PreventUseOfTaskContinue.html b/doc/AG0025.md similarity index 96% rename from src/Agoda.Analyzers/RuleContent/AG0025PreventUseOfTaskContinue.html rename to doc/AG0025.md index 4fce180..e075c25 100644 --- a/src/Agoda.Analyzers/RuleContent/AG0025PreventUseOfTaskContinue.html +++ b/doc/AG0025.md @@ -1,4 +1,6 @@ -

+# Rule AG0025 + +

Use await instead of Task.ContinueWith

diff --git a/src/Agoda.Analyzers/RuleContent/AG0026EnsureOnlyCssSelectorIsUsedToFindElements.html b/doc/AG0026.md similarity index 95% rename from src/Agoda.Analyzers/RuleContent/AG0026EnsureOnlyCssSelectorIsUsedToFindElements.html rename to doc/AG0026.md index fc10c03..c8cf8cf 100644 --- a/src/Agoda.Analyzers/RuleContent/AG0026EnsureOnlyCssSelectorIsUsedToFindElements.html +++ b/doc/AG0026.md @@ -1,4 +1,6 @@ -

+# Rule AG0026 + +

Use only CSS Selectors to find elements in Selenium tests

@@ -22,4 +24,4 @@

Do

Do

     Driver.FindElementsByCssSelector("[data-selenium=hotel-item]")
-
\ No newline at end of file + diff --git a/doc/AG0027.md b/doc/AG0027.md new file mode 100644 index 0000000..a208280 --- /dev/null +++ b/doc/AG0027.md @@ -0,0 +1,64 @@ +# Use data-selenium Attributes in Selenium Tests + +ID: AG0027 + +Type: Best Practice + +## Summary + +Use the `data-selenium` attribute to identify elements in Selenium tests rather than relying on HTML structure, classes, or IDs. This makes tests more resilient to UI changes. + +## Explanation + +Using HTML structure, classes, or IDs for test element selection: + +- Couples tests to presentation logic +- Makes tests brittle to UI changes +- Complicates test maintenance +- Creates conflicts between styling and testing needs +- Makes element targeting unclear + +The `data-selenium` attribute provides a dedicated hook for tests that won't be affected by UI changes. + +### Don't ❌ + +```html +
+ +
+``` + +```csharp +// Brittle to UI changes +Driver.FindElement(By.CssSelector("form button.login-button")); + +// Coupled to styling classes +Driver.FindElement(By.ClassName("login-button")); + +// Dependent on HTML structure +Driver.FindElement(By.CssSelector("form > button")); +``` + +### Do ✅ + +```html +
+ +
+``` + +```csharp +// Clear and resilient +Driver.FindElement(By.CssSelector("[data-selenium=login-button]")); +``` + +## Why Use data-selenium? + +- Decouples tests from UI implementation +- Makes tests more resilient to changes +- Clearly indicates elements used in tests +- Separates testing concerns from styling +- Improves test maintainability +- Minimal impact on page size (especially when gzipped) + +Remember: The small overhead of adding `data-selenium` attributes is worth the improved test reliability and maintainability. diff --git a/doc/AG0030.md b/doc/AG0030.md new file mode 100644 index 0000000..b5e3875 --- /dev/null +++ b/doc/AG0030.md @@ -0,0 +1,58 @@ +# Do Not Use dynamics in C# + +ID: AG0030 + +Type: Bug Risk / Code Smell + +## Summary + +The `dynamic` type should not be used in C# code. It bypasses compile-time type checking and reduces code reliability. + +## Explanation + +The `dynamic` type was designed for interoperating with dynamically typed languages, but in a statically typed codebase, it: + +- Bypasses compile-time type checking +- Hides errors until runtime +- Makes code harder to understand and maintain +- Reduces IDE support and refactoring capabilities +- Degrades performance +- Makes code more error-prone +- Complicates debugging + +### Don't ❌ + +```csharp +// Runtime errors instead of compile-time +dynamic data = GetData(); +int result = data.SomeMethod(); // Could fail at runtime + +// Type safety lost +dynamic config = new ExpandoObject(); +config.Setting = "value"; // No IntelliSense or compile checks + +// Unnecessary dynamic usage +dynamic json = JsonConvert.DeserializeObject(jsonString); +var name = json.name; // Use strongly typed models instead +``` + +## Why Avoid dynamic? + +1. **Safety**: No compile-time type checking +2. **Performance**: Runtime binding is slower +3. **Tooling**: Limited IDE support and refactoring +4. **Maintainability**: Code intent is unclear +5. **Debugging**: More difficult to trace issues +6. **Reliability**: More prone to runtime errors +7. **IL Bloat**: The C# compiler generates significant amounts of additional IL code to handle dynamic dispatch, increasing assembly size and load time +8. **Runtime Overhead**: The DLR (Dynamic Language Runtime) infrastructure needed to support dynamic operations adds complexity to the IL and requires additional runtime resources + +Instead of `dynamic`: + +- Use strongly typed classes/models +- Use interfaces for polymorphism +- Use generics for flexible typing +- Create proper type hierarchies +- Use pattern matching for type handling + +Remember: There's almost always a better statically-typed solution than using `dynamic`. The additional IL code generated for dynamic dispatch not only increases your assembly size but also adds unnecessary complexity to your application's runtime behavior. diff --git a/doc/AG0032.md b/doc/AG0032.md new file mode 100644 index 0000000..a4506e0 --- /dev/null +++ b/doc/AG0032.md @@ -0,0 +1,74 @@ +# Don't Use Blocking Task Methods + +ID: AG0032 + +Type: Bug / Performance + +## Summary + +Avoid using blocking Task methods like `Task.Wait()`, `Task.WaitAll()`, and `Task.WaitAny()`. These methods block threads and can cause deadlocks. + +## Explanation + +Blocking task methods: + +- Defeat the purpose of async programming +- Can cause deadlocks, especially in UI applications +- Waste thread pool resources +- Reduce application scalability +- Create responsiveness issues +- Make debugging more difficult + +Instead, use the async alternatives: + +- `await task` +- `await Task.WhenAll()` +- `await Task.WhenAny()` + +### Don't ❌ + +```csharp +// Blocks thread and risks deadlock +var task = DoSomethingAsync(); +task.Wait(); + +// Multiple task blocking +var task1 = DownloadFileAsync("..."); +var task2 = DownloadFileAsync("..."); +Task.WaitAll(task1, task2); + +// Blocking in UI context +button.Click += (s, e) => { + var task = LoadDataAsync(); + task.Wait(); // Can deadlock UI +}; +``` + +### Do ✅ + +```csharp +// Proper async/await usage +await DoSomethingAsync(); + +// Multiple task awaiting +var task1 = DownloadFileAsync("..."); +var task2 = DownloadFileAsync("..."); +await Task.WhenAll(task1, task2); + +// Async event handler +button.Click += async (s, e) => { + await LoadDataAsync(); +}; +``` + +## Why Avoid Blocking Methods? + +- Prevent deadlocks +- Maintain application responsiveness +- Better thread utilization +- Improved scalability +- Follows async/await best practices +- Easier to debug and maintain +- More predictable behavior + +Remember: If you find yourself using `Task.Wait*` methods, you're likely doing something wrong in your async design. diff --git a/doc/AG0033.md b/doc/AG0033.md new file mode 100644 index 0000000..4bc7d5c --- /dev/null +++ b/doc/AG0033.md @@ -0,0 +1,72 @@ +# Don't Use Task.Result + +ID: AG0033 + +Type: Bug / Performance + +## Summary + +Never use `Task.Result` to get the result of a Task. Use `await` instead. `Task.Result` blocks the current thread and can cause deadlocks. + +## Explanation + +Using `Task.Result`: + +- Blocks the current thread until completion +- Can cause deadlocks, especially in UI or ASP.NET contexts +- Defeats the purpose of async/await +- Wastes thread pool resources +- Makes exception handling more difficult +- Can harm application scalability + +### Don't ❌ + +```csharp +// Risks deadlock and blocks thread +var task = GetDataAsync(); +var result = task.Result; + +// Multiple blocking calls +var task1 = FetchUserAsync(); +var task2 = FetchOrdersAsync(); +var user = task1.Result; // Blocking +var orders = task2.Result; // Blocking + +// Common antipattern in constructors +public class Service +{ + public Service() + { + // Extremely dangerous - can deadlock + _data = InitializeAsync().Result; + } +} +``` + +### Do ✅ + +```csharp +// Safe and efficient +var result = await GetDataAsync(); + +// Handle multiple tasks properly +var user = await FetchUserAsync(); +var orders = await FetchOrdersAsync(); + +// Better yet, run in parallel +var userTask = FetchUserAsync(); +var ordersTask = FetchOrdersAsync(); +await Task.WhenAll(userTask, ordersTask); +``` + +## Why Avoid Task.Result? + +- Prevents deadlocks +- Maintains responsiveness +- Better thread utilization +- Proper exception propagation +- Follows async/await patterns +- More maintainable code +- Improved performance + +Remember: If you find yourself wanting to use `Task.Result`, you should probably restructure your code to use async/await properly instead. \ No newline at end of file diff --git a/src/Agoda.Analyzers/RuleContent/AG0035PreventUseOfMachineName.html b/doc/AG0035.md similarity index 96% rename from src/Agoda.Analyzers/RuleContent/AG0035PreventUseOfMachineName.html rename to doc/AG0035.md index c60fbb9..00db013 100644 --- a/src/Agoda.Analyzers/RuleContent/AG0035PreventUseOfMachineName.html +++ b/doc/AG0035.md @@ -1,4 +1,6 @@ -

+# Rule AG0035 + +

Do not use MachineName.

    @@ -8,4 +10,4 @@

One exception is logging, where it can be useful to see the exact DC / cluster / server that made the request. Here, it makes sense to log the machine name. -

\ No newline at end of file +

diff --git a/src/Agoda.Analyzers/RuleContent/AG0037EnsureSeleniumTestHasOwnedByAttribute.html b/doc/AG0037.md similarity index 97% rename from src/Agoda.Analyzers/RuleContent/AG0037EnsureSeleniumTestHasOwnedByAttribute.html rename to doc/AG0037.md index b1a34a7..72bb253 100644 --- a/src/Agoda.Analyzers/RuleContent/AG0037EnsureSeleniumTestHasOwnedByAttribute.html +++ b/doc/AG0037.md @@ -1,4 +1,6 @@ -

+# Rule AG0037 + +

A Selenium test must indicate the team responsible for its maintenance.

@@ -63,4 +65,4 @@

Compliant Code Examples

} } } - \ No newline at end of file + diff --git a/src/Agoda.Analyzers/RuleContent/AG0038PreventUseOfRegionPreprocessorDirective.html b/doc/AG0038.md similarity index 95% rename from src/Agoda.Analyzers/RuleContent/AG0038PreventUseOfRegionPreprocessorDirective.html rename to doc/AG0038.md index 20ea544..9115987 100644 --- a/src/Agoda.Analyzers/RuleContent/AG0038PreventUseOfRegionPreprocessorDirective.html +++ b/doc/AG0038.md @@ -1,4 +1,6 @@ -

+# Rule AG0038 + +

Do not use #region

    @@ -9,4 +11,4 @@
  • In many editors, including Visual Studio, the region will appear collapsed by default, hiding the code within the region. It is generally bad practice to hide code by default, as this can lead to bad decisions as the code is maintained over time.
  • -
\ No newline at end of file + diff --git a/doc/AG0039.md b/doc/AG0039.md new file mode 100644 index 0000000..30c8f93 --- /dev/null +++ b/doc/AG0039.md @@ -0,0 +1,113 @@ +# Long Methods Should Be Refactored + +ID: AG0039 + +Type: Code Smell + +## Summary + +Methods should be kept reasonably short (recommended max 40 lines excluding whitespace). Long methods should be refactored into smaller, more focused methods while avoiding method chaining. + +## Explanation + +Long methods often: + +- Are harder to understand +- Have multiple responsibilities +- Are difficult to test +- Are prone to bugs +- Hide business logic +- Are harder to maintain + +When refactoring, avoid creating chains of method calls that simply pass data through. + +### Don't ❌ + +```csharp +public class UserProcessor +{ + // Long method with multiple responsibilities + public void ProcessUser(User user) + { + // 50+ lines of code doing multiple things: + // - Validate user + // - Update database + // - Send notifications + // - Log activities + // - Update cache + // etc... + } + + // Bad refactoring - method chaining + public void ProcessUser(User user) + { + ValidateUser(user); + } + + private void ValidateUser(User user) + { + // ... + UpdateDatabase(user); + } + + private void UpdateDatabase(User user) + { + // ... + SendNotification(user); + } + private void SendNotification(User user) + { + // ... + LogActivity(user); + } + private void LogActivity(User user) + { + // ... + UpdateCache(user); + } +} +``` + +### Do ✅ + +Very basic initial approach, then after this you should look at breaking down the private methods potentially into other classes depending on their size. + +```csharp +public class UserProcessor +{ + public void ProcessUser(User user) + { + ValidateUser(user); + UpdateDatabase(user); + SendNotification(user); + LogActivity(user); + UpdateCache(user); + } + + private void ValidateUser(User user) + { + // focused validation logic + } + + private void UpdateDatabase(User user) + { + // focused database update + } + + // Additional focused methods... +} +``` + +## Why Refactor Long Methods? + +- Improves code readability +- Makes testing easier +- Enhances maintainability +- Clarifies business logic +- Reduces cognitive load +- Makes debugging easier +- Follows Single Responsibility Principle + +If you are having problems with breaking methods down, try Martin Fowler's Book called refactoring https://www.amazon.com/Refactoring-Improving-Existing-Addison-Wesley-Signature/dp/0134757599/ + +Remember: While 40 lines is a guideline, focus on making methods do one thing well rather than strictly adhering to a line count. diff --git a/doc/AG0040.md b/doc/AG0040.md new file mode 100644 index 0000000..a69c95d --- /dev/null +++ b/doc/AG0040.md @@ -0,0 +1,3 @@ +# Rule AG0040 + +No HTML documentation available. Rule name: $name diff --git a/doc/AG0041.md b/doc/AG0041.md index 577fe6a..f70f2fc 100644 --- a/doc/AG0041.md +++ b/doc/AG0041.md @@ -1,68 +1,65 @@ -# AG0041: Use message templates in logging +# Use Message Templates in Logging -Structured logging should be employed instead of string interpolation or concatenation. +ID: AG0041 -## Why is this an issue? +Type: Bug / Performance / Best Practice -Logging is a critical aspect of any application, providing insights into its behavior and facilitating debugging. -However, not all logging approaches are created equal. Using string interpolation or concatenation for log messages is -a common pitfall that can lead to performance issues and hinder log analysis. +## Summary -Consider the following problematic code: +Use structured logging with message templates instead of string interpolation or concatenation. This improves performance, enables better log analysis, and follows logging best practices. -```csharp -logger.Information($"User {userId} logged in at {DateTime.Now}"); // Noncompliant -logger.Information("Error occurred: " + errorMessage); // Noncompliant -``` - -Instead, you should use message templates: - -```csharp -logger.Information("User {UserId} logged in at {LoginTime}", userId, DateTime.Now); // Compliant -logger.Information("Error occurred: {ErrorMessage}", errorMessage); // Compliant -``` +## Explanation -## Benefits of using message templates +String interpolation and concatenation in logging: -The use of message templates in logging offers several significant advantages over traditional string interpolation or -concatenation methods. Firstly, message templates can provide a notable performance boost, particularly in scenarios where -log messages aren't actually written due to log level settings. This efficiency is crucial in high-volume logging environments -where every millisecond counts. +- Reduces performance due to unnecessary string operations +- Makes log analysis more difficult +- Loses semantic information +- Can't be properly indexed or searched +- Doesn't support structured data +- Creates inconsistent log formats -Structured logging, enabled by message templates, is another key benefit. It allows variables to be captured as separate entities -rather than being merged into a single string. This separation enables powerful log analysis capabilities, making it easier to search, -filter, and aggregate log data based on specific parameters. The ability to maintain consistent log formats across an application -is also greatly enhanced with message templates, leading to more uniform and easily interpretable logs. +### Don't ❌ -Message templates also facilitate semantic logging, allowing developers to attach additional metadata to log events. This extra -context can be invaluable when debugging complex issues or analyzing application behavior. Furthermore, modern logging frameworks such -as Serilog and Microsoft.Extensions.Logging are optimized for template-style logging. By using message templates, developers can -take full advantage of these frameworks' features and performance optimizations, ensuring their logging strategy is future-proof -and aligned with industry best practices. +```csharp +// String interpolation - loses structure +logger.Information($"User {userId} logged in at {DateTime.Now}"); -## More Info +// String concatenation - poor performance +logger.Information("Error occurred: " + errorMessage); -This analyzer checks for the use of string interpolation (`$"..."`) or string concatenation in logging method calls. It specifically targets methods of `ILogger` from both `Microsoft.Extensions.Logging` and `Serilog`. +// Multiple interpolations +_logger.LogError($"Failed to process order {orderId} for user {userId} with error: {error}"); +``` -### Noncompliant Code Examples +### Do ✅ ```csharp -_logger.LogInformation($"Processing order {orderId}"); -_logger.LogError("Error: " + exception.Message); -``` +// Message templates with named parameters +logger.Information("User {UserId} logged in at {LoginTime}", userId, DateTime.Now); -### Compliant Solution +// Simple message template +logger.Information("Error occurred: {ErrorMessage}", errorMessage); -```csharp -_logger.LogInformation("Processing order {OrderId}", orderId); -_logger.LogError("Error: {ErrorMessage}", exception.Message); +// Multiple parameters +_logger.LogError("Failed to process order {OrderId} for user {UserId} with error: {Error}", + orderId, userId, error); ``` -### Exceptions +## Why Use Message Templates? + +- Better performance (avoids string operations when not logging) +- Enables structured logging +- Supports semantic logging +- Easier to analyze and search logs +- Maintains consistent log formats +- Works better with modern logging frameworks +- Allows for proper indexing and querying +- Adds valuable metadata to log events -This rule doesn't apply to non-logging methods or to logging methods that explicitly expect formatted strings. +Remember: Message templates should always be literal strings, with parameters passed as separate arguments. ## Resources * [Structured logging with ILogger in .NET](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-5.0#log-message-template) -* [Serilog message templates](https://github.com/serilog/serilog/wiki/Writing-Log-Events#message-templates) +* [Serilog message templates](https://github.com/serilog/serilog/wiki/Writing-Log-Events#message-templates) \ No newline at end of file diff --git a/doc/AG0042.md b/doc/AG0042.md index 18d387e..ea8a1f2 100644 --- a/doc/AG0042.md +++ b/doc/AG0042.md @@ -1,94 +1,70 @@ -# AG0042: ElementHandle methods should not be used with Playwright +# Don't Use ElementHandle Methods in Playwright -## Problem Description +ID: AG0042 -Using methods that return ElementHandle (such as `QuerySelectorAsync()`, `QuerySelectorAllAsync()`, `WaitForSelectorAsync()`, etc.) in Playwright tests can lead to brittle and unreliable tests. These methods often rely on CSS selectors which can be fragile and may break when the UI structure changes. Instead, more reliable locator strategies like data-testid or role-based selectors should be used. +Type: Bug / Test Reliability -## Rule Details +## Summary -This rule raises an issue when any method returning an `IElementHandle`, `ElementHandle`, or their nullable variants is called on Playwright `IPage`, `Page`, or `IElementHandle` objects. +Avoid using methods that return ElementHandle (like `QuerySelectorAsync()`, `QuerySelectorAllAsync()`, `WaitForSelectorAsync()`) in Playwright tests. These methods create brittle tests and can lead to flaky behavior. -### Noncompliant Code Examples +## Explanation -```csharp -public async Task InteractWithElements(IPage page) -{ - // Noncompliant: Using methods that return ElementHandle - var loginButton = await page.QuerySelectorAsync(".login-button"); - var menuItems = await page.QuerySelectorAllAsync(".menu-item"); - var dynamicElement = await page.WaitForSelectorAsync("#dynamic-content"); - - // Noncompliant: Chaining ElementHandle operations - var childElement = await loginButton.QuerySelectorAsync(".child"); -} -``` +ElementHandle methods: -### Compliant Solution +- Create stale references when DOM changes +- Rely on fragile CSS selectors +- Are tightly coupled to DOM structure +- Lead to flaky tests +- Are harder to maintain +- Don't follow Playwright best practices -```csharp -public async Task InteractWithElements(IPage page) -{ - // Compliant: Using Locator with data-testid - await page.GetByTestId("login-button").ClickAsync(); - - // Compliant: Using role-based selector - await page.GetByRole(AriaRole.Button, new() { Name = "Login" }).ClickAsync(); - - // Compliant: Using text content - await page.GetByText("Login").ClickAsync(); - - // Compliant: Working with multiple elements - var menuItems = page.Locator("[data-testid='menu-item']"); - await menuItems.First.ClickAsync(); - - // Compliant: Waiting for elements - await page.Locator("#dynamic-content").WaitForAsync(); -} -``` +### Don't ❌ -## Why is this an Issue? +```csharp +// Using ElementHandle methods +var loginButton = await page.QuerySelectorAsync(".login-button"); +await loginButton.ClickAsync(); -1. **ElementHandle Limitations**: ElementHandle references can become stale when the DOM changes, leading to flaky tests. +// Chaining ElementHandle calls +var menuItem = await page + .QuerySelectorAsync(".menu") + .QuerySelectorAsync(".item"); -2. **Fragile Selectors**: CSS selectors are tightly coupled to the DOM structure and styling classes, making tests brittle when: - - CSS classes are renamed or removed - - DOM hierarchy changes - - Styling frameworks are updated +// Waiting with ElementHandle +var element = await page.WaitForSelectorAsync("#dynamic-content"); +``` -3. **Maintainability**: ElementHandle-based selectors can be complex and hard to maintain, especially when dealing with nested elements. +### Do ✅ -4. **Best Practices**: Playwright provides better alternatives through Locators that are: - - More resilient to changes - - More readable and maintainable - - Better aligned with testing best practices - - Auto-waiting and retry-able +```csharp +// Using data-testid (preferred) +await page.GetByTestId("login-button").ClickAsync(); -## Better Alternatives +// Using role-based selectors +await page.GetByRole(AriaRole.Button, new() { Name = "Login" }).ClickAsync(); -Playwright provides several better methods for selecting and interacting with elements: +// Using text content +await page.GetByText("Submit").ClickAsync(); -1. **Data Test IDs**: - ```csharp - await page.GetByTestId("submit-button").ClickAsync(); - ``` +// Using labels and placeholders +await page.GetByLabel("Username").FillAsync("user"); +await page.GetByPlaceholder("Enter email").FillAsync("test@example.com"); +``` -2. **Role-based Selectors**: - ```csharp - await page.GetByRole(AriaRole.Button).ClickAsync(); - await page.GetByRole(AriaRole.Textbox, new() { Name = "Username" }).FillAsync("user"); - ``` +## Why Avoid ElementHandle? -3. **Text Content**: - ```csharp - await page.GetByText("Sign up").ClickAsync(); - await page.GetByLabel("Password").FillAsync("secret"); - ``` +- References become stale with DOM changes +- CSS selectors are fragile +- Poor maintainability +- No auto-waiting/retry mechanism +- Not recommended by Playwright +- Can cause test flakiness +- Harder to debug failures -4. **Placeholder Text**: - ```csharp - await page.GetByPlaceholder("Enter email").FillAsync("test@example.com"); - ``` +Remember: Use Playwright's built-in Locators and role-based selectors for more reliable and maintainable tests. ## References + - [ElementHandle is Discouraged by official Documents](https://playwright.dev/dotnet/docs/api/class-elementhandle) -- [Playwright Locators Documentation](https://playwright.dev/docs/locators) +- [Playwright Locators Documentation](https://playwright.dev/docs/locators) \ No newline at end of file diff --git a/doc/AG0043.md b/doc/AG0043.md index 0e1861e..7ff2963 100644 --- a/doc/AG0043.md +++ b/doc/AG0043.md @@ -1,121 +1,81 @@ -# AG0043: BuildServiceProvider should not be used in production code +# Don't Use BuildServiceProvider in Production Code -## Problem Description -Using `BuildServiceProvider()` in production code can lead to memory leaks and other issues because it creates a new container. This is especially problematic when called repeatedly, such as in request processing scenarios. +ID: AG0043 -## Rule Details -This rule raises an issue when `BuildServiceProvider()` is called on `IServiceCollection` instances. +Type: Bug / Memory Leak -### Noncompliant Code Examples +## Summary + +Avoid using `BuildServiceProvider()` in production code as it creates new dependency injection containers, which can cause memory leaks and performance issues. + +## Explanation + +Using `BuildServiceProvider()`: + +- Creates memory leaks with repeated calls +- Impacts performance negatively +- Duplicates singleton instances +- Breaks proper service scoping +- Creates unnecessary container instances +- Wastes system resources + +### Don't ❌ -#### Traditional ASP.NET Core ```csharp +// Creating new container in service configuration public void ConfigureServices(IServiceCollection services) { services.AddTransient(); - var serviceProvider = services.BuildServiceProvider(); // Noncompliant + var serviceProvider = services.BuildServiceProvider(); // Memory leak risk var myService = serviceProvider.GetService(); } -``` - -#### Minimal API -```csharp -var builder = WebApplication.CreateBuilder(args); - -builder.Services.AddTransient(); - -var app = builder.Build(); +// Creating container in request handling app.MapGet("/", () => { - var serviceProvider = app.Services.BuildServiceProvider(); // Noncompliant - var myService = serviceProvider.GetService(); - return myService.GetMessage(); + var serviceProvider = app.Services.BuildServiceProvider(); // Very bad! + return serviceProvider.GetService(); }); - -app.Run(); ``` -### Compliant Solutions +### Do ✅ -#### Traditional ASP.NET Core ```csharp -public class Startup +// Let framework manage container +public void ConfigureServices(IServiceCollection services) { - public void ConfigureServices(IServiceCollection services) - { - services.AddTransient(); - // Let ASP.NET Core build the service provider - } - public void Configure(IApplicationBuilder app, IMyService myService) + services.AddTransient(); +} + +// Use dependency injection +public class MyController +{ + private readonly IMyService _myService; + + public MyController(IMyService myService) { - // Services are injected by the framework + _myService = myService; } } -``` - -#### Minimal API -```csharp -var builder = WebApplication.CreateBuilder(args); - -builder.Services.AddTransient(); - -var app = builder.Build(); +// Minimal API with injection app.MapGet("/", (IMyService myService) => myService.GetMessage()); +``` -app.Run(); +## Why Avoid BuildServiceProvider? -// Service interfaces and implementations -public interface IMyService -{ - string GetMessage(); -} +- Prevents memory leaks +- Better performance +- Proper singleton behavior +- Correct service scoping +- Framework-managed lifecycle +- Reduced resource usage -public class MyService : IMyService -{ - public string GetMessage() => "Hello from MyService!"; -} -``` +Exception: Only use in development for configuration validation or in unit tests. -## Why is this an Issue? -1. **Memory Leaks**: Each call to `BuildServiceProvider()` creates a new dependency injection container, which holds references to all registered services. If called repeatedly (e.g., during request processing), this leads to memory leaks. -2. **Performance Impact**: Creating new service providers is computationally expensive and can impact application performance. -3. **Singleton Duplication**: Multiple service providers can result in multiple instances of services that should be singletons. - -## Exceptions -`BuildServiceProvider()` may be acceptable in the following scenarios: -- Unit tests where a full DI container is needed -- Development-time configuration validation -- Tools and utilities that run outside the normal application lifecycle - -## How to Fix It -1. In ASP.NET Core applications: - - Let the framework handle service provider creation - - Use constructor injection to obtain dependencies - - Use `IServiceScope` for creating scoped service providers when needed - - `HttpContext` and other services have methods like `RequestServices.GetService()` to get scoped services -2. For configuration validation: - ```csharp - public void ConfigureServices(IServiceCollection services) - { - services.AddTransient(); - // Only in development - if (Environment.IsDevelopment()) - { - // Validate service registration - var serviceProvider = services.BuildServiceProvider(validateScopes: true); - serviceProvider.Dispose(); - } - } - ``` - -## Benefits -- Prevents memory leaks -- Improves application performance -- Ensures proper singleton behavior -- Maintains proper service scoping +Remember: Let the framework manage the service container lifecycle and use dependency injection for accessing services. ## References + - [ASP.NET Core Dependency Injection Best Practices](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection#service-lifetimes) - [Dependency injection in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection) diff --git a/doc/AG0044.md b/doc/AG0044.md index 7b83e1b..7fc2f43 100644 --- a/doc/AG0044.md +++ b/doc/AG0044.md @@ -1,106 +1,73 @@ -# AG0044: Force option should not be used in Locator methods +# Don't Use Force Option in Playwright Locators -## Problem Description -Using `Force = true` in Playwright's Locator methods bypasses important built-in validation checks. This can lead to brittle tests, inconsistent behavior, and difficulty in identifying real UI/UX issues. +ID: AG0044 -## Rule Details -This rule raises an issue when `Force = true` is set in options objects passed to ILocator action methods. +Type: Bug / Test Reliability -### Noncompliant Code Examples +## Summary -#### Using Force in Click Actions -```csharp -public async Task ClickButtonAsync(ILocator button) -{ - // Noncompliant: Forces click without proper element state validation - await button.ClickAsync(new() { Force = true }); -} -``` +Avoid using `Force = true` in Playwright's Locator methods. This option bypasses important validation checks and creates unreliable tests that don't accurately reflect real user interactions. -#### Using Force in Multiple Actions -```csharp -public async Task FillFormAsync(ILocator form) -{ - var textbox = form.Locator(".textbox"); - var checkbox = form.Locator(".checkbox"); - var dropdown = form.Locator(".select"); - - // Noncompliant: Multiple forced interactions - await textbox.FillAsync("text", new() { Force = true }); - await checkbox.CheckAsync(new() { Force = true }); - await dropdown.SelectOptionAsync("option", new() { Force = true }); -} -``` +## Explanation -#### Pre-defined Options Object -```csharp -public async Task InteractWithElementAsync(ILocator element) -{ - // Noncompliant: Force option in pre-defined options - var options = new LocatorClickOptions { Force = true }; - await element.ClickAsync(options); -} -``` +Using Force option: -### Compliant Solutions +- Skips critical element state validations +- Hides real UI/UX issues +- Creates unreliable tests +- Makes debugging harder +- Doesn't reflect actual user behavior +- Can mask timing problems -#### Using Default Behavior -```csharp -public async Task ClickButtonAsync(ILocator button) -{ - // Compliant: Uses Playwright's built-in waiting and validation - await button.ClickAsync(); -} -``` +### Don't ❌ -#### Using Proper Wait Strategies ```csharp -public async Task FillFormAsync(ILocator form) -{ - var textbox = form.Locator(".textbox"); - var checkbox = form.Locator(".checkbox"); - var dropdown = form.Locator(".select"); - - // Compliant: Uses appropriate waiting and state checks - await textbox.WaitForAsync(); - await textbox.FillAsync("text"); - - await checkbox.WaitForAsync(new() { State = WaitForSelectorState.Attached }); - await checkbox.CheckAsync(); - - await dropdown.WaitForAsync(new() { State = WaitForSelectorState.Visible }); - await dropdown.SelectOptionAsync("option"); -} +// Forcing clicks without validation +await button.ClickAsync(new() { Force = true }); + +// Multiple forced interactions +await textbox.FillAsync("text", new() { Force = true }); +await checkbox.CheckAsync(new() { Force = true }); + +// Pre-defined forced options +var options = new LocatorClickOptions { Force = true }; +await element.ClickAsync(options); ``` -#### Using Custom Timeout When Needed +### Do ✅ + ```csharp -public async Task InteractWithSlowElementAsync(ILocator element) -{ - // Compliant: Adjusts timeout instead of forcing interaction - await element.ClickAsync(new() { Timeout = 10000 }); -} +// Use default behavior +await button.ClickAsync(); + +// Use proper wait strategies +await textbox.WaitForAsync(); +await textbox.FillAsync("text"); + +// Adjust timeouts if needed +await element.ClickAsync(new() { Timeout = 10000 }); + +// Wait for specific states +await element.WaitForAsync(new() +{ + State = WaitForSelectorState.Visible +}); ``` -## Why is this an Issue? -1. **Bypasses Important Checks**: Force option bypasses Playwright's built-in validations for: - - Element visibility - - Element being attached to DOM - - Element being enabled - - Element being stable (not moving) - -2. **Masks Real Issues**: Using Force can hide actual problems in the application: - - Elements that are not properly visible to users - - Race conditions in UI rendering - - Timing issues in dynamic content loading - - Accessibility problems - -3. **Unreliable Tests**: Tests using Force: - - May pass locally but fail in CI/CD - - Don't accurately reflect real user interactions - - Can become flaky or inconsistent - - Are harder to debug when they fail +## Why Avoid Force Option? + +- Skips visibility checks +- Ignores element attachment to DOM +- Bypasses enabled state validation +- Misses element stability checks +- Creates flaky tests +- Hides real application issues +- Makes tests unreliable +- Doesn't match user behavior + +Remember: Let Playwright's built-in validations ensure your tests accurately reflect real user interactions. ## References + - [Playwright Actionability](https://playwright.dev/dotnet/docs/actionability) - [Playwright Forcing Actions](https://playwright.dev/dotnet/docs/actionability#forcing-actions) diff --git a/doc/prompt.md b/doc/prompt.md new file mode 100644 index 0000000..63dd311 --- /dev/null +++ b/doc/prompt.md @@ -0,0 +1,185 @@ + +Here is a template to use for markdown, followed by the description of the rule rule please create the markdown for this following the template example. +Be concise provide examples of non compliant as well as compliant code, if the rule is a blocker (meaning the compliant example is not needed because the rule says "dont use this"), then explain why we should not write code like this and the risks +follow standard common linting rules for markdown + +MARKDOWN TEMPLATE HERE +--- + +# Type inheritance should not be recursive + +ID: AG0000 + +Type: Vulnerability / Bug / Code Smell / Quick Fix + +Recursion is a technique used to define a problem in terms of the problem itself, usually in terms of a simpler version of the problem itself. + +For example, the implementation of the generator for the n-th value of the Fibonacci sequence comes naturally from its mathematical definition, when recursion is used: + +```csharp +int NthFibonacciNumber(int n) +{ + if (n <= 1) + { + return 1; + } + else + { + return NthFibonacciNumber(n - 1) + NthFibonacciNumber(n - 2); + } +} +``` + +As opposed to: + +```csharp +int NthFibonacciNumber(int n) +{ + int previous = 0; + int last = 1; + for (var i = 0; i < n; i++) + { + (previous, last) = (last, last + previous); + } + return last; +} +``` + +The use of recursion is acceptable in methods, like the one above, where you can break out of it. + +```csharp +int NthFibonacciNumber(int n) +{ + if (n <= 1) + { + return 1; // Base case: stop the recursion + } + // ... +} +``` + +It is also acceptable and makes sense in some type definitions: + +```csharp +class Box : IComparable +{ + public int CompareTo(Box? other) + { + // Compare the two Box instances... + } +} +``` + +With types, some invalid recursive definitions are caught by the compiler: + +```csharp +class C2 : C2 // Error CS0146: Circular base type dependency +{ +} + +class C2 : C2> // Error CS0146: Circular base type dependency +{ +} +``` + +In more complex scenarios, however, the code will compile but execution will result in a TypeLoadException if you try to instantiate the class. + +```csharp +class C1 +{ +} + +class C2 : C1>> // Noncompliant +{ +} + +var c2 = new C2(); // This would result into a TypeLoadException +``` + +WRITING STYLE TO FOLLOW +--- + +## Avoid blocking when possible + +- Increase performance and scalability of your code by avoiding blocking calls. +- A blocking call ties up the thread while it waits for a response. During this time, it could have been doing something more useful, like serving other requests or redrawing the GUI. +- A non-blocking call returns the thread to the threadpool, or keeps the GUI thread responsive, while the task completes. + +#### Don't +```c# +Thread.Sleep(5000); // thread is blocked for 5 seconds +``` + +#### Do +```c# +await Task.Delay(5000); // thread can do other stuff for 5 seconds. +``` + +#### Don't +```c# +public void CreateCsv() +{ + using(var writer = File.CreateText("myfile.csv")) + { + writer.WriteLine("..."); + } +} +``` + +#### Do +```c# +public async Task CreateCsvAsync() +{ + using(var writer = File.CreateText("myfile.csv")) + { + await writer.WriteLineAsync("..."); + } +} +``` +## Use attribute-based routing instead of convention-based routing + +- Convention-based routing tightly couples your code to your URLs. By default, renaming an action method would change +the URL it handles. +- Attribute-based routing decouples your class and method names from URLs, giving you the freedom to refactor +without breaking the site, or at least having to implement redirects. +- Attribute-based routing is arguably easier to understand, as the URL appears right above the code to which it points. +- For these reasons, always use attribute-based routing when exposing HTTP endpoints. + +#### Don't + +```c# +public class Global +{ + public void Application_Start() + { + RouteConfig.RegisterRoutes(RouteTable.Routes); + } + + public static void RegisterRoutes(RouteCollection routes) + { + routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); + + routes.MapRoute( + name: "Default", + url: "{controller}/{action}/{id}", + defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } + ); + } +} +``` + +#### Do +```c# +public class HomeController: Controller +{ + [Route("{department}/employees/{employeeId ?}")] + public string Employee(string department, int? employeeId) + { + ... + } +} +``` + + +Description of rule +--- diff --git a/src/Agoda.Analyzers.Test/AllAnalyzersUnitTests.cs b/src/Agoda.Analyzers.Test/AllAnalyzersUnitTests.cs index 7ae33c1..d8e66fd 100644 --- a/src/Agoda.Analyzers.Test/AllAnalyzersUnitTests.cs +++ b/src/Agoda.Analyzers.Test/AllAnalyzersUnitTests.cs @@ -1,8 +1,11 @@ using System; +using System.Collections.Generic; using System.Linq; +using System.Reflection; using Agoda.Analyzers.Helpers; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Framework; +using Shouldly; namespace Agoda.Analyzers.Test; @@ -29,4 +32,120 @@ public void Analyzer_MustHaveDescriptorTitle() } }); } + private static IEnumerable GetAnalyzerTestCases() + { + var assembly = typeof(AnalyzerConstants).Assembly; + + return assembly.GetTypes() + .Where(t => typeof(DiagnosticAnalyzer).IsAssignableFrom(t) && !t.IsAbstract) + .Select(analyzerType => new TestCaseData(analyzerType) + .SetName(analyzerType.Name) + .SetDescription($"Verifies that {analyzerType.Name} has required properties for all Diagnostic.Create calls")); + } + + [TestCaseSource(nameof(GetAnalyzerTestCases))] + public void Analyzer_Should_Have_Required_Properties_For_Diagnostic_Create(Type analyzerType) + { + // Get all methods in the analyzer + var methods = analyzerType.GetMethods(BindingFlags.Instance | + BindingFlags.NonPublic | + BindingFlags.Public | + BindingFlags.DeclaredOnly); + + var violations = new List(); + + 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)) + { + // 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; + + properties.ShouldNotBeNull($"Properties is null or not a dictionary"); + + properties.ContainsKey("MyKey") + .ShouldBeTrue($"Method {method.Name} doesn't contain required 'MyKey' property"); + + // Optional: Check property value if needed + properties["MyKey"].ShouldNotBeNullOrWhiteSpace( + $"MyKey property in method {method.Name} should have a value"); + } + } + catch (Exception ex) + { + violations.Add($"Error analyzing method {method.Name}: {ex.Message}"); + } + } + + violations.ShouldBeEmpty(); + } + + private bool ContainsDiagnosticCreate(MethodInfo method) + { + try + { + // Get all method calls within the method + var body = method.GetMethodBody(); + if (body == null) return false; + + // Decompile IL to find calls to Diagnostic.Create + var instructions = body.GetILAsByteArray(); + if (instructions == null) return false; + + // Get all method calls from the IL + var moduleHandle = method.Module.ModuleHandle; + var methodCalls = new List(); + + for (int i = 0; i < instructions.Length; i++) + { + // Look for call or callvirt instructions + if (instructions[i] == 0x28 || instructions[i] == 0x6F) // call or callvirt + { + var methodToken = BitConverter.ToInt32(instructions, i + 1); + try + { + var calledMethod = MethodBase.GetMethodFromHandle( + moduleHandle.ResolveMethodHandle(methodToken)); + + if (calledMethod?.DeclaringType?.FullName == "Microsoft.CodeAnalysis.Diagnostic" && + calledMethod.Name == "Create") + { + return true; + } + } + catch + { + // Skip if we can't resolve the method + continue; + } + } + } + } + catch + { + // If we can't analyze the method, assume it might contain Diagnostic.Create + return true; + } + + return false; + } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/Agoda.Analyzers.csproj b/src/Agoda.Analyzers/Agoda.Analyzers.csproj index 1938ac8..923f275 100644 --- a/src/Agoda.Analyzers/Agoda.Analyzers.csproj +++ b/src/Agoda.Analyzers/Agoda.Analyzers.csproj @@ -94,87 +94,4 @@ Resources.Designer.cs - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs b/src/Agoda.Analyzers/AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs index 4755df4..cd24096 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs @@ -13,6 +13,9 @@ namespace Agoda.Analyzers.AgodaCustom [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AG0001DependencyResolverMustNotBeUsed : PropertyInvocationAnalyzerBase { + internal override Dictionary Properties => new Dictionary() + { { Const.KEY_TECH_DEBT_IN_MINUTES, "10" } }; + public const string DIAGNOSTIC_ID = "AG0001"; private static readonly LocalizableString Title = new LocalizableResourceString( diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0011NoDirectQueryStringAccess.cs b/src/Agoda.Analyzers/AgodaCustom/AG0011NoDirectQueryStringAccess.cs index f72dae0..bcb808d 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0011NoDirectQueryStringAccess.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0011NoDirectQueryStringAccess.cs @@ -40,5 +40,8 @@ public class AG0011NoDirectQueryStringAccess : PropertyInvocationAnalyzerBase { new BlacklistedInvocationRule("Microsoft.AspNetCore.Http.HttpRequest", "QueryString") }; + + internal override Dictionary Properties => new Dictionary() + { { Const.KEY_TECH_DEBT_IN_MINUTES, "10" } }; } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0032PreventUseOfBlockingTaskMethods.cs b/src/Agoda.Analyzers/AgodaCustom/AG0032PreventUseOfBlockingTaskMethods.cs index 69810a6..504ba14 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0032PreventUseOfBlockingTaskMethods.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0032PreventUseOfBlockingTaskMethods.cs @@ -13,6 +13,9 @@ namespace Agoda.Analyzers.AgodaCustom [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AG0032PreventUseOfBlockingTaskMethods : PropertyInvocationAnalyzerBase { + internal override Dictionary Properties => new Dictionary() + { { Const.KEY_TECH_DEBT_IN_MINUTES, "10" } }; + public const string DIAGNOSTIC_ID = "AG0032"; private static readonly LocalizableString Title = new LocalizableResourceString( diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0033PreventUseOfTaskResult.cs b/src/Agoda.Analyzers/AgodaCustom/AG0033PreventUseOfTaskResult.cs index 544d856..c76fe2d 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0033PreventUseOfTaskResult.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0033PreventUseOfTaskResult.cs @@ -13,6 +13,9 @@ namespace Agoda.Analyzers.AgodaCustom [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AG0033PreventUseOfTaskResult : PropertyInvocationAnalyzerBase { + internal override Dictionary Properties => new Dictionary() + { { Const.KEY_TECH_DEBT_IN_MINUTES, "10" } }; + public const string DIAGNOSTIC_ID = "AG0033"; private static readonly LocalizableString Title = new LocalizableResourceString( diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0035PreventUseOfMachineName.cs b/src/Agoda.Analyzers/AgodaCustom/AG0035PreventUseOfMachineName.cs index 269d216..5994a54 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0035PreventUseOfMachineName.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0035PreventUseOfMachineName.cs @@ -8,6 +8,9 @@ namespace Agoda.Analyzers.AgodaCustom [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AG0035PreventUseOfMachineName : PropertyInvocationAnalyzerBase { + internal override Dictionary Properties => new Dictionary() + { { Const.KEY_TECH_DEBT_IN_MINUTES, "10" } }; + public const string DIAGNOSTIC_ID = "AG0035"; private static readonly LocalizableString Title = new LocalizableResourceString( diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0040WaitUntilStateNetworkIdleMustNotBeUsed.cs b/src/Agoda.Analyzers/AgodaCustom/AG0040WaitUntilStateNetworkIdleMustNotBeUsed.cs index e9765cb..2155250 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0040WaitUntilStateNetworkIdleMustNotBeUsed.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0040WaitUntilStateNetworkIdleMustNotBeUsed.cs @@ -8,6 +8,9 @@ namespace Agoda.Analyzers.AgodaCustom [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AG0040WaitUntilStateNetworkIdleMustNotBeUsed : PropertyInvocationAnalyzerBase { + internal override Dictionary Properties => new Dictionary() + { { Const.KEY_TECH_DEBT_IN_MINUTES, "10" } }; + public const string DIAGNOSTIC_ID = "AG0040"; private static readonly LocalizableString Title = new LocalizableResourceString( diff --git a/src/Agoda.Analyzers/AgodaCustom/PropertyInvocationAnalyzerBase.cs b/src/Agoda.Analyzers/AgodaCustom/PropertyInvocationAnalyzerBase.cs index b3aeaa5..e002c3e 100644 --- a/src/Agoda.Analyzers/AgodaCustom/PropertyInvocationAnalyzerBase.cs +++ b/src/Agoda.Analyzers/AgodaCustom/PropertyInvocationAnalyzerBase.cs @@ -19,6 +19,7 @@ public abstract class PropertyInvocationAnalyzerBase : DiagnosticAnalyzer public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); protected abstract DiagnosticDescriptor Descriptor { get; } protected abstract IEnumerable Rules { get; } + internal abstract Dictionary Properties { get; } private static readonly Regex MatchGeneric = new Regex("<.*>$"); diff --git a/src/Agoda.Analyzers/Const.cs b/src/Agoda.Analyzers/Const.cs new file mode 100644 index 0000000..cef4387 --- /dev/null +++ b/src/Agoda.Analyzers/Const.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Agoda.Analyzers +{ + internal static class Const + { + internal const string KEY_TECH_DEBT_IN_MINUTES = "TechDebtInMinutes"; + } +} diff --git a/src/Agoda.Analyzers/RuleContent/AG0002PrivateMethodsShouldNotBeTested.html b/src/Agoda.Analyzers/RuleContent/AG0002PrivateMethodsShouldNotBeTested.html deleted file mode 100644 index a5202b7..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0002PrivateMethodsShouldNotBeTested.html +++ /dev/null @@ -1,39 +0,0 @@ -

- You should not expose any functionality from your class that not used in the public interface. -

- -

Noncompliant Code Example

-
-   interface ISomething 
-    {
-	    void DoSomething();
-	}
-			
-    class TestClass : ISomething 
-    {
-        public void DoSomething() 
-        {
-
-        }
-        internal void DoSomething2() 
-        {
-
-        }
-    }
-
- -

Compliant Code Example

-
-    interface ISomething 
-    {
-	    void DoSomething();
-	}
-			
-    class TestClass : ISomething 
-    {
-        public void DoSomething() 
-        {
-
-        }
-    }
-
diff --git a/src/Agoda.Analyzers/RuleContent/AG0003HttpContextCannotBePassedAsMethodArgument.html b/src/Agoda.Analyzers/RuleContent/AG0003HttpContextCannotBePassedAsMethodArgument.html deleted file mode 100644 index ee722f5..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0003HttpContextCannotBePassedAsMethodArgument.html +++ /dev/null @@ -1,52 +0,0 @@ -

- Passing the whole System.Web.HttpContext to your method as a parameter create hard dependency on the HTTPContext and makes testing really hard ( mocking HTTPContext itself). - You should only pass parts of the System.Web.HttpContext that you actually using. -

- -

Noncompliant Code Example

-
-using System.Web;
-
-interface ISomething 
-{
-    void SomeMethod(HttpContext c, string sampleString); // ugly interface method
-}
-			
-class TestClass: ISomething 
-{
-                    
-    public void SomeMethod(HttpContext context, string sampleString) 
-    {
-    // this method is ugly
-    }
-
-    public TestClass(System.Web.HttpContext context) 
-    {
-        // this constructor is uglier
-    }
-}
-
- -

Compliant Code Example

-
-using System.Web;
-
-interface ISomething 
-{
-    void SomeMethod(HttpContext c, string sampleString); // ugly interface method
-}
-			
-class TestClass: ISomething 
-{
-                    
-    public void SomeMethod(string userAgent, string sampleString) 
-    {
-    
-    }
-
-    public TestClass(string userAgent) 
-    {
-       
-    }
-}
-
diff --git a/src/Agoda.Analyzers/RuleContent/AG0004DoNotUseHardCodedStringsToIdentifyTypes.html b/src/Agoda.Analyzers/RuleContent/AG0004DoNotUseHardCodedStringsToIdentifyTypes.html deleted file mode 100644 index 4645794..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0004DoNotUseHardCodedStringsToIdentifyTypes.html +++ /dev/null @@ -1,22 +0,0 @@ -

- Do not use hard coded string to identify types -

- -

- Do not use hard coded strings to identify namespaces and types. They make refactorings such as moving namespaces or renaming types harder, - and failures will be caught at runtime instead of compile time. -

- -

Noncompliant Code Example

-
-// both fail at runtime after change of namespace 
-var instance Activator.CreateInstance("Agoda", "Agoda.MyType");
-var type = Type.GetType("Agoda.MyType")
-
- -

Compliant Code Example

-
-// caught at compile time after change of namespace 
-var instance = Activator.CreateInstance(typeof(Agoda.MyType));
-var type = typeof(Agoda.MyType);
-
diff --git a/src/Agoda.Analyzers/RuleContent/AG0005TestMethodNamesMustFollowConvention.html b/src/Agoda.Analyzers/RuleContent/AG0005TestMethodNamesMustFollowConvention.html deleted file mode 100644 index cf60e37..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0005TestMethodNamesMustFollowConvention.html +++ /dev/null @@ -1,46 +0,0 @@ -

- Test method names should clearly indicate what they are testing. -

-

- A test’s purpose (what is being tested, any pre-conditions, and the expected result) should be obvious from its name alone. - It should not be necessary to read the code to work out what is being tested. -

- -

The general naming convention for a test class and its fixtures is:

- -
-public class <ClassNameUnderTest>Tests 
-{
-    // separated by underscores
-    [Test]
-    public void <SystemUnderTest>_<PostCondition>()
-    {...}
-
-    // or
-    [Test]
-    public void <SystemUnderTest>_<PreCondition>_<PostCondition>()
-    {...}
-}
-
- -

For unit tests, the SystemUnderTest will usually be the name of the method you are testing.

- -

Noncompliant Code Example

-
-[Test]
-public void HazardLightsTest()
-{...}
-
- -

Compliant Code Example

-
-[Test]
-public void HazardLightsToggleButton_WhenPushedWithLightsBlinking_StopsLightsBlinking()
-{...}
-
- -

- If you are having difficultly naming your test this way, it might indicate it is doing - too much and should be split into more granular tests. -

- diff --git a/src/Agoda.Analyzers/RuleContent/AG0006RegisteredComponentShouldHaveExactlyOnePublicConstructor.html b/src/Agoda.Analyzers/RuleContent/AG0006RegisteredComponentShouldHaveExactlyOnePublicConstructor.html deleted file mode 100644 index 380b3be..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0006RegisteredComponentShouldHaveExactlyOnePublicConstructor.html +++ /dev/null @@ -1,9 +0,0 @@ -

- Registered components should have exactly one public constructor. -

- -

- Each component should expose only a single public constructor. - - Multiple constructors lead to a fragile design and present maintainability issues. Use the null object pattern for truly optional dependencies. -

\ No newline at end of file diff --git a/src/Agoda.Analyzers/RuleContent/AG0009IHttpContextAccessorCannotBePassedAsMethodArgument.html b/src/Agoda.Analyzers/RuleContent/AG0009IHttpContextAccessorCannotBePassedAsMethodArgument.html deleted file mode 100644 index 6091318..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0009IHttpContextAccessorCannotBePassedAsMethodArgument.html +++ /dev/null @@ -1,57 +0,0 @@ -

- Passing the whole Microsoft.AspNetCore.Http.IHttpContextAccessor to your method as a parameter create hard dependency on the IHttpContextAccessor and makes testing really hard ( mocking IHttpContextAccessor itself). - You should only pass parts of the Microsoft.AspNetCore.Http.IHttpContextAccessor that you actually using. -

- -

Noncompliant Code Example

-
-using Microsoft.AspNetCore.Http;
-
-interface ISomething
-{
-    void SomeMethod(IHttpContextAccessor c, string sampleString); // ugly interface method
-    void SomeMethod(HttpContextAccessor c, string sampleString); // ugly interface method
-}
-
-class TestClass : ISomething
-{
-    public void SomeMethod(IHttpContextAccessor context, string sampleString)
-    {
-        // this method is ugly
-    }
-
-    public void SomeMethod(HttpContextAccessor context, string sampleString)
-    {
-        // this method is ugly
-    }
-
-     public TestClass(Microsoft.AspNetCore.Http.IHttpContextAccessor context)
-    {
-        // this constructor is uglier
-    }
-
-    public TestClass(Microsoft.AspNetCore.Http.HttpContextAccessor context)
-    {
-        // this constructor is uglier
-    }
-}
-
- -

Compliant Code Example

-
-interface ISomething
-{
-    void SomeMethod(string userAgent, string sampleString);
-}
-
-class TestClass : ISomething
-{
-    public void SomeMethod(string userAgent, string sampleString)
-    {
-    }
-
-    public TestClass(string userAgent)
-    {
-    }
-}
-
diff --git a/src/Agoda.Analyzers/RuleContent/AG0010PreventTestFixtureInheritance.html b/src/Agoda.Analyzers/RuleContent/AG0010PreventTestFixtureInheritance.html deleted file mode 100644 index dd27800..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0010PreventTestFixtureInheritance.html +++ /dev/null @@ -1,21 +0,0 @@ -

- Prevent Test Fixture Inheritance. -

- -

Noncompliant Code Example

-
-[TestFixture]
-public class TestClass : BaseTest
-{
-        // Code
-}
-
- -

Compliant Code Example

-
-[TestFixture]
-public class TestClass
-{
-        // Code
-}
-
\ No newline at end of file diff --git a/src/Agoda.Analyzers/RuleContent/AG0011NoDirectQueryStringAccess.html b/src/Agoda.Analyzers/RuleContent/AG0011NoDirectQueryStringAccess.html deleted file mode 100644 index 4a69df9..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0011NoDirectQueryStringAccess.html +++ /dev/null @@ -1,20 +0,0 @@ -

- The QueryString collection should not be used directly in the codebase, and ASP.NET model binding should be used instead. -

- -

Noncompliant Code Example

-
-    public ActionResult Index()
-    {
-        var id = HttpContext.Current.Request.QueryString["id"];
-        // Use id here
-    }
-
- -

Compliant Code Example

-
-    public ActionResult Index([FromQuery] int id)
-    {
-        // Use id here
-    }
-
diff --git a/src/Agoda.Analyzers/RuleContent/AG0012TestMethodMustContainAtLeastOneAssertion.html b/src/Agoda.Analyzers/RuleContent/AG0012TestMethodMustContainAtLeastOneAssertion.html deleted file mode 100644 index 31c689c..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0012TestMethodMustContainAtLeastOneAssertion.html +++ /dev/null @@ -1,55 +0,0 @@ -

- Test method names should contain at least one assertion. -

-

- This may be NUnit's Assert or one of Shouldly's (Shouldly documentation). -

- -

Example for NUnit's Assert

-
-using NUnit.Framework;
-
-namespace Tests
-{
-    public class TestClass
-    {
-        [Test]
-        public void This_Is_Valid()
-        {
-            int[] arrayToAssert = { 1, 2, 3 };
-            Assert.That(arrayToAssert, Has.Exactly(1).EqualTo(3));
-        }
-
-        [Test]
-        public void This_Is_Not_Valid()
-        {
-            int[] arrayToAssert = { 1, 2, 3 };
-        }
-    }
-}
-
- -

Example for Shouldly's Assert

-
-using NUnit.Framework;
-using Shouldly;
-
-namespace Tests
-{
-    public class TestClass
-    {
-        [Test]
-        public void This_Is_Valid()
-        {
-            int[] arrayForShouldBe = { 1, 2, 3 };
-            arrayForShouldBe.Length.ShouldBe(3);
-        }
-
-        [Test]
-        public void This_Is_Not_Valid()
-        {
-            int[] arrayForShouldBe = { 1, 2, 3 };
-        }
-	}
-}
-
diff --git a/src/Agoda.Analyzers/RuleContent/AG0018PermitOnlyCertainPubliclyExposedEnumerables.html b/src/Agoda.Analyzers/RuleContent/AG0018PermitOnlyCertainPubliclyExposedEnumerables.html deleted file mode 100644 index fe6c88c..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0018PermitOnlyCertainPubliclyExposedEnumerables.html +++ /dev/null @@ -1,24 +0,0 @@ -

- If public class/interface method/property return value implements IEnumerable, then it can only be declared as one of the following: - - IEnumerable<T> - - ISet<T> - - IList<T> - - IDictionary<K, V> - - IReadOnlyDictionary<K, V> - - KeyedCollection<K, V> - - byte[] (special case for raw binary data) - - string (which happens to implement `IEnumerable<char>`) -

- - -

Noncompliant Code Example

-
-    public List<string> GetStrings() { ... }
-    public List<string> GetStringsV2 { get; set; }
-
- -

Compliant Code Example

-
-    public IList<string> GetStrings() { ... }
-    public IList<string> GetStringsV2 { get; set; }
-
diff --git a/src/Agoda.Analyzers/RuleContent/AG0019PreventUseOfInternalsVisibleToAttribute.html b/src/Agoda.Analyzers/RuleContent/AG0019PreventUseOfInternalsVisibleToAttribute.html deleted file mode 100644 index e2ce7c6..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0019PreventUseOfInternalsVisibleToAttribute.html +++ /dev/null @@ -1,31 +0,0 @@ -

- Prevent use of InternalsVisibleTo Attribute. -

- -

- Only test the public surface of your class. Making internals visible violates this principle. The attribute is - usually added to test internal methods that are not part of the public API. This tightly couples the - tests to the implementation, making the tests brittle and refactoring harder. -

- -

Noncompliant Code Example

-
-using System;
-using System.Runtime.CompilerServices;
-
-[assembly: InternalsVisibleTo("Agoda.Website.UnitTestFramework")]
-namespace RoslynTest
-{
-  ...
-}
-
- -

Compliant Code Example

-
-using System;
-
-namespace RoslynTest
-{
-  ...
-}
-
\ No newline at end of file diff --git a/src/Agoda.Analyzers/RuleContent/AG0020AvoidReturningNullEnumerables.html b/src/Agoda.Analyzers/RuleContent/AG0020AvoidReturningNullEnumerables.html deleted file mode 100644 index e729fc1..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0020AvoidReturningNullEnumerables.html +++ /dev/null @@ -1,48 +0,0 @@ -

- Prevent returning null from a method / property whose return value implements IEnumerable. -

- -

Noncompliant Code Example

-
-public IEnumerable GetProductIds(int brandId)
-{
-    var products = productService.GetProductsForBrand(brandId);
-    
-    if (products == null || !products.Any())
-    {
-        return null; 
-        // Now the caller has to somehow know to deal with this special case.
-        // You are asking for a NullReferenceException in prod.
-    }
-    
-    return products.Select(p => p.Id);
-}
-
- -

Compliant Code Example

-
-public IEnumerable GetProductIds(int brandId)
-{
-    var products = productService.GetProductsForBrand(brandId);
-    
-    if (products == null)
-    {
-        // Just return an empty enumerable and everything should just work.
-        return Enumerable.Empty();
-    }
-    
-    return products.Select(p => p.Id);
-}
-
- -

Even better

-
-public IEnumerable GetProductIds(int brandId)
-{
-    // Fix productService.GetProducts() to never return null itself, and we
-    // can skip the null check entirely.
-    return productService
-        .GetProductsForBrand(brandId)
-        .Select(p => p.Id);
-}
-
\ No newline at end of file diff --git a/src/Agoda.Analyzers/RuleContent/AG0021PreferAsyncMethods.html b/src/Agoda.Analyzers/RuleContent/AG0021PreferAsyncMethods.html deleted file mode 100644 index e75f979..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0021PreferAsyncMethods.html +++ /dev/null @@ -1,8 +0,0 @@ -

- Do not use synchronous version of method when async version exists -

- -

- When consuming an API, always prefer the asynchronous version of a method if it exists. - Performance and scalability may improve. There are no downsides. -

diff --git a/src/Agoda.Analyzers/RuleContent/AG0022DoNotExposeBothSyncAndAsyncVersionsOfMethods.html b/src/Agoda.Analyzers/RuleContent/AG0022DoNotExposeBothSyncAndAsyncVersionsOfMethods.html deleted file mode 100644 index dfea18f..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0022DoNotExposeBothSyncAndAsyncVersionsOfMethods.html +++ /dev/null @@ -1,32 +0,0 @@ -

- Do not expose both sync and async versions of methods -

- -
    -
  • When writing an API that performs I/O or long running CPU intensive work, only expose an asynchronous version of the method.
  • -
  • Suffix it with Async.
  • -
  • Do not provide an inferior synchronous option, because then people will use it.
  • -
  • If it can be async, then it must be. If it can’t be async, then it should not be. It cannot be both.
  • -
- -

- Don't -

-
-    interface IFileDownloader
-    {
-        byte[] DownloadFile(string url);
-        Task<byte[]> DownloadFileAsync(string url);
-    }
-
- - -

- Do -

-
-    interface IFileDownloader
-    {
-        Task<byte[]> DownloadFileAsync(string url);
-    }
-
\ No newline at end of file diff --git a/src/Agoda.Analyzers/RuleContent/AG0024PreventUseOfTaskFactoryStartNew.html b/src/Agoda.Analyzers/RuleContent/AG0024PreventUseOfTaskFactoryStartNew.html deleted file mode 100644 index 77ed943..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0024PreventUseOfTaskFactoryStartNew.html +++ /dev/null @@ -1,20 +0,0 @@ -

- Use Task.Run instead of Task.Factory.StartNew -

- -

-

    -
  • Task.Factory.StartNew is considered dangerous and should only be used in very specific circumstances..
  • -
  • The most common use-case is to specify TaskCreationOptions.LongRunning.In the current implementation of.NET this creates a new thread for your task, instead of running it on the threadpool.This could change between implementations / platforms, so should not be relied upon. Only do this if you have profiled your application and found it to be necessary.If you really need a new thread, consider Thread.Start.
  • -
-

- -

Noncompliant Code Example

-
-   Task.Factory.StartNew(MyMethod, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
-
- -

Compliant Code Example

-
-    Task.Run(() => MyMethod());
-
diff --git a/src/Agoda.Analyzers/RuleContent/AG0027EnsureOnlyDataSeleniumIsUsedToFindElements.html b/src/Agoda.Analyzers/RuleContent/AG0027EnsureOnlyDataSeleniumIsUsedToFindElements.html deleted file mode 100644 index 29bf75b..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0027EnsureOnlyDataSeleniumIsUsedToFindElements.html +++ /dev/null @@ -1,30 +0,0 @@ -

Use the data-selenium attribute to identify your elements

-

-

    -
  • Make it simple to change HTML structure and CSS classes by identifying elements under test with the data-selenium HTML attribute,
  • -
  • rather than HTML tags, classes or IDs. The addition of data-selenium attributes may slightly increase the size of the page, but when
  • -
  • gzipped the difference will probably be negligable. We believe the trade off in developer productivity is worth it.
  • -
-

- - -

Don't

-
-        
-            
-        
-        ]]>
-
-    Driver.FindElement(By.CssSelector("form button.login-button"));
-
- -

Do

-
-         
-            
-        
-        ]]>
-    Driver.FindElement(By.CssSelector("[data-selenium=login-button]"));
-
\ No newline at end of file diff --git a/src/Agoda.Analyzers/RuleContent/AG0030PreventUseOfDynamics.html b/src/Agoda.Analyzers/RuleContent/AG0030PreventUseOfDynamics.html deleted file mode 100644 index 0c8e432..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0030PreventUseOfDynamics.html +++ /dev/null @@ -1,9 +0,0 @@ -

- Do not use dynamics in C# -

- -

- Dynamics were invented to allow the CLR to interoperate more easily with dynamically typed languages. We do not do - this in Agoda - we move between Scala, C# and Typescript, all of which are statically typed. Therefore, - we have no use case for dynamics. -

\ No newline at end of file diff --git a/src/Agoda.Analyzers/RuleContent/AG0032PreventUseOfBlockingTaskMethods.html b/src/Agoda.Analyzers/RuleContent/AG0032PreventUseOfBlockingTaskMethods.html deleted file mode 100644 index 6f394b7..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0032PreventUseOfBlockingTaskMethods.html +++ /dev/null @@ -1,22 +0,0 @@ -

- Do not use blocking task methods such as Task.Wait(), Task.WaitAny() - and Task.WaitAll() -

-

- By design, the Task.Wait* methods block the current thread until the task(s) complete. - This defeats the purpose of writing async code. It also has the potential to cause deadlocks. -

- -

Noncompliant Code Example

-
-    var task1 = DownloadFileAsync("...");
-    var task2 = DownloadFileAsync("...");
-    Task.WaitAll(task1, task2);
-
- -

Compliant Code Example

-
-    var task1 = DownloadFileAsync("...");
-    var task2 = DownloadFileAsync("...");
-    await Task.WhenAll(task1, task2); // does not block
-
diff --git a/src/Agoda.Analyzers/RuleContent/AG0033PreventUseOfTaskResult.html b/src/Agoda.Analyzers/RuleContent/AG0033PreventUseOfTaskResult.html deleted file mode 100644 index 778e4d0..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0033PreventUseOfTaskResult.html +++ /dev/null @@ -1,17 +0,0 @@ -

- Use await task instead of task.Result -

-

- By design, Task.Result blocks the current thread until the task completes. - This defeats the purpose of writing async code. It also has the potential to cause deadlocks. -

- -

Noncompliant Code Example

-
-    var result = task.Result;
-
- -

Compliant Code Example

-
-    var result = await task;
-
\ No newline at end of file diff --git a/src/Agoda.Analyzers/RuleContent/AG0039MethodLineLengthAnalyzer.html b/src/Agoda.Analyzers/RuleContent/AG0039MethodLineLengthAnalyzer.html deleted file mode 100644 index 9ff1cae..0000000 --- a/src/Agoda.Analyzers/RuleContent/AG0039MethodLineLengthAnalyzer.html +++ /dev/null @@ -1,71 +0,0 @@ -

- Long methods should be considered for refactoring -

-
    -
  • - This rule is to help people identify potentially long methods, so they can consider refactoring them. -
  • -
  • - The rule does not count lines of whitespace in the recommended maximum is 40 lines, this varies per context though and should not be looked at as a hard limit. -
  • -
  • - Try to break up long methods into smaller private methods that describe what's going on. -
  • -
  • - However, don't chain methods together to avoid long methods when refactoring. -
  • -

    Noncompliant Code Example

    -
    -class MyClass()
    -{
    -    public void MethodThatDoesALotOfStuff()
    -    {
    -        return DoThisFirst();
    -    }
    -
    -    private int DoThisFirst()
    -    {
    -    // do some stuff...
    -        return DoThisSecond()
    -    }
    -    private int DoThisSecond()
    -    {
    -    // do some stuff...
    -        return DoThisThird();
    -    }
    -    private int DoThisThird()
    -    {
    -    // do some stuff...
    -    }
    -
    -}
    -    
    -

    Compliant Code Example

    -
    -class MyClass()
    -{
    -    public void MethodThatDoesALotOfStuff()
    -    {
    -        DoThisFirst();
    -        DoThisSecond();
    -        return DoThisThird();
    -    }
    -
    -    private void DoThisFirst()
    -    {
    -    // do some stuff...
    -    }
    -    private void DoThisSecond()
    -    {
    -    // do some stuff...
    -    }
    -    private int DoThisThird()
    -    {
    -    // do some stuff...
    -    }
    -}
    -    
    -
  • - If you are having problems with breaking methods down, try Martin Fowler's Book called refactoring https://www.amazon.com/Refactoring-Improving-Existing-Addison-Wesley-Signature/dp/0134757599/ -
  • -
\ No newline at end of file diff --git a/src/AgodaAnalyzers.sln b/src/AgodaAnalyzers.sln index 2f945e8..90ea7b4 100644 --- a/src/AgodaAnalyzers.sln +++ b/src/AgodaAnalyzers.sln @@ -16,6 +16,43 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\README.md = ..\README.md EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Doc", "Doc", "{670A7514-B0EC-4BB2-B0A9-81F96D4B6AD5}" + ProjectSection(SolutionItems) = preProject + ..\doc\AG0001.md = ..\doc\AG0001.md + ..\doc\AG0002.md = ..\doc\AG0002.md + ..\doc\AG0003.md = ..\doc\AG0003.md + ..\doc\AG0004.md = ..\doc\AG0004.md + ..\doc\AG0005.md = ..\doc\AG0005.md + ..\doc\AG0006.md = ..\doc\AG0006.md + ..\doc\AG0009.md = ..\doc\AG0009.md + ..\doc\AG0010.md = ..\doc\AG0010.md + ..\doc\AG0011.md = ..\doc\AG0011.md + ..\doc\AG0012.md = ..\doc\AG0012.md + ..\doc\AG0013.md = ..\doc\AG0013.md + ..\doc\AG0018.md = ..\doc\AG0018.md + ..\doc\AG0019.md = ..\doc\AG0019.md + ..\doc\AG0020.md = ..\doc\AG0020.md + ..\doc\AG0021.md = ..\doc\AG0021.md + ..\doc\AG0022.md = ..\doc\AG0022.md + ..\doc\AG0023.md = ..\doc\AG0023.md + ..\doc\AG0024.md = ..\doc\AG0024.md + ..\doc\AG0025.md = ..\doc\AG0025.md + ..\doc\AG0026.md = ..\doc\AG0026.md + ..\doc\AG0027.md = ..\doc\AG0027.md + ..\doc\AG0030.md = ..\doc\AG0030.md + ..\doc\AG0032.md = ..\doc\AG0032.md + ..\doc\AG0033.md = ..\doc\AG0033.md + ..\doc\AG0035.md = ..\doc\AG0035.md + ..\doc\AG0037.md = ..\doc\AG0037.md + ..\doc\AG0038.md = ..\doc\AG0038.md + ..\doc\AG0039.md = ..\doc\AG0039.md + ..\doc\AG0040.md = ..\doc\AG0040.md + ..\doc\AG0041.md = ..\doc\AG0041.md + ..\doc\AG0042.md = ..\doc\AG0042.md + ..\doc\AG0043.md = ..\doc\AG0043.md + ..\doc\AG0044.md = ..\doc\AG0044.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU From 8e1b103e87cfe7cf0fa2bd4b8e92f8196a230ca7 Mon Sep 17 00:00:00 2001 From: jdickson Date: Mon, 18 Nov 2024 13:48:45 +0700 Subject: [PATCH 2/7] Add failing test --- src/Agoda.Analyzers.Test/AllAnalyzersUnitTests.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Agoda.Analyzers.Test/AllAnalyzersUnitTests.cs b/src/Agoda.Analyzers.Test/AllAnalyzersUnitTests.cs index d8e66fd..14acfba 100644 --- a/src/Agoda.Analyzers.Test/AllAnalyzersUnitTests.cs +++ b/src/Agoda.Analyzers.Test/AllAnalyzersUnitTests.cs @@ -46,11 +46,13 @@ private static IEnumerable GetAnalyzerTestCases() [TestCaseSource(nameof(GetAnalyzerTestCases))] public void Analyzer_Should_Have_Required_Properties_For_Diagnostic_Create(Type analyzerType) { - // Get all methods in the analyzer + // Modified BindingFlags to include all methods var methods = analyzerType.GetMethods(BindingFlags.Instance | - BindingFlags.NonPublic | - BindingFlags.Public | - BindingFlags.DeclaredOnly); + BindingFlags.NonPublic | + BindingFlags.Public | + BindingFlags.DeclaredOnly | + BindingFlags.Static | // Added to catch static methods + BindingFlags.FlattenHierarchy); // Added to get inherited methods var violations = new List(); @@ -76,7 +78,7 @@ public void Analyzer_Should_Have_Required_Properties_For_Diagnostic_Create(Type BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); propertiesInfo.ShouldNotBeNull($"Analyzer uses Diagnostic.Create in method {method.Name} " + - "but doesn't have a Properties property"); + "but doesn't have a Properties property"); var properties = propertiesInfo.GetValue(analyzer) as IDictionary; @@ -85,7 +87,6 @@ public void Analyzer_Should_Have_Required_Properties_For_Diagnostic_Create(Type properties.ContainsKey("MyKey") .ShouldBeTrue($"Method {method.Name} doesn't contain required 'MyKey' property"); - // Optional: Check property value if needed properties["MyKey"].ShouldNotBeNullOrWhiteSpace( $"MyKey property in method {method.Name} should have a value"); } From ee15456ac57b4ee256bd30b9e9c5fd7c930b2692 Mon Sep 17 00:00:00 2001 From: jdickson Date: Mon, 18 Nov 2024 14:01:02 +0700 Subject: [PATCH 3/7] more updates to docs --- doc/AG0001.md | 46 ++++--- doc/AG0013.md | 94 +++++++++------ doc/AG0023.md | 108 +++++++++++++---- doc/AG0025.md | 99 +++++++++++---- doc/AG0026.md | 86 ++++++++----- doc/AG0035.md | 85 +++++++++++-- doc/AG0037.md | 114 +++++++++--------- doc/AG0038.md | 81 ++++++++++--- doc/AG0040.md | 71 ++++++++++- doc/prompt.md | 18 ++- .../AllAnalyzersUnitTests.cs | 9 +- src/Agoda.Analyzers/AnalyzerConstants.cs | 2 + src/Agoda.Analyzers/Const.cs | 11 -- 13 files changed, 592 insertions(+), 232 deletions(-) delete mode 100644 src/Agoda.Analyzers/Const.cs diff --git a/doc/AG0001.md b/doc/AG0001.md index b1a4350..7233417 100644 --- a/doc/AG0001.md +++ b/doc/AG0001.md @@ -1,19 +1,35 @@ -# Rule AG0001 +# Avoid direct usage of DependencyResolver -

- The DependencyResolver should not be used directly in the codebase. We should expose our dependencies as a constructor parameter. -

+ID: AG0001 -

Noncompliant Code Example

-
-   var exampleService = DependencyResolver.Current.GetService<IExampleService>();
-
+Type: Code Smell -

Compliant Code Example

-
-    public class ExampleClass
-    {
-        public ExampleClass(IExampleService exampleService) { }
-    }
+Direct usage of `DependencyResolver` creates tight coupling and makes code harder to test. Dependencies should be explicitly declared through constructor injection, which promotes:
 
-
+- Better testability through clear dependency declaration +- Improved maintainability by making dependencies visible +- Stronger adherence to SOLID principles, particularly Dependency Inversion + +#### Don't + +```c# +var exampleService = DependencyResolver.Current.GetService(); +``` + +#### Do + +```c# +public class ExampleClass +{ + public ExampleClass(IExampleService exampleService) { } +} +``` + +The use of `DependencyResolver.Current` creates several problems: + +- It makes unit testing more difficult since you can't easily mock dependencies +- It hides class dependencies, making the code harder to understand and maintain +- It bypasses the dependency injection container's lifecycle management +- It creates a direct dependency on the DI container, violating the Service Locator anti-pattern + +Always prefer constructor injection to make dependencies explicit and improve code quality. diff --git a/doc/AG0013.md b/doc/AG0013.md index e8fc0b9..054d63d 100644 --- a/doc/AG0013.md +++ b/doc/AG0013.md @@ -1,38 +1,56 @@ -# Rule AG0013 - -

- Limit number of test method input parameters to 5 -

-

- Overuse of [TestCase] can make it difficult to see what is actually being tested, especially when there are many parameters. - This can results in a combinatorial explosion of cases as new parameters are added. -

- -

Don't

-
-    [Test]
-    [TestCase(0, 1, 2, 3, 4, 5)]
-    public void This_Is_NotValid(int a, int b, int c, int d, int e, int f)
-    {
-        // ...
-    }
-
- -

Do

-
-    // By splitting the test into multiple, the test intention becomes more obvious.
-
-    [Test]
-    [TestCase(0, 1, 2)]
-    public void This_Is_Valid1(int a, int b, int c)
-    {
-        // ...
-    }
-
-    [Test]
-    [TestCase(3, 4, 5)]
-    public void This_Is_Valid2(int d, int e, int f)
-    {
-        // ...
-    }
-
+# Limit test method parameters to 5 or fewer + +ID: AG0013 + +Type: Code Smell + +Test methods with too many input parameters become difficult to understand and maintain. When test cases need many parameters, split them into smaller, more focused tests with clear intentions. + +#### Don't + +```csharp +[Test] +[TestCase(0, 1, 2, 3, 4, 5)] // Noncompliant - too many parameters +public void This_Is_NotValid(int a, int b, int c, int d, int e, int f) +{ + // Test becomes hard to understand +} +``` + +#### Do + +```csharp +[Test] +[TestCase(0, 1, 2)] +public void This_Is_Valid1(int a, int b, int c) +{ + // Clear test purpose +} + +[Test] +[TestCase(3, 4, 5)] +public void This_Is_Valid2(int d, int e, int f) +{ + // Clear test purpose +} +``` + +Having too many test parameters creates problems: + +- Makes test intention unclear +- Leads to combinatorial explosion of test cases +- Harder to maintain and modify +- Difficult to understand test failures +- Reduces test readability +- Makes refactoring more difficult +- Can hide logical groupings of test cases + +Instead: + +- Split into multiple focused tests +- Use test helper methods +- Create test data builders +- Use meaningful parameter names +- Group related parameters into objects + +This keeps tests clear, maintainable, and easier to understand. \ No newline at end of file diff --git a/doc/AG0023.md b/doc/AG0023.md index d611d28..1df3463 100644 --- a/doc/AG0023.md +++ b/doc/AG0023.md @@ -1,23 +1,85 @@ -# Rule AG0023 - -

- Use Task.Delay instead of Thread.Sleep -

- -

-

    -
  • Thread.Sleep will block the current thread
  • -
  • blocking threads has negative impact on our servers capacity, scalability and performance
  • -
  • using Task.Delay will pause your execution but will free the thread to do other stuff.
  • -
-

- -

Noncompliant Code Examples

-
-   Thread.Sleep(500);
-
- -

Compliant Code Example

-
-    await Task.Delay(500);
-
+# Use Task.Delay Instead of Thread.Sleep + +ID: AG0023 + +Type: Performance / Code Smell + +## Summary + +Never use `Thread.Sleep()` for delays in code. Instead, use the asynchronous `Task.Delay()` to avoid blocking threads. + +## Explanation + +`Thread.Sleep()` blocks the current thread, preventing it from doing any work during the sleep period. This has several negative impacts: +- Reduces server capacity +- Decreases application scalability +- Degrades overall performance +- Wastes thread pool resources +- Can cause deadlocks in UI applications + +Using `Task.Delay()` allows the thread to handle other work while waiting for the delay to complete. + +### Don't ❌ + +```csharp +public void ProcessWithDelay() +{ + // Do something + Thread.Sleep(1000); // Blocks thread for 1 second + // Do more stuff +} + +public void RetryOperation() +{ + for (int i = 0; i < 3; i++) + { + try + { + // Do operation + return; + } + catch + { + Thread.Sleep(500); // Bad! Blocks thread between retries + } + } +} +``` + +### Do ✅ + +```csharp +public async Task ProcessWithDelayAsync() +{ + // Do something + await Task.Delay(1000); // Thread remains free + // Do more stuff +} + +public async Task RetryOperationAsync() +{ + for (int i = 0; i < 3; i++) + { + try + { + // Do operation + return; + } + catch + { + await Task.Delay(500); // Good! Thread can do other work + } + } +} +``` + +## Why Use Task.Delay? + +- Maintains thread pool efficiency +- Improves application responsiveness +- Better resource utilization +- Prevents UI freezing +- Scales better under load +- Follows async/await best practices + +Remember: There's never a good reason to use `Thread.Sleep()` in modern .NET applications. diff --git a/doc/AG0025.md b/doc/AG0025.md index e075c25..ff83800 100644 --- a/doc/AG0025.md +++ b/doc/AG0025.md @@ -1,23 +1,76 @@ -# Rule AG0025 - -

- Use await instead of Task.ContinueWith -

- -

-

    -
  • Task.ContinueWith has some surprising and non-intuitive behaviors, so must be used with care.
  • -
  • await has none of these problems. It is also more readable.
  • -
  • Therefore, Task.ContinueWith should not be used unless you are doing dynamic task parallelism, which is rare.
  • -
-

- -

Noncompliant Code Example

-
-   await downloadTask.ContinueWith(async t => await SaveFileAsync(t.Result)); 
-
- -

Compliant Code Example

-
-    await SaveFileAsync(await downloadTask);
-
+# Use await Instead of Task.ContinueWith + +ID: AG0025 + +Type: Code Smell / Best Practice + +## Summary + +Avoid using `Task.ContinueWith` as it has subtle and non-intuitive behaviors. Use the `await` keyword instead, which is clearer and safer. + +## Explanation + +`Task.ContinueWith` comes with several pitfalls: + +- Unexpected exception handling behavior +- Complex task scheduling rules +- Unclear execution context flow +- Harder to read and maintain +- Can lead to deadlocks if not used carefully +- More prone to error than `await` + +Only use `Task.ContinueWith` for dynamic task parallelism scenarios, which are rare. + +### Don't ❌ + +```csharp +// Confusing and error-prone +await downloadTask.ContinueWith(async t => await SaveFileAsync(t.Result)); + +// Exception handling is tricky +task.ContinueWith(t => +{ + if (t.IsFaulted) + HandleException(t.Exception); + else + ProcessResult(t.Result); +}); + +// Complicated continuation chains +task.ContinueWith(t => DoSomething()) + .ContinueWith(t => DoSomethingElse()); +``` + +### Do ✅ + +```csharp +// Clear and straightforward +await SaveFileAsync(await downloadTask); + +// Clean exception handling +try +{ + var result = await task; + ProcessResult(result); +} +catch (Exception ex) +{ + HandleException(ex); +} + +// Easy to understand flow +var result1 = await DoSomething(); +var result2 = await DoSomethingElse(); +``` + +## Why Avoid Task.ContinueWith? + +- More complex than necessary +- Easy to misuse +- Poor exception handling +- Confusing task scheduling behavior +- Hard to reason about execution context +- Less readable and maintainable +- `await` provides cleaner, safer alternatives + +Remember: Use `await` by default. Only consider `Task.ContinueWith` for specific dynamic task parallelism scenarios. diff --git a/doc/AG0026.md b/doc/AG0026.md index c8cf8cf..4c29f76 100644 --- a/doc/AG0026.md +++ b/doc/AG0026.md @@ -1,27 +1,59 @@ -# Rule AG0026 - -

- Use only CSS Selectors to find elements in Selenium tests -

-

-

    -
  • We are all familiar with CSS selectors, but many of us are not comfortable with XPath.
  • -
  • XPath just adds noise and confusion, so should not be used.
  • -
-

- -

Don't

-
-    // What do all those characters even mean? I don't even want to have to know.
-    Driver.FindElements(By.XPath(".//*[@data-selenium='hotel-item']")) 
-
- -

Do

-
-    Driver.FindElements(By.CssSelector("[data-selenium=hotel-item]"))
-
- -

Do

-
-    Driver.FindElementsByCssSelector("[data-selenium=hotel-item]")
-
+# Use CSS Selectors Instead of XPath in Selenium Tests + +ID: AG0026 + +Type: Code Style / Best Practice + +## Summary + +In Selenium tests, use CSS selectors instead of XPath to locate elements. CSS selectors are more familiar, easier to read, and maintain. + +## Explanation + +XPath selectors: + +- Are harder to read and understand +- Have complex syntax that's error-prone +- Require additional knowledge beyond web development +- Add unnecessary complexity to test code +- Are less maintainable +- Can be slower than CSS selectors in some browsers + +CSS selectors leverage knowledge that web developers already have and are more intuitive. + +### Don't ❌ + +```csharp +// Complex and hard to understand +Driver.FindElements(By.XPath(".//*[@data-selenium='hotel-item']")); + +// Confusing hierarchy navigation +Driver.FindElement(By.XPath("//div/span/parent::div//a")); + +// Complex attribute selection +Driver.FindElement(By.XPath("//input[contains(@class, 'submit-button')]")); +``` + +### Do ✅ + +```csharp +// Clear and simple +Driver.FindElements(By.CssSelector("[data-selenium=hotel-item]")); + +// Alternative direct method +Driver.FindElementsByCssSelector("[data-selenium=hotel-item]"); + +// Familiar CSS patterns +Driver.FindElement(By.CssSelector(".submit-button")); +``` + +## Why Avoid XPath? + +- More complex syntax than CSS +- Harder to maintain and debug +- Requires learning additional syntax +- Less performant in some browsers +- Not as intuitive as CSS selectors +- Teams are more familiar with CSS + +Remember: Use data attributes with CSS selectors for the most maintainable test code. diff --git a/doc/AG0035.md b/doc/AG0035.md index 00db013..da2f50b 100644 --- a/doc/AG0035.md +++ b/doc/AG0035.md @@ -1,13 +1,72 @@ -# Rule AG0035 - -

- Do not use MachineName. -

-
    -
  • Use of machine name tightly couples your code to our infrastructure and its naming scheme, which can and will change over time.
  • -
  • Your code should be agnostic of environment, data center, cluster and server. Having different code paths for different environments can lead to bugs that can only be caught in production.
  • -
  • Such environmental variations are usually only required when calling external services, as you will want to call the service running in your local data center. For this, use Consul's service discovery. It automatically supplies the correct configuration based on your environment.
  • -
-

- One exception is logging, where it can be useful to see the exact DC / cluster / server that made the request. Here, it makes sense to log the machine name. -

+# Don't Use MachineName + +ID: AG0035 + +Type: Code Smell / Architecture + +## Summary + +Avoid using `MachineName` in your code logic. Using machine names creates tight coupling to infrastructure and can lead to environment-specific bugs. + +## Explanation + +Using `MachineName`: + +- Couples code to infrastructure naming conventions +- Creates different code paths per environment +- Makes testing difficult +- Can mask issues until production +- Breaks cloud-native principles +- Makes deployment and scaling harder +- Reduces portability + +Exception: Logging is allowed to use `MachineName` for traceability. + +### Don't ❌ + +```csharp +// Environment-specific logic +if (Environment.MachineName.StartsWith("PROD")) +{ + url = "https://prod.api.example.com"; +} +else if (Environment.MachineName.StartsWith("STG")) +{ + url = "https://staging.api.example.com"; +} + +// Hard-coded machine dependencies +public class Service +{ + private readonly string _configPath = + Environment.MachineName == "SERVER1" + ? @"\\server1\config" + : @"\\server2\config"; +} + +// Direct infrastructure coupling +if (Environment.MachineName.Contains("DC1")) +{ + useDataCenter1(); +} +``` + +## Why Avoid MachineName? + +- Makes code environment-dependent +- Complicates deployment and scaling +- Creates hidden production-only bugs +- Breaks containerization +- Harder to test and maintain +- Not cloud-native friendly +- Makes configuration more complex + +Instead: + +- Use service discovery (e.g., Consul) +- Use configuration management +- Use environment variables +- Implement proper dependency injection +- Use cloud-native patterns + +Remember: Service discovery and proper configuration management should handle environment-specific needs, not machine names. diff --git a/doc/AG0037.md b/doc/AG0037.md index 72bb253..0899295 100644 --- a/doc/AG0037.md +++ b/doc/AG0037.md @@ -1,68 +1,74 @@ -# Rule AG0037 +# Selenium Tests Must Indicate Ownership -

- A Selenium test must indicate the team responsible for its maintenance. -

-

- The entire test class and/or each individual tests must be decorated with the [OwnedBy()] attribute. - This allows us to automatically notify the relevant parties of a problematic test. -

+ID: AG0037 -

Noncompliant Code Example

+Type: Maintainability -
-    namespace Agoda.Website.Selenium.Tests
+## Summary
+
+All Selenium tests must be decorated with the `[OwnedBy()]` attribute to indicate which team is responsible for maintenance. This can be applied at the class level or individual test level.
+
+## Explanation
+
+Test ownership must be clearly indicated to:
+
+- Enable automatic notification of test failures
+- Ensure clear responsibility for maintenance
+- Speed up issue resolution
+- Prevent orphaned tests
+- Maintain test quality over time
+
+### Don't ❌
+
+```csharp
+namespace Agoda.Website.Selenium.Tests
+{
+    public class HotelSearchTests
     {
-        class TestClass
+        [Test]
+        public void SearchByLocation() // No ownership specified
         {
-            [Test]
-            public void BadMethod() 
-            {
-            }
+            // Test implementation
         }
-    }
-
-

Compliant Code Examples

-
-    namespace Agoda.Website.Selenium.Tests
-    {
-        class TestClass
+        [Test]
+        public void FilterByPrice() // No ownership specified
         {
-            [Test]
-            [OwnedBy(Team.MyTeam)]
-            public void Test1() 
-            {
-            }
-    
-            [Test]
-            [OwnedBy(Team.AnotherTeam)]
-            public void Test2() 
-            {
-            }
+            // Test implementation
         }
     }
-    
-    namespace Agoda.Website.Selenium.Tests
+}
+```
+
+### Do ✅
+
+```csharp
+namespace Agoda.Website.Selenium.Tests
+{
+    [OwnedBy(Team.MyTeam)] // Class-level ownership
+    public class HotelSearchTests
     {
-        [OwnedBy(Team.MyTeam)] // all tests is class owned by MyTeam, except Test3
-        class TestClass
+        [Test]
+        public void SearchByLocation()
+        {
+            // Test implementation
+        }
+
+        [Test]
+        [OwnedBy(Team.OtherTeam)] // Override for specific test
+        public void FilterByPrice()
         {
-            [Test]
-            public void Test1() 
-            {
-            }
-    
-            [Test]
-            public void Test2() 
-            {
-            }
-    
-            [Test]
-            [OwnedBy(Team.AnotherTeam)]
-            public void Test3() 
-            {
-            }
+            // Test implementation
         }
     }
-
+} +``` + +## How to Apply Ownership + +- Use `[OwnedBy(Team.TeamName)]` at class level for all tests owned by one team +- Use test-level attributes to override class-level ownership +- Every test must have ownership either from class or test level +- Use the `Team` enum to specify the owning team + +Remember: Clear ownership is crucial for maintaining a healthy test suite and quick issue resolution. diff --git a/doc/AG0038.md b/doc/AG0038.md index 9115987..85efd34 100644 --- a/doc/AG0038.md +++ b/doc/AG0038.md @@ -1,14 +1,67 @@ -# Rule AG0038 - -

- Do not use #region -

-
    -
  • If you need regions to make your code understandable then your class / method is too big. - Do not hide the problem inside regions, refactor it.
  • -
  • Separating fields, properties, constructors, private methods etc into their own regions just adds noise, - and should not be necessary if your class is well factored.
  • -
  • In many editors, including Visual Studio, the region will appear collapsed by default, hiding the code - within the region. It is generally bad practice to hide code by default, as this can lead to - bad decisions as the code is maintained over time.
  • -
+# Don't Use #region Directives + +ID: AG0038 + +Type: Code Smell + +## Summary + +The `#region` directive should not be used to organize code. If you feel the need for regions, it's a sign that your code needs refactoring. + +## Explanation + +Using `#region` directives: + +- Hides complexity instead of addressing it +- Encourages large, poorly organized classes +- Makes code harder to review and maintain +- Masks code structure issues +- Creates artificial groupings +- Defaults to collapsed in many editors, hiding code +- Makes navigation more difficult + +### Don't ❌ + +```csharp +public class UserManager +{ + #region Fields and Properties + private readonly IUserRepository _repository; + private readonly ILogger _logger; + public bool IsInitialized { get; private set; } + #endregion + + #region Constructors + public UserManager(IUserRepository repository, ILogger logger) + { + _repository = repository; + _logger = logger; + } + #endregion + + #region Public Methods + public async Task CreateUser(string username) + { + // Implementation + } + #endregion + + #region Private Helper Methods + private void ValidateUser(User user) + { + // Implementation + } + #endregion +} +``` + +Instead: + +- Break large classes into smaller ones +- Use proper class organization +- Extract related functionality +- Create well-named methods +- Follow Single Responsibility Principle +- Use proper abstraction layers + +Remember: If you need regions, your code probably needs refactoring instead. diff --git a/doc/AG0040.md b/doc/AG0040.md index a69c95d..5c00674 100644 --- a/doc/AG0040.md +++ b/doc/AG0040.md @@ -1,3 +1,68 @@ -# Rule AG0040 - -No HTML documentation available. Rule name: $name +# Don't Use WaitUntilState.NetworkIdle in Playwright + +ID: AG0040 + +Type: Bug Risk / Test Reliability + +## Summary + +Avoid using `WaitUntilState.NetworkIdle` in Playwright tests as it creates flaky tests. Instead, use explicit web assertions to verify page readiness. + +## Explanation + +`WaitUntilState.NetworkIdle` can cause tests to be unreliable because: + +- Network activity can be unpredictable +- Background requests may interfere +- Third-party resources can cause delays +- Analytics and tracking can trigger false waits +- CDN behavior can vary +- Network conditions can fluctuate + +### Don't ❌ + +```csharp +// Flaky - depends on network stability +await page.GotoAsync(url, new() { + WaitUntil = WaitUntilState.NetworkIdle +}); + +// Unreliable timing +await page.ClickAsync("button", new() { + WaitUntil = WaitUntilState.NetworkIdle +}); +``` + +### Do ✅ + +```csharp +// Wait for specific elements or states +await page.WaitForSelectorAsync(".content-loaded"); + +// Use explicit assertions +await Expect(page.Locator(".data-table")) + .ToBeVisibleAsync(); + +// Check for specific conditions +await page.WaitForFunctionAsync("() => window.dataLoaded === true"); +``` + +## Why Avoid NetworkIdle? + +- Creates non-deterministic tests +- Increases test flakiness +- Leads to timeout issues +- Makes tests environment-dependent +- Hides actual loading issues +- Difficult to debug failures + +Instead: + +- Wait for specific UI elements +- Use explicit assertions +- Check for application state +- Verify data loading completion +- Use meaningful selectors +- Test for actual readiness conditions + +Remember: Reliable tests depend on explicit, deterministic conditions, not network activity. \ No newline at end of file diff --git a/doc/prompt.md b/doc/prompt.md index 63dd311..f4486a7 100644 --- a/doc/prompt.md +++ b/doc/prompt.md @@ -105,17 +105,20 @@ WRITING STYLE TO FOLLOW - A blocking call ties up the thread while it waits for a response. During this time, it could have been doing something more useful, like serving other requests or redrawing the GUI. - A non-blocking call returns the thread to the threadpool, or keeps the GUI thread responsive, while the task completes. -#### Don't +#### Don't ❌ + ```c# Thread.Sleep(5000); // thread is blocked for 5 seconds ``` -#### Do +#### Do ✅ + ```c# await Task.Delay(5000); // thread can do other stuff for 5 seconds. ``` -#### Don't +#### Don't ❌ + ```c# public void CreateCsv() { @@ -126,7 +129,8 @@ public void CreateCsv() } ``` -#### Do +#### Do ✅ + ```c# public async Task CreateCsvAsync() { @@ -136,6 +140,7 @@ public async Task CreateCsvAsync() } } ``` + ## Use attribute-based routing instead of convention-based routing - Convention-based routing tightly couples your code to your URLs. By default, renaming an action method would change @@ -145,7 +150,7 @@ without breaking the site, or at least having to implement redirects. - Attribute-based routing is arguably easier to understand, as the URL appears right above the code to which it points. - For these reasons, always use attribute-based routing when exposing HTTP endpoints. -#### Don't +#### Don't ❌ ```c# public class Global @@ -168,7 +173,8 @@ public class Global } ``` -#### Do +#### Do ✅ + ```c# public class HomeController: Controller { diff --git a/src/Agoda.Analyzers.Test/AllAnalyzersUnitTests.cs b/src/Agoda.Analyzers.Test/AllAnalyzersUnitTests.cs index 14acfba..932a232 100644 --- a/src/Agoda.Analyzers.Test/AllAnalyzersUnitTests.cs +++ b/src/Agoda.Analyzers.Test/AllAnalyzersUnitTests.cs @@ -46,13 +46,12 @@ private static IEnumerable GetAnalyzerTestCases() [TestCaseSource(nameof(GetAnalyzerTestCases))] public void Analyzer_Should_Have_Required_Properties_For_Diagnostic_Create(Type analyzerType) { - // Modified BindingFlags to include all methods var methods = analyzerType.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly | - BindingFlags.Static | // Added to catch static methods - BindingFlags.FlattenHierarchy); // Added to get inherited methods + BindingFlags.Static | + BindingFlags.FlattenHierarchy); var violations = new List(); @@ -84,8 +83,8 @@ public void Analyzer_Should_Have_Required_Properties_For_Diagnostic_Create(Type properties.ShouldNotBeNull($"Properties is null or not a dictionary"); - properties.ContainsKey("MyKey") - .ShouldBeTrue($"Method {method.Name} doesn't contain required 'MyKey' property"); + 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"); diff --git a/src/Agoda.Analyzers/AnalyzerConstants.cs b/src/Agoda.Analyzers/AnalyzerConstants.cs index 06bf5d9..2803dbf 100644 --- a/src/Agoda.Analyzers/AnalyzerConstants.cs +++ b/src/Agoda.Analyzers/AnalyzerConstants.cs @@ -7,6 +7,8 @@ namespace Agoda.Analyzers { internal static class AnalyzerConstants { + internal const string KEY_TECH_DEBT_IN_MINUTES = "TechDebtInMinutes"; + static AnalyzerConstants() { #if DEBUG diff --git a/src/Agoda.Analyzers/Const.cs b/src/Agoda.Analyzers/Const.cs deleted file mode 100644 index cef4387..0000000 --- a/src/Agoda.Analyzers/Const.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Agoda.Analyzers -{ - internal static class Const - { - internal const string KEY_TECH_DEBT_IN_MINUTES = "TechDebtInMinutes"; - } -} From 53aef96f2fc7ad43b750886b269771673601cfe7 Mon Sep 17 00:00:00 2001 From: jdickson Date: Mon, 18 Nov 2024 14:51:22 +0700 Subject: [PATCH 4/7] passing tests --- .../AllAnalyzersUnitTests.cs | 81 +++++++++---------- .../AG0001DependencyResolverMustNotBeUsed.cs | 2 +- .../AG0002PrivateMethodsShouldNotBeTested.cs | 12 ++- ...tpContextCannotBePassedAsMethodArgument.cs | 10 ++- ...DoNotUseHardCodedStringsToIdentifyTypes.cs | 9 ++- ...0005TestMethodNamesMustFollowConvention.cs | 7 +- ...ntShouldHaveExactlyOnePublicConstructor.cs | 9 ++- ...tAccessorCannotBePassedAsMethodArgument.cs | 7 +- .../AG0010PreventTestFixtureInheritance.cs | 7 +- .../AG0011NoDirectQueryStringAccess.cs | 32 ++++---- ...estMethodMustContainAtLeastOneAssertion.cs | 7 +- ...013LimitNumberOfTestMethodParametersTo5.cs | 8 +- ...itOnlyCertainPubliclyExposedEnumerables.cs | 7 +- ...PreventUseOfInternalsVisibleToAttribute.cs | 8 +- .../AG0020AvoidReturningNullEnumerables.cs | 9 ++- ...voidReturningNullEnumerablesFixProvider.cs | 3 +- .../AgodaCustom/AG0021PreferAsyncMethods.cs | 6 +- ...ExposeBothSyncAndAsyncVersionsOfMethods.cs | 10 ++- .../AG0022RemoveSyncMethodFixProvider.cs | 3 +- .../AG0024PreventUseOfTaskFactoryStartNew.cs | 7 +- ...ureOnlyDataSeleniumIsUsedToFindElements.cs | 9 ++- .../AgodaCustom/AG0030PreventUseOfDynamics.cs | 14 +++- .../AG0032PreventUseOfBlockingTaskMethods.cs | 2 +- .../AG0033PreventUseOfTaskResult.cs | 2 +- .../AG0035PreventUseOfMachineName.cs | 2 +- ...37EnsureSeleniumTestHasOwnedByAttribute.cs | 7 +- ...PreventUseOfRegionPreprocessorDirective.cs | 8 +- .../AG0039MethodLineLengthAnalyzer.cs | 7 +- ...0WaitUntilStateNetworkIdleMustNotBeUsed.cs | 2 +- .../AgodaCustom/AG0041LogTemplateAnalyzer.cs | 10 ++- .../AG0042ElementHandlerShouldNotBeUsed.cs | 8 +- .../AG0043NoBuildServiceProvider.cs | 8 +- .../AG0044ForceOptionShouldNotBeUsed.cs | 8 +- ...SA1106CodeMustNotContainEmptyStatements.cs | 12 ++- ...stNotContainMultipleStatementsOnOneLine.cs | 8 +- .../SA1123DoNotPlaceRegionsWithinElements.cs | 8 +- 36 files changed, 249 insertions(+), 110 deletions(-) diff --git a/src/Agoda.Analyzers.Test/AllAnalyzersUnitTests.cs b/src/Agoda.Analyzers.Test/AllAnalyzersUnitTests.cs index 932a232..3735c98 100644 --- a/src/Agoda.Analyzers.Test/AllAnalyzersUnitTests.cs +++ b/src/Agoda.Analyzers.Test/AllAnalyzersUnitTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Reflection; using Agoda.Analyzers.Helpers; @@ -44,14 +45,14 @@ private static IEnumerable 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(); @@ -59,35 +60,12 @@ public void Analyzer_Should_Have_Required_Properties_For_Diagnostic_Create(Type { 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; - - 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) @@ -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 FindDiagnosticCreateCalls(MethodInfo method) { + var calls = new List(); 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(); for (int i = 0; i < instructions.Length; i++) { @@ -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 @@ -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; } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs b/src/Agoda.Analyzers/AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs index cd24096..d3e30c5 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs @@ -14,7 +14,7 @@ namespace Agoda.Analyzers.AgodaCustom public class AG0001DependencyResolverMustNotBeUsed : PropertyInvocationAnalyzerBase { internal override Dictionary Properties => new Dictionary() - { { Const.KEY_TECH_DEBT_IN_MINUTES, "10" } }; + { { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } }; public const string DIAGNOSTIC_ID = "AG0001"; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0002PrivateMethodsShouldNotBeTested.cs b/src/Agoda.Analyzers/AgodaCustom/AG0002PrivateMethodsShouldNotBeTested.cs index 33cf8e2..5d16df3 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0002PrivateMethodsShouldNotBeTested.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0002PrivateMethodsShouldNotBeTested.cs @@ -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; @@ -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 _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } -} \ No newline at end of file +} diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0003HttpContextCannotBePassedAsMethodArgument.cs b/src/Agoda.Analyzers/AgodaCustom/AG0003HttpContextCannotBePassedAsMethodArgument.cs index 319999e..75e0ca7 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0003HttpContextCannotBePassedAsMethodArgument.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0003HttpContextCannotBePassedAsMethodArgument.cs @@ -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 @@ -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 _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; + } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0004DoNotUseHardCodedStringsToIdentifyTypes.cs b/src/Agoda.Analyzers/AgodaCustom/AG0004DoNotUseHardCodedStringsToIdentifyTypes.cs index 0bba77a..011af20 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0004DoNotUseHardCodedStringsToIdentifyTypes.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0004DoNotUseHardCodedStringsToIdentifyTypes.cs @@ -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 { @@ -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; } @@ -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 _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0005TestMethodNamesMustFollowConvention.cs b/src/Agoda.Analyzers/AgodaCustom/AG0005TestMethodNamesMustFollowConvention.cs index ac78a28..3e5f643 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0005TestMethodNamesMustFollowConvention.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0005TestMethodNamesMustFollowConvention.cs @@ -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 { @@ -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 _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0006RegisteredComponentShouldHaveExactlyOnePublicConstructor.cs b/src/Agoda.Analyzers/AgodaCustom/AG0006RegisteredComponentShouldHaveExactlyOnePublicConstructor.cs index 2c87790..d487d52 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0006RegisteredComponentShouldHaveExactlyOnePublicConstructor.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0006RegisteredComponentShouldHaveExactlyOnePublicConstructor.cs @@ -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; @@ -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 _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0009IHttpContextAccessorCannotBePassedAsMethodArgument.cs b/src/Agoda.Analyzers/AgodaCustom/AG0009IHttpContextAccessorCannotBePassedAsMethodArgument.cs index d8a7efc..4e613da 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0009IHttpContextAccessorCannotBePassedAsMethodArgument.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0009IHttpContextAccessorCannotBePassedAsMethodArgument.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Collections.Immutable; using Agoda.Analyzers.Helpers; using Microsoft.CodeAnalysis; @@ -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())); } } @@ -57,5 +58,9 @@ public override void Initialize(AnalysisContext context) context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.Parameter); } + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0010PreventTestFixtureInheritance.cs b/src/Agoda.Analyzers/AgodaCustom/AG0010PreventTestFixtureInheritance.cs index e5eb25a..fa55de5 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0010PreventTestFixtureInheritance.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0010PreventTestFixtureInheritance.cs @@ -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 { @@ -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 _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0011NoDirectQueryStringAccess.cs b/src/Agoda.Analyzers/AgodaCustom/AG0011NoDirectQueryStringAccess.cs index bcb808d..793d60c 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0011NoDirectQueryStringAccess.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0011NoDirectQueryStringAccess.cs @@ -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 Rules => new[] @@ -42,6 +42,8 @@ public class AG0011NoDirectQueryStringAccess : PropertyInvocationAnalyzerBase }; internal override Dictionary Properties => new Dictionary() - { { Const.KEY_TECH_DEBT_IN_MINUTES, "10" } }; - } + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; +} } \ No newline at end of file diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0012TestMethodMustContainAtLeastOneAssertion.cs b/src/Agoda.Analyzers/AgodaCustom/AG0012TestMethodMustContainAtLeastOneAssertion.cs index 2514e32..9ae1f00 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0012TestMethodMustContainAtLeastOneAssertion.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0012TestMethodMustContainAtLeastOneAssertion.cs @@ -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; @@ -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) @@ -128,5 +129,9 @@ public AssertLibraryInfo(string namespaceTitle, string module, string name, stri HasExtenstionMethods = type != null; } } + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0013LimitNumberOfTestMethodParametersTo5.cs b/src/Agoda.Analyzers/AgodaCustom/AG0013LimitNumberOfTestMethodParametersTo5.cs index cef738a..d327b40 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0013LimitNumberOfTestMethodParametersTo5.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0013LimitNumberOfTestMethodParametersTo5.cs @@ -6,6 +6,7 @@ using System.Text.RegularExpressions; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Collections.Generic; namespace Agoda.Analyzers.AgodaCustom { @@ -53,9 +54,14 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context) if(!IsTestPrametersMoreThanLimit(methodDeclaration)) { return; } - context.ReportDiagnostic(Diagnostic.Create(_diagnosticDescriptor, methodDeclaration.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(_diagnosticDescriptor, methodDeclaration.GetLocation(),_props.ToImmutableDictionary())); } private bool IsTestPrametersMoreThanLimit(MethodDeclarationSyntax method) => (method.ParameterList?.Parameters.Count ?? 0) > MAXIMUM_TEST_PARAMETERS; + + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0018PermitOnlyCertainPubliclyExposedEnumerables.cs b/src/Agoda.Analyzers/AgodaCustom/AG0018PermitOnlyCertainPubliclyExposedEnumerables.cs index a532b59..3256c63 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0018PermitOnlyCertainPubliclyExposedEnumerables.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0018PermitOnlyCertainPubliclyExposedEnumerables.cs @@ -64,7 +64,7 @@ public override void Initialize(AnalysisContext context) private static void Analyze(SyntaxNodeAnalysisContext context) { if (context.ContainingSymbol.DeclaredAccessibility != Accessibility.Public || IsPubliclyExposedIEnumerableTypes(context.ContainingSymbol)) { return; } - context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation(), properties: _props.ToImmutableDictionary())); } private static bool IsPubliclyExposedIEnumerableTypes(ISymbol symbol) @@ -113,5 +113,10 @@ private static bool IsPubliclyExposedIEnumerableTypes(ISymbol symbol) return true; } + + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0019PreventUseOfInternalsVisibleToAttribute.cs b/src/Agoda.Analyzers/AgodaCustom/AG0019PreventUseOfInternalsVisibleToAttribute.cs index 872900d..50fd877 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0019PreventUseOfInternalsVisibleToAttribute.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0019PreventUseOfInternalsVisibleToAttribute.cs @@ -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; namespace Agoda.Analyzers.AgodaCustom @@ -51,9 +52,14 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context) { if (attribute.Name is IdentifierNameSyntax name && name.Identifier.Text == "InternalsVisibleTo") { - context.ReportDiagnostic(Diagnostic.Create(_diagnosticDescriptor, attribute.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(_diagnosticDescriptor, attribute.GetLocation(),properties: _props.ToImmutableDictionary())); } } } + + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0020AvoidReturningNullEnumerables.cs b/src/Agoda.Analyzers/AgodaCustom/AG0020AvoidReturningNullEnumerables.cs index 7ec69c2..2319f58 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0020AvoidReturningNullEnumerables.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0020AvoidReturningNullEnumerables.cs @@ -1,4 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using Agoda.Analyzers.Helpers; using Microsoft.CodeAnalysis; @@ -93,10 +94,14 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context) && methodReturnType.ConstructedFrom.Interfaces.Any(x => x.ToDisplayString() == "System.Collections.IEnumerable") && methodReturnType.ConstructedFrom.ToDisplayString() != "string")) { - context.ReportDiagnostic(Diagnostic.Create(Descriptor, location)); + context.ReportDiagnostic(Diagnostic.Create(Descriptor, location, properties: _props.ToImmutableDictionary())); } } } + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0020AvoidReturningNullEnumerablesFixProvider.cs b/src/Agoda.Analyzers/AgodaCustom/AG0020AvoidReturningNullEnumerablesFixProvider.cs index d589c55..3c91ff8 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0020AvoidReturningNullEnumerablesFixProvider.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0020AvoidReturningNullEnumerablesFixProvider.cs @@ -1,4 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Generic; +using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0021PreferAsyncMethods.cs b/src/Agoda.Analyzers/AgodaCustom/AG0021PreferAsyncMethods.cs index 1aed8f7..ab9f761 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0021PreferAsyncMethods.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0021PreferAsyncMethods.cs @@ -67,7 +67,7 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context) if (alternativeAsyncMethods.Any()) { - context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation(), properties: _props.ToImmutableDictionary())); } } @@ -180,5 +180,9 @@ private struct MethodDescriptor /// public ITypeSymbol CallingType { get; set; } } + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0022DoNotExposeBothSyncAndAsyncVersionsOfMethods.cs b/src/Agoda.Analyzers/AgodaCustom/AG0022DoNotExposeBothSyncAndAsyncVersionsOfMethods.cs index 1ce211a..a9a4cb2 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0022DoNotExposeBothSyncAndAsyncVersionsOfMethods.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0022DoNotExposeBothSyncAndAsyncVersionsOfMethods.cs @@ -87,10 +87,14 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context) var methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodSyntax); if (!AsyncHelpers.IsAsyncIntent(methodSymbol)) { - context.ReportDiagnostic(Diagnostic.Create(Descriptor, methodSyntax.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(Descriptor, methodSyntax.GetLocation(), properties: _props.ToImmutableDictionary())); } } - - } + } + + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0022RemoveSyncMethodFixProvider.cs b/src/Agoda.Analyzers/AgodaCustom/AG0022RemoveSyncMethodFixProvider.cs index ee0a0a0..780d16b 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0022RemoveSyncMethodFixProvider.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0022RemoveSyncMethodFixProvider.cs @@ -1,4 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Generic; +using System.Collections.Immutable; using System.Composition; using System.Threading; using System.Threading.Tasks; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0024PreventUseOfTaskFactoryStartNew.cs b/src/Agoda.Analyzers/AgodaCustom/AG0024PreventUseOfTaskFactoryStartNew.cs index 5d0967c..b1c7c6b 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0024PreventUseOfTaskFactoryStartNew.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0024PreventUseOfTaskFactoryStartNew.cs @@ -68,8 +68,13 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context) ); if (isLongRunning) return; - context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation(), properties: _props.ToImmutableDictionary())); } + + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0027EnsureOnlyDataSeleniumIsUsedToFindElements.cs b/src/Agoda.Analyzers/AgodaCustom/AG0027EnsureOnlyDataSeleniumIsUsedToFindElements.cs index 63012a7..fa6855a 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0027EnsureOnlyDataSeleniumIsUsedToFindElements.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0027EnsureOnlyDataSeleniumIsUsedToFindElements.cs @@ -63,7 +63,7 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context) var firstArgument = invocationExpressionSyntax.ArgumentList.Arguments.FirstOrDefault(); if (firstArgument?.Expression is LiteralExpressionSyntax && !MatchDataSelenium.IsMatch(firstArgument.ToString())) { - context.ReportDiagnostic(Diagnostic.Create(Descriptor, firstArgument.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(Descriptor, firstArgument.GetLocation(), properties: _props.ToImmutableDictionary())); } if (!(firstArgument?.Expression is IdentifierNameSyntax)) @@ -75,8 +75,13 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context) var constantValue = context.SemanticModel.GetConstantValue(firstArgument.Expression); if (constantValue.HasValue && !MatchDataSelenium.IsMatch($@"""{constantValue.Value}""")) { - context.ReportDiagnostic(Diagnostic.Create(Descriptor, firstArgument.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(Descriptor, firstArgument.GetLocation(), properties: _props.ToImmutableDictionary())); } } + + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0030PreventUseOfDynamics.cs b/src/Agoda.Analyzers/AgodaCustom/AG0030PreventUseOfDynamics.cs index 1cb05a1..693e71d 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0030PreventUseOfDynamics.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0030PreventUseOfDynamics.cs @@ -1,4 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using Agoda.Analyzers.Helpers; using Microsoft.CodeAnalysis; @@ -52,21 +53,21 @@ private void AnalyzeVariableDeclaration(SyntaxNodeAnalysisContext context) { var variableDeclaration = (VariableDeclarationSyntax)context.Node; if (ValidateReturnType(variableDeclaration.Type)) return; - context.ReportDiagnostic(Diagnostic.Create(Descriptor, variableDeclaration.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(Descriptor, variableDeclaration.GetLocation(), properties: _props.ToImmutableDictionary())); } private void AnalyzeMethodDeclaration(SyntaxNodeAnalysisContext context) { var methodDeclaration = (MethodDeclarationSyntax)context.Node; if (ValidateReturnType(methodDeclaration.ReturnType)) return; - context.ReportDiagnostic(Diagnostic.Create(Descriptor, methodDeclaration.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(Descriptor, methodDeclaration.GetLocation(), properties: _props.ToImmutableDictionary())); } private void AnalyzeGenericName(SyntaxNodeAnalysisContext context) { var genericName = (GenericNameSyntax)context.Node; if (ValidateGenericType(genericName)) return; - context.ReportDiagnostic(Diagnostic.Create(Descriptor, genericName.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(Descriptor, genericName.GetLocation(), properties: _props.ToImmutableDictionary())); } private bool ValidateReturnType(TypeSyntax returnTypeSyntax) @@ -79,5 +80,10 @@ private bool ValidateGenericType(GenericNameSyntax genericName) { return genericName.TypeArgumentList.Arguments.All(argument => argument.GetText().ToString() != "dynamic"); } + + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "30" } + }; } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0032PreventUseOfBlockingTaskMethods.cs b/src/Agoda.Analyzers/AgodaCustom/AG0032PreventUseOfBlockingTaskMethods.cs index 504ba14..440b936 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0032PreventUseOfBlockingTaskMethods.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0032PreventUseOfBlockingTaskMethods.cs @@ -14,7 +14,7 @@ namespace Agoda.Analyzers.AgodaCustom public class AG0032PreventUseOfBlockingTaskMethods : PropertyInvocationAnalyzerBase { internal override Dictionary Properties => new Dictionary() - { { Const.KEY_TECH_DEBT_IN_MINUTES, "10" } }; + { { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } }; public const string DIAGNOSTIC_ID = "AG0032"; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0033PreventUseOfTaskResult.cs b/src/Agoda.Analyzers/AgodaCustom/AG0033PreventUseOfTaskResult.cs index c76fe2d..97a7330 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0033PreventUseOfTaskResult.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0033PreventUseOfTaskResult.cs @@ -14,7 +14,7 @@ namespace Agoda.Analyzers.AgodaCustom public class AG0033PreventUseOfTaskResult : PropertyInvocationAnalyzerBase { internal override Dictionary Properties => new Dictionary() - { { Const.KEY_TECH_DEBT_IN_MINUTES, "10" } }; + { { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } }; public const string DIAGNOSTIC_ID = "AG0033"; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0035PreventUseOfMachineName.cs b/src/Agoda.Analyzers/AgodaCustom/AG0035PreventUseOfMachineName.cs index 5994a54..6c38230 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0035PreventUseOfMachineName.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0035PreventUseOfMachineName.cs @@ -9,7 +9,7 @@ namespace Agoda.Analyzers.AgodaCustom public class AG0035PreventUseOfMachineName : PropertyInvocationAnalyzerBase { internal override Dictionary Properties => new Dictionary() - { { Const.KEY_TECH_DEBT_IN_MINUTES, "10" } }; + { { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } }; public const string DIAGNOSTIC_ID = "AG0035"; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0037EnsureSeleniumTestHasOwnedByAttribute.cs b/src/Agoda.Analyzers/AgodaCustom/AG0037EnsureSeleniumTestHasOwnedByAttribute.cs index b8e4930..2856fa5 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0037EnsureSeleniumTestHasOwnedByAttribute.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0037EnsureSeleniumTestHasOwnedByAttribute.cs @@ -88,7 +88,12 @@ private void AnalyzeMethodDeclaration(SyntaxNodeAnalysisContext context) return; } - context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation(), properties: _props.ToImmutableDictionary())); } + + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0038PreventUseOfRegionPreprocessorDirective.cs b/src/Agoda.Analyzers/AgodaCustom/AG0038PreventUseOfRegionPreprocessorDirective.cs index 8254ed7..3e15b85 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0038PreventUseOfRegionPreprocessorDirective.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0038PreventUseOfRegionPreprocessorDirective.cs @@ -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 @@ -50,7 +51,12 @@ public override void Initialize(AnalysisContext context) private static void AnalyzeNode(SyntaxNodeAnalysisContext context) { - context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation(), properties: _props.ToImmutableDictionary())); } + + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0039MethodLineLengthAnalyzer.cs b/src/Agoda.Analyzers/AgodaCustom/AG0039MethodLineLengthAnalyzer.cs index 1ecb9ec..e9f5df6 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0039MethodLineLengthAnalyzer.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0039MethodLineLengthAnalyzer.cs @@ -71,7 +71,7 @@ private void AnalyzeMethod(SyntaxNodeAnalysisContext context) if (lineCount <= MaxLines) return; - var diagnostic = Diagnostic.Create(Descriptor, methodNode.Identifier.GetLocation(), methodNode.Identifier.Text, lineCount, MaxLines); + var diagnostic = Diagnostic.Create(Descriptor, methodNode.Identifier.GetLocation(), properties: _props.ToImmutableDictionary(), methodNode.Identifier.Text, lineCount, MaxLines); context.ReportDiagnostic(diagnostic); } @@ -79,5 +79,10 @@ private static int GetNonEmptyLineCount(IEnumerable lines) { return lines.Count(line => !string.IsNullOrWhiteSpace(line.ToString())); } + + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0040WaitUntilStateNetworkIdleMustNotBeUsed.cs b/src/Agoda.Analyzers/AgodaCustom/AG0040WaitUntilStateNetworkIdleMustNotBeUsed.cs index 2155250..46edcdc 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0040WaitUntilStateNetworkIdleMustNotBeUsed.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0040WaitUntilStateNetworkIdleMustNotBeUsed.cs @@ -9,7 +9,7 @@ namespace Agoda.Analyzers.AgodaCustom public class AG0040WaitUntilStateNetworkIdleMustNotBeUsed : PropertyInvocationAnalyzerBase { internal override Dictionary Properties => new Dictionary() - { { Const.KEY_TECH_DEBT_IN_MINUTES, "10" } }; + { { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } }; public const string DIAGNOSTIC_ID = "AG0040"; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0041LogTemplateAnalyzer.cs b/src/Agoda.Analyzers/AgodaCustom/AG0041LogTemplateAnalyzer.cs index 540a672..4f665f5 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0041LogTemplateAnalyzer.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0041LogTemplateAnalyzer.cs @@ -1,4 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -47,7 +48,7 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context) if (argument.Expression.IsKind(SyntaxKind.InterpolatedStringExpression) || ContainsStringConcatenation(argument.Expression)) { - var diagnostic = Diagnostic.Create(Rule, argument.GetLocation()); + var diagnostic = Diagnostic.Create(Rule, argument.GetLocation(), properties: _props.ToImmutableDictionary()); context.ReportDiagnostic(diagnostic); } } @@ -86,5 +87,10 @@ private static bool ContainsStringConcatenation(ExpressionSyntax expression) (bes.Left.IsKind(SyntaxKind.StringLiteralExpression) || bes.Right.IsKind(SyntaxKind.StringLiteralExpression))); } + + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0042ElementHandlerShouldNotBeUsed.cs b/src/Agoda.Analyzers/AgodaCustom/AG0042ElementHandlerShouldNotBeUsed.cs index 38912e4..d6ac217 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0042ElementHandlerShouldNotBeUsed.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0042ElementHandlerShouldNotBeUsed.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Collections.Immutable; using Agoda.Analyzers.Helpers; using Microsoft.CodeAnalysis; @@ -70,7 +71,7 @@ private static void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context) var returnType = methodSymbol.ReturnType; if (!IsElementHandleReturnType(returnType)) return; - var diagnostic = Diagnostic.Create(Descriptor, invocationExpression.GetLocation()); + var diagnostic = Diagnostic.Create(Descriptor, invocationExpression.GetLocation(), properties: _props.ToImmutableDictionary()); context.ReportDiagnostic(diagnostic); } @@ -130,5 +131,10 @@ private static bool IsPlaywrightPageOrElementType(INamedTypeSymbol typeSymbol) typeName == "Microsoft.Playwright.IElementHandle" || typeName == "Microsoft.Playwright.ElementHandle"; } + + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0043NoBuildServiceProvider.cs b/src/Agoda.Analyzers/AgodaCustom/AG0043NoBuildServiceProvider.cs index 8653f89..152a414 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0043NoBuildServiceProvider.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0043NoBuildServiceProvider.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Agoda.Analyzers.Helpers; @@ -86,9 +87,14 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context) if (isServiceCollectionExtension || isExpressionServiceCollection) { - var diagnostic = Diagnostic.Create(Descriptor, memberAccessExpr.Name.GetLocation()); + var diagnostic = Diagnostic.Create(Descriptor, memberAccessExpr.Name.GetLocation(), properties: _props.ToImmutableDictionary()); context.ReportDiagnostic(diagnostic); } } + + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0044ForceOptionShouldNotBeUsed.cs b/src/Agoda.Analyzers/AgodaCustom/AG0044ForceOptionShouldNotBeUsed.cs index b73ff06..a1dc3f8 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0044ForceOptionShouldNotBeUsed.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0044ForceOptionShouldNotBeUsed.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -70,7 +71,7 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context) if (!(assignment.Right is LiteralExpressionSyntax literal) || literal.Token.ValueText != "true") continue; - var diagnostic = Diagnostic.Create(Rule, expression.GetLocation()); + var diagnostic = Diagnostic.Create(Rule, expression.GetLocation(), properties: _props.ToImmutableDictionary()); context.ReportDiagnostic(diagnostic); } } @@ -87,5 +88,10 @@ private static bool IsLocatorOptionsType(string typeName) typeName.Contains("LocatorUncheck") || typeName.Contains("LocatorSelectOption")); } + + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "60" } + }; } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/StyleCop/SA1106CodeMustNotContainEmptyStatements.cs b/src/Agoda.Analyzers/StyleCop/SA1106CodeMustNotContainEmptyStatements.cs index ab8aef9..45318f2 100644 --- a/src/Agoda.Analyzers/StyleCop/SA1106CodeMustNotContainEmptyStatements.cs +++ b/src/Agoda.Analyzers/StyleCop/SA1106CodeMustNotContainEmptyStatements.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Collections.Immutable; using Agoda.Analyzers.Helpers; using Microsoft.CodeAnalysis; @@ -57,7 +58,7 @@ private static void HandleBaseTypeDeclaration(SyntaxNodeAnalysisContext context) if (declaration.SemicolonToken.IsKind(SyntaxKind.SemicolonToken)) { - context.ReportDiagnostic(Diagnostic.Create(Descriptor, declaration.SemicolonToken.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(Descriptor, declaration.SemicolonToken.GetLocation(), properties: _props.ToImmutableDictionary())); } } @@ -67,7 +68,7 @@ private static void HandleNamespaceDeclaration(SyntaxNodeAnalysisContext context if (declaration.SemicolonToken.IsKind(SyntaxKind.SemicolonToken)) { - context.ReportDiagnostic(Diagnostic.Create(Descriptor, declaration.SemicolonToken.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(Descriptor, declaration.SemicolonToken.GetLocation(), properties: _props.ToImmutableDictionary())); } } @@ -101,7 +102,12 @@ private static void HandleEmptyStatement(SyntaxNodeAnalysisContext context) } // Code must not contain empty statements - context.ReportDiagnostic(Diagnostic.Create(Descriptor, syntax.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(Descriptor, syntax.GetLocation(), properties: _props.ToImmutableDictionary())); } + + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/StyleCop/SA1107CodeMustNotContainMultipleStatementsOnOneLine.cs b/src/Agoda.Analyzers/StyleCop/SA1107CodeMustNotContainMultipleStatementsOnOneLine.cs index 63b87c1..a7fc783 100644 --- a/src/Agoda.Analyzers/StyleCop/SA1107CodeMustNotContainMultipleStatementsOnOneLine.cs +++ b/src/Agoda.Analyzers/StyleCop/SA1107CodeMustNotContainMultipleStatementsOnOneLine.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Collections.Immutable; using Agoda.Analyzers.Helpers; using Microsoft.CodeAnalysis; @@ -67,7 +68,7 @@ private static void HandleBlock(SyntaxNodeAnalysisContext context) == currentStatementLocation.StartLinePosition.Line && !IsLastTokenMissing(previousStatement)) { - context.ReportDiagnostic(Diagnostic.Create(Descriptor, block.Statements[i].GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(Descriptor, block.Statements[i].GetLocation(), properties: _props.ToImmutableDictionary())); } previousStatementLocation = currentStatementLocation; @@ -80,5 +81,10 @@ private static bool IsLastTokenMissing(StatementSyntax previousStatement) { return previousStatement.GetLastToken(includeZeroWidth: true, includeSkipped: true).IsMissing; } + + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } \ No newline at end of file diff --git a/src/Agoda.Analyzers/StyleCop/SA1123DoNotPlaceRegionsWithinElements.cs b/src/Agoda.Analyzers/StyleCop/SA1123DoNotPlaceRegionsWithinElements.cs index e6d1a54..bb4e391 100644 --- a/src/Agoda.Analyzers/StyleCop/SA1123DoNotPlaceRegionsWithinElements.cs +++ b/src/Agoda.Analyzers/StyleCop/SA1123DoNotPlaceRegionsWithinElements.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; @@ -94,8 +95,13 @@ private static void HandleRegionDirectiveTrivia(SyntaxNodeAnalysisContext contex if (IsCompletelyContainedInBody(regionSyntax)) { // Region must not be located within a code element. - context.ReportDiagnostic(Diagnostic.Create(Descriptor, regionSyntax.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(Descriptor, regionSyntax.GetLocation(), properties: _props.ToImmutableDictionary())); } } + + private static Dictionary _props = new Dictionary() + { + { AnalyzerConstants.KEY_TECH_DEBT_IN_MINUTES, "10" } + }; } } \ No newline at end of file From 3b73b742255eafa2eb7b4b05e46f29ce36e75772 Mon Sep 17 00:00:00 2001 From: jdickson Date: Mon, 18 Nov 2024 16:30:25 +0700 Subject: [PATCH 5/7] Update all docs links --- doc/AG0001.md | 2 ++ doc/AG0003.md | 2 ++ doc/AG0004.md | 2 ++ doc/AG0005.md | 2 ++ doc/AG0010.md | 2 ++ doc/AG0011.md | 2 ++ doc/AG0012.md | 2 ++ doc/AG0013.md | 2 ++ doc/AG0018.md | 2 ++ doc/AG0019.md | 2 ++ doc/AG0020.md | 2 ++ doc/AG0021.md | 2 ++ doc/AG0022.md | 2 ++ doc/AG0023.md | 2 ++ doc/AG0024.md | 2 ++ doc/AG0025.md | 2 ++ doc/AG0026.md | 2 ++ doc/AG0027.md | 2 ++ doc/AG0030.md | 2 ++ doc/AG0032.md | 2 ++ doc/AG0033.md | 2 ++ doc/AG0035.md | 2 ++ doc/AG0038.md | 2 ++ doc/AG0039.md | 2 ++ doc/AG0040.md | 2 ++ .../AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs | 2 +- .../AgodaCustom/AG0002PrivateMethodsShouldNotBeTested.cs | 3 ++- .../AG0003HttpContextCannotBePassedAsMethodArgument.cs | 4 ++-- .../AG0004DoNotUseHardCodedStringsToIdentifyTypes.cs | 4 ++-- .../AG0005TestMethodNamesMustFollowConvention.cs | 4 ++-- ...isteredComponentShouldHaveExactlyOnePublicConstructor.cs | 4 ++-- ...009IHttpContextAccessorCannotBePassedAsMethodArgument.cs | 2 +- .../AgodaCustom/AG0010PreventTestFixtureInheritance.cs | 2 +- .../AgodaCustom/AG0011NoDirectQueryStringAccess.cs | 2 +- .../AG0012TestMethodMustContainAtLeastOneAssertion.cs | 2 +- .../AG0013LimitNumberOfTestMethodParametersTo5.cs | 4 ++-- .../AG0018PermitOnlyCertainPubliclyExposedEnumerables.cs | 4 ++-- .../AG0019PreventUseOfInternalsVisibleToAttribute.cs | 2 +- .../AgodaCustom/AG0020AvoidReturningNullEnumerables.cs | 4 ++-- src/Agoda.Analyzers/AgodaCustom/AG0021PreferAsyncMethods.cs | 4 ++-- .../AG0022DoNotExposeBothSyncAndAsyncVersionsOfMethods.cs | 4 ++-- .../AgodaCustom/AG0023PreventUseOfThreadSleep.cs | 2 +- .../AgodaCustom/AG0024PreventUseOfTaskFactoryStartNew.cs | 2 +- .../AgodaCustom/AG0025PreventUseOfTaskContinue.cs | 4 ++-- .../AG0026EnsureOnlyCssSelectorIsUsedToFindElements.cs | 2 +- .../AG0027EnsureOnlyDataSeleniumIsUsedToFindElements.cs | 2 +- .../AgodaCustom/AG0030PreventUseOfDynamics.cs | 2 +- .../AgodaCustom/AG0032PreventUseOfBlockingTaskMethods.cs | 2 +- .../AgodaCustom/AG0033PreventUseOfTaskResult.cs | 2 +- .../AgodaCustom/AG0035PreventUseOfMachineName.cs | 2 +- .../AG0037EnsureSeleniumTestHasOwnedByAttribute.cs | 2 +- .../AG0038PreventUseOfRegionPreprocessorDirective.cs | 4 ++-- .../AgodaCustom/AG0039MethodLineLengthAnalyzer.cs | 2 +- .../AG0040WaitUntilStateNetworkIdleMustNotBeUsed.cs | 2 +- src/Agoda.Analyzers/AgodaCustom/AG0041CodeFixProvider.cs | 2 +- .../AgodaCustom/AG0041LogTemplateAnalyzer.cs | 6 +++--- .../AgodaCustom/AG0042ElementHandlerShouldNotBeUsed.cs | 2 +- .../AgodaCustom/AG0043NoBuildServiceProvider.cs | 2 +- .../AgodaCustom/AG0044ForceOptionShouldNotBeUsed.cs | 2 +- 59 files changed, 98 insertions(+), 47 deletions(-) diff --git a/doc/AG0001.md b/doc/AG0001.md index 7233417..6f2ad01 100644 --- a/doc/AG0001.md +++ b/doc/AG0001.md @@ -4,6 +4,8 @@ ID: AG0001 Type: Code Smell +https://agoda-com.github.io/standards-c-sharp/di/attribute-based-registration.html + Direct usage of `DependencyResolver` creates tight coupling and makes code harder to test. Dependencies should be explicitly declared through constructor injection, which promotes: - Better testability through clear dependency declaration diff --git a/doc/AG0003.md b/doc/AG0003.md index b9bfca1..7ab9c86 100644 --- a/doc/AG0003.md +++ b/doc/AG0003.md @@ -4,6 +4,8 @@ ID: AG0003 Type: Code Smell +https://agoda-com.github.io/standards-c-sharp/services/framework-abstractions.html + Passing `System.Web.HttpContext` as a parameter creates tight coupling to the web infrastructure and makes unit testing significantly more difficult. Instead, pass only the specific HttpContext properties that your code actually needs. #### Don't diff --git a/doc/AG0004.md b/doc/AG0004.md index 706c81b..ba97c97 100644 --- a/doc/AG0004.md +++ b/doc/AG0004.md @@ -4,6 +4,8 @@ ID: AG0004 Type: Bug +https://agoda-com.github.io/standards-c-sharp/reflection/hard-coded-strings.html + Hard-coded strings to identify namespaces and types make refactoring risky and move type resolution errors from compile-time to runtime. Always use the `typeof` operator to reference types, which provides compile-time safety. #### Don't diff --git a/doc/AG0005.md b/doc/AG0005.md index fe1367d..2e0c187 100644 --- a/doc/AG0005.md +++ b/doc/AG0005.md @@ -4,6 +4,8 @@ ID: AG0005 Type: Code Smell +https://agoda-com.github.io/standards-c-sharp/testing/test-method-names-should-clearly-indicate-what-they-are-testing.html + Test method names should clearly communicate what is being tested, under what conditions, and the expected outcome. This makes tests serve as documentation and helps quickly identify what failed when tests break. Test names should follow this pattern: diff --git a/doc/AG0010.md b/doc/AG0010.md index 3bce3cf..462b95b 100644 --- a/doc/AG0010.md +++ b/doc/AG0010.md @@ -4,6 +4,8 @@ ID: AG0010 Type: Code Smell +https://agoda-com.github.io/standards-c-sharp/unit-testing/be-wary-of-refactoring-tests.html + Test fixture inheritance should be avoided as it creates hidden dependencies, makes tests harder to understand, and violates the test isolation principle. Each test class should be independent and self-contained. #### Don't diff --git a/doc/AG0011.md b/doc/AG0011.md index 4e193d3..1414a02 100644 --- a/doc/AG0011.md +++ b/doc/AG0011.md @@ -4,6 +4,8 @@ ID: AG0011 Type: Code Smell +https://agoda-com.github.io/standards-c-sharp/services/framework-abstractions.html + Direct access to `QueryString` collection bypasses ASP.NET model binding, which provides validation, type conversion, and security features. Always use model binding with action method parameters instead. #### Don't diff --git a/doc/AG0012.md b/doc/AG0012.md index bdf5858..387e9c2 100644 --- a/doc/AG0012.md +++ b/doc/AG0012.md @@ -4,6 +4,8 @@ ID: AG0012 Type: Bug +https://agoda-com.github.io/standards-c-sharp/testing/tests-as-a-specification.html + Each test method should contain at least one assertion to verify expected behavior. Tests without assertions don't validate anything and provide a false sense of security. #### Don't diff --git a/doc/AG0013.md b/doc/AG0013.md index 054d63d..604775c 100644 --- a/doc/AG0013.md +++ b/doc/AG0013.md @@ -4,6 +4,8 @@ ID: AG0013 Type: Code Smell +https://agoda-com.github.io/standards-c-sharp/unit-testing/use-test-cases-appropriately.html + Test methods with too many input parameters become difficult to understand and maintain. When test cases need many parameters, split them into smaller, more focused tests with clear intentions. #### Don't diff --git a/doc/AG0018.md b/doc/AG0018.md index db4610b..0f84a8e 100644 --- a/doc/AG0018.md +++ b/doc/AG0018.md @@ -6,6 +6,8 @@ Type: Code Smell ## Summary +https://agoda-com.github.io/standards-c-sharp/collections/choosing-collection-implementation.html + Public methods and properties that return IEnumerable-implementing types must be declared using specific interface types rather than concrete implementations. ## Explanation diff --git a/doc/AG0019.md b/doc/AG0019.md index 78dfecf..3b81d2b 100644 --- a/doc/AG0019.md +++ b/doc/AG0019.md @@ -6,6 +6,8 @@ Type: Code Smell ## Summary +https://agoda-com.github.io/standards-c-sharp/unit-testing/only-test-the-public-interface.html + The `InternalsVisibleTo` attribute should not be used as it violates encapsulation principles and creates tight coupling between test code and implementation details. ## Explanation diff --git a/doc/AG0020.md b/doc/AG0020.md index 0889ab1..6c7ea04 100644 --- a/doc/AG0020.md +++ b/doc/AG0020.md @@ -6,6 +6,8 @@ Type: Bug / Code Smell ## Summary +https://agoda-com.github.io/standards-c-sharp/collections/null-empty-enumerables.html + Methods or properties returning `IEnumerable` types should never return null. Instead, return an empty collection. ## Explanation diff --git a/doc/AG0021.md b/doc/AG0021.md index 819f008..2fd634a 100644 --- a/doc/AG0021.md +++ b/doc/AG0021.md @@ -6,6 +6,8 @@ Type: Code Smell / Performance ## Summary +https://agoda-com.github.io/standards-c-sharp/async/consume-async-method.html + Always use the asynchronous version of a method when available. Using synchronous methods when async alternatives exist can impact performance and scalability. ## Explanation diff --git a/doc/AG0022.md b/doc/AG0022.md index 4cb84f3..db866e4 100644 --- a/doc/AG0022.md +++ b/doc/AG0022.md @@ -6,6 +6,8 @@ Type: Code Smell / API Design ## Summary +https://agoda-com.github.io/standards-c-sharp/async/expose-async-method.html + When designing APIs that perform I/O or CPU-intensive work, expose only the asynchronous version of the method. Never provide both synchronous and asynchronous versions of the same functionality. ## Explanation diff --git a/doc/AG0023.md b/doc/AG0023.md index 1df3463..fcd2d28 100644 --- a/doc/AG0023.md +++ b/doc/AG0023.md @@ -6,6 +6,8 @@ Type: Performance / Code Smell ## Summary +https://agoda-com.github.io/standards-c-sharp/async/avoid-blocking.html + Never use `Thread.Sleep()` for delays in code. Instead, use the asynchronous `Task.Delay()` to avoid blocking threads. ## Explanation diff --git a/doc/AG0024.md b/doc/AG0024.md index bc075d9..1090443 100644 --- a/doc/AG0024.md +++ b/doc/AG0024.md @@ -6,6 +6,8 @@ Type: Code Smell / Best Practice ## Summary +https://agoda-com.github.io/standards-c-sharp/async/task-run.html + Avoid using `Task.Factory.StartNew` as it's complex and error-prone. Use `Task.Run` for most scenarios as it provides safer defaults and clearer semantics. ## Explanation diff --git a/doc/AG0025.md b/doc/AG0025.md index ff83800..6a53272 100644 --- a/doc/AG0025.md +++ b/doc/AG0025.md @@ -6,6 +6,8 @@ Type: Code Smell / Best Practice ## Summary +https://agoda-com.github.io/standards-c-sharp/async/never-task-continue-with.html + Avoid using `Task.ContinueWith` as it has subtle and non-intuitive behaviors. Use the `await` keyword instead, which is clearer and safer. ## Explanation diff --git a/doc/AG0026.md b/doc/AG0026.md index 4c29f76..0e849e7 100644 --- a/doc/AG0026.md +++ b/doc/AG0026.md @@ -6,6 +6,8 @@ Type: Code Style / Best Practice ## Summary +https://agoda-com.github.io/standards-c-sharp/gui-testing/css-selectors.html + In Selenium tests, use CSS selectors instead of XPath to locate elements. CSS selectors are more familiar, easier to read, and maintain. ## Explanation diff --git a/doc/AG0027.md b/doc/AG0027.md index a208280..f981e27 100644 --- a/doc/AG0027.md +++ b/doc/AG0027.md @@ -6,6 +6,8 @@ Type: Best Practice ## Summary +https://agoda-com.github.io/standards-c-sharp/gui-testing/data-selenium.html + Use the `data-selenium` attribute to identify elements in Selenium tests rather than relying on HTML structure, classes, or IDs. This makes tests more resilient to UI changes. ## Explanation diff --git a/doc/AG0030.md b/doc/AG0030.md index b5e3875..35207c1 100644 --- a/doc/AG0030.md +++ b/doc/AG0030.md @@ -6,6 +6,8 @@ Type: Bug Risk / Code Smell ## Summary +https://agoda-com.github.io/standards-c-sharp/code-style/dynamics.html + The `dynamic` type should not be used in C# code. It bypasses compile-time type checking and reduces code reliability. ## Explanation diff --git a/doc/AG0032.md b/doc/AG0032.md index a4506e0..e9987c3 100644 --- a/doc/AG0032.md +++ b/doc/AG0032.md @@ -6,6 +6,8 @@ Type: Bug / Performance ## Summary +https://agoda-com.github.io/standards-c-sharp/async/never-task-wait.html + Avoid using blocking Task methods like `Task.Wait()`, `Task.WaitAll()`, and `Task.WaitAny()`. These methods block threads and can cause deadlocks. ## Explanation diff --git a/doc/AG0033.md b/doc/AG0033.md index 4bc7d5c..67059f1 100644 --- a/doc/AG0033.md +++ b/doc/AG0033.md @@ -6,6 +6,8 @@ Type: Bug / Performance ## Summary +https://agoda-com.github.io/standards-c-sharp/async/await-task-result.html + Never use `Task.Result` to get the result of a Task. Use `await` instead. `Task.Result` blocks the current thread and can cause deadlocks. ## Explanation diff --git a/doc/AG0035.md b/doc/AG0035.md index da2f50b..6a11e4b 100644 --- a/doc/AG0035.md +++ b/doc/AG0035.md @@ -6,6 +6,8 @@ Type: Code Smell / Architecture ## Summary +https://agoda-com.github.io/standards-c-sharp/configuration/machine-name.html + Avoid using `MachineName` in your code logic. Using machine names creates tight coupling to infrastructure and can lead to environment-specific bugs. ## Explanation diff --git a/doc/AG0038.md b/doc/AG0038.md index 85efd34..76e9b61 100644 --- a/doc/AG0038.md +++ b/doc/AG0038.md @@ -6,6 +6,8 @@ Type: Code Smell ## Summary +https://agoda-com.github.io/standards-c-sharp/code-style/regions.html + The `#region` directive should not be used to organize code. If you feel the need for regions, it's a sign that your code needs refactoring. ## Explanation diff --git a/doc/AG0039.md b/doc/AG0039.md index 30c8f93..d847917 100644 --- a/doc/AG0039.md +++ b/doc/AG0039.md @@ -6,6 +6,8 @@ Type: Code Smell ## Summary +https://github.com/agoda-com/AgodaAnalyzers/blob/master/src/Agoda.Analyzers/RuleContent/AG0039MethodLineLengthAnalyzer.html + Methods should be kept reasonably short (recommended max 40 lines excluding whitespace). Long methods should be refactored into smaller, more focused methods while avoiding method chaining. ## Explanation diff --git a/doc/AG0040.md b/doc/AG0040.md index 5c00674..ca89b3b 100644 --- a/doc/AG0040.md +++ b/doc/AG0040.md @@ -6,6 +6,8 @@ Type: Bug Risk / Test Reliability ## Summary +https://playwright.dev/dotnet/docs/api/class-page#page-go-back + Avoid using `WaitUntilState.NetworkIdle` in Playwright tests as it creates flaky tests. Instead, use explicit web assertions to verify page readiness. ## Explanation diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs b/src/Agoda.Analyzers/AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs index d3e30c5..2e6b4be 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs @@ -39,7 +39,7 @@ private static readonly LocalizableString Description DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, - "https://agoda-com.github.io/standards-c-sharp/di/attribute-based-registration.html", + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); protected override IEnumerable Rules => new[] diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0002PrivateMethodsShouldNotBeTested.cs b/src/Agoda.Analyzers/AgodaCustom/AG0002PrivateMethodsShouldNotBeTested.cs index 5d16df3..6fe79c2 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0002PrivateMethodsShouldNotBeTested.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0002PrivateMethodsShouldNotBeTested.cs @@ -33,7 +33,8 @@ private static readonly LocalizableString Description AnalyzerCategory.CustomQualityRules, DiagnosticSeverity.Warning, isEnabledByDefault: true, - description: Description + description: Description, + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md" ); public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0003HttpContextCannotBePassedAsMethodArgument.cs b/src/Agoda.Analyzers/AgodaCustom/AG0003HttpContextCannotBePassedAsMethodArgument.cs index 75e0ca7..574878a 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0003HttpContextCannotBePassedAsMethodArgument.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0003HttpContextCannotBePassedAsMethodArgument.cs @@ -34,8 +34,8 @@ private static readonly LocalizableString Description AnalyzerCategory.CustomQualityRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, - Description, - "https://agoda-com.github.io/standards-c-sharp/services/framework-abstractions.html", + Description, + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0004DoNotUseHardCodedStringsToIdentifyTypes.cs b/src/Agoda.Analyzers/AgodaCustom/AG0004DoNotUseHardCodedStringsToIdentifyTypes.cs index 011af20..9e1b3ea 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0004DoNotUseHardCodedStringsToIdentifyTypes.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0004DoNotUseHardCodedStringsToIdentifyTypes.cs @@ -34,8 +34,8 @@ public class AG0004DoNotUseHardCodedStringsToIdentifyTypes : DiagnosticAnalyzer AnalyzerCategory.CustomQualityRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, - Description, - "https://agoda-com.github.io/standards-c-sharp/reflection/hard-coded-strings.html", + Description, + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0005TestMethodNamesMustFollowConvention.cs b/src/Agoda.Analyzers/AgodaCustom/AG0005TestMethodNamesMustFollowConvention.cs index 3e5f643..620ca25 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0005TestMethodNamesMustFollowConvention.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0005TestMethodNamesMustFollowConvention.cs @@ -35,8 +35,8 @@ public class AG0005TestMethodNamesMustFollowConvention : DiagnosticAnalyzer AnalyzerCategory.CustomQualityRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, - Description, - "https://agoda-com.github.io/standards-c-sharp/testing/test-method-names-should-clearly-indicate-what-they-are-testing.html", + Description, + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); // Test names must be in the format Xxxx_Yyyy or Xxxx_Yyyy_Zzzz diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0006RegisteredComponentShouldHaveExactlyOnePublicConstructor.cs b/src/Agoda.Analyzers/AgodaCustom/AG0006RegisteredComponentShouldHaveExactlyOnePublicConstructor.cs index d487d52..1182c6d 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0006RegisteredComponentShouldHaveExactlyOnePublicConstructor.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0006RegisteredComponentShouldHaveExactlyOnePublicConstructor.cs @@ -37,8 +37,8 @@ public class AG0006RegisteredComponentShouldHaveExactlyOnePublicConstructor : Di AnalyzerCategory.CustomQualityRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, - Description, - null, + Description, + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); public override void Initialize(AnalysisContext context) diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0009IHttpContextAccessorCannotBePassedAsMethodArgument.cs b/src/Agoda.Analyzers/AgodaCustom/AG0009IHttpContextAccessorCannotBePassedAsMethodArgument.cs index 4e613da..588ffa7 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0009IHttpContextAccessorCannotBePassedAsMethodArgument.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0009IHttpContextAccessorCannotBePassedAsMethodArgument.cs @@ -30,7 +30,7 @@ public class AG0009IHttpContextAccessorCannotBePassedAsMethodArgument : Diagnost private static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor(DIAGNOSTIC_ID, Title, MessageFormat, AnalyzerCategory.CustomQualityRules, - DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, null, + DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0010PreventTestFixtureInheritance.cs b/src/Agoda.Analyzers/AgodaCustom/AG0010PreventTestFixtureInheritance.cs index fa55de5..d69dcdc 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0010PreventTestFixtureInheritance.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0010PreventTestFixtureInheritance.cs @@ -36,7 +36,7 @@ public class AG0010PreventTestFixtureInheritance : DiagnosticAnalyzer DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, - "https://agoda-com.github.io/standards-c-sharp/unit-testing/be-wary-of-refactoring-tests.html", + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); public override void Initialize(AnalysisContext context) diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0011NoDirectQueryStringAccess.cs b/src/Agoda.Analyzers/AgodaCustom/AG0011NoDirectQueryStringAccess.cs index 793d60c..a49c6c7 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0011NoDirectQueryStringAccess.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0011NoDirectQueryStringAccess.cs @@ -33,7 +33,7 @@ public class AG0011NoDirectQueryStringAccess : PropertyInvocationAnalyzerBase DiagnosticSeverity.Error, AnalyzerConstants.EnabledByDefault, DescriptionContentLoader.GetAnalyzerDescription(nameof(AG0011NoDirectQueryStringAccess)), - "https://agoda-com.github.io/standards-c-sharp/services/framework-abstractions.html", + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); protected override IEnumerable Rules => new[] diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0012TestMethodMustContainAtLeastOneAssertion.cs b/src/Agoda.Analyzers/AgodaCustom/AG0012TestMethodMustContainAtLeastOneAssertion.cs index 9ae1f00..cea7766 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0012TestMethodMustContainAtLeastOneAssertion.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0012TestMethodMustContainAtLeastOneAssertion.cs @@ -36,7 +36,7 @@ private static readonly LocalizableString Description DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, - "https://agoda-com.github.io/standards-c-sharp/testing/tests-as-a-specification.html", + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Descriptor); diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0013LimitNumberOfTestMethodParametersTo5.cs b/src/Agoda.Analyzers/AgodaCustom/AG0013LimitNumberOfTestMethodParametersTo5.cs index d327b40..c7b15f8 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0013LimitNumberOfTestMethodParametersTo5.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0013LimitNumberOfTestMethodParametersTo5.cs @@ -31,8 +31,8 @@ public AG0013LimitNumberOfTestMethodParametersTo5() AnalyzerCategory.CustomQualityRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, - DescriptionContentLoader.GetAnalyzerDescription(nameof(AG0013LimitNumberOfTestMethodParametersTo5)), - "https://agoda-com.github.io/standards-c-sharp/unit-testing/use-test-cases-appropriately.html", + DescriptionContentLoader.GetAnalyzerDescription(nameof(AG0013LimitNumberOfTestMethodParametersTo5)), + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue ); } diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0018PermitOnlyCertainPubliclyExposedEnumerables.cs b/src/Agoda.Analyzers/AgodaCustom/AG0018PermitOnlyCertainPubliclyExposedEnumerables.cs index 3256c63..52d05a1 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0018PermitOnlyCertainPubliclyExposedEnumerables.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0018PermitOnlyCertainPubliclyExposedEnumerables.cs @@ -48,8 +48,8 @@ private static readonly LocalizableString Description AnalyzerCategory.CustomQualityRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, - Description, - "https://agoda-com.github.io/standards-c-sharp/collections/choosing-collection-implementation.html", + Description, + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Descriptor); diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0019PreventUseOfInternalsVisibleToAttribute.cs b/src/Agoda.Analyzers/AgodaCustom/AG0019PreventUseOfInternalsVisibleToAttribute.cs index 50fd877..12d7da1 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0019PreventUseOfInternalsVisibleToAttribute.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0019PreventUseOfInternalsVisibleToAttribute.cs @@ -29,7 +29,7 @@ public AG0019PreventUseOfInternalsVisibleToAttribute() DiagnosticSeverity.Error, AnalyzerConstants.EnabledByDefault, DescriptionContentLoader.GetAnalyzerDescription(nameof(AG0019PreventUseOfInternalsVisibleToAttribute)), - "https://agoda-com.github.io/standards-c-sharp/unit-testing/only-test-the-public-interface.html", + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); } diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0020AvoidReturningNullEnumerables.cs b/src/Agoda.Analyzers/AgodaCustom/AG0020AvoidReturningNullEnumerables.cs index 2319f58..19b2e24 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0020AvoidReturningNullEnumerables.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0020AvoidReturningNullEnumerables.cs @@ -34,8 +34,8 @@ private static readonly LocalizableString Description AnalyzerCategory.CustomQualityRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, - Description, - "https://agoda-com.github.io/standards-c-sharp/collections/null-empty-enumerables.html", + Description, + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); public override void Initialize(AnalysisContext context) diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0021PreferAsyncMethods.cs b/src/Agoda.Analyzers/AgodaCustom/AG0021PreferAsyncMethods.cs index ab9f761..1e4f9fc 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0021PreferAsyncMethods.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0021PreferAsyncMethods.cs @@ -35,8 +35,8 @@ public class AG0021PreferAsyncMethods : DiagnosticAnalyzer AnalyzerCategory.CustomQualityRules, DiagnosticSeverity.Info, AnalyzerConstants.EnabledByDefault, - Description, - "https://agoda-com.github.io/standards-c-sharp/async/consume-async-method.html", + Description, + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); public override void Initialize(AnalysisContext context) diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0022DoNotExposeBothSyncAndAsyncVersionsOfMethods.cs b/src/Agoda.Analyzers/AgodaCustom/AG0022DoNotExposeBothSyncAndAsyncVersionsOfMethods.cs index a9a4cb2..3cfad9d 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0022DoNotExposeBothSyncAndAsyncVersionsOfMethods.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0022DoNotExposeBothSyncAndAsyncVersionsOfMethods.cs @@ -39,8 +39,8 @@ public class AG0022DoNotExposeBothSyncAndAsyncVersionsOfMethods : DiagnosticAnal AnalyzerCategory.CustomQualityRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, - Description, - "https://agoda-com.github.io/standards-c-sharp/async/expose-async-method.html", + Description, + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0023PreventUseOfThreadSleep.cs b/src/Agoda.Analyzers/AgodaCustom/AG0023PreventUseOfThreadSleep.cs index d545b43..295e8b6 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0023PreventUseOfThreadSleep.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0023PreventUseOfThreadSleep.cs @@ -35,7 +35,7 @@ public class AG0023PreventUseOfThreadSleep : MethodInvocationAnalyzerBase DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, nameof(AG0023PreventUseOfThreadSleep), - "https://agoda-com.github.io/standards-c-sharp/async/avoid-blocking.html", + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0024PreventUseOfTaskFactoryStartNew.cs b/src/Agoda.Analyzers/AgodaCustom/AG0024PreventUseOfTaskFactoryStartNew.cs index b1c7c6b..5943fb9 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0024PreventUseOfTaskFactoryStartNew.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0024PreventUseOfTaskFactoryStartNew.cs @@ -32,7 +32,7 @@ public class AG0024PreventUseOfTaskFactoryStartNew : DiagnosticAnalyzer DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, - "https://agoda-com.github.io/standards-c-sharp/async/task-run.html", + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0025PreventUseOfTaskContinue.cs b/src/Agoda.Analyzers/AgodaCustom/AG0025PreventUseOfTaskContinue.cs index 5e631a7..4f4e451 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0025PreventUseOfTaskContinue.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0025PreventUseOfTaskContinue.cs @@ -36,8 +36,8 @@ public class AG0025PreventUseOfTaskContinue : MethodInvocationAnalyzerBase AnalyzerCategory.CustomQualityRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, - Description, - "https://agoda-com.github.io/standards-c-sharp/async/never-task-continue-with.html", + Description, + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); protected override IEnumerable Rules => new[] diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0026EnsureOnlyCssSelectorIsUsedToFindElements.cs b/src/Agoda.Analyzers/AgodaCustom/AG0026EnsureOnlyCssSelectorIsUsedToFindElements.cs index 3483ed3..ae370de 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0026EnsureOnlyCssSelectorIsUsedToFindElements.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0026EnsureOnlyCssSelectorIsUsedToFindElements.cs @@ -25,7 +25,7 @@ public class AG0026EnsureOnlyCssSelectorIsUsedToFindElements : MethodInvocationA DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, DescriptionContentLoader.GetAnalyzerDescription(nameof(AG0026EnsureOnlyCssSelectorIsUsedToFindElements)), - "https://agoda-com.github.io/standards-c-sharp/gui-testing/css-selectors.html", + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); protected override IEnumerable Rules => TestMethodHelpers.PermittedSeleniumAccessors; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0027EnsureOnlyDataSeleniumIsUsedToFindElements.cs b/src/Agoda.Analyzers/AgodaCustom/AG0027EnsureOnlyDataSeleniumIsUsedToFindElements.cs index fa6855a..662f9db 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0027EnsureOnlyDataSeleniumIsUsedToFindElements.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0027EnsureOnlyDataSeleniumIsUsedToFindElements.cs @@ -33,7 +33,7 @@ public class AG0027EnsureOnlyDataSeleniumIsUsedToFindElements : DiagnosticAnalyz DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, DescriptionContentLoader.GetAnalyzerDescription(nameof(AG0027EnsureOnlyDataSeleniumIsUsedToFindElements)), - "https://agoda-com.github.io/standards-c-sharp/gui-testing/data-selenium.html", + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0030PreventUseOfDynamics.cs b/src/Agoda.Analyzers/AgodaCustom/AG0030PreventUseOfDynamics.cs index 693e71d..43511b4 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0030PreventUseOfDynamics.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0030PreventUseOfDynamics.cs @@ -35,7 +35,7 @@ public class AG0030PreventUseOfDynamics : DiagnosticAnalyzer DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, - "https://agoda-com.github.io/standards-c-sharp/code-style/dynamics.html", + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); public override void Initialize(AnalysisContext context) diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0032PreventUseOfBlockingTaskMethods.cs b/src/Agoda.Analyzers/AgodaCustom/AG0032PreventUseOfBlockingTaskMethods.cs index 440b936..5ca2289 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0032PreventUseOfBlockingTaskMethods.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0032PreventUseOfBlockingTaskMethods.cs @@ -36,7 +36,7 @@ public class AG0032PreventUseOfBlockingTaskMethods : PropertyInvocationAnalyzerB DiagnosticSeverity.Error, AnalyzerConstants.EnabledByDefault, DescriptionContentLoader.GetAnalyzerDescription(nameof(AG0032PreventUseOfBlockingTaskMethods)), - "https://agoda-com.github.io/standards-c-sharp/async/never-task-wait.html", + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); protected override IEnumerable Rules => new[] diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0033PreventUseOfTaskResult.cs b/src/Agoda.Analyzers/AgodaCustom/AG0033PreventUseOfTaskResult.cs index 97a7330..a57d11e 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0033PreventUseOfTaskResult.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0033PreventUseOfTaskResult.cs @@ -36,7 +36,7 @@ public class AG0033PreventUseOfTaskResult : PropertyInvocationAnalyzerBase DiagnosticSeverity.Error, AnalyzerConstants.EnabledByDefault, DescriptionContentLoader.GetAnalyzerDescription(nameof(AG0033PreventUseOfTaskResult)), - "https://agoda-com.github.io/standards-c-sharp/async/await-task-result.html", + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); protected override IEnumerable Rules => new[] diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0035PreventUseOfMachineName.cs b/src/Agoda.Analyzers/AgodaCustom/AG0035PreventUseOfMachineName.cs index 6c38230..de86f45 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0035PreventUseOfMachineName.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0035PreventUseOfMachineName.cs @@ -31,7 +31,7 @@ public class AG0035PreventUseOfMachineName : PropertyInvocationAnalyzerBase DiagnosticSeverity.Error, AnalyzerConstants.EnabledByDefault, DescriptionContentLoader.GetAnalyzerDescription(nameof(AG0035PreventUseOfMachineName)), - "https://agoda-com.github.io/standards-c-sharp/configuration/machine-name.html", + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); protected override IEnumerable Rules => new[] diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0037EnsureSeleniumTestHasOwnedByAttribute.cs b/src/Agoda.Analyzers/AgodaCustom/AG0037EnsureSeleniumTestHasOwnedByAttribute.cs index 2856fa5..f874e32 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0037EnsureSeleniumTestHasOwnedByAttribute.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0037EnsureSeleniumTestHasOwnedByAttribute.cs @@ -42,7 +42,7 @@ public class AG0037EnsureSeleniumTestHasOwnedByAttribute : DiagnosticAnalyzer DiagnosticSeverity.Error, AnalyzerConstants.EnabledByDefault, DescriptionContentLoader.GetAnalyzerDescription(nameof(AG0037EnsureSeleniumTestHasOwnedByAttribute)), - null, + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0038PreventUseOfRegionPreprocessorDirective.cs b/src/Agoda.Analyzers/AgodaCustom/AG0038PreventUseOfRegionPreprocessorDirective.cs index 3e15b85..9246c08 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0038PreventUseOfRegionPreprocessorDirective.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0038PreventUseOfRegionPreprocessorDirective.cs @@ -34,8 +34,8 @@ private static readonly LocalizableString Description AnalyzerCategory.CustomQualityRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, - Description, - "https://agoda-com.github.io/standards-c-sharp/code-style/regions.html", + Description, + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0039MethodLineLengthAnalyzer.cs b/src/Agoda.Analyzers/AgodaCustom/AG0039MethodLineLengthAnalyzer.cs index e9f5df6..b9866c5 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0039MethodLineLengthAnalyzer.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0039MethodLineLengthAnalyzer.cs @@ -39,7 +39,7 @@ private static readonly LocalizableString Description Category, DiagnosticSeverity.Hidden, // THis rule should be opt in and not on by default isEnabledByDefault: true, - helpLinkUri: "https://github.com/agoda-com/AgodaAnalyzers/blob/master/src/Agoda.Analyzers/RuleContent/AG0039MethodLineLengthAnalyzer.html"); + helpLinkUri: $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md"); public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Descriptor); } } diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0040WaitUntilStateNetworkIdleMustNotBeUsed.cs b/src/Agoda.Analyzers/AgodaCustom/AG0040WaitUntilStateNetworkIdleMustNotBeUsed.cs index 46edcdc..26f5824 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0040WaitUntilStateNetworkIdleMustNotBeUsed.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0040WaitUntilStateNetworkIdleMustNotBeUsed.cs @@ -34,7 +34,7 @@ private static readonly LocalizableString Description DiagnosticSeverity.Error, AnalyzerConstants.EnabledByDefault, Description, - "https://playwright.dev/dotnet/docs/api/class-page#page-go-back", + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); protected override IEnumerable Rules => new[] diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0041CodeFixProvider.cs b/src/Agoda.Analyzers/AgodaCustom/AG0041CodeFixProvider.cs index 7df9a3c..588be5d 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0041CodeFixProvider.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0041CodeFixProvider.cs @@ -17,7 +17,7 @@ public class AG0041CodeFixProvider : CodeFixProvider { private const string Title = "Use message template"; - public sealed override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(AG0041LogTemplateAnalyzer.DiagnosticId); + public sealed override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(AG0041LogTemplateAnalyzer.DIAGNOSTIC_ID); public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0041LogTemplateAnalyzer.cs b/src/Agoda.Analyzers/AgodaCustom/AG0041LogTemplateAnalyzer.cs index 4f665f5..1d7b296 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0041LogTemplateAnalyzer.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0041LogTemplateAnalyzer.cs @@ -11,19 +11,19 @@ namespace Agoda.Analyzers.AgodaCustom [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AG0041LogTemplateAnalyzer : DiagnosticAnalyzer { - public const string DiagnosticId = "AG0041"; + public const string DIAGNOSTIC_ID = "AG0041"; private static readonly LocalizableString Title = new LocalizableResourceString(nameof(CustomRulesResources.AG0041Title), CustomRulesResources.ResourceManager, typeof(CustomRulesResources)); private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(CustomRulesResources.AG0041Title), CustomRulesResources.ResourceManager, typeof(CustomRulesResources)); private const string Category = "Best Practices"; - internal static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, + internal static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DIAGNOSTIC_ID, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, - helpLinkUri: "https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/AG0041.md"); + helpLinkUri: $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md"); public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0042ElementHandlerShouldNotBeUsed.cs b/src/Agoda.Analyzers/AgodaCustom/AG0042ElementHandlerShouldNotBeUsed.cs index d6ac217..1b0053a 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0042ElementHandlerShouldNotBeUsed.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0042ElementHandlerShouldNotBeUsed.cs @@ -32,7 +32,7 @@ private static readonly LocalizableString Description DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, - "https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/AG0042.md", + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0043NoBuildServiceProvider.cs b/src/Agoda.Analyzers/AgodaCustom/AG0043NoBuildServiceProvider.cs index 152a414..e68c25b 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0043NoBuildServiceProvider.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0043NoBuildServiceProvider.cs @@ -35,7 +35,7 @@ private static readonly LocalizableString Description DiagnosticSeverity.Error, AnalyzerConstants.EnabledByDefault, Description, - "https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/AG0043.md", + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0044ForceOptionShouldNotBeUsed.cs b/src/Agoda.Analyzers/AgodaCustom/AG0044ForceOptionShouldNotBeUsed.cs index a1dc3f8..82db1ce 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0044ForceOptionShouldNotBeUsed.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0044ForceOptionShouldNotBeUsed.cs @@ -36,7 +36,7 @@ public class AG0044ForceOptionShouldNotBeUsed : DiagnosticAnalyzer DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description, - "https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/AG0044.md", + $"https://github.com/agoda-com/AgodaAnalyzers/blob/master/doc/{DIAGNOSTIC_ID}.md", WellKnownDiagnosticTags.EditAndContinue); public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); From 737d5b03c02295b764e3bf55e456beaf372b94ac Mon Sep 17 00:00:00 2001 From: jdickson Date: Sat, 14 Dec 2024 12:27:27 +0700 Subject: [PATCH 6/7] updates from review comments --- doc/AG0001.md | 4 ++-- doc/AG0002.md | 4 ++-- doc/AG0003.md | 4 ++-- doc/AG0004.md | 4 ++-- doc/AG0005.md | 6 +++--- doc/AG0006.md | 4 +--- doc/AG0009.md | 4 ++-- doc/AG0010.md | 6 +++--- doc/AG0011.md | 4 ++-- doc/AG0012.md | 4 ++-- doc/AG0013.md | 7 +++---- doc/AG0018.md | 2 +- doc/AG0019.md | 3 ++- 13 files changed, 27 insertions(+), 29 deletions(-) diff --git a/doc/AG0001.md b/doc/AG0001.md index 6f2ad01..d9749ed 100644 --- a/doc/AG0001.md +++ b/doc/AG0001.md @@ -12,13 +12,13 @@ Direct usage of `DependencyResolver` creates tight coupling and makes code harde - Improved maintainability by making dependencies visible - Stronger adherence to SOLID principles, particularly Dependency Inversion -#### Don't +#### Don't ❌ ```c# var exampleService = DependencyResolver.Current.GetService(); ``` -#### Do +#### Do ✅ ```c# public class ExampleClass diff --git a/doc/AG0002.md b/doc/AG0002.md index 0fec37c..bdb6459 100644 --- a/doc/AG0002.md +++ b/doc/AG0002.md @@ -6,7 +6,7 @@ Type: Code Smell When implementing an interface, classes should only expose methods that are part of their public contract. Additional public or internal methods that aren't part of the implemented interfaces create confusion about the class's responsibilities and violate the Interface Segregation Principle. -#### Don't +#### Don't ❌ ```csharp interface ISomething @@ -25,7 +25,7 @@ class TestClass : ISomething } ``` -#### Do +#### Do ✅ ```csharp interface ISomething diff --git a/doc/AG0003.md b/doc/AG0003.md index 7ab9c86..2b43e7e 100644 --- a/doc/AG0003.md +++ b/doc/AG0003.md @@ -8,7 +8,7 @@ https://agoda-com.github.io/standards-c-sharp/services/framework-abstractions.ht Passing `System.Web.HttpContext` as a parameter creates tight coupling to the web infrastructure and makes unit testing significantly more difficult. Instead, pass only the specific HttpContext properties that your code actually needs. -#### Don't +#### Don't ❌ ```csharp using System.Web; @@ -32,7 +32,7 @@ class TestClass: ISomething } ``` -#### Do +#### Do ✅ ```csharp using System.Web; diff --git a/doc/AG0004.md b/doc/AG0004.md index ba97c97..531087c 100644 --- a/doc/AG0004.md +++ b/doc/AG0004.md @@ -8,7 +8,7 @@ https://agoda-com.github.io/standards-c-sharp/reflection/hard-coded-strings.html Hard-coded strings to identify namespaces and types make refactoring risky and move type resolution errors from compile-time to runtime. Always use the `typeof` operator to reference types, which provides compile-time safety. -#### Don't +#### Don't ❌ ```csharp // Both fail at runtime after namespace changes @@ -16,7 +16,7 @@ var instance = Activator.CreateInstance("Agoda", "Agoda.MyType"); var type = Type.GetType("Agoda.MyType"); ``` -#### Do +#### Do ✅ ```csharp // Caught at compile time after namespace changes diff --git a/doc/AG0005.md b/doc/AG0005.md index 2e0c187..99734eb 100644 --- a/doc/AG0005.md +++ b/doc/AG0005.md @@ -13,7 +13,7 @@ Test names should follow this pattern: - Class: `Tests` - Methods: `__` or `_` -#### Don't +#### Don't ❌ ```csharp [Test] @@ -21,7 +21,7 @@ public void HazardLightsTest() // Noncompliant - unclear what aspect is being te {...} ``` -#### Do +#### Do ✅ ```csharp [Test] @@ -43,4 +43,4 @@ If naming a test is difficult, it might indicate the test is doing too much and - Easy identification of failures - Documentation of behavior - Coverage visibility -- Maintainable test suite \ No newline at end of file +- Maintainable test suite diff --git a/doc/AG0006.md b/doc/AG0006.md index 8fe5a55..512a0e6 100644 --- a/doc/AG0006.md +++ b/doc/AG0006.md @@ -6,11 +6,9 @@ Type: Code Smell Each component registered with the dependency injection container should have exactly one public constructor. Having multiple public constructors leads to ambiguity in dependency resolution and makes the codebase harder to maintain. -This is a blocker rule - any component with multiple public constructors should be refactored to have only one. - Reasons to avoid multiple public constructors: -- Creates ambiguity for the DI container when resolving dependencies +- Creates ambiguity for the DI container when resolving dependencies - Makes it harder to track and manage dependencies - Increases complexity of dependency resolution - Makes code harder to test and maintain diff --git a/doc/AG0009.md b/doc/AG0009.md index 727eee1..1413b3e 100644 --- a/doc/AG0009.md +++ b/doc/AG0009.md @@ -6,7 +6,7 @@ Type: Code Smell Passing `IHttpContextAccessor` or `HttpContextAccessor` as a parameter creates tight coupling to ASP.NET Core infrastructure and makes testing difficult. Instead, pass only the specific HTTP context properties that your code actually needs. -#### Don't +#### Don't ❌ ```csharp using Microsoft.AspNetCore.Http; @@ -31,7 +31,7 @@ class TestClass : ISomething } ``` -#### Do +#### Do ✅ ```csharp interface ISomething diff --git a/doc/AG0010.md b/doc/AG0010.md index 462b95b..a1dc0de 100644 --- a/doc/AG0010.md +++ b/doc/AG0010.md @@ -8,7 +8,7 @@ https://agoda-com.github.io/standards-c-sharp/unit-testing/be-wary-of-refactorin Test fixture inheritance should be avoided as it creates hidden dependencies, makes tests harder to understand, and violates the test isolation principle. Each test class should be independent and self-contained. -#### Don't +#### Don't ❌ ```csharp [TestFixture] @@ -18,7 +18,7 @@ public class TestClass : BaseTest // Noncompliant - inheriting from base test c } ``` -#### Do +#### Do ✅ ```csharp [TestFixture] @@ -46,4 +46,4 @@ Instead of inheritance, prefer: - Shared test data factories - Clear, explicit test setup within each test class -This keeps tests more maintainable and easier to understand at a glance. \ No newline at end of file +This keeps tests more maintainable and easier to understand at a glance. diff --git a/doc/AG0011.md b/doc/AG0011.md index 1414a02..a2d1017 100644 --- a/doc/AG0011.md +++ b/doc/AG0011.md @@ -8,7 +8,7 @@ https://agoda-com.github.io/standards-c-sharp/services/framework-abstractions.ht Direct access to `QueryString` collection bypasses ASP.NET model binding, which provides validation, type conversion, and security features. Always use model binding with action method parameters instead. -#### Don't +#### Don't ❌ ```csharp public ActionResult Index() @@ -18,7 +18,7 @@ public ActionResult Index() } ``` -#### Do +#### Do ✅ ```csharp public ActionResult Index([FromQuery] int id) diff --git a/doc/AG0012.md b/doc/AG0012.md index 387e9c2..2941001 100644 --- a/doc/AG0012.md +++ b/doc/AG0012.md @@ -8,7 +8,7 @@ https://agoda-com.github.io/standards-c-sharp/testing/tests-as-a-specification.h Each test method should contain at least one assertion to verify expected behavior. Tests without assertions don't validate anything and provide a false sense of security. -#### Don't +#### Don't ❌ ```csharp using NUnit.Framework; @@ -23,7 +23,7 @@ public class TestClass } ``` -#### Do +#### Do ✅ With NUnit: diff --git a/doc/AG0013.md b/doc/AG0013.md index 604775c..8b8c469 100644 --- a/doc/AG0013.md +++ b/doc/AG0013.md @@ -8,7 +8,7 @@ https://agoda-com.github.io/standards-c-sharp/unit-testing/use-test-cases-approp Test methods with too many input parameters become difficult to understand and maintain. When test cases need many parameters, split them into smaller, more focused tests with clear intentions. -#### Don't +#### Don't ❌ ```csharp [Test] @@ -19,7 +19,7 @@ public void This_Is_NotValid(int a, int b, int c, int d, int e, int f) } ``` -#### Do +#### Do ✅ ```csharp [Test] @@ -50,9 +50,8 @@ Having too many test parameters creates problems: Instead: - Split into multiple focused tests -- Use test helper methods - Create test data builders - Use meaningful parameter names - Group related parameters into objects -This keeps tests clear, maintainable, and easier to understand. \ No newline at end of file +This keeps tests clear, maintainable, and easier to understand. diff --git a/doc/AG0018.md b/doc/AG0018.md index 0f84a8e..04736f9 100644 --- a/doc/AG0018.md +++ b/doc/AG0018.md @@ -17,7 +17,7 @@ When exposing collections through public APIs, you should return interface types ### Allowed Return Types - `IEnumerable` -- `ISet` +- `ISet` - `IList` - `IDictionary` - `IReadOnlyDictionary` diff --git a/doc/AG0019.md b/doc/AG0019.md index 3b81d2b..4b3b103 100644 --- a/doc/AG0019.md +++ b/doc/AG0019.md @@ -59,5 +59,6 @@ Using `InternalsVisibleTo` leads to several problems: - Makes refactoring more difficult as tests depend on internal structure - Violates encapsulation principles - Can hide poor API design by allowing tests to bypass public interfaces +- Avoids you doing whitebox testing, and focuses you more towards blackbox testing which leads to easier refactoring of code in the long run -Focus on testing behavior through public interfaces rather than implementation details through internal members. \ No newline at end of file +Focus on testing behavior through public interfaces rather than implementation details through internal members. From c476056617fbbd82a9a462ff84966d4e525ac5ea Mon Sep 17 00:00:00 2001 From: jdickson Date: Sat, 14 Dec 2024 12:28:52 +0700 Subject: [PATCH 7/7] using clean up --- src/Agoda.Analyzers.Test/AgodaCustom/AG0001UnitTests.cs | 2 -- src/Agoda.Analyzers.Test/AgodaCustom/AG0004UnitTests.cs | 1 - src/Agoda.Analyzers.Test/AgodaCustom/AG0005UnitTests.cs | 7 +------ src/Agoda.Analyzers.Test/AgodaCustom/AG0006UnitTests.cs | 8 +------- src/Agoda.Analyzers.Test/AgodaCustom/AG0010UnitTests.cs | 8 +------- src/Agoda.Analyzers.Test/AgodaCustom/AG0011UnitTests.cs | 7 +------ src/Agoda.Analyzers.Test/AgodaCustom/AG0012UnitTests.cs | 1 - src/Agoda.Analyzers.Test/AgodaCustom/AG0018UnitTests.cs | 6 ------ src/Agoda.Analyzers.Test/AgodaCustom/AG0020UnitTests.cs | 6 +----- src/Agoda.Analyzers.Test/AgodaCustom/AG0021UnitTests.cs | 7 +------ src/Agoda.Analyzers.Test/AgodaCustom/AG0022UnitTests.cs | 7 +------ src/Agoda.Analyzers.Test/AgodaCustom/AG0026UnitTests.cs | 6 ------ src/Agoda.Analyzers.Test/AgodaCustom/AG0027UnitTests.cs | 6 ------ src/Agoda.Analyzers.Test/AgodaCustom/AG0032UnitTests.cs | 8 +------- src/Agoda.Analyzers.Test/AgodaCustom/AG0033UnitTests.cs | 8 +------- src/Agoda.Analyzers.Test/AgodaCustom/AG0035UnitTests.cs | 1 - src/Agoda.Analyzers.Test/AgodaCustom/AG0038UnitTests.cs | 3 +-- src/Agoda.Analyzers.Test/AgodaCustom/AG0040UnitTests.cs | 1 - .../CodeFixes/AgodaCustom/AG0019FixProviderUnitTests.cs | 4 ---- .../CodeFixes/AgodaCustom/AG0020FixProviderUnitTests.cs | 8 +------- .../CodeFixes/AgodaCustom/AG0022FixProviderUnitTests.cs | 8 +------- src/Agoda.Analyzers.Test/StyleCop/SA1106UnitTests.cs | 4 +--- src/Agoda.Analyzers.Test/StyleCop/SA1107UnitTests.cs | 5 +---- src/Agoda.Analyzers.Test/StyleCop/SA1123UnitTests.cs | 3 +-- .../AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs | 5 ----- .../AG0003HttpContextCannotBePassedAsMethodArgument.cs | 1 - .../AG0004DoNotUseHardCodedStringsToIdentifyTypes.cs | 3 +-- ...9IHttpContextAccessorCannotBePassedAsMethodArgument.cs | 3 +-- .../AgodaCustom/AG0010PreventTestFixtureInheritance.cs | 1 - .../AgodaCustom/AG0011NoDirectQueryStringAccess.cs | 5 ----- .../AG0012TestMethodMustContainAtLeastOneAssertion.cs | 1 - .../AG0013LimitNumberOfTestMethodParametersTo5.cs | 4 +--- .../AG0020AvoidReturningNullEnumerablesFixProvider.cs | 3 +-- .../AG0022DoNotExposeBothSyncAndAsyncVersionsOfMethods.cs | 4 +--- .../AgodaCustom/AG0022RemoveSyncMethodFixProvider.cs | 3 +-- .../AgodaCustom/AG0023PreventUseOfThreadSleep.cs | 8 +------- .../AgodaCustom/AG0024PreventUseOfTaskFactoryStartNew.cs | 2 -- .../AgodaCustom/AG0025PreventUseOfTaskContinue.cs | 5 ----- .../AG0026EnsureOnlyCssSelectorIsUsedToFindElements.cs | 2 -- .../AG0027EnsureOnlyDataSeleniumIsUsedToFindElements.cs | 5 +---- .../AgodaCustom/AG0032PreventUseOfBlockingTaskMethods.cs | 4 ---- .../AgodaCustom/AG0033PreventUseOfTaskResult.cs | 5 ----- .../AG0038PreventUseOfRegionPreprocessorDirective.cs | 2 -- .../AgodaCustom/AG0039MethodLineLengthAnalyzer.cs | 2 -- src/Agoda.Analyzers/AgodaCustom/AG0041CodeFixProvider.cs | 3 +-- .../AgodaCustom/MethodInvocationAnalyzerBase.cs | 4 +--- src/Agoda.Analyzers/Helpers/InvocationRule.cs | 1 - src/Agoda.Analyzers/Helpers/TestMethodHelpers.cs | 1 - .../StyleCop/RemoveRegionFixAllProvider.cs | 1 - src/Agoda.Analyzers/StyleCop/SA1107FixAllProvider.cs | 1 - 50 files changed, 25 insertions(+), 179 deletions(-) diff --git a/src/Agoda.Analyzers.Test/AgodaCustom/AG0001UnitTests.cs b/src/Agoda.Analyzers.Test/AgodaCustom/AG0001UnitTests.cs index a242e8b..8752f10 100644 --- a/src/Agoda.Analyzers.Test/AgodaCustom/AG0001UnitTests.cs +++ b/src/Agoda.Analyzers.Test/AgodaCustom/AG0001UnitTests.cs @@ -2,9 +2,7 @@ using System.Web.Mvc; using Agoda.Analyzers.AgodaCustom; using Agoda.Analyzers.Test.Helpers; -using Microsoft.CodeAnalysis.CSharp.Testing; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Testing.Verifiers; using NUnit.Framework; namespace Agoda.Analyzers.Test.AgodaCustom; diff --git a/src/Agoda.Analyzers.Test/AgodaCustom/AG0004UnitTests.cs b/src/Agoda.Analyzers.Test/AgodaCustom/AG0004UnitTests.cs index 8f090fe..967f157 100644 --- a/src/Agoda.Analyzers.Test/AgodaCustom/AG0004UnitTests.cs +++ b/src/Agoda.Analyzers.Test/AgodaCustom/AG0004UnitTests.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; -using System.Web; using Agoda.Analyzers.AgodaCustom; using Agoda.Analyzers.Test.Helpers; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Agoda.Analyzers.Test/AgodaCustom/AG0005UnitTests.cs b/src/Agoda.Analyzers.Test/AgodaCustom/AG0005UnitTests.cs index 4b9e148..1655ae5 100644 --- a/src/Agoda.Analyzers.Test/AgodaCustom/AG0005UnitTests.cs +++ b/src/Agoda.Analyzers.Test/AgodaCustom/AG0005UnitTests.cs @@ -1,11 +1,6 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; +using System.Threading.Tasks; using Agoda.Analyzers.AgodaCustom; using Agoda.Analyzers.Test.Helpers; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Framework; diff --git a/src/Agoda.Analyzers.Test/AgodaCustom/AG0006UnitTests.cs b/src/Agoda.Analyzers.Test/AgodaCustom/AG0006UnitTests.cs index 6656324..eacf1ce 100644 --- a/src/Agoda.Analyzers.Test/AgodaCustom/AG0006UnitTests.cs +++ b/src/Agoda.Analyzers.Test/AgodaCustom/AG0006UnitTests.cs @@ -1,12 +1,6 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; +using System.Threading.Tasks; using Agoda.Analyzers.AgodaCustom; using Agoda.Analyzers.Test.Helpers; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Framework; diff --git a/src/Agoda.Analyzers.Test/AgodaCustom/AG0010UnitTests.cs b/src/Agoda.Analyzers.Test/AgodaCustom/AG0010UnitTests.cs index cb3acd9..09ffa2c 100644 --- a/src/Agoda.Analyzers.Test/AgodaCustom/AG0010UnitTests.cs +++ b/src/Agoda.Analyzers.Test/AgodaCustom/AG0010UnitTests.cs @@ -1,12 +1,6 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; +using System.Threading.Tasks; using Agoda.Analyzers.AgodaCustom; using Agoda.Analyzers.Test.Helpers; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Framework; diff --git a/src/Agoda.Analyzers.Test/AgodaCustom/AG0011UnitTests.cs b/src/Agoda.Analyzers.Test/AgodaCustom/AG0011UnitTests.cs index 0e52c6b..2ae5f06 100644 --- a/src/Agoda.Analyzers.Test/AgodaCustom/AG0011UnitTests.cs +++ b/src/Agoda.Analyzers.Test/AgodaCustom/AG0011UnitTests.cs @@ -1,12 +1,7 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; +using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Agoda.Analyzers.AgodaCustom; using Agoda.Analyzers.Test.Helpers; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Framework; diff --git a/src/Agoda.Analyzers.Test/AgodaCustom/AG0012UnitTests.cs b/src/Agoda.Analyzers.Test/AgodaCustom/AG0012UnitTests.cs index a658d5b..3acde2a 100644 --- a/src/Agoda.Analyzers.Test/AgodaCustom/AG0012UnitTests.cs +++ b/src/Agoda.Analyzers.Test/AgodaCustom/AG0012UnitTests.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; -using FluentAssertions; namespace Agoda.Analyzers.Test.AgodaCustom; diff --git a/src/Agoda.Analyzers.Test/AgodaCustom/AG0018UnitTests.cs b/src/Agoda.Analyzers.Test/AgodaCustom/AG0018UnitTests.cs index bf68cc1..4d722be 100644 --- a/src/Agoda.Analyzers.Test/AgodaCustom/AG0018UnitTests.cs +++ b/src/Agoda.Analyzers.Test/AgodaCustom/AG0018UnitTests.cs @@ -3,14 +3,8 @@ namespace Agoda.Analyzers.Test.AgodaCustom; using Agoda.Analyzers.AgodaCustom; using Agoda.Analyzers.Test.Helpers; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; using System.Threading.Tasks; [TestFixture] diff --git a/src/Agoda.Analyzers.Test/AgodaCustom/AG0020UnitTests.cs b/src/Agoda.Analyzers.Test/AgodaCustom/AG0020UnitTests.cs index 5ecceae..236e8d3 100644 --- a/src/Agoda.Analyzers.Test/AgodaCustom/AG0020UnitTests.cs +++ b/src/Agoda.Analyzers.Test/AgodaCustom/AG0020UnitTests.cs @@ -1,13 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Agoda.Analyzers.AgodaCustom; using Agoda.Analyzers.Test.Helpers; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Framework; diff --git a/src/Agoda.Analyzers.Test/AgodaCustom/AG0021UnitTests.cs b/src/Agoda.Analyzers.Test/AgodaCustom/AG0021UnitTests.cs index 98b838f..3755454 100644 --- a/src/Agoda.Analyzers.Test/AgodaCustom/AG0021UnitTests.cs +++ b/src/Agoda.Analyzers.Test/AgodaCustom/AG0021UnitTests.cs @@ -1,11 +1,6 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; +using System.Threading.Tasks; using Agoda.Analyzers.AgodaCustom; using Agoda.Analyzers.Test.Helpers; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Framework; diff --git a/src/Agoda.Analyzers.Test/AgodaCustom/AG0022UnitTests.cs b/src/Agoda.Analyzers.Test/AgodaCustom/AG0022UnitTests.cs index bb608ba..307ab33 100644 --- a/src/Agoda.Analyzers.Test/AgodaCustom/AG0022UnitTests.cs +++ b/src/Agoda.Analyzers.Test/AgodaCustom/AG0022UnitTests.cs @@ -1,11 +1,6 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; +using System.Threading.Tasks; using Agoda.Analyzers.AgodaCustom; using Agoda.Analyzers.Test.Helpers; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Framework; diff --git a/src/Agoda.Analyzers.Test/AgodaCustom/AG0026UnitTests.cs b/src/Agoda.Analyzers.Test/AgodaCustom/AG0026UnitTests.cs index c4df244..21794e7 100644 --- a/src/Agoda.Analyzers.Test/AgodaCustom/AG0026UnitTests.cs +++ b/src/Agoda.Analyzers.Test/AgodaCustom/AG0026UnitTests.cs @@ -1,15 +1,9 @@ -using System.Collections.Generic; using NUnit.Framework; using System.Threading.Tasks; using Agoda.Analyzers.Test.Helpers; using Microsoft.CodeAnalysis.Diagnostics; -using System.Collections.Immutable; -using System.Threading; -using System.Linq; -using Microsoft.CodeAnalysis; using OpenQA.Selenium; using Agoda.Analyzers.AgodaCustom; -using System.Collections.ObjectModel; namespace Agoda.Analyzers.Test.AgodaCustom; diff --git a/src/Agoda.Analyzers.Test/AgodaCustom/AG0027UnitTests.cs b/src/Agoda.Analyzers.Test/AgodaCustom/AG0027UnitTests.cs index bf961f7..005f602 100644 --- a/src/Agoda.Analyzers.Test/AgodaCustom/AG0027UnitTests.cs +++ b/src/Agoda.Analyzers.Test/AgodaCustom/AG0027UnitTests.cs @@ -1,13 +1,7 @@ -using System.Collections.Generic; using NUnit.Framework; using System.Threading.Tasks; using Agoda.Analyzers.Test.Helpers; using Microsoft.CodeAnalysis.Diagnostics; -using System.Collections.Immutable; -using System.Collections.ObjectModel; -using System.Threading; -using System.Linq; -using Microsoft.CodeAnalysis; using Agoda.Analyzers.AgodaCustom; using OpenQA.Selenium; diff --git a/src/Agoda.Analyzers.Test/AgodaCustom/AG0032UnitTests.cs b/src/Agoda.Analyzers.Test/AgodaCustom/AG0032UnitTests.cs index 233be70..f38c210 100644 --- a/src/Agoda.Analyzers.Test/AgodaCustom/AG0032UnitTests.cs +++ b/src/Agoda.Analyzers.Test/AgodaCustom/AG0032UnitTests.cs @@ -1,12 +1,6 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Web; +using System.Threading.Tasks; using Agoda.Analyzers.AgodaCustom; using Agoda.Analyzers.Test.Helpers; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Framework; diff --git a/src/Agoda.Analyzers.Test/AgodaCustom/AG0033UnitTests.cs b/src/Agoda.Analyzers.Test/AgodaCustom/AG0033UnitTests.cs index d68ec99..9933ece 100644 --- a/src/Agoda.Analyzers.Test/AgodaCustom/AG0033UnitTests.cs +++ b/src/Agoda.Analyzers.Test/AgodaCustom/AG0033UnitTests.cs @@ -1,12 +1,6 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Web; +using System.Threading.Tasks; using Agoda.Analyzers.AgodaCustom; using Agoda.Analyzers.Test.Helpers; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Framework; diff --git a/src/Agoda.Analyzers.Test/AgodaCustom/AG0035UnitTests.cs b/src/Agoda.Analyzers.Test/AgodaCustom/AG0035UnitTests.cs index 5b4d6d3..2d39373 100644 --- a/src/Agoda.Analyzers.Test/AgodaCustom/AG0035UnitTests.cs +++ b/src/Agoda.Analyzers.Test/AgodaCustom/AG0035UnitTests.cs @@ -4,7 +4,6 @@ #if NET462 using System.Web; #else -using Microsoft.AspNetCore.Http; #endif using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Framework; diff --git a/src/Agoda.Analyzers.Test/AgodaCustom/AG0038UnitTests.cs b/src/Agoda.Analyzers.Test/AgodaCustom/AG0038UnitTests.cs index 942c0c1..ea12091 100644 --- a/src/Agoda.Analyzers.Test/AgodaCustom/AG0038UnitTests.cs +++ b/src/Agoda.Analyzers.Test/AgodaCustom/AG0038UnitTests.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Agoda.Analyzers.AgodaCustom; using Agoda.Analyzers.Test.Helpers; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Agoda.Analyzers.Test/AgodaCustom/AG0040UnitTests.cs b/src/Agoda.Analyzers.Test/AgodaCustom/AG0040UnitTests.cs index ee20d91..e90b594 100644 --- a/src/Agoda.Analyzers.Test/AgodaCustom/AG0040UnitTests.cs +++ b/src/Agoda.Analyzers.Test/AgodaCustom/AG0040UnitTests.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; -using System.Web.Mvc; using Agoda.Analyzers.AgodaCustom; using Agoda.Analyzers.Test.Helpers; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Agoda.Analyzers.Test/CodeFixes/AgodaCustom/AG0019FixProviderUnitTests.cs b/src/Agoda.Analyzers.Test/CodeFixes/AgodaCustom/AG0019FixProviderUnitTests.cs index 92edce7..8cffb10 100644 --- a/src/Agoda.Analyzers.Test/CodeFixes/AgodaCustom/AG0019FixProviderUnitTests.cs +++ b/src/Agoda.Analyzers.Test/CodeFixes/AgodaCustom/AG0019FixProviderUnitTests.cs @@ -3,10 +3,6 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Framework; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; using System.Threading.Tasks; namespace Agoda.Analyzers.Test.CodeFixes.AgodaCustom; diff --git a/src/Agoda.Analyzers.Test/CodeFixes/AgodaCustom/AG0020FixProviderUnitTests.cs b/src/Agoda.Analyzers.Test/CodeFixes/AgodaCustom/AG0020FixProviderUnitTests.cs index c5ee0a3..aef51fb 100644 --- a/src/Agoda.Analyzers.Test/CodeFixes/AgodaCustom/AG0020FixProviderUnitTests.cs +++ b/src/Agoda.Analyzers.Test/CodeFixes/AgodaCustom/AG0020FixProviderUnitTests.cs @@ -1,13 +1,7 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; +using System.Threading.Tasks; using Agoda.Analyzers.AgodaCustom; using Agoda.Analyzers.Test.Helpers; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Framework; diff --git a/src/Agoda.Analyzers.Test/CodeFixes/AgodaCustom/AG0022FixProviderUnitTests.cs b/src/Agoda.Analyzers.Test/CodeFixes/AgodaCustom/AG0022FixProviderUnitTests.cs index 2e40862..44b6499 100644 --- a/src/Agoda.Analyzers.Test/CodeFixes/AgodaCustom/AG0022FixProviderUnitTests.cs +++ b/src/Agoda.Analyzers.Test/CodeFixes/AgodaCustom/AG0022FixProviderUnitTests.cs @@ -1,12 +1,6 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; +using System.Threading.Tasks; using Agoda.Analyzers.AgodaCustom; using Agoda.Analyzers.Test.Helpers; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Framework; diff --git a/src/Agoda.Analyzers.Test/StyleCop/SA1106UnitTests.cs b/src/Agoda.Analyzers.Test/StyleCop/SA1106UnitTests.cs index a1a2060..76d627b 100644 --- a/src/Agoda.Analyzers.Test/StyleCop/SA1106UnitTests.cs +++ b/src/Agoda.Analyzers.Test/StyleCop/SA1106UnitTests.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; +using System.Threading.Tasks; using Agoda.Analyzers.StyleCop; using Agoda.Analyzers.Test.Helpers; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Agoda.Analyzers.Test/StyleCop/SA1107UnitTests.cs b/src/Agoda.Analyzers.Test/StyleCop/SA1107UnitTests.cs index 29bbcdf..f425954 100644 --- a/src/Agoda.Analyzers.Test/StyleCop/SA1107UnitTests.cs +++ b/src/Agoda.Analyzers.Test/StyleCop/SA1107UnitTests.cs @@ -1,9 +1,6 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; +using System.Threading.Tasks; using Agoda.Analyzers.StyleCop; using Agoda.Analyzers.Test.Helpers; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Framework; diff --git a/src/Agoda.Analyzers.Test/StyleCop/SA1123UnitTests.cs b/src/Agoda.Analyzers.Test/StyleCop/SA1123UnitTests.cs index c308469..f84fc58 100644 --- a/src/Agoda.Analyzers.Test/StyleCop/SA1123UnitTests.cs +++ b/src/Agoda.Analyzers.Test/StyleCop/SA1123UnitTests.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Threading; +using System.Threading; using System.Threading.Tasks; using Agoda.Analyzers.StyleCop; using Agoda.Analyzers.Test.Helpers; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs b/src/Agoda.Analyzers/AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs index 2e6b4be..8e9b45f 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0001DependencyResolverMustNotBeUsed.cs @@ -1,12 +1,7 @@ using Agoda.Analyzers.Helpers; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using System; using System.Collections.Generic; -using System.Collections.Immutable; -using System.Text.RegularExpressions; namespace Agoda.Analyzers.AgodaCustom { diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0003HttpContextCannotBePassedAsMethodArgument.cs b/src/Agoda.Analyzers/AgodaCustom/AG0003HttpContextCannotBePassedAsMethodArgument.cs index 574878a..56e6246 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0003HttpContextCannotBePassedAsMethodArgument.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0003HttpContextCannotBePassedAsMethodArgument.cs @@ -3,7 +3,6 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using System; using System.Collections.Generic; using System.Collections.Immutable; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0004DoNotUseHardCodedStringsToIdentifyTypes.cs b/src/Agoda.Analyzers/AgodaCustom/AG0004DoNotUseHardCodedStringsToIdentifyTypes.cs index 9e1b3ea..0dac329 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0004DoNotUseHardCodedStringsToIdentifyTypes.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0004DoNotUseHardCodedStringsToIdentifyTypes.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0009IHttpContextAccessorCannotBePassedAsMethodArgument.cs b/src/Agoda.Analyzers/AgodaCustom/AG0009IHttpContextAccessorCannotBePassedAsMethodArgument.cs index 588ffa7..f6e3952 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0009IHttpContextAccessorCannotBePassedAsMethodArgument.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0009IHttpContextAccessorCannotBePassedAsMethodArgument.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.Immutable; using Agoda.Analyzers.Helpers; using Microsoft.CodeAnalysis; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0010PreventTestFixtureInheritance.cs b/src/Agoda.Analyzers/AgodaCustom/AG0010PreventTestFixtureInheritance.cs index d69dcdc..1462c65 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0010PreventTestFixtureInheritance.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0010PreventTestFixtureInheritance.cs @@ -1,6 +1,5 @@ using System.Linq; using System.Collections.Immutable; -using System.Text.RegularExpressions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0011NoDirectQueryStringAccess.cs b/src/Agoda.Analyzers/AgodaCustom/AG0011NoDirectQueryStringAccess.cs index a49c6c7..f11dd8d 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0011NoDirectQueryStringAccess.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0011NoDirectQueryStringAccess.cs @@ -1,12 +1,7 @@ using Agoda.Analyzers.Helpers; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using System; using System.Collections.Generic; -using System.Collections.Immutable; -using System.Text.RegularExpressions; namespace Agoda.Analyzers.AgodaCustom { diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0012TestMethodMustContainAtLeastOneAssertion.cs b/src/Agoda.Analyzers/AgodaCustom/AG0012TestMethodMustContainAtLeastOneAssertion.cs index cea7766..08c6827 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0012TestMethodMustContainAtLeastOneAssertion.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0012TestMethodMustContainAtLeastOneAssertion.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using System.Net; namespace Agoda.Analyzers.AgodaCustom { diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0013LimitNumberOfTestMethodParametersTo5.cs b/src/Agoda.Analyzers/AgodaCustom/AG0013LimitNumberOfTestMethodParametersTo5.cs index c7b15f8..fdd08e0 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0013LimitNumberOfTestMethodParametersTo5.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0013LimitNumberOfTestMethodParametersTo5.cs @@ -1,9 +1,7 @@ -using System.Linq; -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; using Agoda.Analyzers.Helpers; using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp; -using System.Text.RegularExpressions; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Syntax; using System.Collections.Generic; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0020AvoidReturningNullEnumerablesFixProvider.cs b/src/Agoda.Analyzers/AgodaCustom/AG0020AvoidReturningNullEnumerablesFixProvider.cs index 3c91ff8..d589c55 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0020AvoidReturningNullEnumerablesFixProvider.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0020AvoidReturningNullEnumerablesFixProvider.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0022DoNotExposeBothSyncAndAsyncVersionsOfMethods.cs b/src/Agoda.Analyzers/AgodaCustom/AG0022DoNotExposeBothSyncAndAsyncVersionsOfMethods.cs index 3cfad9d..9a13db7 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0022DoNotExposeBothSyncAndAsyncVersionsOfMethods.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0022DoNotExposeBothSyncAndAsyncVersionsOfMethods.cs @@ -1,5 +1,4 @@ -using System; -using Agoda.Analyzers.Helpers; +using Agoda.Analyzers.Helpers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -8,7 +7,6 @@ using System.Collections.Immutable; using System.Linq; using System.Text.RegularExpressions; -using System.Threading.Tasks; namespace Agoda.Analyzers.AgodaCustom { diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0022RemoveSyncMethodFixProvider.cs b/src/Agoda.Analyzers/AgodaCustom/AG0022RemoveSyncMethodFixProvider.cs index 780d16b..ee0a0a0 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0022RemoveSyncMethodFixProvider.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0022RemoveSyncMethodFixProvider.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; using System.Threading; using System.Threading.Tasks; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0023PreventUseOfThreadSleep.cs b/src/Agoda.Analyzers/AgodaCustom/AG0023PreventUseOfThreadSleep.cs index 295e8b6..34f7f4a 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0023PreventUseOfThreadSleep.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0023PreventUseOfThreadSleep.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; +using System.Collections.Generic; using Agoda.Analyzers.Helpers; using Microsoft.CodeAnalysis; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0024PreventUseOfTaskFactoryStartNew.cs b/src/Agoda.Analyzers/AgodaCustom/AG0024PreventUseOfTaskFactoryStartNew.cs index 5943fb9..37e98d2 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0024PreventUseOfTaskFactoryStartNew.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0024PreventUseOfTaskFactoryStartNew.cs @@ -1,13 +1,11 @@ using System.Collections.Generic; using System.Linq; using System.Collections.Immutable; -using System.Text.RegularExpressions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Syntax; using Agoda.Analyzers.Helpers; -using System.Threading.Tasks; namespace Agoda.Analyzers.AgodaCustom { diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0025PreventUseOfTaskContinue.cs b/src/Agoda.Analyzers/AgodaCustom/AG0025PreventUseOfTaskContinue.cs index 4f4e451..2019e18 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0025PreventUseOfTaskContinue.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0025PreventUseOfTaskContinue.cs @@ -1,13 +1,8 @@ using System.Collections.Generic; -using System.Linq; -using System.Collections.Immutable; using System.Text.RegularExpressions; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Agoda.Analyzers.Helpers; -using System.Threading.Tasks; namespace Agoda.Analyzers.AgodaCustom { diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0026EnsureOnlyCssSelectorIsUsedToFindElements.cs b/src/Agoda.Analyzers/AgodaCustom/AG0026EnsureOnlyCssSelectorIsUsedToFindElements.cs index ae370de..3440d70 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0026EnsureOnlyCssSelectorIsUsedToFindElements.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0026EnsureOnlyCssSelectorIsUsedToFindElements.cs @@ -1,9 +1,7 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis; using Agoda.Analyzers.Helpers; -using System.Collections.Immutable; using Microsoft.CodeAnalysis.Diagnostics; -using System.Text.RegularExpressions; namespace Agoda.Analyzers.AgodaCustom { diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0027EnsureOnlyDataSeleniumIsUsedToFindElements.cs b/src/Agoda.Analyzers/AgodaCustom/AG0027EnsureOnlyDataSeleniumIsUsedToFindElements.cs index 662f9db..31ad928 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0027EnsureOnlyDataSeleniumIsUsedToFindElements.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0027EnsureOnlyDataSeleniumIsUsedToFindElements.cs @@ -1,11 +1,8 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using Microsoft.CodeAnalysis; using Agoda.Analyzers.Helpers; using System.Collections.Immutable; -using System.ComponentModel; using System.Linq; -using System.Linq.Expressions; using Microsoft.CodeAnalysis.Diagnostics; using System.Text.RegularExpressions; using Microsoft.CodeAnalysis.CSharp; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0032PreventUseOfBlockingTaskMethods.cs b/src/Agoda.Analyzers/AgodaCustom/AG0032PreventUseOfBlockingTaskMethods.cs index 5ca2289..fffee4a 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0032PreventUseOfBlockingTaskMethods.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0032PreventUseOfBlockingTaskMethods.cs @@ -1,11 +1,7 @@ using Agoda.Analyzers.Helpers; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Text.RegularExpressions; namespace Agoda.Analyzers.AgodaCustom diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0033PreventUseOfTaskResult.cs b/src/Agoda.Analyzers/AgodaCustom/AG0033PreventUseOfTaskResult.cs index a57d11e..5f34d92 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0033PreventUseOfTaskResult.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0033PreventUseOfTaskResult.cs @@ -1,12 +1,7 @@ using Agoda.Analyzers.Helpers; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using System; using System.Collections.Generic; -using System.Collections.Immutable; -using System.Text.RegularExpressions; namespace Agoda.Analyzers.AgodaCustom { diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0038PreventUseOfRegionPreprocessorDirective.cs b/src/Agoda.Analyzers/AgodaCustom/AG0038PreventUseOfRegionPreprocessorDirective.cs index 9246c08..35fb650 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0038PreventUseOfRegionPreprocessorDirective.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0038PreventUseOfRegionPreprocessorDirective.cs @@ -1,9 +1,7 @@ using Agoda.Analyzers.Helpers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using System; using System.Collections.Generic; using System.Collections.Immutable; diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0039MethodLineLengthAnalyzer.cs b/src/Agoda.Analyzers/AgodaCustom/AG0039MethodLineLengthAnalyzer.cs index b9866c5..01a56ad 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0039MethodLineLengthAnalyzer.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0039MethodLineLengthAnalyzer.cs @@ -1,5 +1,4 @@ using System.Collections.Immutable; -using System.Resources; using Agoda.Analyzers.Helpers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -7,7 +6,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; using System.Collections.Generic; -using System.Data; using System.Linq; namespace Agoda.Analyzers.AgodaCustom diff --git a/src/Agoda.Analyzers/AgodaCustom/AG0041CodeFixProvider.cs b/src/Agoda.Analyzers/AgodaCustom/AG0041CodeFixProvider.cs index 588be5d..b7b5def 100644 --- a/src/Agoda.Analyzers/AgodaCustom/AG0041CodeFixProvider.cs +++ b/src/Agoda.Analyzers/AgodaCustom/AG0041CodeFixProvider.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; diff --git a/src/Agoda.Analyzers/AgodaCustom/MethodInvocationAnalyzerBase.cs b/src/Agoda.Analyzers/AgodaCustom/MethodInvocationAnalyzerBase.cs index 377f391..0ba99a3 100644 --- a/src/Agoda.Analyzers/AgodaCustom/MethodInvocationAnalyzerBase.cs +++ b/src/Agoda.Analyzers/AgodaCustom/MethodInvocationAnalyzerBase.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using System.Collections.Immutable; using System.Linq; diff --git a/src/Agoda.Analyzers/Helpers/InvocationRule.cs b/src/Agoda.Analyzers/Helpers/InvocationRule.cs index fc1a481..9c308a9 100644 --- a/src/Agoda.Analyzers/Helpers/InvocationRule.cs +++ b/src/Agoda.Analyzers/Helpers/InvocationRule.cs @@ -1,6 +1,5 @@ using System.Linq; using System.Text.RegularExpressions; -using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Agoda.Analyzers/Helpers/TestMethodHelpers.cs b/src/Agoda.Analyzers/Helpers/TestMethodHelpers.cs index 617ec68..2132be8 100644 --- a/src/Agoda.Analyzers/Helpers/TestMethodHelpers.cs +++ b/src/Agoda.Analyzers/Helpers/TestMethodHelpers.cs @@ -5,7 +5,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using System.Linq; using System.Text.RegularExpressions; -using Agoda.Analyzers.AgodaCustom; namespace Agoda.Analyzers.Helpers { diff --git a/src/Agoda.Analyzers/StyleCop/RemoveRegionFixAllProvider.cs b/src/Agoda.Analyzers/StyleCop/RemoveRegionFixAllProvider.cs index 62ae423..4f4b411 100644 --- a/src/Agoda.Analyzers/StyleCop/RemoveRegionFixAllProvider.cs +++ b/src/Agoda.Analyzers/StyleCop/RemoveRegionFixAllProvider.cs @@ -4,7 +4,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; -using DocumentBasedFixAllProvider = Agoda.Analyzers.Helpers.DocumentBasedFixAllProvider; namespace Agoda.Analyzers.StyleCop { diff --git a/src/Agoda.Analyzers/StyleCop/SA1107FixAllProvider.cs b/src/Agoda.Analyzers/StyleCop/SA1107FixAllProvider.cs index 96daa52..d4bb990 100644 --- a/src/Agoda.Analyzers/StyleCop/SA1107FixAllProvider.cs +++ b/src/Agoda.Analyzers/StyleCop/SA1107FixAllProvider.cs @@ -4,7 +4,6 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Editing; -using DocumentBasedFixAllProvider = Agoda.Analyzers.Helpers.DocumentBasedFixAllProvider; namespace Agoda.Analyzers.StyleCop {