Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12753,5 +12753,72 @@ internal class Program
Await state.AssertSelectedCompletionItem("Create")
End Using
End Function

<WpfTheory, CombinatorialData>
<WorkItem("https://github.com/dotnet/roslyn/issues/5399")>
Public Async Function TestFilterOutOwnTypeInBaseList1(showCompletionInArgumentLists As Boolean) As Task
Using state = TestStateFactory.CreateCSharpTestState(
<Document>
class C : $$
{
}
</Document>,
showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersion.CSharp12)

state.SendInvokeCompletionList()
Await state.AssertCompletionItemsDoNotContainAny("C")
End Using
End Function

<WpfTheory, CombinatorialData>
<WorkItem("https://github.com/dotnet/roslyn/issues/5399")>
Public Async Function TestFilterOutOwnTypeInBaseList2(showCompletionInArgumentLists As Boolean) As Task
Using state = TestStateFactory.CreateCSharpTestState(
<Document>
class C : $$
{
interface IGoo
{
}
}
</Document>,
showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersion.CSharp12)

state.SendInvokeCompletionList()
Await state.AssertCompletionItemsContain("C", displayTextSuffix:="")
End Using
End Function

<WpfTheory, CombinatorialData>
<WorkItem("https://github.com/dotnet/roslyn/issues/5399")>
Public Async Function TestFilterOutOwnTypeInBaseList3(showCompletionInArgumentLists As Boolean) As Task
Using state = TestStateFactory.CreateCSharpTestState(
<Document>
class C : IComparable&lt;$$&gt;
{
}
</Document>,
showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersion.CSharp12)

state.SendInvokeCompletionList()
Await state.AssertCompletionItemsContain("C", displayTextSuffix:="")
End Using
End Function

<WpfTheory, CombinatorialData>
<WorkItem("https://github.com/dotnet/roslyn/issues/5399")>
Public Async Function TestFilterOutOwnTypeInBaseList4(showCompletionInArgumentLists As Boolean) As Task
Using state = TestStateFactory.CreateCSharpTestState(
<Document>
class C : BaseType($$);
{
}
</Document>,
showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersion.CSharp12)

state.SendInvokeCompletionList()
Await state.AssertCompletionItemsContain("C", displayTextSuffix:="")
End Using
End Function
End Class
End Namespace
Original file line number Diff line number Diff line change
Expand Up @@ -307,13 +307,26 @@ private ImmutableArray<ISymbol> GetSymbolsForLabelContext()

private ImmutableArray<ISymbol> GetSymbolsForTypeOrNamespaceContext()
{
var symbols = _context.SemanticModel.LookupNamespacesAndTypes(_context.LeftToken.SpanStart);
var semanticModel = _context.SemanticModel;
var symbols = semanticModel.LookupNamespacesAndTypes(_context.LeftToken.SpanStart);

if (_context.TargetToken.IsUsingKeywordInUsingDirective())
return symbols.WhereAsArray(s => s is INamespaceSymbol);
return symbols.WhereAsArray(static s => s is INamespaceSymbol);

if (_context.TargetToken.IsStaticKeywordContextInUsingDirective())
return symbols.WhereAsArray(s => !s.IsDelegateType());
return symbols.WhereAsArray(static s => !s.IsDelegateType());

if (_context.IsBaseListContext)
{
// Filter out the type we're in the inheritance list for if it has no nested types. A type can't show
// up in its own inheritance list (unless being used to
//
// Note: IsBaseListContext requires that we have a type declaration ancestor above us..
var containingType = semanticModel.GetRequiredDeclaredSymbol(
_context.TargetToken.GetRequiredAncestor<TypeDeclarationSyntax>(), _cancellationToken).OriginalDefinition;
if (containingType.GetTypeMembers().IsEmpty)
return symbols.WhereAsArray(static (s, containingType) => !Equals(s.OriginalDefinition, containingType), containingType);
}

return symbols;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ private CSharpSyntaxContext(
bool isAtStartOfPattern,
bool isAttributeNameContext,
bool isAwaitKeywordContext,
bool isBaseListContext,
bool isCatchFilterContext,
bool isConstantExpressionContext,
bool isCrefContext,
Expand Down Expand Up @@ -119,6 +120,7 @@ private CSharpSyntaxContext(
isAtStartOfPattern: isAtStartOfPattern,
isAttributeNameContext: isAttributeNameContext,
isAwaitKeywordContext: isAwaitKeywordContext,
isBaseListContext: isBaseListContext,
isEnumBaseListContext: isEnumBaseListContext,
isEnumTypeMemberAccessContext: isEnumTypeMemberAccessContext,
isGenericConstraintContext: isGenericConstraintContext,
Expand Down Expand Up @@ -194,40 +196,22 @@ private static CSharpSyntaxContext CreateContextWorker(

var targetToken = leftToken.GetPreviousTokenIfTouchingWord(position);

var isPreProcessorKeywordContext = isPreProcessorDirectiveContext
? syntaxTree.IsPreProcessorKeywordContext(position, leftToken)
: false;
var isPreProcessorKeywordContext = isPreProcessorDirectiveContext && syntaxTree.IsPreProcessorKeywordContext(position, leftToken);
var isPreProcessorExpressionContext = isPreProcessorDirectiveContext && targetToken.IsPreProcessorExpressionContext();

var isPreProcessorExpressionContext = isPreProcessorDirectiveContext
? targetToken.IsPreProcessorExpressionContext()
: false;

var isStatementContext = !isPreProcessorDirectiveContext
? targetToken.IsBeginningOfStatementContext()
: false;

var isGlobalStatementContext = !isPreProcessorDirectiveContext
? syntaxTree.IsGlobalStatementContext(position, cancellationToken)
: false;

var isAnyExpressionContext = !isPreProcessorDirectiveContext
? syntaxTree.IsExpressionContext(position, leftToken, attributes: true, cancellationToken: cancellationToken, semanticModel: semanticModel)
: false;

var isNonAttributeExpressionContext = !isPreProcessorDirectiveContext
? syntaxTree.IsExpressionContext(position, leftToken, attributes: false, cancellationToken: cancellationToken, semanticModel: semanticModel)
: false;

var isConstantExpressionContext = !isPreProcessorDirectiveContext
? syntaxTree.IsConstantExpressionContext(position, leftToken)
: false;
var isStatementContext = !isPreProcessorDirectiveContext && targetToken.IsBeginningOfStatementContext();
var isGlobalStatementContext = !isPreProcessorDirectiveContext && syntaxTree.IsGlobalStatementContext(position, cancellationToken);
var isAnyExpressionContext = !isPreProcessorDirectiveContext && syntaxTree.IsExpressionContext(position, leftToken, attributes: true, cancellationToken: cancellationToken, semanticModel: semanticModel);
var isNonAttributeExpressionContext = !isPreProcessorDirectiveContext && syntaxTree.IsExpressionContext(position, leftToken, attributes: false, cancellationToken: cancellationToken, semanticModel: semanticModel);
var isConstantExpressionContext = !isPreProcessorDirectiveContext && syntaxTree.IsConstantExpressionContext(position, leftToken);

var containingTypeDeclaration = syntaxTree.GetContainingTypeDeclaration(position, cancellationToken);
var containingTypeOrEnumDeclaration = syntaxTree.GetContainingTypeOrEnumDeclaration(position, cancellationToken);

var isDestructorTypeContext = targetToken.IsKind(SyntaxKind.TildeToken) &&
targetToken.Parent.IsKind(SyntaxKind.DestructorDeclaration) &&
targetToken.Parent.Parent is (kind: SyntaxKind.ClassDeclaration or SyntaxKind.RecordDeclaration);
var isDestructorTypeContext =
targetToken.IsKind(SyntaxKind.TildeToken) &&
targetToken.Parent.IsKind(SyntaxKind.DestructorDeclaration) &&
targetToken.Parent.Parent is (kind: SyntaxKind.ClassDeclaration or SyntaxKind.RecordDeclaration);

// Typing a dot after a numeric expression (numericExpression.)
// - maybe a start of MemberAccessExpression like numericExpression.Member.
Expand Down Expand Up @@ -255,6 +239,7 @@ private static CSharpSyntaxContext CreateContextWorker(
isAtStartOfPattern: syntaxTree.IsAtStartOfPattern(leftToken, position),
isAttributeNameContext: syntaxTree.IsAttributeNameContext(position, cancellationToken),
isAwaitKeywordContext: ComputeIsAwaitKeywordContext(position, leftToken, targetToken, isGlobalStatementContext, isAnyExpressionContext, isStatementContext),
isBaseListContext: syntaxTree.IsBaseListContext(targetToken),
isCatchFilterContext: syntaxTree.IsCatchFilterContext(position, leftToken),
isConstantExpressionContext: isConstantExpressionContext,
isCrefContext: syntaxTree.IsCrefContext(position, cancellationToken) && !leftToken.IsKind(SyntaxKind.DotToken),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2985,6 +2985,19 @@ public static bool IsCatchFilterContext(this SyntaxTree syntaxTree, int position
return false;
}

public static bool IsBaseListContext(this SyntaxTree syntaxTree, SyntaxToken targetToken)
{
// Options:
// class E : |
// class E : i|
// class E : i, |
// class E : i, j|

return
targetToken is (kind: SyntaxKind.ColonToken or SyntaxKind.CommaToken) &&
targetToken.Parent is BaseListSyntax { Parent: TypeDeclarationSyntax };
}

public static bool IsEnumBaseListContext(this SyntaxTree syntaxTree, SyntaxToken targetToken)
{
// Options:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,123 +9,92 @@

namespace Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery;

internal abstract class SyntaxContext
internal abstract class SyntaxContext(
Document document,
SemanticModel semanticModel,
int position,
SyntaxToken leftToken,
SyntaxToken targetToken,
bool isAnyExpressionContext,
bool isAtEndOfPattern,
bool isAtStartOfPattern,
bool isAttributeNameContext,
bool isAwaitKeywordContext,
bool isBaseListContext,
bool isEnumBaseListContext,
bool isEnumTypeMemberAccessContext,
bool isGenericConstraintContext,
bool isGlobalStatementContext,
bool isInImportsDirective,
bool isInQuery,
bool isTaskLikeTypeContext,
bool isNameOfContext,
bool isNamespaceContext,
bool isNamespaceDeclarationNameContext,
bool isObjectCreationTypeContext,
bool isOnArgumentListBracketOrComma,
bool isPossibleTupleContext,
bool isPreProcessorDirectiveContext,
bool isPreProcessorExpressionContext,
bool isRightAfterUsingOrImportDirective,
bool isRightOfNameSeparator,
bool isRightSideOfNumericType,
bool isStatementContext,
bool isTypeContext,
bool isWithinAsyncMethod,
CancellationToken cancellationToken)
{
public Document Document { get; }
public SemanticModel SemanticModel { get; }
public SyntaxTree SyntaxTree { get; }
public int Position { get; }
public Document Document { get; } = document;
public SemanticModel SemanticModel { get; } = semanticModel;
public SyntaxTree SyntaxTree { get; } = semanticModel.SyntaxTree;
public int Position { get; } = position;

/// <summary>
/// The token to the left of <see cref="Position"/>. This token may be touching the position.
/// </summary>
public SyntaxToken LeftToken { get; }
public SyntaxToken LeftToken { get; } = leftToken;

/// <summary>
/// The first token to the left of <see cref="Position"/> that we're not touching. Equal to <see cref="LeftToken"/>
/// if we aren't touching <see cref="LeftToken" />.
/// </summary>
public SyntaxToken TargetToken { get; }
public SyntaxToken TargetToken { get; } = targetToken;

public bool IsAnyExpressionContext { get; }
public bool IsAtEndOfPattern { get; }
public bool IsAtStartOfPattern { get; }
public bool IsAttributeNameContext { get; }
public bool IsAwaitKeywordContext { get; }
public bool IsEnumBaseListContext { get; }
public bool IsEnumTypeMemberAccessContext { get; }
public bool IsGenericConstraintContext { get; }
public bool IsGlobalStatementContext { get; }
public bool IsInImportsDirective { get; }
public bool IsInQuery { get; }
public bool IsTaskLikeTypeContext { get; }
public bool IsNameOfContext { get; }
public bool IsNamespaceContext { get; }
public bool IsNamespaceDeclarationNameContext { get; }
public bool IsObjectCreationTypeContext { get; }
public bool IsOnArgumentListBracketOrComma { get; }
public bool IsPossibleTupleContext { get; }
public bool IsPreProcessorDirectiveContext { get; }
public bool IsPreProcessorExpressionContext { get; }
public bool IsRightAfterUsingOrImportDirective { get; }
public bool IsRightOfNameSeparator { get; }
public bool IsRightSideOfNumericType { get; }
public bool IsStatementContext { get; }
public bool IsTypeContext { get; }
public bool IsWithinAsyncMethod { get; }
public bool IsAnyExpressionContext { get; } = isAnyExpressionContext;
public bool IsAtEndOfPattern { get; } = isAtEndOfPattern;
public bool IsAtStartOfPattern { get; } = isAtStartOfPattern;
public bool IsAttributeNameContext { get; } = isAttributeNameContext;
public bool IsAwaitKeywordContext { get; } = isAwaitKeywordContext;

public ImmutableArray<ITypeSymbol> InferredTypes { get; }

protected SyntaxContext(
Document document,
SemanticModel semanticModel,
int position,
SyntaxToken leftToken,
SyntaxToken targetToken,
bool isAnyExpressionContext,
bool isAtEndOfPattern,
bool isAtStartOfPattern,
bool isAttributeNameContext,
bool isAwaitKeywordContext,
bool isEnumBaseListContext,
bool isEnumTypeMemberAccessContext,
bool isGenericConstraintContext,
bool isGlobalStatementContext,
bool isInImportsDirective,
bool isInQuery,
bool isTaskLikeTypeContext,
bool isNameOfContext,
bool isNamespaceContext,
bool isNamespaceDeclarationNameContext,
bool isObjectCreationTypeContext,
bool isOnArgumentListBracketOrComma,
bool isPossibleTupleContext,
bool isPreProcessorDirectiveContext,
bool isPreProcessorExpressionContext,
bool isRightAfterUsingOrImportDirective,
bool isRightOfNameSeparator,
bool isRightSideOfNumericType,
bool isStatementContext,
bool isTypeContext,
bool isWithinAsyncMethod,
CancellationToken cancellationToken)
{
this.Document = document;
this.SemanticModel = semanticModel;
this.SyntaxTree = semanticModel.SyntaxTree;
this.Position = position;
this.LeftToken = leftToken;
this.TargetToken = targetToken;

this.IsAnyExpressionContext = isAnyExpressionContext;
this.IsAtEndOfPattern = isAtEndOfPattern;
this.IsAtStartOfPattern = isAtStartOfPattern;
this.IsAttributeNameContext = isAttributeNameContext;
this.IsAwaitKeywordContext = isAwaitKeywordContext;
this.IsEnumBaseListContext = isEnumBaseListContext;
this.IsEnumTypeMemberAccessContext = isEnumTypeMemberAccessContext;
this.IsGenericConstraintContext = isGenericConstraintContext;
this.IsGlobalStatementContext = isGlobalStatementContext;
this.IsInImportsDirective = isInImportsDirective;
this.IsInQuery = isInQuery;
this.IsTaskLikeTypeContext = isTaskLikeTypeContext;
this.IsNameOfContext = isNameOfContext;
this.IsNamespaceContext = isNamespaceContext;
this.IsNamespaceDeclarationNameContext = isNamespaceDeclarationNameContext;
this.IsObjectCreationTypeContext = isObjectCreationTypeContext;
this.IsOnArgumentListBracketOrComma = isOnArgumentListBracketOrComma;
this.IsPossibleTupleContext = isPossibleTupleContext;
this.IsPreProcessorDirectiveContext = isPreProcessorDirectiveContext;
this.IsPreProcessorExpressionContext = isPreProcessorExpressionContext;
this.IsRightAfterUsingOrImportDirective = isRightAfterUsingOrImportDirective;
this.IsRightOfNameSeparator = isRightOfNameSeparator;
this.IsRightSideOfNumericType = isRightSideOfNumericType;
this.IsStatementContext = isStatementContext;
this.IsTypeContext = isTypeContext;
this.IsWithinAsyncMethod = isWithinAsyncMethod;
/// <summary>
/// Is in the base list of a type declaration. Note, this only counts when at the top level of the base list, not
/// *within* any type already in the base list. For example <c>class C : $$</c> is in the base list. But <c>class
/// C : A&lt;$$&gt;</c> is not.
/// </summary>
public bool IsBaseListContext { get; } = isBaseListContext;
public bool IsEnumBaseListContext { get; } = isEnumBaseListContext;
public bool IsEnumTypeMemberAccessContext { get; } = isEnumTypeMemberAccessContext;
public bool IsGenericConstraintContext { get; } = isGenericConstraintContext;
public bool IsGlobalStatementContext { get; } = isGlobalStatementContext;
public bool IsInImportsDirective { get; } = isInImportsDirective;
public bool IsInQuery { get; } = isInQuery;
public bool IsTaskLikeTypeContext { get; } = isTaskLikeTypeContext;
public bool IsNameOfContext { get; } = isNameOfContext;
public bool IsNamespaceContext { get; } = isNamespaceContext;
public bool IsNamespaceDeclarationNameContext { get; } = isNamespaceDeclarationNameContext;
public bool IsObjectCreationTypeContext { get; } = isObjectCreationTypeContext;
public bool IsOnArgumentListBracketOrComma { get; } = isOnArgumentListBracketOrComma;
public bool IsPossibleTupleContext { get; } = isPossibleTupleContext;
public bool IsPreProcessorDirectiveContext { get; } = isPreProcessorDirectiveContext;
public bool IsPreProcessorExpressionContext { get; } = isPreProcessorExpressionContext;
public bool IsRightAfterUsingOrImportDirective { get; } = isRightAfterUsingOrImportDirective;
public bool IsRightOfNameSeparator { get; } = isRightOfNameSeparator;
public bool IsRightSideOfNumericType { get; } = isRightSideOfNumericType;
public bool IsStatementContext { get; } = isStatementContext;
public bool IsTypeContext { get; } = isTypeContext;
public bool IsWithinAsyncMethod { get; } = isWithinAsyncMethod;

this.InferredTypes = document.GetRequiredLanguageService<ITypeInferenceService>().InferTypes(semanticModel, position, cancellationToken);
}
public ImmutableArray<ITypeSymbol> InferredTypes { get; } = document.GetRequiredLanguageService<ITypeInferenceService>().InferTypes(semanticModel, position, cancellationToken);

public TService? GetLanguageService<TService>() where TService : class, ILanguageService
=> Document.GetLanguageService<TService>();
Expand Down
Loading
Loading