|
// 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;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Analyzer.Utilities.PooledObjects;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Analyzer.Utilities
{
using static CategorizedAnalyzerConfigOptionsExtensions;
/// <summary>
/// Aggregate analyzer configuration options:
///
/// <list type="number">
/// <item><description>Per syntax tree options from <see cref="AnalyzerConfigOptionsProvider"/>.</description></item>
/// <item><description>Options from an <strong>.editorconfig</strong> file passed in as an additional file (back compat).</description></item>
/// </list>
///
/// <inheritdoc cref="ICategorizedAnalyzerConfigOptions"/>
/// </summary>
internal sealed class AggregateCategorizedAnalyzerConfigOptions : ICategorizedAnalyzerConfigOptions
{
public static readonly AggregateCategorizedAnalyzerConfigOptions Empty = new(
globalOptions: null,
ImmutableDictionary<SyntaxTree, Lazy<SyntaxTreeCategorizedAnalyzerConfigOptions>>.Empty);
private readonly Lazy<SyntaxTreeCategorizedAnalyzerConfigOptions>? _globalOptions;
private readonly ImmutableDictionary<SyntaxTree, Lazy<SyntaxTreeCategorizedAnalyzerConfigOptions>> _perTreeOptions;
private AggregateCategorizedAnalyzerConfigOptions(Lazy<SyntaxTreeCategorizedAnalyzerConfigOptions>? globalOptions, ImmutableDictionary<SyntaxTree, Lazy<SyntaxTreeCategorizedAnalyzerConfigOptions>> perTreeOptions)
{
_globalOptions = globalOptions;
_perTreeOptions = perTreeOptions;
}
public bool IsEmpty
{
get
{
Debug.Assert(ReferenceEquals(this, Empty) || !_perTreeOptions.IsEmpty);
return ReferenceEquals(this, Empty);
}
}
public static AggregateCategorizedAnalyzerConfigOptions Create(AnalyzerConfigOptionsProvider analyzerConfigOptionsProvider, Compilation compilation)
{
analyzerConfigOptionsProvider = analyzerConfigOptionsProvider ?? throw new ArgumentNullException(nameof(analyzerConfigOptionsProvider));
if (analyzerConfigOptionsProvider.IsEmpty())
{
return Empty;
}
Lazy<SyntaxTreeCategorizedAnalyzerConfigOptions>? globalOptions;
#if CODEANALYSIS_V3_7_OR_BETTER
globalOptions = new Lazy<SyntaxTreeCategorizedAnalyzerConfigOptions>(() => SyntaxTreeCategorizedAnalyzerConfigOptions.Create(analyzerConfigOptionsProvider.GlobalOptions));
#else
globalOptions = null;
#endif
var perTreeOptionsBuilder = PooledDictionary<SyntaxTree, Lazy<SyntaxTreeCategorizedAnalyzerConfigOptions>>.GetInstance();
foreach (var tree in compilation.SyntaxTrees)
{
perTreeOptionsBuilder.Add(tree, new Lazy<SyntaxTreeCategorizedAnalyzerConfigOptions>(() => Create(tree, analyzerConfigOptionsProvider)));
}
return new AggregateCategorizedAnalyzerConfigOptions(globalOptions, perTreeOptionsBuilder.ToImmutableDictionaryAndFree());
static SyntaxTreeCategorizedAnalyzerConfigOptions Create(SyntaxTree tree, AnalyzerConfigOptionsProvider analyzerConfigOptionsProvider)
{
var options = analyzerConfigOptionsProvider.GetOptions(tree);
return SyntaxTreeCategorizedAnalyzerConfigOptions.Create(options);
}
}
public T GetOptionValue<T>(string optionName, SyntaxTree? tree, DiagnosticDescriptor? rule, TryParseValue<T> tryParseValue, T defaultValue, OptionKind kind = OptionKind.DotnetCodeQuality)
{
if (TryGetOptionValue(
optionName,
kind,
tree,
rule,
static (string s, TryParseValue<T> tryParseValue, [MaybeNullWhen(returnValue: false)] out T parsedValue) => tryParseValue(s, out parsedValue),
tryParseValue,
defaultValue,
out var value))
{
return value;
}
return defaultValue;
}
public T GetOptionValue<T, TArg>(string optionName, SyntaxTree? tree, DiagnosticDescriptor? rule, TryParseValue<T, TArg> tryParseValue, TArg arg, T defaultValue, OptionKind kind = OptionKind.DotnetCodeQuality)
{
if (TryGetOptionValue(optionName, kind, tree, rule, tryParseValue, arg, defaultValue, out var value))
{
return value;
}
return defaultValue;
}
private bool TryGetOptionValue<T, TArg>(string optionName, OptionKind kind, SyntaxTree? tree, DiagnosticDescriptor? rule, TryParseValue<T, TArg> tryParseValue, TArg arg, T defaultValue, [MaybeNullWhen(false)] out T value)
{
value = defaultValue;
if (ReferenceEquals(this, Empty))
{
return false;
}
if (tree is null)
{
if (_globalOptions is null)
{
return false;
}
return _globalOptions.Value.TryGetOptionValue(optionName, kind, rule, tryParseValue, arg, defaultValue, out value);
}
return _perTreeOptions.TryGetValue(tree, out var lazyTreeOptions) &&
lazyTreeOptions.Value.TryGetOptionValue(optionName, kind, rule, tryParseValue, arg, defaultValue, out value);
}
}
}
#endif
|