|
// 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.
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.PooledObjects;
namespace Microsoft.CodeAnalysis.CSharp
{
#pragma warning disable CA1200 // Avoid using cref tags with a prefix
/// <summary>
/// Displays a symbol in the C# style.
/// </summary>
/// <seealso cref="T:Microsoft.CodeAnalysis.VisualBasic.SymbolDisplay"/>
#pragma warning restore CA1200 // Avoid using cref tags with a prefix
public static class SymbolDisplay
{
/// <summary>
/// Displays a symbol in the C# style, based on a <see cref="SymbolDisplayFormat"/>.
/// </summary>
/// <param name="symbol">The symbol to be displayed.</param>
/// <param name="format">The formatting options to apply. If null is passed, <see cref="SymbolDisplayFormat.CSharpErrorMessageFormat"/> will be used.</param>
/// <returns>A formatted string that can be displayed to the user.</returns>
/// <remarks>
/// The return value is not expected to be syntactically valid C#.
/// </remarks>
public static string ToDisplayString(
ISymbol symbol,
SymbolDisplayFormat? format = null)
{
// null indicates the default format
format = format ?? SymbolDisplayFormat.CSharpErrorMessageFormat;
return ToDisplayString(symbol, semanticModelOpt: null, positionOpt: -1, format: format, minimal: false);
}
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
public static string ToDisplayString(
ITypeSymbol symbol,
CodeAnalysis.NullableFlowState nullableFlowState,
SymbolDisplayFormat? format = null)
{
return ToDisplayString(symbol, nullableFlowState.ToAnnotation(), format);
}
public static string ToDisplayString(
ITypeSymbol symbol,
CodeAnalysis.NullableAnnotation nullableAnnotation,
SymbolDisplayFormat? format = null)
{
// null indicates the default format
format = format ?? SymbolDisplayFormat.CSharpErrorMessageFormat;
symbol = symbol.WithNullableAnnotation(nullableAnnotation);
return ToDisplayString(symbol, semanticModelOpt: null, positionOpt: -1, format: format, minimal: false);
}
private static string ToDisplayString(
ISymbol symbol,
SemanticModel? semanticModelOpt,
int positionOpt,
SymbolDisplayFormat format,
bool minimal)
{
var builder = ArrayBuilder<SymbolDisplayPart>.GetInstance();
PopulateDisplayParts(builder, symbol, semanticModelOpt, positionOpt, format, minimal);
var result = builder.ToDisplayString();
builder.Free();
return result;
}
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
/// <summary>
/// Displays a symbol in the C# style, based on a <see cref="SymbolDisplayFormat"/>.
/// Based on the context, qualify type and member names as little as possible without
/// introducing ambiguities.
/// </summary>
/// <param name="symbol">The symbol to be displayed.</param>
/// <param name="semanticModel">Semantic information about the context in which the symbol is being displayed.</param>
/// <param name="position">A position within the <see cref="SyntaxTree"/> or <paramref name="semanticModel"/>.</param>
/// <param name="format">The formatting options to apply. If null is passed, <see cref="SymbolDisplayFormat.CSharpErrorMessageFormat"/> will be used.</param>
/// <returns>A formatted string that can be displayed to the user.</returns>
/// <remarks>
/// The return value is not expected to be syntactically valid C#.
/// </remarks>
public static string ToMinimalDisplayString(
ISymbol symbol,
SemanticModel semanticModel,
int position,
SymbolDisplayFormat? format = null)
{
format ??= SymbolDisplayFormat.MinimallyQualifiedFormat;
return ToDisplayString(symbol, semanticModel, position, format, minimal: true);
}
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
public static string ToMinimalDisplayString(
ITypeSymbol symbol,
CodeAnalysis.NullableFlowState nullableFlowState,
SemanticModel semanticModel,
int position,
SymbolDisplayFormat? format = null)
{
return ToMinimalDisplayString(symbol, nullableFlowState.ToAnnotation(), semanticModel, position, format);
}
public static string ToMinimalDisplayString(
ITypeSymbol symbol,
CodeAnalysis.NullableAnnotation nullableAnnotation,
SemanticModel semanticModel,
int position,
SymbolDisplayFormat? format = null)
{
format ??= SymbolDisplayFormat.MinimallyQualifiedFormat;
symbol = symbol.WithNullableAnnotation(nullableAnnotation);
return ToDisplayString(symbol, semanticModel, position, format, minimal: true);
}
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
/// <summary>
/// Convert a symbol to an array of string parts, each of which has a kind. Useful for
/// colorizing the display string.
/// </summary>
/// <param name="symbol">The symbol to be displayed.</param>
/// <param name="format">The formatting options to apply. If null is passed, <see cref="SymbolDisplayFormat.CSharpErrorMessageFormat"/> will be used.</param>
/// <returns>A list of display parts.</returns>
/// <remarks>
/// Parts are not localized until they are converted to strings.
/// </remarks>
public static ImmutableArray<SymbolDisplayPart> ToDisplayParts(
ISymbol symbol,
SymbolDisplayFormat? format = null)
{
// null indicates the default format
format = format ?? SymbolDisplayFormat.CSharpErrorMessageFormat;
return ToDisplayParts(
symbol, semanticModelOpt: null, positionOpt: -1, format: format, minimal: false);
}
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
// https://github.com/dotnet/roslyn/issues/35035: Add tests
public static ImmutableArray<SymbolDisplayPart> ToDisplayParts(
ITypeSymbol symbol,
CodeAnalysis.NullableFlowState nullableFlowState,
SymbolDisplayFormat? format = null)
{
// null indicates the default format
format = format ?? SymbolDisplayFormat.CSharpErrorMessageFormat;
return ToDisplayParts(
symbol, nullableFlowState, semanticModelOpt: null, positionOpt: -1, format: format, minimal: false);
}
public static ImmutableArray<SymbolDisplayPart> ToDisplayParts(
ITypeSymbol symbol,
CodeAnalysis.NullableAnnotation nullableAnnotation,
SymbolDisplayFormat? format = null)
{
// null indicates the default format
format ??= SymbolDisplayFormat.CSharpErrorMessageFormat;
return ToDisplayParts(
symbol.WithNullableAnnotation(nullableAnnotation), semanticModelOpt: null, positionOpt: -1, format: format, minimal: false);
}
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
/// <summary>
/// Convert a symbol to an array of string parts, each of which has a kind. Useful for
/// colorizing the display string.
/// </summary>
/// <param name="symbol">The symbol to be displayed.</param>
/// <param name="semanticModel">Semantic information about the context in which the symbol is being displayed.</param>
/// <param name="position">A position within the <see cref="SyntaxTree"/> or <paramref name="semanticModel"/>.</param>
/// <param name="format">The formatting options to apply. If null is passed, <see cref="SymbolDisplayFormat.CSharpErrorMessageFormat"/> will be used.</param>
/// <returns>A list of display parts.</returns>
/// <remarks>
/// Parts are not localized until they are converted to strings.
/// </remarks>
public static ImmutableArray<SymbolDisplayPart> ToMinimalDisplayParts(
ISymbol symbol,
SemanticModel semanticModel,
int position,
SymbolDisplayFormat? format = null)
{
format ??= SymbolDisplayFormat.MinimallyQualifiedFormat;
return ToDisplayParts(symbol, semanticModel, position, format, minimal: true);
}
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
// https://github.com/dotnet/roslyn/issues/35035: Add tests
public static ImmutableArray<SymbolDisplayPart> ToMinimalDisplayParts(
ITypeSymbol symbol,
CodeAnalysis.NullableFlowState nullableFlowState,
SemanticModel semanticModel,
int position,
SymbolDisplayFormat? format = null)
{
format ??= SymbolDisplayFormat.MinimallyQualifiedFormat;
return ToDisplayParts(symbol, nullableFlowState, semanticModel, position, format, minimal: true);
}
public static ImmutableArray<SymbolDisplayPart> ToMinimalDisplayParts(
ITypeSymbol symbol,
CodeAnalysis.NullableAnnotation nullableAnnotation,
SemanticModel semanticModel,
int position,
SymbolDisplayFormat? format = null)
{
format ??= SymbolDisplayFormat.MinimallyQualifiedFormat;
return ToDisplayParts(symbol.WithNullableAnnotation(nullableAnnotation), semanticModel, position, format, minimal: true);
}
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
private static ImmutableArray<SymbolDisplayPart> ToDisplayParts(
ITypeSymbol symbol,
CodeAnalysis.NullableFlowState nullableFlowState,
SemanticModel? semanticModelOpt,
int positionOpt,
SymbolDisplayFormat format,
bool minimal)
{
return ToDisplayParts(symbol.WithNullableAnnotation(nullableFlowState.ToAnnotation()), semanticModelOpt, positionOpt, format, minimal);
}
private static ImmutableArray<SymbolDisplayPart> ToDisplayParts(
ISymbol symbol,
SemanticModel? semanticModelOpt,
int positionOpt,
SymbolDisplayFormat format,
bool minimal)
{
ArrayBuilder<SymbolDisplayPart> builder = ArrayBuilder<SymbolDisplayPart>.GetInstance();
PopulateDisplayParts(builder, symbol, semanticModelOpt, positionOpt, format, minimal);
return builder.ToImmutableAndFree();
}
private static ArrayBuilder<SymbolDisplayPart> PopulateDisplayParts(
ArrayBuilder<SymbolDisplayPart> builder,
ISymbol symbol,
SemanticModel? semanticModelOpt,
int positionOpt,
SymbolDisplayFormat format,
bool minimal)
{
if (symbol == null)
{
throw new ArgumentNullException(nameof(symbol));
}
if (minimal)
{
if (semanticModelOpt == null)
{
throw new ArgumentException(CSharpResources.SyntaxTreeSemanticModelMust);
}
else if (positionOpt < 0 || positionOpt > semanticModelOpt.SyntaxTree.Length) // Note: not >= since EOF is allowed.
{
throw new ArgumentOutOfRangeException(CSharpResources.PositionNotWithinTree);
}
}
else
{
Debug.Assert(semanticModelOpt == null);
Debug.Assert(positionOpt < 0);
}
// Do not leak unspeakable name of a Simple Program entry point through diagnostics,
// and, for consistency, with other display options.
if ((symbol as Symbols.PublicModel.MethodSymbol)?.UnderlyingMethodSymbol is SynthesizedSimpleProgramEntryPointSymbol)
{
builder.Add(new SymbolDisplayPart(SymbolDisplayPartKind.MethodName, symbol, "<top-level-statements-entry-point>"));
}
else
{
var visitor = SymbolDisplayVisitor.GetInstance(builder, format, semanticModelOpt, positionOpt);
symbol.Accept(visitor);
visitor.Free();
}
return builder;
}
/// <summary>
/// Returns a string representation of an object of primitive type.
/// </summary>
/// <param name="obj">A value to display as a string.</param>
/// <param name="quoteStrings">Whether or not to quote string literals.</param>
/// <param name="useHexadecimalNumbers">Whether or not to display integral literals in hexadecimal.</param>
/// <returns>A string representation of an object of primitive type (or null if the type is not supported).</returns>
/// <remarks>
/// Handles <see cref="bool"/>, <see cref="string"/>, <see cref="char"/>, <see cref="sbyte"/>
/// <see cref="byte"/>, <see cref="short"/>, <see cref="ushort"/>, <see cref="int"/>, <see cref="uint"/>,
/// <see cref="long"/>, <see cref="ulong"/>, <see cref="double"/>, <see cref="float"/>, <see cref="decimal"/>,
/// and <c>null</c>.
/// </remarks>
public static string FormatPrimitive(object obj, bool quoteStrings, bool useHexadecimalNumbers)
{
var options = ObjectDisplayOptions.EscapeNonPrintableCharacters;
if (quoteStrings)
{
options |= ObjectDisplayOptions.UseQuotes;
}
if (useHexadecimalNumbers)
{
options |= ObjectDisplayOptions.UseHexadecimalNumbers;
}
return ObjectDisplay.FormatPrimitive(obj, options);
}
/// <summary>
/// Returns a C# string literal with the given value.
/// </summary>
/// <param name="value">The value that the resulting string literal should have.</param>
/// <param name="quote">True to put (double) quotes around the string literal.</param>
/// <returns>A string literal with the given value.</returns>
/// <remarks>
/// Escapes non-printable characters.
/// </remarks>
public static string FormatLiteral(string value, bool quote)
{
var options = ObjectDisplayOptions.EscapeNonPrintableCharacters |
(quote ? ObjectDisplayOptions.UseQuotes : ObjectDisplayOptions.None);
return ObjectDisplay.FormatLiteral(value, options);
}
/// <summary>
/// Returns a C# character literal with the given value.
/// </summary>
/// <param name="c">The value that the resulting character literal should have.</param>
/// <param name="quote">True to put (single) quotes around the character literal.</param>
/// <returns>A character literal with the given value.</returns>
/// <remarks>
/// Escapes non-printable characters.
/// </remarks>
public static string FormatLiteral(char c, bool quote)
{
var options = ObjectDisplayOptions.EscapeNonPrintableCharacters |
(quote ? ObjectDisplayOptions.UseQuotes : ObjectDisplayOptions.None);
return ObjectDisplay.FormatLiteral(c, options);
}
}
}
|