diff --git a/src/Dhgms.GripeWithRoslyn.Analyzer/Analyzers/Logging/ConstructorShouldAcceptLoggingFrameworkArgumentAnalyzer.cs b/src/Dhgms.GripeWithRoslyn.Analyzer/Analyzers/Logging/ConstructorShouldAcceptLoggingFrameworkArgumentAnalyzer.cs
new file mode 100644
index 0000000..f5b565f
--- /dev/null
+++ b/src/Dhgms.GripeWithRoslyn.Analyzer/Analyzers/Logging/ConstructorShouldAcceptLoggingFrameworkArgumentAnalyzer.cs
@@ -0,0 +1,215 @@
+// Copyright (c) 2019 DHGMS Solutions and Contributors. All rights reserved.
+// This file is licensed to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Immutable;
+using System.Linq;
+using Dhgms.GripeWithRoslyn.Analyzer.Analyzers.Language;
+using Dhgms.GripeWithRoslyn.Analyzer.CodeCracker.Extensions;
+using Dhgms.GripeWithRoslyn.Analyzer.Extensions;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Dhgms.GripeWithRoslyn.Analyzer.Analyzers.Logging
+{
+ ///
+ /// Analyzer for checking a constructor has a logging framework instance passed into it.
+ ///
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ public sealed class ConstructorShouldAcceptLoggingFrameworkArgumentAnalyzer : DiagnosticAnalyzer
+ {
+ internal const string Title = "Constructor should have a logging framework instance as the final parameter.";
+
+ private const string MessageFormat = Title;
+
+ private const string Category = SupportedCategories.Design;
+
+ private readonly DiagnosticDescriptor _rule;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ConstructorShouldAcceptLoggingFrameworkArgumentAnalyzer()
+ {
+ _rule = new DiagnosticDescriptor(
+ DiagnosticIdsHelper.ConstructorShouldAcceptLoggingFrameworkArgument,
+ Title,
+ MessageFormat,
+ Category,
+ DiagnosticSeverity.Warning,
+ isEnabledByDefault: true,
+ description: DiagnosticResultDescriptionFactory.ConstructorShouldAcceptLoggingFrameworkArgument());
+ }
+
+ ///
+ public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(_rule);
+
+ ///
+ public override void Initialize(AnalysisContext context)
+ {
+ context.EnableConcurrentExecution();
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
+ context.RegisterSyntaxNodeAction(AnalyzeInvocationExpression, SyntaxKind.ConstructorDeclaration);
+ }
+
+ private static string GetFullName(
+ ConstructorDeclarationSyntax constructorDeclarationSyntax,
+ ClassDeclarationSyntax classDeclarationSyntax)
+ {
+ var namespaceDeclarationSyntax = constructorDeclarationSyntax.GetAncestor();
+
+ var namespaceName = namespaceDeclarationSyntax.Name.ToString();
+ return $"global::{namespaceName}.{classDeclarationSyntax.Identifier}";
+ }
+
+ private void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext context)
+ {
+ var node = context.Node;
+
+ var constructorDeclarationSyntax = (ConstructorDeclarationSyntax)context.Node;
+ var classDeclarationSyntax = constructorDeclarationSyntax.GetAncestor();
+
+ // skip if the class implements whipstaff ILogMessageActions or ILogMessageActionsWrapper
+ // or Splat IEnableLogger
+ var baseClasses = Array.Empty();
+
+ var interfaces = new[]
+ {
+ "global::Whipstaff.Core.Logging.ILogMessageActions",
+ "global::Whipstaff.Core.Logging.ILogMessageActionsWrapper",
+ "global::Splat.IEnableLogger"
+ };
+
+ if (classDeclarationSyntax.HasImplementedAnyOfType(baseClasses, interfaces, context.SemanticModel))
+ {
+ return;
+ }
+
+ // we can also skip out if the class has no methods
+ // as there won't be any logging going on.
+ if (!classDeclarationSyntax.ChildNodes().OfType().Any())
+ {
+ return;
+ }
+
+ // check the parameters
+ var parametersList = constructorDeclarationSyntax.ParameterList.Parameters;
+
+ if (parametersList.Count == 0)
+ {
+ // no parameters, so no logging framework
+ LogWarning(context, node);
+ return;
+ }
+
+ var lastParameter = parametersList.Last();
+ var lastParameterType = lastParameter.Type;
+ if (lastParameterType == null)
+ {
+ // this is a problem, as we can't determine the type
+ LogWarning(context, node);
+ return;
+ }
+
+ var typeInfo = ModelExtensions.GetTypeInfo(context.SemanticModel, lastParameterType);
+ var argType = typeInfo.Type;
+
+ if (argType == null)
+ {
+ // this is a problem, as we can't determine the type
+ LogWarning(context, node);
+ return;
+ }
+
+ // TODO: refactor this to take an array of types to check for, with an optional generic types array
+ var myType = GetFullName(constructorDeclarationSyntax, classDeclarationSyntax);
+ var typeFullName = argType.GetFullName();
+ if (typeFullName.Equals(
+ $"global::XUnit.Abstractions.ITestOutputHelper",
+ StringComparison.Ordinal))
+ {
+ return;
+ }
+
+ if (typeFullName.Equals(
+ $"global::Microsoft.Extensions.Logging.ILogger",
+ StringComparison.Ordinal))
+ {
+ CheckGenericArgument(context, lastParameter, node, myType);
+
+ return;
+ }
+
+ // check if implementing Whipstaff.Core.Logging.ILogMessageActionsWrapper
+ var lastParameterAllInterfaces = argType.AllInterfaces;
+ foreach (var namedTypeSymbol in lastParameterAllInterfaces)
+ {
+ var interfaceName = namedTypeSymbol.GetFullName();
+ if (interfaceName.Equals(
+ $"global::Whipstaff.Core.Logging.ILogMessageActionsWrapper",
+ StringComparison.Ordinal)
+ && namedTypeSymbol.TypeArguments.Any(x => x.GetFullName().Equals(myType, StringComparison.Ordinal)))
+ {
+ return;
+ }
+ }
+
+ LogWarning(context, node);
+ }
+
+ private void CheckGenericArgument(
+ SyntaxNodeAnalysisContext context,
+ ParameterSyntax lastParameter,
+ SyntaxNode node,
+ string myType)
+ {
+ // var genericArgs = argType.GetGenericArguments();
+ var childNodes = lastParameter.ChildNodes();
+
+ // QualifiedNameSyntax
+ var qualifiedNameSyntax = childNodes.OfType().FirstOrDefault();
+
+ // GenericNameSyntax
+ var genericNameSyntax = qualifiedNameSyntax.ChildNodes().OfType().ToArray();
+
+ // GenericTokenSyntax
+ var genericTokenSyntax = genericNameSyntax[0];
+
+ // type arg list
+ var typeArgumentList = genericTokenSyntax.TypeArgumentList;
+ var typeArgumentListArgs = typeArgumentList.Arguments;
+
+ // we should only have 1 arg for ILogger.
+ if (typeArgumentListArgs.Count != 1)
+ {
+ LogWarning(context, node);
+ return;
+ }
+
+ var genericArgType = ModelExtensions.GetTypeInfo(context.SemanticModel, typeArgumentListArgs[0]);
+ var genericArgTypeType = genericArgType.Type;
+ if (genericArgTypeType == null)
+ {
+ // this is a problem, as we can't determine the type
+ LogWarning(context, node);
+ return;
+ }
+
+ var genericArgTypeTypeFullName = genericArgTypeType.GetFullName();
+ if (!genericArgTypeTypeFullName.Equals(
+ myType,
+ StringComparison.Ordinal))
+ {
+ LogWarning(context, node);
+ }
+ }
+
+ private void LogWarning(SyntaxNodeAnalysisContext context, SyntaxNode node)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(_rule, node.GetLocation()));
+ }
+ }
+}
diff --git a/src/Dhgms.GripeWithRoslyn.Analyzer/Analyzers/Logging/MethodShouldInvokeLoggingActionAnalyzer.cs b/src/Dhgms.GripeWithRoslyn.Analyzer/Analyzers/Logging/MethodShouldInvokeLoggingActionAnalyzer.cs
new file mode 100644
index 0000000..cf71c69
--- /dev/null
+++ b/src/Dhgms.GripeWithRoslyn.Analyzer/Analyzers/Logging/MethodShouldInvokeLoggingActionAnalyzer.cs
@@ -0,0 +1,14 @@
+// Copyright (c) 2019 DHGMS Solutions and Contributors. All rights reserved.
+// This file is licensed to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Dhgms.GripeWithRoslyn.Analyzer.Analyzers.Logging
+{
+ internal class MethodShouldInvokeLoggingActionAnalyzer
+ {
+ }
+}
diff --git a/src/Dhgms.GripeWithRoslyn.Analyzer/Dhgms.GripeWithRoslyn.Analyzer.csproj b/src/Dhgms.GripeWithRoslyn.Analyzer/Dhgms.GripeWithRoslyn.Analyzer.csproj
index 8f01b05..aaa7338 100644
--- a/src/Dhgms.GripeWithRoslyn.Analyzer/Dhgms.GripeWithRoslyn.Analyzer.csproj
+++ b/src/Dhgms.GripeWithRoslyn.Analyzer/Dhgms.GripeWithRoslyn.Analyzer.csproj
@@ -8,6 +8,7 @@
true
$(TargetsForTfmSpecificContentInPackage);_AddAnalyzersToOutput
false
+ latest
diff --git a/src/Dhgms.GripeWithRoslyn.Analyzer/DiagnosticIdsHelper.cs b/src/Dhgms.GripeWithRoslyn.Analyzer/DiagnosticIdsHelper.cs
index c46f8e5..1823404 100644
--- a/src/Dhgms.GripeWithRoslyn.Analyzer/DiagnosticIdsHelper.cs
+++ b/src/Dhgms.GripeWithRoslyn.Analyzer/DiagnosticIdsHelper.cs
@@ -57,5 +57,7 @@ internal static class DiagnosticIdsHelper
internal static string DoNotUseEnumToString => "GR0025";
internal static string DoNotUseXUnitInlineDataAttribute => "GR0026";
+
+ internal static string ConstructorShouldAcceptLoggingFrameworkArgument => "GR0027";
}
}
diff --git a/src/Dhgms.GripeWithRoslyn.Analyzer/DiagnosticResultDescriptionFactory.cs b/src/Dhgms.GripeWithRoslyn.Analyzer/DiagnosticResultDescriptionFactory.cs
new file mode 100644
index 0000000..3930343
--- /dev/null
+++ b/src/Dhgms.GripeWithRoslyn.Analyzer/DiagnosticResultDescriptionFactory.cs
@@ -0,0 +1,13 @@
+// Copyright (c) 2019 DHGMS Solutions and Contributors. All rights reserved.
+// This file is licensed to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+using System;
+
+namespace Dhgms.GripeWithRoslyn.Analyzer
+{
+ internal static class DiagnosticResultDescriptionFactory
+ {
+ internal static string ConstructorShouldAcceptLoggingFrameworkArgument() => $"Constructors should have a final parameter of \nMicrosoft.Extensions.Logging.ILogging or, \na sublass of Whipstaff.Core.ILogMessageActionsWrapper or,\nXUnit.Abstractions.ITestOutputHelper.\n\nThis is to encourage a design that contains sufficient logging.";
+ }
+}
diff --git a/src/Dhgms.GripeWithRoslyn.Analyzer/DiagnosticResultTitleFactory.cs b/src/Dhgms.GripeWithRoslyn.Analyzer/DiagnosticResultTitleFactory.cs
new file mode 100644
index 0000000..f0779f7
--- /dev/null
+++ b/src/Dhgms.GripeWithRoslyn.Analyzer/DiagnosticResultTitleFactory.cs
@@ -0,0 +1,11 @@
+// Copyright (c) 2019 DHGMS Solutions and Contributors. All rights reserved.
+// This file is licensed to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+namespace Dhgms.GripeWithRoslyn.Analyzer
+{
+ internal static class DiagnosticResultTitleFactory
+ {
+ internal static string ConstructorShouldAcceptLoggingFrameworkArgument() => "Constructor should have a logging framework instance as the final parameter.";
+ }
+}
diff --git a/src/Dhgms.GripeWithRoslyn.Analyzer/Extensions/BaseTypeSyntaxExtensions.cs b/src/Dhgms.GripeWithRoslyn.Analyzer/Extensions/BaseTypeSyntaxExtensions.cs
new file mode 100644
index 0000000..9648f66
--- /dev/null
+++ b/src/Dhgms.GripeWithRoslyn.Analyzer/Extensions/BaseTypeSyntaxExtensions.cs
@@ -0,0 +1,64 @@
+// Copyright (c) 2019 DHGMS Solutions and Contributors. All rights reserved.
+// This file is licensed to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+using System;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Dhgms.GripeWithRoslyn.Analyzer.Extensions
+{
+ ///
+ /// Extensions for .
+ ///
+ public static class BaseTypeSyntaxExtensions
+ {
+ ///
+ /// Checks if the has implemented any of the specified types.
+ ///
+ /// The base type syntax node to check.
+ /// The base classes to check for.
+ /// The interfaces to check for.
+ /// The semantic model for the code being checked.
+ /// Whether the has implemented any of the specified types.
+ public static bool HasImplementedAnyOfType(
+ this BaseTypeSyntax baseTypeSyntax,
+ string[] baseClasses,
+ string[] interfaces,
+ SemanticModel semanticModel)
+ {
+ var typeSyntax = baseTypeSyntax.Type;
+ var baseTypeInfo = semanticModel.GetTypeInfo(typeSyntax);
+ var baseTypeSymbol = baseTypeInfo.Type;
+
+ if (baseTypeSymbol == null)
+ {
+ return false;
+ }
+
+ var baseTypeFullName = baseTypeSymbol.GetFullName();
+
+ if (baseClasses.Any(bc => bc.Equals(baseTypeFullName, StringComparison.Ordinal)))
+ {
+ return true;
+ }
+
+ if (interfaces.Any(i => i.Equals(baseTypeFullName, StringComparison.Ordinal)))
+ {
+ return true;
+ }
+
+ if (baseTypeSymbol.AllInterfaces.Any(symbol =>
+ {
+ var fn = symbol.GetFullName();
+ return interfaces.Any(i => i.Equals(fn, StringComparison.Ordinal));
+ }))
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/src/Dhgms.GripeWithRoslyn.Analyzer/Extensions/ClassDeclarationSyntaxExtensions.cs b/src/Dhgms.GripeWithRoslyn.Analyzer/Extensions/ClassDeclarationSyntaxExtensions.cs
new file mode 100644
index 0000000..6fc9bb3
--- /dev/null
+++ b/src/Dhgms.GripeWithRoslyn.Analyzer/Extensions/ClassDeclarationSyntaxExtensions.cs
@@ -0,0 +1,55 @@
+// Copyright (c) 2019 DHGMS Solutions and Contributors. All rights reserved.
+// This file is licensed to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Dhgms.GripeWithRoslyn.Analyzer.Extensions
+{
+ ///
+ /// Extensions for .
+ ///
+ public static class ClassDeclarationSyntaxExtensions
+ {
+ ///
+ /// Checks if the has implemented any of the specified types.
+ ///
+ /// The class declaration node to check.
+ /// The base classes to check for.
+ /// The interfaces to check for.
+ /// The semantic model for the code being checked.
+ /// Whether the has implemented any of the specified types.
+ public static bool HasImplementedAnyOfType(
+ this ClassDeclarationSyntax classDeclarationSyntax,
+ string[] baseClasses,
+ string[] interfaces,
+ SemanticModel semanticModel)
+ {
+ var baseList = classDeclarationSyntax.BaseList;
+ if (baseList == null)
+ {
+ return false;
+ }
+
+ var baseTypes = baseList.Types;
+ if (baseTypes.Count < 1)
+ {
+ return false;
+ }
+
+ foreach (var baseTypeSyntax in baseTypes)
+ {
+ if (baseTypeSyntax.HasImplementedAnyOfType(
+ baseClasses,
+ interfaces,
+ semanticModel))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/src/Dhgms.GripeWithRoslyn.UnitTests/Analyzers/Logging/ConstructorShouldAcceptLoggingFrameworkArgumentAnalyzerTest.cs b/src/Dhgms.GripeWithRoslyn.UnitTests/Analyzers/Logging/ConstructorShouldAcceptLoggingFrameworkArgumentAnalyzerTest.cs
new file mode 100644
index 0000000..5826012
--- /dev/null
+++ b/src/Dhgms.GripeWithRoslyn.UnitTests/Analyzers/Logging/ConstructorShouldAcceptLoggingFrameworkArgumentAnalyzerTest.cs
@@ -0,0 +1,327 @@
+// Copyright (c) 2019 DHGMS Solutions and Contributors. All rights reserved.
+// This file is licensed to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+using Dhgms.GripeWithRoslyn.Analyzer;
+using Dhgms.GripeWithRoslyn.Analyzer.Analyzers.Logging;
+using Dhgms.GripeWithRoslyn.UnitTests.Helpers;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Xunit;
+
+namespace Dhgms.GripeWithRoslyn.UnitTests.Analyzers.Logging
+{
+ ///
+ /// Unit tests for the class.
+ ///
+ public sealed class ConstructorShouldAcceptLoggingFrameworkArgumentAnalyzerTest : CodeFixVerifier
+ {
+ ///
+ /// Test to ensure good code doesn't return a warning.
+ ///
+ [Fact]
+ public void ReturnsNoWarnings()
+ {
+ var test = @"
+ namespace Microsoft.Extensions.Logging
+ {
+ public interface ILogger
+ {
+ }
+ }
+
+ namespace Whipstaff.Core.Logging
+ {
+ public abstract class AbstractLogMessageActionsWrapper : ILogMessageActionsWrapper
+ {
+ }
+
+ public interface ILogMessageActionsWrapper
+ {
+ }
+ }
+
+ namespace ConsoleApplication1
+ {
+ using XUnit;
+
+ public class TypeWithCorrectLoggerType
+ {
+ public TypeWithWrongLoggerType(Microsoft.Extensions.Logging.ILogger logger)
+ {
+ }
+
+ public void SomeMethod()
+ {
+ }
+ }
+
+ public class TypeWithLoggerTypeInCorrectPosition
+ {
+ public TypeWithLoggerTypeInCorrectPosition(string someArg, Microsoft.Extensions.Logging.ILogger logger)
+ {
+ }
+
+ public void SomeMethod()
+ {
+ }
+ }
+
+ ///
+ /// This should not be flagged as it is a message action helper.
+ ///
+ public sealed class TypeWithCorrectLoggerTypeAndLogMessageActionsInCorrectOrderMessageActions : Whipstaff.Core.Logging.ILogMessageActions
+ {
+ public TypeWithCorrectLoggerTypeAndLogMessageActionsInCorrectOrderMessageActions()
+ {
+ }
+
+ public void SomeLoggingMethod(int someId)
+ {
+ }
+ }
+
+ public class TypeWithCorrectLoggerTypeAndLogMessageActionsInCorrectOrder
+ {
+ public TypeWithCorrectLoggerTypeAndLogMessageActionsInCorrectOrder(TypeWithWrongLoggerTypeAndLogMessageActionsInWrongOrderMessageActions someArg, Microsoft.Extensions.Logging.ILogger logger)
+ {
+ }
+
+ public void SomeMethod()
+ {
+ }
+ }
+
+ public sealed class TypeWithLogMessageActionWrapperPassedMessageActions : Whipstaff.Core.Logging.ILogMessageActions
+ {
+ public void SomeLoggingMethod(int someId)
+ {
+ }
+ }
+
+ public sealed class LogMessageActionWrapper : Whipstaff.Core.Logging.AbstractLogMessageActionsWrapper
+ {
+ public void SomeLoggingMethod(int someId)
+ {
+ }
+ }
+
+ public class TypeWithLogMessageActionWrapperPassed
+ {
+ public TypeWithLogMessageActionWrapperPassed(LogMessageActionWrapper logMessageActionsWrapper)
+ {
+ }
+
+ public void SomeMethod()
+ {
+ }
+ }
+
+ public class TypeWithNoLoggerTypeButNoMethods
+ {
+ public TypeWithNoLoggerTypeButNoMethods()
+ {
+ }
+ }
+ }";
+
+ VerifyCSharpDiagnostic(test);
+ }
+
+ ///
+ /// Test to ensure bad code returns a warning.
+ ///
+ [Fact]
+ public void ReturnsWarning()
+ {
+ var test = @"
+ namespace Microsoft.Extensions.Logging
+ {
+ public interface ILogger
+ {
+ }
+ }
+
+ namespace ConsoleApplication1
+ {
+ using XUnit;
+
+ public class TypeWithEmptyCtor
+ {
+ public TypeWithEmptyCtor()
+ {
+ }
+
+ public void SomeMethod()
+ {
+ }
+ }
+
+ public class TypeWithSingleArgument
+ {
+ public TypeWithSingleArgument(string someArg)
+ {
+ }
+
+ public void SomeMethod()
+ {
+ }
+ }
+
+ public class TypeWithWrongLoggerType
+ {
+ public TypeWithWrongLoggerType(Microsoft.Extensions.Logging.ILogger logger)
+ {
+ }
+
+ public void SomeMethod()
+ {
+ }
+ }
+
+ public class TypeWithWrongLoggerTypeInWrongPosition
+ {
+ public TypeWithWrongLoggerTypeInWrongPosition(Microsoft.Extensions.Logging.ILogger logger, string someArg)
+ {
+ }
+
+ public void SomeMethod()
+ {
+ }
+ }
+
+ ///
+ /// This should not be flagged as it is a message action helper.
+ ///
+ public sealed class TypeWithWrongLoggerTypeAndLogMessageActionsInWrongOrderMessageActions : ILogMessageActions
+ {
+ public TypeWithWrongLoggerTypeAndLogMessageActionsInWrongOrder()
+ {
+ }
+
+ public void SomeMethod()
+ {
+ }
+ }
+
+ public class TypeWithWrongLoggerTypeAndLogMessageActionsInWrongOrder
+ {
+ public TypeWithWrongLoggerTypeAndLogMessageActionsInWrongOrder(Microsoft.Extensions.Logging.ILogger logger, TypeWithWrongLoggerTypeAndLogMessageActionsInWrongOrderMessageActions someArg)
+ {
+ }
+
+ public void SomeMethod()
+ {
+ }
+ }
+
+ public sealed class LogMessageActionWrapper : Whipstaff.Core.Logging.AbstractLogMessageActionsWrapper
+ {
+ public void SomeMethod()
+ {
+ }
+ }
+
+ public class TypeWithWrongLogMessageType
+ {
+ public TypeWithWrongLoggerTypeAndLogMessageActionsInWrongOrder(Microsoft.Extensions.Logging.ILogger logger, TypeWithWrongLoggerTypeAndLogMessageActionsInWrongOrderMessageActions someArg)
+ {
+ }
+
+ public void SomeMethod()
+ {
+ }
+ }
+ }";
+
+ var expected = new[]
+ {
+ new DiagnosticResult
+ {
+ Id = DiagnosticIdsHelper.ConstructorShouldAcceptLoggingFrameworkArgument,
+ Message = DiagnosticResultTitleFactory.ConstructorShouldAcceptLoggingFrameworkArgument(),
+ Severity = DiagnosticSeverity.Warning,
+ Locations =
+ new[]
+ {
+ new DiagnosticResultLocation("Test0.cs", 15, 13),
+ }
+ },
+ new DiagnosticResult
+ {
+ Id = DiagnosticIdsHelper.ConstructorShouldAcceptLoggingFrameworkArgument,
+ Message = DiagnosticResultTitleFactory.ConstructorShouldAcceptLoggingFrameworkArgument(),
+ Severity = DiagnosticSeverity.Warning,
+ Locations =
+ new[]
+ {
+ new DiagnosticResultLocation("Test0.cs", 26, 13),
+ }
+ },
+ new DiagnosticResult
+ {
+ Id = DiagnosticIdsHelper.ConstructorShouldAcceptLoggingFrameworkArgument,
+ Message = DiagnosticResultTitleFactory.ConstructorShouldAcceptLoggingFrameworkArgument(),
+ Severity = DiagnosticSeverity.Warning,
+ Locations =
+ new[]
+ {
+ new DiagnosticResultLocation("Test0.cs", 37, 13),
+ }
+ },
+ new DiagnosticResult
+ {
+ Id = DiagnosticIdsHelper.ConstructorShouldAcceptLoggingFrameworkArgument,
+ Message = DiagnosticResultTitleFactory.ConstructorShouldAcceptLoggingFrameworkArgument(),
+ Severity = DiagnosticSeverity.Warning,
+ Locations =
+ new[]
+ {
+ new DiagnosticResultLocation("Test0.cs", 48, 13),
+ }
+ },
+ new DiagnosticResult
+ {
+ Id = DiagnosticIdsHelper.ConstructorShouldAcceptLoggingFrameworkArgument,
+ Message = DiagnosticResultTitleFactory.ConstructorShouldAcceptLoggingFrameworkArgument(),
+ Severity = DiagnosticSeverity.Warning,
+ Locations =
+ new[]
+ {
+ new DiagnosticResultLocation("Test0.cs", 62, 13),
+ }
+ },
+ new DiagnosticResult
+ {
+ Id = DiagnosticIdsHelper.ConstructorShouldAcceptLoggingFrameworkArgument,
+ Message = DiagnosticResultTitleFactory.ConstructorShouldAcceptLoggingFrameworkArgument(),
+ Severity = DiagnosticSeverity.Warning,
+ Locations =
+ new[]
+ {
+ new DiagnosticResultLocation("Test0.cs", 73, 13)
+ }
+ },
+ new DiagnosticResult
+ {
+ Id = DiagnosticIdsHelper.ConstructorShouldAcceptLoggingFrameworkArgument,
+ Message = DiagnosticResultTitleFactory.ConstructorShouldAcceptLoggingFrameworkArgument(),
+ Severity = DiagnosticSeverity.Warning,
+ Locations =
+ new[]
+ {
+ new DiagnosticResultLocation("Test0.cs", 91, 13)
+ }
+ },
+ };
+
+ VerifyCSharpDiagnostic(test, expected);
+ }
+
+ ///
+ protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer()
+ {
+ return new ConstructorShouldAcceptLoggingFrameworkArgumentAnalyzer();
+ }
+ }
+}
diff --git a/src/Dhgms.GripeWithRoslyn.UnitTests/Analyzers/Logging/MethodShouldInvokeLoggingActionAnalyzerTest.cs b/src/Dhgms.GripeWithRoslyn.UnitTests/Analyzers/Logging/MethodShouldInvokeLoggingActionAnalyzerTest.cs
new file mode 100644
index 0000000..0db0df1
--- /dev/null
+++ b/src/Dhgms.GripeWithRoslyn.UnitTests/Analyzers/Logging/MethodShouldInvokeLoggingActionAnalyzerTest.cs
@@ -0,0 +1,15 @@
+// Copyright (c) 2019 DHGMS Solutions and Contributors. All rights reserved.
+// This file is licensed to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+using Dhgms.GripeWithRoslyn.Analyzer.Analyzers.Logging;
+
+namespace Dhgms.GripeWithRoslyn.UnitTests.Analyzers.Logging
+{
+ ///
+ /// Unit tests for the class.
+ ///
+ public sealed class MethodShouldInvokeLoggingActionAnalyzerTest
+ {
+ }
+}
diff --git a/version.json b/version.json
index cdc9437..6bc3574 100644
--- a/version.json
+++ b/version.json
@@ -1,5 +1,5 @@
{
- "version": "1.12",
+ "version": "1.13",
"publicReleaseRefSpec": [
"^refs/heads/main$",
"^refs/heads/preview/.*",