Skip to content

Commit

Permalink
Cache StyleCop settings instances in analyzers
Browse files Browse the repository at this point in the history
  • Loading branch information
sharwell committed Aug 22, 2024
1 parent 88caa25 commit a780b34
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 18 deletions.
11 changes: 9 additions & 2 deletions StyleCop.Analyzers/StyleCop.Analyzers/AnalyzerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace StyleCop.Analyzers
using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using StyleCop.Analyzers.Settings.ObjectModel;
Expand All @@ -32,9 +33,12 @@ public static void RegisterSyntaxTreeAction(this CompilationStartAnalysisContext
context.RegisterSyntaxTreeAction(
context =>
{
StyleCopSettings settings = context.GetStyleCopSettings(settingsFile);
StyleCopSettings settings = context.GetStyleCopSettings(GetOrCreateSettingsStorage(context.Tree), settingsFile);
action(context, settings);
});

StrongBox<StyleCopSettings> GetOrCreateSettingsStorage(SyntaxTree tree)
=> SettingsHelper.GetOrCreateSettingsStorage(context, tree);
}

/// <summary>
Expand Down Expand Up @@ -74,10 +78,13 @@ public static void RegisterSyntaxNodeAction<TLanguageKindEnum>(this CompilationS
context.RegisterSyntaxNodeAction(
context =>
{
StyleCopSettings settings = context.GetStyleCopSettings(settingsFile);
StyleCopSettings settings = context.GetStyleCopSettings(GetOrCreateSettingsStorage(context.Node.SyntaxTree), settingsFile);
action(context, settings);
},
syntaxKinds);

StrongBox<StyleCopSettings> GetOrCreateSettingsStorage(SyntaxTree tree)
=> SettingsHelper.GetOrCreateSettingsStorage(context, tree);
}

private static class LanguageKindArrays<TLanguageKindEnum>
Expand Down
70 changes: 54 additions & 16 deletions StyleCop.Analyzers/StyleCop.Analyzers/Settings/SettingsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace StyleCop.Analyzers
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
using LightJson;
using LightJson.Serialization;
Expand All @@ -29,7 +30,10 @@ internal static class SettingsHelper

private static SourceTextValueProvider<Lazy<JsonValue>> JsonValueProvider { get; } =
new SourceTextValueProvider<Lazy<JsonValue>>(
text => ParseJson(text));
static text => ParseJson(text));

private static SyntaxTreeValueProvider<StrongBox<StyleCopSettings>> SettingsStorageValueProvider { get; } =
new SyntaxTreeValueProvider<StrongBox<StyleCopSettings>>(static _ => new StrongBox<StyleCopSettings>());

/// <summary>
/// Gets the StyleCop settings from the JSON file, i.e. without adding information frmo the .editorconfig.
Expand Down Expand Up @@ -58,6 +62,27 @@ Lazy<JsonValue> GetJsonValue(SourceText settingsText)
}
}

[SuppressMessage("MicrosoftCodeAnalysisPerformance", "RS1012:Start action has no registered actions", Justification = "This is not a start action")]
internal static StrongBox<StyleCopSettings> GetOrCreateSettingsStorage(this CompilationStartAnalysisContext context, SyntaxTree tree)
{
if (!context.TryGetValue(tree, SettingsStorageValueProvider, out var storage))
{
storage = new StrongBox<StyleCopSettings>();
}

return storage;
}

internal static StrongBox<StyleCopSettings> GetOrCreateSettingsStorage(this CompilationAnalysisContext context, SyntaxTree tree)
{
if (!context.TryGetValue(tree, SettingsStorageValueProvider, out var storage))
{
storage = new StrongBox<StyleCopSettings>();
}

return storage;
}

/// <summary>
/// Gets the StyleCop settings.
/// </summary>
Expand All @@ -66,11 +91,12 @@ Lazy<JsonValue> GetJsonValue(SourceText settingsText)
/// deserializing the settings file, a default settings instance is returned.</para>
/// </remarks>
/// <param name="context">The context that will be used to determine the StyleCop settings.</param>
/// <param name="settingsStorage">The storage location for caching an evaluated <see cref="StyleCopSettings"/> object.</param>
/// <param name="settingsFile">Information about the StyleCop settings file.</param>
/// <returns>A <see cref="StyleCopSettings"/> instance that represents the StyleCop settings for the given context.</returns>
internal static StyleCopSettings GetStyleCopSettings(this SyntaxTreeAnalysisContext context, SettingsFile settingsFile)
internal static StyleCopSettings GetStyleCopSettings(this SyntaxTreeAnalysisContext context, StrongBox<StyleCopSettings> settingsStorage, SettingsFile settingsFile)
{
return GetSettings(context.Options, context.Tree, settingsFile, DeserializationFailureBehavior.ReturnDefaultSettings);
return GetSettings(context.Options, settingsStorage, context.Tree, settingsFile, DeserializationFailureBehavior.ReturnDefaultSettings);
}

/// <summary>
Expand All @@ -87,7 +113,7 @@ internal static StyleCopSettings GetStyleCopSettings(this SyntaxTreeAnalysisCont
internal static StyleCopSettings GetStyleCopSettings(this SyntaxTreeAnalysisContext context, CancellationToken cancellationToken)
{
var settingsFile = GetSettingsFile(context.Options, ParseJson, cancellationToken);
return GetSettings(context.Options, context.Tree, settingsFile, DeserializationFailureBehavior.ReturnDefaultSettings);
return GetSettings(context.Options, settingsStorage: new StrongBox<StyleCopSettings>(), context.Tree, settingsFile, DeserializationFailureBehavior.ReturnDefaultSettings);
}

/// <summary>
Expand All @@ -98,11 +124,12 @@ internal static StyleCopSettings GetStyleCopSettings(this SyntaxTreeAnalysisCont
/// deserializing the settings file, a default settings instance is returned.</para>
/// </remarks>
/// <param name="context">The context that will be used to determine the StyleCop settings.</param>
/// <param name="settingsStorage">The storage location for caching an evaluated <see cref="StyleCopSettings"/> object.</param>
/// <param name="settingsFile">The contents of the StyleCop settnigs file.</param>
/// <returns>A <see cref="StyleCopSettings"/> instance that represents the StyleCop settings for the given context.</returns>
internal static StyleCopSettings GetStyleCopSettings(this SyntaxNodeAnalysisContext context, SettingsFile settingsFile)
internal static StyleCopSettings GetStyleCopSettings(this SyntaxNodeAnalysisContext context, StrongBox<StyleCopSettings> settingsStorage, SettingsFile settingsFile)
{
return GetSettings(context.Options, context.Node.SyntaxTree, settingsFile, DeserializationFailureBehavior.ReturnDefaultSettings);
return GetSettings(context.Options, settingsStorage, context.Node.SyntaxTree, settingsFile, DeserializationFailureBehavior.ReturnDefaultSettings);
}

/// <summary>
Expand All @@ -120,7 +147,7 @@ internal static StyleCopSettings GetStyleCopSettings(this SyntaxNodeAnalysisCont
internal static StyleCopSettings GetStyleCopSettings(this AnalyzerOptions options, SyntaxTree tree, CancellationToken cancellationToken)
{
var settingsFile = GetSettingsFile(options, ParseJson, cancellationToken);
return GetSettings(options, tree, settingsFile, DeserializationFailureBehavior.ReturnDefaultSettings);
return GetSettings(options, settingsStorage: new StrongBox<StyleCopSettings>(), tree, settingsFile, DeserializationFailureBehavior.ReturnDefaultSettings);
}

/// <summary>
Expand All @@ -135,7 +162,7 @@ internal static StyleCopSettings GetStyleCopSettings(this AnalyzerOptions option
internal static StyleCopSettings GetStyleCopSettings(this CompilationAnalysisContext context, SyntaxTree tree, DeserializationFailureBehavior failureBehavior, CancellationToken cancellationToken)
{
var settingsFile = GetSettingsFile(context.Options, GetJsonValue, cancellationToken);
return GetSettings(context.Options, tree, settingsFile, failureBehavior);
return GetSettings(context.Options, GetOrCreateSettingsStorage(context, tree), tree, settingsFile, failureBehavior);

Lazy<JsonValue> GetJsonValue(SourceText settingsText)
{
Expand Down Expand Up @@ -171,21 +198,32 @@ internal static bool IsStyleCopSettingsFile(string path)
|| string.Equals(fileName, AltSettingsFileName, StringComparison.OrdinalIgnoreCase);
}

private static StyleCopSettings GetSettings(AnalyzerOptions options, SyntaxTree tree, SettingsFile settingsFile, DeserializationFailureBehavior failureBehavior)
private static StyleCopSettings GetSettings(AnalyzerOptions options, StrongBox<StyleCopSettings> settingsStorage, SyntaxTree tree, SettingsFile settingsFile, DeserializationFailureBehavior failureBehavior)
{
if (settingsFile != null)
if (settingsStorage.Value is { } settings)
{
return CreateSettingsObjectFromFile(options, tree, settingsFile, failureBehavior);
return settings;
}

// TODO: Can this really be null? Review when nullable references has been enabled
if (tree != null)
if (settingsFile != null)
{
settings = CreateSettingsObjectFromFile(options, tree, settingsFile, failureBehavior);
}
else
{
var analyzerConfigOptions = options.AnalyzerConfigOptionsProvider().GetOptions(tree);
return new StyleCopSettings(new JsonObject(), analyzerConfigOptions);
// TODO: Can this really be null? Review when nullable references has been enabled
if (tree != null)
{
var analyzerConfigOptions = options.AnalyzerConfigOptionsProvider().GetOptions(tree);
settings = new StyleCopSettings(new JsonObject(), analyzerConfigOptions);
}
else
{
settings = new StyleCopSettings();
}
}

return new StyleCopSettings();
return Interlocked.CompareExchange(ref settingsStorage.Value, settings, null) ?? settings;
}

private static StyleCopSettings CreateSettingsObjectFromFile(AnalyzerOptions options, SyntaxTree tree, SettingsFile settingsFile, DeserializationFailureBehavior failureBehavior)
Expand Down

0 comments on commit a780b34

Please # to comment.