|
// 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.
namespace Microsoft.CodeAnalysis.BannedApiAnalyzers
{
/// <summary>
/// Stripped down port of the code in roslyn. Responsible only for determining the <see cref="ISymbol.Name"/> and
/// name of the <see cref="ISymbol.ContainingSymbol"/> for a given xml doc comment symbol id.
/// </summary>
internal static class DocumentationCommentIdParser
{
private static readonly char[] s_nameDelimiters = { ':', '.', '(', ')', '{', '}', '[', ']', ',', '\'', '@', '*', '`', '~' };
public static (string ParentName, string SymbolName)? ParseDeclaredSymbolId(string id)
{
if (id == null)
return null;
if (id.Length < 2)
return null;
int index = 0;
return ParseDeclaredId(id, ref index);
}
private static (string ParentName, string SymbolName)? ParseDeclaredId(string id, ref int index)
{
var kindChar = PeekNextChar(id, index);
switch (kindChar)
{
case 'E': // Events
case 'F': // Fields
case 'M': // Methods
case 'P': // Properties
case 'T': // Types
case 'N': // Namespaces
break;
default:
// Documentation comment id must start with E, F, M, N, P or T.
return null;
}
index++;
if (PeekNextChar(id, index) == ':')
index++;
string parentName = "";
// process dotted names
while (true)
{
var symbolName = ParseName(id, ref index);
// has type parameters?
if (PeekNextChar(id, index) == '`')
{
index++;
// method type parameters?
if (PeekNextChar(id, index) == '`')
index++;
ReadNextInteger(id, ref index);
}
if (PeekNextChar(id, index) == '.')
{
index++;
parentName = symbolName;
continue;
}
else
{
return (parentName, symbolName);
}
}
}
private static char PeekNextChar(string id, int index)
=> index >= id.Length ? '\0' : id[index];
private static string ParseName(string id, ref int index)
{
string name;
int delimiterOffset = id.IndexOfAny(s_nameDelimiters, index);
if (delimiterOffset >= 0)
{
name = id[index..delimiterOffset];
index = delimiterOffset;
}
else
{
name = id[index..];
index = id.Length;
}
return name.Replace('#', '.');
}
private static void ReadNextInteger(string id, ref int index)
{
while (index < id.Length && char.IsDigit(id[index]))
index++;
}
}
}
|