-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Detect when CollectionDefinition class is not in same assembly as tes…
…t class (xunit/xunit#2311) (#169)
- Loading branch information
1 parent
45a493e
commit 81de769
Showing
9 changed files
with
200 additions
and
1 deletion.
There are no files selected for viewing
57 changes: 57 additions & 0 deletions
57
...xunit.analyzers.tests/Analyzers/X1000/CollectionDefinitionMustBeInTheSameAssemblyTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
using Microsoft.CodeAnalysis; | ||
using Xunit; | ||
using Verify = CSharpVerifier<Xunit.Analyzers.CollectionDefinitionMustBeInTheSameAssembly>; | ||
|
||
public class CollectionDefinitionMustBeInTheSameAssemblyTests | ||
{ | ||
public static TheoryData<string, string> NoDiagnosticsCases = new() | ||
{ | ||
{ | ||
"[Collection(\"Test collection definition\")]", | ||
"[CollectionDefinition(\"Test collection definition\")]" | ||
}, | ||
{ | ||
string.Empty, | ||
"[CollectionDefinition(\"Test collection definition\")]" | ||
}, | ||
{ | ||
string.Empty, | ||
string.Empty | ||
} | ||
}; | ||
|
||
static readonly string Template = @" | ||
using Xunit; | ||
{0} | ||
public class TestClass {{ }} | ||
namespace TestNamespace {{ | ||
{1} | ||
public class TestDefinition {{ }} | ||
}}"; | ||
|
||
[Theory] | ||
[MemberData(nameof(NoDiagnosticsCases))] | ||
public async void CollectionDefinitionIsPresentInTheAssembly_NoDiagnostics(string classAttribute, string definitionAttribute) | ||
{ | ||
var source = string.Format(Template, classAttribute, definitionAttribute); | ||
|
||
await Verify.VerifyAnalyzer(source); | ||
} | ||
|
||
[Fact] | ||
public async void CollectionDefinitionIsMissingInTheAssembly_ReturnsError() | ||
{ | ||
var source = string.Format(Template, "[Collection(\"Test collection definition\")]", string.Empty); | ||
|
||
var expected = | ||
Verify | ||
.Diagnostic() | ||
.WithSpan(5, 14, 5, 23) | ||
.WithSeverity(DiagnosticSeverity.Error) | ||
.WithArguments("Test collection definition", "TestProject"); | ||
|
||
await Verify.VerifyAnalyzer(source, expected); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
using System; | ||
using System.Linq; | ||
using Microsoft.CodeAnalysis; | ||
|
||
namespace Xunit.Analyzers; | ||
|
||
/// <summary> | ||
/// Visits every type in every namespace within an assembly. Expressions are provided which indicate | ||
/// whether the item being searched for has been found. Searches stop once at least one of the | ||
/// short circuit expressions returned true. | ||
/// </summary> | ||
public class SymbolAssemblyVisitor : SymbolVisitor | ||
{ | ||
readonly Func<INamedTypeSymbol, bool>[] shortCircuitExpressions; | ||
|
||
public SymbolAssemblyVisitor(params Func<INamedTypeSymbol, bool>[] shortCircuitExpressions) | ||
{ | ||
this.shortCircuitExpressions = shortCircuitExpressions; | ||
} | ||
|
||
public bool ShortCircuitTriggered { get; private set; } | ||
|
||
public override void VisitAssembly(IAssemblySymbol symbol) | ||
{ | ||
symbol.GlobalNamespace.Accept(this); | ||
} | ||
|
||
public override void VisitNamespace(INamespaceSymbol symbol) | ||
{ | ||
var namespaceOrTypes = symbol.GetMembers(); | ||
foreach (var member in namespaceOrTypes) | ||
{ | ||
if (ShortCircuitTriggered) | ||
return; | ||
|
||
member.Accept(this); | ||
} | ||
} | ||
|
||
public override void VisitNamedType(INamedTypeSymbol symbol) | ||
{ | ||
if (shortCircuitExpressions.Any(e => e(symbol))) | ||
{ | ||
ShortCircuitTriggered = true; | ||
return; | ||
} | ||
|
||
var nestedTypes = symbol.GetTypeMembers(); | ||
if (nestedTypes.IsDefaultOrEmpty) | ||
return; | ||
|
||
foreach (var nestedType in nestedTypes) | ||
{ | ||
if (ShortCircuitTriggered) | ||
return; | ||
|
||
nestedType.Accept(this); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
src/xunit.analyzers/X1000/CollectionDefinitionMustBeInTheSameAssembly.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
using System.Linq; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
|
||
namespace Xunit.Analyzers; | ||
|
||
[DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
public class CollectionDefinitionMustBeInTheSameAssembly : XunitDiagnosticAnalyzer | ||
{ | ||
public CollectionDefinitionMustBeInTheSameAssembly() : | ||
base(Descriptors.X1041_CollectionDefinitionMustBeInTheSameAssembly) | ||
{ } | ||
|
||
public override void AnalyzeCompilation( | ||
CompilationStartAnalysisContext context, | ||
XunitContext xunitContext) | ||
{ | ||
context.RegisterSymbolAction(context => | ||
{ | ||
if (context.Symbol is not INamedTypeSymbol namedType) | ||
return; | ||
|
||
var collectionAttributeType = xunitContext.Core.CollectionAttributeType; | ||
var collectionAttribute = namedType | ||
.GetAttributes() | ||
.FirstOrDefault(a => a.AttributeClass.IsAssignableFrom(collectionAttributeType)); | ||
|
||
var collectionDefinitionName = collectionAttribute?.ConstructorArguments[0].Value?.ToString(); | ||
if (collectionDefinitionName == null) | ||
return; | ||
|
||
var collectionDefinitionAttributeType = xunitContext.Core.CollectionDefinitionAttributeType; | ||
var visitor = new SymbolAssemblyVisitor(symbol => | ||
symbol | ||
.GetAttributes() | ||
.Any(a => | ||
a.AttributeClass.IsAssignableFrom(collectionDefinitionAttributeType) && | ||
!a.ConstructorArguments.IsDefaultOrEmpty && | ||
a.ConstructorArguments[0].Value?.ToString() == collectionDefinitionName | ||
) | ||
); | ||
|
||
var currentAssembly = context.Compilation.Assembly; | ||
visitor.Visit(currentAssembly); | ||
if (visitor.ShortCircuitTriggered) | ||
return; | ||
|
||
context.ReportDiagnostic( | ||
Diagnostic.Create( | ||
Descriptors.X1041_CollectionDefinitionMustBeInTheSameAssembly, | ||
namedType.Locations.First(), | ||
collectionDefinitionName, | ||
currentAssembly.Name | ||
) | ||
); | ||
}, SymbolKind.NamedType); | ||
} | ||
} |