From b6e55eafc3b0d63bd2148464b900f6c863f291e7 Mon Sep 17 00:00:00 2001 From: ds5678 <49847914+ds5678@users.noreply.github.com> Date: Sat, 26 Apr 2025 20:58:58 -0700 Subject: [PATCH] Parenthesize interpolations containing global:: --- .../ICSharpCode.Decompiler.Tests.csproj | 1 + .../PrettyTestRunner.cs | 13 ++++++++++++ ...ballyQualifiedTypeInStringInterpolation.cs | 7 +++++++ .../OutputVisitor/InsertParenthesesVisitor.cs | 20 +++++++++++++++++++ 4 files changed, 41 insertions(+) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/GloballyQualifiedTypeInStringInterpolation.cs diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index e6053e558..5a89e7178 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -137,6 +137,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 9b4184aa7..b528cd65b 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -308,6 +308,19 @@ namespace ICSharpCode.Decompiler.Tests await RunForLibrary(cscOptions: cscOptions); } + [Test] + public async Task GloballyQualifiedTypeInStringInterpolation([ValueSource(nameof(roslynOnlyWithNet40Options))] CompilerOptions cscOptions) + { + // https://github.com/icsharpcode/ILSpy/issues/3447 + await RunForLibrary( + cscOptions: cscOptions, + configureDecompiler: settings => { + settings.UsingDeclarations = false; + settings.AlwaysUseGlobal = true; + } + ); + } + [Test] public async Task LiftedOperators([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/GloballyQualifiedTypeInStringInterpolation.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/GloballyQualifiedTypeInStringInterpolation.cs new file mode 100644 index 000000000..d15031502 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/GloballyQualifiedTypeInStringInterpolation.cs @@ -0,0 +1,7 @@ +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + public static class GloballyQualifiedTypeInStringInterpolation + { + public static string CurrentDateTime => $"Time: {(global::System.DateTime.Now)}"; + } +} diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs index 2fb3a4546..2c67b55a5 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs @@ -389,6 +389,26 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor base.VisitAsExpression(asExpression); } + public override void VisitInterpolation(Interpolation interpolation) + { + // If an interpolation contains global::, we need to parenthesize the expression. + if (IsThisOrChildMemberTypeWithDoubleColon(interpolation)) + Parenthesize(interpolation.Expression); + base.VisitInterpolation(interpolation); + + static bool IsThisOrChildMemberTypeWithDoubleColon(AstNode node) + { + if (node is MemberType { IsDoubleColon: true }) + return true; + foreach (var child in node.Children) + { + if (IsThisOrChildMemberTypeWithDoubleColon(child)) + return true; + } + return false; + } + } + // Conditional operator public override void VisitConditionalExpression(ConditionalExpression conditionalExpression) {