Fully qualify C# default values in exported fields.

This avoids issues when the default values rely on using
namespaces.
This commit is contained in:
R. Alex Hofer 2022-11-11 22:34:43 -05:00
parent c17f17eb98
commit 6ab93bd919
4 changed files with 83 additions and 2 deletions

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
#pragma warning disable CS0169
@ -83,6 +84,10 @@ namespace Godot.SourceGenerators.Sample
[Export] private StringName[] field_StringNameArray = { "foo", "bar" };
[Export] private NodePath[] field_NodePathArray = { "foo", "bar" };
[Export] private RID[] field_RIDArray = { default, default, default };
// Note we use Array and not System.Array. This tests the generated namespace qualification.
[Export] private Int32[] field_empty_Int32Array = Array.Empty<Int32>();
// Note we use List and not System.Collections.Generic.
[Export] private int[] field_array_from_list = new List<int>(Array.Empty<int>()).ToArray();
// Variant
[Export] private Variant field_Variant = "foo";

View file

@ -0,0 +1,19 @@
using System;
using System.Diagnostics.CodeAnalysis;
#pragma warning disable CS0169
#pragma warning disable CS0414
namespace Godot.SourceGenerators.Sample
{
[SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle")]
[SuppressMessage("ReSharper", "RedundantNameQualifier")]
[SuppressMessage("ReSharper", "ArrangeObjectCreationWhenTypeEvident")]
[SuppressMessage("ReSharper", "InconsistentNaming")]
// We split the definition of ExportedFields to verify properties work across multiple files.
public partial class ExportedFields : Godot.Object
{
// Note we use Array and not System.Array. This tests the generated namespace qualification.
[Export] private Int64[] field_empty_Int64Array = Array.Empty<Int64>();
}
}

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@ -165,6 +166,50 @@ namespace Godot.SourceGenerators
public static string FullQualifiedName(this INamespaceSymbol namespaceSymbol)
=> namespaceSymbol.ToDisplayString(FullyQualifiedFormatOmitGlobal);
public static string FullQualifiedName(this ISymbol symbol)
=> symbol.ToDisplayString(FullyQualifiedFormatOmitGlobal);
public static string FullQualifiedSyntax(this SyntaxNode node, SemanticModel sm)
{
StringBuilder sb = new();
FullQualifiedSyntax_(node, sm, sb, true);
return sb.ToString();
}
private static void FullQualifiedSyntax_(SyntaxNode node, SemanticModel sm, StringBuilder sb, bool isFirstNode)
{
if (node is NameSyntax ns && isFirstNode)
{
SymbolInfo nameInfo = sm.GetSymbolInfo(ns);
sb.Append(nameInfo.Symbol?.FullQualifiedName() ?? ns.ToString());
return;
}
bool innerIsFirstNode = true;
foreach (var child in node.ChildNodesAndTokens())
{
if (child.HasLeadingTrivia)
{
sb.Append(child.GetLeadingTrivia());
}
if (child.IsNode)
{
FullQualifiedSyntax_(child.AsNode()!, sm, sb, isFirstNode: innerIsFirstNode);
innerIsFirstNode = false;
}
else
{
sb.Append(child);
}
if (child.HasTrailingTrivia)
{
sb.Append(child.GetTrailingTrivia());
}
}
}
public static string SanitizeQualifiedNameForUniqueHint(this string qualifiedName)
=> qualifiedName
// AddSource() doesn't support angle brackets

View file

@ -170,7 +170,13 @@ namespace Godot.SourceGenerators
.Select(s => s?.Initializer ?? null)
.FirstOrDefault();
string? value = initializer?.Value.ToString();
// Fully qualify the value to avoid issues with namespaces.
string? value = null;
if (initializer != null)
{
var sm = context.Compilation.GetSemanticModel(initializer.SyntaxTree);
value = initializer.Value.FullQualifiedSyntax(sm);
}
exportedMembers.Add(new ExportedPropertyMetadata(
property.Name, marshalType.Value, propertyType, value));
@ -207,7 +213,13 @@ namespace Godot.SourceGenerators
.Select(s => s.Initializer)
.FirstOrDefault(i => i != null);
string? value = initializer?.Value.ToString();
// This needs to be fully qualified to avoid issues with namespaces.
string? value = null;
if (initializer != null)
{
var sm = context.Compilation.GetSemanticModel(initializer.SyntaxTree);
value = initializer.Value.FullQualifiedSyntax(sm);
}
exportedMembers.Add(new ExportedPropertyMetadata(
field.Name, marshalType.Value, fieldType, value));