diff --git a/src/Moq.Analyzers/Analyzers/ConstructorArgumentCannotBePassedForInterfaceAnalyzer.cs b/src/Moq.Analyzers/Analyzers/ConstructorArgumentCannotBePassedForInterfaceAnalyzer.cs
deleted file mode 100644
index b983433..0000000
--- a/src/Moq.Analyzers/Analyzers/ConstructorArgumentCannotBePassedForInterfaceAnalyzer.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-//-----------------------------------------------------------------------
-//
-// Copyright (c) P.O.S Informatique. All rights reserved.
-//
-//-----------------------------------------------------------------------
-
-namespace PosInformatique.Moq.Analyzers
-{
- 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 ConstructorArgumentCannotBePassedForInterfaceAnalyzer : DiagnosticAnalyzer
- {
- internal static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
- "PosInfoMoq2004",
- "Constructor arguments cannot be passed for interface mocks",
- "Constructor arguments cannot be passed for interface mocks",
- "Compilation",
- DiagnosticSeverity.Error,
- isEnabledByDefault: true,
- description: "Constructor arguments cannot be passed for interface mocks.",
- helpLinkUri: "https://posinformatique.github.io/PosInformatique.Moq.Analyzers/docs/Compilation/PosInfoMoq2004.html");
-
- public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule);
-
- public override void Initialize(AnalysisContext context)
- {
- context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
- context.EnableConcurrentExecution();
-
- context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.ObjectCreationExpression);
- }
-
- private static void Analyze(SyntaxNodeAnalysisContext context)
- {
- var objectCreation = (ObjectCreationExpressionSyntax)context.Node;
-
- var moqSymbols = MoqSymbols.FromCompilation(context.Compilation);
-
- if (moqSymbols is null)
- {
- return;
- }
-
- // Check if the mock instantiation is with a factory.
- // In this case, we ignore the matching of the constructor arguments.
- var constructorSymbol = context.SemanticModel.GetSymbolInfo(objectCreation, context.CancellationToken);
-
- if (moqSymbols.IsMockConstructorWithFactory(constructorSymbol.Symbol))
- {
- return;
- }
-
- var moqExpressionAnalyzer = new MoqExpressionAnalyzer(moqSymbols, context.SemanticModel);
-
- // Check there is "new Mock()" statement.
- var mockedType = moqExpressionAnalyzer.GetMockedType(objectCreation, out var typeExpression, context.CancellationToken);
- if (mockedType is null)
- {
- return;
- }
-
- // Check the mocked type is an interface
- if (mockedType.TypeKind != TypeKind.Interface)
- {
- return;
- }
-
- // Check that the "new Mock()" statement have at least one argument (else skip analysis).
- if (objectCreation.ArgumentList is null)
- {
- return;
- }
-
- // Gets the first argument (skip analysis if no argument)
- var firstArgument = objectCreation.ArgumentList.Arguments.FirstOrDefault();
-
- if (firstArgument is null)
- {
- return;
- }
-
- // Check if the first argument is MockBehavior argument
- var argumentCheckStart = 0;
-
- if (moqExpressionAnalyzer.IsStrictBehaviorArgument(firstArgument, out var _, context.CancellationToken))
- {
- argumentCheckStart = 1;
- }
-
- if (objectCreation.ArgumentList.Arguments.Count > argumentCheckStart)
- {
- var locations = objectCreation.ArgumentList.Arguments
- .Skip(argumentCheckStart)
- .Select(a => a.Expression.GetLocation())
- .ToArray();
-
- context.ReportDiagnostic(Rule, locations);
-
- return;
- }
- }
- }
-}
diff --git a/src/Moq.Analyzers/Analyzers/ConstructorArgumentsMustMatchAnalyzer.cs b/src/Moq.Analyzers/Analyzers/ConstructorArgumentsMustMatchAnalyzer.cs
index 39b6685..205bbc5 100644
--- a/src/Moq.Analyzers/Analyzers/ConstructorArgumentsMustMatchAnalyzer.cs
+++ b/src/Moq.Analyzers/Analyzers/ConstructorArgumentsMustMatchAnalyzer.cs
@@ -16,6 +16,16 @@ namespace PosInformatique.Moq.Analyzers
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ConstructorArgumentsMustMatchAnalyzer : DiagnosticAnalyzer
{
+ internal static readonly DiagnosticDescriptor ConstructorArgumentsCanBePassedToInterfaceRule = new DiagnosticDescriptor(
+ "PosInfoMoq2004",
+ "Constructor arguments cannot be passed for interface mocks",
+ "Constructor arguments cannot be passed for interface mocks",
+ "Compilation",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description: "Constructor arguments cannot be passed for interface mocks.",
+ helpLinkUri: "https://posinformatique.github.io/PosInformatique.Moq.Analyzers/docs/Compilation/PosInfoMoq2004.html");
+
private static readonly DiagnosticDescriptor ConstructorArgumentsMustMatchMockedClassRule = new DiagnosticDescriptor(
"PosInfoMoq2005",
"Constructor arguments must match the constructors of the mocked class",
@@ -47,6 +57,7 @@ public class ConstructorArgumentsMustMatchAnalyzer : DiagnosticAnalyzer
helpLinkUri: "https://posinformatique.github.io/PosInformatique.Moq.Analyzers/docs/Compilation/PosInfoMoq2016.html");
public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(
+ ConstructorArgumentsCanBePassedToInterfaceRule,
ConstructorArgumentsMustMatchMockedClassRule,
ConstructorMockedClassMustBeAccessibleRule,
ConstructorWithLambdaExpressionCanBeUseWithClassesOnlyRule);
@@ -100,12 +111,6 @@ private static void Analyze(SyntaxNodeAnalysisContext context)
return;
}
- // Check the type is a class (other type are ignored)
- if (mockedType.TypeKind != TypeKind.Class)
- {
- return;
- }
-
// Check the type is a named type
if (mockedType is not INamedTypeSymbol namedTypeSymbol)
{
@@ -131,6 +136,21 @@ private static void Analyze(SyntaxNodeAnalysisContext context)
}
}
+ // If the type is an interface and contains arguments, raise an error.
+ if (mockedType.TypeKind == TypeKind.Interface)
+ {
+ if (constructorArguments.Count > 0)
+ {
+ context.ReportDiagnostic(ConstructorArgumentsCanBePassedToInterfaceRule, constructorArguments.Select(a => a.GetLocation()));
+ }
+ }
+
+ // Check the type is a class (other type are ignored)
+ if (mockedType.TypeKind != TypeKind.Class)
+ {
+ return;
+ }
+
var matchedConstructor = default(MatchedConstructor);
// Iterate on each constructor and check if the arguments match.
diff --git a/tests/Moq.Analyzers.Tests/Analyzers/ConstructorArgumentCannotBePassedForInterfaceAnalyzerTest.cs b/tests/Moq.Analyzers.Tests/Analyzers/ConstructorArgumentCannotBePassedForInterfaceAnalyzerTest.cs
deleted file mode 100644
index 6f48b13..0000000
--- a/tests/Moq.Analyzers.Tests/Analyzers/ConstructorArgumentCannotBePassedForInterfaceAnalyzerTest.cs
+++ /dev/null
@@ -1,204 +0,0 @@
-//-----------------------------------------------------------------------
-//
-// Copyright (c) P.O.S Informatique. All rights reserved.
-//
-//-----------------------------------------------------------------------
-
-namespace PosInformatique.Moq.Analyzers.Tests
-{
- using Microsoft.CodeAnalysis.Testing;
- using Verifier = MoqCSharpAnalyzerVerifier;
-
- public class ConstructorArgumentCannotBePassedForInterfaceAnalyzerTest
- {
- [Fact]
- public async Task Interface_NoMock()
- {
- var source = @"
- namespace ConsoleApplication1
- {
- public class TestClass
- {
- public void TestMethod()
- {
- var obj = new object();
- }
- }
-
- public interface I
- {
- }
- }";
-
- await Verifier.VerifyAnalyzerAsync(source);
- }
-
- [Fact]
- public async Task Interface_WithNoArgument()
- {
- var source = @"
- namespace ConsoleApplication1
- {
- using Moq;
-
- public class TestClass
- {
- public void TestMethod()
- {
- var mock1 = new Mock();
- }
- }
-
- public interface I
- {
- }
- }";
-
- await Verifier.VerifyAnalyzerAsync(source);
- }
-
- [Fact]
- public async Task Interface_WithoutBehaviorStrict()
- {
- var source = @"
- namespace ConsoleApplication1
- {
- using Moq;
-
- public class TestClass
- {
- public void TestMethod()
- {
- var mock1 = new Mock({|#0:1|}, {|#1:2|});
- }
- }
-
- public interface I
- {
- }
- }";
-
- await Verifier.VerifyAnalyzerAsync(
- source,
- new DiagnosticResult(ConstructorArgumentCannotBePassedForInterfaceAnalyzer.Rule)
- .WithLocation(0).WithArguments("1")
- .WithLocation(1).WithArguments("2"));
- }
-
- [Fact]
- public async Task Interface_WithBehaviorStrict()
- {
- var source = @"
- namespace ConsoleApplication1
- {
- using Moq;
-
- public class TestClass
- {
- public void TestMethod()
- {
- var mock1 = new Mock(MockBehavior.Strict, {|#0:1|}, {|#1:2|});
- }
- }
-
- public interface I
- {
- }
- }";
-
- await Verifier.VerifyAnalyzerAsync(
- source,
- new DiagnosticResult(ConstructorArgumentCannotBePassedForInterfaceAnalyzer.Rule)
- .WithLocation(0).WithArguments("1")
- .WithLocation(1).WithArguments("2"));
- }
-
- [Theory]
- [InlineData("")]
- [InlineData(", MockBehavior.Strict")]
- public async Task Interface_WithFactoryLambdaExpression(string behavior)
- {
- var source = @"
- namespace ConsoleApplication1
- {
- using Moq;
-
- public class TestClass
- {
- public void TestMethod()
- {
- var mock1 = new Mock(() => new C()" + behavior + @");
- }
- }
-
- public interface I
- {
- }
-
- public class C : I
- {
- }
- }";
-
- await Verifier.VerifyAnalyzerAsync(source);
- }
-
- [Fact]
- public async Task Class()
- {
- var source = @"
- namespace ConsoleApplication1
- {
- using Moq;
-
- public class TestClass
- {
- public void TestMethod()
- {
- var mock1 = new Mock(1, 2, 3);
- }
- }
-
- public abstract class C
- {
- }
- }";
-
- await Verifier.VerifyAnalyzerAsync(source);
- }
-
- [Fact]
- public async Task Interface_NoMoqLibrary()
- {
- var source = @"
- namespace ConsoleApplication1
- {
- using OtherNamespace;
-
- public class TestClass
- {
- public void TestMethod()
- {
- var mock1 = new Mock(MockBehavior.Strict, 1, 2, 3);
- }
- }
-
- public interface I
- {
- }
- }
-
- namespace OtherNamespace
- {
- public class Mock
- {
- public Mock(MockBehavior _, int a, int b, int c) { }
- }
-
- public enum MockBehavior { Strict, Loose }
- }";
-
- await Verifier.VerifyAnalyzerWithNoMoqLibraryAsync(source);
- }
- }
-}
\ No newline at end of file
diff --git a/tests/Moq.Analyzers.Tests/Analyzers/ConstructorArgumentsMustMatchAnalyzerTest.cs b/tests/Moq.Analyzers.Tests/Analyzers/ConstructorArgumentsMustMatchAnalyzerTest.cs
index 1ea68ea..6a2b0e0 100644
--- a/tests/Moq.Analyzers.Tests/Analyzers/ConstructorArgumentsMustMatchAnalyzerTest.cs
+++ b/tests/Moq.Analyzers.Tests/Analyzers/ConstructorArgumentsMustMatchAnalyzerTest.cs
@@ -6,6 +6,7 @@
namespace PosInformatique.Moq.Analyzers.Tests
{
+ using Microsoft.CodeAnalysis.Testing;
using Verifier = MoqCSharpAnalyzerVerifier;
public class ConstructorArgumentsMustMatchAnalyzerTest
@@ -54,6 +55,108 @@ public void TestMethod()
await Verifier.VerifyAnalyzerAsync(source);
}
+ [Fact]
+ public async Task Interface_NoMock()
+ {
+ var source = @"
+ namespace ConsoleApplication1
+ {
+ public class TestClass
+ {
+ public void TestMethod()
+ {
+ var obj = new object();
+ }
+ }
+
+ public interface I
+ {
+ }
+ }";
+
+ await Verifier.VerifyAnalyzerAsync(source);
+ }
+
+ [Fact]
+ public async Task Interface_WithNoArgument()
+ {
+ var source = @"
+ namespace ConsoleApplication1
+ {
+ using Moq;
+
+ public class TestClass
+ {
+ public void TestMethod()
+ {
+ var mock1 = new Mock();
+ }
+ }
+
+ public interface I
+ {
+ }
+ }";
+
+ await Verifier.VerifyAnalyzerAsync(source);
+ }
+
+ [Fact]
+ public async Task Interface_WithoutBehaviorStrict()
+ {
+ var source = @"
+ namespace ConsoleApplication1
+ {
+ using Moq;
+
+ public class TestClass
+ {
+ public void TestMethod()
+ {
+ var mock1 = new Mock({|#0:1|}, {|#1:2|});
+ }
+ }
+
+ public interface I
+ {
+ }
+ }";
+
+ await Verifier.VerifyAnalyzerAsync(
+ source,
+ new DiagnosticResult(ConstructorArgumentsMustMatchAnalyzer.ConstructorArgumentsCanBePassedToInterfaceRule)
+ .WithLocation(0).WithArguments("1")
+ .WithLocation(1).WithArguments("2"));
+ }
+
+ [Fact]
+ public async Task Interface_WithBehaviorStrict()
+ {
+ var source = @"
+ namespace ConsoleApplication1
+ {
+ using Moq;
+
+ public class TestClass
+ {
+ public void TestMethod()
+ {
+ var mock1 = new Mock(MockBehavior.Strict, {|#0:1|}, {|#1:2|});
+ }
+ }
+
+ public interface I
+ {
+ }
+ }";
+
+ await Verifier.VerifyAnalyzerAsync(
+ source,
+ new DiagnosticResult(ConstructorArgumentsMustMatchAnalyzer.ConstructorArgumentsCanBePassedToInterfaceRule)
+ .WithLocation(0).WithArguments("1")
+ .WithLocation(1).WithArguments("2"));
+ }
+
[Fact]
public async Task Arguments_Empty()
{