Skip to content

Commit a593a7f

Browse files
committed
Implement FileInfoCodeFix
1 parent 440151f commit a593a7f

6 files changed

Lines changed: 162 additions & 1 deletion

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System.Collections.Generic;
2+
using System.IO.Abstractions.Analyzers.Analyzers.FileSystemTypeAnalyzers;
3+
using System.IO.Abstractions.Analyzers.CodeFixes;
4+
using Microsoft.CodeAnalysis;
5+
using Roslyn.Testing.CodeFix;
6+
using Xunit;
7+
8+
namespace System.IO.Abstractions.Analyzers.Tests.CodeFixes
9+
{
10+
public class FileInfoCodeFixTests :
11+
CSharpCodeFixProviderTest<FileInfoAnalyzer, FileInfoCodeFix>
12+
{
13+
[Theory]
14+
[InlineData("BeforeFix.txt", "AfterFix.txt")]
15+
public void CodeFix(string sourceBefore, string sourceAfter)
16+
{
17+
var sourceBeforeFix = ReadFile(sourceBefore);
18+
var sourceAfterFix = ReadFile(sourceAfter);
19+
VerifyFix(sourceBeforeFix, sourceAfterFix, 0, true);
20+
}
21+
22+
protected override IEnumerable<MetadataReference> GetAdditionalReferences() => new[]
23+
{
24+
MetadataReference.CreateFromFile(typeof(IFileSystem).Assembly.Location)
25+
};
26+
}
27+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System.IO.Abstractions;
2+
3+
namespace SomeNameSpace
4+
{
5+
public class WithOutFileSystem
6+
{
7+
private readonly IFileSystem _fileSystem;
8+
9+
public WithOutFileSystem(IFileSystem fileSystem)
10+
{
11+
_fileSystem = fileSystem;
12+
}
13+
14+
public void SomeMethod()
15+
{
16+
const string filePath = "C:\\temp.txt";
17+
18+
FileInfo fileInf = _fileSystem.FileInfo.FromFileName(filePath);
19+
}
20+
}
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System.IO.Abstractions;
2+
3+
namespace SomeNameSpace
4+
{
5+
public class WithOutFileSystem
6+
{
7+
private readonly IFileSystem _fileSystem;
8+
9+
public WithOutFileSystem(IFileSystem fileSystem)
10+
{
11+
_fileSystem = fileSystem;
12+
}
13+
14+
public void SomeMethod()
15+
{
16+
const string filePath = "C:\\temp.txt";
17+
18+
FileInfo fileInf = new FileInfo(filePath);
19+
}
20+
}
21+
}

System.IO.Abstractions.Analyzers/Analyzers/FileSystemTypeAnalyzers/FileInfoAnalyzer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class FileInfoAnalyzer : BaseFileSystemNodeAnalyzer
1313
/// Diagnostic Identifier
1414
/// </summary>
1515
[UsedImplicitly]
16-
public const string DiagnosticId = "IO0004";
16+
public const string DiagnosticId = Constants.Io0004;
1717

1818
/// <summary>
1919
/// Diagnostic Title
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System.Linq;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using Microsoft.CodeAnalysis;
5+
using Microsoft.CodeAnalysis.CodeActions;
6+
using Microsoft.CodeAnalysis.CSharp.Syntax;
7+
using Microsoft.CodeAnalysis.Editing;
8+
using SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
9+
10+
namespace System.IO.Abstractions.Analyzers.CodeActions
11+
{
12+
public class FileInfoCodeAction : CodeAction
13+
{
14+
private readonly Document _document;
15+
16+
private readonly ObjectCreationExpressionSyntax _creationExpressionSyntax;
17+
18+
private readonly FieldDeclarationSyntax _field;
19+
20+
public override string Title { get; }
21+
22+
public override string EquivalenceKey => Title;
23+
24+
public FileInfoCodeAction(string title, Document document, ObjectCreationExpressionSyntax creationExpressionSyntax,
25+
FieldDeclarationSyntax field)
26+
{
27+
Title = title;
28+
_document = document;
29+
_creationExpressionSyntax = creationExpressionSyntax;
30+
_field = field;
31+
}
32+
33+
protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
34+
{
35+
var editor = await DocumentEditor.CreateAsync(_document, cancellationToken).ConfigureAwait(false);
36+
var arguments = _creationExpressionSyntax.ArgumentList.Arguments.Select(x => x.ToFullString());
37+
38+
editor.ReplaceNode(_creationExpressionSyntax,
39+
SF.ParseExpression($"{_field.Declaration.Variables.ToFullString()}.FileInfo.FromFileName({string.Join(",", arguments)})"));
40+
41+
return editor.GetChangedDocument();
42+
}
43+
}
44+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System.Collections.Immutable;
2+
using System.Composition;
3+
using System.IO.Abstractions.Analyzers.CodeActions;
4+
using System.IO.Abstractions.Analyzers.RoslynToken;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.CodeFixes;
9+
using Microsoft.CodeAnalysis.CSharp.Syntax;
10+
11+
namespace System.IO.Abstractions.Analyzers.CodeFixes
12+
{
13+
[Shared]
14+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(FileInfoCodeFix))]
15+
public class FileInfoCodeFix : CodeFixProvider
16+
{
17+
private const string Title = "Use IFileInfoFactory instead creation FileInfo for improved testablity";
18+
19+
public override ImmutableArray<string> FixableDiagnosticIds =>
20+
ImmutableArray.Create(Constants.Io0007);
21+
22+
public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
23+
24+
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
25+
{
26+
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
27+
var classDeclarationSyntax = root.FindNode(context.Span).FirstAncestorOrSelf<ClassDeclarationSyntax>();
28+
29+
if (RoslynClassFileSystem.HasFileSystemField(classDeclarationSyntax))
30+
{
31+
var creationExpressionSyntax = root.FindNode(context.Span).FirstAncestorOrSelf<ObjectCreationExpressionSyntax>();
32+
33+
var fieldDeclarationSyntax = classDeclarationSyntax
34+
.Members
35+
.OfType<FieldDeclarationSyntax>()
36+
.FirstOrDefault(x =>
37+
x.Declaration.Type.NormalizeWhitespace().ToFullString()
38+
== RoslynClassFileSystem.GetFileSystemType().ToFullString());
39+
40+
context.RegisterCodeFix(new FileInfoCodeAction(Title,
41+
context.Document,
42+
creationExpressionSyntax,
43+
fieldDeclarationSyntax),
44+
context.Diagnostics);
45+
}
46+
}
47+
}
48+
}

0 commit comments

Comments
 (0)