Skip to content

Commit 5f4f3f5

Browse files
authored
feat: support proprties of objects (#211)
* reproduce flow with failing test * reproduce flow with failing test * reproduce flow with failing test * feat: support assertions on property of object * feat: support assertions on property of object * revert rename refactoring
1 parent 5f0abeb commit 5f4f3f5

File tree

3 files changed

+54
-19
lines changed

3 files changed

+54
-19
lines changed

src/FluentAssertions.Analyzers.Tests/Tips/SanityTests.cs

+43-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
using FluentAssertions.Analyzers.Xunit;
22
using Microsoft.CodeAnalysis;
3-
using Microsoft.CodeAnalysis.CSharp;
4-
using Microsoft.CodeAnalysis.CSharp.Syntax;
5-
using Microsoft.CodeAnalysis.Text;
63
using Microsoft.VisualStudio.TestTools.UnitTesting;
74

85
namespace FluentAssertions.Analyzers.Tests
@@ -314,5 +311,48 @@ public static void True(bool input)
314311
Locations = new[] { new DiagnosticResultLocation("Test0.cs", 7, 9) }
315312
});
316313
}
314+
315+
[TestMethod]
316+
[Implemented(Reason = "https://github.com/fluentassertions/fluentassertions.analyzers/issues/207")]
317+
public void PropertiesOfTypes()
318+
{
319+
const string source = @"
320+
using Xunit;
321+
using FluentAssertions;
322+
using FluentAssertions.Extensions;
323+
using System.Collections.Generic;
324+
using System.Linq;
325+
public class TestClass
326+
{
327+
public static void Main()
328+
{
329+
var x = new TestType1();
330+
x.Prop1.Prop2.List.Any().Should().BeTrue();
331+
x.Prop1.Prop2.Count.Should().BeGreaterThan(10);
332+
}
333+
}
334+
335+
public class TestType1
336+
{
337+
public TestType2 Prop1 { get; set; }
338+
}
339+
public class TestType2
340+
{
341+
public TestType3 Prop2 { get; set; }
342+
}
343+
public class TestType3
344+
{
345+
public List<int> List { get; set; }
346+
public int Count { get; set; }
347+
}";
348+
349+
DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(new[] { source }, new DiagnosticResult()
350+
{
351+
Id = CollectionShouldNotBeEmptyAnalyzer.DiagnosticId,
352+
Message = CollectionShouldNotBeEmptyAnalyzer.Message,
353+
Severity = DiagnosticSeverity.Info,
354+
Locations = new[] { new DiagnosticResultLocation("Test0.cs", 12, 9) }
355+
});
356+
}
317357
}
318358
}

src/FluentAssertions.Analyzers/Utilities/FluentAssertionsAnalyzer.cs

+5-3
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,11 @@ protected virtual Diagnostic AnalyzeExpression(ExpressionSyntax expression, Sema
6262
var variableNameExtractor = new VariableNameExtractor(semanticModel);
6363
expression.Accept(variableNameExtractor);
6464

65-
if (variableNameExtractor.VariableIdentifierName == null) return null;
66-
var typeInfo = semanticModel.GetTypeInfo(variableNameExtractor.VariableIdentifierName);
67-
if (!ShouldAnalyzeVariableTypeCore(typeInfo.Type, semanticModel)) return null;
65+
if (variableNameExtractor.PropertiesAccessed
66+
.ConvertAll(identifier => semanticModel.GetTypeInfo(identifier))
67+
.TrueForAll(typeInfo => !ShouldAnalyzeVariableTypeCore(typeInfo.Type, semanticModel))) {
68+
return null;
69+
}
6870

6971
foreach (var visitor in Visitors)
7072
{

src/FluentAssertions.Analyzers/Utilities/VariableNameExtractor.cs

+6-13
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
using Microsoft.CodeAnalysis.CSharp.Syntax;
22
using Microsoft.CodeAnalysis.CSharp;
33
using Microsoft.CodeAnalysis;
4+
using System.Collections.Generic;
45

56
namespace FluentAssertions.Analyzers
67
{
78
public class VariableNameExtractor : CSharpSyntaxWalker
89
{
910
private readonly SemanticModel _semanticModel;
1011

11-
public string VariableName { get; private set; }
12-
public IdentifierNameSyntax VariableIdentifierName { get; private set; }
12+
public string VariableName => VariableIdentifierName?.Identifier.Text;
13+
public IdentifierNameSyntax VariableIdentifierName => PropertiesAccessed.Count > 0 ? PropertiesAccessed[0] : null;
14+
15+
public List<IdentifierNameSyntax> PropertiesAccessed { get; } = new List<IdentifierNameSyntax>();
1316

1417
public VariableNameExtractor(SemanticModel semanticModel = null)
1518
{
@@ -20,17 +23,7 @@ public override void VisitIdentifierName(IdentifierNameSyntax node)
2023
{
2124
if (IsVariable(node))
2225
{
23-
VariableName = node.Identifier.Text;
24-
VariableIdentifierName = node;
25-
}
26-
}
27-
28-
public override void Visit(SyntaxNode node)
29-
{
30-
// the first identifier encountered will be the one at the bottom of the syntax tree
31-
if (VariableName == null)
32-
{
33-
base.Visit(node);
26+
PropertiesAccessed.Add(node);
3427
}
3528
}
3629

0 commit comments

Comments
 (0)