File: Infrastructure\RouteUsageCache.cs
Web Access
Project: src\src\Framework\AspNetCoreAnalyzers\src\Analyzers\Microsoft.AspNetCore.App.Analyzers.csproj (Microsoft.AspNetCore.App.Analyzers)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Concurrent;
using System.Threading;
using Microsoft.AspNetCore.Analyzers.Infrastructure;
using Microsoft.AspNetCore.Analyzers.Infrastructure.RoutePattern;
using Microsoft.AspNetCore.Analyzers.Infrastructure.VirtualChars;
using Microsoft.AspNetCore.Analyzers.RouteEmbeddedLanguage.Infrastructure;
using Microsoft.CodeAnalysis;
 
namespace Microsoft.AspNetCore.App.Analyzers.Infrastructure;
 
internal sealed class RouteUsageCache
{
    private static readonly BoundedCacheWithFactory<Compilation, RouteUsageCache> LazyRouteUsageCache = new();
 
    public static RouteUsageCache GetOrCreate(Compilation compilation) =>
        LazyRouteUsageCache.GetOrCreateValue(compilation, static c => new RouteUsageCache(c));
 
    private readonly ConcurrentDictionary<SyntaxToken, RouteUsageModel?> _lazyRoutePatterns;
    private readonly Compilation _compilation;
 
    private RouteUsageCache(Compilation compilation)
    {
        _lazyRoutePatterns = new();
        _compilation = compilation;
    }
 
    public RouteUsageModel? Get(SyntaxToken syntaxToken, CancellationToken cancellationToken)
    {
        if (_lazyRoutePatterns.TryGetValue(syntaxToken, out var routeUsageModel))
        {
            return routeUsageModel;
        }
 
        return GetAndCache(syntaxToken, cancellationToken);
    }
 
    private RouteUsageModel? GetAndCache(SyntaxToken syntaxToken, CancellationToken cancellationToken)
    {
        return _lazyRoutePatterns.GetOrAdd(syntaxToken, token =>
        {
            if (syntaxToken.SyntaxTree == null)
            {
                return null;
            }
 
            var semanticModel = _compilation.GetSemanticModel(syntaxToken.SyntaxTree);
 
            if (!RouteStringSyntaxDetector.IsRouteStringSyntaxToken(token, semanticModel, cancellationToken, out var options))
            {
                return null;
            }
 
            var wellKnownTypes = WellKnownTypes.GetOrCreate(_compilation);
            var usageContext = RouteUsageDetector.BuildContext(
                options,
                token,
                semanticModel,
                wellKnownTypes,
                cancellationToken);
 
            var virtualChars = CSharpVirtualCharService.Instance.TryConvertToVirtualChars(token);
            var isMvc = usageContext.UsageType == RouteUsageType.MvcAction || usageContext.UsageType == RouteUsageType.MvcController;
            var tree = RoutePatternParser.TryParse(virtualChars, usageContext.RoutePatternOptions);
            if (tree == null)
            {
                return null;
            }
 
            return new RouteUsageModel
            {
                RoutePattern = tree,
                UsageContext = usageContext
            };
        });
    }
}