Browse Source

Fixed incorrect detection of the 'using' statement pattern when the code was assigning to the using variable. Closes #121.

pull/194/merge
Daniel Grunwald 14 years ago
parent
commit
5c08e10a07
  1. 107
      ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
  2. 26
      ICSharpCode.Decompiler/Tests/ExceptionHandling.cs

107
ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs

@ -184,40 +184,67 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
Match m2 = usingTryCatchPattern.Match(tryCatch); Match m2 = usingTryCatchPattern.Match(tryCatch);
if (!m2.Success) return null; if (!m2.Success) return null;
string variableName = m1.Get<IdentifierExpression>("variable").Single().Identifier; string variableName = m1.Get<IdentifierExpression>("variable").Single().Identifier;
if (variableName == m2.Get<IdentifierExpression>("ident").Single().Identifier) { if (variableName != m2.Get<IdentifierExpression>("ident").Single().Identifier)
if (m2.Has("valueType")) { return null;
// if there's no if(x!=null), then it must be a value type if (m2.Has("valueType")) {
ILVariable v = m1.Get<AstNode>("variable").Single().Annotation<ILVariable>(); // if there's no if(x!=null), then it must be a value type
if (v == null || v.Type == null || !v.Type.IsValueType) ILVariable v = m1.Get<AstNode>("variable").Single().Annotation<ILVariable>();
return null; if (v == null || v.Type == null || !v.Type.IsValueType)
} return null;
node.Remove(); }
BlockStatement body = m2.Get<BlockStatement>("body").Single();
UsingStatement usingStatement = new UsingStatement(); // There are two variants of the using statement:
usingStatement.ResourceAcquisition = node.Expression.Detach(); // "using (var a = init)" and "using (expr)".
usingStatement.EmbeddedStatement = body.Detach(); // The former declares a read-only variable 'a', and the latter declares an unnamed read-only variable
tryCatch.ReplaceWith(usingStatement); // to store the original value of 'expr'.
// Move the variable declaration into the resource acquisition, if possible // This means that in order to introduce a using statement, in both cases we need to detect a read-only
// This is necessary for the foreach-pattern to work on the result of the using-pattern // variable that is used only within that block.
VariableDeclarationStatement varDecl = FindVariableDeclaration(usingStatement, variableName);
if (varDecl != null && varDecl.Parent is BlockStatement) { if (HasAssignment(tryCatch, variableName))
Statement declarationPoint; return null;
if (CanMoveVariableDeclarationIntoStatement(varDecl, usingStatement, out declarationPoint)) {
// Moving the variable into the UsingStatement is allowed: VariableDeclarationStatement varDecl = FindVariableDeclaration(node, variableName);
usingStatement.ResourceAcquisition = new VariableDeclarationStatement { if (varDecl == null || !(varDecl.Parent is BlockStatement))
Type = (AstType)varDecl.Type.Clone(), return null;
Variables = {
new VariableInitializer { // Create the using statement so that we can use definite assignment analysis to check
Name = variableName, // whether the variable is declared correctly there.
Initializer = m1.Get<Expression>("initializer").Single().Detach() node.Remove();
}.CopyAnnotationsFrom(usingStatement.ResourceAcquisition) BlockStatement body = m2.Get<BlockStatement>("body").Single();
} UsingStatement usingStatement = new UsingStatement();
}.CopyAnnotationsFrom(node); usingStatement.ResourceAcquisition = node.Expression.Detach();
} usingStatement.EmbeddedStatement = body.Detach();
tryCatch.ReplaceWith(usingStatement);
// Check whether moving the variable declaration into the resource acquisition is possible
Statement declarationPoint;
if (CanMoveVariableDeclarationIntoStatement(varDecl, usingStatement, out declarationPoint)) {
// Moving the variable into the UsingStatement is allowed.
// But if possible, we'll eliminate the variable completely:
if (body.Descendants.OfType<IdentifierExpression>().Any(ident => ident.Identifier == variableName)) {
usingStatement.ResourceAcquisition = new VariableDeclarationStatement {
Type = (AstType)varDecl.Type.Clone(),
Variables = {
new VariableInitializer {
Name = variableName,
Initializer = m1.Get<Expression>("initializer").Single().Detach()
}.CopyAnnotationsFrom(usingStatement.ResourceAcquisition)
}
}.CopyAnnotationsFrom(node);
} else {
// the variable is never used; eliminate it:
usingStatement.ResourceAcquisition = m1.Get<Expression>("initializer").Single().Detach();
} }
return usingStatement; return usingStatement;
} else {
// oops, we can't use a using statement after all; so revert back to try-finally
usingStatement.ReplaceWith(tryCatch);
((TryCatchStatement)tryCatch).TryBlock = body.Detach();
node.Expression = (Expression)usingStatement.ResourceAcquisition.Detach();
tryCatch.Parent.InsertChildBefore(tryCatch, node, BlockStatement.StatementRole);
return null;
} }
return null;
} }
internal static VariableDeclarationStatement FindVariableDeclaration(AstNode node, string identifier) internal static VariableDeclarationStatement FindVariableDeclaration(AstNode node, string identifier)
@ -251,6 +278,24 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
return true; return true;
} }
/// <summary>
/// Gets whether there is an assignment to 'variableName' anywhere within the given node.
/// </summary>
bool HasAssignment(AstNode root, string variableName)
{
foreach (AstNode node in root.DescendantsAndSelf) {
IdentifierExpression ident = node as IdentifierExpression;
if (ident != null && ident.Identifier == variableName) {
if (ident.Parent is AssignmentExpression && ident.Role == AssignmentExpression.LeftRole
|| ident.Parent is DirectionExpression)
{
return true;
}
}
}
return false;
}
#endregion #endregion
#region foreach (generic) #region foreach (generic)

26
ICSharpCode.Decompiler/Tests/ExceptionHandling.cs

@ -2,6 +2,7 @@
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) // This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System; using System;
using System.Threading;
public class ExceptionHandling public class ExceptionHandling
{ {
@ -64,4 +65,29 @@ public class ExceptionHandling
Console.WriteLine("other"); Console.WriteLine("other");
} }
} }
public void NoUsingStatementBecauseTheVariableIsAssignedTo()
{
CancellationTokenSource cancellationTokenSource = null;
try
{
cancellationTokenSource = new CancellationTokenSource();
}
finally
{
if (cancellationTokenSource != null)
{
cancellationTokenSource.Dispose();
}
}
}
public void UsingStatementThatChangesTheVariable()
{
CancellationTokenSource cancellationTokenSource = null;
using (cancellationTokenSource)
{
cancellationTokenSource = new CancellationTokenSource();
}
}
} }

Loading…
Cancel
Save