|
// 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.
#nullable disable
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel
{
internal partial class CSharpCodeModelService
{
public override string GetPrototype(SyntaxNode node, ISymbol symbol, PrototypeFlags flags)
{
Debug.Assert(symbol != null);
if (node == null)
{
switch (symbol.Kind)
{
case SymbolKind.Field:
return GetVariablePrototype((IFieldSymbol)symbol, flags);
case SymbolKind.Method:
return GetFunctionPrototype((IMethodSymbol)symbol, flags);
case SymbolKind.Property:
return GetPropertyPrototype((IPropertySymbol)symbol, flags);
case SymbolKind.Event:
return GetEventPrototype((IEventSymbol)symbol, flags);
case SymbolKind.NamedType:
var namedType = (INamedTypeSymbol)symbol;
if (namedType.TypeKind == TypeKind.Delegate)
{
return GetDelegatePrototype((INamedTypeSymbol)symbol, flags);
}
break;
}
Debug.Fail("Invalid symbol kind: " + symbol.Kind);
throw Exceptions.ThrowEUnexpected();
}
else
{
switch (node)
{
case BaseMethodDeclarationSyntax methodDeclaration:
return GetFunctionPrototype(methodDeclaration, (IMethodSymbol)symbol, flags);
case BasePropertyDeclarationSyntax propertyDeclaration:
return GetPropertyPrototype(propertyDeclaration, (IPropertySymbol)symbol, flags);
case VariableDeclaratorSyntax variableDeclarator when symbol.Kind == SymbolKind.Field:
return GetVariablePrototype(variableDeclarator, (IFieldSymbol)symbol, flags);
case EnumMemberDeclarationSyntax enumMember:
return GetVariablePrototype(enumMember, (IFieldSymbol)symbol, flags);
case DelegateDeclarationSyntax delegateDeclaration:
return GetDelegatePrototype(delegateDeclaration, (INamedTypeSymbol)symbol, flags);
}
// Crazily, events for source are not implemented by the legacy C#
// code model implementation, but they are for metadata events.
Debug.Fail(string.Format("Invalid node/symbol kind: {0}/{1}", node.Kind(), symbol.Kind));
throw Exceptions.ThrowENotImpl();
}
}
private string GetDelegatePrototype(INamedTypeSymbol symbol, PrototypeFlags flags)
{
if ((flags & PrototypeFlags.Signature) != 0)
{
if (flags != PrototypeFlags.Signature)
{
// vsCMPrototypeUniqueSignature can't be combined with anything else.
// Note that we only throw E_FAIL in this case. All others throw E_INVALIDARG.
throw Exceptions.ThrowEFail();
}
// The unique signature is simply the node key.
return GetExternalSymbolFullName(symbol);
}
var builder = new StringBuilder();
AppendDelegatePrototype(builder, symbol, flags, symbol.Name);
return builder.ToString();
}
private string GetDelegatePrototype(DelegateDeclarationSyntax node, INamedTypeSymbol symbol, PrototypeFlags flags)
{
if ((flags & PrototypeFlags.Signature) != 0)
{
if (flags != PrototypeFlags.Signature)
{
// vsCMPrototypeUniqueSignature can't be combined with anything else.
throw Exceptions.ThrowEInvalidArg();
}
// The unique signature is simply the node key.
return GetNodeKey(node).Name;
}
var builder = new StringBuilder();
AppendDelegatePrototype(builder, symbol, flags, GetName(node));
return builder.ToString();
}
private string GetEventPrototype(IEventSymbol symbol, PrototypeFlags flags)
{
if ((flags & PrototypeFlags.Signature) != 0)
{
if (flags != PrototypeFlags.Signature)
{
// vsCMPrototypeUniqueSignature can't be combined with anything else.
throw Exceptions.ThrowEInvalidArg();
}
// The unique signature is simply the node key.
flags = PrototypeFlags.FullName | PrototypeFlags.Type;
}
var builder = new StringBuilder();
AppendEventPrototype(builder, symbol, flags, symbol.Name);
return builder.ToString();
}
private string GetFunctionPrototype(IMethodSymbol symbol, PrototypeFlags flags)
{
if ((flags & PrototypeFlags.Signature) != 0)
{
if (flags != PrototypeFlags.Signature)
{
// vsCMPrototypeUniqueSignature can't be combined with anything else.
throw Exceptions.ThrowEInvalidArg();
}
// The unique signature is simply the node key.
flags = PrototypeFlags.FullName | PrototypeFlags.Type | PrototypeFlags.ParameterTypes;
}
var builder = new StringBuilder();
AppendFunctionPrototype(builder, symbol, flags, symbol.Name);
return builder.ToString();
}
private string GetFunctionPrototype(BaseMethodDeclarationSyntax node, IMethodSymbol symbol, PrototypeFlags flags)
{
if ((flags & PrototypeFlags.Signature) != 0)
{
if (flags != PrototypeFlags.Signature)
{
// vsCMPrototypeUniqueSignature can't be combined with anything else.
throw Exceptions.ThrowEInvalidArg();
}
// The unique signature is simply the node key.
return GetNodeKey(node).Name;
}
var builder = new StringBuilder();
AppendFunctionPrototype(builder, symbol, flags, GetName(node));
return builder.ToString();
}
private string GetPropertyPrototype(IPropertySymbol symbol, PrototypeFlags flags)
{
if ((flags & PrototypeFlags.Signature) != 0)
{
if (flags != PrototypeFlags.Signature)
{
// vsCMPrototypeUniqueSignature can't be combined with anything else.
throw Exceptions.ThrowEInvalidArg();
}
// The unique signature is simply the node key.
flags = PrototypeFlags.FullName | PrototypeFlags.Type;
}
var builder = new StringBuilder();
AppendPropertyPrototype(builder, symbol, flags, symbol.Name);
return builder.ToString();
}
private string GetPropertyPrototype(BasePropertyDeclarationSyntax node, IPropertySymbol symbol, PrototypeFlags flags)
{
if ((flags & PrototypeFlags.Signature) != 0)
{
if (flags != PrototypeFlags.Signature)
{
// vsCMPrototypeUniqueSignature can't be combined with anything else.
throw Exceptions.ThrowEInvalidArg();
}
// The unique signature is simply the node key.
return GetNodeKey(node).Name;
}
var builder = new StringBuilder();
AppendPropertyPrototype(builder, symbol, flags, GetName(node));
return builder.ToString();
}
private string GetVariablePrototype(IFieldSymbol symbol, PrototypeFlags flags)
{
if ((flags & PrototypeFlags.Signature) != 0)
{
if (flags != PrototypeFlags.Signature)
{
// vsCMPrototypeUniqueSignature can't be combined with anything else.
throw Exceptions.ThrowEInvalidArg();
}
// The unique signature is simply the node key.
flags = PrototypeFlags.FullName | PrototypeFlags.Type;
}
var builder = new StringBuilder();
AppendVariablePrototype(builder, symbol, flags, symbol.Name);
return builder.ToString();
}
private string GetVariablePrototype(VariableDeclaratorSyntax node, IFieldSymbol symbol, PrototypeFlags flags)
{
if ((flags & PrototypeFlags.Signature) != 0)
{
if (flags != PrototypeFlags.Signature)
{
// vsCMPrototypeUniqueSignature can't be combined with anything else.
throw Exceptions.ThrowEInvalidArg();
}
// The unique signature is simply the node key.
return GetNodeKey(node).Name;
}
var builder = new StringBuilder();
AppendVariablePrototype(builder, symbol, flags, GetName(node));
if ((flags & PrototypeFlags.Initializer) != 0 &&
node.Initializer != null &&
node.Initializer.Value != null &&
!node.Initializer.Value.IsMissing)
{
builder.Append(" = ");
builder.Append(node.Initializer.Value);
}
return builder.ToString();
}
private string GetVariablePrototype(EnumMemberDeclarationSyntax node, IFieldSymbol symbol, PrototypeFlags flags)
{
if ((flags & PrototypeFlags.Signature) != 0)
{
if (flags != PrototypeFlags.Signature)
{
// vsCMPrototypeUniqueSignature can't be combined with anything else.
throw Exceptions.ThrowEInvalidArg();
}
// The unique signature is simply the node key.
return GetNodeKey(node).Name;
}
var builder = new StringBuilder();
AppendVariablePrototype(builder, symbol, flags, GetName(node));
if ((flags & PrototypeFlags.Initializer) != 0 &&
node.EqualsValue != null &&
node.EqualsValue.Value != null &&
!node.EqualsValue.Value.IsMissing)
{
builder.Append(" = ");
builder.Append(node.EqualsValue.Value);
}
return builder.ToString();
}
private void AppendDelegatePrototype(StringBuilder builder, INamedTypeSymbol symbol, PrototypeFlags flags, string baseName)
{
builder.Append("delegate ");
if ((flags & PrototypeFlags.Type) != 0)
{
builder.Append(GetAsStringForCodeTypeRef(symbol.DelegateInvokeMethod.ReturnType));
if (((flags & PrototypeFlags.NameMask) != PrototypeFlags.NoName) ||
((flags & (PrototypeFlags.ParameterNames | PrototypeFlags.ParameterTypes)) != 0))
{
builder.Append(' ');
}
}
var addSpace = true;
switch (flags & PrototypeFlags.NameMask)
{
case PrototypeFlags.FullName:
AppendTypeNamePrototype(builder, includeNamespaces: true, includeGenerics: false, symbol: symbol.ContainingSymbol);
builder.Append('.');
goto case PrototypeFlags.BaseName;
case PrototypeFlags.TypeName:
AppendTypeNamePrototype(builder, includeNamespaces: true, includeGenerics: true, symbol: symbol.ContainingSymbol);
builder.Append('.');
goto case PrototypeFlags.BaseName;
case PrototypeFlags.BaseName:
builder.Append(baseName);
break;
case PrototypeFlags.NoName:
addSpace = false;
break;
}
if ((flags & (PrototypeFlags.ParameterNames | PrototypeFlags.ParameterTypes)) != 0)
{
if (addSpace)
{
builder.Append(' ');
}
builder.Append('(');
AppendParametersPrototype(builder, symbol.DelegateInvokeMethod.Parameters, flags);
builder.Append(')');
}
}
private void AppendEventPrototype(StringBuilder builder, IEventSymbol symbol, PrototypeFlags flags, string baseName)
{
if ((flags & PrototypeFlags.Type) != 0)
{
builder.Append(GetAsStringForCodeTypeRef(symbol.Type));
if ((flags & PrototypeFlags.NameMask) != PrototypeFlags.NoName)
{
builder.Append(' ');
}
}
switch (flags & PrototypeFlags.NameMask)
{
case PrototypeFlags.FullName:
AppendTypeNamePrototype(builder, includeNamespaces: true, includeGenerics: false, symbol: symbol.ContainingSymbol);
builder.Append('.');
goto case PrototypeFlags.BaseName;
case PrototypeFlags.TypeName:
AppendTypeNamePrototype(builder, includeNamespaces: false, includeGenerics: true, symbol: symbol.ContainingSymbol);
builder.Append('.');
goto case PrototypeFlags.BaseName;
case PrototypeFlags.BaseName:
builder.Append(baseName);
break;
}
}
private void AppendFunctionPrototype(StringBuilder builder, IMethodSymbol symbol, PrototypeFlags flags, string baseName)
{
if ((flags & PrototypeFlags.Type) != 0)
{
builder.Append(GetAsStringForCodeTypeRef(symbol.ReturnType));
if ((flags & PrototypeFlags.NameMask) != PrototypeFlags.NoName)
{
builder.Append(' ');
}
}
var addSpace = true;
switch (flags & PrototypeFlags.NameMask)
{
case PrototypeFlags.FullName:
AppendTypeNamePrototype(builder, includeNamespaces: true, includeGenerics: false, symbol: symbol.ContainingSymbol);
builder.Append('.');
goto case PrototypeFlags.BaseName;
case PrototypeFlags.TypeName:
AppendTypeNamePrototype(builder, includeNamespaces: false, includeGenerics: true, symbol: symbol.ContainingSymbol);
builder.Append('.');
goto case PrototypeFlags.BaseName;
case PrototypeFlags.BaseName:
builder.Append(baseName);
break;
case PrototypeFlags.NoName:
addSpace = false;
break;
}
if ((flags & (PrototypeFlags.ParameterNames | PrototypeFlags.ParameterTypes)) != 0)
{
if (addSpace)
{
builder.Append(' ');
}
builder.Append('(');
AppendParametersPrototype(builder, symbol.Parameters, flags);
builder.Append(')');
}
}
private void AppendPropertyPrototype(StringBuilder builder, IPropertySymbol symbol, PrototypeFlags flags, string baseName)
{
if ((flags & PrototypeFlags.Type) != 0)
{
builder.Append(GetAsStringForCodeTypeRef(symbol.Type));
if ((flags & PrototypeFlags.NameMask) != PrototypeFlags.NoName)
{
builder.Append(' ');
}
}
switch (flags & PrototypeFlags.NameMask)
{
case PrototypeFlags.FullName:
AppendTypeNamePrototype(builder, includeNamespaces: true, includeGenerics: false, symbol: symbol.ContainingSymbol);
builder.Append('.');
goto case PrototypeFlags.BaseName;
case PrototypeFlags.TypeName:
AppendTypeNamePrototype(builder, includeNamespaces: false, includeGenerics: true, symbol: symbol.ContainingSymbol);
builder.Append('.');
goto case PrototypeFlags.BaseName;
case PrototypeFlags.BaseName:
if (symbol.IsIndexer)
{
builder.Append("this[");
AppendParametersPrototype(builder, symbol.Parameters, PrototypeFlags.ParameterTypes | PrototypeFlags.ParameterNames);
builder.Append(']');
}
else
{
builder.Append(baseName);
}
break;
}
}
private void AppendVariablePrototype(StringBuilder builder, IFieldSymbol symbol, PrototypeFlags flags, string baseName)
{
if ((flags & PrototypeFlags.Type) != 0)
{
builder.Append(GetAsStringForCodeTypeRef(symbol.Type));
if ((flags & PrototypeFlags.NameMask) != PrototypeFlags.NoName)
{
builder.Append(' ');
}
}
switch (flags & PrototypeFlags.NameMask)
{
case PrototypeFlags.FullName:
AppendTypeNamePrototype(builder, includeNamespaces: true, includeGenerics: false, symbol: symbol.ContainingSymbol);
builder.Append('.');
goto case PrototypeFlags.BaseName;
case PrototypeFlags.TypeName:
AppendTypeNamePrototype(builder, includeNamespaces: false, includeGenerics: true, symbol: symbol.ContainingSymbol);
builder.Append('.');
goto case PrototypeFlags.BaseName;
case PrototypeFlags.BaseName:
builder.Append(baseName);
break;
}
}
private void AppendParametersPrototype(StringBuilder builder, ImmutableArray<IParameterSymbol> parameters, PrototypeFlags flags)
{
var first = true;
foreach (var parameter in parameters)
{
if (!first)
{
builder.Append(", ");
}
AppendParameterPrototype(builder, flags, parameter);
first = false;
}
}
private void AppendParameterPrototype(StringBuilder builder, PrototypeFlags flags, IParameterSymbol parameter)
{
var addSpace = false;
if ((flags & PrototypeFlags.ParameterTypes) != 0)
{
if (parameter.RefKind == RefKind.Ref)
{
builder.Append("ref ");
}
else if (parameter.RefKind == RefKind.Out)
{
builder.Append("out ");
}
else if (parameter.IsParams)
{
builder.Append("params ");
}
builder.Append(GetAsStringForCodeTypeRef(parameter.Type));
addSpace = true;
}
if ((flags & PrototypeFlags.ParameterNames) != 0)
{
if (addSpace)
{
builder.Append(' ');
}
builder.Append(parameter.Name);
}
}
private static void AppendTypeNamePrototype(StringBuilder builder, bool includeNamespaces, bool includeGenerics, ISymbol symbol)
{
using var _ = ArrayBuilder<ISymbol>.GetInstance(out var symbols);
while (symbol != null)
{
if (symbol.Kind == SymbolKind.Namespace)
{
if (!includeNamespaces || ((INamespaceSymbol)symbol).IsGlobalNamespace)
{
break;
}
}
symbols.Push(symbol);
symbol = symbol.ContainingSymbol;
}
var first = true;
while (symbols.TryPop(out var current))
{
if (!first)
{
builder.Append('.');
}
builder.Append(current.Name);
if (current.Kind == SymbolKind.NamedType)
{
var namedType = (INamedTypeSymbol)current;
if (includeGenerics && namedType.Arity > 0)
{
builder.Append('<');
builder.Append(',', namedType.Arity - 1);
builder.Append('>');
}
}
first = false;
}
}
}
}
|