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)
{