|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#if CODEANALYSIS_V3_OR_BETTER
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Analyzer.Utilities
{
/// <summary>
/// Analyzer configuration options for a given syntax tree from <see cref="AnalyzerConfigOptions"/>
/// </summary>
internal sealed class SyntaxTreeCategorizedAnalyzerConfigOptions : AbstractCategorizedAnalyzerConfigOptions
{
private readonly AnalyzerConfigOptions? _analyzerConfigOptions;
private static readonly ConditionalWeakTable<ImmutableDictionary<string, string>, SyntaxTreeCategorizedAnalyzerConfigOptions> s_perTreeOptionsCache = new();
public static readonly SyntaxTreeCategorizedAnalyzerConfigOptions Empty = new(analyzerConfigOptions: null);
private SyntaxTreeCategorizedAnalyzerConfigOptions(AnalyzerConfigOptions? analyzerConfigOptions)
{
_analyzerConfigOptions = analyzerConfigOptions;
}
public static SyntaxTreeCategorizedAnalyzerConfigOptions Create(AnalyzerConfigOptions? analyzerConfigOptions)
{
if (analyzerConfigOptions == null)
{
return Empty;
}
var optionsMap = TryGetBackingOptionsDictionary(analyzerConfigOptions);
if (optionsMap == null)
{
return new SyntaxTreeCategorizedAnalyzerConfigOptions(analyzerConfigOptions);
}
if (optionsMap.IsEmpty)
{
return Empty;
}
return s_perTreeOptionsCache.GetValue(optionsMap, _ => new SyntaxTreeCategorizedAnalyzerConfigOptions(analyzerConfigOptions));
// Local functions.
static ImmutableDictionary<string, string>? TryGetBackingOptionsDictionary(AnalyzerConfigOptions analyzerConfigOptions)
{
// Reflection based optimization for analyzer config options.
// Ideally 'AnalyzerConfigOptions' would expose such an the API.
var type = analyzerConfigOptions.GetType();
const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
return type.GetField("_backing", flags)?.GetValue(analyzerConfigOptions) as ImmutableDictionary<string, string> ??
type.GetField("_analyzerOptions", flags)?.GetValue(analyzerConfigOptions) as ImmutableDictionary<string, string>;
}
}
public override bool IsEmpty => ReferenceEquals(this, Empty);
protected override bool TryGetOptionValue(string optionKeyPrefix, string? optionKeySuffix, string optionName, [NotNullWhen(returnValue: true)] out string? valueString)
{
if (IsEmpty)
{
valueString = null;
return false;
}
RoslynDebug.Assert(_analyzerConfigOptions != null);
var key = optionKeyPrefix;
if (optionKeySuffix != null)
{
key += $"{optionKeySuffix}.";
}
key += optionName;
return _analyzerConfigOptions.TryGetValue(key, out valueString);
}
}
}
#endif
|