提交 d53c1f47 编写于 作者: C Cyrus Najmabadi

Suppress inline hints in certain clear cases.

上级 e38b7cde
...@@ -10,10 +10,16 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.InlineHints ...@@ -10,10 +10,16 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.InlineHints
<[UseExportProvider]> <[UseExportProvider]>
Public MustInherit Class AbstractInlineParameterNameHintsTests Public MustInherit Class AbstractInlineParameterNameHintsTests
Protected Async Function VerifyParamHints(test As XElement, Optional optionIsEnabled As Boolean = True) As Tasks.Task Protected Async Function VerifyParamHints(test As XElement, Optional optionIsEnabled As Boolean = True) As Task
Using workspace = TestWorkspace.Create(test) Using workspace = TestWorkspace.Create(test)
WpfTestRunner.RequireWpfFact($"{NameOf(AbstractInlineParameterNameHintsTests)}.{NameOf(Me.VerifyParamHints)} creates asynchronous taggers") WpfTestRunner.RequireWpfFact($"{NameOf(AbstractInlineParameterNameHintsTests)}.{NameOf(Me.VerifyParamHints)} creates asynchronous taggers")
workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(
workspace.Options.WithChangedOption(
InlineHintsOptions.EnabledForParameters,
workspace.CurrentSolution.Projects.Single().Language,
optionIsEnabled)))
Dim hostDocument = workspace.Documents.Single() Dim hostDocument = workspace.Documents.Single()
Dim snapshot = hostDocument.GetTextBuffer().CurrentSnapshot Dim snapshot = hostDocument.GetTextBuffer().CurrentSnapshot
Dim document = workspace.CurrentSolution.GetDocument(hostDocument.Id) Dim document = workspace.CurrentSolution.GetDocument(hostDocument.Id)
......
...@@ -398,5 +398,155 @@ class Derived : Base ...@@ -398,5 +398,155 @@ class Derived : Base
Await VerifyParamHints(input) Await VerifyParamHints(input)
End Function End Function
<WorkItem(47597, "https://github.com/dotnet/roslyn/issues/47597")>
<WpfFact, Trait(Traits.Feature, Traits.Features.InlineParameterNameHints)>
Public Async Function TestNotOnEnableDisableBoolean1() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class A
{
void EnableLogging(bool value)
{
}
void Main()
{
EnableLogging(true);
}
}
</Document>
</Project>
</Workspace>
Await VerifyParamHints(input)
End Function
<WorkItem(47597, "https://github.com/dotnet/roslyn/issues/47597")>
<WpfFact, Trait(Traits.Feature, Traits.Features.InlineParameterNameHints)>
Public Async Function TestNotOnEnableDisableBoolean2() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class A
{
void DisableLogging(bool value)
{
}
void Main()
{
DisableLogging(true);
}
}
</Document>
</Project>
</Workspace>
Await VerifyParamHints(input)
End Function
<WorkItem(47597, "https://github.com/dotnet/roslyn/issues/47597")>
<WpfFact, Trait(Traits.Feature, Traits.Features.InlineParameterNameHints)>
Public Async Function TestOnEnableDisableNonBoolean1() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class A
{
void EnableLogging(string value)
{
}
void Main()
{
EnableLogging({|value:"IO"|});
}
}
</Document>
</Project>
</Workspace>
Await VerifyParamHints(input)
End Function
<WorkItem(47597, "https://github.com/dotnet/roslyn/issues/47597")>
<WpfFact, Trait(Traits.Feature, Traits.Features.InlineParameterNameHints)>
Public Async Function TestOnEnableDisableNonBoolean2() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class A
{
void DisableLogging(string value)
{
}
void Main()
{
DisableLogging({|value:"IO"|});
}
}
</Document>
</Project>
</Workspace>
Await VerifyParamHints(input)
End Function
<WorkItem(47597, "https://github.com/dotnet/roslyn/issues/47597")>
<WpfFact, Trait(Traits.Feature, Traits.Features.InlineParameterNameHints)>
Public Async Function TestOnSetMethodWithClearContext() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class A
{
void SetClassification(string classification)
{
}
void Main()
{
SetClassification("IO");
}
}
</Document>
</Project>
</Workspace>
Await VerifyParamHints(input)
End Function
<WorkItem(47597, "https://github.com/dotnet/roslyn/issues/47597")>
<WpfFact, Trait(Traits.Feature, Traits.Features.InlineParameterNameHints)>
Public Async Function TestOnSetMethodWithUnclearContext() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class A
{
void SetClassification(string values)
{
}
void Main()
{
SetClassification({|values:"IO"|});
}
}
</Document>
</Project>
</Workspace>
Await VerifyParamHints(input)
End Function
End Class End Class
End Namespace End Namespace
...@@ -3,14 +3,12 @@ ...@@ -3,14 +3,12 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using System;
using System.Collections.Generic;
using System.Composition; using System.Composition;
using System.Threading; using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.InlineHints; using Microsoft.CodeAnalysis.InlineHints;
using Microsoft.CodeAnalysis.PooledObjects;
namespace Microsoft.CodeAnalysis.CSharp.InlineHints namespace Microsoft.CodeAnalysis.CSharp.InlineHints
{ {
...@@ -28,29 +26,61 @@ public CSharpInlineParameterNameHintsService() ...@@ -28,29 +26,61 @@ public CSharpInlineParameterNameHintsService()
} }
protected override void AddAllParameterNameHintLocations( protected override void AddAllParameterNameHintLocations(
SemanticModel semanticModel, SyntaxNode node, SemanticModel semanticModel,
Action<InlineParameterHint> addHint, CancellationToken cancellationToken) SyntaxNode node,
Action<InlineParameterHint> addHint,
bool hideForParametersThatDifferBySuffix,
bool hideForParametersThatMatchMethodIntent,
CancellationToken cancellationToken)
{ {
if (node is ArgumentSyntax argument) if (node is ArgumentSyntax argument)
{ {
if (argument.NameColon == null) if (argument.NameColon != null)
{ return;
var param = argument.DetermineParameter(semanticModel, cancellationToken: cancellationToken);
if (!string.IsNullOrEmpty(param?.Name)) var parameter = argument.DetermineParameter(semanticModel, cancellationToken: cancellationToken);
addHint(new InlineParameterHint(param.GetSymbolKey(cancellationToken), param.Name, argument.SpanStart, GetKind(argument.Expression))); if (string.IsNullOrEmpty(parameter?.Name))
} return;
if (hideForParametersThatMatchMethodIntent && MatchesMethodIntent(argument, parameter))
return;
addHint(new InlineParameterHint(parameter.GetSymbolKey(cancellationToken), parameter.Name, argument.Span.Start, GetKind(argument.Expression)));
} }
else if (node is AttributeArgumentSyntax attribute) else if (node is AttributeArgumentSyntax attribute)
{ {
if (attribute.NameEquals == null && attribute.NameColon == null) if (attribute.NameEquals != null || attribute.NameColon != null)
{ return;
var param = attribute.DetermineParameter(semanticModel, cancellationToken: cancellationToken);
if (!string.IsNullOrEmpty(param?.Name)) var parameter = attribute.DetermineParameter(semanticModel, cancellationToken: cancellationToken);
addHint(new InlineParameterHint(param.GetSymbolKey(cancellationToken), param.Name, attribute.SpanStart, GetKind(attribute.Expression))); if (string.IsNullOrEmpty(parameter?.Name))
} return;
addHint(new InlineParameterHint(parameter.GetSymbolKey(cancellationToken), parameter.Name, attribute.SpanStart, GetKind(attribute.Expression)));
} }
} }
private static bool MatchesMethodIntent(ArgumentSyntax argument, IParameterSymbol parameter)
{
// Methods like `SetColor(color: "y")` `FromResult(result: "x")` `Enable/DisablePolling(bool)` don't need
// parameter names to improve clarity. The parameter is clear from the context of the method name.
if (argument.Parent is not ArgumentListSyntax argumentList)
return false;
if (argumentList.Arguments[0] != argument)
return false;
if (argumentList.Parent is not InvocationExpressionSyntax invocationExpression)
return false;
var invokedExpression = invocationExpression.Expression;
var rightMostName = invokedExpression.GetRightmostName();
if (rightMostName == null)
return false;
return MatchesMethodIntent(rightMostName.Identifier.ValueText, parameter);
}
private static InlineParameterHintKind GetKind(ExpressionSyntax arg) private static InlineParameterHintKind GetKind(ExpressionSyntax arg)
=> arg switch => arg switch
{ {
......
...@@ -2,11 +2,9 @@ ...@@ -2,11 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
#nullable disable
using System; using System;
using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.PooledObjects;
...@@ -19,7 +17,9 @@ namespace Microsoft.CodeAnalysis.InlineHints ...@@ -19,7 +17,9 @@ namespace Microsoft.CodeAnalysis.InlineHints
internal abstract class AbstractInlineParameterNameHintsService : IInlineParameterNameHintsService internal abstract class AbstractInlineParameterNameHintsService : IInlineParameterNameHintsService
{ {
protected abstract void AddAllParameterNameHintLocations( protected abstract void AddAllParameterNameHintLocations(
SemanticModel semanticModel, SyntaxNode node, Action<InlineParameterHint> addHint, CancellationToken cancellationToken); SemanticModel semanticModel, SyntaxNode node, Action<InlineParameterHint> addHint,
bool hideForParametersThatDifferBySuffix, bool hideForParametersThatMatchMethodIntent,
CancellationToken cancellationToken);
public async Task<ImmutableArray<InlineParameterHint>> GetInlineParameterNameHintsAsync(Document document, TextSpan textSpan, CancellationToken cancellationToken) public async Task<ImmutableArray<InlineParameterHint>> GetInlineParameterNameHintsAsync(Document document, TextSpan textSpan, CancellationToken cancellationToken)
{ {
...@@ -35,16 +35,24 @@ public async Task<ImmutableArray<InlineParameterHint>> GetInlineParameterNameHin ...@@ -35,16 +35,24 @@ public async Task<ImmutableArray<InlineParameterHint>> GetInlineParameterNameHin
if (!literalParameters && !objectCreationParameters && !otherParameters) if (!literalParameters && !objectCreationParameters && !otherParameters)
return ImmutableArray<InlineParameterHint>.Empty; return ImmutableArray<InlineParameterHint>.Empty;
var hideForParametersThatDifferBySuffix = options.GetOption(InlineHintsOptions.HideForParametersThatDifferBySuffix);
var hideForParametersThatMatchMethodIntent = options.GetOption(InlineHintsOptions.HideForParametersThatMatchMethodIntent);
var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
using var _ = ArrayBuilder<InlineParameterHint>.GetInstance(out var result); using var _1 = ArrayBuilder<InlineParameterHint>.GetInstance(out var result);
Action<InlineParameterHint> addHint = AddHint; Action<InlineParameterHint> addHint = AddHint;
foreach (var node in root.DescendantNodes(textSpan)) foreach (var node in root.DescendantNodes(textSpan))
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
AddAllParameterNameHintLocations(semanticModel, node, addHint, cancellationToken); AddAllParameterNameHintLocations(
semanticModel, node, addHint,
hideForParametersThatDifferBySuffix,
hideForParametersThatMatchMethodIntent,
cancellationToken);
} }
return result.ToImmutable(); return result.ToImmutable();
...@@ -64,5 +72,47 @@ private static bool HintMatches(InlineParameterHint hint, bool literalParameters ...@@ -64,5 +72,47 @@ private static bool HintMatches(InlineParameterHint hint, bool literalParameters
InlineParameterHintKind.Other => otherParameters, InlineParameterHintKind.Other => otherParameters,
_ => throw ExceptionUtilities.UnexpectedValue(hint.Kind), _ => throw ExceptionUtilities.UnexpectedValue(hint.Kind),
}; };
protected static bool MatchesMethodIntent(string methodName, IParameterSymbol parameter)
{
// Check for something like `EnableLogging(true)`
if (TryGetIntent("Enable", methodName, out _) ||
TryGetIntent("Disable", methodName, out _))
{
return parameter.Type.SpecialType == SpecialType.System_Boolean;
}
// More names can be added here if we find other patterns like this.
if (TryGetIntent("Set", methodName, out var methodIntent) ||
TryGetIntent("From", methodName, out methodIntent))
{
return IntentNameMatchesParameterName(methodIntent.Value, parameter.Name);
}
return false;
static bool TryGetIntent(string prefix, string nameValue, [NotNullWhen(true)] out ReadOnlyMemory<char>? result)
{
if (nameValue.Length > prefix.Length &&
nameValue.StartsWith(prefix) &&
char.IsUpper(nameValue[prefix.Length]))
{
result = nameValue.AsMemory().Slice(prefix.Length);
return true;
}
result = null;
return false;
}
static bool IntentNameMatchesParameterName(ReadOnlyMemory<char> intent, string parameterName)
{
// Method's name will be something like 'FromResult', so 'intent' will be 'Result' and parameterName
// will be 'result'. So we check if the first letters differ on case and the rest of the method
// matches.
return char.ToLower(intent.Span[0]) == parameterName[0] &&
intent.Span.Slice(1).Equals(parameterName.AsSpan().Slice(1), StringComparison.Ordinal);
}
}
} }
} }
...@@ -23,14 +23,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.InlineParameterNameHints ...@@ -23,14 +23,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.InlineParameterNameHints
semanticModel As SemanticModel, semanticModel As SemanticModel,
node As SyntaxNode, node As SyntaxNode,
addHint As Action(Of InlineParameterHint), addHint As Action(Of InlineParameterHint),
cancellationToken As CancellationToken) hideForParametersThatDifferBySuffix As Boolean,
hideForParametersThatMatchMethodIntent As Boolean,
CancellationToken As CancellationToken)
Dim simpleArgument = TryCast(node, SimpleArgumentSyntax) Dim simpleArgument = TryCast(node, SimpleArgumentSyntax)
If simpleArgument?.Expression IsNot Nothing Then If simpleArgument?.Expression IsNot Nothing Then
If Not simpleArgument.IsNamed AndAlso simpleArgument.NameColonEquals Is Nothing Then If Not simpleArgument.IsNamed AndAlso simpleArgument.NameColonEquals Is Nothing Then
Dim param = simpleArgument.DetermineParameter(semanticModel, allowParamArray:=False, cancellationToken) Dim param = simpleArgument.DetermineParameter(semanticModel, allowParamArray:=False, CancellationToken)
If Not String.IsNullOrEmpty(param?.Name) Then If Not String.IsNullOrEmpty(param?.Name) Then
addHint(New InlineParameterHint(param.GetSymbolKey(cancellationToken), param.Name, simpleArgument.Span.Start, GetKind(simpleArgument.Expression))) addHint(New InlineParameterHint(param.GetSymbolKey(CancellationToken), param.Name, simpleArgument.Span.Start, GetKind(simpleArgument.Expression)))
End If End If
End If End If
End If End If
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册