|
// 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.Composition;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.InlineHints;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.PooledObjects;
namespace Microsoft.CodeAnalysis.CSharp.InlineHints;
/// <summary>
/// The service to locate the positions in which the adornments should appear
/// as well as associate the adornments back to the parameter name
/// </summary>
[ExportLanguageService(typeof(IInlineParameterNameHintsService), LanguageNames.CSharp), Shared]
internal class CSharpInlineParameterNameHintsService : AbstractInlineParameterNameHintsService
{
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public CSharpInlineParameterNameHintsService()
{
}
protected override void AddAllParameterNameHintLocations(
SemanticModel semanticModel,
ISyntaxFactsService syntaxFacts,
SyntaxNode node,
ArrayBuilder<(int position, string? identifierArgument, IParameterSymbol? parameter, HintKind kind)> buffer,
CancellationToken cancellationToken)
{
if (node is BaseArgumentListSyntax argumentList)
{
AddArguments(semanticModel, syntaxFacts, buffer, argumentList, cancellationToken);
}
else if (node is AttributeArgumentListSyntax attributeArgumentList)
{
AddArguments(semanticModel, syntaxFacts, buffer, attributeArgumentList, cancellationToken);
}
}
private static void AddArguments(
SemanticModel semanticModel,
ISyntaxFactsService syntaxFacts,
ArrayBuilder<(int position, string? identifierArgument, IParameterSymbol? parameter, HintKind kind)> buffer,
AttributeArgumentListSyntax argumentList,
CancellationToken cancellationToken)
{
foreach (var argument in argumentList.Arguments)
{
if (argument.NameEquals != null || argument.NameColon != null)
continue;
var parameter = argument.DetermineParameter(semanticModel, cancellationToken: cancellationToken);
var identifierArgument = GetIdentifierNameFromArgument(argument, syntaxFacts);
buffer.Add((argument.Span.Start, identifierArgument, parameter, GetKind(argument.Expression)));
}
}
private static void AddArguments(
SemanticModel semanticModel,
ISyntaxFactsService syntaxFacts,
ArrayBuilder<(int position, string? identifierArgument, IParameterSymbol? parameter, HintKind kind)> buffer,
BaseArgumentListSyntax argumentList,
CancellationToken cancellationToken)
{
foreach (var argument in argumentList.Arguments)
{
if (argument.NameColon != null)
continue;
var parameter = argument.DetermineParameter(semanticModel, cancellationToken: cancellationToken);
var identifierArgument = GetIdentifierNameFromArgument(argument, syntaxFacts);
buffer.Add((argument.Span.Start, identifierArgument, parameter, GetKind(argument.Expression)));
}
}
private static HintKind GetKind(ExpressionSyntax arg)
=> arg switch
{
LiteralExpressionSyntax or InterpolatedStringExpressionSyntax => HintKind.Literal,
ObjectCreationExpressionSyntax => HintKind.ObjectCreation,
CastExpressionSyntax cast => GetKind(cast.Expression),
PrefixUnaryExpressionSyntax prefix => GetKind(prefix.Operand),
// Treat `expr!` the same as `expr` (i.e. treat `!` as if it's just trivia).
PostfixUnaryExpressionSyntax(SyntaxKind.SuppressNullableWarningExpression) postfix => GetKind(postfix.Operand),
_ => HintKind.Other,
};
protected override bool IsIndexer(SyntaxNode node, IParameterSymbol parameter)
{
return node is BracketedArgumentListSyntax;
}
protected override string GetReplacementText(string parameterName)
{
var keywordKind = SyntaxFacts.GetKeywordKind(parameterName);
var isReservedKeyword = SyntaxFacts.IsReservedKeyword(keywordKind);
return (isReservedKeyword ? "@" : string.Empty) + parameterName + ": ";
}
}
|