Skip to content

Commit 54a04cc

Browse files
authored
feat: support nullable types (#217)
* reproduce #215 * Update DiagnosticVerifier.cs * use symbolInfo when typeInfo does not work * build for debug too * throw exception for DEBUG
1 parent c1a9d9b commit 54a04cc

File tree

4 files changed

+69
-7
lines changed

4 files changed

+69
-7
lines changed

.github/workflows/ci.yml

+14
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@ on:
77
branches:
88
- main
99
jobs:
10+
build-debug:
11+
strategy:
12+
matrix:
13+
os: [ubuntu-latest, windows-latest, macos-latest]
14+
runs-on: ${{ matrix.os }}
15+
steps:
16+
- uses: actions/checkout@v3
17+
with:
18+
fetch-depth: 0
19+
- name: Setup .NET 6
20+
uses: actions/setup-dotnet@v3
21+
with:
22+
dotnet-version: 6.0.x
23+
- run: dotnet test --configuration Debug --filter 'TestCategory=Completed' /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura
1024
build:
1125
strategy:
1226
matrix:

src/FluentAssertions.Analyzers.Tests/DiagnosticVerifier.cs

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ static DiagnosticVerifier()
3131
{
3232
typeof(object), // System.Private.CoreLib
3333
typeof(Console), // System
34+
typeof(Uri), // System.Private.Uri
3435
typeof(Enumerable), // System.Linq
3536
typeof(CSharpCompilation), // Microsoft.CodeAnalysis.CSharp
3637
typeof(Compilation), // Microsoft.CodeAnalysis

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

+39-1
Original file line numberDiff line numberDiff line change
@@ -354,5 +354,43 @@ public class TestType3
354354
Locations = new[] { new DiagnosticResultLocation("Test0.cs", 12, 9) }
355355
});
356356
}
357+
358+
[TestMethod]
359+
[Implemented(Reason = "https://github.com/fluentassertions/fluentassertions.analyzers/issues/215")]
360+
public void ShouldNotFailToAnalyze()
361+
{
362+
const string source = @"
363+
#nullable enable
364+
using Xunit;
365+
using FluentAssertions;
366+
using FluentAssertions.Extensions;
367+
using System.Collections.Generic;
368+
using System.Linq;
369+
using System;
370+
public class TestClass
371+
{
372+
public static void Main()
373+
{
374+
var otherComponent = new OtherComponent();
375+
otherComponent.Name.Should().Be(""SomeOtherComponent"");
376+
otherComponent.Version.Should().Be(""1.2.3"");
377+
otherComponent.DownloadUrl.Should().Be(new Uri(""https://sampleurl.com""));
378+
otherComponent.Hash.Should().Be(""SampleHash"");
357379
}
358-
}
380+
}
381+
382+
public class OtherComponent
383+
{
384+
public string? Name { get; set; }
385+
386+
public string? Version { get; set; }
387+
388+
public Uri? DownloadUrl { get; set; }
389+
390+
public string? Hash { get; set; }
391+
}";
392+
393+
DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(source);
394+
}
395+
}
396+
}

src/FluentAssertions.Analyzers/Utilities/FluentAssertionsAnalyzer.cs

+15-6
Original file line numberDiff line numberDiff line change
@@ -47,24 +47,28 @@ private void AnalyzeExpressionStatementSyntax(SyntaxNodeAnalysisContext context)
4747
protected virtual bool ShouldAnalyzeVariableNamedType(INamedTypeSymbol type, SemanticModel semanticModel) => true;
4848
protected virtual bool ShouldAnalyzeVariableType(ITypeSymbol type, SemanticModel semanticModel) => true;
4949

50-
private bool ShouldAnalyzeVariableTypeCore(ITypeSymbol type, SemanticModel semanticModel)
50+
private bool ShouldAnalyzeVariableTypeCore(IdentifierNameSyntax identifier, SemanticModel semanticModel)
5151
{
52-
if (type is INamedTypeSymbol namedType)
52+
ISymbol symbol = semanticModel.GetTypeInfo(identifier).Type ?? semanticModel.GetSymbolInfo(identifier).Symbol;
53+
if (symbol is INamedTypeSymbol namedType)
5354
{
5455
return ShouldAnalyzeVariableNamedType(namedType, semanticModel);
5556
}
5657

57-
return ShouldAnalyzeVariableType(type, semanticModel);
58+
if (symbol is ITypeSymbol typeSymbol)
59+
{
60+
return ShouldAnalyzeVariableType(typeSymbol, semanticModel);
61+
}
62+
63+
return false;
5864
}
5965

6066
protected virtual Diagnostic AnalyzeExpression(ExpressionSyntax expression, SemanticModel semanticModel)
6167
{
6268
var variableNameExtractor = new VariableNameExtractor(semanticModel);
6369
expression.Accept(variableNameExtractor);
6470

65-
if (variableNameExtractor.PropertiesAccessed
66-
.ConvertAll(identifier => semanticModel.GetTypeInfo(identifier))
67-
.TrueForAll(typeInfo => !ShouldAnalyzeVariableTypeCore(typeInfo.Type, semanticModel))) {
71+
if (variableNameExtractor.PropertiesAccessed.TrueForAll(identifier => !ShouldAnalyzeVariableTypeCore(identifier, semanticModel))) {
6872
return null;
6973
}
7074

@@ -106,7 +110,12 @@ private Diagnostic AnalyzeExpressionSafely(ExpressionSyntax expression, Semantic
106110
expressionString = expression.ToString();
107111
} catch {}
108112
Console.Error.WriteLine($"Failed to analyze expression in {GetType().FullName}. expression: {expressionString}\n{e}");
113+
114+
#if DEBUG
115+
throw e;
116+
#else
109117
return null;
118+
#endif
110119
}
111120
}
112121
}

0 commit comments

Comments
 (0)