-
Notifications
You must be signed in to change notification settings - Fork 457
/
WellKnownTypeProvider.cs
130 lines (112 loc) · 6.02 KB
/
WellKnownTypeProvider.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Concurrent;
using System.Linq;
using Analyzer.Utilities.Extensions;
using Microsoft.CodeAnalysis;
namespace Analyzer.Utilities
{
/// <summary>
/// Provides and caches well known types in a compilation.
/// </summary>
public class WellKnownTypeProvider
{
private static readonly BoundedCacheWithFactory<Compilation, WellKnownTypeProvider> s_providerCache =
new BoundedCacheWithFactory<Compilation, WellKnownTypeProvider>();
private WellKnownTypeProvider(Compilation compilation)
{
Compilation = compilation;
_fullNameToTypeMap = new ConcurrentDictionary<string, INamedTypeSymbol>(StringComparer.Ordinal);
}
public static WellKnownTypeProvider GetOrCreate(Compilation compilation)
{
return s_providerCache.GetOrCreateValue(compilation, CreateWellKnownTypeProvider);
// Local functions
static WellKnownTypeProvider CreateWellKnownTypeProvider(Compilation compilation)
=> new WellKnownTypeProvider(compilation);
}
public Compilation Compilation { get; }
/// <summary>
/// Mapping of full name to <see cref="INamedTypeSymbol"/>.
/// </summary>
private readonly ConcurrentDictionary<string, INamedTypeSymbol> _fullNameToTypeMap;
/// <summary>
/// Attempts to get the type by the full type name.
/// </summary>
/// <param name="fullTypeName">Namespace + type name, e.g. "System.Exception".</param>
/// <param name="namedTypeSymbol">Named type symbol, if any.</param>
/// <returns>True if found in the compilation, false otherwise.</returns>
public bool TryGetOrCreateTypeByMetadataName(string fullTypeName, out INamedTypeSymbol namedTypeSymbol)
{
namedTypeSymbol = _fullNameToTypeMap.GetOrAdd(
fullTypeName,
fullyQualifiedMetadataName =>
{
// Caching null results in our cache is intended.
#pragma warning disable RS0030 // Do not used banned APIs
// Use of Compilation.GetTypeByMetadataName is allowed here (this is our wrapper for it which
// includes fallback handling for cases where GetTypeByMetadataName returns null).
var type = Compilation.GetTypeByMetadataName(fullyQualifiedMetadataName);
#pragma warning restore RS0030 // Do not used banned APIs
type ??= Compilation.Assembly.GetTypeByMetadataName(fullyQualifiedMetadataName);
if (type is null)
{
foreach (var module in Compilation.Assembly.Modules)
{
foreach (var referencedAssembly in module.ReferencedAssemblySymbols)
{
var currentType = referencedAssembly.GetTypeByMetadataName(fullyQualifiedMetadataName);
if (currentType is null)
{
continue;
}
switch (currentType.GetResultantVisibility())
{
case SymbolVisibility.Public:
case SymbolVisibility.Internal when referencedAssembly.GivesAccessTo(Compilation.Assembly):
break;
default:
continue;
}
if (type is object)
{
// Multiple visible types with the same metadata name are present.
return null;
}
type = currentType;
}
}
}
return type;
});
return namedTypeSymbol != null;
}
/// <summary>
/// Gets a type by its full type name.
/// </summary>
/// <param name="fullTypeName">Namespace + type name, e.g. "System.Exception".</param>
/// <returns>The <see cref="INamedTypeSymbol"/> if found, null otherwise.</returns>
public INamedTypeSymbol GetOrCreateTypeByMetadataName(string fullTypeName)
{
TryGetOrCreateTypeByMetadataName(fullTypeName, out INamedTypeSymbol namedTypeSymbol);
return namedTypeSymbol;
}
/// <summary>
/// Determines if <paramref name="typeSymbol"/> is a <see cref="System.Threading.Tasks.Task{TResult}"/> with its type
/// argument satisfying <paramref name="typeArgumentPredicate"/>.
/// </summary>
/// <param name="typeSymbol">Type potentially representing a <see cref="System.Threading.Tasks.Task{TResult}"/>.</param>
/// <param name="typeArgumentPredicate">Predicate to check the <paramref name="typeSymbol"/>'s type argument.</param>
/// <returns>True if <paramref name="typeSymbol"/> is a <see cref="System.Threading.Tasks.Task{TResult}"/> with its
/// type argument satisfying <paramref name="typeArgumentPredicate"/>, false otherwise.</returns>
internal bool IsTaskOfType(ITypeSymbol typeSymbol, Func<ITypeSymbol, bool> typeArgumentPredicate)
{
return typeSymbol != null
&& typeSymbol.OriginalDefinition != null
&& typeSymbol.OriginalDefinition.Equals(GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksGenericTask))
&& typeSymbol is INamedTypeSymbol namedTypeSymbol
&& namedTypeSymbol.TypeArguments.Length == 1
&& typeArgumentPredicate(namedTypeSymbol.TypeArguments[0]);
}
}
}