Skip to content

Commit

Permalink
Merge pull request #74841 from DoctorKrolic/no-snippets-on-types
Browse files Browse the repository at this point in the history
  • Loading branch information
CyrusNajmabadi committed Aug 23, 2024
2 parents a316f10 + 13abfd6 commit b360f99
Show file tree
Hide file tree
Showing 8 changed files with 290 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Snippets;
[ExportSnippetProvider(nameof(ISnippetProvider), LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class CSharpDoWhileLoopStatementProvider()
internal sealed class CSharpDoWhileLoopSnippetProvider()
: AbstractConditionalBlockSnippetProvider<DoStatementSyntax, ExpressionSyntax>
{
public override string Identifier => CSharpSnippetIdentifiers.Do;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -538,4 +538,32 @@ void M(bool flag)
}
""");
}

[Fact]
public async Task NoInlineSnippetForTypeItselfTest()
{
await VerifySnippetIsAbsentAsync("""
class C
{
void M()
{
bool.$$
}
}
""");
}

[Fact]
public async Task NoInlineSnippetForTypeItselfTest_Parenthesized()
{
await VerifySnippetIsAbsentAsync("""
class C
{
void M()
{
(bool).$$
}
}
""");
}
}
28 changes: 28 additions & 0 deletions src/Features/CSharpTest/Snippets/CSharpDoSnippetProviderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -554,4 +554,32 @@ void M(bool flag)
}
""");
}

[Fact]
public async Task NoInlineDoSnippetForTypeItselfTest()
{
await VerifySnippetIsAbsentAsync("""
class C
{
void M()
{
bool.$$
}
}
""");
}

[Fact]
public async Task NoInlineDoSnippetForTypeItselfTest_Parenthesized()
{
await VerifySnippetIsAbsentAsync("""
class C
{
void M()
{
(bool).$$
}
}
""");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -224,17 +224,10 @@ await VerifySnippetAsync("""
}

[Theory]
[InlineData("List<int>")]
[InlineData("int[]")]
[InlineData("IEnumerable<int>")]
[InlineData("ArrayList")]
[InlineData("IEnumerable")]
[MemberData(nameof(CommonSnippetTestData.CommonEnumerableTypes), MemberType = typeof(CommonSnippetTestData))]
public async Task InsertInlineForEachSnippetForCorrectTypeTest(string collectionType)
{
await VerifySnippetAsync($$"""
using System.Collections;
using System.Collections.Generic;

class C
{
void M({{collectionType}} enumerable)
Expand All @@ -243,9 +236,6 @@ void M({{collectionType}} enumerable)
}
}
""", $$"""
using System.Collections;
using System.Collections.Generic;

class C
{
void M({{collectionType}} enumerable)
Expand Down Expand Up @@ -710,4 +700,82 @@ void M(IAsyncEnumerable<int> asyncInts)
""",
referenceAssemblies: ReferenceAssemblies.Net.Net70);
}

[Theory]
[MemberData(nameof(CommonSnippetTestData.CommonEnumerableTypes), MemberType = typeof(CommonSnippetTestData))]
public async Task NoInlineForEachSnippetForTypeItselfTest(string collectionType)
{
await VerifySnippetIsAbsentAsync($$"""
class C
{
void M()
{
{{collectionType}}.$$
}
}
""");
}

[Theory]
[MemberData(nameof(CommonSnippetTestData.CommonEnumerableTypes), MemberType = typeof(CommonSnippetTestData))]
public async Task NoInlineForEachSnippetForTypeItselfTest_Parenthesized(string collectionType)
{
await VerifySnippetIsAbsentAsync($$"""
class C
{
void M()
{
({{collectionType}}).$$
}
}
""");
}

[Theory]
[InlineData("ArrayList")]
[InlineData("IEnumerable")]
[InlineData("MyCollection")]
public async Task InsertInlineForEachSnippetForVariableNamedLikeTypeTest(string typeAndVariableName)
{
await VerifySnippetAsync($$"""
using System.Collections;
using System.Collections.Generic;

class C
{
void M()
{
{{typeAndVariableName}} {{typeAndVariableName}} = default;
{{typeAndVariableName}}.$$
}
}

class MyCollection : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator() => null;
IEnumerator IEnumerable.GetEnumerator() = null;
}
""", $$"""
using System.Collections;
using System.Collections.Generic;

class C
{
void M()
{
{{typeAndVariableName}} {{typeAndVariableName}} = default;
foreach (var {|0:item|} in {{typeAndVariableName}})
{
$$
}
}
}

class MyCollection : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator() => null;
IEnumerator IEnumerable.GetEnumerator() = null;
}
""");
}
}
67 changes: 67 additions & 0 deletions src/Features/CSharpTest/Snippets/CSharpForSnippetProviderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -875,4 +875,71 @@ public class MyType
}
""");
}

[Theory]
[MemberData(nameof(CommonSnippetTestData.IntegerTypes), MemberType = typeof(CommonSnippetTestData))]
public async Task NoInlineForSnippetForTypeItselfTest(string integerType)
{
await VerifySnippetIsAbsentAsync($$"""
class C
{
void M()
{
{{integerType}}.$$
}
}
""");
}

[Theory]
[MemberData(nameof(CommonSnippetTestData.IntegerTypes), MemberType = typeof(CommonSnippetTestData))]
public async Task NoInlineForSnippetForTypeItselfTest_Parenthesized(string integerType)
{
await VerifySnippetIsAbsentAsync($$"""
class C
{
void M()
{
({{integerType}}).$$
}
}
""");
}

[Fact]
public async Task InsertInlineForSnippetForVariableNamedLikeTypeTest()
{
await VerifySnippetAsync("""
class C
{
void M()
{
MyType MyType = default;
MyType.$$
}
}

class MyType
{
public int Length => 0;
}
""", """
class C
{
void M()
{
MyType MyType = default;
for (int {|0:i|} = 0; {|0:i|} < MyType.Length; {|0:i|}++)
{
$$
}
}
}

class MyType
{
public int Length => 0;
}
""");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -877,4 +877,71 @@ public class MyType
}
""");
}

[Theory]
[MemberData(nameof(CommonSnippetTestData.IntegerTypes), MemberType = typeof(CommonSnippetTestData))]
public async Task NoInlineReversedForSnippetForTypeItselfTest(string integerType)
{
await VerifySnippetIsAbsentAsync($$"""
class C
{
void M()
{
{{integerType}}.$$
}
}
""");
}

[Theory]
[MemberData(nameof(CommonSnippetTestData.IntegerTypes), MemberType = typeof(CommonSnippetTestData))]
public async Task NoInlineReversedForSnippetForTypeItselfTest_Parenthesized(string integerType)
{
await VerifySnippetIsAbsentAsync($$"""
class C
{
void M()
{
({{integerType}}).$$
}
}
""");
}

[Fact]
public async Task InsertInlineReversedForSnippetForVariableNamedLikeTypeTest()
{
await VerifySnippetAsync("""
class C
{
void M()
{
MyType MyType = default;
MyType.$$
}
}

class MyType
{
public int Length => 0;
}
""", """
class C
{
void M()
{
MyType MyType = default;
for (int {|0:i|} = MyType.Length - 1; {|0:i|} >= 0; {|0:i|}--)
{
$$
}
}
}

class MyType
{
public int Length => 0;
}
""");
}
}
10 changes: 10 additions & 0 deletions src/Features/CSharpTest/Snippets/CommonSnippetTestData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,14 @@ public static class CommonSnippetTestData
"private protected",
"protected internal",
};

public static TheoryData<string> CommonEnumerableTypes => new()
{
"string",
"System.Collections.Generic.List<int>",
"int[]",
"System.Collections.Generic.IEnumerable<int>",
"System.Collections.ArrayList",
"System.Collections.IEnumerable",
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ private static bool TryGetInlineExpressionInfo(SyntaxToken targetToken, ISyntaxF
syntaxFacts.IsExpressionStatement(parentNode?.Parent))
{
var expression = syntaxFacts.GetExpressionOfMemberAccessExpression(parentNode)!;
var symbolInfo = semanticModel.GetSymbolInfo(expression, cancellationToken);

// Forbid a case when we are dotting of a type, e.g. `string.$$`.
// Inline statement snippets are not valid in this context
if (symbolInfo.Symbol is ITypeSymbol)
{
expressionInfo = null;
return false;
}

var typeInfo = semanticModel.GetTypeInfo(expression, cancellationToken);
expressionInfo = new(expression, typeInfo);
return true;
Expand Down

0 comments on commit b360f99

Please sign in to comment.