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 @@ -12,32 +12,32 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations
public class StackAllocKeywordRecommenderTests : KeywordRecommenderTests
{
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestNotAtRoot_Interactive()
public async Task TestAtRoot_Interactive()
{
await VerifyAbsenceAsync(SourceCodeKind.Script,
await VerifyKeywordAsync(SourceCodeKind.Script,
@"$$");
}

[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestNotAfterClass_Interactive()
public async Task TestAfterClass_Interactive()
{
await VerifyAbsenceAsync(SourceCodeKind.Script,
await VerifyKeywordAsync(SourceCodeKind.Script,
@"class C { }
$$");
}

[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestNotAfterGlobalStatement_Interactive()
public async Task TestAfterGlobalStatement_Interactive()
{
await VerifyAbsenceAsync(SourceCodeKind.Script,
await VerifyKeywordAsync(SourceCodeKind.Script,
@"System.Console.WriteLine();
$$");
}

[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestNotAfterGlobalVariableDeclaration_Interactive()
public async Task TestAfterGlobalVariableDeclaration_Interactive()
{
await VerifyAbsenceAsync(SourceCodeKind.Script,
await VerifyKeywordAsync(SourceCodeKind.Script,
@"int i = 0;
$$");
}
Expand All @@ -50,9 +50,11 @@ await VerifyAbsenceAsync(
}

[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestNotInEmptyStatement()
public async Task TestInEmptyStatement()
{
await VerifyAbsenceAsync(AddInsideMethod(
// e.g. this is a valid statement
// stackalloc[] { 1, 2, 3 }.IndexOf(1);
await VerifyKeywordAsync(AddInsideMethod(
@"$$"));
}

Expand Down Expand Up @@ -92,11 +94,14 @@ void Goo() {
}

[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestNotInField()
public async Task TestInField()
{
await VerifyAbsenceAsync(
@"unsafe class C {
int* v = $$");
// While assigning stackalloc'd value to a field is invalid,
// using one in the initializer is OK. e.g.
// int _f = stackalloc[] { 1, 2, 3 }.IndexOf(1);
await VerifyKeywordAsync(
@"class C {
int v = $$");
}

[WorkItem(544504, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544504")]
Expand Down Expand Up @@ -268,5 +273,39 @@ await VerifyAbsenceAsync(AddInsideMethod(@"
await VerifyAbsenceAsync(AddInsideMethod(@"
x $$ ="));
}

[WorkItem(41736, "https://github.com/dotnet/roslyn/issues/41736")]
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInArgument()
{
await VerifyKeywordAsync(@"
class Program
{
static void Method(System.Span<byte> span)
{
Method($$);
}
}");

await VerifyKeywordAsync(@"
class Program
{
static void Method(int x, System.Span<byte> span)
{
Method(1, $$);
}
}");
}

[WorkItem(41736, "https://github.com/dotnet/roslyn/issues/41736")]
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestNotInConstFieldInitializer()
{
await VerifyAbsenceAsync(@"
class Program
{
private const int _f = $$
}");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,52 +17,11 @@ public StackAllocKeywordRecommender()

protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
{
var node = context.TargetToken.Parent;

// At start of a file
if (node == null)
{
return false;
}

// After a cast or parenthesized expression: (Span<int>)stackalloc
if (context.TargetToken.IsAfterPossibleCast())
{
node = node.Parent;
}

// Inside a conditional expression: value ? stackalloc : stackalloc
while (node.IsKind(SyntaxKind.ConditionalExpression) &&
(context.TargetToken.IsKind(SyntaxKind.QuestionToken, SyntaxKind.ColonToken) || context.TargetToken.IsAfterPossibleCast()))
{
node = node.Parent;
}

// assignment: x = stackalloc
if (node.IsKind(SyntaxKind.SimpleAssignmentExpression))
{
return node.Parent.IsKind(SyntaxKind.ExpressionStatement);
}

// declaration: var x = stackalloc
if (node.IsKind(SyntaxKind.EqualsValueClause))
{
node = node.Parent;

if (node.IsKind(SyntaxKind.VariableDeclarator))
{
node = node.Parent;

if (node.IsKind(SyntaxKind.VariableDeclaration))
{
node = node.Parent;

return node.IsKind(SyntaxKind.LocalDeclarationStatement, SyntaxKind.ForStatement);
}
}
}

return false;
// Beginning with C# 8.0, stackalloc expression can be used inside other expressions
// whenever a Span<T> or ReadOnlySpan<T> variable is allowed.
return (context.IsAnyExpressionContext && !context.IsConstantExpressionContext) ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mixing && ! || together is tougher to read. there's nothing wrong with statements :)

context.IsStatementContext ||
context.IsGlobalStatementContext;
}
}
}