diff --git a/TUnit.Analyzers.Tests/GlobalTestHooksAnalyzerTests.cs b/TUnit.Analyzers.Tests/GlobalTestHooksAnalyzerTests.cs index 8b776e7479..d7bfa7bf26 100644 --- a/TUnit.Analyzers.Tests/GlobalTestHooksAnalyzerTests.cs +++ b/TUnit.Analyzers.Tests/GlobalTestHooksAnalyzerTests.cs @@ -89,11 +89,11 @@ await Verifier """ using TUnit.Core; using static TUnit.Core.HookType; - + public class Tests { [BeforeEvery(Test)] - public static void {|#0:SetUp|}(string unknown) + public static void SetUp(string {|#0:unknown|}) { } } @@ -269,11 +269,11 @@ await Verifier """ using TUnit.Core; using static TUnit.Core.HookType; - + public class Tests { [AfterEvery(Test)] - public static void {|#0:CleanUp|}(string unknown) + public static void CleanUp(string {|#0:unknown|}) { } } @@ -292,11 +292,11 @@ await Verifier """ using TUnit.Core; using static TUnit.Core.HookType; - + public class Tests { [AfterEvery(Class)] - public static void {|#0:CleanUp|}(int unknown1, string unknown2) + public static void CleanUp(int {|#0:unknown1|}, string unknown2) { } } @@ -315,11 +315,11 @@ await Verifier """ using TUnit.Core; using static TUnit.Core.HookType; - + public class Tests { [AfterEvery(Assembly)] - public static void {|#0:CleanUp|}(object unknown) + public static void CleanUp(object {|#0:unknown|}) { } } diff --git a/TUnit.Analyzers.Tests/TestMethodParametersAnalyzerTests.cs b/TUnit.Analyzers.Tests/TestMethodParametersAnalyzerTests.cs index edbe404541..ee5ddd109c 100644 --- a/TUnit.Analyzers.Tests/TestMethodParametersAnalyzerTests.cs +++ b/TUnit.Analyzers.Tests/TestMethodParametersAnalyzerTests.cs @@ -34,7 +34,7 @@ await Verifier public class MyClass { [Test] - public void {|#0:MyTest|}(int value) + public void MyTest(int {|#0:value|}) { } } diff --git a/TUnit.Analyzers/AssemblyTestHooksAnalyzer.cs b/TUnit.Analyzers/AssemblyTestHooksAnalyzer.cs index ce34e439c4..059b3fb1a8 100644 --- a/TUnit.Analyzers/AssemblyTestHooksAnalyzer.cs +++ b/TUnit.Analyzers/AssemblyTestHooksAnalyzer.cs @@ -65,8 +65,9 @@ private void AnalyzeSymbol(SymbolAnalysisContext context) if (!IsAssemblyHookContextParameter(methodSymbol)) { + var firstBadParam = FindFirstUnknownParameter(methodSymbol); context.ReportDiagnostic(Diagnostic.Create(Rules.UnknownParameters, - context.Symbol.Locations.FirstOrDefault(), + firstBadParam?.Locations.FirstOrDefault() ?? context.Symbol.Locations.FirstOrDefault(), "empty or only contain `AssemblyHookContext` and `CancellationToken`") ); } @@ -105,4 +106,20 @@ private static bool IsAssemblyHookContextParameter(IMethodSymbol methodSymbol) return true; } + + private static IParameterSymbol? FindFirstUnknownParameter(IMethodSymbol methodSymbol) + { + foreach (var parameter in methodSymbol.Parameters) + { + if (parameter.Type.GloballyQualified() != + WellKnown.AttributeFullyQualifiedClasses.AssemblyHookContext.WithGlobalPrefix && + parameter.Type.GloballyQualified() != + WellKnown.AttributeFullyQualifiedClasses.CancellationToken.WithGlobalPrefix) + { + return parameter; + } + } + + return null; + } } diff --git a/TUnit.Analyzers/ClassHooksAnalyzer.cs b/TUnit.Analyzers/ClassHooksAnalyzer.cs index 5b4f0cb831..d819592598 100644 --- a/TUnit.Analyzers/ClassHooksAnalyzer.cs +++ b/TUnit.Analyzers/ClassHooksAnalyzer.cs @@ -64,8 +64,9 @@ private void AnalyzeSymbol(SymbolAnalysisContext context) if (!IsClassHookContextParameter(methodSymbol)) { + var firstBadParam = FindFirstUnknownParameter(methodSymbol); context.ReportDiagnostic(Diagnostic.Create(Rules.UnknownParameters, - context.Symbol.Locations.FirstOrDefault(), + firstBadParam?.Locations.FirstOrDefault() ?? context.Symbol.Locations.FirstOrDefault(), "empty or only contain `ClassHookContext`") ); } @@ -97,4 +98,20 @@ private static bool IsClassHookContextParameter(IMethodSymbol methodSymbol) return true; } + + private static IParameterSymbol? FindFirstUnknownParameter(IMethodSymbol methodSymbol) + { + foreach (var parameter in methodSymbol.Parameters) + { + if (parameter.Type.GloballyQualified() != + WellKnown.AttributeFullyQualifiedClasses.ClassHookContext.WithGlobalPrefix && + parameter.Type.GloballyQualified() != + WellKnown.AttributeFullyQualifiedClasses.CancellationToken.WithGlobalPrefix) + { + return parameter; + } + } + + return null; + } } diff --git a/TUnit.Analyzers/GlobalTestHooksAnalyzer.cs b/TUnit.Analyzers/GlobalTestHooksAnalyzer.cs index 4528097787..fb53bc66ba 100644 --- a/TUnit.Analyzers/GlobalTestHooksAnalyzer.cs +++ b/TUnit.Analyzers/GlobalTestHooksAnalyzer.cs @@ -110,9 +110,10 @@ private void AnalyzeSymbol(SymbolAnalysisContext context) HookLevel.Assembly => "AssemblyHookContext", _ => "context" }; + var firstBadParam = FindFirstUnknownParameter(methodSymbol, contextType!); context.ReportDiagnostic(Diagnostic.Create( - Rules.HookUnknownParameters, - methodSymbol.Locations.FirstOrDefault(), + Rules.HookUnknownParameters, + firstBadParam?.Locations.FirstOrDefault() ?? methodSymbol.Locations.FirstOrDefault(), expectedContextTypeName)); break; } @@ -182,4 +183,19 @@ private static HookParameterStatus CheckHookParameters(IMethodSymbol methodSymbo // Anything else is unknown/invalid return HookParameterStatus.UnknownParameters; } + + private static IParameterSymbol? FindFirstUnknownParameter(IMethodSymbol methodSymbol, string contextType) + { + foreach (var parameter in methodSymbol.Parameters) + { + var paramType = parameter.Type.GloballyQualifiedNonGeneric(); + if (paramType != contextType && + paramType != "global::System.Threading.CancellationToken") + { + return parameter; + } + } + + return null; + } } diff --git a/TUnit.Analyzers/InstanceTestHooksAnalyzer.cs b/TUnit.Analyzers/InstanceTestHooksAnalyzer.cs index 40dc753a3c..542a081939 100644 --- a/TUnit.Analyzers/InstanceTestHooksAnalyzer.cs +++ b/TUnit.Analyzers/InstanceTestHooksAnalyzer.cs @@ -44,8 +44,9 @@ private void AnalyzeSymbol(SymbolAnalysisContext context) if (!IsContextParameter(methodSymbol)) { + var firstBadParam = FindFirstUnknownParameter(methodSymbol); context.ReportDiagnostic(Diagnostic.Create(Rules.MethodMustBeParameterless, - context.Symbol.Locations.FirstOrDefault()) + firstBadParam?.Locations.FirstOrDefault() ?? context.Symbol.Locations.FirstOrDefault()) ); } @@ -83,4 +84,20 @@ private static bool IsContextParameter(IMethodSymbol methodSymbol) return true; } + + private static IParameterSymbol? FindFirstUnknownParameter(IMethodSymbol methodSymbol) + { + foreach (var parameter in methodSymbol.Parameters) + { + if (parameter.Type.GloballyQualified() != + WellKnown.AttributeFullyQualifiedClasses.TestContext.WithGlobalPrefix && + parameter.Type.GloballyQualified() != + WellKnown.AttributeFullyQualifiedClasses.CancellationToken.WithGlobalPrefix) + { + return parameter; + } + } + + return null; + } } diff --git a/TUnit.Analyzers/MatrixAnalyzer.cs b/TUnit.Analyzers/MatrixAnalyzer.cs index 129d0d5331..277803a99a 100644 --- a/TUnit.Analyzers/MatrixAnalyzer.cs +++ b/TUnit.Analyzers/MatrixAnalyzer.cs @@ -95,7 +95,7 @@ private void CheckMatrixErrors(SymbolAnalysisContext context, ImmutableArray