Skip to content

Commit c2d711e

Browse files
committed
Work around dotnet/roslyn#6596 by using file system APIs
Fixes #1736
1 parent 87c0963 commit c2d711e

1 file changed

Lines changed: 52 additions & 1 deletion

File tree

StyleCop.Analyzers/StyleCop.Analyzers/Settings/SettingsHelper.cs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33

44
namespace StyleCop.Analyzers
55
{
6+
using System;
67
using System.Collections.Immutable;
78
using System.IO;
9+
using System.Reflection;
810
using System.Threading;
911
using Microsoft.CodeAnalysis;
1012
using Microsoft.CodeAnalysis.Diagnostics;
@@ -18,6 +20,9 @@ internal static class SettingsHelper
1820
{
1921
private const string SettingsFileName = "stylecop.json";
2022

23+
private static Func<string, bool> fileExists;
24+
private static Func<string, string> fileReadAllText;
25+
2126
/// <summary>
2227
/// Gets the StyleCop settings.
2328
/// </summary>
@@ -48,7 +53,7 @@ private static StyleCopSettings GetStyleCopSettings(ImmutableArray<AdditionalTex
4853
{
4954
if (Path.GetFileName(additionalFile.Path).ToLowerInvariant() == SettingsFileName)
5055
{
51-
string additionalTextContent = additionalFile.GetText(cancellationToken).ToString();
56+
string additionalTextContent = ReadAdditionalText(additionalFile, cancellationToken);
5257
var root = JsonConvert.DeserializeObject<SettingsFile>(additionalTextContent);
5358
return root.Settings;
5459
}
@@ -61,5 +66,51 @@ private static StyleCopSettings GetStyleCopSettings(ImmutableArray<AdditionalTex
6166

6267
return new StyleCopSettings();
6368
}
69+
70+
/// <summary>
71+
/// This code works around dotnet/roslyn#6596 by using the file system APIs instead of the Roslyn APIs to read
72+
/// the additional text. If the file system APIs are not available, the code falls back to the previous
73+
/// behavior.
74+
/// </summary>
75+
/// <param name="additionalText">The additional text to read.</param>
76+
/// <param name="cancellationToken">The cancellation token that the operation will observe.</param>
77+
/// <returns>The content of the additional text as a string.</returns>
78+
private static string ReadAdditionalText(AdditionalText additionalText, CancellationToken cancellationToken)
79+
{
80+
if (fileExists == null)
81+
{
82+
Type fileClass = typeof(string).GetTypeInfo().Assembly.GetType("System.IO.File");
83+
if (fileClass != null)
84+
{
85+
MethodInfo readAllText = fileClass.GetRuntimeMethod("ReadAllText", new[] { typeof(string) });
86+
MethodInfo exists = fileClass.GetRuntimeMethod("Exists", new[] { typeof(string) });
87+
if (readAllText != null && exists != null)
88+
{
89+
Interlocked.CompareExchange(ref fileReadAllText, (Func<string, string>)readAllText.CreateDelegate(typeof(Func<string, string>)), null);
90+
Interlocked.CompareExchange(ref fileExists, (Func<string, bool>)exists.CreateDelegate(typeof(Func<string, bool>)), null);
91+
}
92+
}
93+
94+
if (fileExists == null)
95+
{
96+
// this special case allows for a clean fall back to AdditionalText.GetText()
97+
fileExists = _ => false;
98+
}
99+
}
100+
101+
try
102+
{
103+
if (fileExists(additionalText.Path))
104+
{
105+
return fileReadAllText(additionalText.Path);
106+
}
107+
}
108+
catch (IOException)
109+
{
110+
// fall back to AdditionalFile.GetText()
111+
}
112+
113+
return additionalText.GetText(cancellationToken).ToString();
114+
}
64115
}
65116
}

0 commit comments

Comments
 (0)