33
44namespace 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