From 7aab36606b6f44e79cd27c8af5960724be1a7835 Mon Sep 17 00:00:00 2001 From: Mihail Gribkov <61027276+Misha-133@users.noreply.github.com> Date: Sun, 14 Apr 2024 00:01:07 +0300 Subject: [PATCH] Remove analyzer project & resolve some build warnings (#2905) * yeet analyzer & analyzer test * fix test warning * yeet analyzer from the workflow * resolve more warnings * forgot to push --- .github/workflows/dotnet.yml | 4 - Discord.Net.sln | 30 -- .../InteractionHandler.cs | 11 +- .../Modules/ExampleModule.cs | 2 +- samples/InteractionFramework/Program.cs | 7 +- .../Discord.Net.Analyzers.csproj | 17 -- .../GuildAccessAnalyzer.cs | 72 ----- src/Discord.Net.Analyzers/SymbolExtensions.cs | 21 -- src/Discord.Net.Analyzers/docs/DNET0001.md | 30 -- .../Discord.Net.Analyzers.Tests.csproj | 27 -- .../GuildAccessTests.cs | 107 ------- .../Helpers/CodeFixVerifier.Helper.cs | 85 ------ .../Helpers/DiagnosticResult.cs | 87 ------ .../Helpers/DiagnosticVerifier.Helper.cs | 205 -------------- .../Verifiers/CodeFixVerifier.cs | 128 --------- .../Verifiers/DiagnosticVerifier.cs | 262 ------------------ .../TimeSpanTypeReaderTests.cs | 5 +- 17 files changed, 17 insertions(+), 1083 deletions(-) delete mode 100644 src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj delete mode 100644 src/Discord.Net.Analyzers/GuildAccessAnalyzer.cs delete mode 100644 src/Discord.Net.Analyzers/SymbolExtensions.cs delete mode 100644 src/Discord.Net.Analyzers/docs/DNET0001.md delete mode 100644 test/Discord.Net.Analyzers.Tests/Discord.Net.Analyzers.Tests.csproj delete mode 100644 test/Discord.Net.Analyzers.Tests/GuildAccessTests.cs delete mode 100644 test/Discord.Net.Analyzers.Tests/Helpers/CodeFixVerifier.Helper.cs delete mode 100644 test/Discord.Net.Analyzers.Tests/Helpers/DiagnosticResult.cs delete mode 100644 test/Discord.Net.Analyzers.Tests/Helpers/DiagnosticVerifier.Helper.cs delete mode 100644 test/Discord.Net.Analyzers.Tests/Verifiers/CodeFixVerifier.cs delete mode 100644 test/Discord.Net.Analyzers.Tests/Verifiers/DiagnosticVerifier.cs diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 61f8f84444..a1face93b9 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -45,9 +45,6 @@ jobs: - name: Unit Test run: dotnet test "test/Discord.Net.Tests.Unit/Discord.Net.Tests.Unit.csproj" --no-restore --no-build -v minimal -c Release --logger trx - - name: Analyzer Test - run: dotnet test "test/Discord.Net.Analyzers.Tests/Discord.Net.Analyzers.Tests.csproj" --no-restore --no-build -v minimal -c Release --logger trx - - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 if: failure() || success() @@ -63,7 +60,6 @@ jobs: dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" --no-restore --no-build -v minimal -c Release -o ${{ env.ArtifactStagingDirectory }} /p:BuildNumber=${{ env.Suffix }} /p:IsTagBuild=${{ env.IsTagBuild }} dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" --no-restore --no-build -v minimal -c Release -o ${{ env.ArtifactStagingDirectory }} /p:BuildNumber=${{ env.Suffix }} /p:IsTagBuild=${{ env.IsTagBuild }} dotnet pack "src\Discord.Net.Webhook\Discord.Net.Webhook.csproj" --no-restore --no-build -v minimal -c Release -o ${{ env.ArtifactStagingDirectory }} /p:BuildNumber=${{ env.Suffix }} /p:IsTagBuild=${{ env.IsTagBuild }} - dotnet pack "src\Discord.Net.Analyzers\Discord.Net.Analyzers.csproj" --no-restore --no-build -v minimal -c Release -o ${{ env.ArtifactStagingDirectory }} /p:BuildNumber=${{ env.Suffix }} /p:IsTagBuild=${{ env.IsTagBuild }} dotnet pack "src\Discord.Net.Interactions\Discord.Net.Interactions.csproj" --no-restore --no-build -v minimal -c Release -o ${{ env.ArtifactStagingDirectory }} /p:BuildNumber=${{ env.Suffix }} /p:IsTagBuild=${{ env.IsTagBuild }} # dotnet pack "experiment\Discord.Net.BuildOverrides\Discord.Net.BuildOverrides.csproj" --no-restore --no-build -v minimal -c Release -o ${{ env.ArtifactStagingDirectory }} /p:BuildNumber=${{ env.Suffix }} /p:IsTagBuild=${{ env.IsTagBuild }} diff --git a/Discord.Net.sln b/Discord.Net.sln index 544283b8b3..48c80d54fe 100644 --- a/Discord.Net.sln +++ b/Discord.Net.sln @@ -16,10 +16,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Examples", "src EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Interactions", "src\Discord.Net.Interactions\Discord.Net.Interactions.csproj", "{137DB209-B357-4EE8-A6EE-4B6127F6DEE9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Analyzers", "src\Discord.Net.Analyzers\Discord.Net.Analyzers.csproj", "{24C231FD-8CF3-444A-9E7C-45C18BAD4A0D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Analyzers.Tests", "test\Discord.Net.Analyzers.Tests\Discord.Net.Analyzers.Tests.csproj", "{FC67057C-E92F-4E1C-98BE-46F839C8AD71}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Tests.Unit", "test\Discord.Net.Tests.Unit\Discord.Net.Tests.Unit.csproj", "{DBF8B16E-5967-4480-8EDE-15D98A0DF0C4}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Tests.Integration", "test\Discord.Net.Tests.Integration\Discord.Net.Tests.Integration.csproj", "{E169E15A-E82C-45BF-8C24-C2CADB7093AA}" @@ -140,30 +136,6 @@ Global {137DB209-B357-4EE8-A6EE-4B6127F6DEE9}.Release|x64.Build.0 = Release|Any CPU {137DB209-B357-4EE8-A6EE-4B6127F6DEE9}.Release|x86.ActiveCfg = Release|Any CPU {137DB209-B357-4EE8-A6EE-4B6127F6DEE9}.Release|x86.Build.0 = Release|Any CPU - {24C231FD-8CF3-444A-9E7C-45C18BAD4A0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {24C231FD-8CF3-444A-9E7C-45C18BAD4A0D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {24C231FD-8CF3-444A-9E7C-45C18BAD4A0D}.Debug|x64.ActiveCfg = Debug|Any CPU - {24C231FD-8CF3-444A-9E7C-45C18BAD4A0D}.Debug|x64.Build.0 = Debug|Any CPU - {24C231FD-8CF3-444A-9E7C-45C18BAD4A0D}.Debug|x86.ActiveCfg = Debug|Any CPU - {24C231FD-8CF3-444A-9E7C-45C18BAD4A0D}.Debug|x86.Build.0 = Debug|Any CPU - {24C231FD-8CF3-444A-9E7C-45C18BAD4A0D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {24C231FD-8CF3-444A-9E7C-45C18BAD4A0D}.Release|Any CPU.Build.0 = Release|Any CPU - {24C231FD-8CF3-444A-9E7C-45C18BAD4A0D}.Release|x64.ActiveCfg = Release|Any CPU - {24C231FD-8CF3-444A-9E7C-45C18BAD4A0D}.Release|x64.Build.0 = Release|Any CPU - {24C231FD-8CF3-444A-9E7C-45C18BAD4A0D}.Release|x86.ActiveCfg = Release|Any CPU - {24C231FD-8CF3-444A-9E7C-45C18BAD4A0D}.Release|x86.Build.0 = Release|Any CPU - {FC67057C-E92F-4E1C-98BE-46F839C8AD71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FC67057C-E92F-4E1C-98BE-46F839C8AD71}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FC67057C-E92F-4E1C-98BE-46F839C8AD71}.Debug|x64.ActiveCfg = Debug|Any CPU - {FC67057C-E92F-4E1C-98BE-46F839C8AD71}.Debug|x64.Build.0 = Debug|Any CPU - {FC67057C-E92F-4E1C-98BE-46F839C8AD71}.Debug|x86.ActiveCfg = Debug|Any CPU - {FC67057C-E92F-4E1C-98BE-46F839C8AD71}.Debug|x86.Build.0 = Debug|Any CPU - {FC67057C-E92F-4E1C-98BE-46F839C8AD71}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FC67057C-E92F-4E1C-98BE-46F839C8AD71}.Release|Any CPU.Build.0 = Release|Any CPU - {FC67057C-E92F-4E1C-98BE-46F839C8AD71}.Release|x64.ActiveCfg = Release|Any CPU - {FC67057C-E92F-4E1C-98BE-46F839C8AD71}.Release|x64.Build.0 = Release|Any CPU - {FC67057C-E92F-4E1C-98BE-46F839C8AD71}.Release|x86.ActiveCfg = Release|Any CPU - {FC67057C-E92F-4E1C-98BE-46F839C8AD71}.Release|x86.Build.0 = Release|Any CPU {DBF8B16E-5967-4480-8EDE-15D98A0DF0C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DBF8B16E-5967-4480-8EDE-15D98A0DF0C4}.Debug|Any CPU.Build.0 = Debug|Any CPU {DBF8B16E-5967-4480-8EDE-15D98A0DF0C4}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -283,8 +255,6 @@ Global {9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A} {47820065-3CFB-401C-ACEA-862BD564A404} = {BB59D5B5-E7B0-4BF4-8F82-D14431B2799B} {137DB209-B357-4EE8-A6EE-4B6127F6DEE9} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A} - {24C231FD-8CF3-444A-9E7C-45C18BAD4A0D} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A} - {FC67057C-E92F-4E1C-98BE-46F839C8AD71} = {C7CF5621-7D36-433B-B337-5A2E3C101A71} {DBF8B16E-5967-4480-8EDE-15D98A0DF0C4} = {C7CF5621-7D36-433B-B337-5A2E3C101A71} {E169E15A-E82C-45BF-8C24-C2CADB7093AA} = {C7CF5621-7D36-433B-B337-5A2E3C101A71} {F2FF84FB-F6AD-47E5-9EE5-18206CAE136E} = {BB59D5B5-E7B0-4BF4-8F82-D14431B2799B} diff --git a/samples/InteractionFramework/InteractionHandler.cs b/samples/InteractionFramework/InteractionHandler.cs index 2d362e742b..c090a54635 100644 --- a/samples/InteractionFramework/InteractionHandler.cs +++ b/samples/InteractionFramework/InteractionHandler.cs @@ -39,8 +39,11 @@ public async Task InitializeAsync() _handler.InteractionExecuted += HandleInteractionExecute; } - private async Task LogAsync(LogMessage log) - => Console.WriteLine(log); + private Task LogAsync(LogMessage log) + { + Console.WriteLine(log); + return Task.CompletedTask; + } private async Task ReadyAsync() { @@ -80,7 +83,7 @@ private async Task HandleInteraction(SocketInteraction interaction) } } - private async Task HandleInteractionExecute(ICommandInfo commandInfo, IInteractionContext context, IResult result) + private Task HandleInteractionExecute(ICommandInfo commandInfo, IInteractionContext context, IResult result) { if (!result.IsSuccess) switch (result.Error) @@ -91,5 +94,7 @@ private async Task HandleInteractionExecute(ICommandInfo commandInfo, IInteracti default: break; } + + return Task.CompletedTask; } } diff --git a/samples/InteractionFramework/Modules/ExampleModule.cs b/samples/InteractionFramework/Modules/ExampleModule.cs index 1d587b6587..3a6606faf0 100644 --- a/samples/InteractionFramework/Modules/ExampleModule.cs +++ b/samples/InteractionFramework/Modules/ExampleModule.cs @@ -61,7 +61,7 @@ public async Task ButtonPress(string id, string name) // Select Menu interactions, contain ids of the menu options that were selected by the user. You can access the option ids from the method parameters. // You can also use the wild card pattern with Select Menus, in that case, the wild card captures will be passed on to the method first, followed by the option ids. [ComponentInteraction("roleSelect")] - public async Task RoleSelect(string[] selections) + public Task RoleSelect(string[] selections) { throw new NotImplementedException(); } diff --git a/samples/InteractionFramework/Program.cs b/samples/InteractionFramework/Program.cs index d7572d1265..2335e46b14 100644 --- a/samples/InteractionFramework/Program.cs +++ b/samples/InteractionFramework/Program.cs @@ -59,6 +59,9 @@ await _services.GetRequiredService() await Task.Delay(Timeout.Infinite); } - private static async Task LogAsync(LogMessage message) - => Console.WriteLine(message.ToString()); + private static Task LogAsync(LogMessage message) + { + Console.WriteLine(message.ToString()); + return Task.CompletedTask; + } } diff --git a/src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj b/src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj deleted file mode 100644 index a44e972176..0000000000 --- a/src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - Discord.Net.Analyzers - Discord.Analyzers - A Discord.Net extension adding support for design-time analysis of the API usage. - netstandard2.0;netstandard2.1 - false - false - - - - - - - - diff --git a/src/Discord.Net.Analyzers/GuildAccessAnalyzer.cs b/src/Discord.Net.Analyzers/GuildAccessAnalyzer.cs deleted file mode 100644 index 98d7856067..0000000000 --- a/src/Discord.Net.Analyzers/GuildAccessAnalyzer.cs +++ /dev/null @@ -1,72 +0,0 @@ -using Discord.Commands; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; -using System; -using System.Collections.Immutable; -using System.Linq; - -namespace Discord.Analyzers -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public sealed class GuildAccessAnalyzer : DiagnosticAnalyzer - { - private const string DiagnosticId = "DNET0001"; - private const string Title = "Limit command to Guild contexts."; - private const string MessageFormat = "Command method '{0}' is accessing 'Context.Guild' but is not restricted to Guild contexts."; - private const string Description = "Accessing 'Context.Guild' in a command without limiting the command to run only in guilds."; - private const string Category = "API Usage"; - - private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description); - - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); - - public override void Initialize(AnalysisContext context) - { - context.EnableConcurrentExecution(); - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); - context.RegisterSyntaxNodeAction(AnalyzeMemberAccess, SyntaxKind.SimpleMemberAccessExpression); - } - - private static void AnalyzeMemberAccess(SyntaxNodeAnalysisContext context) - { - // Bail out if the accessed member isn't named 'Guild' - var memberAccessSymbol = context.SemanticModel.GetSymbolInfo(context.Node).Symbol; - if (memberAccessSymbol.Name != "Guild") - return; - - // Bail out if it happens to be 'ContextType.Guild' in the '[RequireContext]' argument - if (context.Node.Parent is AttributeArgumentSyntax) - return; - - // Bail out if the containing class doesn't derive from 'ModuleBase' - var typeNode = context.Node.FirstAncestorOrSelf(); - var typeSymbol = context.SemanticModel.GetDeclaredSymbol(typeNode); - if (!typeSymbol.DerivesFromModuleBase()) - return; - - // Bail out if the containing method isn't marked with '[Command]' - var methodNode = context.Node.FirstAncestorOrSelf(); - var methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodNode); - var methodAttributes = methodSymbol.GetAttributes(); - if (!methodAttributes.Any(a => a.AttributeClass.Name == nameof(CommandAttribute))) - return; - - // Is the '[RequireContext]' attribute not applied to either the - // method or the class, or its argument isn't 'ContextType.Guild'? - var ctxAttribute = methodAttributes.SingleOrDefault(_attributeDataPredicate) - ?? typeSymbol.GetAttributes().SingleOrDefault(_attributeDataPredicate); - - if (ctxAttribute == null || ctxAttribute.ConstructorArguments.Any(arg => !arg.Value.Equals((int)ContextType.Guild))) - { - // Report the diagnostic - var diagnostic = Diagnostic.Create(Rule, context.Node.GetLocation(), methodSymbol.Name); - context.ReportDiagnostic(diagnostic); - } - } - - private static readonly Func _attributeDataPredicate = - (a => a.AttributeClass.Name == nameof(RequireContextAttribute)); - } -} diff --git a/src/Discord.Net.Analyzers/SymbolExtensions.cs b/src/Discord.Net.Analyzers/SymbolExtensions.cs deleted file mode 100644 index cf9dd36566..0000000000 --- a/src/Discord.Net.Analyzers/SymbolExtensions.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Discord.Commands; -using Microsoft.CodeAnalysis; -using System; - -namespace Discord.Analyzers -{ - internal static class SymbolExtensions - { - private static readonly string _moduleBaseName = typeof(ModuleBase<>).Name; - - public static bool DerivesFromModuleBase(this ITypeSymbol symbol) - { - for (var bType = symbol.BaseType; bType != null; bType = bType.BaseType) - { - if (bType.MetadataName == _moduleBaseName) - return true; - } - return false; - } - } -} diff --git a/src/Discord.Net.Analyzers/docs/DNET0001.md b/src/Discord.Net.Analyzers/docs/DNET0001.md deleted file mode 100644 index 0c1b8098ff..0000000000 --- a/src/Discord.Net.Analyzers/docs/DNET0001.md +++ /dev/null @@ -1,30 +0,0 @@ -# DNET0001 - - - - - - - - - - - - - - -
TypeNameGuildAccessAnalyzer
CheckIdDNET0001
CategoryAPI Usage
- -## Cause - -A method identified as a command is accessing `Context.Guild` without the requisite precondition. - -## Rule description - -The value of `Context.Guild` is `null` if a command is invoked in a DM channel. Attempting to access -guild properties in such a case will result in a `NullReferenceException` at runtime. -This exception is entirely avoidable by using the library's provided preconditions. - -## How to fix violations - -Add the precondition `[RequireContext(ContextType.Guild)]` to the command or module class. diff --git a/test/Discord.Net.Analyzers.Tests/Discord.Net.Analyzers.Tests.csproj b/test/Discord.Net.Analyzers.Tests/Discord.Net.Analyzers.Tests.csproj deleted file mode 100644 index abb8a0f48a..0000000000 --- a/test/Discord.Net.Analyzers.Tests/Discord.Net.Analyzers.Tests.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - - net6.0 - - false - - - - - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - diff --git a/test/Discord.Net.Analyzers.Tests/GuildAccessTests.cs b/test/Discord.Net.Analyzers.Tests/GuildAccessTests.cs deleted file mode 100644 index e6a4107703..0000000000 --- a/test/Discord.Net.Analyzers.Tests/GuildAccessTests.cs +++ /dev/null @@ -1,107 +0,0 @@ -using Discord.Analyzers; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; -using System; -using TestHelper; -using Xunit; - -namespace Discord -{ - public partial class AnalyserTests - { - public class GuildAccessTests : DiagnosticVerifier - { - [Fact] - public void VerifyDiagnosticWhenLackingRequireContext() - { - string source = @"using System; -using System.Threading.Tasks; -using Discord.Commands; - -namespace Test -{ - public class TestModule : ModuleBase - { - [Command(""test"")] - public Task TestCmd() => ReplyAsync(Context.Guild.Name); - } -}"; - var expected = new DiagnosticResult() - { - Id = "DNET0001", - Locations = new[] { new DiagnosticResultLocation("Test0.cs", line: 10, column: 45) }, - Message = "Command method 'TestCmd' is accessing 'Context.Guild' but is not restricted to Guild contexts.", - Severity = DiagnosticSeverity.Warning - }; - VerifyCSharpDiagnostic(source, expected); - } - - [Fact] - public void VerifyDiagnosticWhenWrongRequireContext() - { - string source = @"using System; -using System.Threading.Tasks; -using Discord.Commands; - -namespace Test -{ - public class TestModule : ModuleBase - { - [Command(""test""), RequireContext(ContextType.Group)] - public Task TestCmd() => ReplyAsync(Context.Guild.Name); - } -}"; - var expected = new DiagnosticResult() - { - Id = "DNET0001", - Locations = new[] { new DiagnosticResultLocation("Test0.cs", line: 10, column: 45) }, - Message = "Command method 'TestCmd' is accessing 'Context.Guild' but is not restricted to Guild contexts.", - Severity = DiagnosticSeverity.Warning - }; - VerifyCSharpDiagnostic(source, expected); - } - - [Fact] - public void VerifyNoDiagnosticWhenRequireContextOnMethod() - { - string source = @"using System; -using System.Threading.Tasks; -using Discord.Commands; - -namespace Test -{ - public class TestModule : ModuleBase - { - [Command(""test""), RequireContext(ContextType.Guild)] - public Task TestCmd() => ReplyAsync(Context.Guild.Name); - } -}"; - - VerifyCSharpDiagnostic(source, Array.Empty()); - } - - [Fact] - public void VerifyNoDiagnosticWhenRequireContextOnClass() - { - string source = @"using System; -using System.Threading.Tasks; -using Discord.Commands; - -namespace Test -{ - [RequireContext(ContextType.Guild)] - public class TestModule : ModuleBase - { - [Command(""test"")] - public Task TestCmd() => ReplyAsync(Context.Guild.Name); - } -}"; - - VerifyCSharpDiagnostic(source, Array.Empty()); - } - - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - => new GuildAccessAnalyzer(); - } - } -} diff --git a/test/Discord.Net.Analyzers.Tests/Helpers/CodeFixVerifier.Helper.cs b/test/Discord.Net.Analyzers.Tests/Helpers/CodeFixVerifier.Helper.cs deleted file mode 100644 index 1631b843ca..0000000000 --- a/test/Discord.Net.Analyzers.Tests/Helpers/CodeFixVerifier.Helper.cs +++ /dev/null @@ -1,85 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Simplification; -using System.Collections.Generic; -using System.Linq; -using System.Threading; - -namespace TestHelper -{ - /// - /// Diagnostic Producer class with extra methods dealing with applying codefixes - /// All methods are static - /// - public abstract partial class CodeFixVerifier : DiagnosticVerifier - { - /// - /// Apply the inputted CodeAction to the inputted document. - /// Meant to be used to apply codefixes. - /// - /// The Document to apply the fix on. - /// A CodeAction that will be applied to the Document. - /// A Document with the changes from the CodeAction - private static Document ApplyFix(Document document, CodeAction codeAction) - { - var operations = codeAction.GetOperationsAsync(CancellationToken.None).Result; - var solution = operations.OfType().Single().ChangedSolution; - return solution.GetDocument(document.Id); - } - - /// - /// Compare two collections of Diagnostics,and return a list of any new diagnostics that appear only in the second collection. - /// Note: Considers Diagnostics to be the same if they have the same Ids. In the case of multiple diagnostics with the same Id in a row, - /// this method may not necessarily return the new one. - /// - /// The Diagnostics that existed in the code before the CodeFix was applied. - /// The Diagnostics that exist in the code after the CodeFix was applied. - /// A list of Diagnostics that only surfaced in the code after the CodeFix was applied - private static IEnumerable GetNewDiagnostics(IEnumerable diagnostics, IEnumerable newDiagnostics) - { - var oldArray = diagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray(); - var newArray = newDiagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray(); - - int oldIndex = 0; - int newIndex = 0; - - while (newIndex < newArray.Length) - { - if (oldIndex < oldArray.Length && oldArray[oldIndex].Id == newArray[newIndex].Id) - { - ++oldIndex; - ++newIndex; - } - else - { - yield return newArray[newIndex++]; - } - } - } - - /// - /// Get the existing compiler diagnostics on the inputted document. - /// - /// The Document to run the compiler diagnostic analyzers on. - /// The compiler diagnostics that were found in the code - private static IEnumerable GetCompilerDiagnostics(Document document) - { - return document.GetSemanticModelAsync().Result.GetDiagnostics(); - } - - /// - /// Given a document, turn it into a string based on the syntax root - /// - /// The Document to be converted to a string. - /// A string containing the syntax of the Document after formatting - private static string GetStringFromDocument(Document document) - { - var simplifiedDoc = Simplifier.ReduceAsync(document, Simplifier.Annotation).Result; - var root = simplifiedDoc.GetSyntaxRootAsync().Result; - root = Formatter.Format(root, Formatter.Annotation, simplifiedDoc.Project.Solution.Workspace); - return root.GetText().ToString(); - } - } -} - diff --git a/test/Discord.Net.Analyzers.Tests/Helpers/DiagnosticResult.cs b/test/Discord.Net.Analyzers.Tests/Helpers/DiagnosticResult.cs deleted file mode 100644 index 8009f8a23e..0000000000 --- a/test/Discord.Net.Analyzers.Tests/Helpers/DiagnosticResult.cs +++ /dev/null @@ -1,87 +0,0 @@ -using Microsoft.CodeAnalysis; -using System; - -namespace TestHelper -{ - /// - /// Location where the diagnostic appears, as determined by path, line number, and column number. - /// - public struct DiagnosticResultLocation - { - public DiagnosticResultLocation(string path, int line, int column) - { - if (line < -1) - { - throw new ArgumentOutOfRangeException(nameof(line), "line must be >= -1"); - } - - if (column < -1) - { - throw new ArgumentOutOfRangeException(nameof(column), "column must be >= -1"); - } - - Path = path; - Line = line; - Column = column; - } - - public string Path { get; } - public int Line { get; } - public int Column { get; } - } - - /// - /// Struct that stores information about a Diagnostic appearing in a source - /// - public struct DiagnosticResult - { - private DiagnosticResultLocation[] locations; - - public DiagnosticResultLocation[] Locations - { - get - { - if (locations == null) - { - locations = new DiagnosticResultLocation[] { }; - } - return locations; - } - - set - { - locations = value; - } - } - - public DiagnosticSeverity Severity { get; set; } - - public string Id { get; set; } - - public string Message { get; set; } - - public string Path - { - get - { - return Locations.Length > 0 ? Locations[0].Path : ""; - } - } - - public int Line - { - get - { - return Locations.Length > 0 ? Locations[0].Line : -1; - } - } - - public int Column - { - get - { - return Locations.Length > 0 ? Locations[0].Column : -1; - } - } - } -} diff --git a/test/Discord.Net.Analyzers.Tests/Helpers/DiagnosticVerifier.Helper.cs b/test/Discord.Net.Analyzers.Tests/Helpers/DiagnosticVerifier.Helper.cs deleted file mode 100644 index 8bf930e680..0000000000 --- a/test/Discord.Net.Analyzers.Tests/Helpers/DiagnosticVerifier.Helper.cs +++ /dev/null @@ -1,205 +0,0 @@ -using Discord.Commands; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Text; -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Reflection; - -namespace TestHelper -{ - /// - /// Class for turning strings into documents and getting the diagnostics on them - /// All methods are static - /// - public abstract partial class DiagnosticVerifier - { - private static readonly MetadataReference CorlibReference = MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location); - private static readonly MetadataReference SystemCoreReference = MetadataReference.CreateFromFile(typeof(Enumerable).GetTypeInfo().Assembly.Location); - private static readonly MetadataReference CSharpSymbolsReference = MetadataReference.CreateFromFile(typeof(CSharpCompilation).GetTypeInfo().Assembly.Location); - private static readonly MetadataReference CodeAnalysisReference = MetadataReference.CreateFromFile(typeof(Compilation).GetTypeInfo().Assembly.Location); - //private static readonly MetadataReference DiscordNetReference = MetadataReference.CreateFromFile(typeof(IDiscordClient).GetTypeInfo().Assembly.Location); - //private static readonly MetadataReference DiscordCommandsReference = MetadataReference.CreateFromFile(typeof(CommandAttribute).GetTypeInfo().Assembly.Location); - private static readonly Assembly DiscordCommandsAssembly = typeof(CommandAttribute).GetTypeInfo().Assembly; - - internal static string DefaultFilePathPrefix = "Test"; - internal static string CSharpDefaultFileExt = "cs"; - internal static string VisualBasicDefaultExt = "vb"; - internal static string TestProjectName = "TestProject"; - - #region Get Diagnostics - - /// - /// Given classes in the form of strings, their language, and an IDiagnosticAnalyzer to apply to it, return the diagnostics found in the string after converting it to a document. - /// - /// Classes in the form of strings. - /// The language the source classes are in. - /// The analyzer to be run on the sources. - /// An IEnumerable of Diagnostics that surfaced in the source code, sorted by Location - private static Diagnostic[] GetSortedDiagnostics(string[] sources, string language, DiagnosticAnalyzer analyzer) - { - return GetSortedDiagnosticsFromDocuments(analyzer, GetDocuments(sources, language)); - } - - /// - /// Given an analyzer and a document to apply it to, run the analyzer and gather an array of diagnostics found in it. - /// The returned diagnostics are then ordered by location in the source document. - /// - /// The analyzer to run on the documents. - /// The Documents that the analyzer will be run on. - /// An IEnumerable of Diagnostics that surfaced in the source code, sorted by Location - protected static Diagnostic[] GetSortedDiagnosticsFromDocuments(DiagnosticAnalyzer analyzer, Document[] documents) - { - var projects = new HashSet(); - foreach (var document in documents) - { - projects.Add(document.Project); - } - - var diagnostics = new List(); - foreach (var project in projects) - { - var compilationWithAnalyzers = project.GetCompilationAsync().Result.WithAnalyzers(ImmutableArray.Create(analyzer)); - var diags = compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync().Result; - foreach (var diag in diags) - { - if (diag.Location == Location.None || diag.Location.IsInMetadata) - { - diagnostics.Add(diag); - } - else - { - for (int i = 0; i < documents.Length; i++) - { - var document = documents[i]; - var tree = document.GetSyntaxTreeAsync().Result; - if (tree == diag.Location.SourceTree) - { - diagnostics.Add(diag); - } - } - } - } - } - - var results = SortDiagnostics(diagnostics); - diagnostics.Clear(); - return results; - } - - /// - /// Sort diagnostics by location in source document - /// - /// The list of Diagnostics to be sorted. - /// An IEnumerable containing the Diagnostics in order of Location - private static Diagnostic[] SortDiagnostics(IEnumerable diagnostics) - { - return diagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray(); - } - - #endregion - - #region Set up compilation and documents - /// - /// Given an array of strings as sources and a language, turn them into a project and return the documents and spans of it. - /// - /// Classes in the form of strings. - /// The language the source code is in. - /// A Tuple containing the Documents produced from the sources and their TextSpans if relevant - private static Document[] GetDocuments(string[] sources, string language) - { - if (language != LanguageNames.CSharp && language != LanguageNames.VisualBasic) - { - throw new ArgumentException("Unsupported Language"); - } - - var project = CreateProject(sources, language); - var documents = project.Documents.ToArray(); - - if (sources.Length != documents.Length) - { - throw new Exception("Amount of sources did not match amount of Documents created"); - } - - return documents; - } - - /// - /// Create a Document from a string through creating a project that contains it. - /// - /// Classes in the form of a string. - /// The language the source code is in. - /// A Document created from the source string - protected static Document CreateDocument(string source, string language = LanguageNames.CSharp) - { - return CreateProject(new[] { source }, language).Documents.First(); - } - - /// - /// Create a project using the inputted strings as sources. - /// - /// Classes in the form of strings. - /// The language the source code is in. - /// A Project created out of the Documents created from the source strings - private static Project CreateProject(string[] sources, string language = LanguageNames.CSharp) - { - string fileNamePrefix = DefaultFilePathPrefix; - string fileExt = language == LanguageNames.CSharp ? CSharpDefaultFileExt : VisualBasicDefaultExt; - - var projectId = ProjectId.CreateNewId(debugName: TestProjectName); - - var solution = new AdhocWorkspace() - .CurrentSolution - .AddProject(projectId, TestProjectName, TestProjectName, language) - .AddMetadataReference(projectId, CorlibReference) - .AddMetadataReference(projectId, SystemCoreReference) - .AddMetadataReference(projectId, CSharpSymbolsReference) - .AddMetadataReference(projectId, CodeAnalysisReference) - .AddMetadataReferences(projectId, Transitive(DiscordCommandsAssembly)); - - int count = 0; - foreach (var source in sources) - { - var newFileName = fileNamePrefix + count + "." + fileExt; - var documentId = DocumentId.CreateNewId(projectId, debugName: newFileName); - solution = solution.AddDocument(documentId, newFileName, SourceText.From(source)); - count++; - } - return solution.GetProject(projectId); - } - #endregion - - /// - /// Get the for and all assemblies referenced by - /// - /// The assembly. - /// s. - private static IEnumerable Transitive(Assembly assembly) - { - foreach (var a in RecursiveReferencedAssemblies(assembly)) - { - yield return MetadataReference.CreateFromFile(a.Location); - } - } - - private static HashSet RecursiveReferencedAssemblies(Assembly a, HashSet assemblies = null) - { - assemblies ??= new HashSet(); - if (assemblies.Add(a)) - { - foreach (var referencedAssemblyName in a.GetReferencedAssemblies()) - { - var referencedAssembly = AppDomain.CurrentDomain.GetAssemblies() - .SingleOrDefault(x => x.GetName() == referencedAssemblyName) ?? - Assembly.Load(referencedAssemblyName); - RecursiveReferencedAssemblies(referencedAssembly, assemblies); - } - } - - return assemblies; - } - } -} diff --git a/test/Discord.Net.Analyzers.Tests/Verifiers/CodeFixVerifier.cs b/test/Discord.Net.Analyzers.Tests/Verifiers/CodeFixVerifier.cs deleted file mode 100644 index 570f8b4d06..0000000000 --- a/test/Discord.Net.Analyzers.Tests/Verifiers/CodeFixVerifier.cs +++ /dev/null @@ -1,128 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Formatting; -//using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using Xunit; - -namespace TestHelper -{ - /// - /// Superclass of all Unit tests made for diagnostics with codefixes. - /// Contains methods used to verify correctness of codefixes - /// - public abstract partial class CodeFixVerifier : DiagnosticVerifier - { - /// - /// Returns the codefix being tested (C#) - to be implemented in non-abstract class - /// - /// The CodeFixProvider to be used for CSharp code - protected virtual CodeFixProvider GetCSharpCodeFixProvider() - { - return null; - } - - /// - /// Returns the codefix being tested (VB) - to be implemented in non-abstract class - /// - /// The CodeFixProvider to be used for VisualBasic code - protected virtual CodeFixProvider GetBasicCodeFixProvider() - { - return null; - } - - /// - /// Called to test a C# codefix when applied on the inputted string as a source - /// - /// A class in the form of a string before the CodeFix was applied to it. - /// A class in the form of a string after the CodeFix was applied to it. - /// Index determining which codefix to apply if there are multiple. - /// A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied. - protected void VerifyCSharpFix(string oldSource, string newSource, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false) - { - VerifyFix(LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), GetCSharpCodeFixProvider(), oldSource, newSource, codeFixIndex, allowNewCompilerDiagnostics); - } - - /// - /// Called to test a VB codefix when applied on the inputted string as a source - /// - /// A class in the form of a string before the CodeFix was applied to it. - /// A class in the form of a string after the CodeFix was applied to it. - /// Index determining which codefix to apply if there are multiple. - /// A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied. - protected void VerifyBasicFix(string oldSource, string newSource, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false) - { - VerifyFix(LanguageNames.VisualBasic, GetBasicDiagnosticAnalyzer(), GetBasicCodeFixProvider(), oldSource, newSource, codeFixIndex, allowNewCompilerDiagnostics); - } - - /// - /// General verifier for codefixes. - /// Creates a Document from the source string, then gets diagnostics on it and applies the relevant codefixes. - /// Then gets the string after the codefix is applied and compares it with the expected result. - /// Note: If any codefix causes new diagnostics to show up, the test fails unless allowNewCompilerDiagnostics is set to true. - /// - /// The language the source code is in. - /// The analyzer to be applied to the source code. - /// The codefix to be applied to the code wherever the relevant Diagnostic is found. - /// A class in the form of a string before the CodeFix was applied to it. - /// A class in the form of a string after the CodeFix was applied to it. - /// Index determining which codefix to apply if there are multiple. - /// A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied. - private void VerifyFix(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string oldSource, string newSource, int? codeFixIndex, bool allowNewCompilerDiagnostics) - { - var document = CreateDocument(oldSource, language); - var analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document }); - var compilerDiagnostics = GetCompilerDiagnostics(document); - var attempts = analyzerDiagnostics.Length; - - for (int i = 0; i < attempts; ++i) - { - var actions = new List(); - var context = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a), CancellationToken.None); - codeFixProvider.RegisterCodeFixesAsync(context).Wait(); - - if (!actions.Any()) - { - break; - } - - if (codeFixIndex != null) - { - document = ApplyFix(document, actions.ElementAt((int)codeFixIndex)); - break; - } - - document = ApplyFix(document, actions.ElementAt(0)); - analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document }); - - var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document)); - - //check if applying the code fix introduced any new compiler diagnostics - if (!allowNewCompilerDiagnostics && newCompilerDiagnostics.Any()) - { - // Format and get the compiler diagnostics again so that the locations make sense in the output - document = document.WithSyntaxRoot(Formatter.Format(document.GetSyntaxRootAsync().Result, Formatter.Annotation, document.Project.Solution.Workspace)); - newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document)); - - Assert.Fail(string.Format("Fix introduced new compiler diagnostics:\r\n{0}\r\n\r\nNew document:\r\n{1}\r\n", - string.Join("\r\n", newCompilerDiagnostics.Select(d => d.ToString())), - document.GetSyntaxRootAsync().Result.ToFullString())); - } - - //check if there are analyzer diagnostics left after the code fix - if (!analyzerDiagnostics.Any()) - { - break; - } - } - - //after applying all of the code fixes, compare the resulting string to the inputted one - var actual = GetStringFromDocument(document); - Assert.Equal(newSource, actual); - } - } -} diff --git a/test/Discord.Net.Analyzers.Tests/Verifiers/DiagnosticVerifier.cs b/test/Discord.Net.Analyzers.Tests/Verifiers/DiagnosticVerifier.cs deleted file mode 100644 index 3f64c3a593..0000000000 --- a/test/Discord.Net.Analyzers.Tests/Verifiers/DiagnosticVerifier.cs +++ /dev/null @@ -1,262 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; -using System.Collections.Generic; -using System.Linq; -using System.Text; -//using Microsoft.VisualStudio.TestTools.UnitTesting; -using Xunit; - -namespace TestHelper -{ - /// - /// Superclass of all Unit Tests for DiagnosticAnalyzers - /// - public abstract partial class DiagnosticVerifier - { - #region To be implemented by Test classes - /// - /// Get the CSharp analyzer being tested - to be implemented in non-abstract class - /// - protected virtual DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return null; - } - - /// - /// Get the Visual Basic analyzer being tested (C#) - to be implemented in non-abstract class - /// - protected virtual DiagnosticAnalyzer GetBasicDiagnosticAnalyzer() - { - return null; - } - #endregion - - #region Verifier wrappers - - /// - /// Called to test a C# DiagnosticAnalyzer when applied on the single inputted string as a source - /// Note: input a DiagnosticResult for each Diagnostic expected - /// - /// A class in the form of a string to run the analyzer on. - /// DiagnosticResults that should appear after the analyzer is run on the source. - protected void VerifyCSharpDiagnostic(string source, params DiagnosticResult[] expected) - { - VerifyDiagnostics(new[] { source }, LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), expected); - } - - /// - /// Called to test a VB DiagnosticAnalyzer when applied on the single inputted string as a source - /// Note: input a DiagnosticResult for each Diagnostic expected - /// - /// A class in the form of a string to run the analyzer on. - /// DiagnosticResults that should appear after the analyzer is run on the source. - protected void VerifyBasicDiagnostic(string source, params DiagnosticResult[] expected) - { - VerifyDiagnostics(new[] { source }, LanguageNames.VisualBasic, GetBasicDiagnosticAnalyzer(), expected); - } - - /// - /// Called to test a C# DiagnosticAnalyzer when applied on the inputted strings as a source - /// Note: input a DiagnosticResult for each Diagnostic expected - /// - /// An array of strings to create source documents from to run the analyzers on. - /// DiagnosticResults that should appear after the analyzer is run on the sources. - protected void VerifyCSharpDiagnostic(string[] sources, params DiagnosticResult[] expected) - { - VerifyDiagnostics(sources, LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), expected); - } - - /// - /// Called to test a VB DiagnosticAnalyzer when applied on the inputted strings as a source - /// Note: input a DiagnosticResult for each Diagnostic expected - /// - /// An array of strings to create source documents from to run the analyzers on. - /// DiagnosticResults that should appear after the analyzer is run on the sources. - protected void VerifyBasicDiagnostic(string[] sources, params DiagnosticResult[] expected) - { - VerifyDiagnostics(sources, LanguageNames.VisualBasic, GetBasicDiagnosticAnalyzer(), expected); - } - - /// - /// General method that gets a collection of actual diagnostics found in the source after the analyzer is run, - /// then verifies each of them. - /// - /// An array of strings to create source documents from to run the analyzers on. - /// The language of the classes represented by the source strings. - /// The analyzer to be run on the source code. - /// DiagnosticResults that should appear after the analyzer is run on the sources. - private void VerifyDiagnostics(string[] sources, string language, DiagnosticAnalyzer analyzer, params DiagnosticResult[] expected) - { - var diagnostics = GetSortedDiagnostics(sources, language, analyzer); - VerifyDiagnosticResults(diagnostics, analyzer, expected); - } - - #endregion - - #region Actual comparisons and verifications - /// - /// Checks each of the actual Diagnostics found and compares them with the corresponding DiagnosticResult in the array of expected results. - /// Diagnostics are considered equal only if the DiagnosticResultLocation, Id, Severity, and Message of the DiagnosticResult match the actual diagnostic. - /// - /// The Diagnostics found by the compiler after running the analyzer on the source code. - /// The analyzer that was being run on the sources. - /// Diagnostic Results that should have appeared in the code. - private static void VerifyDiagnosticResults(IEnumerable actualResults, DiagnosticAnalyzer analyzer, params DiagnosticResult[] expectedResults) - { - int expectedCount = expectedResults.Length; - int actualCount = actualResults.Count(); - - if (expectedCount != actualCount) - { - string diagnosticsOutput = actualResults.Any() ? FormatDiagnostics(analyzer, actualResults.ToArray()) : " NONE."; - - Assert.Fail(string.Format("Mismatch between number of diagnostics returned, expected \"{0}\" actual \"{1}\"\r\n\r\nDiagnostics:\r\n{2}\r\n", expectedCount, actualCount, diagnosticsOutput)); - } - - for (int i = 0; i < expectedResults.Length; i++) - { - var actual = actualResults.ElementAt(i); - var expected = expectedResults[i]; - - if (expected.Line == -1 && expected.Column == -1) - { - if (actual.Location != Location.None) - { - Assert.Fail(string.Format("Expected:\nA project diagnostic with No location\nActual:\n{0}", - FormatDiagnostics(analyzer, actual))); - } - } - else - { - VerifyDiagnosticLocation(analyzer, actual, actual.Location, expected.Locations.First()); - var additionalLocations = actual.AdditionalLocations.ToArray(); - - if (additionalLocations.Length != expected.Locations.Length - 1) - { - Assert.Fail(string.Format("Expected {0} additional locations but got {1} for Diagnostic:\r\n {2}\r\n", - expected.Locations.Length - 1, additionalLocations.Length, - FormatDiagnostics(analyzer, actual))); - } - - for (int j = 0; j < additionalLocations.Length; ++j) - { - VerifyDiagnosticLocation(analyzer, actual, additionalLocations[j], expected.Locations[j + 1]); - } - } - - if (actual.Id != expected.Id) - { - Assert.Fail(string.Format("Expected diagnostic id to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", - expected.Id, actual.Id, FormatDiagnostics(analyzer, actual))); - } - - if (actual.Severity != expected.Severity) - { - Assert.Fail(string.Format("Expected diagnostic severity to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", - expected.Severity, actual.Severity, FormatDiagnostics(analyzer, actual))); - } - - if (actual.GetMessage() != expected.Message) - { - Assert.Fail(string.Format("Expected diagnostic message to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", - expected.Message, actual.GetMessage(), FormatDiagnostics(analyzer, actual))); - } - } - } - - /// - /// Helper method to VerifyDiagnosticResult that checks the location of a diagnostic and compares it with the location in the expected DiagnosticResult. - /// - /// The analyzer that was being run on the sources. - /// The diagnostic that was found in the code. - /// The Location of the Diagnostic found in the code. - /// The DiagnosticResultLocation that should have been found. - private static void VerifyDiagnosticLocation(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Location actual, DiagnosticResultLocation expected) - { - var actualSpan = actual.GetLineSpan(); - - Assert.True(actualSpan.Path == expected.Path || (actualSpan.Path != null && actualSpan.Path.Contains("Test0.") && expected.Path.Contains("Test.")), - string.Format("Expected diagnostic to be in file \"{0}\" was actually in file \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", - expected.Path, actualSpan.Path, FormatDiagnostics(analyzer, diagnostic))); - - var actualLinePosition = actualSpan.StartLinePosition; - - // Only check line position if there is an actual line in the real diagnostic - if (actualLinePosition.Line > 0) - { - if (actualLinePosition.Line + 1 != expected.Line) - { - Assert.Fail(string.Format("Expected diagnostic to be on line \"{0}\" was actually on line \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", - expected.Line, actualLinePosition.Line + 1, FormatDiagnostics(analyzer, diagnostic))); - } - } - - // Only check column position if there is an actual column position in the real diagnostic - if (actualLinePosition.Character > 0) - { - if (actualLinePosition.Character + 1 != expected.Column) - { - Assert.Fail(string.Format("Expected diagnostic to start at column \"{0}\" was actually at column \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", - expected.Column, actualLinePosition.Character + 1, FormatDiagnostics(analyzer, diagnostic))); - } - } - } - #endregion - - #region Formatting Diagnostics - /// - /// Helper method to format a Diagnostic into an easily readable string - /// - /// The analyzer that this verifier tests. - /// The Diagnostics to be formatted. - /// The Diagnostics formatted as a string - private static string FormatDiagnostics(DiagnosticAnalyzer analyzer, params Diagnostic[] diagnostics) - { - var builder = new StringBuilder(); - for (int i = 0; i < diagnostics.Length; ++i) - { - builder.AppendLine("// " + diagnostics[i].ToString()); - - var analyzerType = analyzer.GetType(); - var rules = analyzer.SupportedDiagnostics; - - foreach (var rule in rules) - { - if (rule != null && rule.Id == diagnostics[i].Id) - { - var location = diagnostics[i].Location; - if (location == Location.None) - { - builder.AppendFormat("GetGlobalResult({0}.{1})", analyzerType.Name, rule.Id); - } - else - { - Assert.True(location.IsInSource, - $"Test base does not currently handle diagnostics in metadata locations. Diagnostic in metadata: {diagnostics[i]}\r\n"); - - string resultMethodName = diagnostics[i].Location.SourceTree.FilePath.EndsWith(".cs") ? "GetCSharpResultAt" : "GetBasicResultAt"; - var linePosition = diagnostics[i].Location.GetLineSpan().StartLinePosition; - - builder.AppendFormat("{0}({1}, {2}, {3}.{4})", - resultMethodName, - linePosition.Line + 1, - linePosition.Character + 1, - analyzerType.Name, - rule.Id); - } - - if (i != diagnostics.Length - 1) - { - builder.Append(','); - } - - builder.AppendLine(); - break; - } - } - } - return builder.ToString(); - } - #endregion - } -} diff --git a/test/Discord.Net.Tests.Unit/TimeSpanTypeReaderTests.cs b/test/Discord.Net.Tests.Unit/TimeSpanTypeReaderTests.cs index 4cd9cae094..f93579ac12 100644 --- a/test/Discord.Net.Tests.Unit/TimeSpanTypeReaderTests.cs +++ b/test/Discord.Net.Tests.Unit/TimeSpanTypeReaderTests.cs @@ -1,5 +1,6 @@ using Discord.Commands; using System; +using System.Threading.Tasks; using Xunit; namespace Discord @@ -38,10 +39,10 @@ public class TimeSpanTypeReaderTests [InlineData("-2m1s", true)] // tests format "-%m'm'%s's'" [InlineData("-2m", true)] // tests format "-%m'm'" [InlineData("-1s", true)] // tests format "-%s's'" - public void TestTimeSpanParse(string input, bool isNegative) + public async Task TestTimeSpanParse(string input, bool isNegative) { var reader = new TimeSpanTypeReader(); - var result = reader.ReadAsync(null, input, null).Result; + var result = await reader.ReadAsync(null, input, null); Assert.True(result.IsSuccess); var actual = (TimeSpan)result.BestMatch;