From 3e12addc02a20f40beb8becd8a08fa357fde79bd Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Mon, 3 Jun 2024 11:37:12 -0700 Subject: [PATCH] Move usings to top of file and using file-scoped namespaces (#40) * Move usings outside namespace * Rebaseline tests * Use file-scoped namespaces * Rebaseline tests --- .editorconfig | 7 +- ...rametersAndFailOnTypeMismatch.verified.txt | 12 +- .../Moq.Analyzers.Test/AbstractClassTests.cs | 42 ++- ...ts.ShouldPassIfGoodParameters.verified.txt | 4 +- .../AsAcceptOnlyInterfaceAnalyzerTests.cs | 35 +- ...ts.ShouldPassIfGoodParameters.verified.txt | 8 +- ...ureShouldMatchMockedMethodAnalyzerTests.cs | 35 +- ...uggestQuickFixIfBadParameters.verified.txt | 97 +++--- ...tureShouldMatchMockedMethodCodeFixTests.cs | 45 ++- ...ilIfClassParametersDoNotMatch.verified.txt | 14 +- ...ructorArgumentsShouldMatchAnalyzerTests.cs | 45 ++- .../Data/AbstractClass.Bad.cs | 39 ++- .../Data/AbstractClass.Good.cs | 107 +++---- .../Data/AsAcceptOnlyInterface.cs | 94 +++--- ...allbackSignatureShouldMatchMockedMethod.cs | 95 +++--- .../Data/ConstructorArgumentsShouldMatch.cs | 91 +++--- ...oConstructorArgumentsForInterfaceMock_1.cs | 57 ++-- ...oConstructorArgumentsForInterfaceMock_2.cs | 87 +++-- .../Data/NoMethodsInPropertySetup.cs | 51 ++- .../Data/NoSealedClassMocks.cs | 39 ++- .../Data/SetupOnlyForOverridableMembers.cs | 135 ++++---- .../Data/SetupShouldNotIncludeAsyncResult.cs | 49 ++- .../Helpers/CodeFixVerifier.Helper.cs | 141 ++++---- .../Helpers/CodeFixVerifier.cs | 177 +++++----- .../Helpers/DiagnosticResult.cs | 71 ++-- .../Helpers/DiagnosticResultLocation.cs | 49 ++- .../Helpers/DiagnosticVerifier.Helper.cs | 303 +++++++++--------- .../Helpers/DiagnosticVerifier.cs | 151 +++++---- .../Moq.Analyzers.Test/ModuleInitializer.cs | 19 +- ...fMockedInterfaceHasParameters.verified.txt | 16 +- ...ldPassIfCustomMockClassIsUsed.verified.txt | 12 +- ...rArgumentsForInterfaceMockAnalyzerTests.cs | 45 ++- ...opertySetupAnalyzerTests.Test.verified.txt | 4 +- .../NoMethodsInPropertySetupAnalyzerTests.cs | 35 +- ...ests.ShouldFailIfFileIsSealed.verified.txt | 8 +- .../NoSealedClassMocksAnalyzerTests.cs | 35 +- Source/Moq.Analyzers.Test/PackageTests.cs | 43 ++- ...ts.ShouldPassIfGoodParameters.verified.txt | 6 +- ...dOnlyForOverridableMembersAnalyzerTests.cs | 35 +- ...sts.ShouldPassIfSetupProperly.verified.txt | 2 +- ...houldNotIncludeAsyncResultAnalyzerTests.cs | 33 +- .../AsShouldBeUsedOnlyForInterfaceAnalyzer.cs | 65 ++-- ...ignatureShouldMatchMockedMethodAnalyzer.cs | 113 ++++--- ...SignatureShouldMatchMockedMethodCodeFix.cs | 117 ++++--- ...ConstructorArgumentsShouldMatchAnalyzer.cs | 243 +++++++------- Source/Moq.Analyzers/Diagnostics.cs | 57 ++-- Source/Moq.Analyzers/Helpers.cs | 163 +++++----- Source/Moq.Analyzers/MoqMethodDescriptor.cs | 83 +++-- ...ructorArgumentsForInterfaceMockAnalyzer.cs | 113 ++++--- .../NoMethodsInPropertySetupAnalyzer.cs | 87 +++-- .../NoSealedClassMocksAnalyzer.cs | 107 +++---- ...BeUsedOnlyForOverridableMembersAnalyzer.cs | 83 +++-- ...etupShouldNotIncludeAsyncResultAnalyzer.cs | 105 +++--- 53 files changed, 1834 insertions(+), 1875 deletions(-) diff --git a/.editorconfig b/.editorconfig index 28f73b3c..f3c393c3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -35,7 +35,7 @@ insert_final_newline = true [*.{cs,vb}] # Organize usings -dotnet_separate_import_directive_groups = true +dotnet_separate_import_directive_groups = false dotnet_sort_system_directives_first = true file_header_template = unset @@ -132,7 +132,10 @@ csharp_style_unused_value_assignment_preference = discard_variable:suggestion csharp_style_unused_value_expression_statement_preference = discard_variable:silent # 'using' directive preferences -csharp_using_directive_placement = outside_namespace:silent +csharp_using_directive_placement = outside_namespace:warning + +# namespace preferences +csharp_style_namespace_declarations = file_scoped:warning #### C# Formatting Rules #### diff --git a/Source/Moq.Analyzers.Test/AbstractClassTests.ShouldPassIfGoodParametersAndFailOnTypeMismatch.verified.txt b/Source/Moq.Analyzers.Test/AbstractClassTests.ShouldPassIfGoodParametersAndFailOnTypeMismatch.verified.txt index c98b4132..a91dc11a 100644 --- a/Source/Moq.Analyzers.Test/AbstractClassTests.ShouldPassIfGoodParametersAndFailOnTypeMismatch.verified.txt +++ b/Source/Moq.Analyzers.Test/AbstractClassTests.ShouldPassIfGoodParametersAndFailOnTypeMismatch.verified.txt @@ -1,6 +1,6 @@ Diagnostic 1 Id: Moq1002 - Location: SourceFile(Test1.cs[247..253)) + Location: SourceFile(Test1.cs[223..229)) Highlight: ("42") Lines: var mock = new Mock("42"); Severity: Warning @@ -8,7 +8,7 @@ Diagnostic 2 Id: Moq1002 - Location: SourceFile(Test1.cs[420..430)) + Location: SourceFile(Test1.cs[388..398)) Highlight: ("42", 42) Lines: var mock1 = new Mock("42", 42); Severity: Warning @@ -16,7 +16,7 @@ Diagnostic 2 Diagnostic 3 Id: Moq1002 - Location: SourceFile(Test1.cs[559..563)) + Location: SourceFile(Test1.cs[519..523)) Highlight: (42) Lines: var mock2 = new Mock(42); Severity: Warning @@ -24,7 +24,7 @@ Diagnostic 3 Diagnostic 4 Id: Moq1002 - Location: SourceFile(Test1.cs[780..786)) + Location: SourceFile(Test1.cs[720..726)) Highlight: ("42") Lines: var mock = new Mock>("42"); Severity: Warning @@ -32,7 +32,7 @@ Diagnostic 4 Diagnostic 5 Id: Moq1002 - Location: SourceFile(Test1.cs[968..978)) + Location: SourceFile(Test1.cs[900..910)) Highlight: ("42", 42) Lines: var mock1 = new Mock>("42", 42); Severity: Warning @@ -40,7 +40,7 @@ Diagnostic 5 Diagnostic 6 Id: Moq1002 - Location: SourceFile(Test1.cs[1122..1126)) + Location: SourceFile(Test1.cs[1046..1050)) Highlight: (42) Lines: var mock2 = new Mock>(42); Severity: Warning diff --git a/Source/Moq.Analyzers.Test/AbstractClassTests.cs b/Source/Moq.Analyzers.Test/AbstractClassTests.cs index 5421e867..13c87a92 100644 --- a/Source/Moq.Analyzers.Test/AbstractClassTests.cs +++ b/Source/Moq.Analyzers.Test/AbstractClassTests.cs @@ -1,29 +1,25 @@ -using Microsoft.CodeAnalysis.Diagnostics; +using System.IO; +using Microsoft.CodeAnalysis.Diagnostics; +using TestHelper; +using Xunit; -namespace Moq.Analyzers.Test -{ - using System.IO; - - using TestHelper; - - using Xunit; +namespace Moq.Analyzers.Test; - public class AbstractClassTests : DiagnosticVerifier +public class AbstractClassTests : DiagnosticVerifier +{ + [Fact] + public Task ShouldPassIfGoodParametersAndFailOnTypeMismatch() { - [Fact] - public Task ShouldPassIfGoodParametersAndFailOnTypeMismatch() - { - return Verify(VerifyCSharpDiagnostic( - [ - File.ReadAllText("Data/AbstractClass.Good.cs"), - File.ReadAllText("Data/AbstractClass.Bad.cs") - ] - )); - } + return Verify(VerifyCSharpDiagnostic( + [ + File.ReadAllText("Data/AbstractClass.Good.cs"), + File.ReadAllText("Data/AbstractClass.Bad.cs") + ] + )); + } - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new ConstructorArgumentsShouldMatchAnalyzer(); - } + protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() + { + return new ConstructorArgumentsShouldMatchAnalyzer(); } } diff --git a/Source/Moq.Analyzers.Test/AsAcceptOnlyInterfaceAnalyzerTests.ShouldPassIfGoodParameters.verified.txt b/Source/Moq.Analyzers.Test/AsAcceptOnlyInterfaceAnalyzerTests.ShouldPassIfGoodParameters.verified.txt index 31d23fd1..b61e0229 100644 --- a/Source/Moq.Analyzers.Test/AsAcceptOnlyInterfaceAnalyzerTests.ShouldPassIfGoodParameters.verified.txt +++ b/Source/Moq.Analyzers.Test/AsAcceptOnlyInterfaceAnalyzerTests.ShouldPassIfGoodParameters.verified.txt @@ -1,6 +1,6 @@ Diagnostic 1 Id: Moq1300 - Location: SourceFile(Test0.cs[1369..1384)) + Location: SourceFile(Test0.cs[1212..1227)) Highlight: BaseSampleClass Lines: mock.As(); Severity: Error @@ -8,7 +8,7 @@ Diagnostic 2 Id: Moq1300 - Location: SourceFile(Test0.cs[1534..1544)) + Location: SourceFile(Test0.cs[1357..1367)) Highlight: OtherClass Lines: mock.As(); Severity: Error diff --git a/Source/Moq.Analyzers.Test/AsAcceptOnlyInterfaceAnalyzerTests.cs b/Source/Moq.Analyzers.Test/AsAcceptOnlyInterfaceAnalyzerTests.cs index 380cd32e..9e273482 100644 --- a/Source/Moq.Analyzers.Test/AsAcceptOnlyInterfaceAnalyzerTests.cs +++ b/Source/Moq.Analyzers.Test/AsAcceptOnlyInterfaceAnalyzerTests.cs @@ -1,22 +1,21 @@ -namespace Moq.Analyzers.Test -{ - using System.IO; - using System.Threading.Tasks; - using Microsoft.CodeAnalysis.Diagnostics; - using TestHelper; - using Xunit; +using System.IO; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; +using TestHelper; +using Xunit; + +namespace Moq.Analyzers.Test; - public class AsAcceptOnlyInterfaceAnalyzerTests : DiagnosticVerifier +public class AsAcceptOnlyInterfaceAnalyzerTests : DiagnosticVerifier +{ + [Fact] + public Task ShouldPassIfGoodParameters() { - [Fact] - public Task ShouldPassIfGoodParameters() - { - return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/AsAcceptOnlyInterface.cs"))); - } + return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/AsAcceptOnlyInterface.cs"))); + } - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new AsShouldBeUsedOnlyForInterfaceAnalyzer(); - } + protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() + { + return new AsShouldBeUsedOnlyForInterfaceAnalyzer(); } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodAnalyzerTests.ShouldPassIfGoodParameters.verified.txt b/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodAnalyzerTests.ShouldPassIfGoodParameters.verified.txt index a051d3c6..3de4ad67 100644 --- a/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodAnalyzerTests.ShouldPassIfGoodParameters.verified.txt +++ b/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodAnalyzerTests.ShouldPassIfGoodParameters.verified.txt @@ -1,6 +1,6 @@ Diagnostic 1 Id: Moq1100 - Location: SourceFile(Test0.cs[1090..1097)) + Location: SourceFile(Test0.cs[1030..1037)) Highlight: (int i) Lines: mock.Setup(x => x.Do(It.IsAny())).Callback((int i) => { }); Severity: Warning @@ -8,7 +8,7 @@ Diagnostic 2 Id: Moq1100 - Location: SourceFile(Test0.cs[1170..1192)) + Location: SourceFile(Test0.cs[1106..1128)) Highlight: (string s1, string s2) Lines: mock.Setup(x => x.Do(It.IsAny())).Callback((string s1, string s2) => { }); Severity: Warning @@ -16,7 +16,7 @@ Diagnostic 2 Diagnostic 3 Id: Moq1100 - Location: SourceFile(Test0.cs[1304..1323)) + Location: SourceFile(Test0.cs[1236..1255)) Highlight: (string s1, int i1) Lines: mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback((string s1, int i1) => { }); Severity: Warning @@ -24,7 +24,7 @@ Diagnostic 3 Diagnostic 4 Id: Moq1100 - Location: SourceFile(Test0.cs[1402..1409)) + Location: SourceFile(Test0.cs[1330..1337)) Highlight: (int i) Lines: mock.Setup(x => x.Do(It.IsAny>())).Callback((int i) => { }); Severity: Warning diff --git a/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodAnalyzerTests.cs b/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodAnalyzerTests.cs index a1736720..fab69a81 100644 --- a/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodAnalyzerTests.cs +++ b/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodAnalyzerTests.cs @@ -1,22 +1,21 @@ -namespace Moq.Analyzers.Test -{ - using System.IO; - using System.Threading.Tasks; - using Microsoft.CodeAnalysis.Diagnostics; - using TestHelper; - using Xunit; +using System.IO; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; +using TestHelper; +using Xunit; + +namespace Moq.Analyzers.Test; - public class CallbackSignatureShouldMatchMockedMethodAnalyzerTests : DiagnosticVerifier +public class CallbackSignatureShouldMatchMockedMethodAnalyzerTests : DiagnosticVerifier +{ + [Fact] + public Task ShouldPassIfGoodParameters() { - [Fact] - public Task ShouldPassIfGoodParameters() - { - return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/CallbackSignatureShouldMatchMockedMethod.cs"))); - } + return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/CallbackSignatureShouldMatchMockedMethod.cs"))); + } - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new CallbackSignatureShouldMatchMockedMethodAnalyzer(); - } + protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() + { + return new CallbackSignatureShouldMatchMockedMethodAnalyzer(); } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodCodeFixTests.ShouldSuggestQuickFixIfBadParameters.verified.txt b/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodCodeFixTests.ShouldSuggestQuickFixIfBadParameters.verified.txt index fc17fa59..657e2682 100644 --- a/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodCodeFixTests.ShouldSuggestQuickFixIfBadParameters.verified.txt +++ b/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodCodeFixTests.ShouldSuggestQuickFixIfBadParameters.verified.txt @@ -1,4 +1,8 @@ -#pragma warning disable SA1402 // File may only contain a single class +using System; +using System.Collections.Generic; +using Moq; + +#pragma warning disable SA1402 // File may only contain a single class #pragma warning disable SA1502 // Element must not be on a single line #pragma warning disable SA1602 // Undocumented enum values #pragma warning disable SA1649 // File name must match first type name @@ -8,62 +12,57 @@ #pragma warning disable IDE0051 // Unused private member #pragma warning disable IDE0059 // Unnecessary value assignment #pragma warning disable IDE0060 // Unused parameter -namespace CallbackSignatureShouldMatchMockedMethod +namespace CallbackSignatureShouldMatchMockedMethod; + +internal interface IFoo { - using System; - using System.Collections.Generic; - using Moq; + int Do(string s); - internal interface IFoo - { - int Do(string s); + int Do(int i, string s, DateTime dt); - int Do(int i, string s, DateTime dt); + int Do(List l); +} - int Do(List l); +internal class MyUnitTests +{ + private void TestBadCallbacks() + { + var mock = new Mock(); + mock.Setup(x => x.Do(It.IsAny())).Callback((string s) => { }); + mock.Setup(x => x.Do(It.IsAny())).Callback((string s) => { }); + mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback((int i, string s, DateTime dt) => { }); + mock.Setup(x => x.Do(It.IsAny>())).Callback((List l) => { }); } - internal class MyUnitTests + private void TestGoodSetupAndParameterlessCallback() { - private void TestBadCallbacks() - { - var mock = new Mock(); - mock.Setup(x => x.Do(It.IsAny())).Callback((string s) => { }); - mock.Setup(x => x.Do(It.IsAny())).Callback((string s) => { }); - mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback((int i, string s, DateTime dt) => { }); - mock.Setup(x => x.Do(It.IsAny>())).Callback((List l) => { }); - } - - private void TestGoodSetupAndParameterlessCallback() - { - var mock = new Mock(); - mock.Setup(x => x.Do(It.IsAny())).Callback(() => { }); - mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback(() => { }); - mock.Setup(x => x.Do(It.IsAny>())).Callback(() => { }); - } + var mock = new Mock(); + mock.Setup(x => x.Do(It.IsAny())).Callback(() => { }); + mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback(() => { }); + mock.Setup(x => x.Do(It.IsAny>())).Callback(() => { }); + } - private void TestGoodSetupAndCallback() - { - var mock = new Mock(); - mock.Setup(x => x.Do(It.IsAny())).Callback((string s) => { }); - mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback((int i, string s, DateTime dt) => { }); - mock.Setup(x => x.Do(It.IsAny>())).Callback((List l) => { }); - } + private void TestGoodSetupAndCallback() + { + var mock = new Mock(); + mock.Setup(x => x.Do(It.IsAny())).Callback((string s) => { }); + mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback((int i, string s, DateTime dt) => { }); + mock.Setup(x => x.Do(It.IsAny>())).Callback((List l) => { }); + } - private void TestGoodSetupAndReturnsAndCallback() - { - var mock = new Mock(); - mock.Setup(x => x.Do(It.IsAny())).Returns(0).Callback((string s) => { }); - mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Returns(0).Callback((int i, string s, DateTime dt) => { }); - mock.Setup(x => x.Do(It.IsAny>())).Returns(0).Callback((List l) => { }); - } + private void TestGoodSetupAndReturnsAndCallback() + { + var mock = new Mock(); + mock.Setup(x => x.Do(It.IsAny())).Returns(0).Callback((string s) => { }); + mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Returns(0).Callback((int i, string s, DateTime dt) => { }); + mock.Setup(x => x.Do(It.IsAny>())).Returns(0).Callback((List l) => { }); + } - private void MyGoodSetupAndReturns() - { - var mock = new Mock(); - mock.Setup(x => x.Do(It.IsAny())).Returns((string s) => { return 0; }); - mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Returns((int i, string s, DateTime dt) => { return 0; }); - mock.Setup(x => x.Do(It.IsAny>())).Returns((List l) => { return 0; }); - } + private void MyGoodSetupAndReturns() + { + var mock = new Mock(); + mock.Setup(x => x.Do(It.IsAny())).Returns((string s) => { return 0; }); + mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Returns((int i, string s, DateTime dt) => { return 0; }); + mock.Setup(x => x.Do(It.IsAny>())).Returns((List l) => { return 0; }); } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodCodeFixTests.cs b/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodCodeFixTests.cs index b1b7fec7..4f5c9987 100644 --- a/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodCodeFixTests.cs +++ b/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodCodeFixTests.cs @@ -1,28 +1,27 @@ -namespace Moq.Analyzers.Test -{ - using System.IO; - using System.Threading.Tasks; - using Microsoft.CodeAnalysis.CodeFixes; - using Microsoft.CodeAnalysis.Diagnostics; - using TestHelper; - using Xunit; +using System.IO; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using TestHelper; +using Xunit; + +namespace Moq.Analyzers.Test; - public class CallbackSignatureShouldMatchMockedMethodCodeFixTests : CodeFixVerifier +public class CallbackSignatureShouldMatchMockedMethodCodeFixTests : CodeFixVerifier +{ + [Fact] + public Task ShouldSuggestQuickFixIfBadParameters() { - [Fact] - public Task ShouldSuggestQuickFixIfBadParameters() - { - return Verify(VerifyCSharpFix(File.ReadAllText("Data/CallbackSignatureShouldMatchMockedMethod.cs"))); - } + return Verify(VerifyCSharpFix(File.ReadAllText("Data/CallbackSignatureShouldMatchMockedMethod.cs"))); + } - protected override CodeFixProvider GetCSharpCodeFixProvider() - { - return new CallbackSignatureShouldMatchMockedMethodCodeFix(); - } + protected override CodeFixProvider GetCSharpCodeFixProvider() + { + return new CallbackSignatureShouldMatchMockedMethodCodeFix(); + } - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new CallbackSignatureShouldMatchMockedMethodAnalyzer(); - } + protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() + { + return new CallbackSignatureShouldMatchMockedMethodAnalyzer(); } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/ConstructorArgumentsShouldMatchAnalyzerTests.ShouldFailIfClassParametersDoNotMatch.verified.txt b/Source/Moq.Analyzers.Test/ConstructorArgumentsShouldMatchAnalyzerTests.ShouldFailIfClassParametersDoNotMatch.verified.txt index 39861618..12a11093 100644 --- a/Source/Moq.Analyzers.Test/ConstructorArgumentsShouldMatchAnalyzerTests.ShouldFailIfClassParametersDoNotMatch.verified.txt +++ b/Source/Moq.Analyzers.Test/ConstructorArgumentsShouldMatchAnalyzerTests.ShouldFailIfClassParametersDoNotMatch.verified.txt @@ -1,6 +1,6 @@ Diagnostic 1 Id: Moq1002 - Location: SourceFile(Test0.cs[1227..1236)) + Location: SourceFile(Test0.cs[1167..1176)) Highlight: (1, true) Lines: var mock1 = new Moq.Mock(1, true); Severity: Warning @@ -8,7 +8,7 @@ Diagnostic 2 Id: Moq1002 - Location: SourceFile(Test0.cs[1307..1316)) + Location: SourceFile(Test0.cs[1243..1252)) Highlight: (2, true) Lines: var mock2 = new Mock(2, true); Severity: Warning @@ -16,7 +16,7 @@ Diagnostic 2 Diagnostic 3 Id: Moq1002 - Location: SourceFile(Test0.cs[1387..1395)) + Location: SourceFile(Test0.cs[1319..1327)) Highlight: ("1", 3) Lines: var mock3 = new Mock("1", 3); Severity: Warning @@ -24,7 +24,7 @@ Diagnostic 3 Diagnostic 4 Id: Moq1002 - Location: SourceFile(Test0.cs[1466..1489)) + Location: SourceFile(Test0.cs[1394..1417)) Highlight: (new int[] { 1, 2, 3 }) Lines: var mock4 = new Mock(new int[] { 1, 2, 3 }); Severity: Warning @@ -32,7 +32,7 @@ Diagnostic 4 Diagnostic 5 Id: Moq1002 - Location: SourceFile(Test0.cs[1581..1611)) + Location: SourceFile(Test0.cs[1493..1523)) Highlight: (MockBehavior.Strict, 4, true) Lines: var mock1 = new Mock(MockBehavior.Strict, 4, true); Severity: Warning @@ -40,7 +40,7 @@ Diagnostic 5 Diagnostic 6 Id: Moq1002 - Location: SourceFile(Test0.cs[1686..1715)) + Location: SourceFile(Test0.cs[1594..1623)) Highlight: (MockBehavior.Loose, 5, true) Lines: var mock2 = new Moq.Mock(MockBehavior.Loose, 5, true); Severity: Warning @@ -48,7 +48,7 @@ Diagnostic 6 Diagnostic 7 Id: Moq1002 - Location: SourceFile(Test0.cs[1790..1818)) + Location: SourceFile(Test0.cs[1694..1722)) Highlight: (MockBehavior.Loose, "2", 6) Lines: var mock3 = new Moq.Mock(MockBehavior.Loose, "2", 6); Severity: Warning diff --git a/Source/Moq.Analyzers.Test/ConstructorArgumentsShouldMatchAnalyzerTests.cs b/Source/Moq.Analyzers.Test/ConstructorArgumentsShouldMatchAnalyzerTests.cs index fa0c6a95..de7a63d8 100644 --- a/Source/Moq.Analyzers.Test/ConstructorArgumentsShouldMatchAnalyzerTests.cs +++ b/Source/Moq.Analyzers.Test/ConstructorArgumentsShouldMatchAnalyzerTests.cs @@ -1,28 +1,27 @@ -namespace Moq.Analyzers.Test -{ - using System.IO; - using System.Threading.Tasks; - using Microsoft.CodeAnalysis.Diagnostics; - using TestHelper; - using Xunit; +using System.IO; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; +using TestHelper; +using Xunit; + +namespace Moq.Analyzers.Test; - public class ConstructorArgumentsShouldMatchAnalyzerTests : DiagnosticVerifier +public class ConstructorArgumentsShouldMatchAnalyzerTests : DiagnosticVerifier +{ + [Fact] + public Task ShouldFailIfClassParametersDoNotMatch() { - [Fact] - public Task ShouldFailIfClassParametersDoNotMatch() - { - return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/ConstructorArgumentsShouldMatch.cs"))); - } + return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/ConstructorArgumentsShouldMatch.cs"))); + } - // [Fact] - // public Task ShouldPassIfCustomMockClassIsUsed() - // { - // return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/MockInterfaceWithParametersCustomMockFile.cs"))); - // } + // [Fact] + // public Task ShouldPassIfCustomMockClassIsUsed() + // { + // return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/MockInterfaceWithParametersCustomMockFile.cs"))); + // } - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new ConstructorArgumentsShouldMatchAnalyzer(); - } + protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() + { + return new ConstructorArgumentsShouldMatchAnalyzer(); } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/Data/AbstractClass.Bad.cs b/Source/Moq.Analyzers.Test/Data/AbstractClass.Bad.cs index c45d990b..4945e7e1 100644 --- a/Source/Moq.Analyzers.Test/Data/AbstractClass.Bad.cs +++ b/Source/Moq.Analyzers.Test/Data/AbstractClass.Bad.cs @@ -1,29 +1,28 @@ -namespace Moq.Analyzers.Test.Data +namespace Moq.Analyzers.Test.Data; + +internal class MyBadUnitTests { - internal class MyBadUnitTests + private void TestBad() { - private void TestBad() - { - // The class has a ctor that takes an Int32 but passes a String - var mock = new Mock("42"); + // The class has a ctor that takes an Int32 but passes a String + var mock = new Mock("42"); - // The class has a ctor with two arguments [Int32, String], but they are passed in reverse order - var mock1 = new Mock("42", 42); + // The class has a ctor with two arguments [Int32, String], but they are passed in reverse order + var mock1 = new Mock("42", 42); - // The class has a ctor but does not take any arguments - var mock2 = new Mock(42); - } + // The class has a ctor but does not take any arguments + var mock2 = new Mock(42); + } - private void TestBadWithGeneric() - { - // The class has a constructor that takes an Int32 but passes a String - var mock = new Mock>("42"); + private void TestBadWithGeneric() + { + // The class has a constructor that takes an Int32 but passes a String + var mock = new Mock>("42"); - // The class has a ctor with two arguments [Int32, String], but they are passed in reverse order - var mock1 = new Mock>("42", 42); + // The class has a ctor with two arguments [Int32, String], but they are passed in reverse order + var mock1 = new Mock>("42", 42); - // The class has a ctor but does not take any arguments - var mock2 = new Mock>(42); - } + // The class has a ctor but does not take any arguments + var mock2 = new Mock>(42); } } diff --git a/Source/Moq.Analyzers.Test/Data/AbstractClass.Good.cs b/Source/Moq.Analyzers.Test/Data/AbstractClass.Good.cs index b1ceb47a..0b61bc3b 100644 --- a/Source/Moq.Analyzers.Test/Data/AbstractClass.Good.cs +++ b/Source/Moq.Analyzers.Test/Data/AbstractClass.Good.cs @@ -1,80 +1,79 @@ -namespace Moq.Analyzers.Test.Data +namespace Moq.Analyzers.Test.Data; + +internal abstract class AbstractGenericClassDefaultCtor { - internal abstract class AbstractGenericClassDefaultCtor + protected AbstractGenericClassDefaultCtor() { - protected AbstractGenericClassDefaultCtor() - { - } } +} - internal abstract class AbstractGenericClassWithCtor +internal abstract class AbstractGenericClassWithCtor +{ + protected AbstractGenericClassWithCtor(int a) { - protected AbstractGenericClassWithCtor(int a) - { - } + } - protected AbstractGenericClassWithCtor(int a, string b) - { - } + protected AbstractGenericClassWithCtor(int a, string b) + { } +} - internal abstract class AbstractClassDefaultCtor +internal abstract class AbstractClassDefaultCtor +{ + protected AbstractClassDefaultCtor() { - protected AbstractClassDefaultCtor() - { - } } +} - internal abstract class AbstractClassWithCtor +internal abstract class AbstractClassWithCtor +{ + protected AbstractClassWithCtor(int a) { - protected AbstractClassWithCtor(int a) - { - } + } - protected AbstractClassWithCtor(int a, string b) - { - } + protected AbstractClassWithCtor(int a, string b) + { } +} - internal class MyUnitTests +internal class MyUnitTests +{ + // Base case that we can handle abstract types + private void TestForBaseNoArgs() { - // Base case that we can handle abstract types - private void TestForBaseNoArgs() - { - var mock = new Mock(); - mock.As(); + var mock = new Mock(); + mock.As(); - var mock2 = new Mock(); - var mock3 = new Mock(MockBehavior.Default); - } + var mock2 = new Mock(); + var mock3 = new Mock(MockBehavior.Default); + } - private void TestForBaseGenericNoArgs() - { - var mock = new Mock>(); - mock.As>(); + private void TestForBaseGenericNoArgs() + { + var mock = new Mock>(); + mock.As>(); - var mock1 = new Mock>(); + var mock1 = new Mock>(); - var mock2 = new Mock>(MockBehavior.Default); - } + var mock2 = new Mock>(MockBehavior.Default); + } - // This is syntatically not allowed by C#, but you can do it with Moq - private void TestForBaseWithArgsNonePassed() - { - var mock = new Mock(); - mock.As(); - } + // This is syntatically not allowed by C#, but you can do it with Moq + private void TestForBaseWithArgsNonePassed() + { + var mock = new Mock(); + mock.As(); + } - private void TestForBaseWithArgsPassed() - { - var mock = new Mock(42); - var mock2 = new Mock(MockBehavior.Default, 42); + private void TestForBaseWithArgsPassed() + { + var mock = new Mock(42); + var mock2 = new Mock(MockBehavior.Default, 42); - var mock3 = new Mock(42, "42"); - var mock4 = new Mock(MockBehavior.Default, 42, "42"); + var mock3 = new Mock(42, "42"); + var mock4 = new Mock(MockBehavior.Default, 42, "42"); - var mock5 = new Mock>(42); - var mock6 = new Mock>(MockBehavior.Default, 42); - } + var mock5 = new Mock>(42); + var mock6 = new Mock>(MockBehavior.Default, 42); } } diff --git a/Source/Moq.Analyzers.Test/Data/AsAcceptOnlyInterface.cs b/Source/Moq.Analyzers.Test/Data/AsAcceptOnlyInterface.cs index 753ffbf6..deeff346 100644 --- a/Source/Moq.Analyzers.Test/Data/AsAcceptOnlyInterface.cs +++ b/Source/Moq.Analyzers.Test/Data/AsAcceptOnlyInterface.cs @@ -1,64 +1,64 @@ -#pragma warning disable SA1402 // File may only contain a single class +using Moq; + +#pragma warning disable SA1402 // File may only contain a single class #pragma warning disable SA1649 // File name must match first type name #pragma warning disable SA1502 // Element must not be on a single line -namespace AsAcceptOnlyInterface +namespace AsAcceptOnlyInterface; + +public interface ISampleInterface { - using Moq; - public interface ISampleInterface - { - int Calculate(int a, int b); + int Calculate(int a, int b); - int TestProperty { get; set; } - } + int TestProperty { get; set; } +} - public abstract class BaseSampleClass +public abstract class BaseSampleClass +{ + public int Calculate() { - public int Calculate() - { - return 0; - } - - public abstract int Calculate(int a, int b, int c); + return 0; } - public class SampleClass - { + public abstract int Calculate(int a, int b, int c); +} - public virtual int Calculate(int a, int b) => 0; - } +public class SampleClass +{ - public class OtherClass - { + public virtual int Calculate(int a, int b) => 0; +} - public virtual int Calculate() => 0; - } +public class OtherClass +{ + + public virtual int Calculate() => 0; +} - internal class MyUnitTests +internal class MyUnitTests +{ + private void TestOkAsForInterface() { - private void TestOkAsForInterface() - { - var mock = new Mock(); - mock.As(); - } + var mock = new Mock(); + mock.As(); + } - private void TestOkAsForInterfaceWithConfiguration() - { - var mock = new Mock(); - mock.As() - .Setup(x=>x.Calculate(It.IsAny(), It.IsAny())) - .Returns(10); - } + private void TestOkAsForInterfaceWithConfiguration() + { + var mock = new Mock(); + mock.As() + .Setup(x => x.Calculate(It.IsAny(), It.IsAny())) + .Returns(10); + } - private void TestBadAsForAbstractClass() - { - var mock = new Mock(); - mock.As(); - } + private void TestBadAsForAbstractClass() + { + var mock = new Mock(); + mock.As(); + } - private void TestBadAsForNonAbstractClass() - { - var mock = new Mock(); - mock.As(); - } + private void TestBadAsForNonAbstractClass() + { + var mock = new Mock(); + mock.As(); } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/Data/CallbackSignatureShouldMatchMockedMethod.cs b/Source/Moq.Analyzers.Test/Data/CallbackSignatureShouldMatchMockedMethod.cs index 20a40015..48bab2ba 100644 --- a/Source/Moq.Analyzers.Test/Data/CallbackSignatureShouldMatchMockedMethod.cs +++ b/Source/Moq.Analyzers.Test/Data/CallbackSignatureShouldMatchMockedMethod.cs @@ -1,3 +1,7 @@ +using System; +using System.Collections.Generic; +using Moq; + #pragma warning disable SA1402 // File may only contain a single class #pragma warning disable SA1502 // Element must not be on a single line #pragma warning disable SA1602 // Undocumented enum values @@ -8,62 +12,57 @@ #pragma warning disable IDE0051 // Unused private member #pragma warning disable IDE0059 // Unnecessary value assignment #pragma warning disable IDE0060 // Unused parameter -namespace CallbackSignatureShouldMatchMockedMethod +namespace CallbackSignatureShouldMatchMockedMethod; + +internal interface IFoo { - using System; - using System.Collections.Generic; - using Moq; + int Do(string s); - internal interface IFoo - { - int Do(string s); + int Do(int i, string s, DateTime dt); - int Do(int i, string s, DateTime dt); + int Do(List l); +} - int Do(List l); +internal class MyUnitTests +{ + private void TestBadCallbacks() + { + var mock = new Mock(); + mock.Setup(x => x.Do(It.IsAny())).Callback((int i) => { }); + mock.Setup(x => x.Do(It.IsAny())).Callback((string s1, string s2) => { }); + mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback((string s1, int i1) => { }); + mock.Setup(x => x.Do(It.IsAny>())).Callback((int i) => { }); } - internal class MyUnitTests + private void TestGoodSetupAndParameterlessCallback() { - private void TestBadCallbacks() - { - var mock = new Mock(); - mock.Setup(x => x.Do(It.IsAny())).Callback((int i) => { }); - mock.Setup(x => x.Do(It.IsAny())).Callback((string s1, string s2) => { }); - mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback((string s1, int i1) => { }); - mock.Setup(x => x.Do(It.IsAny>())).Callback((int i) => { }); - } - - private void TestGoodSetupAndParameterlessCallback() - { - var mock = new Mock(); - mock.Setup(x => x.Do(It.IsAny())).Callback(() => { }); - mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback(() => { }); - mock.Setup(x => x.Do(It.IsAny>())).Callback(() => { }); - } + var mock = new Mock(); + mock.Setup(x => x.Do(It.IsAny())).Callback(() => { }); + mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback(() => { }); + mock.Setup(x => x.Do(It.IsAny>())).Callback(() => { }); + } - private void TestGoodSetupAndCallback() - { - var mock = new Mock(); - mock.Setup(x => x.Do(It.IsAny())).Callback((string s) => { }); - mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback((int i, string s, DateTime dt) => { }); - mock.Setup(x => x.Do(It.IsAny>())).Callback((List l) => { }); - } + private void TestGoodSetupAndCallback() + { + var mock = new Mock(); + mock.Setup(x => x.Do(It.IsAny())).Callback((string s) => { }); + mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback((int i, string s, DateTime dt) => { }); + mock.Setup(x => x.Do(It.IsAny>())).Callback((List l) => { }); + } - private void TestGoodSetupAndReturnsAndCallback() - { - var mock = new Mock(); - mock.Setup(x => x.Do(It.IsAny())).Returns(0).Callback((string s) => { }); - mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Returns(0).Callback((int i, string s, DateTime dt) => { }); - mock.Setup(x => x.Do(It.IsAny>())).Returns(0).Callback((List l) => { }); - } + private void TestGoodSetupAndReturnsAndCallback() + { + var mock = new Mock(); + mock.Setup(x => x.Do(It.IsAny())).Returns(0).Callback((string s) => { }); + mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Returns(0).Callback((int i, string s, DateTime dt) => { }); + mock.Setup(x => x.Do(It.IsAny>())).Returns(0).Callback((List l) => { }); + } - private void MyGoodSetupAndReturns() - { - var mock = new Mock(); - mock.Setup(x => x.Do(It.IsAny())).Returns((string s) => { return 0; }); - mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Returns((int i, string s, DateTime dt) => { return 0; }); - mock.Setup(x => x.Do(It.IsAny>())).Returns((List l) => { return 0; }); - } + private void MyGoodSetupAndReturns() + { + var mock = new Mock(); + mock.Setup(x => x.Do(It.IsAny())).Returns((string s) => { return 0; }); + mock.Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Returns((int i, string s, DateTime dt) => { return 0; }); + mock.Setup(x => x.Do(It.IsAny>())).Returns((List l) => { return 0; }); } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/Data/ConstructorArgumentsShouldMatch.cs b/Source/Moq.Analyzers.Test/Data/ConstructorArgumentsShouldMatch.cs index 9a2d70d6..27c782e3 100644 --- a/Source/Moq.Analyzers.Test/Data/ConstructorArgumentsShouldMatch.cs +++ b/Source/Moq.Analyzers.Test/Data/ConstructorArgumentsShouldMatch.cs @@ -1,3 +1,7 @@ +using System; +using System.Collections.Generic; +using Moq; + #pragma warning disable SA1402 // File may only contain a single class #pragma warning disable SA1502 // Element must not be on a single line #pragma warning disable SA1602 // Undocumented enum values @@ -8,64 +12,59 @@ #pragma warning disable IDE0051 // Unused private member #pragma warning disable IDE0059 // Unnecessary value assignment #pragma warning disable IDE0060 // Unused parameter -namespace ConstructorArgumentsShouldMatch -{ - using System; - using System.Collections.Generic; - using Moq; +namespace ConstructorArgumentsShouldMatch; #pragma warning disable SA1402 // File may only contain a single class - internal class Foo - { - public Foo(string s) { } +internal class Foo +{ + public Foo(string s) { } - public Foo(bool b, int i) { } + public Foo(bool b, int i) { } - public Foo(params DateTime[] dates) { } + public Foo(params DateTime[] dates) { } - public Foo(List l, string s = "A") { } - } + public Foo(List l, string s = "A") { } +} - internal class MyUnitTests +internal class MyUnitTests #pragma warning restore SA1402 // File may only contain a single class +{ + private void TestBad() { - private void TestBad() - { - var mock1 = new Moq.Mock(1, true); - var mock2 = new Mock(2, true); - var mock3 = new Mock("1", 3); - var mock4 = new Mock(new int[] { 1, 2, 3 }); - } + var mock1 = new Moq.Mock(1, true); + var mock2 = new Mock(2, true); + var mock3 = new Mock("1", 3); + var mock4 = new Mock(new int[] { 1, 2, 3 }); + } - private void TestBad2() - { - var mock1 = new Mock(MockBehavior.Strict, 4, true); - var mock2 = new Moq.Mock(MockBehavior.Loose, 5, true); - var mock3 = new Moq.Mock(MockBehavior.Loose, "2", 6); - } + private void TestBad2() + { + var mock1 = new Mock(MockBehavior.Strict, 4, true); + var mock2 = new Moq.Mock(MockBehavior.Loose, 5, true); + var mock3 = new Moq.Mock(MockBehavior.Loose, "2", 6); + } - private void TestGood1() - { - var mock1 = new Moq.Mock(MockBehavior.Default); - var mock2 = new Mock(MockBehavior.Strict); - var mock3 = new Mock(MockBehavior.Loose); - var mock4 = new Moq.Mock(MockBehavior.Default); + private void TestGood1() + { + var mock1 = new Moq.Mock(MockBehavior.Default); + var mock2 = new Mock(MockBehavior.Strict); + var mock3 = new Mock(MockBehavior.Loose); + var mock4 = new Moq.Mock(MockBehavior.Default); - var mock5 = new Mock("3"); - var mock6 = new Moq.Mock("4"); - var mock7 = new Moq.Mock(Moq.MockBehavior.Default, "5"); - var mock8 = new Mock(Moq.MockBehavior.Default, "6"); + var mock5 = new Mock("3"); + var mock6 = new Moq.Mock("4"); + var mock7 = new Moq.Mock(Moq.MockBehavior.Default, "5"); + var mock8 = new Mock(Moq.MockBehavior.Default, "6"); - var mock9 = new Moq.Mock(false, 0); - var mock10 = new Moq.Mock(Moq.MockBehavior.Default, true, 1); + var mock9 = new Moq.Mock(false, 0); + var mock10 = new Moq.Mock(Moq.MockBehavior.Default, true, 1); - var mock11 = new Mock(DateTime.Now, DateTime.Now); - var mock12 = new Mock(MockBehavior.Default, DateTime.Now, DateTime.Now); + var mock11 = new Mock(DateTime.Now, DateTime.Now); + var mock12 = new Mock(MockBehavior.Default, DateTime.Now, DateTime.Now); - var mock13 = new Mock(new List(), "7"); - var mock14 = new Mock(new List()); - var mock15 = new Mock(MockBehavior.Default, new List(), "8"); - var mock16 = new Mock(MockBehavior.Default, new List()); - } + var mock13 = new Mock(new List(), "7"); + var mock14 = new Mock(new List()); + var mock15 = new Mock(MockBehavior.Default, new List(), "8"); + var mock16 = new Mock(MockBehavior.Default, new List()); } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/Data/NoConstructorArgumentsForInterfaceMock_1.cs b/Source/Moq.Analyzers.Test/Data/NoConstructorArgumentsForInterfaceMock_1.cs index 041ed9b8..656ef2fc 100644 --- a/Source/Moq.Analyzers.Test/Data/NoConstructorArgumentsForInterfaceMock_1.cs +++ b/Source/Moq.Analyzers.Test/Data/NoConstructorArgumentsForInterfaceMock_1.cs @@ -1,3 +1,5 @@ +using Moq; + #pragma warning disable SA1402 // File may only contain a single class #pragma warning disable SA1502 // Element must not be on a single line #pragma warning disable SA1602 // Undocumented enum values @@ -7,40 +9,37 @@ #pragma warning disable IDE0051 // Unused private member #pragma warning disable IDE0059 // Unnecessary value assignment #pragma warning disable IDE0060 // Unused parameter -namespace NoConstructorArgumentsForInterfaceMock_1 +namespace NoConstructorArgumentsForInterfaceMock_1; + +internal interface IMyService { - using Moq; + void Do(string s); +} - internal interface IMyService +internal class MyUnitTests +{ + private void TestBad() { - void Do(string s); + var mock1 = new Moq.Mock(25, true); + var mock2 = new Mock("123"); + var mock3 = new Mock(25, true); + var mock4 = new Moq.Mock("123"); } - internal class MyUnitTests + private void TestBad2() { - private void TestBad() - { - var mock1 = new Moq.Mock(25, true); - var mock2 = new Mock("123"); - var mock3 = new Mock(25, true); - var mock4 = new Moq.Mock("123"); - } - - private void TestBad2() - { - var mock1 = new Moq.Mock(Moq.MockBehavior.Default, "123"); - var mock2 = new Mock(MockBehavior.Strict, 25, true); - var mock3 = new Mock(Moq.MockBehavior.Default, "123"); - var mock4 = new Moq.Mock(MockBehavior.Loose, 25, true); - } + var mock1 = new Moq.Mock(Moq.MockBehavior.Default, "123"); + var mock2 = new Mock(MockBehavior.Strict, 25, true); + var mock3 = new Mock(Moq.MockBehavior.Default, "123"); + var mock4 = new Moq.Mock(MockBehavior.Loose, 25, true); + } - private void TestGood1() - { - var mock1 = new Moq.Mock(); - var mock2 = new Moq.Mock(MockBehavior.Default); - var mock3 = new Mock(MockBehavior.Strict); - var mock4 = new Mock(MockBehavior.Loose); - var mock5 = new Moq.Mock(MockBehavior.Default); - } + private void TestGood1() + { + var mock1 = new Moq.Mock(); + var mock2 = new Moq.Mock(MockBehavior.Default); + var mock3 = new Mock(MockBehavior.Strict); + var mock4 = new Mock(MockBehavior.Loose); + var mock5 = new Moq.Mock(MockBehavior.Default); } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/Data/NoConstructorArgumentsForInterfaceMock_2.cs b/Source/Moq.Analyzers.Test/Data/NoConstructorArgumentsForInterfaceMock_2.cs index ee42ce88..65c9cbca 100644 --- a/Source/Moq.Analyzers.Test/Data/NoConstructorArgumentsForInterfaceMock_2.cs +++ b/Source/Moq.Analyzers.Test/Data/NoConstructorArgumentsForInterfaceMock_2.cs @@ -7,58 +7,57 @@ #pragma warning disable IDE0051 // Unused private member #pragma warning disable IDE0059 // Unnecessary value assignment #pragma warning disable IDE0060 // Unused parameter -namespace NoConstructorArgumentsForInterfaceMock_2 +namespace NoConstructorArgumentsForInterfaceMock_2; + +public enum MockBehavior { - public enum MockBehavior - { - Default, - Strict, - Loose, - } + Default, + Strict, + Loose, +} - internal interface IMyService - { - void Do(string s); - } +internal interface IMyService +{ + void Do(string s); +} - public class Mock - where T : class - { - public Mock() { } +public class Mock + where T : class +{ + public Mock() { } - public Mock(params object[] ar) { } + public Mock(params object[] ar) { } - public Mock(MockBehavior behavior) { } + public Mock(MockBehavior behavior) { } - public Mock(MockBehavior behavior, params object[] args) { } - } + public Mock(MockBehavior behavior, params object[] args) { } +} - internal class MyUnitTests +internal class MyUnitTests +{ + private void TestRealMoqWithBadParameters() { - private void TestRealMoqWithBadParameters() - { - var mock1 = new Moq.Mock(1, true); - var mock2 = new Moq.Mock("2"); - var mock3 = new Moq.Mock(Moq.MockBehavior.Default, "3"); - var mock4 = new Moq.Mock(MockBehavior.Loose, 4, true); - var mock5 = new Moq.Mock(MockBehavior.Default); - var mock6 = new Moq.Mock(MockBehavior.Default); - } + var mock1 = new Moq.Mock(1, true); + var mock2 = new Moq.Mock("2"); + var mock3 = new Moq.Mock(Moq.MockBehavior.Default, "3"); + var mock4 = new Moq.Mock(MockBehavior.Loose, 4, true); + var mock5 = new Moq.Mock(MockBehavior.Default); + var mock6 = new Moq.Mock(MockBehavior.Default); + } - private void TestRealMoqWithGoodParameters() - { - var mock1 = new Moq.Mock(Moq.MockBehavior.Default); - var mock2 = new Moq.Mock(Moq.MockBehavior.Default); - } + private void TestRealMoqWithGoodParameters() + { + var mock1 = new Moq.Mock(Moq.MockBehavior.Default); + var mock2 = new Moq.Mock(Moq.MockBehavior.Default); + } - private void TestFakeMoq() - { - var mock1 = new Mock("4"); - var mock2 = new Mock(5, true); - var mock3 = new Mock(MockBehavior.Strict, 6, true); - var mock4 = new Mock(Moq.MockBehavior.Default, "5"); - var mock5 = new Mock(MockBehavior.Strict); - var mock6 = new Mock(MockBehavior.Loose); - } + private void TestFakeMoq() + { + var mock1 = new Mock("4"); + var mock2 = new Mock(5, true); + var mock3 = new Mock(MockBehavior.Strict, 6, true); + var mock4 = new Mock(Moq.MockBehavior.Default, "5"); + var mock5 = new Mock(MockBehavior.Strict); + var mock6 = new Mock(MockBehavior.Loose); } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/Data/NoMethodsInPropertySetup.cs b/Source/Moq.Analyzers.Test/Data/NoMethodsInPropertySetup.cs index af007b99..7db85124 100644 --- a/Source/Moq.Analyzers.Test/Data/NoMethodsInPropertySetup.cs +++ b/Source/Moq.Analyzers.Test/Data/NoMethodsInPropertySetup.cs @@ -1,3 +1,5 @@ +using Moq; + #pragma warning disable SA1402 // File may only contain a single class #pragma warning disable SA1502 // Element must not be on a single line #pragma warning disable SA1602 // Undocumented enum values @@ -7,38 +9,35 @@ #pragma warning disable IDE0051 // Unused private member #pragma warning disable IDE0059 // Unnecessary value assignment #pragma warning disable IDE0060 // Unused parameter -namespace NoMethodsInPropertySetup +namespace NoMethodsInPropertySetup; + +public interface IFoo { - using Moq; + string Prop1 { get; set; } - public interface IFoo - { - string Prop1 { get; set; } + string Prop2 { get; } - string Prop2 { get; } + string Prop3 { set; } - string Prop3 { set; } + string Method(); +} - string Method(); +public class MyUnitTests +{ + private void TestBad() + { + var mock = new Mock(); + mock.SetupGet(x => x.Method()); + mock.SetupSet(x => x.Method()); } - public class MyUnitTests + private void TestGood() { - private void TestBad() - { - var mock = new Mock(); - mock.SetupGet(x => x.Method()); - mock.SetupSet(x => x.Method()); - } - - private void TestGood() - { - var mock = new Mock(); - mock.SetupGet(x => x.Prop1); - mock.SetupGet(x => x.Prop2); - mock.SetupSet(x => x.Prop1 = "1"); - mock.SetupSet(x => x.Prop3 = "2"); - mock.Setup(x => x.Method()); - } + var mock = new Mock(); + mock.SetupGet(x => x.Prop1); + mock.SetupGet(x => x.Prop2); + mock.SetupSet(x => x.Prop1 = "1"); + mock.SetupSet(x => x.Prop3 = "2"); + mock.Setup(x => x.Method()); } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/Data/NoSealedClassMocks.cs b/Source/Moq.Analyzers.Test/Data/NoSealedClassMocks.cs index 80d1f542..c31f778d 100644 --- a/Source/Moq.Analyzers.Test/Data/NoSealedClassMocks.cs +++ b/Source/Moq.Analyzers.Test/Data/NoSealedClassMocks.cs @@ -1,3 +1,6 @@ +using System; +using Moq; + #pragma warning disable SA1402 // File may only contain a single class #pragma warning disable SA1502 // Element must not be on a single line #pragma warning disable SA1602 // Undocumented enum values @@ -7,30 +10,26 @@ #pragma warning disable IDE0051 // Unused private member #pragma warning disable IDE0059 // Unnecessary value assignment #pragma warning disable IDE0060 // Unused parameter -namespace NoSealedClassMocks +namespace NoSealedClassMocks; + +internal sealed class FooSealed { - using System; - using Moq; + private void Do(string s) { } +} - internal sealed class FooSealed +internal class MyUnitTests +{ + private void Test() { - private void Do(string s) { } + var mock1 = new Moq.Mock(); + var mock2 = new Mock(); + var mock3 = new Mock(); + var mock4 = new Moq.Mock(); } - internal class MyUnitTests + private void Test2() { - private void Test() - { - var mock1 = new Moq.Mock(); - var mock2 = new Mock(); - var mock3 = new Mock(); - var mock4 = new Moq.Mock(); - } - - private void Test2() - { - new Mock>(); - new Mock(); - } + new Mock>(); + new Mock(); } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/Data/SetupOnlyForOverridableMembers.cs b/Source/Moq.Analyzers.Test/Data/SetupOnlyForOverridableMembers.cs index c35c0853..8a79ffd1 100644 --- a/Source/Moq.Analyzers.Test/Data/SetupOnlyForOverridableMembers.cs +++ b/Source/Moq.Analyzers.Test/Data/SetupOnlyForOverridableMembers.cs @@ -1,90 +1,89 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; +using Moq; #pragma warning disable SA1402 // File may only contain a single class #pragma warning disable SA1649 // File name must match first type name #pragma warning disable SA1502 // Element must not be on a single line -namespace SetupOnlyForOverridableMembers +namespace SetupOnlyForOverridableMembers; + +public interface ISampleInterface { - using Moq; - public interface ISampleInterface - { - int Calculate(int a, int b); + int Calculate(int a, int b); - int TestProperty { get; set; } - } + int TestProperty { get; set; } +} - public abstract class BaseSampleClass +public abstract class BaseSampleClass +{ + public int Calculate() { - public int Calculate() - { - return 0; - } + return 0; + } + + public abstract int Calculate(int a, int b); - public abstract int Calculate(int a, int b); + public abstract int Calculate(int a, int b, int c); +} - public abstract int Calculate(int a, int b, int c); +public class SampleClass : BaseSampleClass +{ + + public override int Calculate(int a, int b) => 0; + + public sealed override int Calculate(int a, int b, int c) => 0; + + public virtual int DoSth() => 0; + + public int Property { get; set; } +} + +internal class MyUnitTests +{ + private void TestOkForAbstractMethod() + { + var mock = new Mock(); + mock.Setup(x => x.Calculate(It.IsAny(), It.IsAny())); + } + + private void TestOkForOverrideAbstractMethod() + { + var mock = new Mock(); + mock.Setup(x => x.Calculate(It.IsAny(), It.IsAny())); } - public class SampleClass : BaseSampleClass + private void TestOkForInterfaceMethod() { + var mock = new Mock(); + mock.Setup(x => x.Calculate(It.IsAny(), It.IsAny())); + } - public override int Calculate(int a, int b) => 0; + private void TestOkForInterfaceProperty() + { + var mock = new Mock(); + mock.Setup(x => x.TestProperty); + } - public sealed override int Calculate(int a, int b, int c) => 0; + private void TestOkForVirtualMethod() + { + var mock = new Mock(); + mock.Setup(x => x.DoSth()); + } - public virtual int DoSth() => 0; + private void TestBadSetupForNonVirtualMethod() + { + var mock = new Mock(); + mock.Setup(x => x.Calculate()); + } - public int Property { get; set; } + private void TestBadSetupForSealedMethod() + { + var mock = new Mock(); + mock.Setup(x => x.Calculate(It.IsAny(), It.IsAny(), It.IsAny())); } - internal class MyUnitTests + private void TestBadSetupForNonVirtualProperty() { - private void TestOkForAbstractMethod() - { - var mock = new Mock(); - mock.Setup(x => x.Calculate(It.IsAny(), It.IsAny())); - } - - private void TestOkForOverrideAbstractMethod() - { - var mock = new Mock(); - mock.Setup(x => x.Calculate(It.IsAny(), It.IsAny())); - } - - private void TestOkForInterfaceMethod() - { - var mock = new Mock(); - mock.Setup(x => x.Calculate(It.IsAny(), It.IsAny())); - } - - private void TestOkForInterfaceProperty() - { - var mock = new Mock(); - mock.Setup(x => x.TestProperty); - } - - private void TestOkForVirtualMethod() - { - var mock = new Mock(); - mock.Setup(x => x.DoSth()); - } - - private void TestBadSetupForNonVirtualMethod() - { - var mock = new Mock(); - mock.Setup(x => x.Calculate()); - } - - private void TestBadSetupForSealedMethod() - { - var mock = new Mock(); - mock.Setup(x => x.Calculate(It.IsAny(), It.IsAny(), It.IsAny())); - } - - private void TestBadSetupForNonVirtualProperty() - { - var mock = new Mock(); - mock.Setup(x => x.Property); - } + var mock = new Mock(); + mock.Setup(x => x.Property); } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/Data/SetupShouldNotIncludeAsyncResult.cs b/Source/Moq.Analyzers.Test/Data/SetupShouldNotIncludeAsyncResult.cs index 8b19166b..fbb903f3 100644 --- a/Source/Moq.Analyzers.Test/Data/SetupShouldNotIncludeAsyncResult.cs +++ b/Source/Moq.Analyzers.Test/Data/SetupShouldNotIncludeAsyncResult.cs @@ -1,37 +1,36 @@ -#pragma warning disable SA1402 // File may only contain a single class +using System.Threading.Tasks; +using Moq; + +#pragma warning disable SA1402 // File may only contain a single class #pragma warning disable SA1649 // File name must match first type name #pragma warning disable SA1502 // Element must not be on a single line -namespace SetupShouldNotIncludeAsyncResult +namespace SetupShouldNotIncludeAsyncResult; + +public class AsyncClient { - using Moq; - using System.Threading.Tasks; + public virtual Task VoidAsync() => Task.CompletedTask; + public virtual Task GenericAsyncWithConcreteReturn() => Task.FromResult(string.Empty); +} - public class AsyncClient +internal class MyUnitTests +{ + private void TestOkForTask() { - public virtual Task VoidAsync() => Task.CompletedTask; - public virtual Task GenericAsyncWithConcreteReturn() => Task.FromResult(string.Empty); + var mock = new Mock(); + mock.Setup(c => c.VoidAsync()); } - internal class MyUnitTests + private void TestOkForTaskWithConcreteReturn() { - private void TestOkForTask() - { - var mock = new Mock(); - mock.Setup(c => c.VoidAsync()); - } - - private void TestOkForTaskWithConcreteReturn() - { - var mock = new Mock(); - mock.Setup(c => c.GenericAsyncWithConcreteReturn().Result); - } + var mock = new Mock(); + mock.Setup(c => c.GenericAsyncWithConcreteReturn().Result); + } - private void TestOkForTaskWithConcreteReturnProperSetup() - { - var mock = new Mock(); - mock.Setup(c => c.GenericAsyncWithConcreteReturn()) - .ReturnsAsync(string.Empty); - } + private void TestOkForTaskWithConcreteReturnProperSetup() + { + var mock = new Mock(); + mock.Setup(c => c.GenericAsyncWithConcreteReturn()) + .ReturnsAsync(string.Empty); } } diff --git a/Source/Moq.Analyzers.Test/Helpers/CodeFixVerifier.Helper.cs b/Source/Moq.Analyzers.Test/Helpers/CodeFixVerifier.Helper.cs index d830fcd3..a6b06168 100644 --- a/Source/Moq.Analyzers.Test/Helpers/CodeFixVerifier.Helper.cs +++ b/Source/Moq.Analyzers.Test/Helpers/CodeFixVerifier.Helper.cs @@ -1,84 +1,83 @@ -namespace TestHelper -{ - using System.Collections.Generic; - using System.Linq; - using System.Threading; - 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; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Simplification; + +namespace TestHelper; +/// +/// Diagnostic Producer class with extra methods dealing with applying codefixes +/// All methods are static +/// +public abstract partial class CodeFixVerifier : DiagnosticVerifier +{ /// - /// Diagnostic Producer class with extra methods dealing with applying codefixes - /// All methods are static + /// Apply the inputted CodeAction to the inputted document. + /// Meant to be used to apply codefixes. /// - public abstract partial class CodeFixVerifier : DiagnosticVerifier + /// 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) { - /// - /// 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); - } + 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(); + /// + /// 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; + int oldIndex = 0; + int newIndex = 0; - while (newIndex < newArray.Length) + while (newIndex < newArray.Length) + { + if (oldIndex < oldArray.Length && oldArray[oldIndex].Id == newArray[newIndex].Id) + { + ++oldIndex; + ++newIndex; + } + else { - if (oldIndex < oldArray.Length && oldArray[oldIndex].Id == newArray[newIndex].Id) - { - ++oldIndex; - ++newIndex; - } - else - { - yield return newArray[newIndex++]; - } + 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(); - } + /// + /// 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(); - } + /// + /// 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(); } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/Helpers/CodeFixVerifier.cs b/Source/Moq.Analyzers.Test/Helpers/CodeFixVerifier.cs index 24e39b9d..7490e4ba 100644 --- a/Source/Moq.Analyzers.Test/Helpers/CodeFixVerifier.cs +++ b/Source/Moq.Analyzers.Test/Helpers/CodeFixVerifier.cs @@ -1,108 +1,107 @@ -namespace TestHelper +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Formatting; +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 { - using System.Collections.Generic; - using System.Linq; - using System.Threading; - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CodeActions; - using Microsoft.CodeAnalysis.CodeFixes; - using Microsoft.CodeAnalysis.Diagnostics; - using Microsoft.CodeAnalysis.Formatting; - using Xunit; + /// + /// 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; + } /// - /// Superclass of all Unit tests made for diagnostics with codefixes. - /// Contains methods used to verify correctness of codefixes + /// Called to test a C# codefix when applied on the inputted string as a source /// - public abstract partial class CodeFixVerifier : DiagnosticVerifier + /// A class in the form of a string before 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 + /// Code with quick fix applied + protected string VerifyCSharpFix(string oldSource, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false) { - /// - /// 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; - } + return VerifyFix(LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), GetCSharpCodeFixProvider(), oldSource, codeFixIndex, allowNewCompilerDiagnostics); + } - /// - /// 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 - /// 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 - /// Code with quick fix applied - protected string VerifyCSharpFix(string oldSource, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false) - { - return VerifyFix(LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), GetCSharpCodeFixProvider(), oldSource, 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 + /// 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 + /// Code with quick fix applied + private string VerifyFix(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string oldSource, int? codeFixIndex, bool allowNewCompilerDiagnostics) + { + var document = CreateDocument(oldSource, language); + var analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document }); + var compilerDiagnostics = GetCompilerDiagnostics(document); + var attempts = analyzerDiagnostics.Length; - /// - /// 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 - /// 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 - /// Code with quick fix applied - private string VerifyFix(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string oldSource, int? codeFixIndex, bool allowNewCompilerDiagnostics) + for (int i = 0; i < attempts; ++i) { - var document = CreateDocument(oldSource, language); - var analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document }); - var compilerDiagnostics = GetCompilerDiagnostics(document); - var attempts = analyzerDiagnostics.Length; + var actions = new List(); + var context = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a), CancellationToken.None); + codeFixProvider.RegisterCodeFixesAsync(context).Wait(); - for (int i = 0; i < attempts; ++i) + if (!actions.Any()) { - 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; - } + break; + } - document = ApplyFix(document, actions.ElementAt(0)); - analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document }); + if (codeFixIndex != null) + { + document = ApplyFix(document, actions.ElementAt((int)codeFixIndex)); + break; + } - var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document)); + document = ApplyFix(document, actions.ElementAt(0)); + analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { 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)); + var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document)); - Assert.True( - false, - 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 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)); - // check if there are analyzer diagnostics left after the code fix - if (!analyzerDiagnostics.Any()) - { - break; - } + Assert.True( + false, + 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())); } - // after applying all of the code fixes, compare the resulting string to the inputted one - return GetStringFromDocument(document); + // 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 + return GetStringFromDocument(document); } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/Helpers/DiagnosticResult.cs b/Source/Moq.Analyzers.Test/Helpers/DiagnosticResult.cs index 88c03bab..1d47fbd3 100644 --- a/Source/Moq.Analyzers.Test/Helpers/DiagnosticResult.cs +++ b/Source/Moq.Analyzers.Test/Helpers/DiagnosticResult.cs @@ -1,55 +1,54 @@ -namespace TestHelper +using Microsoft.CodeAnalysis; + +namespace TestHelper; + +/// +/// Struct that stores information about a Diagnostic appearing in a source +/// +public struct DiagnosticResult { - using Microsoft.CodeAnalysis; + private DiagnosticResultLocation[] locations; - /// - /// Struct that stores information about a Diagnostic appearing in a source - /// - public struct DiagnosticResult + public DiagnosticResultLocation[] Locations { - private DiagnosticResultLocation[] locations; + get + { + return locations ?? (this.locations = new DiagnosticResultLocation[] { }); + } - public DiagnosticResultLocation[] Locations + set { - get - { - return locations ?? (this.locations = new DiagnosticResultLocation[] { }); - } - - set - { - locations = value; - } + locations = value; } + } - public DiagnosticSeverity Severity { get; set; } + public DiagnosticSeverity Severity { get; set; } - public string Id { get; set; } + public string Id { get; set; } - public string Message { get; set; } + public string Message { get; set; } - public string Path + public string Path + { + get { - get - { - return this.Locations.Length > 0 ? this.Locations[0].Path : string.Empty; - } + return this.Locations.Length > 0 ? this.Locations[0].Path : string.Empty; } + } - public int Line + public int Line + { + get { - get - { - return this.Locations.Length > 0 ? this.Locations[0].Line : -1; - } + return this.Locations.Length > 0 ? this.Locations[0].Line : -1; } + } - public int Column + public int Column + { + get { - get - { - return this.Locations.Length > 0 ? this.Locations[0].Column : -1; - } + return this.Locations.Length > 0 ? this.Locations[0].Column : -1; } } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/Helpers/DiagnosticResultLocation.cs b/Source/Moq.Analyzers.Test/Helpers/DiagnosticResultLocation.cs index 9c4dd473..114061d7 100644 --- a/Source/Moq.Analyzers.Test/Helpers/DiagnosticResultLocation.cs +++ b/Source/Moq.Analyzers.Test/Helpers/DiagnosticResultLocation.cs @@ -1,33 +1,32 @@ -namespace TestHelper -{ - using System; +using System; + +namespace TestHelper; - /// - /// Location where the diagnostic appears, as determined by path, line number, and column number. - /// - public struct DiagnosticResultLocation +/// +/// 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) { - public DiagnosticResultLocation(string path, int line, int column) + if (line < -1) { - if (line < -1) - { - throw new ArgumentOutOfRangeException(nameof(line), "line must be >= -1"); - } - - if (column < -1) - { - throw new ArgumentOutOfRangeException(nameof(column), "column must be >= -1"); - } + throw new ArgumentOutOfRangeException(nameof(line), "line must be >= -1"); + } - this.Path = path; - this.Line = line; - this.Column = column; + if (column < -1) + { + throw new ArgumentOutOfRangeException(nameof(column), "column must be >= -1"); } - public string Path { get; } + this.Path = path; + this.Line = line; + this.Column = column; + } + + public string Path { get; } - public int Line { get; } + public int Line { get; } - public int Column { get; } - } -} \ No newline at end of file + public int Column { get; } +} diff --git a/Source/Moq.Analyzers.Test/Helpers/DiagnosticVerifier.Helper.cs b/Source/Moq.Analyzers.Test/Helpers/DiagnosticVerifier.Helper.cs index 4f1ee1de..de996e55 100644 --- a/Source/Moq.Analyzers.Test/Helpers/DiagnosticVerifier.Helper.cs +++ b/Source/Moq.Analyzers.Test/Helpers/DiagnosticVerifier.Helper.cs @@ -1,181 +1,180 @@ -namespace TestHelper +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using Moq; + +namespace TestHelper; + +/// +/// Class for turning strings into documents and getting the diagnostics on them +/// All methods are static. +/// +public abstract partial class DiagnosticVerifier { - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Diagnostics; - using System.Linq; - using System.Linq.Expressions; - using System.Reflection; - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp; - using Microsoft.CodeAnalysis.Diagnostics; - using Microsoft.CodeAnalysis.Text; - using Moq; + private const string DefaultFilePathPrefix = "Test"; + private const string CSharpDefaultFileExt = "cs"; + private const string VisualBasicDefaultExt = "vb"; + private const string TestProjectName = "TestProject"; + + private static readonly MetadataReference CorlibReference = MetadataReference.CreateFromFile(typeof(object).Assembly.Location); + private static readonly MetadataReference SystemCoreReference = MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location); + private static readonly MetadataReference SystemLinqExpressions = MetadataReference.CreateFromFile(typeof(Expression).Assembly.Location); + private static readonly MetadataReference GenericCollectionsReference = MetadataReference.CreateFromFile(typeof(IEnumerable<>).Assembly.Location); + private static readonly MetadataReference CSharpSymbolsReference = MetadataReference.CreateFromFile(typeof(CSharpCompilation).Assembly.Location); + private static readonly MetadataReference CodeAnalysisReference = MetadataReference.CreateFromFile(typeof(Compilation).Assembly.Location); + private static readonly MetadataReference MoqReference = MetadataReference.CreateFromFile(typeof(Mock).Assembly.Location); + private static readonly MetadataReference NetStandardReference = MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("netstandard")).Location); + private static readonly MetadataReference SystemRuntimeReference = MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("System.Runtime")).Location); /// - /// Class for turning strings into documents and getting the diagnostics on them - /// All methods are static. + /// 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. /// - public abstract partial class DiagnosticVerifier + /// 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) { - private const string DefaultFilePathPrefix = "Test"; - private const string CSharpDefaultFileExt = "cs"; - private const string VisualBasicDefaultExt = "vb"; - private const string TestProjectName = "TestProject"; - - private static readonly MetadataReference CorlibReference = MetadataReference.CreateFromFile(typeof(object).Assembly.Location); - private static readonly MetadataReference SystemCoreReference = MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location); - private static readonly MetadataReference SystemLinqExpressions = MetadataReference.CreateFromFile(typeof(Expression).Assembly.Location); - private static readonly MetadataReference GenericCollectionsReference = MetadataReference.CreateFromFile(typeof(IEnumerable<>).Assembly.Location); - private static readonly MetadataReference CSharpSymbolsReference = MetadataReference.CreateFromFile(typeof(CSharpCompilation).Assembly.Location); - private static readonly MetadataReference CodeAnalysisReference = MetadataReference.CreateFromFile(typeof(Compilation).Assembly.Location); - private static readonly MetadataReference MoqReference = MetadataReference.CreateFromFile(typeof(Mock).Assembly.Location); - private static readonly MetadataReference NetStandardReference = MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("netstandard")).Location); - private static readonly MetadataReference SystemRuntimeReference = MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("System.Runtime")).Location); - - /// - /// 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) - { - Debug.Assert(documents != null, nameof(documents) + " != null"); + Debug.Assert(documents != null, nameof(documents) + " != null"); - var projects = new HashSet(); - foreach (var document in documents) - { - projects.Add(document.Project); - } + var projects = new HashSet(); + foreach (var document in documents) + { + projects.Add(document.Project); + } - var diagnostics = new List(); - foreach (var project in projects) + var diagnostics = new List(); + foreach (var project in projects) + { + Debug.Assert(analyzer != null, nameof(analyzer) + " != null"); + var compilationWithAnalyzers = project.GetCompilationAsync().Result.WithAnalyzers(ImmutableArray.Create(analyzer)); + var diags = compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync().Result; + foreach (var diag in diags) { - Debug.Assert(analyzer != null, nameof(analyzer) + " != null"); - 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) { - if (diag.Location == Location.None || diag.Location.IsInMetadata) - { - diagnostics.Add(diag); - } - else + diagnostics.Add(diag); + } + else + { + for (int i = 0; i < documents.Length; i++) { - for (int i = 0; i < documents.Length; i++) + var document = documents[i]; + var tree = document.GetSyntaxTreeAsync().Result; + if (tree == diag.Location.SourceTree) { - var document = documents[i]; - var tree = document.GetSyntaxTreeAsync().Result; - if (tree == diag.Location.SourceTree) - { - diagnostics.Add(diag); - } + diagnostics.Add(diag); } } } } - - var results = SortDiagnostics(diagnostics); - diagnostics.Clear(); - return results; } - /// - /// 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(); - } + var results = SortDiagnostics(diagnostics); + diagnostics.Clear(); + return results; + } - /// - /// Given classes in the form of strings, their language, and an IDiagnosticAnlayzer 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)); - } + /// + /// 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(); + } - /// - /// 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(); - } + /// + /// Given classes in the form of strings, their language, and an IDiagnosticAnlayzer 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 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) + /// + /// 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(); + } + + /// + /// 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) { - if (language != LanguageNames.CSharp && language != LanguageNames.VisualBasic) - { - throw new ArgumentException("Unsupported Language"); - } + throw new ArgumentException("Unsupported Language"); + } - var project = CreateProject(sources, language); - var documents = project.Documents.ToArray(); + var project = CreateProject(sources, language); + var documents = project.Documents.ToArray(); - if (sources.Length != documents.Length) - { - throw new SystemException("Amount of sources did not match amount of Documents created"); - } - - return documents; + if (sources.Length != documents.Length) + { + throw new SystemException("Amount of sources did not match amount of Documents created"); } - /// - /// 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) - { - const 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, CodeAnalysisReference) - .AddMetadataReference(projectId, SystemLinqExpressions) - .AddMetadataReference(projectId, CSharpSymbolsReference) - .AddMetadataReference(projectId, GenericCollectionsReference) - .AddMetadataReference(projectId, SystemRuntimeReference) - .AddMetadataReference(projectId, NetStandardReference) - .AddMetadataReference(projectId, MoqReference); - - 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 documents; + } - return solution.GetProject(projectId); + /// + /// 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) + { + const 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, CodeAnalysisReference) + .AddMetadataReference(projectId, SystemLinqExpressions) + .AddMetadataReference(projectId, CSharpSymbolsReference) + .AddMetadataReference(projectId, GenericCollectionsReference) + .AddMetadataReference(projectId, SystemRuntimeReference) + .AddMetadataReference(projectId, NetStandardReference) + .AddMetadataReference(projectId, MoqReference); + + 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); } } diff --git a/Source/Moq.Analyzers.Test/Helpers/DiagnosticVerifier.cs b/Source/Moq.Analyzers.Test/Helpers/DiagnosticVerifier.cs index 59c79114..7e52849f 100644 --- a/Source/Moq.Analyzers.Test/Helpers/DiagnosticVerifier.cs +++ b/Source/Moq.Analyzers.Test/Helpers/DiagnosticVerifier.cs @@ -1,89 +1,88 @@ -namespace TestHelper -{ - using System.Collections.Generic; - using System.Linq; - using System.Text; - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace TestHelper; +/// +/// Superclass of all Unit Tests for DiagnosticAnalyzers +/// +public abstract partial class DiagnosticVerifier +{ /// - /// Superclass of all Unit Tests for DiagnosticAnalyzers + /// Get the CSharp analyzer being tested - to be implemented in non-abstract class /// - public abstract partial class DiagnosticVerifier + /// Diagnostics to be used in test + protected virtual DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() { - /// - /// Get the CSharp analyzer being tested - to be implemented in non-abstract class - /// - /// Diagnostics to be used in test - protected virtual DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return null; - } + return null; + } - /// - /// 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 - /// String representation of diagnostics results - protected string VerifyCSharpDiagnostic(string source) - { - return VerifyDiagnostics(new[] { source }, LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer()); - } + /// + /// 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 + /// String representation of diagnostics results + protected string VerifyCSharpDiagnostic(string source) + { + return VerifyDiagnostics(new[] { source }, LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer()); + } - /// - /// 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 - /// String representation of diagnostics results - protected string VerifyCSharpDiagnostic(string[] sources) - { - return VerifyDiagnostics(sources, LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer()); - } + /// + /// 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 + /// String representation of diagnostics results + protected string VerifyCSharpDiagnostic(string[] sources) + { + return VerifyDiagnostics(sources, LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer()); + } - /// - /// 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 sources - /// String representation of diagnostics results - private string VerifyDiagnostics(string[] sources, string language, DiagnosticAnalyzer analyzer) - { - var diagnostics = GetSortedDiagnostics(sources, language, analyzer); - return VerifyDiagnosticResults(diagnostics); - } + /// + /// 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 sources + /// String representation of diagnostics results + private string VerifyDiagnostics(string[] sources, string language, DiagnosticAnalyzer analyzer) + { + var diagnostics = GetSortedDiagnostics(sources, language, analyzer); + return VerifyDiagnosticResults(diagnostics); + } - /// - /// 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 - /// String representation of diagnostics results - private string VerifyDiagnosticResults(IEnumerable actualResults) + /// + /// 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 + /// String representation of diagnostics results + private string VerifyDiagnosticResults(IEnumerable actualResults) + { + StringBuilder result = new StringBuilder(); + int i = 1; + foreach (var diagnostic in actualResults) { - StringBuilder result = new StringBuilder(); - int i = 1; - foreach (var diagnostic in actualResults) - { - result.AppendLine("Diagnostic " + i); - result.AppendLine("\tId: " + diagnostic.Id); - result.AppendLine("\tLocation: " + diagnostic.Location); - var sourceSpan = diagnostic.Location.SourceSpan; - var code = diagnostic.Location.SourceTree.GetText(); - result.AppendLine("\tHighlight: " + code.GetSubText(sourceSpan)); - var lineSpan = diagnostic.Location.GetLineSpan(); - result.AppendLine("\tLines: " + string.Join("\n", code.Lines.Where(x => x.LineNumber >= lineSpan.StartLinePosition.Line && x.LineNumber <= lineSpan.EndLinePosition.Line).Select(x => x.ToString().Trim()))); - result.AppendLine("\tSeverity: " + diagnostic.Severity); - result.AppendLine("\tMessage: " + diagnostic.GetMessage()); - result.AppendLine(); - - i += 1; - } + result.AppendLine("Diagnostic " + i); + result.AppendLine("\tId: " + diagnostic.Id); + result.AppendLine("\tLocation: " + diagnostic.Location); + var sourceSpan = diagnostic.Location.SourceSpan; + var code = diagnostic.Location.SourceTree.GetText(); + result.AppendLine("\tHighlight: " + code.GetSubText(sourceSpan)); + var lineSpan = diagnostic.Location.GetLineSpan(); + result.AppendLine("\tLines: " + string.Join("\n", code.Lines.Where(x => x.LineNumber >= lineSpan.StartLinePosition.Line && x.LineNumber <= lineSpan.EndLinePosition.Line).Select(x => x.ToString().Trim()))); + result.AppendLine("\tSeverity: " + diagnostic.Severity); + result.AppendLine("\tMessage: " + diagnostic.GetMessage()); + result.AppendLine(); - return result.ToString(); + i += 1; } + + return result.ToString(); } } diff --git a/Source/Moq.Analyzers.Test/ModuleInitializer.cs b/Source/Moq.Analyzers.Test/ModuleInitializer.cs index 3331f3c9..17ad0e5c 100644 --- a/Source/Moq.Analyzers.Test/ModuleInitializer.cs +++ b/Source/Moq.Analyzers.Test/ModuleInitializer.cs @@ -1,14 +1,13 @@ -namespace Moq.Analyzers.Test -{ - using System.Runtime.CompilerServices; - using VerifyTests; +using System.Runtime.CompilerServices; +using VerifyTests; + +namespace Moq.Analyzers.Test; - public static class ModuleInitializer +public static class ModuleInitializer +{ + [ModuleInitializer] + public static void Initialize() { - [ModuleInitializer] - public static void Initialize() - { - VerifyNupkg.Initialize(); - } + VerifyNupkg.Initialize(); } } diff --git a/Source/Moq.Analyzers.Test/NoConstructorArgumentsForInterfaceMockAnalyzerTests.ShouldFailIfMockedInterfaceHasParameters.verified.txt b/Source/Moq.Analyzers.Test/NoConstructorArgumentsForInterfaceMockAnalyzerTests.ShouldFailIfMockedInterfaceHasParameters.verified.txt index 64c98c44..db89c886 100644 --- a/Source/Moq.Analyzers.Test/NoConstructorArgumentsForInterfaceMockAnalyzerTests.ShouldFailIfMockedInterfaceHasParameters.verified.txt +++ b/Source/Moq.Analyzers.Test/NoConstructorArgumentsForInterfaceMockAnalyzerTests.ShouldFailIfMockedInterfaceHasParameters.verified.txt @@ -1,6 +1,6 @@ Diagnostic 1 Id: Moq1001 - Location: SourceFile(Test0.cs[834..844)) + Location: SourceFile(Test0.cs[794..804)) Highlight: (25, true) Lines: var mock1 = new Moq.Mock(25, true); Severity: Warning @@ -8,7 +8,7 @@ Diagnostic 2 Id: Moq1001 - Location: SourceFile(Test0.cs[890..897)) + Location: SourceFile(Test0.cs[846..853)) Highlight: ("123") Lines: var mock2 = new Mock("123"); Severity: Warning @@ -16,7 +16,7 @@ Diagnostic 2 Diagnostic 3 Id: Moq1001 - Location: SourceFile(Test0.cs[984..994)) + Location: SourceFile(Test0.cs[936..946)) Highlight: (25, true) Lines: var mock3 = new Mock(25, true); Severity: Warning @@ -24,7 +24,7 @@ Diagnostic 3 Diagnostic 4 Id: Moq1001 - Location: SourceFile(Test0.cs[1085..1092)) + Location: SourceFile(Test0.cs[1033..1040)) Highlight: ("123") Lines: var mock4 = new Moq.Mock("123"); Severity: Warning @@ -32,7 +32,7 @@ Diagnostic 4 Diagnostic 5 Id: Moq1001 - Location: SourceFile(Test0.cs[1195..1228)) + Location: SourceFile(Test0.cs[1127..1160)) Highlight: (Moq.MockBehavior.Default, "123") Lines: var mock1 = new Moq.Mock(Moq.MockBehavior.Default, "123"); Severity: Warning @@ -40,7 +40,7 @@ Diagnostic 5 Diagnostic 6 Id: Moq1001 - Location: SourceFile(Test0.cs[1274..1305)) + Location: SourceFile(Test0.cs[1202..1233)) Highlight: (MockBehavior.Strict, 25, true) Lines: var mock2 = new Mock(MockBehavior.Strict, 25, true); Severity: Warning @@ -48,7 +48,7 @@ Diagnostic 6 Diagnostic 7 Id: Moq1001 - Location: SourceFile(Test0.cs[1392..1425)) + Location: SourceFile(Test0.cs[1316..1349)) Highlight: (Moq.MockBehavior.Default, "123") Lines: var mock3 = new Mock(Moq.MockBehavior.Default, "123"); Severity: Warning @@ -56,7 +56,7 @@ Diagnostic 7 Diagnostic 8 Id: Moq1001 - Location: SourceFile(Test0.cs[1516..1546)) + Location: SourceFile(Test0.cs[1436..1466)) Highlight: (MockBehavior.Loose, 25, true) Lines: var mock4 = new Moq.Mock(MockBehavior.Loose, 25, true); Severity: Warning diff --git a/Source/Moq.Analyzers.Test/NoConstructorArgumentsForInterfaceMockAnalyzerTests.ShouldPassIfCustomMockClassIsUsed.verified.txt b/Source/Moq.Analyzers.Test/NoConstructorArgumentsForInterfaceMockAnalyzerTests.ShouldPassIfCustomMockClassIsUsed.verified.txt index 1216d0c9..657c227b 100644 --- a/Source/Moq.Analyzers.Test/NoConstructorArgumentsForInterfaceMockAnalyzerTests.ShouldPassIfCustomMockClassIsUsed.verified.txt +++ b/Source/Moq.Analyzers.Test/NoConstructorArgumentsForInterfaceMockAnalyzerTests.ShouldPassIfCustomMockClassIsUsed.verified.txt @@ -1,6 +1,6 @@ Diagnostic 1 Id: Moq1001 - Location: SourceFile(Test0.cs[1180..1189)) + Location: SourceFile(Test0.cs[1088..1097)) Highlight: (1, true) Lines: var mock1 = new Moq.Mock(1, true); Severity: Warning @@ -8,7 +8,7 @@ Diagnostic 2 Id: Moq1001 - Location: SourceFile(Test0.cs[1280..1285)) + Location: SourceFile(Test0.cs[1184..1189)) Highlight: ("2") Lines: var mock2 = new Moq.Mock("2"); Severity: Warning @@ -16,7 +16,7 @@ Diagnostic 2 Diagnostic 3 Id: Moq1001 - Location: SourceFile(Test0.cs[1335..1366)) + Location: SourceFile(Test0.cs[1235..1266)) Highlight: (Moq.MockBehavior.Default, "3") Lines: var mock3 = new Moq.Mock(Moq.MockBehavior.Default, "3"); Severity: Warning @@ -24,7 +24,7 @@ Diagnostic 3 Diagnostic 4 Id: Moq1001 - Location: SourceFile(Test0.cs[1457..1486)) + Location: SourceFile(Test0.cs[1353..1382)) Highlight: (MockBehavior.Loose, 4, true) Lines: var mock4 = new Moq.Mock(MockBehavior.Loose, 4, true); Severity: Warning @@ -32,7 +32,7 @@ Diagnostic 4 Diagnostic 5 Id: Moq1001 - Location: SourceFile(Test0.cs[1536..1558)) + Location: SourceFile(Test0.cs[1428..1450)) Highlight: (MockBehavior.Default) Lines: var mock5 = new Moq.Mock(MockBehavior.Default); Severity: Warning @@ -40,7 +40,7 @@ Diagnostic 5 Diagnostic 6 Id: Moq1001 - Location: SourceFile(Test0.cs[1649..1671)) + Location: SourceFile(Test0.cs[1537..1559)) Highlight: (MockBehavior.Default) Lines: var mock6 = new Moq.Mock(MockBehavior.Default); Severity: Warning diff --git a/Source/Moq.Analyzers.Test/NoConstructorArgumentsForInterfaceMockAnalyzerTests.cs b/Source/Moq.Analyzers.Test/NoConstructorArgumentsForInterfaceMockAnalyzerTests.cs index fda735cd..c72b969d 100644 --- a/Source/Moq.Analyzers.Test/NoConstructorArgumentsForInterfaceMockAnalyzerTests.cs +++ b/Source/Moq.Analyzers.Test/NoConstructorArgumentsForInterfaceMockAnalyzerTests.cs @@ -1,28 +1,27 @@ -namespace Moq.Analyzers.Test -{ - using System.IO; - using System.Threading.Tasks; - using Microsoft.CodeAnalysis.Diagnostics; - using TestHelper; - using Xunit; +using System.IO; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; +using TestHelper; +using Xunit; + +namespace Moq.Analyzers.Test; - public class NoConstructorArgumentsForInterfaceMockAnalyzerTests : DiagnosticVerifier +public class NoConstructorArgumentsForInterfaceMockAnalyzerTests : DiagnosticVerifier +{ + [Fact] + public Task ShouldFailIfMockedInterfaceHasParameters() { - [Fact] - public Task ShouldFailIfMockedInterfaceHasParameters() - { - return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/NoConstructorArgumentsForInterfaceMock_1.cs"))); - } + return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/NoConstructorArgumentsForInterfaceMock_1.cs"))); + } - [Fact] - public Task ShouldPassIfCustomMockClassIsUsed() - { - return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/NoConstructorArgumentsForInterfaceMock_2.cs"))); - } + [Fact] + public Task ShouldPassIfCustomMockClassIsUsed() + { + return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/NoConstructorArgumentsForInterfaceMock_2.cs"))); + } - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new NoConstructorArgumentsForInterfaceMockAnalyzer(); - } + protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() + { + return new NoConstructorArgumentsForInterfaceMockAnalyzer(); } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/NoMethodsInPropertySetupAnalyzerTests.Test.verified.txt b/Source/Moq.Analyzers.Test/NoMethodsInPropertySetupAnalyzerTests.Test.verified.txt index 85ddfdae..d5f86c56 100644 --- a/Source/Moq.Analyzers.Test/NoMethodsInPropertySetupAnalyzerTests.Test.verified.txt +++ b/Source/Moq.Analyzers.Test/NoMethodsInPropertySetupAnalyzerTests.Test.verified.txt @@ -1,6 +1,6 @@ Diagnostic 1 Id: Moq1101 - Location: SourceFile(Test0.cs[928..938)) + Location: SourceFile(Test0.cs[872..882)) Highlight: x.Method() Lines: mock.SetupGet(x => x.Method()); Severity: Warning @@ -8,7 +8,7 @@ Diagnostic 2 Id: Moq1101 - Location: SourceFile(Test0.cs[972..982)) + Location: SourceFile(Test0.cs[912..922)) Highlight: x.Method() Lines: mock.SetupSet(x => x.Method()); Severity: Warning diff --git a/Source/Moq.Analyzers.Test/NoMethodsInPropertySetupAnalyzerTests.cs b/Source/Moq.Analyzers.Test/NoMethodsInPropertySetupAnalyzerTests.cs index fa967dc1..c6cdfcce 100644 --- a/Source/Moq.Analyzers.Test/NoMethodsInPropertySetupAnalyzerTests.cs +++ b/Source/Moq.Analyzers.Test/NoMethodsInPropertySetupAnalyzerTests.cs @@ -1,22 +1,21 @@ -namespace Moq.Analyzers.Test -{ - using System.IO; - using System.Threading.Tasks; - using Microsoft.CodeAnalysis.Diagnostics; - using TestHelper; - using Xunit; +using System.IO; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; +using TestHelper; +using Xunit; + +namespace Moq.Analyzers.Test; - public class NoMethodsInPropertySetupAnalyzerTests : DiagnosticVerifier +public class NoMethodsInPropertySetupAnalyzerTests : DiagnosticVerifier +{ + [Fact] + public Task Test() { - [Fact] - public Task Test() - { - return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/NoMethodsInPropertySetup.cs"))); - } + return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/NoMethodsInPropertySetup.cs"))); + } - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new NoMethodsInPropertySetupAnalyzer(); - } + protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() + { + return new NoMethodsInPropertySetupAnalyzer(); } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/NoSealedClassMocksAnalyzerTests.ShouldFailIfFileIsSealed.verified.txt b/Source/Moq.Analyzers.Test/NoSealedClassMocksAnalyzerTests.ShouldFailIfFileIsSealed.verified.txt index aa0f8da7..b9435c14 100644 --- a/Source/Moq.Analyzers.Test/NoSealedClassMocksAnalyzerTests.ShouldFailIfFileIsSealed.verified.txt +++ b/Source/Moq.Analyzers.Test/NoSealedClassMocksAnalyzerTests.ShouldFailIfFileIsSealed.verified.txt @@ -1,6 +1,6 @@ Diagnostic 1 Id: Moq1000 - Location: SourceFile(Test0.cs[829..838)) + Location: SourceFile(Test0.cs[785..794)) Highlight: FooSealed Lines: var mock1 = new Moq.Mock(); Severity: Warning @@ -8,7 +8,7 @@ Diagnostic 2 Id: Moq1000 - Location: SourceFile(Test0.cs[876..885)) + Location: SourceFile(Test0.cs[828..837)) Highlight: FooSealed Lines: var mock2 = new Mock(); Severity: Warning @@ -16,7 +16,7 @@ Diagnostic 2 Diagnostic 3 Id: Moq1000 - Location: SourceFile(Test0.cs[923..951)) + Location: SourceFile(Test0.cs[871..899)) Highlight: NoSealedClassMocks.FooSealed Lines: var mock3 = new Mock(); Severity: Warning @@ -24,7 +24,7 @@ Diagnostic 3 Diagnostic 4 Id: Moq1000 - Location: SourceFile(Test0.cs[993..1021)) + Location: SourceFile(Test0.cs[937..965)) Highlight: NoSealedClassMocks.FooSealed Lines: var mock4 = new Moq.Mock(); Severity: Warning diff --git a/Source/Moq.Analyzers.Test/NoSealedClassMocksAnalyzerTests.cs b/Source/Moq.Analyzers.Test/NoSealedClassMocksAnalyzerTests.cs index 06e4dd04..d09e25cb 100644 --- a/Source/Moq.Analyzers.Test/NoSealedClassMocksAnalyzerTests.cs +++ b/Source/Moq.Analyzers.Test/NoSealedClassMocksAnalyzerTests.cs @@ -1,22 +1,21 @@ -namespace Moq.Analyzers.Test -{ - using System.IO; - using System.Threading.Tasks; - using Microsoft.CodeAnalysis.Diagnostics; - using TestHelper; - using Xunit; +using System.IO; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; +using TestHelper; +using Xunit; + +namespace Moq.Analyzers.Test; - public class NoSealedClassMocksAnalyzerTests : DiagnosticVerifier +public class NoSealedClassMocksAnalyzerTests : DiagnosticVerifier +{ + [Fact] + public Task ShouldFailIfFileIsSealed() { - [Fact] - public Task ShouldFailIfFileIsSealed() - { - return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/NoSealedClassMocks.cs"))); - } + return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/NoSealedClassMocks.cs"))); + } - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new NoSealedClassMocksAnalyzer(); - } + protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() + { + return new NoSealedClassMocksAnalyzer(); } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/PackageTests.cs b/Source/Moq.Analyzers.Test/PackageTests.cs index ab8a083e..b55ad2fd 100644 --- a/Source/Moq.Analyzers.Test/PackageTests.cs +++ b/Source/Moq.Analyzers.Test/PackageTests.cs @@ -1,28 +1,27 @@ -namespace Moq.Analyzers.Test +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Xunit; + +namespace Moq.Analyzers.Test; + +public class PackageTests { - using System.IO; - using System.Linq; - using System.Reflection; - using System.Threading.Tasks; - using Xunit; + private static readonly FileInfo Package; - public class PackageTests + static PackageTests() { - private static readonly FileInfo Package; - - static PackageTests() - { - Package = new FileInfo(Assembly.GetExecutingAssembly().Location) - .Directory! - .GetFiles("Moq.Analyzers*.nupkg") - .OrderByDescending(f => f.LastWriteTimeUtc) - .First(); - } + Package = new FileInfo(Assembly.GetExecutingAssembly().Location) + .Directory! + .GetFiles("Moq.Analyzers*.nupkg") + .OrderByDescending(f => f.LastWriteTimeUtc) + .First(); + } - [Fact] - public Task Baseline() - { - return VerifyFile(Package).ScrubNuspec(); - } + [Fact] + public Task Baseline() + { + return VerifyFile(Package).ScrubNuspec(); } } diff --git a/Source/Moq.Analyzers.Test/SetupShouldBeUsedOnlyForOverridableMembersAnalyzerTests.ShouldPassIfGoodParameters.verified.txt b/Source/Moq.Analyzers.Test/SetupShouldBeUsedOnlyForOverridableMembersAnalyzerTests.ShouldPassIfGoodParameters.verified.txt index f8c67a12..1a68dc7a 100644 --- a/Source/Moq.Analyzers.Test/SetupShouldBeUsedOnlyForOverridableMembersAnalyzerTests.ShouldPassIfGoodParameters.verified.txt +++ b/Source/Moq.Analyzers.Test/SetupShouldBeUsedOnlyForOverridableMembersAnalyzerTests.ShouldPassIfGoodParameters.verified.txt @@ -1,6 +1,6 @@ Diagnostic 1 Id: Moq1200 - Location: SourceFile(Test0.cs[2064..2077)) + Location: SourceFile(Test0.cs[1852..1865)) Highlight: x.Calculate() Lines: mock.Setup(x => x.Calculate()); Severity: Error @@ -8,7 +8,7 @@ Diagnostic 2 Id: Moq1200 - Location: SourceFile(Test0.cs[2228..2290)) + Location: SourceFile(Test0.cs[1996..2058)) Highlight: x.Calculate(It.IsAny(), It.IsAny(), It.IsAny()) Lines: mock.Setup(x => x.Calculate(It.IsAny(), It.IsAny(), It.IsAny())); Severity: Error @@ -16,7 +16,7 @@ Diagnostic 2 Diagnostic 3 Id: Moq1200 - Location: SourceFile(Test0.cs[2447..2457)) + Location: SourceFile(Test0.cs[2195..2205)) Highlight: x.Property Lines: mock.Setup(x => x.Property); Severity: Error diff --git a/Source/Moq.Analyzers.Test/SetupShouldBeUsedOnlyForOverridableMembersAnalyzerTests.cs b/Source/Moq.Analyzers.Test/SetupShouldBeUsedOnlyForOverridableMembersAnalyzerTests.cs index e47d7312..7edac2b2 100644 --- a/Source/Moq.Analyzers.Test/SetupShouldBeUsedOnlyForOverridableMembersAnalyzerTests.cs +++ b/Source/Moq.Analyzers.Test/SetupShouldBeUsedOnlyForOverridableMembersAnalyzerTests.cs @@ -1,22 +1,21 @@ -namespace Moq.Analyzers.Test -{ - using System.IO; - using System.Threading.Tasks; - using Microsoft.CodeAnalysis.Diagnostics; - using TestHelper; - using Xunit; +using System.IO; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; +using TestHelper; +using Xunit; + +namespace Moq.Analyzers.Test; - public class SetupShouldBeUsedOnlyForOverridableMembersAnalyzerTests : DiagnosticVerifier +public class SetupShouldBeUsedOnlyForOverridableMembersAnalyzerTests : DiagnosticVerifier +{ + [Fact] + public Task ShouldPassIfGoodParameters() { - [Fact] - public Task ShouldPassIfGoodParameters() - { - return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/SetupOnlyForOverridableMembers.cs"))); - } + return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/SetupOnlyForOverridableMembers.cs"))); + } - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new SetupShouldBeUsedOnlyForOverridableMembersAnalyzer(); - } + protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() + { + return new SetupShouldBeUsedOnlyForOverridableMembersAnalyzer(); } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers.Test/SetupShouldNotIncludeAsyncResultAnalyzerTests.ShouldPassIfSetupProperly.verified.txt b/Source/Moq.Analyzers.Test/SetupShouldNotIncludeAsyncResultAnalyzerTests.ShouldPassIfSetupProperly.verified.txt index 33d23abf..8b863fb7 100644 --- a/Source/Moq.Analyzers.Test/SetupShouldNotIncludeAsyncResultAnalyzerTests.ShouldPassIfSetupProperly.verified.txt +++ b/Source/Moq.Analyzers.Test/SetupShouldNotIncludeAsyncResultAnalyzerTests.ShouldPassIfSetupProperly.verified.txt @@ -1,6 +1,6 @@ Diagnostic 1 Id: Moq1201 - Location: SourceFile(Test0.cs[845..886)) + Location: SourceFile(Test0.cs[773..814)) Highlight: c.GenericAsyncWithConcreteReturn().Result Lines: mock.Setup(c => c.GenericAsyncWithConcreteReturn().Result); Severity: Error diff --git a/Source/Moq.Analyzers.Test/SetupShouldNotIncludeAsyncResultAnalyzerTests.cs b/Source/Moq.Analyzers.Test/SetupShouldNotIncludeAsyncResultAnalyzerTests.cs index bd02f342..58b07741 100644 --- a/Source/Moq.Analyzers.Test/SetupShouldNotIncludeAsyncResultAnalyzerTests.cs +++ b/Source/Moq.Analyzers.Test/SetupShouldNotIncludeAsyncResultAnalyzerTests.cs @@ -1,22 +1,21 @@ -namespace Moq.Analyzers.Test -{ - using System.IO; - using System.Threading.Tasks; - using Microsoft.CodeAnalysis.Diagnostics; - using TestHelper; - using Xunit; +using System.IO; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; +using TestHelper; +using Xunit; + +namespace Moq.Analyzers.Test; - public class SetupShouldNotIncludeAsyncResultAnalyzerTests : DiagnosticVerifier +public class SetupShouldNotIncludeAsyncResultAnalyzerTests : DiagnosticVerifier +{ + [Fact] + public Task ShouldPassIfSetupProperly() { - [Fact] - public Task ShouldPassIfSetupProperly() - { - return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/SetupShouldNotIncludeAsyncResult.cs"))); - } + return Verify(VerifyCSharpDiagnostic(File.ReadAllText("Data/SetupShouldNotIncludeAsyncResult.cs"))); + } - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new SetupShouldNotIncludeAsyncResultAnalyzer(); - } + protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() + { + return new SetupShouldNotIncludeAsyncResultAnalyzer(); } } diff --git a/Source/Moq.Analyzers/AsShouldBeUsedOnlyForInterfaceAnalyzer.cs b/Source/Moq.Analyzers/AsShouldBeUsedOnlyForInterfaceAnalyzer.cs index 853326a9..6d1b843a 100644 --- a/Source/Moq.Analyzers/AsShouldBeUsedOnlyForInterfaceAnalyzer.cs +++ b/Source/Moq.Analyzers/AsShouldBeUsedOnlyForInterfaceAnalyzer.cs @@ -1,44 +1,43 @@ -namespace Moq.Analyzers +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Moq.Analyzers; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public class AsShouldBeUsedOnlyForInterfaceAnalyzer : DiagnosticAnalyzer { - using System.Collections.Immutable; - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp; - using Microsoft.CodeAnalysis.CSharp.Syntax; - using Microsoft.CodeAnalysis.Diagnostics; + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + Diagnostics.AsShouldBeUsedOnlyForInterfaceId, + Diagnostics.AsShouldBeUsedOnlyForInterfaceTitle, + Diagnostics.AsShouldBeUsedOnlyForInterfaceMessage, + Diagnostics.Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true); - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public class AsShouldBeUsedOnlyForInterfaceAnalyzer : DiagnosticAnalyzer - { - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( - Diagnostics.AsShouldBeUsedOnlyForInterfaceId, - Diagnostics.AsShouldBeUsedOnlyForInterfaceTitle, - Diagnostics.AsShouldBeUsedOnlyForInterfaceMessage, - Diagnostics.Category, - DiagnosticSeverity.Error, - isEnabledByDefault: true); + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.InvocationExpression); + } - public override void Initialize(AnalysisContext context) - { - context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.InvocationExpression); - } + private static void Analyze(SyntaxNodeAnalysisContext context) + { + var asInvocation = (InvocationExpressionSyntax)context.Node; - private static void Analyze(SyntaxNodeAnalysisContext context) + if (asInvocation.Expression is MemberAccessExpressionSyntax memberAccessExpression && Helpers.IsMoqAsMethod(context.SemanticModel, memberAccessExpression)) { - var asInvocation = (InvocationExpressionSyntax)context.Node; - - if (asInvocation.Expression is MemberAccessExpressionSyntax memberAccessExpression && Helpers.IsMoqAsMethod(context.SemanticModel, memberAccessExpression)) + if (memberAccessExpression.Name is GenericNameSyntax genericName && genericName.TypeArgumentList.Arguments.Count == 1) { - if (memberAccessExpression.Name is GenericNameSyntax genericName && genericName.TypeArgumentList.Arguments.Count == 1) + var typeArgument = genericName.TypeArgumentList.Arguments[0]; + var symbolInfo = context.SemanticModel.GetSymbolInfo(typeArgument); + if (symbolInfo.Symbol != null && symbolInfo.Symbol is ITypeSymbol typeSymbol && typeSymbol.TypeKind != TypeKind.Interface) { - var typeArgument = genericName.TypeArgumentList.Arguments[0]; - var symbolInfo = context.SemanticModel.GetSymbolInfo(typeArgument); - if (symbolInfo.Symbol != null && symbolInfo.Symbol is ITypeSymbol typeSymbol && typeSymbol.TypeKind != TypeKind.Interface) - { - var diagnostic = Diagnostic.Create(Rule, typeArgument.GetLocation()); - context.ReportDiagnostic(diagnostic); - } + var diagnostic = Diagnostic.Create(Rule, typeArgument.GetLocation()); + context.ReportDiagnostic(diagnostic); } } } diff --git a/Source/Moq.Analyzers/CallbackSignatureShouldMatchMockedMethodAnalyzer.cs b/Source/Moq.Analyzers/CallbackSignatureShouldMatchMockedMethodAnalyzer.cs index ee232562..2400bd2d 100644 --- a/Source/Moq.Analyzers/CallbackSignatureShouldMatchMockedMethodAnalyzer.cs +++ b/Source/Moq.Analyzers/CallbackSignatureShouldMatchMockedMethodAnalyzer.cs @@ -1,76 +1,75 @@ -namespace Moq.Analyzers +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Moq.Analyzers; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public class CallbackSignatureShouldMatchMockedMethodAnalyzer : DiagnosticAnalyzer { - using System.Collections.Immutable; - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp; - using Microsoft.CodeAnalysis.CSharp.Syntax; - using Microsoft.CodeAnalysis.Diagnostics; + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + Diagnostics.CallbackSignatureShouldMatchMockedMethodId, + Diagnostics.CallbackSignatureShouldMatchMockedMethodTitle, + Diagnostics.CallbackSignatureShouldMatchMockedMethodMessage, + Diagnostics.Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public class CallbackSignatureShouldMatchMockedMethodAnalyzer : DiagnosticAnalyzer + public override ImmutableArray SupportedDiagnostics { - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( - Diagnostics.CallbackSignatureShouldMatchMockedMethodId, - Diagnostics.CallbackSignatureShouldMatchMockedMethodTitle, - Diagnostics.CallbackSignatureShouldMatchMockedMethodMessage, - Diagnostics.Category, - DiagnosticSeverity.Warning, - isEnabledByDefault: true); - - public override ImmutableArray SupportedDiagnostics - { - get { return ImmutableArray.Create(Rule); } - } + get { return ImmutableArray.Create(Rule); } + } - public override void Initialize(AnalysisContext context) - { - context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.InvocationExpression); - } + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.InvocationExpression); + } - private static void Analyze(SyntaxNodeAnalysisContext context) - { - var callbackOrReturnsInvocation = (InvocationExpressionSyntax)context.Node; + private static void Analyze(SyntaxNodeAnalysisContext context) + { + var callbackOrReturnsInvocation = (InvocationExpressionSyntax)context.Node; - var callbackOrReturnsMethodArguments = callbackOrReturnsInvocation.ArgumentList.Arguments; + var callbackOrReturnsMethodArguments = callbackOrReturnsInvocation.ArgumentList.Arguments; - // Ignoring Callback() and Return() calls without lambda arguments - if (callbackOrReturnsMethodArguments.Count == 0) return; + // Ignoring Callback() and Return() calls without lambda arguments + if (callbackOrReturnsMethodArguments.Count == 0) return; - if (!Helpers.IsCallbackOrReturnInvocation(context.SemanticModel, callbackOrReturnsInvocation)) return; + if (!Helpers.IsCallbackOrReturnInvocation(context.SemanticModel, callbackOrReturnsInvocation)) return; - var callbackLambda = callbackOrReturnsInvocation.ArgumentList.Arguments[0]?.Expression as ParenthesizedLambdaExpressionSyntax; + var callbackLambda = callbackOrReturnsInvocation.ArgumentList.Arguments[0]?.Expression as ParenthesizedLambdaExpressionSyntax; - // Ignoring callbacks without lambda - if (callbackLambda == null) return; + // Ignoring callbacks without lambda + if (callbackLambda == null) return; - // Ignoring calls with no arguments because those are valid in Moq - var lambdaParameters = callbackLambda.ParameterList.Parameters; - if (lambdaParameters.Count == 0) return; + // Ignoring calls with no arguments because those are valid in Moq + var lambdaParameters = callbackLambda.ParameterList.Parameters; + if (lambdaParameters.Count == 0) return; - var setupInvocation = Helpers.FindSetupMethodFromCallbackInvocation(context.SemanticModel, callbackOrReturnsInvocation); - var mockedMethodInvocation = Helpers.FindMockedMethodInvocationFromSetupMethod(setupInvocation); - if (mockedMethodInvocation == null) return; + var setupInvocation = Helpers.FindSetupMethodFromCallbackInvocation(context.SemanticModel, callbackOrReturnsInvocation); + var mockedMethodInvocation = Helpers.FindMockedMethodInvocationFromSetupMethod(setupInvocation); + if (mockedMethodInvocation == null) return; - var mockedMethodArguments = mockedMethodInvocation.ArgumentList.Arguments; + var mockedMethodArguments = mockedMethodInvocation.ArgumentList.Arguments; - if (mockedMethodArguments.Count != lambdaParameters.Count) - { - var diagnostic = Diagnostic.Create(Rule, callbackLambda.ParameterList.GetLocation()); - context.ReportDiagnostic(diagnostic); - } - else + if (mockedMethodArguments.Count != lambdaParameters.Count) + { + var diagnostic = Diagnostic.Create(Rule, callbackLambda.ParameterList.GetLocation()); + context.ReportDiagnostic(diagnostic); + } + else + { + for (int i = 0; i < mockedMethodArguments.Count; i++) { - for (int i = 0; i < mockedMethodArguments.Count; i++) + var mockedMethodArgumentType = context.SemanticModel.GetTypeInfo(mockedMethodArguments[i].Expression); + var lambdaParameterType = context.SemanticModel.GetTypeInfo(lambdaParameters[i].Type); + string mockedMethodTypeName = mockedMethodArgumentType.ConvertedType.ToString(); + string lambdaParameterTypeName = lambdaParameterType.ConvertedType.ToString(); + if (mockedMethodTypeName != lambdaParameterTypeName) { - var mockedMethodArgumentType = context.SemanticModel.GetTypeInfo(mockedMethodArguments[i].Expression); - var lambdaParameterType = context.SemanticModel.GetTypeInfo(lambdaParameters[i].Type); - string mockedMethodTypeName = mockedMethodArgumentType.ConvertedType.ToString(); - string lambdaParameterTypeName = lambdaParameterType.ConvertedType.ToString(); - if (mockedMethodTypeName != lambdaParameterTypeName) - { - var diagnostic = Diagnostic.Create(Rule, callbackLambda.ParameterList.GetLocation()); - context.ReportDiagnostic(diagnostic); - } + var diagnostic = Diagnostic.Create(Rule, callbackLambda.ParameterList.GetLocation()); + context.ReportDiagnostic(diagnostic); } } } diff --git a/Source/Moq.Analyzers/CallbackSignatureShouldMatchMockedMethodCodeFix.cs b/Source/Moq.Analyzers/CallbackSignatureShouldMatchMockedMethodCodeFix.cs index 6be01992..79d34091 100644 --- a/Source/Moq.Analyzers/CallbackSignatureShouldMatchMockedMethodCodeFix.cs +++ b/Source/Moq.Analyzers/CallbackSignatureShouldMatchMockedMethodCodeFix.cs @@ -1,73 +1,72 @@ -namespace Moq.Analyzers -{ - using System.Collections.Immutable; - using System.Composition; - using System.Linq; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CodeActions; - using Microsoft.CodeAnalysis.CodeFixes; - using Microsoft.CodeAnalysis.CSharp; - using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Moq.Analyzers; - [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CallbackSignatureShouldMatchMockedMethodCodeFix))] - [Shared] - public class CallbackSignatureShouldMatchMockedMethodCodeFix : CodeFixProvider +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CallbackSignatureShouldMatchMockedMethodCodeFix))] +[Shared] +public class CallbackSignatureShouldMatchMockedMethodCodeFix : CodeFixProvider +{ + public sealed override ImmutableArray FixableDiagnosticIds { - public sealed override ImmutableArray FixableDiagnosticIds - { - get { return ImmutableArray.Create(Diagnostics.CallbackSignatureShouldMatchMockedMethodId); } - } + get { return ImmutableArray.Create(Diagnostics.CallbackSignatureShouldMatchMockedMethodId); } + } - public sealed override FixAllProvider GetFixAllProvider() - { - return WellKnownFixAllProviders.BatchFixer; - } + public sealed override FixAllProvider GetFixAllProvider() + { + return WellKnownFixAllProviders.BatchFixer; + } - public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) - { - var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); - var diagnostic = context.Diagnostics.First(); - var diagnosticSpan = diagnostic.Location.SourceSpan; + var diagnostic = context.Diagnostics.First(); + var diagnosticSpan = diagnostic.Location.SourceSpan; - // Find the type declaration identified by the diagnostic. - var badArgumentListSyntax = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType().First(); + // Find the type declaration identified by the diagnostic. + var badArgumentListSyntax = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType().First(); - // Register a code action that will invoke the fix. - context.RegisterCodeFix( - CodeAction.Create( - title: "Fix Moq callback signature", - createChangedDocument: c => FixCallbackSignature(root, context.Document, badArgumentListSyntax, c), - equivalenceKey: "Fix Moq callback signature"), - diagnostic); - } + // Register a code action that will invoke the fix. + context.RegisterCodeFix( + CodeAction.Create( + title: "Fix Moq callback signature", + createChangedDocument: c => FixCallbackSignature(root, context.Document, badArgumentListSyntax, c), + equivalenceKey: "Fix Moq callback signature"), + diagnostic); + } - private async Task FixCallbackSignature(SyntaxNode root, Document document, ParameterListSyntax oldParameters, CancellationToken cancellationToken) + private async Task FixCallbackSignature(SyntaxNode root, Document document, ParameterListSyntax oldParameters, CancellationToken cancellationToken) + { + var semanticModel = await document.GetSemanticModelAsync(cancellationToken); + var callbackInvocation = oldParameters?.Parent?.Parent?.Parent?.Parent as InvocationExpressionSyntax; + if (callbackInvocation != null) { - var semanticModel = await document.GetSemanticModelAsync(cancellationToken); - var callbackInvocation = oldParameters?.Parent?.Parent?.Parent?.Parent as InvocationExpressionSyntax; - if (callbackInvocation != null) - { - var setupMethodInvocation = Helpers.FindSetupMethodFromCallbackInvocation(semanticModel, callbackInvocation); - var matchingMockedMethods = Helpers.GetAllMatchingMockedMethodSymbolsFromSetupMethodInvocation(semanticModel, setupMethodInvocation).ToArray(); + var setupMethodInvocation = Helpers.FindSetupMethodFromCallbackInvocation(semanticModel, callbackInvocation); + var matchingMockedMethods = Helpers.GetAllMatchingMockedMethodSymbolsFromSetupMethodInvocation(semanticModel, setupMethodInvocation).ToArray(); - if (matchingMockedMethods.Length == 1) - { - var newParameters = SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(matchingMockedMethods[0].Parameters.Select( - p => - { - var type = SyntaxFactory.ParseTypeName(p.Type.ToMinimalDisplayString(semanticModel, oldParameters.SpanStart)); - return SyntaxFactory.Parameter(default(SyntaxList), SyntaxFactory.TokenList(), type, SyntaxFactory.Identifier(p.Name), null); - }))); + if (matchingMockedMethods.Length == 1) + { + var newParameters = SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(matchingMockedMethods[0].Parameters.Select( + p => + { + var type = SyntaxFactory.ParseTypeName(p.Type.ToMinimalDisplayString(semanticModel, oldParameters.SpanStart)); + return SyntaxFactory.Parameter(default(SyntaxList), SyntaxFactory.TokenList(), type, SyntaxFactory.Identifier(p.Name), null); + }))); - var newRoot = root.ReplaceNode(oldParameters, newParameters); - return document.WithSyntaxRoot(newRoot); - } + var newRoot = root.ReplaceNode(oldParameters, newParameters); + return document.WithSyntaxRoot(newRoot); } - - return document; } + + return document; } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs b/Source/Moq.Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs index 793d6d92..8a661bf4 100644 --- a/Source/Moq.Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs +++ b/Source/Moq.Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs @@ -1,165 +1,164 @@ -namespace Moq.Analyzers +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Moq.Analyzers; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public class ConstructorArgumentsShouldMatchAnalyzer : DiagnosticAnalyzer { - using System.Collections.Immutable; - using System.Linq; - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp; - using Microsoft.CodeAnalysis.CSharp.Syntax; - using Microsoft.CodeAnalysis.Diagnostics; - - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public class ConstructorArgumentsShouldMatchAnalyzer : DiagnosticAnalyzer + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + Diagnostics.ConstructorArgumentsShouldMatchId, + Diagnostics.ConstructorArgumentsShouldMatchTitle, + Diagnostics.ConstructorArgumentsShouldMatchMessage, + Diagnostics.Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics { - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( - Diagnostics.ConstructorArgumentsShouldMatchId, - Diagnostics.ConstructorArgumentsShouldMatchTitle, - Diagnostics.ConstructorArgumentsShouldMatchMessage, - Diagnostics.Category, - DiagnosticSeverity.Warning, - isEnabledByDefault: true); - - public override ImmutableArray SupportedDiagnostics - { - get { return ImmutableArray.Create(Rule); } - } + get { return ImmutableArray.Create(Rule); } + } - public override void Initialize(AnalysisContext context) - { - context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.ObjectCreationExpression); - } + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.ObjectCreationExpression); + } - private static void Analyze(SyntaxNodeAnalysisContext context) - { - var objectCreation = (ObjectCreationExpressionSyntax)context.Node; + private static void Analyze(SyntaxNodeAnalysisContext context) + { + var objectCreation = (ObjectCreationExpressionSyntax)context.Node; - var genericName = GetGenericNameSyntax(objectCreation.Type); - if (genericName == null) return; + var genericName = GetGenericNameSyntax(objectCreation.Type); + if (genericName == null) return; - if (!IsMockGenericType(genericName)) return; + if (!IsMockGenericType(genericName)) return; - // Full check that we are calling new Mock() - var constructorSymbol = GetConstructorSymbol(context, objectCreation); + // Full check that we are calling new Mock() + var constructorSymbol = GetConstructorSymbol(context, objectCreation); - // Vararg parameter is the one that takes all arguments for mocked class constructor - var varArgsConstructorParameter = constructorSymbol.Parameters.FirstOrDefault(x => x.IsParams); + // Vararg parameter is the one that takes all arguments for mocked class constructor + var varArgsConstructorParameter = constructorSymbol.Parameters.FirstOrDefault(x => x.IsParams); - // Vararg parameter are not used, so there are no arguments for mocked class constructor - if (varArgsConstructorParameter == null) return; - var varArgsConstructorParameterIdx = constructorSymbol.Parameters.IndexOf(varArgsConstructorParameter); + // Vararg parameter are not used, so there are no arguments for mocked class constructor + if (varArgsConstructorParameter == null) return; + var varArgsConstructorParameterIdx = constructorSymbol.Parameters.IndexOf(varArgsConstructorParameter); - // Find mocked type - var mockedTypeSymbol = GetMockedSymbol(context, genericName); - if (mockedTypeSymbol == null) return; + // Find mocked type + var mockedTypeSymbol = GetMockedSymbol(context, genericName); + if (mockedTypeSymbol == null) return; - // Skip first argument if it is not vararg - typically it is MockingBehavior argument - var constructorArguments = objectCreation.ArgumentList.Arguments.Skip(varArgsConstructorParameterIdx == 0 ? 0 : 1).ToArray(); + // Skip first argument if it is not vararg - typically it is MockingBehavior argument + var constructorArguments = objectCreation.ArgumentList.Arguments.Skip(varArgsConstructorParameterIdx == 0 ? 0 : 1).ToArray(); - if (!mockedTypeSymbol.IsAbstract) - { - if (IsConstructorMismatch(context, objectCreation, genericName, constructorArguments)) - { - var diagnostic = Diagnostic.Create(Rule, objectCreation.ArgumentList.GetLocation()); - context.ReportDiagnostic(diagnostic); - } - } - else + if (!mockedTypeSymbol.IsAbstract) + { + if (IsConstructorMismatch(context, objectCreation, genericName, constructorArguments)) { - // Issue #1: Currently detection does not work well for abstract classes because they cannot be instantiated - - // The mocked symbol is abstract, so we need to check if the constructor arguments match the abstract class constructor - - // Extract types of arguments passed in the constructor call - var argumentTypes = constructorArguments - .Select(arg => context.SemanticModel.GetTypeInfo(arg.Expression).Type) - .ToArray(); - - // Check all constructors of the abstract type - foreach (var constructor in mockedTypeSymbol.Constructors) - { - if (AreParametersMatching(constructor.Parameters, argumentTypes)) - { - return; // Found a matching constructor - } - } - var diagnostic = Diagnostic.Create(Rule, objectCreation.ArgumentList.GetLocation()); context.ReportDiagnostic(diagnostic); } } - - private static INamedTypeSymbol GetMockedSymbol( - SyntaxNodeAnalysisContext context, - GenericNameSyntax genericName) + else { - var typeArguments = genericName.TypeArgumentList.Arguments; - if (typeArguments == null || typeArguments.Count != 1) return null; - var mockedTypeSymbolInfo = context.SemanticModel.GetSymbolInfo(typeArguments[0]); - var mockedTypeSymbol = mockedTypeSymbolInfo.Symbol as INamedTypeSymbol; - if (mockedTypeSymbol == null || mockedTypeSymbol.TypeKind != TypeKind.Class) return null; - return mockedTypeSymbol; - } + // Issue #1: Currently detection does not work well for abstract classes because they cannot be instantiated - private static bool AreParametersMatching(ImmutableArray constructorParameters, ITypeSymbol[] argumentTypes2) - { - // Check if the number of parameters matches - if (constructorParameters.Length != argumentTypes2.Length) - { - return false; - } + // The mocked symbol is abstract, so we need to check if the constructor arguments match the abstract class constructor + + // Extract types of arguments passed in the constructor call + var argumentTypes = constructorArguments + .Select(arg => context.SemanticModel.GetTypeInfo(arg.Expression).Type) + .ToArray(); - // Check if each parameter type matches in order - for (int i = 0; i < constructorParameters.Length; i++) + // Check all constructors of the abstract type + foreach (var constructor in mockedTypeSymbol.Constructors) { - if (!constructorParameters[i].Type.Equals(argumentTypes2[i])) + if (AreParametersMatching(constructor.Parameters, argumentTypes)) { - return false; + return; // Found a matching constructor } } - return true; + var diagnostic = Diagnostic.Create(Rule, objectCreation.ArgumentList.GetLocation()); + context.ReportDiagnostic(diagnostic); } + } - private static GenericNameSyntax GetGenericNameSyntax(TypeSyntax typeSyntax) + private static INamedTypeSymbol GetMockedSymbol( + SyntaxNodeAnalysisContext context, + GenericNameSyntax genericName) + { + var typeArguments = genericName.TypeArgumentList.Arguments; + if (typeArguments == null || typeArguments.Count != 1) return null; + var mockedTypeSymbolInfo = context.SemanticModel.GetSymbolInfo(typeArguments[0]); + var mockedTypeSymbol = mockedTypeSymbolInfo.Symbol as INamedTypeSymbol; + if (mockedTypeSymbol == null || mockedTypeSymbol.TypeKind != TypeKind.Class) return null; + return mockedTypeSymbol; + } + + private static bool AreParametersMatching(ImmutableArray constructorParameters, ITypeSymbol[] argumentTypes2) + { + // Check if the number of parameters matches + if (constructorParameters.Length != argumentTypes2.Length) { - if (typeSyntax is GenericNameSyntax genericNameSyntax) - { - return genericNameSyntax; - } + return false; + } - if (typeSyntax is QualifiedNameSyntax qualifiedNameSyntax) + // Check if each parameter type matches in order + for (int i = 0; i < constructorParameters.Length; i++) + { + if (!constructorParameters[i].Type.Equals(argumentTypes2[i])) { - return qualifiedNameSyntax.Right as GenericNameSyntax; + return false; } - - return null; } - private static bool IsMockGenericType(GenericNameSyntax genericName) + return true; + } + + private static GenericNameSyntax GetGenericNameSyntax(TypeSyntax typeSyntax) + { + if (typeSyntax is GenericNameSyntax genericNameSyntax) { - return genericName?.Identifier.Text == "Mock" && genericName.TypeArgumentList.Arguments.Count == 1; + return genericNameSyntax; } - private static IMethodSymbol GetConstructorSymbol(SyntaxNodeAnalysisContext context, ObjectCreationExpressionSyntax objectCreation) + if (typeSyntax is QualifiedNameSyntax qualifiedNameSyntax) { - var constructorSymbolInfo = context.SemanticModel.GetSymbolInfo(objectCreation); - var constructorSymbol = constructorSymbolInfo.Symbol as IMethodSymbol; - return constructorSymbol?.MethodKind == MethodKind.Constructor && - constructorSymbol.ContainingType?.ConstructedFrom.ToDisplayString() == "Moq.Mock" - ? constructorSymbol - : null; + return qualifiedNameSyntax.Right as GenericNameSyntax; } - private static bool IsConstructorMismatch(SyntaxNodeAnalysisContext context, ObjectCreationExpressionSyntax objectCreation, GenericNameSyntax genericName, ArgumentSyntax[] constructorArguments) - { - var fakeConstructorCall = SyntaxFactory.ObjectCreationExpression( - genericName.TypeArgumentList.Arguments.First(), - SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(constructorArguments)), - null); + return null; + } - var mockedClassConstructorSymbolInfo = context.SemanticModel.GetSpeculativeSymbolInfo( - objectCreation.SpanStart, fakeConstructorCall, SpeculativeBindingOption.BindAsExpression); + private static bool IsMockGenericType(GenericNameSyntax genericName) + { + return genericName?.Identifier.Text == "Mock" && genericName.TypeArgumentList.Arguments.Count == 1; + } - return mockedClassConstructorSymbolInfo.Symbol == null; - } + private static IMethodSymbol GetConstructorSymbol(SyntaxNodeAnalysisContext context, ObjectCreationExpressionSyntax objectCreation) + { + var constructorSymbolInfo = context.SemanticModel.GetSymbolInfo(objectCreation); + var constructorSymbol = constructorSymbolInfo.Symbol as IMethodSymbol; + return constructorSymbol?.MethodKind == MethodKind.Constructor && + constructorSymbol.ContainingType?.ConstructedFrom.ToDisplayString() == "Moq.Mock" + ? constructorSymbol + : null; + } + + private static bool IsConstructorMismatch(SyntaxNodeAnalysisContext context, ObjectCreationExpressionSyntax objectCreation, GenericNameSyntax genericName, ArgumentSyntax[] constructorArguments) + { + var fakeConstructorCall = SyntaxFactory.ObjectCreationExpression( + genericName.TypeArgumentList.Arguments.First(), + SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(constructorArguments)), + null); + + var mockedClassConstructorSymbolInfo = context.SemanticModel.GetSpeculativeSymbolInfo( + objectCreation.SpanStart, fakeConstructorCall, SpeculativeBindingOption.BindAsExpression); + + return mockedClassConstructorSymbolInfo.Symbol == null; } } diff --git a/Source/Moq.Analyzers/Diagnostics.cs b/Source/Moq.Analyzers/Diagnostics.cs index ee482725..f643c369 100644 --- a/Source/Moq.Analyzers/Diagnostics.cs +++ b/Source/Moq.Analyzers/Diagnostics.cs @@ -1,40 +1,39 @@ -namespace Moq.Analyzers +namespace Moq.Analyzers; + +internal static class Diagnostics { - internal static class Diagnostics - { - internal const string Category = "Moq"; + internal const string Category = "Moq"; - internal const string NoSealedClassMocksId = "Moq1000"; - internal const string NoSealedClassMocksTitle = "Moq: Sealed class mocked"; - internal const string NoSealedClassMocksMessage = "Sealed classes cannot be mocked."; + internal const string NoSealedClassMocksId = "Moq1000"; + internal const string NoSealedClassMocksTitle = "Moq: Sealed class mocked"; + internal const string NoSealedClassMocksMessage = "Sealed classes cannot be mocked."; - internal const string NoConstructorArgumentsForInterfaceMockId = "Moq1001"; - internal const string NoConstructorArgumentsForInterfaceMockTitle = "Moq: Parameters specified for mocked interface"; - internal const string NoConstructorArgumentsForInterfaceMockMessage = "Mocked interfaces cannot have constructor parameters."; + internal const string NoConstructorArgumentsForInterfaceMockId = "Moq1001"; + internal const string NoConstructorArgumentsForInterfaceMockTitle = "Moq: Parameters specified for mocked interface"; + internal const string NoConstructorArgumentsForInterfaceMockMessage = "Mocked interfaces cannot have constructor parameters."; - internal const string ConstructorArgumentsShouldMatchId = "Moq1002"; - internal const string ConstructorArgumentsShouldMatchTitle = "Moq: No matching constructor"; - internal const string ConstructorArgumentsShouldMatchMessage = "Parameters provided into mock do not match any existing constructors."; + internal const string ConstructorArgumentsShouldMatchId = "Moq1002"; + internal const string ConstructorArgumentsShouldMatchTitle = "Moq: No matching constructor"; + internal const string ConstructorArgumentsShouldMatchMessage = "Parameters provided into mock do not match any existing constructors."; - internal const string CallbackSignatureShouldMatchMockedMethodId = "Moq1100"; - internal const string CallbackSignatureShouldMatchMockedMethodTitle = "Moq: Bad callback parameters"; - internal const string CallbackSignatureShouldMatchMockedMethodMessage = "Callback signature must match the signature of the mocked method."; + internal const string CallbackSignatureShouldMatchMockedMethodId = "Moq1100"; + internal const string CallbackSignatureShouldMatchMockedMethodTitle = "Moq: Bad callback parameters"; + internal const string CallbackSignatureShouldMatchMockedMethodMessage = "Callback signature must match the signature of the mocked method."; - internal const string NoMethodsInPropertySetupId = "Moq1101"; - internal const string NoMethodsInPropertySetupTitle = "Moq: Property setup used for a method"; - internal const string NoMethodsInPropertySetupMessage = "SetupGet/SetupSet should be used for properties, not for methods."; + internal const string NoMethodsInPropertySetupId = "Moq1101"; + internal const string NoMethodsInPropertySetupTitle = "Moq: Property setup used for a method"; + internal const string NoMethodsInPropertySetupMessage = "SetupGet/SetupSet should be used for properties, not for methods."; - internal const string SetupShouldBeUsedOnlyForOverridableMembersId = "Moq1200"; - internal const string SetupShouldBeUsedOnlyForOverridableMembersTitle = "Moq: Invalid setup parameter"; - internal const string SetupShouldBeUsedOnlyForOverridableMembersMessage = "Setup should be used only for overridable members."; + internal const string SetupShouldBeUsedOnlyForOverridableMembersId = "Moq1200"; + internal const string SetupShouldBeUsedOnlyForOverridableMembersTitle = "Moq: Invalid setup parameter"; + internal const string SetupShouldBeUsedOnlyForOverridableMembersMessage = "Setup should be used only for overridable members."; - internal const string SetupShouldNotIncludeAsyncResultId = "Moq1201"; - internal const string SetupShouldNotIncludeAsyncResultTitle = SetupShouldBeUsedOnlyForOverridableMembersTitle; - internal const string SetupShouldNotIncludeAsyncResultMessage = "Setup of async methods should use ReturnsAsync instead of .Result"; + internal const string SetupShouldNotIncludeAsyncResultId = "Moq1201"; + internal const string SetupShouldNotIncludeAsyncResultTitle = SetupShouldBeUsedOnlyForOverridableMembersTitle; + internal const string SetupShouldNotIncludeAsyncResultMessage = "Setup of async methods should use ReturnsAsync instead of .Result"; - internal const string AsShouldBeUsedOnlyForInterfaceId = "Moq1300"; - internal const string AsShouldBeUsedOnlyForInterfaceTitle = "Moq: Invalid As type parameter"; - internal const string AsShouldBeUsedOnlyForInterfaceMessage = "Mock.As() should take interfaces only"; - } + internal const string AsShouldBeUsedOnlyForInterfaceId = "Moq1300"; + internal const string AsShouldBeUsedOnlyForInterfaceTitle = "Moq: Invalid As type parameter"; + internal const string AsShouldBeUsedOnlyForInterfaceMessage = "Mock.As() should take interfaces only"; } diff --git a/Source/Moq.Analyzers/Helpers.cs b/Source/Moq.Analyzers/Helpers.cs index 88249ed8..10e190f3 100644 --- a/Source/Moq.Analyzers/Helpers.cs +++ b/Source/Moq.Analyzers/Helpers.cs @@ -1,107 +1,106 @@ -namespace Moq.Analyzers -{ - using System.Collections.Generic; - using System.Linq; - using System.Text.RegularExpressions; - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp.Syntax; - - internal static class Helpers - { - private static MoqMethodDescriptor moqSetupMethodDescriptor = new MoqMethodDescriptor("Setup", new Regex("^Moq\\.Mock<.*>\\.Setup\\.*")); +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; - private static MoqMethodDescriptor moqAsMethodDescriptor = new MoqMethodDescriptor("As", new Regex("^Moq\\.Mock\\.As<\\.*"), isGeneric: true); +namespace Moq.Analyzers; - internal static bool IsMoqSetupMethod(SemanticModel semanticModel, MemberAccessExpressionSyntax method) - { - return moqSetupMethodDescriptor.IsMoqMethod(semanticModel, method); - } +internal static class Helpers +{ + private static MoqMethodDescriptor moqSetupMethodDescriptor = new MoqMethodDescriptor("Setup", new Regex("^Moq\\.Mock<.*>\\.Setup\\.*")); - internal static bool IsMoqAsMethod(SemanticModel semanticModel, MemberAccessExpressionSyntax method) - { - return moqAsMethodDescriptor.IsMoqMethod(semanticModel, method); - } + private static MoqMethodDescriptor moqAsMethodDescriptor = new MoqMethodDescriptor("As", new Regex("^Moq\\.Mock\\.As<\\.*"), isGeneric: true); - internal static bool IsCallbackOrReturnInvocation(SemanticModel semanticModel, InvocationExpressionSyntax callbackOrReturnsInvocation) - { - var callbackOrReturnsMethod = callbackOrReturnsInvocation.Expression as MemberAccessExpressionSyntax; - var methodName = callbackOrReturnsMethod?.Name.ToString(); + internal static bool IsMoqSetupMethod(SemanticModel semanticModel, MemberAccessExpressionSyntax method) + { + return moqSetupMethodDescriptor.IsMoqMethod(semanticModel, method); + } - // First fast check before walking semantic model - if (methodName != "Callback" && methodName != "Returns") - { - return false; - } + internal static bool IsMoqAsMethod(SemanticModel semanticModel, MemberAccessExpressionSyntax method) + { + return moqAsMethodDescriptor.IsMoqMethod(semanticModel, method); + } - var symbolInfo = semanticModel.GetSymbolInfo(callbackOrReturnsMethod); - if (symbolInfo.CandidateReason == CandidateReason.OverloadResolutionFailure) - { - return symbolInfo.CandidateSymbols.Any(s => IsCallbackOrReturnSymbol(s)); - } - else if (symbolInfo.CandidateReason == CandidateReason.None) - { - return IsCallbackOrReturnSymbol(symbolInfo.Symbol); - } + internal static bool IsCallbackOrReturnInvocation(SemanticModel semanticModel, InvocationExpressionSyntax callbackOrReturnsInvocation) + { + var callbackOrReturnsMethod = callbackOrReturnsInvocation.Expression as MemberAccessExpressionSyntax; + var methodName = callbackOrReturnsMethod?.Name.ToString(); + // First fast check before walking semantic model + if (methodName != "Callback" && methodName != "Returns") + { return false; } - internal static InvocationExpressionSyntax FindSetupMethodFromCallbackInvocation(SemanticModel semanticModel, ExpressionSyntax expression) + var symbolInfo = semanticModel.GetSymbolInfo(callbackOrReturnsMethod); + if (symbolInfo.CandidateReason == CandidateReason.OverloadResolutionFailure) { - var invocation = expression as InvocationExpressionSyntax; - var method = invocation?.Expression as MemberAccessExpressionSyntax; - if (method == null) return null; - if (IsMoqSetupMethod(semanticModel, method)) return invocation; - return FindSetupMethodFromCallbackInvocation(semanticModel, method.Expression); + return symbolInfo.CandidateSymbols.Any(s => IsCallbackOrReturnSymbol(s)); } - - internal static InvocationExpressionSyntax FindMockedMethodInvocationFromSetupMethod(InvocationExpressionSyntax setupInvocation) + else if (symbolInfo.CandidateReason == CandidateReason.None) { - var setupLambdaArgument = setupInvocation?.ArgumentList.Arguments[0]?.Expression as LambdaExpressionSyntax; - return setupLambdaArgument?.Body as InvocationExpressionSyntax; + return IsCallbackOrReturnSymbol(symbolInfo.Symbol); } - internal static ExpressionSyntax FindMockedMemberExpressionFromSetupMethod(InvocationExpressionSyntax setupInvocation) - { - var setupLambdaArgument = setupInvocation?.ArgumentList.Arguments[0]?.Expression as LambdaExpressionSyntax; - return setupLambdaArgument?.Body as ExpressionSyntax; - } + return false; + } - internal static IEnumerable GetAllMatchingMockedMethodSymbolsFromSetupMethodInvocation(SemanticModel semanticModel, InvocationExpressionSyntax setupMethodInvocation) - { - var setupLambdaArgument = setupMethodInvocation?.ArgumentList.Arguments[0]?.Expression as LambdaExpressionSyntax; - var mockedMethodInvocation = setupLambdaArgument?.Body as InvocationExpressionSyntax; + internal static InvocationExpressionSyntax FindSetupMethodFromCallbackInvocation(SemanticModel semanticModel, ExpressionSyntax expression) + { + var invocation = expression as InvocationExpressionSyntax; + var method = invocation?.Expression as MemberAccessExpressionSyntax; + if (method == null) return null; + if (IsMoqSetupMethod(semanticModel, method)) return invocation; + return FindSetupMethodFromCallbackInvocation(semanticModel, method.Expression); + } - return GetAllMatchingSymbols(semanticModel, mockedMethodInvocation); - } + internal static InvocationExpressionSyntax FindMockedMethodInvocationFromSetupMethod(InvocationExpressionSyntax setupInvocation) + { + var setupLambdaArgument = setupInvocation?.ArgumentList.Arguments[0]?.Expression as LambdaExpressionSyntax; + return setupLambdaArgument?.Body as InvocationExpressionSyntax; + } + + internal static ExpressionSyntax FindMockedMemberExpressionFromSetupMethod(InvocationExpressionSyntax setupInvocation) + { + var setupLambdaArgument = setupInvocation?.ArgumentList.Arguments[0]?.Expression as LambdaExpressionSyntax; + return setupLambdaArgument?.Body as ExpressionSyntax; + } + + internal static IEnumerable GetAllMatchingMockedMethodSymbolsFromSetupMethodInvocation(SemanticModel semanticModel, InvocationExpressionSyntax setupMethodInvocation) + { + var setupLambdaArgument = setupMethodInvocation?.ArgumentList.Arguments[0]?.Expression as LambdaExpressionSyntax; + var mockedMethodInvocation = setupLambdaArgument?.Body as InvocationExpressionSyntax; - internal static IEnumerable GetAllMatchingSymbols(SemanticModel semanticModel, ExpressionSyntax expression) - where T : class + return GetAllMatchingSymbols(semanticModel, mockedMethodInvocation); + } + + internal static IEnumerable GetAllMatchingSymbols(SemanticModel semanticModel, ExpressionSyntax expression) + where T : class + { + var matchingSymbols = new List(); + if (expression != null) { - var matchingSymbols = new List(); - if (expression != null) + var symbolInfo = semanticModel.GetSymbolInfo(expression); + if (symbolInfo.CandidateReason == CandidateReason.None && symbolInfo.Symbol is T) { - var symbolInfo = semanticModel.GetSymbolInfo(expression); - if (symbolInfo.CandidateReason == CandidateReason.None && symbolInfo.Symbol is T) - { - matchingSymbols.Add(symbolInfo.Symbol as T); - } - else if (symbolInfo.CandidateReason == CandidateReason.OverloadResolutionFailure) - { - matchingSymbols.AddRange(symbolInfo.CandidateSymbols.OfType()); - } + matchingSymbols.Add(symbolInfo.Symbol as T); + } + else if (symbolInfo.CandidateReason == CandidateReason.OverloadResolutionFailure) + { + matchingSymbols.AddRange(symbolInfo.CandidateSymbols.OfType()); } - - return matchingSymbols; } - private static bool IsCallbackOrReturnSymbol(ISymbol symbol) - { - // TODO: Check what is the best way to do such checks - var methodSymbol = symbol as IMethodSymbol; - if (methodSymbol == null) return false; - var methodName = methodSymbol.ToString(); - return methodName.StartsWith("Moq.Language.ICallback") || methodName.StartsWith("Moq.Language.IReturns"); - } + return matchingSymbols; + } + + private static bool IsCallbackOrReturnSymbol(ISymbol symbol) + { + // TODO: Check what is the best way to do such checks + var methodSymbol = symbol as IMethodSymbol; + if (methodSymbol == null) return false; + var methodName = methodSymbol.ToString(); + return methodName.StartsWith("Moq.Language.ICallback") || methodName.StartsWith("Moq.Language.IReturns"); } } diff --git a/Source/Moq.Analyzers/MoqMethodDescriptor.cs b/Source/Moq.Analyzers/MoqMethodDescriptor.cs index 10c9746b..d717d425 100644 --- a/Source/Moq.Analyzers/MoqMethodDescriptor.cs +++ b/Source/Moq.Analyzers/MoqMethodDescriptor.cs @@ -1,55 +1,54 @@ -namespace Moq.Analyzers +using System.Linq; +using System.Text.RegularExpressions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Moq.Analyzers; + +internal class MoqMethodDescriptor { - using System.Linq; - using System.Text.RegularExpressions; - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp.Syntax; + private readonly bool isGeneric; - internal class MoqMethodDescriptor + public MoqMethodDescriptor(string shortMethodName, Regex fullMethodNamePattern, bool isGeneric = false) { - private readonly bool isGeneric; + this.isGeneric = isGeneric; + ShortMethodName = shortMethodName; + FullMethodNamePattern = fullMethodNamePattern; + } - public MoqMethodDescriptor(string shortMethodName, Regex fullMethodNamePattern, bool isGeneric=false) - { - this.isGeneric = isGeneric; - ShortMethodName = shortMethodName; - FullMethodNamePattern = fullMethodNamePattern; - } + private string ShortMethodName { get; } + + private Regex FullMethodNamePattern { get; } - private string ShortMethodName { get; } + public bool IsMoqMethod(SemanticModel semanticModel, MemberAccessExpressionSyntax method) + { + var methodName = method?.Name.ToString(); - private Regex FullMethodNamePattern { get; } + // First fast check before walking semantic model + if (DoesShortMethodMatch(methodName) == false) return false; - public bool IsMoqMethod(SemanticModel semanticModel, MemberAccessExpressionSyntax method) + var symbolInfo = semanticModel.GetSymbolInfo(method); + if (symbolInfo.CandidateReason == CandidateReason.OverloadResolutionFailure) + { + return symbolInfo.CandidateSymbols.OfType() + .Any(s => this.FullMethodNamePattern.IsMatch(s.ToString())); + } + else if (symbolInfo.CandidateReason == CandidateReason.None) { - var methodName = method?.Name.ToString(); - - // First fast check before walking semantic model - if (DoesShortMethodMatch(methodName) == false) return false; - - var symbolInfo = semanticModel.GetSymbolInfo(method); - if (symbolInfo.CandidateReason == CandidateReason.OverloadResolutionFailure) - { - return symbolInfo.CandidateSymbols.OfType() - .Any(s => this.FullMethodNamePattern.IsMatch(s.ToString())); - } - else if (symbolInfo.CandidateReason == CandidateReason.None) - { - // TODO: Replace regex with something more elegant - return symbolInfo.Symbol is IMethodSymbol && - this.FullMethodNamePattern.IsMatch(symbolInfo.Symbol.ToString()); - } - - return false; + // TODO: Replace regex with something more elegant + return symbolInfo.Symbol is IMethodSymbol && + this.FullMethodNamePattern.IsMatch(symbolInfo.Symbol.ToString()); } - private bool DoesShortMethodMatch(string methodName) + return false; + } + + private bool DoesShortMethodMatch(string methodName) + { + if (isGeneric) { - if (isGeneric) - { - return methodName.StartsWith($"{this.ShortMethodName}<"); - } - return methodName == this.ShortMethodName; + return methodName.StartsWith($"{this.ShortMethodName}<"); } + return methodName == this.ShortMethodName; } -} \ No newline at end of file +} diff --git a/Source/Moq.Analyzers/NoConstructorArgumentsForInterfaceMockAnalyzer.cs b/Source/Moq.Analyzers/NoConstructorArgumentsForInterfaceMockAnalyzer.cs index 107f4b3f..a03c80f7 100644 --- a/Source/Moq.Analyzers/NoConstructorArgumentsForInterfaceMockAnalyzer.cs +++ b/Source/Moq.Analyzers/NoConstructorArgumentsForInterfaceMockAnalyzer.cs @@ -1,72 +1,71 @@ -namespace Moq.Analyzers +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Moq.Analyzers; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public class NoConstructorArgumentsForInterfaceMockAnalyzer : DiagnosticAnalyzer { - using System.Collections.Immutable; - using System.Linq; - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp; - using Microsoft.CodeAnalysis.CSharp.Syntax; - using Microsoft.CodeAnalysis.Diagnostics; + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + Diagnostics.NoConstructorArgumentsForInterfaceMockId, + Diagnostics.NoConstructorArgumentsForInterfaceMockTitle, + Diagnostics.NoConstructorArgumentsForInterfaceMockMessage, + Diagnostics.Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public class NoConstructorArgumentsForInterfaceMockAnalyzer : DiagnosticAnalyzer + public override ImmutableArray SupportedDiagnostics { - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( - Diagnostics.NoConstructorArgumentsForInterfaceMockId, - Diagnostics.NoConstructorArgumentsForInterfaceMockTitle, - Diagnostics.NoConstructorArgumentsForInterfaceMockMessage, - Diagnostics.Category, - DiagnosticSeverity.Warning, - isEnabledByDefault: true); + get { return ImmutableArray.Create(Rule); } + } - public override ImmutableArray SupportedDiagnostics - { - get { return ImmutableArray.Create(Rule); } - } + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.ObjectCreationExpression); + } - public override void Initialize(AnalysisContext context) - { - context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.ObjectCreationExpression); - } + private static void Analyze(SyntaxNodeAnalysisContext context) + { + var objectCreation = (ObjectCreationExpressionSyntax)context.Node; - private static void Analyze(SyntaxNodeAnalysisContext context) + // TODO Think how to make this piece more elegant while fast + GenericNameSyntax genericName = objectCreation.Type as GenericNameSyntax; + if (objectCreation.Type is QualifiedNameSyntax) { - var objectCreation = (ObjectCreationExpressionSyntax)context.Node; - - // TODO Think how to make this piece more elegant while fast - GenericNameSyntax genericName = objectCreation.Type as GenericNameSyntax; - if (objectCreation.Type is QualifiedNameSyntax) - { - var qualifiedName = objectCreation.Type as QualifiedNameSyntax; - genericName = qualifiedName.Right as GenericNameSyntax; - } + var qualifiedName = objectCreation.Type as QualifiedNameSyntax; + genericName = qualifiedName.Right as GenericNameSyntax; + } - if (genericName?.Identifier == null || genericName.TypeArgumentList == null) return; + if (genericName?.Identifier == null || genericName.TypeArgumentList == null) return; - // Quick and dirty check - if (genericName.Identifier.ToFullString() != "Mock") return; + // Quick and dirty check + if (genericName.Identifier.ToFullString() != "Mock") return; - // Full check - var constructorSymbolInfo = context.SemanticModel.GetSymbolInfo(objectCreation); - var constructorSymbol = constructorSymbolInfo.Symbol as IMethodSymbol; - if (constructorSymbol == null || constructorSymbol.ContainingType == null || constructorSymbol.ContainingType.ConstructedFrom == null) return; - if (constructorSymbol.MethodKind != MethodKind.Constructor) return; - if (constructorSymbol.ContainingType.ConstructedFrom.ToDisplayString() != "Moq.Mock") return; - if (constructorSymbol.Parameters == null || constructorSymbol.Parameters.Length == 0) return; - if (!constructorSymbol.Parameters.Any(x => x.IsParams)) return; + // Full check + var constructorSymbolInfo = context.SemanticModel.GetSymbolInfo(objectCreation); + var constructorSymbol = constructorSymbolInfo.Symbol as IMethodSymbol; + if (constructorSymbol == null || constructorSymbol.ContainingType == null || constructorSymbol.ContainingType.ConstructedFrom == null) return; + if (constructorSymbol.MethodKind != MethodKind.Constructor) return; + if (constructorSymbol.ContainingType.ConstructedFrom.ToDisplayString() != "Moq.Mock") return; + if (constructorSymbol.Parameters == null || constructorSymbol.Parameters.Length == 0) return; + if (!constructorSymbol.Parameters.Any(x => x.IsParams)) return; - // Find mocked type - var typeArguments = genericName.TypeArgumentList.Arguments; - if (typeArguments == null || typeArguments.Count != 1) return; - var symbolInfo = context.SemanticModel.GetSymbolInfo(typeArguments[0]); - var symbol = symbolInfo.Symbol as INamedTypeSymbol; - if (symbol == null) return; + // Find mocked type + var typeArguments = genericName.TypeArgumentList.Arguments; + if (typeArguments == null || typeArguments.Count != 1) return; + var symbolInfo = context.SemanticModel.GetSymbolInfo(typeArguments[0]); + var symbol = symbolInfo.Symbol as INamedTypeSymbol; + if (symbol == null) return; - // Checked mocked type - if (symbol.TypeKind == TypeKind.Interface) - { - var diagnostic = Diagnostic.Create(Rule, objectCreation.ArgumentList.GetLocation()); - context.ReportDiagnostic(diagnostic); - } + // Checked mocked type + if (symbol.TypeKind == TypeKind.Interface) + { + var diagnostic = Diagnostic.Create(Rule, objectCreation.ArgumentList.GetLocation()); + context.ReportDiagnostic(diagnostic); } } } diff --git a/Source/Moq.Analyzers/NoMethodsInPropertySetupAnalyzer.cs b/Source/Moq.Analyzers/NoMethodsInPropertySetupAnalyzer.cs index 005754f6..4a36cb9e 100644 --- a/Source/Moq.Analyzers/NoMethodsInPropertySetupAnalyzer.cs +++ b/Source/Moq.Analyzers/NoMethodsInPropertySetupAnalyzer.cs @@ -1,48 +1,47 @@ -namespace Moq.Analyzers +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Moq.Analyzers; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public class NoMethodsInPropertySetupAnalyzer : DiagnosticAnalyzer { - using System.Collections.Immutable; - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp; - using Microsoft.CodeAnalysis.CSharp.Syntax; - using Microsoft.CodeAnalysis.Diagnostics; - - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public class NoMethodsInPropertySetupAnalyzer : DiagnosticAnalyzer + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + Diagnostics.NoMethodsInPropertySetupId, + Diagnostics.NoMethodsInPropertySetupTitle, + Diagnostics.NoMethodsInPropertySetupMessage, + Diagnostics.Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics + { + get { return ImmutableArray.Create(Rule); } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.InvocationExpression); + } + + private static void Analyze(SyntaxNodeAnalysisContext context) { - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( - Diagnostics.NoMethodsInPropertySetupId, - Diagnostics.NoMethodsInPropertySetupTitle, - Diagnostics.NoMethodsInPropertySetupMessage, - Diagnostics.Category, - DiagnosticSeverity.Warning, - isEnabledByDefault: true); - - public override ImmutableArray SupportedDiagnostics - { - get { return ImmutableArray.Create(Rule); } - } - - public override void Initialize(AnalysisContext context) - { - context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.InvocationExpression); - } - - private static void Analyze(SyntaxNodeAnalysisContext context) - { - var setupGetOrSetInvocation = (InvocationExpressionSyntax)context.Node; - - var setupGetOrSetMethod = setupGetOrSetInvocation.Expression as MemberAccessExpressionSyntax; - if (setupGetOrSetMethod == null) return; - if (setupGetOrSetMethod.Name.ToFullString() != "SetupGet" && setupGetOrSetMethod.Name.ToFullString() != "SetupSet") return; - - var mockedMethodCall = Helpers.FindMockedMethodInvocationFromSetupMethod(setupGetOrSetInvocation); - if (mockedMethodCall == null) return; - - var mockedMethodSymbol = context.SemanticModel.GetSymbolInfo(mockedMethodCall).Symbol; - if (mockedMethodSymbol == null) return; - - var diagnostic = Diagnostic.Create(Rule, mockedMethodCall.GetLocation()); - context.ReportDiagnostic(diagnostic); - } + var setupGetOrSetInvocation = (InvocationExpressionSyntax)context.Node; + + var setupGetOrSetMethod = setupGetOrSetInvocation.Expression as MemberAccessExpressionSyntax; + if (setupGetOrSetMethod == null) return; + if (setupGetOrSetMethod.Name.ToFullString() != "SetupGet" && setupGetOrSetMethod.Name.ToFullString() != "SetupSet") return; + + var mockedMethodCall = Helpers.FindMockedMethodInvocationFromSetupMethod(setupGetOrSetInvocation); + if (mockedMethodCall == null) return; + + var mockedMethodSymbol = context.SemanticModel.GetSymbolInfo(mockedMethodCall).Symbol; + if (mockedMethodSymbol == null) return; + + var diagnostic = Diagnostic.Create(Rule, mockedMethodCall.GetLocation()); + context.ReportDiagnostic(diagnostic); } } diff --git a/Source/Moq.Analyzers/NoSealedClassMocksAnalyzer.cs b/Source/Moq.Analyzers/NoSealedClassMocksAnalyzer.cs index a568c766..cea04996 100644 --- a/Source/Moq.Analyzers/NoSealedClassMocksAnalyzer.cs +++ b/Source/Moq.Analyzers/NoSealedClassMocksAnalyzer.cs @@ -1,69 +1,68 @@ -namespace Moq.Analyzers +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Moq.Analyzers; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public class NoSealedClassMocksAnalyzer : DiagnosticAnalyzer { - using System.Collections.Immutable; - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp; - using Microsoft.CodeAnalysis.CSharp.Syntax; - using Microsoft.CodeAnalysis.Diagnostics; + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + Diagnostics.NoSealedClassMocksId, + Diagnostics.NoSealedClassMocksTitle, + Diagnostics.NoSealedClassMocksMessage, + Diagnostics.Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public class NoSealedClassMocksAnalyzer : DiagnosticAnalyzer + public override ImmutableArray SupportedDiagnostics { - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( - Diagnostics.NoSealedClassMocksId, - Diagnostics.NoSealedClassMocksTitle, - Diagnostics.NoSealedClassMocksMessage, - Diagnostics.Category, - DiagnosticSeverity.Warning, - isEnabledByDefault: true); + get { return ImmutableArray.Create(Rule); } + } - public override ImmutableArray SupportedDiagnostics - { - get { return ImmutableArray.Create(Rule); } - } + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.ObjectCreationExpression); + } - public override void Initialize(AnalysisContext context) - { - context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.ObjectCreationExpression); - } + private static void Analyze(SyntaxNodeAnalysisContext context) + { + var objectCreation = (ObjectCreationExpressionSyntax)context.Node; - private static void Analyze(SyntaxNodeAnalysisContext context) + // TODO Think how to make this piece more elegant while fast + GenericNameSyntax genericName = objectCreation.Type as GenericNameSyntax; + if (objectCreation.Type is QualifiedNameSyntax) { - var objectCreation = (ObjectCreationExpressionSyntax)context.Node; - - // TODO Think how to make this piece more elegant while fast - GenericNameSyntax genericName = objectCreation.Type as GenericNameSyntax; - if (objectCreation.Type is QualifiedNameSyntax) - { - var qualifiedName = objectCreation.Type as QualifiedNameSyntax; - genericName = qualifiedName.Right as GenericNameSyntax; - } + var qualifiedName = objectCreation.Type as QualifiedNameSyntax; + genericName = qualifiedName.Right as GenericNameSyntax; + } - if (genericName?.Identifier == null || genericName.TypeArgumentList == null) return; + if (genericName?.Identifier == null || genericName.TypeArgumentList == null) return; - // Quick and dirty check - if (genericName.Identifier.ToFullString() != "Mock") return; + // Quick and dirty check + if (genericName.Identifier.ToFullString() != "Mock") return; - // Full check - var constructorSymbolInfo = context.SemanticModel.GetSymbolInfo(objectCreation); - var constructorSymbol = constructorSymbolInfo.Symbol as IMethodSymbol; - if (constructorSymbol == null || constructorSymbol.ContainingType == null || constructorSymbol.ContainingType.ConstructedFrom == null) return; - if (constructorSymbol.MethodKind != MethodKind.Constructor) return; - if (constructorSymbol.ContainingType.ConstructedFrom.ToDisplayString() != "Moq.Mock") return; + // Full check + var constructorSymbolInfo = context.SemanticModel.GetSymbolInfo(objectCreation); + var constructorSymbol = constructorSymbolInfo.Symbol as IMethodSymbol; + if (constructorSymbol == null || constructorSymbol.ContainingType == null || constructorSymbol.ContainingType.ConstructedFrom == null) return; + if (constructorSymbol.MethodKind != MethodKind.Constructor) return; + if (constructorSymbol.ContainingType.ConstructedFrom.ToDisplayString() != "Moq.Mock") return; - // Find mocked type - var typeArguments = genericName.TypeArgumentList.Arguments; - if (typeArguments == null || typeArguments.Count != 1) return; - var symbolInfo = context.SemanticModel.GetSymbolInfo(typeArguments[0]); - var symbol = symbolInfo.Symbol as INamedTypeSymbol; - if (symbol == null) return; + // Find mocked type + var typeArguments = genericName.TypeArgumentList.Arguments; + if (typeArguments == null || typeArguments.Count != 1) return; + var symbolInfo = context.SemanticModel.GetSymbolInfo(typeArguments[0]); + var symbol = symbolInfo.Symbol as INamedTypeSymbol; + if (symbol == null) return; - // Checked mocked type - if (symbol.IsSealed && symbol.TypeKind != TypeKind.Delegate) - { - var diagnostic = Diagnostic.Create(Rule, typeArguments[0].GetLocation()); - context.ReportDiagnostic(diagnostic); - } + // Checked mocked type + if (symbol.IsSealed && symbol.TypeKind != TypeKind.Delegate) + { + var diagnostic = Diagnostic.Create(Rule, typeArguments[0].GetLocation()); + context.ReportDiagnostic(diagnostic); } } } diff --git a/Source/Moq.Analyzers/SetupShouldBeUsedOnlyForOverridableMembersAnalyzer.cs b/Source/Moq.Analyzers/SetupShouldBeUsedOnlyForOverridableMembersAnalyzer.cs index 29505721..638b2a82 100644 --- a/Source/Moq.Analyzers/SetupShouldBeUsedOnlyForOverridableMembersAnalyzer.cs +++ b/Source/Moq.Analyzers/SetupShouldBeUsedOnlyForOverridableMembersAnalyzer.cs @@ -1,56 +1,55 @@ -namespace Moq.Analyzers +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Moq.Analyzers; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public class SetupShouldBeUsedOnlyForOverridableMembersAnalyzer : DiagnosticAnalyzer { - using System.Collections.Immutable; - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp; - using Microsoft.CodeAnalysis.CSharp.Syntax; - using Microsoft.CodeAnalysis.Diagnostics; - - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public class SetupShouldBeUsedOnlyForOverridableMembersAnalyzer : DiagnosticAnalyzer - { - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( - Diagnostics.SetupShouldBeUsedOnlyForOverridableMembersId, - Diagnostics.SetupShouldBeUsedOnlyForOverridableMembersTitle, - Diagnostics.SetupShouldBeUsedOnlyForOverridableMembersMessage, - Diagnostics.Category, - DiagnosticSeverity.Error, - isEnabledByDefault: true); + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + Diagnostics.SetupShouldBeUsedOnlyForOverridableMembersId, + Diagnostics.SetupShouldBeUsedOnlyForOverridableMembersTitle, + Diagnostics.SetupShouldBeUsedOnlyForOverridableMembersMessage, + Diagnostics.Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); - public override void Initialize(AnalysisContext context) - { - context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.InvocationExpression); - } + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.InvocationExpression); + } - private static void Analyze(SyntaxNodeAnalysisContext context) - { - var setupInvocation = (InvocationExpressionSyntax)context.Node; + private static void Analyze(SyntaxNodeAnalysisContext context) + { + var setupInvocation = (InvocationExpressionSyntax)context.Node; - if (setupInvocation.Expression is MemberAccessExpressionSyntax memberAccessExpression && Helpers.IsMoqSetupMethod(context.SemanticModel, memberAccessExpression)) + if (setupInvocation.Expression is MemberAccessExpressionSyntax memberAccessExpression && Helpers.IsMoqSetupMethod(context.SemanticModel, memberAccessExpression)) + { + var mockedMemberExpression = Helpers.FindMockedMemberExpressionFromSetupMethod(setupInvocation); + if (mockedMemberExpression == null) { - var mockedMemberExpression = Helpers.FindMockedMemberExpressionFromSetupMethod(setupInvocation); - if (mockedMemberExpression == null) - { - return; - } + return; + } - var symbolInfo = context.SemanticModel.GetSymbolInfo(mockedMemberExpression); - if (symbolInfo.Symbol is IPropertySymbol || symbolInfo.Symbol is IMethodSymbol) + var symbolInfo = context.SemanticModel.GetSymbolInfo(mockedMemberExpression); + if (symbolInfo.Symbol is IPropertySymbol || symbolInfo.Symbol is IMethodSymbol) + { + if (IsMethodOverridable(symbolInfo.Symbol) == false) { - if (IsMethodOverridable(symbolInfo.Symbol) == false) - { - var diagnostic = Diagnostic.Create(Rule, mockedMemberExpression.GetLocation()); - context.ReportDiagnostic(diagnostic); - } + var diagnostic = Diagnostic.Create(Rule, mockedMemberExpression.GetLocation()); + context.ReportDiagnostic(diagnostic); } } } + } - private static bool IsMethodOverridable(ISymbol methodSymbol) - { - return methodSymbol.IsSealed == false && (methodSymbol.IsVirtual || methodSymbol.IsAbstract || methodSymbol.IsOverride); - } + private static bool IsMethodOverridable(ISymbol methodSymbol) + { + return methodSymbol.IsSealed == false && (methodSymbol.IsVirtual || methodSymbol.IsAbstract || methodSymbol.IsOverride); } } diff --git a/Source/Moq.Analyzers/SetupShouldNotIncludeAsyncResultAnalyzer.cs b/Source/Moq.Analyzers/SetupShouldNotIncludeAsyncResultAnalyzer.cs index 27464ed9..08cc9779 100644 --- a/Source/Moq.Analyzers/SetupShouldNotIncludeAsyncResultAnalyzer.cs +++ b/Source/Moq.Analyzers/SetupShouldNotIncludeAsyncResultAnalyzer.cs @@ -1,69 +1,68 @@ -namespace Moq.Analyzers +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Moq.Analyzers; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public class SetupShouldNotIncludeAsyncResultAnalyzer : DiagnosticAnalyzer { - using System; - using System.Collections.Immutable; - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp; - using Microsoft.CodeAnalysis.CSharp.Syntax; - using Microsoft.CodeAnalysis.Diagnostics; + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + Diagnostics.SetupShouldNotIncludeAsyncResultId, + Diagnostics.SetupShouldNotIncludeAsyncResultTitle, + Diagnostics.SetupShouldNotIncludeAsyncResultMessage, + Diagnostics.Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true); - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public class SetupShouldNotIncludeAsyncResultAnalyzer : DiagnosticAnalyzer - { - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( - Diagnostics.SetupShouldNotIncludeAsyncResultId, - Diagnostics.SetupShouldNotIncludeAsyncResultTitle, - Diagnostics.SetupShouldNotIncludeAsyncResultMessage, - Diagnostics.Category, - DiagnosticSeverity.Error, - isEnabledByDefault: true); + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.InvocationExpression); + } - public override void Initialize(AnalysisContext context) - { - context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.InvocationExpression); - } + private static void Analyze(SyntaxNodeAnalysisContext context) + { + var setupInvocation = (InvocationExpressionSyntax)context.Node; - private static void Analyze(SyntaxNodeAnalysisContext context) + if (setupInvocation.Expression is MemberAccessExpressionSyntax memberAccessExpression && Helpers.IsMoqSetupMethod(context.SemanticModel, memberAccessExpression)) { - var setupInvocation = (InvocationExpressionSyntax)context.Node; - - if (setupInvocation.Expression is MemberAccessExpressionSyntax memberAccessExpression && Helpers.IsMoqSetupMethod(context.SemanticModel, memberAccessExpression)) + var mockedMemberExpression = Helpers.FindMockedMemberExpressionFromSetupMethod(setupInvocation); + if (mockedMemberExpression == null) { - var mockedMemberExpression = Helpers.FindMockedMemberExpressionFromSetupMethod(setupInvocation); - if (mockedMemberExpression == null) - { - return; - } + return; + } - var symbolInfo = context.SemanticModel.GetSymbolInfo(mockedMemberExpression); - if (symbolInfo.Symbol is IPropertySymbol || symbolInfo.Symbol is IMethodSymbol) + var symbolInfo = context.SemanticModel.GetSymbolInfo(mockedMemberExpression); + if (symbolInfo.Symbol is IPropertySymbol || symbolInfo.Symbol is IMethodSymbol) + { + if (IsMethodOverridable(symbolInfo.Symbol) == false && + IsMethodReturnTypeTask(symbolInfo.Symbol) == true) { - if (IsMethodOverridable(symbolInfo.Symbol) == false && - IsMethodReturnTypeTask(symbolInfo.Symbol) == true) - { - var diagnostic = Diagnostic.Create(Rule, mockedMemberExpression.GetLocation()); - context.ReportDiagnostic(diagnostic); - } + var diagnostic = Diagnostic.Create(Rule, mockedMemberExpression.GetLocation()); + context.ReportDiagnostic(diagnostic); } } } + } - private static bool IsMethodOverridable(ISymbol methodSymbol) - { - return methodSymbol.IsSealed == false && (methodSymbol.IsVirtual || methodSymbol.IsAbstract || methodSymbol.IsOverride); - } + private static bool IsMethodOverridable(ISymbol methodSymbol) + { + return methodSymbol.IsSealed == false && (methodSymbol.IsVirtual || methodSymbol.IsAbstract || methodSymbol.IsOverride); + } - private static bool IsMethodReturnTypeTask(ISymbol methodSymbol) - { - var type = methodSymbol.ToDisplayString(); - return type != null && - (type == "System.Threading.Tasks.Task" || - type == "System.Threading.ValueTask" || - type.StartsWith("System.Threading.Tasks.Task<", StringComparison.Ordinal) || - type.StartsWith("System.Threading.Tasks.ValueTask<", StringComparison.Ordinal) && - type.EndsWith(".Result", StringComparison.Ordinal)); - } + private static bool IsMethodReturnTypeTask(ISymbol methodSymbol) + { + var type = methodSymbol.ToDisplayString(); + return type != null && + (type == "System.Threading.Tasks.Task" || + type == "System.Threading.ValueTask" || + type.StartsWith("System.Threading.Tasks.Task<", StringComparison.Ordinal) || + type.StartsWith("System.Threading.Tasks.ValueTask<", StringComparison.Ordinal) && + type.EndsWith(".Result", StringComparison.Ordinal)); } }