Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restrict some types in object initializer setters when not directly settable #75507

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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 @@ -998,7 +998,7 @@ public static T Create<T>()
}
""";

await VerifyItemExistsAsync(markup, "Target");
await VerifyItemIsAbsentAsync(markup, "Target");
await VerifyItemExistsAsync(markup, "Method");
}

Expand All @@ -1022,6 +1022,74 @@ public static T Create<T>()
await VerifyNoItemsExistAsync(markup);
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74331")]
public async Task ObjectInitializerOnVariousMembers_01()
{
var markup = """
using System.Collections;
using System.Collections.Generic;

internal class Example
{
public object ObjectProp { get; }
public double DoubleProp { get; }
public string StringProp { get; } = "some name";
public System.Enum EnumProp { get; }
public System.Array ArrayProp { get; }
public void* PointerProp { get; }
public delegate*<void> FunctionPointerProp { get; }
public IEnumerable EnumerableProp { get; }
public IEnumerable<string> StringEnumerableProp { get; }
public IEnumerator EnumeratorProp { get; }
public IEnumerator<string> StringEnumeratorProp { get; }

public static Example Create()
{
return new()
{
$$
};
}
}
""";

await VerifyNoItemsExistAsync(markup);
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74331")]
public async Task ObjectInitializerOnVariousMembers_02()
{
var markup = """
using System.Collections;
using System.Collections.Generic;

internal class Example
{
public readonly object ObjectField;
public readonly double DoubleField;
public readonly string StringField = "some name";
public readonly System.Enum EnumField;
public System.Array ArrayField { get; }
public readonly void* PointerField;
public readonly delegate*<void> FunctionPointerField;
public readonly IEnumerable EnumerableField;
public readonly IEnumerable<string> StringEnumerableField;
public readonly IEnumerator EnumeratorField;
public readonly IEnumerator<string> StringEnumeratorField;

public static Example Create()
{
return new()
{
$$
};
}
}
""";

await VerifyNoItemsExistAsync(markup);
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26560")]
public async Task ObjectInitializerEscapeKeywords()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,60 @@ private static bool CanSupportObjectInitializer(ISymbol symbol)

if (symbol is IFieldSymbol fieldSymbol)
{
return !fieldSymbol.Type.IsStructType();
return MemberTypeCanSupportObjectInitializer(fieldSymbol.Type);
}
else if (symbol is IPropertySymbol propertySymbol)
{
return !propertySymbol.Type.IsStructType();
return MemberTypeCanSupportObjectInitializer(propertySymbol.Type);
}

throw ExceptionUtilities.Unreachable();
}

private static bool MemberTypeCanSupportObjectInitializer(ITypeSymbol type)
{
// NOTE: While in C# it is legal to write 'Member = {}' on a member of any of
// the ruled out types below, it has no effects and is thus a needless recommendation

// We avoid some types that are common and easy to rule out
switch (type.SpecialType)
{
case SpecialType.System_Enum:
case SpecialType.System_String:
case SpecialType.System_Object:
case SpecialType.System_Delegate:
case SpecialType.System_MulticastDelegate:

// We cannot use collection initializers in Array members,
// but for members of an array type with a typed rank we can
// For example, assuming Array2D is int[,]:
// Array2D = { [0, 0] = value, [0, 1] = value1 },
case SpecialType.System_Array:

// We cannot add to an enumerable or enumerator
// so we cannot use a collection initializer
case SpecialType.System_Collections_IEnumerable:
case SpecialType.System_Collections_IEnumerator:
return false;
}

if (type is INamedTypeSymbol { IsGenericType: true } named)
{
var definition = named.OriginalDefinition;
switch (definition.SpecialType)
{
case SpecialType.System_Collections_Generic_IEnumerable_T:
case SpecialType.System_Collections_Generic_IEnumerator_T:
Copy link
Member

Choose a reason for hiding this comment

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

these need explanations.

return false;
}
}

// - Delegate types have no settable members, which is the case for Delegate and MulticastDelegate too
// - Non-settable struct members cannot be used in object initializers
// - Pointers and function pointers do not have accessible members
return !type.IsDelegateType()
&& !type.IsStructType()
&& !type.IsFunctionPointerType()
&& type.TypeKind != TypeKind.Pointer;
Copy link
Member

Choose a reason for hiding this comment

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

these need explanations.

}
}
Loading