Skip to content

Commit a7ff9bf

Browse files
Introduce local variable supporting target-type new syntax (#76342)
2 parents d0a39c3 + 523eab5 commit a7ff9bf

File tree

6 files changed

+126
-7
lines changed

6 files changed

+126
-7
lines changed

src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceVariableService_IntroduceLocal.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
using System.Diagnostics;
1010
using System.Linq;
1111
using System.Threading;
12-
using System.Threading.Tasks;
1312
using Microsoft.CodeAnalysis;
1413
using Microsoft.CodeAnalysis.CodeActions;
14+
using Microsoft.CodeAnalysis.CodeCleanup;
15+
using Microsoft.CodeAnalysis.CSharp.CodeStyle.TypeStyle;
1516
using Microsoft.CodeAnalysis.CSharp.Extensions;
17+
using Microsoft.CodeAnalysis.CSharp.Simplification;
1618
using Microsoft.CodeAnalysis.CSharp.Syntax;
1719
using Microsoft.CodeAnalysis.Editing;
1820
using Microsoft.CodeAnalysis.Formatting;
@@ -28,6 +30,7 @@ internal sealed partial class CSharpIntroduceVariableService
2830
{
2931
protected override Document IntroduceLocal(
3032
SemanticDocument document,
33+
CodeCleanupOptions options,
3134
ExpressionSyntax expression,
3235
bool allOccurrences,
3336
bool isConstant,
@@ -47,14 +50,25 @@ protected override Document IntroduceLocal(
4750
? TokenList(ConstKeyword)
4851
: default;
4952

53+
var updatedExpression = expression.WithoutTrivia();
54+
var simplifierOptions = (CSharpSimplifierOptions)options.SimplifierOptions;
55+
56+
// If the target-type new syntax is preferred and "var" is not preferred under any circumstance, then we use the target-type new syntax.
57+
// The approach is not exhaustive. We aim to support codebases that rely strictly on the target-type new syntax (i.e., no "var").
58+
if (simplifierOptions.ImplicitObjectCreationWhenTypeIsApparent.Value && simplifierOptions.GetUseVarPreference() == UseVarPreference.None
59+
&& updatedExpression is ObjectCreationExpressionSyntax objectCreationExpression)
60+
{
61+
updatedExpression = ImplicitObjectCreationExpression(objectCreationExpression.ArgumentList, objectCreationExpression.Initializer);
62+
}
63+
5064
var declarationStatement = LocalDeclarationStatement(
5165
modifiers,
5266
VariableDeclaration(
5367
GetTypeSyntax(document, expression, cancellationToken),
5468
[VariableDeclarator(
5569
newLocalNameToken.WithAdditionalAnnotations(RenameAnnotation.Create()),
5670
argumentList: null,
57-
EqualsValueClause(expression.WithoutTrivia()))]));
71+
EqualsValueClause(updatedExpression))]));
5872

5973
switch (containerToGenerateInto)
6074
{

src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -935,13 +935,63 @@ class Program
935935
{
936936
static void Main()
937937
{
938-
G<int>.@class {|Rename:@class|} = new G<int>.@class();
938+
G<int>.@class {|Rename:@class|} = new();
939939
G<int>.Add(@class);
940940
}
941941
}
942942
""");
943943
}
944944

945+
[Fact]
946+
public async Task TestNameVerbatimIdentifier1NoVar_1()
947+
{
948+
await TestInRegularAndScriptAsync(
949+
"""
950+
static class G<T>
951+
{
952+
public class @class
953+
{
954+
}
955+
956+
public static void Add(object @class)
957+
{
958+
}
959+
}
960+
961+
class Program
962+
{
963+
static void Main()
964+
{
965+
G<int>.Add([|new G<int>.@class()|]);
966+
}
967+
}
968+
""",
969+
"""
970+
static class G<T>
971+
{
972+
public class @class
973+
{
974+
}
975+
976+
public static void Add(object @class)
977+
{
978+
}
979+
}
980+
981+
class Program
982+
{
983+
static void Main()
984+
{
985+
G<int>.@class {|Rename:@class|} = new G<int>.@class();
986+
G<int>.Add(@class);
987+
}
988+
}
989+
""", options: new(GetLanguage())
990+
{
991+
{ CSharpCodeStyleOptions.ImplicitObjectCreationWhenTypeIsApparent, CodeStyleOption2.FalseWithSilentEnforcement }
992+
});
993+
}
994+
945995
[Fact]
946996
public async Task TestNameVerbatimIdentifier2()
947997
{
@@ -1018,7 +1068,7 @@ public static void Add(object @class)
10181068
10191069
static void Main()
10201070
{
1021-
G<int>.@class {|Rename:@class|} = new G<int>.@class();
1071+
G<int>.@class {|Rename:@class|} = new();
10221072
G<int>.Add(@class);
10231073
}
10241074
}
@@ -3001,7 +3051,7 @@ class Program
30013051
{
30023052
static void Main()
30033053
{
3004-
Nullable<int*> {|Rename:v|} = new Nullable<int*>();
3054+
Nullable<int*> {|Rename:v|} = new();
30053055
v.GetValueOrDefault();
30063056
}
30073057
}
@@ -4283,6 +4333,56 @@ public T this[int i]
42834333
await TestInRegularAndScriptAsync(code, expected, options: ImplicitTypingEverywhere());
42844334
}
42854335

4336+
[Fact]
4337+
public async Task TestIntroduceLocalWithTargetTypedNew()
4338+
{
4339+
var code =
4340+
"""
4341+
using System;
4342+
class SampleType
4343+
{
4344+
public SampleType()
4345+
{
4346+
int sum = Sum([|new Numbers()|]);
4347+
}
4348+
4349+
private int Sum(Numbers numbers)
4350+
{
4351+
return 42;
4352+
}
4353+
4354+
private class Numbers {}
4355+
}
4356+
""";
4357+
4358+
var expected =
4359+
"""
4360+
using System;
4361+
class SampleType
4362+
{
4363+
public SampleType()
4364+
{
4365+
Numbers {|Rename:numbers|} = new();
4366+
int sum = Sum(numbers);
4367+
}
4368+
4369+
private int Sum(Numbers numbers)
4370+
{
4371+
return 42;
4372+
}
4373+
4374+
private class Numbers {}
4375+
}
4376+
""";
4377+
4378+
OptionsCollection optionsCollection = new(GetLanguage())
4379+
{
4380+
{ CSharpCodeStyleOptions.ImplicitObjectCreationWhenTypeIsApparent, new CodeStyleOption2<bool>(true, NotificationOption2.Warning) },
4381+
};
4382+
4383+
await TestInRegularAndScriptAsync(code, expected, options: optionsCollection);
4384+
}
4385+
42864386
[Fact]
42874387
public async Task TestIntroduceFieldInExpressionBodiedPropertyGetter()
42884388
{

src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.IntroduceVariableCodeAction.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ private async Task<Document> GetChangedDocumentCoreAsync(CancellationToken cance
6464
}
6565
else if (_isLocal)
6666
{
67-
return _service.IntroduceLocal(_semanticDocument, _expression, _allOccurrences, _isConstant, cancellationToken);
67+
return _service.IntroduceLocal(_semanticDocument, Options, _expression, _allOccurrences, _isConstant, cancellationToken);
6868
}
6969
else
7070
{

src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using Microsoft.CodeAnalysis.CodeActions;
1414
using Microsoft.CodeAnalysis.CodeCleanup;
1515
using Microsoft.CodeAnalysis.Editing;
16+
using Microsoft.CodeAnalysis.Diagnostics;
1617
using Microsoft.CodeAnalysis.Formatting;
1718
using Microsoft.CodeAnalysis.Internal.Log;
1819
using Microsoft.CodeAnalysis.LanguageService;
@@ -50,7 +51,7 @@ internal abstract partial class AbstractIntroduceVariableService<TService, TExpr
5051
protected abstract bool IsExpressionInStaticLocalFunction(TExpressionSyntax expression);
5152

5253
protected abstract Document IntroduceQueryLocal(SemanticDocument document, TExpressionSyntax expression, bool allOccurrences, CancellationToken cancellationToken);
53-
protected abstract Document IntroduceLocal(SemanticDocument document, TExpressionSyntax expression, bool allOccurrences, bool isConstant, CancellationToken cancellationToken);
54+
protected abstract Document IntroduceLocal(SemanticDocument document, CodeCleanupOptions options, TExpressionSyntax expression, bool allOccurrences, bool isConstant, CancellationToken cancellationToken);
5455
protected abstract Task<Document> IntroduceFieldAsync(SemanticDocument document, TExpressionSyntax expression, bool allOccurrences, bool isConstant, CancellationToken cancellationToken);
5556

5657
protected abstract int DetermineFieldInsertPosition(TTypeDeclarationSyntax oldDeclaration, TTypeDeclarationSyntax newDeclaration);

src/Features/VisualBasic/Portable/IntroduceVariable/VisualBasicIntroduceVariableService_IntroduceLocal.vb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ Imports System.Threading
66
Imports Microsoft.CodeAnalysis
77
Imports Microsoft.CodeAnalysis.CodeActions
88
Imports Microsoft.CodeAnalysis.Formatting
9+
Imports Microsoft.CodeAnalysis.CodeCleanup
910
Imports Microsoft.CodeAnalysis.VisualBasic
1011
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
1112

1213
Namespace Microsoft.CodeAnalysis.VisualBasic.IntroduceVariable
1314
Partial Friend Class VisualBasicIntroduceVariableService
1415
Protected Overrides Function IntroduceLocal(
1516
document As SemanticDocument,
17+
options As CodeCleanupOptions,
1618
expression As ExpressionSyntax,
1719
allOccurrences As Boolean,
1820
isConstant As Boolean,

src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/CSharpSimplifierOptions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ internal sealed record class CSharpSimplifierOptions : SimplifierOptions, IEquat
2828
[DataMember] public CodeStyleOption2<bool> AllowEmbeddedStatementsOnSameLine { get; init; } = CodeStyleOption2.TrueWithSilentEnforcement;
2929
[DataMember] public CodeStyleOption2<PreferBracesPreference> PreferBraces { get; init; } = s_defaultPreferBraces;
3030
[DataMember] public CodeStyleOption2<bool> PreferThrowExpression { get; init; } = CodeStyleOption2.TrueWithSuggestionEnforcement;
31+
[DataMember] public CodeStyleOption2<bool> ImplicitObjectCreationWhenTypeIsApparent { get; init; } = CodeStyleOption2.FalseWithSilentEnforcement;
3132

3233
public CSharpSimplifierOptions()
3334
{
@@ -43,6 +44,7 @@ public CSharpSimplifierOptions(IOptionsReader options)
4344
AllowEmbeddedStatementsOnSameLine = options.GetOption(CSharpCodeStyleOptions.AllowEmbeddedStatementsOnSameLine);
4445
PreferBraces = options.GetOption(CSharpCodeStyleOptions.PreferBraces);
4546
PreferThrowExpression = options.GetOption(CSharpCodeStyleOptions.PreferThrowExpression);
47+
ImplicitObjectCreationWhenTypeIsApparent = options.GetOption(CSharpCodeStyleOptions.ImplicitObjectCreationWhenTypeIsApparent);
4648
}
4749

4850
public UseVarPreference GetUseVarPreference()

0 commit comments

Comments
 (0)