forked from UbiquityDotNET/Ubiquity.NET.Utils
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNamespaceQualifiedTypeName.cs
More file actions
173 lines (153 loc) · 8.45 KB
/
NamespaceQualifiedTypeName.cs
File metadata and controls
173 lines (153 loc) · 8.45 KB
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// Copyright (c) Ubiquity.NET Contributors. All rights reserved.
// Licensed under the Apache-2.0 WITH LLVM-exception license. See the LICENSE.md file in the project root for full license information.
namespace Ubiquity.NET.CodeAnalysis.Utils
{
/// <summary>Captured type name, includes nullability</summary>
/// <remarks>
/// <para>Due to how nullability was introduced much later than the original language and runtime,
/// the concept of nullability is not baked into the type. Reference types are, technically,
/// always nullable, in general. However, sometimes they may be annotated to allow the compiler
/// to perform static analysis to verify the rule. Additionally, any value that might be null is
/// flagged as an error in the compiler (and IDE). Nullable value types have been around since day
/// one and are all just instances of <see cref="Nullable{T}"/>. Thus the mechanics of nullable
/// annotation are not consistent (Though a language compiler may make the syntax seem like it is).
/// So, this will capture the information for both cases.</para>
/// </remarks>
public class NamespaceQualifiedTypeName
: NamespaceQualifiedName
, IEquatable<NamespaceQualifiedTypeName>
, IFormattable
{
/// <summary>Initializes a new instance of the <see cref="NamespaceQualifiedTypeName"/> class.</summary>
public NamespaceQualifiedTypeName( )
: base()
{
}
/// <summary>Initializes a new instance of the <see cref="NamespaceQualifiedTypeName"/> class.</summary>
/// <param name="namespaceNames">Namespace names sequence for this type</param>
/// <param name="simpleName">Simple name for this type</param>
/// <param name="nullableAnnotation">Nullability annotation for this type</param>
public NamespaceQualifiedTypeName(
IEnumerable<string> namespaceNames,
string simpleName,
NullableAnnotation nullableAnnotation = NullableAnnotation.None
)
: base( namespaceNames, simpleName )
{
NullableAnnotation = nullableAnnotation;
}
/// <summary>Initializes a new instance of the <see cref="NamespaceQualifiedTypeName"/> class.</summary>
/// <param name="sym">Symbol to capture the type information for</param>
/// <remarks>
/// If <paramref name="sym"/> is a nullable value type (i.e., <see cref="Nullable{T}"/>> then this
/// will capture the name of <c>T</c> and the <see cref="ITypeSymbol.NullableAnnotation"/>. Otherwise,
/// it captures the name of <paramref name="sym"/> and the <see cref="ITypeSymbol.NullableAnnotation"/>.
/// </remarks>
public NamespaceQualifiedTypeName( ITypeSymbol sym )
: this( GetNullableNamespaceNames( sym ), GetNullableSimpleName( sym ), sym.NullableAnnotation )
{
if(sym is IArrayTypeSymbol arrayType)
{
IsArray = true;
ElementType = arrayType.ElementType.GetNamespaceQualifiedName();
}
}
/// <summary>Gets a value indicating whether this type has nullability annotation (and a generator should use a language specific nullability form)</summary>
public bool IsNullable => NullableAnnotation == NullableAnnotation.Annotated;
/// <summary>Gets the nullability annotation state for this type</summary>
public NullableAnnotation NullableAnnotation { get; }
/// <summary>Gets a value indicating whether this name is an array</summary>
[MemberNotNullWhen( true, nameof( ElementType ) )]
public bool IsArray { get; }
/// <summary>Gets the array element type (Only valid if <see cref="IsArray"/> is true)</summary>
public NamespaceQualifiedTypeName? ElementType { get; }
/// <summary>Formats this instance according to the args</summary>
/// <param name="format">Format string for this instance (see remarks)</param>
/// <param name="formatProvider">[ignored]</param>
/// <returns>Formatted string representation of this instance</returns>
/// <remarks>
/// The supported values for <paramref name="format"/> are:
/// <list type="table">
/// <listheader><term>Value</term><description>Description</description></listheader>
/// <item><term>A</term><description>Format as a language specific alias if possible</description></item>
/// <item><term>G</term><description>Format with a language specific global prefix.</description></item>
/// <item><term>AG</term><description>Format with a language specific alias if possible, otherwise include a global prefix.</description></item>
/// <item><term>R</term><description>The raw full name without any qualifications</description></item>
/// </list>
/// </remarks>
/// <exception cref="NotSupportedException"><paramref name="format"/> is not supported</exception>
[SuppressMessage( "Style", "IDE0046:Convert to conditional expression", Justification = "Nested conditionals is never simpler" )]
public override string ToString( string format, IFormatProvider? formatProvider )
{
// default to the C# formatter unless specified.
formatProvider ??= NamespaceQualifiedNameFormatter.CSharp;
// if no custom formatter is available, then just produce the raw name.
var customFormatter = (ICustomFormatter<NamespaceQualifiedTypeName>)formatProvider.GetFormat(typeof(ICustomFormatter<NamespaceQualifiedTypeName>));
if(customFormatter is null && NullableAnnotation == NullableAnnotation.Annotated)
{
return $"{string.Join( ".", FullNameParts )}?";
}
return customFormatter is null
? string.Join( ".", FullNameParts )
: customFormatter.Format( format, this, formatProvider );
}
#region Equality
/// <inheritdoc/>
public bool Equals( NamespaceQualifiedTypeName other )
{
return IsNullable == other.IsNullable
&& base.Equals( other );
}
/// <inheritdoc/>
public override bool Equals( object obj )
{
return obj is NamespaceQualifiedTypeName other
&& Equals( other );
}
/// <inheritdoc/>
public override int GetHashCode( )
{
return HashCode.Combine( base.GetHashCode(), NullableAnnotation );
}
/// <summary>Equality operator to test two names for equality</summary>
/// <param name="left">left name to test</param>
/// <param name="right">Right name to test</param>
/// <returns>true if the names are equal</returns>
public static bool operator ==( NamespaceQualifiedTypeName left, NamespaceQualifiedTypeName right )
{
return left.Equals( right );
}
/// <summary>Equality operator to test two names for inequality</summary>
/// <param name="left">left name to test</param>
/// <param name="right">Right name to test</param>
/// <returns>true if the names are <em><b>NOT</b></em> equal; false otherwise</returns>
public static bool operator !=( NamespaceQualifiedTypeName left, NamespaceQualifiedTypeName right )
{
return !(left == right);
}
#endregion
// IFF sym is a Nullable<T> (Nullable value type) this will get the simple name of T
[SuppressMessage( "Style", "IDE0046:Convert to conditional expression", Justification = "Nested conditionals != simpler" )]
private static string GetNullableSimpleName( ITypeSymbol sym )
{
if(sym is IArrayTypeSymbol arrayTypeSymbol)
{
return "Array";
}
return sym.IsNullableValueType() && sym is INamedTypeSymbol ns
? ns.TypeArguments[ 0 ].Name
: sym.Name;
}
[SuppressMessage( "Style", "IDE0046:Convert to conditional expression", Justification = "Nested conditionals != simpler" )]
private static IEnumerable<string> GetNullableNamespaceNames( ITypeSymbol sym )
{
if(sym is IArrayTypeSymbol arrayTypeSymbol)
{
return ["System"];
}
return sym.IsNullableValueType() && sym is INamedTypeSymbol ns
? ns.TypeArguments[ 0 ].GetNamespaceNames()
: sym.GetNamespaceNames();
}
}
}