Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions Stackworx.Analyzers.Tests/DiagnosticHelpLinksTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
namespace Stackworx.Analyzers.Tests;

using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.Diagnostics;
using Xunit;

public class DiagnosticHelpLinksTests
{
[Fact]
public void AllSupportedDiagnostics_HaveExpectedDocumentationLinks()
{
var expectedLinks = new Dictionary<string, string>
{
["SW001"] = "https://stackworx-dotnet.github.io/Stackworx.Analyzers/docs/rules/sw001-avoid-implicit-datetime-to-datetimeoffset",
["SW002"] = "https://stackworx-dotnet.github.io/Stackworx.Analyzers/docs/rules/sw002-unused-method",
["SW101"] = "https://stackworx-dotnet.github.io/Stackworx.Analyzers/docs/rules/sw101-forbidden-namespace-reference",
["SW102"] = "https://stackworx-dotnet.github.io/Stackworx.Analyzers/docs/rules/sw102-forbidden-namespace-using",
["SW103"] = "https://stackworx-dotnet.github.io/Stackworx.Analyzers/docs/rules/sw103-avoid-microsoft-extensions-azure",
["SWGQL01"] = "https://stackworx-dotnet.github.io/Stackworx.Analyzers/docs/rules/swgql01-static-extension-method-validation",
["SWGQL02"] = "https://stackworx-dotnet.github.io/Stackworx.Analyzers/docs/rules/swgql02-unused-dataloader-interface",
["SWGQL03"] = "https://stackworx-dotnet.github.io/Stackworx.Analyzers/docs/rules/swgql03-graphql-extension-class-static",
["SWGQL04"] = "https://stackworx-dotnet.github.io/Stackworx.Analyzers/docs/rules/swgql04-graphql-duplicate-extension-field",
["SWGQL05"] = "https://stackworx-dotnet.github.io/Stackworx.Analyzers/docs/rules/swgql05-06-hotchocolate-types-usedimplicitly",
["SWGQL06"] = "https://stackworx-dotnet.github.io/Stackworx.Analyzers/docs/rules/swgql05-06-hotchocolate-types-usedimplicitly",
};

DiagnosticAnalyzer[] analyzers =
[
new AvoidImplicitDateTimeToDateTimeOffsetAnalyzer(),
new AvoidMicrosoftExtensionsAzureAnalyzer(),
new GraphQLDuplicateFieldAnalyzer(),
new GraphQLHotChocolateTypeUsedImplicitlyAnalyzer(),
new GraphQLStaticExtensionClassAnalyzer(),
new GraphQLStaticModifierExtensionMethodAnalyzer(),
new GraphQLUnusedDataLoaderAnalyzer(),
new NamespaceInternalAnalyzer(),
new UnusedMethodAnalyzer(),
];

var descriptors = analyzers
.SelectMany(static analyzer => analyzer.SupportedDiagnostics)
.ToArray();

Assert.Equal(expectedLinks.Count, descriptors.Length);

foreach (var descriptor in descriptors)
{
Assert.True(expectedLinks.TryGetValue(descriptor.Id, out var expectedLink), $"Unexpected diagnostic id: {descriptor.Id}");
Assert.Equal(expectedLink, descriptor.HelpLinkUri);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ public sealed class AvoidImplicitDateTimeToDateTimeOffsetAnalyzer : DiagnosticAn
category: Category,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: Description);
description: Description,
helpLinkUri: DiagnosticHelpLinks.For(DiagnosticId));

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);

Expand Down
3 changes: 2 additions & 1 deletion Stackworx.Analyzers/AvoidMicrosoftExtensionsAzureAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ public sealed class AvoidMicrosoftExtensionsAzureAnalyzer : DiagnosticAnalyzer
category: Category,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: Description);
description: Description,
helpLinkUri: DiagnosticHelpLinks.For(DiagnosticId));

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);

Expand Down
33 changes: 33 additions & 0 deletions Stackworx.Analyzers/DiagnosticHelpLinks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace Stackworx.Analyzers;

using System;

internal static class DiagnosticHelpLinks
{
private const string RulesBaseUrl = "https://stackworx-dotnet.github.io/Stackworx.Analyzers/docs/rules/";

public static string For(string diagnosticId) => RulesBaseUrl + (diagnosticId switch
{
"SW001" =>
"sw001-avoid-implicit-datetime-to-datetimeoffset",
"SW002" =>
"sw002-unused-method",
"SW101" =>
"sw101-forbidden-namespace-reference",
"SW102" =>
"sw102-forbidden-namespace-using",
"SW103" =>
"sw103-avoid-microsoft-extensions-azure",
"SWGQL01" =>
"swgql01-static-extension-method-validation",
"SWGQL02" =>
"swgql02-unused-dataloader-interface",
"SWGQL03" =>
"swgql03-graphql-extension-class-static",
"SWGQL04" =>
"swgql04-graphql-duplicate-extension-field",
"SWGQL05" or "SWGQL06" =>
"swgql05-06-hotchocolate-types-usedimplicitly",
_ => throw new ArgumentOutOfRangeException(nameof(diagnosticId), diagnosticId, "No documentation link configured."),
});
}
1 change: 1 addition & 0 deletions Stackworx.Analyzers/GraphQLDuplicateFieldAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public sealed class GraphQLDuplicateFieldAnalyzer : DiagnosticAnalyzer
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "Detects duplicate extension fields for the same type across [ExtendObjectType<T>] extension classes.",
helpLinkUri: DiagnosticHelpLinks.For("SWGQL04"),
customTags: [WellKnownDiagnosticTags.CompilationEnd]);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public sealed class GraphQLHotChocolateTypeUsedImplicitlyAnalyzer : DiagnosticAn
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description:
"HotChocolate GraphQL types are often discovered via reflection. Mark them with [JetBrains.Annotations.UsedImplicitly] so IDE analyzers don't flag them as unused.");
"HotChocolate GraphQL types are often discovered via reflection. Mark them with [JetBrains.Annotations.UsedImplicitly] so IDE analyzers don't flag them as unused.",
helpLinkUri: DiagnosticHelpLinks.For("SWGQL05"));

public static readonly DiagnosticDescriptor UsedImplicitlyOnNonGraphQLTypeRule = new(
id: "SWGQL06",
Expand All @@ -32,6 +33,7 @@ public sealed class GraphQLHotChocolateTypeUsedImplicitlyAnalyzer : DiagnosticAn
isEnabledByDefault: true,
description:
"Prefer limiting [UsedImplicitly] to types that are actually discovered implicitly (e.g., HotChocolate GraphQL types).",
helpLinkUri: DiagnosticHelpLinks.For("SWGQL06"),
customTags: [WellKnownDiagnosticTags.Unnecessary]);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
Expand Down
3 changes: 2 additions & 1 deletion Stackworx.Analyzers/GraphQLStaticExtensionClassAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public class GraphQLStaticExtensionClassAnalyzer : DiagnosticAnalyzer
Category,
DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: Description);
description: Description,
helpLinkUri: DiagnosticHelpLinks.For(DiagnosticId));

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ public sealed class GraphQLStaticModifierExtensionMethodAnalyzer : DiagnosticAna
category: "GraphQL.Usage",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "GraphQL field extension methods must be instance methods when declared in non-static classes.");
description: "GraphQL field extension methods must be instance methods when declared in non-static classes.",
helpLinkUri: DiagnosticHelpLinks.For("SWGQL01"));

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create(StaticMethodInNonStaticClassRule);
Expand Down
1 change: 1 addition & 0 deletions Stackworx.Analyzers/GraphQLUnusedDataLoaderAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public sealed class GraphQLUnusedDataLoaderAnalyzer : DiagnosticAnalyzer
isEnabledByDefault: true,
description:
"Flags interfaces extending GreenDonut.IDataLoader that appear to be unused in the current compilation.",
helpLinkUri: DiagnosticHelpLinks.For("SWGQL02"),
customTags: [WellKnownDiagnosticTags.CompilationEnd]);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
Expand Down
4 changes: 2 additions & 2 deletions Stackworx.Analyzers/NamespaceInternalAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ public sealed class NamespaceInternalAnalyzer : DiagnosticAnalyzer
"Using directives that import a feature’s Internal namespace are restricted to that feature.";

private static readonly DiagnosticDescriptor RuleReference = new(
Id_Reference, Title1, Message1, "Architecture", DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Desc1);
Id_Reference, Title1, Message1, "Architecture", DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Desc1, helpLinkUri: DiagnosticHelpLinks.For(Id_Reference));

private static readonly DiagnosticDescriptor RuleUsing = new(
Id_Using, Title2, Message2, "Architecture", DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Desc2);
Id_Using, Title2, Message2, "Architecture", DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Desc2, helpLinkUri: DiagnosticHelpLinks.For(Id_Using));

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create(RuleReference, RuleUsing);
Expand Down
1 change: 1 addition & 0 deletions Stackworx.Analyzers/UnusedMethodAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public sealed class UnusedMethodAnalyzer : DiagnosticAnalyzer
isEnabledByDefault: false,
description:
"Flags methods that have no call sites in the current compilation. Methods annotated with JetBrains.Annotations.PublicAPI or JetBrains.Annotations.UsedImplicitly (or whose containing type is annotated) are ignored.",
helpLinkUri: DiagnosticHelpLinks.For("SW002"),
customTags: [WellKnownDiagnosticTags.CompilationEnd]);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
Expand Down
Loading