1
ļ»æ// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
3
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4
// software and associated documentation files (the "Software"), to deal in the Software
5
// without restriction, including without limitation the rights to use, copy, modify, merge,
6
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7
// to whom the Software is furnished to do so, subject to the following conditions:
9
// The above copyright notice and this permission notice shall be included in all copies or
10
// substantial portions of the Software.
12
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17
// DEALINGS IN THE SOFTWARE.
21
using System.Collections.Generic;
23
using ICSharpCode.NRefactory.TypeSystem;
24
using ICSharpCode.NRefactory.TypeSystem.Implementation;
26
namespace ICSharpCode.NRefactory.Documentation
29
/// Provides ID strings for entities. (C# 4.0 spec, Ā§A.3.1)
30
/// ID strings are used to identify members in XML documentation files.
32
public static class IdStringProvider
36
/// Gets the ID string (C# 4.0 spec, Ā§A.3.1) for the specified entity.
38
public static string GetIdString(this IEntity entity)
40
StringBuilder b = new StringBuilder();
41
switch (entity.EntityType) {
42
case EntityType.TypeDefinition:
44
AppendTypeName(b, (ITypeDefinition)entity, false);
46
case EntityType.Field:
49
case EntityType.Property:
50
case EntityType.Indexer:
53
case EntityType.Event:
60
IMember member = (IMember)entity;
61
AppendTypeName(b, member.DeclaringType, false);
63
if (member.IsExplicitInterfaceImplementation && member.Name.IndexOf('.') < 0 && member.ImplementedInterfaceMembers.Count == 1) {
64
AppendTypeName(b, member.ImplementedInterfaceMembers[0].DeclaringType, true);
67
b.Append(member.Name.Replace('.', '#'));
68
IMethod method = member as IMethod;
69
if (method != null && method.TypeParameters.Count > 0) {
71
b.Append(method.TypeParameters.Count);
73
IParameterizedMember parameterizedMember = member as IParameterizedMember;
74
if (parameterizedMember != null && parameterizedMember.Parameters.Count > 0) {
76
var parameters = parameterizedMember.Parameters;
77
for (int i = 0; i < parameters.Count; i++) {
78
if (i > 0) b.Append(',');
79
AppendTypeName(b, parameters[i].Type, false);
83
if (member.EntityType == EntityType.Operator && (member.Name == "op_Implicit" || member.Name == "op_Explicit")) {
85
AppendTypeName(b, member.ReturnType, false);
92
public static string GetTypeName(IType type)
95
throw new ArgumentNullException("type");
96
StringBuilder b = new StringBuilder();
97
AppendTypeName(b, type, false);
101
static void AppendTypeName(StringBuilder b, IType type, bool explicitInterfaceImpl)
104
case TypeKind.Dynamic:
105
b.Append(explicitInterfaceImpl ? "System#Object" : "System.Object");
107
case TypeKind.TypeParameter:
108
ITypeParameter tp = (ITypeParameter)type;
109
if (explicitInterfaceImpl) {
113
if (tp.OwnerType == EntityType.Method)
119
ArrayType array = (ArrayType)type;
120
AppendTypeName(b, array.ElementType, explicitInterfaceImpl);
122
if (array.Dimensions > 1) {
123
for (int i = 0; i < array.Dimensions; i++) {
125
b.Append(explicitInterfaceImpl ? '@' : ',');
126
if (!explicitInterfaceImpl)
132
case TypeKind.Pointer:
133
AppendTypeName(b, ((PointerType)type).ElementType, explicitInterfaceImpl);
136
case TypeKind.ByReference:
137
AppendTypeName(b, ((ByReferenceType)type).ElementType, explicitInterfaceImpl);
141
IType declType = type.DeclaringType;
142
if (declType != null) {
143
AppendTypeName(b, declType, explicitInterfaceImpl);
144
b.Append(explicitInterfaceImpl ? '#' : '.');
146
AppendTypeParameters(b, type, declType.TypeParameterCount, explicitInterfaceImpl);
148
if (explicitInterfaceImpl)
149
b.Append(type.FullName.Replace('.', '#'));
151
b.Append(type.FullName);
152
AppendTypeParameters(b, type, 0, explicitInterfaceImpl);
158
static void AppendTypeParameters(StringBuilder b, IType type, int outerTypeParameterCount, bool explicitInterfaceImpl)
160
int tpc = type.TypeParameterCount - outerTypeParameterCount;
162
ParameterizedType pt = type as ParameterizedType;
165
var ta = pt.TypeArguments;
166
for (int i = outerTypeParameterCount; i < ta.Count; i++) {
167
if (i > outerTypeParameterCount)
168
b.Append(explicitInterfaceImpl ? '@' : ',');
169
AppendTypeName(b, ta[i], explicitInterfaceImpl);
180
#region ParseMemberName
182
/// Parse the ID string into a member reference.
184
/// <param name="memberIdString">The ID string representing the member (with "M:", "F:", "P:" or "E:" prefix).</param>
185
/// <returns>A member reference that represents the ID string.</returns>
186
/// <exception cref="ReflectionNameParseException">The syntax of the ID string is invalid</exception>
188
/// The member reference will look in <see cref="ITypeResolveContext.CurrentAssembly"/> first,
189
/// and if the member is not found there,
190
/// it will look in all other assemblies of the compilation.
192
public static IMemberReference ParseMemberIdString(string memberIdString)
194
if (memberIdString == null)
195
throw new ArgumentNullException("memberIdString");
196
if (memberIdString.Length < 2 || memberIdString[1] != ':')
197
throw new ReflectionNameParseException(0, "Missing type tag");
198
char typeChar = memberIdString[0];
199
int parenPos = memberIdString.IndexOf('(');
201
parenPos = memberIdString.LastIndexOf('~');
203
parenPos = memberIdString.Length;
204
int dotPos = memberIdString.LastIndexOf('.', parenPos - 1);
206
throw new ReflectionNameParseException(0, "Could not find '.' separating type name from member name");
207
string typeName = memberIdString.Substring(0, dotPos);
209
ITypeReference typeReference = ParseTypeName(typeName, ref pos);
210
if (pos != typeName.Length)
211
throw new ReflectionNameParseException(pos, "Expected end of type name");
212
// string memberName = memberIDString.Substring(dotPos + 1, parenPos - (dotPos + 1));
213
// pos = memberName.LastIndexOf("``");
215
// memberName = memberName.Substring(0, pos);
216
// memberName = memberName.Replace('#', '.');
217
return new IdStringMemberReference(typeReference, typeChar, memberIdString);
221
#region ParseTypeName
223
/// Parse the ID string type name into a type reference.
225
/// <param name="typeName">The ID string representing the type (the "T:" prefix is optional).</param>
226
/// <returns>A type reference that represents the ID string.</returns>
227
/// <exception cref="ReflectionNameParseException">The syntax of the ID string is invalid</exception>
230
/// The type reference will look in <see cref="ITypeResolveContext.CurrentAssembly"/> first,
231
/// and if the type is not found there,
232
/// it will look in all other assemblies of the compilation.
235
/// If the type is open (contains type parameters '`0' or '``0'),
236
/// an <see cref="ITypeResolveContext"/> with the appropriate CurrentTypeDefinition/CurrentMember is required
237
/// to resolve the reference to the ITypeParameter.
240
public static ITypeReference ParseTypeName(string typeName)
242
if (typeName == null)
243
throw new ArgumentNullException("typeName");
245
if (typeName.StartsWith("T:", StringComparison.Ordinal))
247
ITypeReference r = ParseTypeName(typeName, ref pos);
248
if (pos < typeName.Length)
249
throw new ReflectionNameParseException(pos, "Expected end of type name");
253
static bool IsIDStringSpecialCharacter(char c)
273
static ITypeReference ParseTypeName(string typeName, ref int pos)
275
string reflectionTypeName = typeName;
276
if (pos == typeName.Length)
277
throw new ReflectionNameParseException(pos, "Unexpected end");
278
ITypeReference result;
279
if (reflectionTypeName[pos] == '`') {
280
// type parameter reference
282
if (pos == reflectionTypeName.Length)
283
throw new ReflectionNameParseException(pos, "Unexpected end");
284
if (reflectionTypeName[pos] == '`') {
285
// method type parameter reference
287
int index = ReflectionHelper.ReadTypeParameterCount(reflectionTypeName, ref pos);
288
result = new TypeParameterReference(EntityType.Method, index);
290
// class type parameter reference
291
int index = ReflectionHelper.ReadTypeParameterCount(reflectionTypeName, ref pos);
292
result = new TypeParameterReference(EntityType.TypeDefinition, index);
295
// not a type parameter reference: read the actual type name
296
List<ITypeReference> typeArguments = new List<ITypeReference>();
297
int typeParameterCount;
298
string typeNameWithoutSuffix = ReadTypeName(typeName, ref pos, true, out typeParameterCount, typeArguments);
299
result = new GetPotentiallyNestedClassTypeReference(typeNameWithoutSuffix, typeParameterCount);
300
while (pos < typeName.Length && typeName[pos] == '.') {
302
string nestedTypeName = ReadTypeName(typeName, ref pos, false, out typeParameterCount, typeArguments);
303
result = new NestedTypeReference(result, nestedTypeName, typeParameterCount);
305
if (typeArguments.Count > 0) {
306
result = new ParameterizedTypeReference(result, typeArguments);
309
while (pos < typeName.Length) {
310
switch (typeName[pos]) {
315
if (pos == typeName.Length)
316
throw new ReflectionNameParseException(pos, "Unexpected end");
317
if (typeName[pos] == ',')
319
} while (typeName[pos] != ']');
320
result = new ArrayTypeReference(result, dimensions);
323
result = new PointerTypeReference(result);
326
result = new ByReferenceTypeReference(result);
336
static string ReadTypeName(string typeName, ref int pos, bool allowDottedName, out int typeParameterCount, List<ITypeReference> typeArguments)
339
// skip the simple name portion:
340
while (pos < typeName.Length && !IsIDStringSpecialCharacter(typeName[pos]) && (allowDottedName || typeName[pos] != '.'))
343
throw new ReflectionNameParseException(pos, "Expected type name");
344
string shortTypeName = typeName.Substring(startPos, pos - startPos);
345
// read type arguments:
346
typeParameterCount = 0;
347
if (pos < typeName.Length && typeName[pos] == '`') {
348
// unbound generic type
350
typeParameterCount = ReflectionHelper.ReadTypeParameterCount(typeName, ref pos);
351
} else if (pos < typeName.Length && typeName[pos] == '{') {
352
// bound generic type
353
typeArguments = new List<ITypeReference>();
356
typeArguments.Add(ParseTypeName(typeName, ref pos));
357
typeParameterCount++;
358
if (pos == typeName.Length)
359
throw new ReflectionNameParseException(pos, "Unexpected end");
360
} while (typeName[pos] == ',');
361
if (typeName[pos] != '}')
362
throw new ReflectionNameParseException(pos, "Expected '}'");
364
return shortTypeName;
370
/// Finds the entity in the given type resolve context.
372
/// <param name="idString">ID string of the entity.</param>
373
/// <param name="context">Type resolve context</param>
374
/// <returns>Returns the entity, or null if it is not found.</returns>
375
/// <exception cref="ReflectionNameParseException">The syntax of the ID string is invalid</exception>
376
public static IEntity FindEntity(string idString, ITypeResolveContext context)
378
if (idString == null)
379
throw new ArgumentNullException("idString");
381
throw new ArgumentNullException("context");
382
if (idString.StartsWith("T:", StringComparison.Ordinal)) {
383
return ParseTypeName(idString.Substring(2)).Resolve(context).GetDefinition();
385
return ParseMemberIdString(idString).Resolve(context);