diff --git a/.gitignore b/.gitignore
index 50943b39..909f431f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -259,7 +259,6 @@ paket-files/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
-launchSettings.json
.vscode/
*.db
*.db-journal
diff --git a/CompatBot/Commands/BotMath.cs b/CompatBot/Commands/BotMath.cs
index 87dda5cd..fb25dcd4 100644
--- a/CompatBot/Commands/BotMath.cs
+++ b/CompatBot/Commands/BotMath.cs
@@ -14,6 +14,7 @@ internal sealed class BotMath
}
[Command("calculate"), TextAlias("calc"), DefaultGroupCommand]
+ [Description("12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901")]
public async ValueTask Calc(CommandContext ctx, [RemainingText, Description("Math expression")] string expression)
{
if (string.IsNullOrEmpty(expression))
diff --git a/CompatBot/CompatBot.csproj b/CompatBot/CompatBot.csproj
index de7a96b1..a1196e54 100644
--- a/CompatBot/CompatBot.csproj
+++ b/CompatBot/CompatBot.csproj
@@ -11,6 +11,7 @@
true
enable
false
+ True
diff --git a/SourceGenerators/AttributeUsageAnalyzer.cs b/SourceGenerators/AttributeUsageAnalyzer.cs
index 4f634f04..3b7284aa 100644
--- a/SourceGenerators/AttributeUsageAnalyzer.cs
+++ b/SourceGenerators/AttributeUsageAnalyzer.cs
@@ -1,6 +1,9 @@
using System.Collections.Immutable;
+using System.Linq;
using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Operations;
namespace SourceGenerators;
@@ -9,21 +12,30 @@ public class AttributeUsageAnalyzer : DiagnosticAnalyzer
{
// You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat.
// See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/Localizing%20Analyzers.md for more on localization
- private const string Category = "Usage";
- private const string DiagnosticId = "DSharpPlusAttributeUsage";
- private static readonly DiagnosticDescriptor AccessCheckAttributeOnGroupCommandRule = new DiagnosticDescriptor(
- DiagnosticId,
+ private static readonly DiagnosticDescriptor AccessCheckAttributeOnGroupCommandRule = new(
+ "DSP0001",
"Access check attributes are ignored",
"Attribute {0} will be ignored for GroupCommand",
- Category,
+ "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "GroupCommand methods will silently ignore any access check attributes, so instead create an instance of the required check attribute and call it explicitly inside the method."
);
+ private static readonly DiagnosticDescriptor DescriptionLengthRule = new(
+ "DSP0002",
+ "Description is too long",
+ "Description is {0} characters long, which is {1} characters longer than allowed",
+ "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description: "Description must be less than or equal to 100 characters."
+ );
- public override ImmutableArray SupportedDiagnostics { get; } =
- [AccessCheckAttributeOnGroupCommandRule];
+ public override ImmutableArray SupportedDiagnostics { get; } = [
+ AccessCheckAttributeOnGroupCommandRule,
+ DescriptionLengthRule,
+ ];
public override void Initialize(AnalysisContext context)
{
@@ -32,10 +44,11 @@ public class AttributeUsageAnalyzer : DiagnosticAnalyzer
// TODO: Consider registering other actions that act on syntax instead of or in addition to symbols
// See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/Analyzer%20Actions%20Semantics.md for more information
- context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method);
+ context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method);
+ context.RegisterOperationAction(AnalyzeDescriptionAttribute, OperationKind.Attribute);
}
- private static void AnalyzeSymbol(SymbolAnalysisContext context)
+ private static void AnalyzeMethod(SymbolAnalysisContext context)
{
var methodSymbol = (IMethodSymbol)context.Symbol;
var methodAttributes = methodSymbol.GetAttributes();
@@ -62,6 +75,67 @@ public class AttributeUsageAnalyzer : DiagnosticAnalyzer
}
}
+ private void AnalyzeDescriptionAttribute(OperationAnalysisContext context)
+ {
+ // The Roslyn architecture is based on inheritance.
+ // To get the required metadata, we should match the 'Operation' and 'Syntax' objects to the particular types,
+ // which are based on the 'OperationKind' parameter specified in the 'Register...' method.
+ if (context.Operation is not IAttributeOperation attributeOperation
+ || context.Operation.Syntax is not AttributeSyntax attributeSyntax)
+ return;
+
+ if (attributeOperation.Kind != OperationKind.Attribute
+ || attributeOperation.Operation is not IObjectCreationOperation
+ {
+ Kind: OperationKind.ObjectCreation,
+ Type:
+ {
+ ContainingNamespace:
+ {
+ ContainingNamespace.Name: "System",
+ Name: "ComponentModel"
+ },
+ Name: "DescriptionAttribute"
+ }
+ } attrCtorOp)
+ return;
+
+ if (attrCtorOp.Arguments.Length is 0
+ || attrCtorOp.Arguments.FirstOrDefault(arg => arg is
+ {
+ Parameter:
+ {
+ Name: "description",
+ Type: {ContainingNamespace.Name: "System", Name: "String"}
+ }
+ }) is not
+ {
+ Value: ILiteralOperation
+ {
+ ConstantValue:
+ {
+ HasValue: true,
+ Value: string actualDescription
+ }
+ }
+ })
+ return;
+
+ const int maxDescriptionLength = 100;
+ if (actualDescription.Length <= maxDescriptionLength)
+ return;
+
+ var diagnostic = Diagnostic.Create(DescriptionLengthRule,
+ // The highlighted area in the analyzed source code. Keep it as specific as possible.
+ attributeSyntax.GetLocation(),
+ // The value is passed to the 'MessageFormat' argument of your rule.
+ actualDescription.Length, actualDescription.Length - maxDescriptionLength
+ );
+
+ // Reporting a diagnostic is the primary outcome of analyzers.
+ context.ReportDiagnostic(diagnostic);
+ }
+
private static bool IsDescendantOfAttribute(AttributeData attributeData, string baseAttributeClassNameWithNamespace)
{
var attrClass = attributeData.AttributeClass;
diff --git a/SourceGenerators/Properties/launchSettings.json b/SourceGenerators/Properties/launchSettings.json
new file mode 100644
index 00000000..1fa18079
--- /dev/null
+++ b/SourceGenerators/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "RoslynAnalyzers": {
+ "commandName": "DebugRoslynComponent",
+ "targetProject": "..\\CompatBot\\CompatBot.csproj"
+ }
+ }
+}
\ No newline at end of file
diff --git a/SourceGenerators/SourceGenerators.csproj b/SourceGenerators/SourceGenerators.csproj
index 555bf5c2..39178e43 100644
--- a/SourceGenerators/SourceGenerators.csproj
+++ b/SourceGenerators/SourceGenerators.csproj
@@ -5,11 +5,12 @@
latest
1701;1702;RS2008;RS1036
enable
+ true
-
+