Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,13 @@ internal sealed partial class CSharpIntroduceParameterCodeRefactoringProvider()
ArgumentSyntax>
{
protected override SyntaxNode GenerateExpressionFromOptionalParameter(IParameterSymbol parameterSymbol)
{
return ExpressionGenerator.GenerateExpression(parameterSymbol.Type, parameterSymbol.ExplicitDefaultValue, canUseFieldReference: true);
}
=> ExpressionGenerator.GenerateExpression(parameterSymbol.Type, parameterSymbol.ExplicitDefaultValue, canUseFieldReference: true);

protected override SyntaxNode? GetLocalDeclarationFromDeclarator(SyntaxNode variableDecl)
{
return variableDecl.Parent?.Parent as LocalDeclarationStatementSyntax;
}
=> variableDecl.Parent?.Parent as LocalDeclarationStatementSyntax;

protected override bool IsDestructor(IMethodSymbol methodSymbol)
{
return false;
}
=> false;

protected override SyntaxNode UpdateArgumentListSyntax(SyntaxNode argumentList, SeparatedSyntaxList<ArgumentSyntax> arguments)
=> ((ArgumentListSyntax)argumentList).WithArguments(arguments);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.IntroduceParameter;
Expand Down Expand Up @@ -1818,4 +1819,59 @@ void M()
}
}
""");

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81013")]
public Task TestNotOnAnonymousObjectMemberName()
=> TestMissingInRegularAndScriptAsync(
"""
class C
{
object M() => new
{
[|a|] = new { },
};
}
""");

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81013")]
public Task TestNotOnAnonymousObjectMemberNameWithValue()
=> TestMissingInRegularAndScriptAsync(
"""
class C
{
object M() => new
{
[|x|] = 5,
};
}
""");

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81013")]
public Task TestNotOnObjectInitializerMemberName()
=> TestMissingInRegularAndScriptAsync(
"""
class Goo { public int X { get; set; } }
class C
{
Goo M() => new Goo
{
[|X|] = 5,
};
}
""");

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81013")]
public Task TestNotOnObjectInitializerMemberNameNested()
=> TestMissingInRegularAndScriptAsync(
"""
class Goo { public int X { get; set; } public Goo Inner { get; set; } }
class C
{
Goo M() => new Goo
{
[|X|] = 5,
Inner = new Goo { X = 10 }
};
}
""");
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,16 @@ private static bool IsValidExpression(SyntaxNode expression, ISyntaxFactsService
if (syntaxFacts.IsNameOfAnyMemberAccessExpression(expression))
return false;

// Need to special case for the left-hand side of member initializers in regular objects (e.g., 'X' in 'new Foo { X = ... }')
// because it does not make sense to introduce a parameter for the property/member name itself.
if (syntaxFacts.IsMemberInitializerNamedAssignmentIdentifier(expression, out _))
return false;

// Need to special case for the left-hand side of member initializers in anonymous objects (e.g., 'a' in 'new { a = ... }').
// This checks if the expression is the name identifier in an anonymous object member declarator.
if (syntaxFacts.IsAnonymousObjectMemberDeclaratorNameIdentifier(expression))
return false;

// Need to special case for expressions that are contained within a parameter or attribute argument
// because it is technically "contained" within a method, but does not make
// sense to introduce.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,10 @@ public bool IsMemberInitializerNamedAssignmentIdentifier(
return false;
}

public bool IsAnonymousObjectMemberDeclaratorNameIdentifier([NotNullWhen(true)] SyntaxNode? expression)
=> expression is IdentifierNameSyntax { Parent: NameEqualsSyntax { Parent: AnonymousObjectMemberDeclaratorSyntax } nameEquals } identifier &&
nameEquals.Name == identifier;

public bool IsAnyInitializerExpression([NotNullWhen(true)] SyntaxNode? node, [NotNullWhen(true)] out SyntaxNode? creationExpression)
{
if (node is InitializerExpressionSyntax
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ void GetPartsOfTupleExpression<TArgumentSyntax>(SyntaxNode node,

bool IsAttributeNamedArgumentIdentifier([NotNullWhen(true)] SyntaxNode? node);
bool IsMemberInitializerNamedAssignmentIdentifier([NotNullWhen(true)] SyntaxNode? node, [NotNullWhen(true)] out SyntaxNode? initializedInstance);
bool IsAnonymousObjectMemberDeclaratorNameIdentifier([NotNullWhen(true)] SyntaxNode? node);
bool IsAnyInitializerExpression([NotNullWhen(true)] SyntaxNode? node, [NotNullWhen(true)] out SyntaxNode? creationExpression);

bool IsDirective([NotNullWhen(true)] SyntaxNode? node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService
Return False
End Function

Public Function IsAnonymousObjectMemberDeclaratorNameIdentifier(expression As SyntaxNode) As Boolean Implements ISyntaxFacts.IsAnonymousObjectMemberDeclaratorNameIdentifier
Dim identifier = TryCast(expression, IdentifierNameSyntax)
Dim namedFieldInit = TryCast(identifier?.Parent, NamedFieldInitializerSyntax)

Return TypeOf namedFieldInit?.Parent Is AnonymousObjectCreationExpressionSyntax AndAlso
namedFieldInit.Name Is identifier
End Function

Public Function IsAnyInitializerExpression(node As SyntaxNode, ByRef creationExpression As SyntaxNode) As Boolean Implements ISyntaxFacts.IsAnyInitializerExpression
If TypeOf node Is CollectionInitializerSyntax Then
If TypeOf node.Parent Is ArrayCreationExpressionSyntax Then
Expand Down
Loading