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

[System.Text.Json] Improve error handling of ref structs, including collection types with ref struct elements. #106083

Merged
merged 4 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
82 changes: 41 additions & 41 deletions src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1157,11 +1157,13 @@ private bool IsValidDataExtensionPropertyType(ITypeSymbol type)
}

if ((!canUseGetter && !canUseSetter && !hasJsonIncludeButIsInaccessible) ||
!IsSymbolAccessibleWithin(memberType, within: contextType))
!IsSymbolAccessibleWithin(memberType, within: contextType) ||
(memberType.IsRefLikeType && ignoreCondition is JsonIgnoreCondition.Always))
{
// Skip the member if either of the two conditions hold
// 1. Member has no accessible getters or setters (but is not marked with JsonIncludeAttribute since we need to throw a runtime exception) OR
// 2. The member type is not accessible within the generated context.
// 2. The member type is not accessible within the generated context OR
eiriktsarpalis marked this conversation as resolved.
Show resolved Hide resolved
// 3. The member is of a ref-like type that is also ignored (we keep all other ignored members for backward compat)
eiriktsarpalis marked this conversation as resolved.
Show resolved Hide resolved
return null;
}

Expand Down Expand Up @@ -1246,62 +1248,60 @@ private void ProcessMemberCustomAttributes(
switch (attributeType.ToDisplayString())
{
case JsonIgnoreAttributeFullName:
{
eiriktsarpalis marked this conversation as resolved.
Show resolved Hide resolved
ImmutableArray<KeyValuePair<string, TypedConstant>> namedArgs = attributeData.NamedArguments;

if (namedArgs.Length == 0)
{
ImmutableArray<KeyValuePair<string, TypedConstant>> namedArgs = attributeData.NamedArguments;

if (namedArgs.Length == 0)
{
ignoreCondition = JsonIgnoreCondition.Always;
}
else if (namedArgs.Length == 1 &&
namedArgs[0].Value.Type?.ToDisplayString() == JsonIgnoreConditionFullName)
{
ignoreCondition = (JsonIgnoreCondition)namedArgs[0].Value.Value!;
}
ignoreCondition = JsonIgnoreCondition.Always;
}
break;
case JsonIncludeAttributeFullName:
else if (namedArgs.Length == 1 &&
namedArgs[0].Value.Type?.ToDisplayString() == JsonIgnoreConditionFullName)
{
hasJsonInclude = true;
ignoreCondition = (JsonIgnoreCondition)namedArgs[0].Value.Value!;
}
break;
}
case JsonIncludeAttributeFullName:
{
hasJsonInclude = true;
break;
}
case JsonNumberHandlingAttributeFullName:
{
ImmutableArray<TypedConstant> ctorArgs = attributeData.ConstructorArguments;
numberHandling = (JsonNumberHandling)ctorArgs[0].Value!;
}
{
ImmutableArray<TypedConstant> ctorArgs = attributeData.ConstructorArguments;
numberHandling = (JsonNumberHandling)ctorArgs[0].Value!;
break;
}
case JsonObjectCreationHandlingAttributeFullName:
{
ImmutableArray<TypedConstant> ctorArgs = attributeData.ConstructorArguments;
objectCreationHandling = (JsonObjectCreationHandling)ctorArgs[0].Value!;
}
{
ImmutableArray<TypedConstant> ctorArgs = attributeData.ConstructorArguments;
objectCreationHandling = (JsonObjectCreationHandling)ctorArgs[0].Value!;
break;
}
case JsonPropertyNameAttributeFullName:
{
ImmutableArray<TypedConstant> ctorArgs = attributeData.ConstructorArguments;
jsonPropertyName = (string)ctorArgs[0].Value!;
// Null check here is done at runtime within JsonSerializer.
}
{
ImmutableArray<TypedConstant> ctorArgs = attributeData.ConstructorArguments;
jsonPropertyName = (string)ctorArgs[0].Value!;
// Null check here is done at runtime within JsonSerializer.
break;
}
case JsonPropertyOrderAttributeFullName:
{
ImmutableArray<TypedConstant> ctorArgs = attributeData.ConstructorArguments;
order = (int)ctorArgs[0].Value!;
}
{
ImmutableArray<TypedConstant> ctorArgs = attributeData.ConstructorArguments;
order = (int)ctorArgs[0].Value!;
break;
}
case JsonExtensionDataAttributeFullName:
{
isExtensionData = true;
}
{
isExtensionData = true;
break;
}
case JsonRequiredAttributeFullName:
{
hasJsonRequiredAttribute = true;
}
break;
default:
{
hasJsonRequiredAttribute = true;
break;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -858,5 +858,30 @@ internal partial class ModelContext : JsonSerializerContext
Compilation compilation = CompilationHelper.CreateCompilation(source);
CompilationHelper.RunJsonSourceGenerator(compilation);
}

[Fact]
public void RefStructPropertyWithJsonIgnore_CompilesSuccessfully()
{
// Regression test for https://github.com/dotnet/runtime/issues/98590

string source = """
using System;
using System.Text.Json.Serialization;

public class MyPoco
{
[JsonIgnore]
public ReadOnlySpan<char> Values => "abc".AsSpan();
}

[JsonSerializable(typeof(MyPoco))]
public partial class MyContext : JsonSerializerContext
{
}
""";

Compilation compilation = CompilationHelper.CreateCompilation(source);
CompilationHelper.RunJsonSourceGenerator(compilation);
}
}
}
Loading