Browse Source

Parenthesize interpolations containing `global::` (#3463)

* Parenthesize interpolations containing global::

* Improvements:
* Cleaner output
* More unit testing
* More efficient tree search

* Implement revisions
* Update Lambda1 to be invariant
* Visit descendents before deciding whether or not to parenthesize an interpolation expression
* Rename local function
* Remove branch for conditional expressions
* Handle Lambda expressions without a block body
* Check for parenthesized expressions

* `NET60` instead of `!NET40`
master
Jeremy Pritts 1 day ago committed by GitHub
parent
commit
aff9649711
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 13
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  3. 25
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/GloballyQualifiedTypeInStringInterpolation.cs
  4. 32
      ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs

1
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -137,6 +137,7 @@ @@ -137,6 +137,7 @@
<Compile Include="TestCases\ILPretty\Issue3442.cs" />
<Compile Include="TestCases\ILPretty\MonoFixed.cs" />
<Compile Include="TestCases\Pretty\Comparisons.cs" />
<Compile Include="TestCases\Pretty\GloballyQualifiedTypeInStringInterpolation.cs" />
<Compile Include="TestCases\Pretty\Issue3406.cs" />
<Compile Include="TestCases\Pretty\PointerArithmetic.cs" />
<Compile Include="TestCases\Pretty\Issue3439.cs" />

13
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -308,6 +308,19 @@ namespace ICSharpCode.Decompiler.Tests @@ -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)
{

25
ICSharpCode.Decompiler.Tests/TestCases/Pretty/GloballyQualifiedTypeInStringInterpolation.cs

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public static class GloballyQualifiedTypeInStringInterpolation
{
public static string Root => $"Prefix {(global::System.DateTime.Now)} suffix";
public static string Cast => $"Prefix {((int)global::System.DateTime.Now.Ticks)} suffix";
#if CS100 && NET60
public static string Lambda1 => $"Prefix {(() => global::System.DateTime.Now)} suffix";
#else
public static string Lambda1 => $"Prefix {(global::System.Func<global::System.DateTime>)(() => global::System.DateTime.Now)} suffix";
#endif
public static string Lambda2 => $"Prefix {((global::System.Func<global::System.DateTime>)(() => global::System.DateTime.Now))()} suffix";
public static string Method1 => $"Prefix {M(global::System.DateTime.Now)} suffix";
public static string Method2 => $"Prefix {(global::System.DateTime.Now.Ticks)} suffix";
public static string Method3 => $"Prefix {(global::System.DateTime.Equals(global::System.DateTime.Now, global::System.DateTime.Now))} suffix";
public static string ConditionalExpression1 => $"Prefix {(Boolean ? global::System.DateTime.Now : global::System.DateTime.UtcNow)} suffix";
public static string ConditionalExpression2 => $"Prefix {(Boolean ? global::System.DateTime.Now : global::System.DateTime.UtcNow).Ticks} suffix";
private static bool Boolean => false;
private static long M(global::System.DateTime time)
{
return time.Ticks;
}
}
}

32
ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs

@ -389,6 +389,38 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -389,6 +389,38 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
base.VisitAsExpression(asExpression);
}
public override void VisitInterpolation(Interpolation interpolation)
{
// Need to do this first, in case the descendents parenthesize themselves.
base.VisitInterpolation(interpolation);
// If an interpolation contains global::, we need to parenthesize the expression.
if (InterpolationNeedsParenthesis(interpolation))
Parenthesize(interpolation.Expression);
static bool InterpolationNeedsParenthesis(AstNode node)
{
if (node is MemberType { IsDoubleColon: true })
return true;
if (node is ParenthesizedExpression)
return false;
if (node is AnonymousMethodExpression or LambdaExpression { Body: BlockStatement })
return false;
if (node is InvocationExpression invocation)
return InterpolationNeedsParenthesis(invocation.Target);
if (node is CastExpression cast)
return InterpolationNeedsParenthesis(cast.Expression);
foreach (var child in node.Children)
{
if (InterpolationNeedsParenthesis(child))
return true;
}
return false;
}
}
// Conditional operator
public override void VisitConditionalExpression(ConditionalExpression conditionalExpression)
{

Loading…
Cancel
Save