Skip to content

Commit 1f93a72

Browse files
Merge remote-tracking branch 'upstream/main' into asyncTupleExtractMethod
2 parents 4dd3f90 + 3409430 commit 1f93a72

File tree

33 files changed

+838
-517
lines changed

33 files changed

+838
-517
lines changed

src/Analyzers/CSharp/Tests/AddParameter/AddParameterTests.cs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3340,4 +3340,110 @@ void M(string s)
33403340
}
33413341
""");
33423342
}
3343+
3344+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71428")]
3345+
public async Task TestAddConstructorParameterWithExistingField_PrimaryConstructor()
3346+
{
3347+
await TestInRegularAndScript1Async(
3348+
"""
3349+
class C()
3350+
{
3351+
private readonly string _name;
3352+
}
3353+
3354+
class D
3355+
{
3356+
void M(string name)
3357+
{
3358+
new [|C|](name);
3359+
}
3360+
}
3361+
""",
3362+
"""
3363+
class C(string name)
3364+
{
3365+
private readonly string _name = name;
3366+
}
3367+
3368+
class D
3369+
{
3370+
void M(string name)
3371+
{
3372+
new C(name);
3373+
}
3374+
}
3375+
""");
3376+
}
3377+
3378+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71428")]
3379+
public async Task TestAddConstructorParameterWithExistingProperty_PrimaryConstructor()
3380+
{
3381+
await TestInRegularAndScript1Async(
3382+
"""
3383+
class C()
3384+
{
3385+
private string Name { get; }
3386+
}
3387+
3388+
class D
3389+
{
3390+
void M(string name)
3391+
{
3392+
new [|C|](name);
3393+
}
3394+
}
3395+
""",
3396+
"""
3397+
class C(string name)
3398+
{
3399+
private string Name { get; } = name;
3400+
}
3401+
3402+
class D
3403+
{
3404+
void M(string name)
3405+
{
3406+
new C(name);
3407+
}
3408+
}
3409+
""");
3410+
}
3411+
3412+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71428")]
3413+
public async Task TestAddConstructorParameterWithExistingThrowingProperty_PrimaryConstructor()
3414+
{
3415+
await TestInRegularAndScript1Async(
3416+
"""
3417+
using System;
3418+
3419+
class C()
3420+
{
3421+
private string Name => throw new NotImplementedException();
3422+
}
3423+
3424+
class D
3425+
{
3426+
void M(string name)
3427+
{
3428+
new [|C|](name);
3429+
}
3430+
}
3431+
""",
3432+
"""
3433+
using System;
3434+
3435+
class C(string name)
3436+
{
3437+
private string Name { get; } = name;
3438+
}
3439+
3440+
class D
3441+
{
3442+
void M(string name)
3443+
{
3444+
new C(name);
3445+
}
3446+
}
3447+
""");
3448+
}
33433449
}

src/Analyzers/Core/CodeFixes/AddParameter/AbstractAddParameterCodeFixProvider.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ internal abstract class AbstractAddParameterCodeFixProvider<
3434
where TInvocationExpressionSyntax : TExpressionSyntax
3535
where TObjectCreationExpressionSyntax : TExpressionSyntax
3636
{
37+
private static readonly SymbolDisplayFormat SimpleFormat = new(
38+
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameOnly,
39+
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
40+
parameterOptions: SymbolDisplayParameterOptions.IncludeParamsRefOut | SymbolDisplayParameterOptions.IncludeType,
41+
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);
42+
3743
protected abstract ImmutableArray<string> TooManyArgumentsDiagnosticIds { get; }
3844
protected abstract ImmutableArray<string> CannotConvertDiagnosticIds { get; }
3945

@@ -422,13 +428,6 @@ private async Task<Solution> FixAsync(
422428
}
423429
}
424430

425-
private static readonly SymbolDisplayFormat SimpleFormat =
426-
new(
427-
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameOnly,
428-
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
429-
parameterOptions: SymbolDisplayParameterOptions.IncludeParamsRefOut | SymbolDisplayParameterOptions.IncludeType,
430-
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);
431-
432431
private static TArgumentSyntax? DetermineFirstArgumentToAdd(
433432
SemanticModel semanticModel,
434433
ISyntaxFactsService syntaxFacts,

src/Analyzers/Core/CodeFixes/AddParameter/AddParameterService.cs

Lines changed: 14 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
using Microsoft.CodeAnalysis.GenerateMember.GenerateConstructor;
1515
using Microsoft.CodeAnalysis.InitializeParameter;
1616
using Microsoft.CodeAnalysis.LanguageService;
17-
using Microsoft.CodeAnalysis.Operations;
1817
using Microsoft.CodeAnalysis.Shared.Extensions;
1918
using Microsoft.CodeAnalysis.Shared.Utilities;
2019
using Roslyn.Utilities;
@@ -180,10 +179,6 @@ async Task<Solution> AddConstructorAssignmentsAsync(Solution rewrittenSolution)
180179

181180
var documentId = documentsUpdated.Single().Id;
182181

183-
var memberToAssignTo = await GetMemberToAssignToAsync(documentId).ConfigureAwait(false);
184-
if (memberToAssignTo is null)
185-
return null;
186-
187182
// Now go find the constructor after the parameter was added to it.
188183
var rewrittenDocument = rewrittenSolution.GetRequiredDocument(documentId);
189184
var rewrittenSyntaxRoot = await rewrittenDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
@@ -192,41 +187,28 @@ async Task<Solution> AddConstructorAssignmentsAsync(Solution rewrittenSolution)
192187
if (parameterDeclaration is null)
193188
return null;
194189

195-
var initializeParameterService = rewrittenDocument.GetRequiredLanguageService<IInitializeParameterService>();
196-
var semanticModel = await rewrittenDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
197-
if (semanticModel.GetDeclaredSymbol(parameterDeclaration, cancellationToken) is not IParameterSymbol parameter)
190+
var semanticDocument = await SemanticDocument.CreateAsync(rewrittenDocument, cancellationToken).ConfigureAwait(false);
191+
if (semanticDocument.SemanticModel.GetDeclaredSymbol(parameterDeclaration, cancellationToken) is not IParameterSymbol parameter)
198192
return null;
199193

200-
if (parameter.ContainingSymbol is not IMethodSymbol { MethodKind: MethodKind.Constructor, DeclaringSyntaxReferences: [var reference] })
194+
if (parameter.ContainingSymbol is not IMethodSymbol { MethodKind: MethodKind.Constructor, DeclaringSyntaxReferences: [var reference] } constructor)
201195
return null;
202196

203-
var methodNode = reference.GetSyntax(cancellationToken);
204-
var body = initializeParameterService.GetBody(methodNode);
205-
if (semanticModel.GetOperation(body, cancellationToken) is not IBlockOperation blockOperation)
206-
return rewrittenSolution;
207-
208-
var editor = new SyntaxEditor(rewrittenSyntaxRoot, rewrittenSolution.Services);
209-
initializeParameterService.AddAssignment(
210-
methodNode, blockOperation, parameter, memberToAssignTo, editor);
211-
212-
var finalDocument = rewrittenDocument.WithSyntaxRoot(editor.GetChangedRoot());
213-
return finalDocument.Project.Solution;
214-
}
215-
216-
async Task<ISymbol?> GetMemberToAssignToAsync(DocumentId documentId)
217-
{
218-
var constructorDocument = invocationDocument.Project.Solution.GetRequiredDocument(documentId);
219-
var constructorSemanticDocument = await SemanticDocument.CreateAsync(constructorDocument, cancellationToken).ConfigureAwait(false);
220-
221197
var (_, parameterToExistingMember, _, _) = await GenerateConstructorHelpers.GetParametersAsync(
222-
constructorSemanticDocument,
223-
method.ContainingType,
198+
semanticDocument,
199+
constructor.ContainingType,
224200
[argument.Value],
225-
[newParameterType],
226-
[parameterName],
201+
[parameter.Type],
202+
[new ParameterName(parameter.Name, isFixed: true)],
227203
cancellationToken).ConfigureAwait(false);
228204

229-
return parameterToExistingMember.FirstOrDefault().Value;
205+
var memberToAssignTo = parameterToExistingMember.FirstOrDefault().Value;
206+
if (memberToAssignTo is null)
207+
return null;
208+
209+
var initializeParameterService = rewrittenDocument.GetRequiredLanguageService<IInitializeParameterService>();
210+
return await initializeParameterService.AddAssignmentAsync(
211+
rewrittenDocument, parameter, memberToAssignTo, cancellationToken).ConfigureAwait(false);
230212
}
231213
}
232214

src/Analyzers/Core/CodeFixes/GenerateConstructor/GenerateConstructorHelpers.cs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using System.Threading.Tasks;
1212
using Microsoft.CodeAnalysis.CodeGeneration;
1313
using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles;
14+
using Microsoft.CodeAnalysis.InitializeParameter;
1415
using Microsoft.CodeAnalysis.LanguageService;
1516
using Microsoft.CodeAnalysis.PooledObjects;
1617
using Microsoft.CodeAnalysis.Shared.Extensions;
@@ -185,7 +186,7 @@ ParameterName FindExistingOrCreateNewMember(
185186
var symbol = TryFindMatchingMember(parameterName);
186187
if (symbol != null)
187188
{
188-
if (IsViableFieldOrProperty(document, parameterType, symbol))
189+
if (IsViableFieldOrProperty(document, parameterType, symbol, cancellationToken))
189190
{
190191
// Ok! We can just the existing field.
191192
parameterToExistingMemberMap[parameterName.BestNameForParameter] = symbol;
@@ -345,25 +346,28 @@ private static bool IsSymbolAccessible(ISymbol? symbol, SemanticDocument documen
345346
private static bool IsViableFieldOrProperty(
346347
SemanticDocument document,
347348
ITypeSymbol parameterType,
348-
ISymbol symbol)
349+
ISymbol symbol,
350+
CancellationToken cancellationToken)
349351
{
350352
if (parameterType.Language != symbol.Language)
351353
return false;
352354

353355
if (symbol != null && !symbol.IsStatic)
354356
{
355-
if (symbol is IFieldSymbol field)
357+
if (symbol is IFieldSymbol { IsConst: false } field)
356358
{
357-
return
358-
!field.IsConst &&
359-
IsConversionImplicit(document.SemanticModel.Compilation, parameterType, field.Type);
359+
return IsConversionImplicit(document.SemanticModel.Compilation, parameterType, field.Type);
360360
}
361-
else if (symbol is IPropertySymbol property)
361+
else if (symbol is IPropertySymbol { Parameters.Length: 0 } property)
362362
{
363-
return
364-
property.Parameters.Length == 0 &&
365-
property.IsWritableInConstructor() &&
366-
IsConversionImplicit(document.SemanticModel.Compilation, parameterType, property.Type);
363+
if (!IsConversionImplicit(document.SemanticModel.Compilation, parameterType, property.Type))
364+
return false;
365+
366+
if (property.IsWritableInConstructor())
367+
return true;
368+
369+
var service = document.Document.GetRequiredLanguageService<IInitializeParameterService>();
370+
return service.IsThrowNotImplementedProperty(document.SemanticModel.Compilation, property, cancellationToken);
367371
}
368372
}
369373

src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2630,9 +2630,8 @@ protected virtual AssemblySymbol GetForwardedToAssemblyInUsingNamespaces(string
26302630
return Next?.GetForwardedToAssemblyInUsingNamespaces(metadataName, ref qualifierOpt, diagnostics, location);
26312631
}
26322632

2633-
protected AssemblySymbol GetForwardedToAssembly(string fullName, BindingDiagnosticBag diagnostics, Location location)
2633+
protected AssemblySymbol GetForwardedToAssembly(MetadataTypeName metadataName, BindingDiagnosticBag diagnostics, Location location)
26342634
{
2635-
var metadataName = MetadataTypeName.FromFullName(fullName);
26362635
foreach (var referencedAssembly in
26372636
Compilation.Assembly.Modules[0].GetReferencedAssemblySymbols())
26382637
{
@@ -2647,7 +2646,7 @@ protected AssemblySymbol GetForwardedToAssembly(string fullName, BindingDiagnost
26472646
if (diagInfo.Code == (int)ErrorCode.ERR_CycleInTypeForwarder)
26482647
{
26492648
Debug.Assert((object)forwardedType.ContainingAssembly != null, "How did we find a cycle if there was no forwarding?");
2650-
diagnostics.Add(ErrorCode.ERR_CycleInTypeForwarder, location, fullName, forwardedType.ContainingAssembly.Name);
2649+
diagnostics.Add(ErrorCode.ERR_CycleInTypeForwarder, location, metadataName.FullName, forwardedType.ContainingAssembly.Name);
26512650
}
26522651
else if (diagInfo.Code == (int)ErrorCode.ERR_TypeForwardedToMultipleAssemblies)
26532652
{
@@ -2717,7 +2716,10 @@ protected AssemblySymbol GetForwardedToAssembly(string name, int arity, ref Name
27172716
// File types can't be forwarded, so we won't attempt to determine a file identifier to attach to the metadata name.
27182717
var metadataName = MetadataHelpers.ComposeAritySuffixedMetadataName(name, arity, associatedFileIdentifier: null);
27192718
var fullMetadataName = MetadataHelpers.BuildQualifiedName(qualifierOpt?.ToDisplayString(SymbolDisplayFormat.QualifiedNameOnlyFormat), metadataName);
2720-
var result = GetForwardedToAssembly(fullMetadataName, diagnostics, location);
2719+
var result = GetForwardedToAssembly(
2720+
MetadataTypeName.FromFullName(fullMetadataName),
2721+
diagnostics,
2722+
location);
27212723
if ((object)result != null)
27222724
{
27232725
return result;

src/Compilers/CSharp/Portable/Binder/WithUsingNamespacesAndTypesBinder.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,10 @@ protected WithUsingNamespacesAndTypesBinder(Binder next, bool withImportChainEnt
5252
{
5353
foreach (var typeOrNamespace in GetUsings(basesBeingResolved: null))
5454
{
55-
var fullName = typeOrNamespace.NamespaceOrType + "." + name;
56-
var result = GetForwardedToAssembly(fullName, diagnostics, location);
55+
var result = GetForwardedToAssembly(
56+
MetadataTypeName.FromNamespaceAndTypeName(typeOrNamespace.NamespaceOrType.ToString(), name),
57+
diagnostics,
58+
location);
5759
if (result != null)
5860
{
5961
qualifierOpt = typeOrNamespace.NamespaceOrType;

src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/InitializeMemberFromParameterTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public partial class InitializeMemberFromParameterTests : AbstractCSharpCodeActi
2121
protected override CodeRefactoringProvider CreateCodeRefactoringProvider(EditorTestWorkspace workspace, TestParameters parameters)
2222
=> new CSharpInitializeMemberFromParameterCodeRefactoringProvider();
2323

24-
private readonly NamingStylesTestOptionSets options = new NamingStylesTestOptionSets(LanguageNames.CSharp);
24+
private readonly NamingStylesTestOptionSets options = new(LanguageNames.CSharp);
2525

2626
[Fact]
2727
public async Task TestInitializeFieldWithSameName()

src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.Analyzer.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ public static AnalyzerResult Analyze(CSharpSelectionResult selectionResult, bool
2525
protected override bool TreatOutAsRef
2626
=> false;
2727

28+
protected override bool IsInPrimaryConstructorBaseType()
29+
=> this.SelectionResult.GetContainingScopeOf<PrimaryConstructorBaseTypeSyntax>() != null;
30+
2831
protected override VariableInfo CreateFromSymbol(
2932
Compilation compilation,
3033
ISymbol symbol,

src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.CallSiteContainerRewriter.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,15 @@ public override SyntaxNode VisitCompilationUnit(CompilationUnitSyntax node)
426426
return node.WithMembers(newMembers);
427427
}
428428

429+
public override SyntaxNode VisitBaseList(BaseListSyntax node)
430+
{
431+
if (node != ContainerOfStatementsOrFieldToReplace)
432+
return base.VisitBaseList(node);
433+
434+
var primaryConstructorBase = (PrimaryConstructorBaseTypeSyntax)_statementsOrMemberOrAccessorToInsert.Single();
435+
return node.WithTypes(node.Types.Replace(node.Types[0], primaryConstructorBase));
436+
}
437+
429438
private SyntaxNode GetUpdatedTypeDeclaration(TypeDeclarationSyntax node)
430439
{
431440
var newMembers = VisitList(ReplaceMembers(node.Members, global: false));

src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.ExpressionCodeGenerator.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,9 @@ protected override SyntaxNode GetFirstStatementOrInitializerSelectedAtCallSite()
136136

137137
// This is similar to FieldDeclaration case but we only want to do this
138138
// if the member has an expression body.
139-
scope ??= this.SelectionResult.GetContainingScopeOf<ArrowExpressionClauseSyntax>().Parent;
139+
scope ??= this.SelectionResult.GetContainingScopeOf<ArrowExpressionClauseSyntax>()?.Parent;
140+
141+
scope ??= this.SelectionResult.GetContainingScopeOf<PrimaryConstructorBaseTypeSyntax>();
140142

141143
return scope;
142144
}

0 commit comments

Comments
 (0)