Skip to content

Commit 0081d68

Browse files
authored
Always use dot separators in XAML SG converters (#31225)
We did parse the values with invariant culture, but didn't take that into account when writing the values. This could cause decimal/double values to be written with comma's, which in turn causes invalid syntax. This change makes sure that we always use the dot separator. Fixes #31142
2 parents f37cc20 + e54e40e commit 0081d68

File tree

3 files changed

+325
-17
lines changed

3 files changed

+325
-17
lines changed

src/Controls/src/SourceGen/GeneratorHelpers.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,4 +420,19 @@ static void SimplifyOnPlatform(XmlNode node, string? targetFramework, XmlNamespa
420420
}
421421
return null;
422422
}
423+
424+
/// <summary>
425+
/// Formats a value as a culture-independent C# literal for source generation.
426+
/// Uses SymbolDisplay.FormatPrimitive to ensure proper handling of special values like NaN and Infinity
427+
/// but also numeric types and makes sure they are formatted correctly.
428+
/// </summary>
429+
/// <param name="value">The value to format</param>
430+
/// <param name="quoted">Whether to include quotes around the formatted value</param>
431+
/// <returns>A culture-independent string representation suitable for source generation</returns>
432+
public static string FormatInvariant(object value, bool quoted = false)
433+
{
434+
return SymbolDisplay.FormatPrimitive(value, quoteStrings: quoted, useHexadecimalNumbers: false);
435+
}
436+
437+
423438
}

src/Controls/src/SourceGen/KnownTypeConverters.cs

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ namespace Microsoft.Maui.Controls.SourceGen;
1313
using Microsoft.CodeAnalysis.CSharp;
1414
using Microsoft.Maui.Controls.Xaml;
1515
using static LocationHelpers;
16+
using static GeneratorHelpers;
1617

1718
static class KnownTypeConverters
1819
{
@@ -221,7 +222,7 @@ public static string ConvertRect(string value, BaseNode node, ITypeSymbol toType
221222
&& double.TryParse(xywh[2], NumberStyles.Number, CultureInfo.InvariantCulture, out double w)
222223
&& double.TryParse(xywh[3], NumberStyles.Number, CultureInfo.InvariantCulture, out double h))
223224
{
224-
return $"new global::Microsoft.Maui.Graphics.Rect({x}, {y}, {w}, {h})";
225+
return $"new global::Microsoft.Maui.Graphics.Rect({FormatInvariant(x)}, {FormatInvariant(y)}, {FormatInvariant(w)}, {FormatInvariant(h)})";
225226
}
226227
}
227228

@@ -273,7 +274,7 @@ public static string ConvertPoint(string value, BaseNode node, ITypeSymbol toTyp
273274
if (xy.Length == 2 && double.TryParse(xy[0], NumberStyles.Number, CultureInfo.InvariantCulture, out var x)
274275
&& double.TryParse(xy[1], NumberStyles.Number, CultureInfo.InvariantCulture, out var y))
275276
{
276-
return $"new global::Microsoft.Maui.Graphics.Point({x}, {y})";
277+
return $"new global::Microsoft.Maui.Graphics.Point({FormatInvariant(x)}, {FormatInvariant(y)})";
277278
}
278279
}
279280

@@ -298,21 +299,21 @@ public static string ConvertThickness(string value, BaseNode node, ITypeSymbol t
298299
case 2:
299300
if (double.TryParse(thickness[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double h)
300301
&& double.TryParse(thickness[1], NumberStyles.Number, CultureInfo.InvariantCulture, out double v))
301-
return $"new global::Microsoft.Maui.Thickness({h}, {v})";
302+
return $"new global::Microsoft.Maui.Thickness({FormatInvariant(h)}, {FormatInvariant(v)})";
302303
break;
303304
case 4:
304305
if (double.TryParse(thickness[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double l)
305306
&& double.TryParse(thickness[1], NumberStyles.Number, CultureInfo.InvariantCulture, out double t)
306307
&& double.TryParse(thickness[2], NumberStyles.Number, CultureInfo.InvariantCulture, out double r)
307308
&& double.TryParse(thickness[3], NumberStyles.Number, CultureInfo.InvariantCulture, out double b))
308-
return $"new global::Microsoft.Maui.Thickness({l}, {t}, {r}, {b})";
309+
return $"new global::Microsoft.Maui.Thickness({FormatInvariant(l)}, {FormatInvariant(t)}, {FormatInvariant(r)}, {FormatInvariant(b)})";
309310
break;
310311
}
311312
}
312313
else
313314
{ //single uniform thickness
314315
if (double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out double l))
315-
return $"new global::Microsoft.Maui.Thickness({l})";
316+
return $"new global::Microsoft.Maui.Thickness({FormatInvariant(l)})";
316317
}
317318
}
318319

@@ -337,17 +338,17 @@ public static string ConvertCornerRadius(string value, BaseNode node, ITypeSymbo
337338
&& double.TryParse(cornerRadius[1], NumberStyles.Number, CultureInfo.InvariantCulture, out double tr)
338339
&& double.TryParse(cornerRadius[2], NumberStyles.Number, CultureInfo.InvariantCulture, out double bl)
339340
&& double.TryParse(cornerRadius[3], NumberStyles.Number, CultureInfo.InvariantCulture, out double br))
340-
return $"new global::Microsoft.Maui.CornerRadius({tl}, {tr}, {bl}, {br})";
341+
return $"new global::Microsoft.Maui.CornerRadius({FormatInvariant(tl)}, {FormatInvariant(tr)}, {FormatInvariant(bl)}, {FormatInvariant(br)})";
341342

342343
if (cornerRadius.Length > 1
343344
&& cornerRadius.Length < 4
344345
&& double.TryParse(cornerRadius[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double l))
345-
return $"new global::Microsoft.Maui.CornerRadius({l})";
346+
return $"new global::Microsoft.Maui.CornerRadius({FormatInvariant(l)})";
346347
}
347348
else
348349
{ //single uniform CornerRadius
349350
if (double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out double l))
350-
return $"new global::Microsoft.Maui.CornerRadius({l})";
351+
return $"new global::Microsoft.Maui.CornerRadius({FormatInvariant(l)})";
351352
}
352353
}
353354

@@ -413,10 +414,10 @@ public static string ConvertFlexBasis(string value, BaseNode node, ITypeSymbol t
413414

414415
if (value.EndsWith("%", StringComparison.OrdinalIgnoreCase)
415416
&& float.TryParse(value.Substring(0, value.Length - 1), NumberStyles.Number, CultureInfo.InvariantCulture, out float relflex))
416-
return $"new global::Microsoft.Maui.Layouts.FlexBasis({relflex / 100}, true)";
417+
return $"new global::Microsoft.Maui.Layouts.FlexBasis({FormatInvariant(relflex / 100)}, true)";
417418

418419
if (float.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out float flex))
419-
return $"new global::Microsoft.Maui.Layouts.FlexBasis({flex}, false)";
420+
return $"new global::Microsoft.Maui.Layouts.FlexBasis({FormatInvariant(flex)}, false)";
420421
}
421422

422423
context.ReportDiagnostic(Diagnostic.Create(Descriptors.FlexBasisConversionFailed, LocationCreate(context.FilePath!, xmlLineInfo, value), value));
@@ -431,7 +432,7 @@ public static string ConvertFontSize(string value, BaseNode node, ITypeSymbol to
431432
{
432433
value = value.Trim();
433434
if (double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out double size))
434-
return $"{SymbolDisplay.FormatPrimitive(size, true, false)}D";
435+
return $"{FormatInvariant(size, quoted: true)}D";
435436

436437

437438
var namedSizeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Maui.Controls.NamedSize")!;
@@ -490,7 +491,7 @@ public static string ConvertGridLength(string value, BaseNode node, ITypeSymbol
490491
{
491492
if (double.TryParse(value.Substring(0, value.Length - 1), NumberStyles.Number, CultureInfo.InvariantCulture, out double val))
492493
{
493-
return $"new global::Microsoft.Maui.GridLength({val}, global::Microsoft.Maui.GridUnitType.Star)";
494+
return $"new global::Microsoft.Maui.GridLength({FormatInvariant(val)}, global::Microsoft.Maui.GridUnitType.Star)";
494495
}
495496
}
496497
else if (value.Equals("Auto", StringComparison.OrdinalIgnoreCase))
@@ -499,7 +500,7 @@ public static string ConvertGridLength(string value, BaseNode node, ITypeSymbol
499500
}
500501
else if (double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out double val))
501502
{
502-
return $"new global::Microsoft.Maui.GridLength({val}, global::Microsoft.Maui.GridUnitType.Absolute)";
503+
return $"new global::Microsoft.Maui.GridLength({FormatInvariant(val)}, global::Microsoft.Maui.GridUnitType.Absolute)";
503504
}
504505
}
505506

@@ -606,7 +607,7 @@ internal static string ConvertPointCollection(string value, BaseNode node, IType
606607
}
607608
else
608609
{
609-
pointCollection.Add(ConvertPoint($"{x},{number}", node, toType, context));
610+
pointCollection.Add(ConvertPoint($"{FormatInvariant(x)},{FormatInvariant(number)}", node, toType, context));
610611
hasX = false;
611612
}
612613
}
@@ -678,14 +679,14 @@ internal static string ConvertStrokeShape(string value, BaseNode node, ITypeSymb
678679
if (coordinates.Length == 2 && double.TryParse(coordinates[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double x1)
679680
&& double.TryParse(coordinates[1], NumberStyles.Number, CultureInfo.InvariantCulture, out double y1))
680681
{
681-
return $"new global::Microsoft.Maui.Controls.Shapes.Line {{ X1 = {x1}, Y1 = {y1} }}";
682+
return $"new global::Microsoft.Maui.Controls.Shapes.Line {{ X1 = {FormatInvariant(x1)}, Y1 = {FormatInvariant(y1)} }}";
682683
}
683684
else if (coordinates.Length == 4 && double.TryParse(coordinates[0], NumberStyles.Number, CultureInfo.InvariantCulture, out x1)
684685
&& double.TryParse(coordinates[1], NumberStyles.Number, CultureInfo.InvariantCulture, out y1)
685686
&& double.TryParse(coordinates[2], NumberStyles.Number, CultureInfo.InvariantCulture, out double x2)
686687
&& double.TryParse(coordinates[3], NumberStyles.Number, CultureInfo.InvariantCulture, out double y2))
687688
{
688-
return $"new global::Microsoft.Maui.Controls.Shapes.Line {{ X1 = {x1}, Y1 = {y1}, X2 = {x2}, Y2 = {y2} }}";
689+
return $"new global::Microsoft.Maui.Controls.Shapes.Line {{ X1 = {FormatInvariant(x1)}, Y1 = {FormatInvariant(y1)}, X2 = {FormatInvariant(x2)}, Y2 = {FormatInvariant(y2)} }}";
689690
}
690691
}
691692

@@ -822,7 +823,7 @@ internal static string ConvertConstraint(string value, BaseNode node, ITypeSymbo
822823
value = value.Trim();
823824

824825
if (double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out var size))
825-
return $"global::Microsoft.Maui.Controls.Compatibility.Constraint.Constant({size})";
826+
return $"global::Microsoft.Maui.Controls.Compatibility.Constraint.Constant({FormatInvariant(size)})";
826827
}
827828

828829
#pragma warning disable RS0030 // Do not use banned APIs

0 commit comments

Comments
 (0)