Skip to content

Commit

Permalink
Merge pull request #66959 from DoctorKrolic/completions-in-range-expr…
Browse files Browse the repository at this point in the history
…ession

Fix completions in range expressions
  • Loading branch information
CyrusNajmabadi authored Feb 24, 2023
2 parents 527c792 + 168de19 commit 3aa2c51
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12002,8 +12002,7 @@ ref struct MyRefStruct { }
await VerifyItemExistsAsync(MakeMarkup(source), "MyRefStruct");
}

[Fact]
[WorkItem(65020, "https://github.com/dotnet/roslyn/issues/65020")]
[Fact, WorkItem(65020, "https://github.com/dotnet/roslyn/issues/65020")]
public async Task DoNotProvideMemberOnSystemVoid()
{
var source = @"
Expand Down Expand Up @@ -12032,6 +12031,48 @@ public async Task NoSymbolCompletionsInEnumBaseList()
await VerifyNoItemsExistAsync(source);
}

[Fact, WorkItem(66903, "https://github.com/dotnet/roslyn/issues/66903")]
public async Task InRangeExpression()
{
var source = """
class C
{
const int Test = 1;
void M(string s)
{
var endIndex = 1;
var substr = s[1..$$];
}
}
""";

await VerifyItemExistsAsync(source, "endIndex");
await VerifyItemExistsAsync(source, "Test");
await VerifyItemExistsAsync(source, "C");
}

[Fact, WorkItem(66903, "https://github.com/dotnet/roslyn/issues/66903")]
public async Task InRangeExpression_WhitespaceAfterDotDotToken()
{
var source = """
class C
{
const int Test = 1;
void M(string s)
{
var endIndex = 1;
var substr = s[1.. $$];
}
}
""";

await VerifyItemExistsAsync(source, "endIndex");
await VerifyItemExistsAsync(source, "Test");
await VerifyItemExistsAsync(source, "C");
}

private static string MakeMarkup(string source, string languageVersion = "Preview")
{
return $$"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9665,6 +9665,9 @@ public class C
showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersion.Preview)

state.SendTypeChars("..")
Await state.AssertNoCompletionSession()

state.SendInvokeCompletionList()
Await state.AssertSelectedCompletionItem(displayText:="async", isHardSelected:=False)

state.SendTypeChars("{ ")
Expand Down Expand Up @@ -9730,6 +9733,9 @@ public class C
showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersion.Preview)

state.SendTypeChars("..")
Await state.AssertNoCompletionSession()

state.SendInvokeCompletionList()
Await state.AssertSelectedCompletionItem(displayText:="async", isHardSelected:=False)

state.SendTypeChars("nu")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ public static bool TreatAsDot(SyntaxToken token, int characterPosition)
internal static bool IsTriggerCharacter(SourceText text, int characterPosition, in CompletionOptions options)
{
var ch = text[characterPosition];
if (ch == '.')

// Trigger off of a normal `.`, but not off of `..`
if (ch == '.' && !(characterPosition >= 1 && text[characterPosition - 1] == '.'))
{
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private static bool IsAfterAsyncKeywordInExpressionContext(CSharpSyntaxContext c
context.TargetToken,
attributes: false,
cancellationToken: cancellationToken,
semanticModelOpt: context.SemanticModel);
semanticModel: context.SemanticModel);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ SyntaxKind.GenericName or
var syntaxTree = semanticModel.SyntaxTree;
var start = nameOrMemberAccessExpression.SpanStart;
var tokenOnLeftOfStart = syntaxTree.FindTokenOnLeftOfPosition(start, cancellationToken);
var isExpressionContext = syntaxTree.IsExpressionContext(start, tokenOnLeftOfStart, attributes: true, cancellationToken: cancellationToken, semanticModelOpt: semanticModel);
var isExpressionContext = syntaxTree.IsExpressionContext(start, tokenOnLeftOfStart, attributes: true, cancellationToken: cancellationToken, semanticModel: semanticModel);
var isStatementContext = syntaxTree.IsStatementContext(start, tokenOnLeftOfStart, cancellationToken);
var isExpressionOrStatementContext = isExpressionContext || isStatementContext;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ internal sealed class CSharpSyntaxContext : SyntaxContext
public readonly bool IsCatchFilterContext;
public readonly bool IsConstantExpressionContext;
public readonly bool IsCrefContext;
public readonly bool IsDeclarationExpressionContext;
public readonly bool IsDefiniteCastTypeContext;
public readonly bool IsDelegateReturnTypeContext;
public readonly bool IsDestructorTypeContext;
Expand Down Expand Up @@ -63,7 +62,6 @@ private CSharpSyntaxContext(
bool isCatchFilterContext,
bool isConstantExpressionContext,
bool isCrefContext,
bool isDeclarationExpressionContext,
bool isDefiniteCastTypeContext,
bool isDelegateReturnTypeContext,
bool isDestructorTypeContext,
Expand Down Expand Up @@ -145,7 +143,6 @@ private CSharpSyntaxContext(
this.IsCatchFilterContext = isCatchFilterContext;
this.IsConstantExpressionContext = isConstantExpressionContext;
this.IsCrefContext = isCrefContext;
this.IsDeclarationExpressionContext = isDeclarationExpressionContext;
this.IsDefiniteCastTypeContext = isDefiniteCastTypeContext;
this.IsDelegateReturnTypeContext = isDelegateReturnTypeContext;
this.IsDestructorTypeContext = isDestructorTypeContext;
Expand Down Expand Up @@ -209,11 +206,11 @@ private static CSharpSyntaxContext CreateContextWorker(Document document, Semant
: false;

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

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

var isConstantExpressionContext = !isPreProcessorDirectiveContext
Expand Down Expand Up @@ -256,7 +253,6 @@ private static CSharpSyntaxContext CreateContextWorker(Document document, Semant
isCatchFilterContext: syntaxTree.IsCatchFilterContext(position, leftToken),
isConstantExpressionContext: isConstantExpressionContext,
isCrefContext: syntaxTree.IsCrefContext(position, cancellationToken) && !leftToken.IsKind(SyntaxKind.DotToken),
isDeclarationExpressionContext: syntaxTree.IsDeclarationExpressionContext(position, leftToken),
isDefiniteCastTypeContext: syntaxTree.IsDefiniteCastTypeContext(position, leftToken),
isDelegateReturnTypeContext: syntaxTree.IsDelegateReturnTypeContext(position, leftToken),
isDestructorTypeContext: isDestructorTypeContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// 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.Host.Mef;
Expand All @@ -13,7 +14,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery
internal class CSharpSyntaxContextService : ISyntaxContextService
{
[ImportingConstructor]
[System.Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public CSharpSyntaxContextService()
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,7 @@ public static bool IsTypeContext(
syntaxTree.IsCatchVariableDeclarationContext(position, cancellationToken) ||
syntaxTree.IsDefiniteCastTypeContext(position, tokenOnLeftOfPosition) ||
syntaxTree.IsDelegateReturnTypeContext(position, tokenOnLeftOfPosition) ||
syntaxTree.IsExpressionContext(position, tokenOnLeftOfPosition, attributes: true, cancellationToken: cancellationToken, semanticModelOpt: semanticModel) ||
syntaxTree.IsExpressionContext(position, tokenOnLeftOfPosition, attributes: true, cancellationToken: cancellationToken, semanticModel: semanticModel) ||
syntaxTree.IsPrimaryFunctionExpressionContext(position, tokenOnLeftOfPosition) ||
syntaxTree.IsGenericTypeArgumentContext(position, tokenOnLeftOfPosition, cancellationToken, semanticModel) ||
syntaxTree.IsFunctionPointerTypeArgumentContext(position, tokenOnLeftOfPosition, cancellationToken) ||
Expand Down Expand Up @@ -1696,7 +1696,7 @@ public static bool IsValidContextForFromClause(
CancellationToken cancellationToken,
SemanticModel? semanticModelOpt = null)
{
if (syntaxTree.IsExpressionContext(position, tokenOnLeftOfPosition, attributes: false, cancellationToken: cancellationToken, semanticModelOpt: semanticModelOpt) &&
if (syntaxTree.IsExpressionContext(position, tokenOnLeftOfPosition, attributes: false, cancellationToken: cancellationToken, semanticModel: semanticModelOpt) &&
!syntaxTree.IsConstantExpressionContext(position, tokenOnLeftOfPosition))
{
return true;
Expand Down Expand Up @@ -1746,32 +1746,6 @@ public static bool IsValidContextForJoinClause(
return false;
}

public static bool IsDeclarationExpressionContext(
this SyntaxTree syntaxTree, int position, SyntaxToken tokenOnLeftOfPosition)
{
// cases:
// M(out var
// var x = var

var token = tokenOnLeftOfPosition;
token = token.GetPreviousTokenIfTouchingWord(position);

if (CodeAnalysis.CSharpExtensions.IsKind(token, SyntaxKind.OutKeyword) &&
token.Parent.IsKind(SyntaxKind.Argument))
{
return true;
}

if (CodeAnalysis.CSharpExtensions.IsKind(token, SyntaxKind.EqualsToken) &&
token.Parent.IsKind(SyntaxKind.EqualsValueClause) &&
token.Parent.IsParentKind(SyntaxKind.VariableDeclarator))
{
return true;
}

return false;
}

public static bool IsLocalVariableDeclarationContext(
this SyntaxTree syntaxTree, int position, SyntaxToken tokenOnLeftOfPosition, CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -2183,7 +2157,7 @@ public static bool IsExpressionContext(
SyntaxToken tokenOnLeftOfPosition,
bool attributes,
CancellationToken cancellationToken,
SemanticModel? semanticModelOpt = null)
SemanticModel? semanticModel = null)
{
// cases:
// var q = |
Expand Down Expand Up @@ -2326,7 +2300,7 @@ public static bool IsExpressionContext(
// then this is not an expression context. i.e. if we have "Goo < |" then it could
// be an expression context, or it could be a type context if Goo binds to a type or
// method.
if (semanticModelOpt != null && syntaxTree.IsGenericTypeArgumentContext(position, tokenOnLeftOfPosition, cancellationToken, semanticModelOpt))
if (semanticModel != null && syntaxTree.IsGenericTypeArgumentContext(position, tokenOnLeftOfPosition, cancellationToken, semanticModel))
{
return false;
}
Expand All @@ -2336,9 +2310,9 @@ public static bool IsExpressionContext(
// If this is a multiplication expression and a semantic model was passed in,
// check to see if the expression to the left is a type name. If it is, treat
// this as a pointer type.
if (token.IsKind(SyntaxKind.AsteriskToken) && semanticModelOpt != null)
if (token.IsKind(SyntaxKind.AsteriskToken) && semanticModel != null)
{
if (binary.Left is TypeSyntax type && type.IsPotentialTypeName(semanticModelOpt, cancellationToken))
if (binary.Left is TypeSyntax type && type.IsPotentialTypeName(semanticModel, cancellationToken))
{
return false;
}
Expand Down Expand Up @@ -2372,7 +2346,7 @@ public static bool IsExpressionContext(
? pointerType.ElementType
: ((NullableTypeSyntax)type).ElementType;

if (!underlyingType.IsPotentialTypeName(semanticModelOpt, cancellationToken))
if (!underlyingType.IsPotentialTypeName(semanticModel, cancellationToken))
{
return true;
}
Expand All @@ -2392,13 +2366,22 @@ public static bool IsExpressionContext(
}
}

// 1..|
// not 1.|.
if (token.IsKind(SyntaxKind.DotDotToken) &&
token.Parent.IsKind(SyntaxKind.RangeExpression) &&
position >= token.Span.End)
{
return true;
}

// goo ? |
if (token.IsKind(SyntaxKind.QuestionToken) &&
token.Parent is ConditionalExpressionSyntax conditionalExpression)
{
// If the condition is simply a TypeSyntax that binds to a type, treat this as a nullable type.
return conditionalExpression.Condition is not TypeSyntax type
|| !type.IsPotentialTypeName(semanticModelOpt, cancellationToken);
|| !type.IsPotentialTypeName(semanticModel, cancellationToken);
}

// goo ? bar : |
Expand Down Expand Up @@ -2519,11 +2502,11 @@ public static bool IsExpressionContext(
// Perform a semantic check to determine whether or not the type being created
// can support a collection initializer. If not, this must be an object initializer
// and can't be an expression context.
if (semanticModelOpt != null &&
if (semanticModel != null &&
token.Parent?.Parent is ObjectCreationExpressionSyntax objectCreation)
{
var containingSymbol = semanticModelOpt.GetEnclosingNamedTypeOrAssembly(position, cancellationToken);
if (semanticModelOpt.GetSymbolInfo(objectCreation.Type, cancellationToken).Symbol is ITypeSymbol type && !type.CanSupportCollectionInitializer(containingSymbol))
var containingSymbol = semanticModel.GetEnclosingNamedTypeOrAssembly(position, cancellationToken);
if (semanticModel.GetSymbolInfo(objectCreation.Type, cancellationToken).Symbol is ITypeSymbol type && !type.CanSupportCollectionInitializer(containingSymbol))
{
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public bool IsExpressionContext(SemanticModel semanticModel, int position, Cance
return semanticModel.SyntaxTree.IsExpressionContext(
position,
semanticModel.SyntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken),
attributes: true, cancellationToken: cancellationToken, semanticModelOpt: semanticModel);
attributes: true, cancellationToken: cancellationToken, semanticModel: semanticModel);
}

public bool IsStatementContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken)
Expand Down

0 comments on commit 3aa2c51

Please sign in to comment.