@@ -10,6 +10,7 @@ namespace StyleCop.Analyzers
1010 using System . Diagnostics ;
1111 using System . Diagnostics . CodeAnalysis ;
1212 using System . IO ;
13+ using System . Runtime . CompilerServices ;
1314 using System . Threading ;
1415 using LightJson ;
1516 using LightJson . Serialization ;
@@ -29,7 +30,10 @@ internal static class SettingsHelper
2930
3031 private static SourceTextValueProvider < Lazy < JsonValue > > JsonValueProvider { get ; } =
3132 new SourceTextValueProvider < Lazy < JsonValue > > (
32- text => ParseJson ( text ) ) ;
33+ static text => ParseJson ( text ) ) ;
34+
35+ private static SyntaxTreeValueProvider < StrongBox < StyleCopSettings > > SettingsStorageValueProvider { get ; } =
36+ new SyntaxTreeValueProvider < StrongBox < StyleCopSettings > > ( static _ => new StrongBox < StyleCopSettings > ( ) ) ;
3337
3438 /// <summary>
3539 /// Gets the StyleCop settings from the JSON file, i.e. without adding information frmo the .editorconfig.
@@ -58,6 +62,27 @@ Lazy<JsonValue> GetJsonValue(SourceText settingsText)
5862 }
5963 }
6064
65+ [ SuppressMessage ( "MicrosoftCodeAnalysisPerformance" , "RS1012:Start action has no registered actions" , Justification = "This is not a start action" ) ]
66+ internal static StrongBox < StyleCopSettings > GetOrCreateSettingsStorage ( this CompilationStartAnalysisContext context , SyntaxTree tree )
67+ {
68+ if ( ! context . TryGetValue ( tree , SettingsStorageValueProvider , out var storage ) )
69+ {
70+ storage = new StrongBox < StyleCopSettings > ( ) ;
71+ }
72+
73+ return storage ;
74+ }
75+
76+ internal static StrongBox < StyleCopSettings > GetOrCreateSettingsStorage ( this CompilationAnalysisContext context , SyntaxTree tree )
77+ {
78+ if ( ! context . TryGetValue ( tree , SettingsStorageValueProvider , out var storage ) )
79+ {
80+ storage = new StrongBox < StyleCopSettings > ( ) ;
81+ }
82+
83+ return storage ;
84+ }
85+
6186 /// <summary>
6287 /// Gets the StyleCop settings.
6388 /// </summary>
@@ -66,11 +91,12 @@ Lazy<JsonValue> GetJsonValue(SourceText settingsText)
6691 /// deserializing the settings file, a default settings instance is returned.</para>
6792 /// </remarks>
6893 /// <param name="context">The context that will be used to determine the StyleCop settings.</param>
94+ /// <param name="settingsStorage">The storage location for caching an evaluated <see cref="StyleCopSettings"/> object.</param>
6995 /// <param name="settingsFile">Information about the StyleCop settings file.</param>
7096 /// <returns>A <see cref="StyleCopSettings"/> instance that represents the StyleCop settings for the given context.</returns>
71- internal static StyleCopSettings GetStyleCopSettings ( this SyntaxTreeAnalysisContext context , SettingsFile settingsFile )
97+ internal static StyleCopSettings GetStyleCopSettings ( this SyntaxTreeAnalysisContext context , StrongBox < StyleCopSettings > settingsStorage , SettingsFile settingsFile )
7298 {
73- return GetSettings ( context . Options , context . Tree , settingsFile , DeserializationFailureBehavior . ReturnDefaultSettings ) ;
99+ return GetSettings ( context . Options , settingsStorage , context . Tree , settingsFile , DeserializationFailureBehavior . ReturnDefaultSettings ) ;
74100 }
75101
76102 /// <summary>
@@ -87,7 +113,7 @@ internal static StyleCopSettings GetStyleCopSettings(this SyntaxTreeAnalysisCont
87113 internal static StyleCopSettings GetStyleCopSettings ( this SyntaxTreeAnalysisContext context , CancellationToken cancellationToken )
88114 {
89115 var settingsFile = GetSettingsFile ( context . Options , ParseJson , cancellationToken ) ;
90- return GetSettings ( context . Options , context . Tree , settingsFile , DeserializationFailureBehavior . ReturnDefaultSettings ) ;
116+ return GetSettings ( context . Options , settingsStorage : new StrongBox < StyleCopSettings > ( ) , context . Tree , settingsFile , DeserializationFailureBehavior . ReturnDefaultSettings ) ;
91117 }
92118
93119 /// <summary>
@@ -98,11 +124,12 @@ internal static StyleCopSettings GetStyleCopSettings(this SyntaxTreeAnalysisCont
98124 /// deserializing the settings file, a default settings instance is returned.</para>
99125 /// </remarks>
100126 /// <param name="context">The context that will be used to determine the StyleCop settings.</param>
127+ /// <param name="settingsStorage">The storage location for caching an evaluated <see cref="StyleCopSettings"/> object.</param>
101128 /// <param name="settingsFile">The contents of the StyleCop settnigs file.</param>
102129 /// <returns>A <see cref="StyleCopSettings"/> instance that represents the StyleCop settings for the given context.</returns>
103- internal static StyleCopSettings GetStyleCopSettings ( this SyntaxNodeAnalysisContext context , SettingsFile settingsFile )
130+ internal static StyleCopSettings GetStyleCopSettings ( this SyntaxNodeAnalysisContext context , StrongBox < StyleCopSettings > settingsStorage , SettingsFile settingsFile )
104131 {
105- return GetSettings ( context . Options , context . Node . SyntaxTree , settingsFile , DeserializationFailureBehavior . ReturnDefaultSettings ) ;
132+ return GetSettings ( context . Options , settingsStorage , context . Node . SyntaxTree , settingsFile , DeserializationFailureBehavior . ReturnDefaultSettings ) ;
106133 }
107134
108135 /// <summary>
@@ -120,7 +147,7 @@ internal static StyleCopSettings GetStyleCopSettings(this SyntaxNodeAnalysisCont
120147 internal static StyleCopSettings GetStyleCopSettings ( this AnalyzerOptions options , SyntaxTree tree , CancellationToken cancellationToken )
121148 {
122149 var settingsFile = GetSettingsFile ( options , ParseJson , cancellationToken ) ;
123- return GetSettings ( options , tree , settingsFile , DeserializationFailureBehavior . ReturnDefaultSettings ) ;
150+ return GetSettings ( options , settingsStorage : new StrongBox < StyleCopSettings > ( ) , tree , settingsFile , DeserializationFailureBehavior . ReturnDefaultSettings ) ;
124151 }
125152
126153 /// <summary>
@@ -135,7 +162,7 @@ internal static StyleCopSettings GetStyleCopSettings(this AnalyzerOptions option
135162 internal static StyleCopSettings GetStyleCopSettings ( this CompilationAnalysisContext context , SyntaxTree tree , DeserializationFailureBehavior failureBehavior , CancellationToken cancellationToken )
136163 {
137164 var settingsFile = GetSettingsFile ( context . Options , GetJsonValue , cancellationToken ) ;
138- return GetSettings ( context . Options , tree , settingsFile , failureBehavior ) ;
165+ return GetSettings ( context . Options , GetOrCreateSettingsStorage ( context , tree ) , tree , settingsFile , failureBehavior ) ;
139166
140167 Lazy < JsonValue > GetJsonValue ( SourceText settingsText )
141168 {
@@ -171,21 +198,32 @@ internal static bool IsStyleCopSettingsFile(string path)
171198 || string . Equals ( fileName , AltSettingsFileName , StringComparison . OrdinalIgnoreCase ) ;
172199 }
173200
174- private static StyleCopSettings GetSettings ( AnalyzerOptions options , SyntaxTree tree , SettingsFile settingsFile , DeserializationFailureBehavior failureBehavior )
201+ private static StyleCopSettings GetSettings ( AnalyzerOptions options , StrongBox < StyleCopSettings > settingsStorage , SyntaxTree tree , SettingsFile settingsFile , DeserializationFailureBehavior failureBehavior )
175202 {
176- if ( settingsFile != null )
203+ if ( settingsStorage . Value is { } settings )
177204 {
178- return CreateSettingsObjectFromFile ( options , tree , settingsFile , failureBehavior ) ;
205+ return settings ;
179206 }
180207
181- // TODO: Can this really be null? Review when nullable references has been enabled
182- if ( tree != null )
208+ if ( settingsFile != null )
209+ {
210+ settings = CreateSettingsObjectFromFile ( options , tree , settingsFile , failureBehavior ) ;
211+ }
212+ else
183213 {
184- var analyzerConfigOptions = options . AnalyzerConfigOptionsProvider ( ) . GetOptions ( tree ) ;
185- return new StyleCopSettings ( new JsonObject ( ) , analyzerConfigOptions ) ;
214+ // TODO: Can this really be null? Review when nullable references has been enabled
215+ if ( tree != null )
216+ {
217+ var analyzerConfigOptions = options . AnalyzerConfigOptionsProvider ( ) . GetOptions ( tree ) ;
218+ settings = new StyleCopSettings ( new JsonObject ( ) , analyzerConfigOptions ) ;
219+ }
220+ else
221+ {
222+ settings = new StyleCopSettings ( ) ;
223+ }
186224 }
187225
188- return new StyleCopSettings ( ) ;
226+ return Interlocked . CompareExchange ( ref settingsStorage . Value , settings , null ) ?? settings ;
189227 }
190228
191229 private static StyleCopSettings CreateSettingsObjectFromFile ( AnalyzerOptions options , SyntaxTree tree , SettingsFile settingsFile , DeserializationFailureBehavior failureBehavior )
0 commit comments