提交 bc9be6c0 编写于 作者: S Sam Harwell 提交者: GitHub

Merge pull request #19258 from sharwell/candidate-test

Verify member is in scope before trying to replace it
......@@ -764,7 +764,8 @@ namespace N1
Await TestAsync(input, expected)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.Simplification)>
<Fact(Skip:="https://github.com/dotnet/roslyn/issues/19368")>
<Trait(Traits.Feature, Traits.Features.Simplification)>
Public Async Function TestSimplifyNot_Delegate1() As Task
Dim input =
<Workspace>
......
......@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CodeStyle;
......@@ -799,6 +800,27 @@ private static bool CanReplace(ISymbol symbol)
}
}
// Try to eliminate cases without actually calling CanReplaceWithReducedName. For expressions of the form
// 'this.Name' or 'base.Name', no additional check here is required.
if (!memberAccess.Expression.IsKind(SyntaxKind.ThisExpression, SyntaxKind.BaseExpression))
{
var actualSymbol = semanticModel.GetSymbolInfo(memberAccess.Name, cancellationToken);
if (!TryGetReplacementCandidates(
semanticModel,
memberAccess,
actualSymbol,
out var speculativeSymbols,
out var speculativeNamespacesAndTypes))
{
return false;
}
if (!IsReplacementCandidate(actualSymbol, speculativeSymbols, speculativeNamespacesAndTypes))
{
return false;
}
}
replacementNode = memberAccess.Name
.WithLeadingTrivia(memberAccess.GetLeadingTriviaForSimplifiedMemberAccess())
.WithTrailingTrivia(memberAccess.GetTrailingTrivia());
......@@ -812,6 +834,97 @@ private static bool CanReplace(ISymbol symbol)
return memberAccess.CanReplaceWithReducedName(replacementNode, semanticModel, cancellationToken);
}
private static bool TryGetReplacementCandidates(
SemanticModel semanticModel,
MemberAccessExpressionSyntax memberAccess,
SymbolInfo actualSymbol,
out ImmutableArray<ISymbol> speculativeSymbols,
out ImmutableArray<ISymbol> speculativeNamespacesAndTypes)
{
bool containsNamespaceOrTypeSymbol;
bool containsOtherSymbol;
if (!(actualSymbol.Symbol is null))
{
containsNamespaceOrTypeSymbol = actualSymbol.Symbol is INamespaceOrTypeSymbol;
containsOtherSymbol = !containsNamespaceOrTypeSymbol;
}
else if (!actualSymbol.CandidateSymbols.IsDefaultOrEmpty)
{
containsNamespaceOrTypeSymbol = actualSymbol.CandidateSymbols.Any(symbol => symbol is INamespaceOrTypeSymbol);
containsOtherSymbol = actualSymbol.CandidateSymbols.Any(symbol => !(symbol is INamespaceOrTypeSymbol));
}
else
{
speculativeSymbols = ImmutableArray<ISymbol>.Empty;
speculativeNamespacesAndTypes = ImmutableArray<ISymbol>.Empty;
return false;
}
speculativeSymbols = containsOtherSymbol
? semanticModel.LookupSymbols(memberAccess.SpanStart, name: memberAccess.Name.Identifier.ValueText)
: ImmutableArray<ISymbol>.Empty;
speculativeNamespacesAndTypes = containsNamespaceOrTypeSymbol
? semanticModel.LookupNamespacesAndTypes(memberAccess.SpanStart, name: memberAccess.Name.Identifier.ValueText)
: ImmutableArray<ISymbol>.Empty;
return true;
}
/// <summary>
/// Determines if <paramref name="speculativeSymbols"/> and <paramref name="speculativeNamespacesAndTypes"/>
/// together contain a superset of the symbols in <paramref name="actualSymbol"/>.
/// </summary>
private static bool IsReplacementCandidate(SymbolInfo actualSymbol, ImmutableArray<ISymbol> speculativeSymbols, ImmutableArray<ISymbol> speculativeNamespacesAndTypes)
{
if (speculativeSymbols.IsEmpty && speculativeNamespacesAndTypes.IsEmpty)
{
return false;
}
if (!(actualSymbol.Symbol is null))
{
return speculativeSymbols.Contains(actualSymbol.Symbol, CandidateSymbolEqualityComparer.Instance)
|| speculativeNamespacesAndTypes.Contains(actualSymbol.Symbol, CandidateSymbolEqualityComparer.Instance);
}
foreach (var symbol in actualSymbol.CandidateSymbols)
{
if (!speculativeSymbols.Contains(symbol, CandidateSymbolEqualityComparer.Instance)
&& !speculativeNamespacesAndTypes.Contains(symbol, CandidateSymbolEqualityComparer.Instance))
{
return false;
}
}
return true;
}
/// <summary>
/// Compares symbols by their original definition.
/// </summary>
private sealed class CandidateSymbolEqualityComparer : IEqualityComparer<ISymbol>
{
public static CandidateSymbolEqualityComparer Instance { get; } = new CandidateSymbolEqualityComparer();
private CandidateSymbolEqualityComparer()
{
}
public bool Equals(ISymbol x, ISymbol y)
{
if (x is null || y is null)
{
return x == y;
}
return x.OriginalDefinition.Equals(y.OriginalDefinition);
}
public int GetHashCode(ISymbol obj)
{
return obj?.OriginalDefinition.GetHashCode() ?? 0;
}
}
private static SyntaxTriviaList GetLeadingTriviaForSimplifiedMemberAccess(this MemberAccessExpressionSyntax memberAccess)
{
// We want to include any user-typed trivia that may be present between the 'Expression', 'OperatorToken' and 'Identifier' of the MemberAccessExpression.
......
......@@ -8,6 +8,9 @@ namespace Roslyn.Utilities
{
internal static class ImmutableArrayExtensions
{
internal static bool Contains<T>(this ImmutableArray<T> items, T item, IEqualityComparer<T> equalityComparer)
=> items.IndexOf(item, 0, equalityComparer) >= 0;
internal static ImmutableArray<T> ToImmutableArrayOrEmpty<T>(this T[] items)
{
if (items == null)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册