Browse Source

Evaluate constant expressions in definite assignment analysis.

newNRvisualizers
Daniel Grunwald 15 years ago
parent
commit
ccfd4ea12c
  1. 26
      ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs
  2. 29
      ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs
  3. 25
      ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs
  4. 1
      ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs
  5. 1
      ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs
  6. 4
      ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs
  7. 2
      ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs
  8. 12
      ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs
  9. 15
      ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs

26
ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs

@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
using System;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
using NUnit.Framework;
namespace ICSharpCode.NRefactory.CSharp.Analysis
@ -39,7 +40,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -39,7 +40,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
Statement stmt5 = tryCatchStatement.FinallyBlock.Statements.Single();
LabelStatement label = (LabelStatement)block.Statements.ElementAt(1);
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(block);
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(block, CecilLoaderTests.Mscorlib);
da.Analyze("i");
Assert.AreEqual(0, da.UnassignedVariableUses.Count);
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(tryCatchStatement));
@ -89,7 +90,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -89,7 +90,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
TrueStatement = new BlockStatement(),
FalseStatement = new BlockStatement()
};
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(ifStmt);
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(ifStmt, CecilLoaderTests.Mscorlib);
da.Analyze("i");
Assert.AreEqual(0, da.UnassignedVariableUses.Count);
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(ifStmt));
@ -120,7 +121,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -120,7 +121,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
TrueStatement = new BlockStatement(),
FalseStatement = new BlockStatement()
};
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(ifStmt);
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(ifStmt, CecilLoaderTests.Mscorlib);
da.Analyze("i");
Assert.AreEqual(0, da.UnassignedVariableUses.Count);
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(ifStmt));
@ -128,5 +129,24 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -128,5 +129,24 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBefore(ifStmt.FalseStatement));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusAfter(ifStmt));
}
[Test]
public void WhileTrue()
{
WhileStatement loop = new WhileStatement {
Condition = new PrimitiveExpression(true),
EmbeddedStatement = new BlockStatement {
new AssignmentExpression(new IdentifierExpression("i"), new PrimitiveExpression(0)),
new BreakStatement()
}
};
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(loop, CecilLoaderTests.Mscorlib);
da.Analyze("i");
Assert.AreEqual(0, da.UnassignedVariableUses.Count);
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(loop));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(loop.EmbeddedStatement));
Assert.AreEqual(DefiniteAssignmentStatus.CodeUnreachable, da.GetStatusAfter(loop.EmbeddedStatement));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(loop));
}
}
}

29
ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs

@ -5,7 +5,10 @@ using System; @@ -5,7 +5,10 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp.Analysis
{
@ -135,12 +138,24 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -135,12 +138,24 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
}
Statement rootStatement;
ResolveVisitor resolveVisitor;
List<ControlFlowNode> nodes;
Dictionary<string, ControlFlowNode> labels;
List<ControlFlowNode> gotoStatements;
public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement)
public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement, ITypeResolveContext context, CancellationToken cancellationToken = default(CancellationToken))
{
return BuildControlFlowGraph(statement, new ResolveVisitor(
new CSharpResolver(context, cancellationToken), null, ConstantModeResolveVisitorNavigator.Skip));
}
public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement, ResolveVisitor resolveVisitor)
{
if (statement == null)
throw new ArgumentNullException("statement");
if (resolveVisitor == null)
throw new ArgumentNullException("resolveVisitor");
NodeCreationVisitor nodeCreationVisitor = new NodeCreationVisitor();
nodeCreationVisitor.builder = this;
try {
@ -148,6 +163,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -148,6 +163,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
this.labels = new Dictionary<string, ControlFlowNode>();
this.gotoStatements = new List<ControlFlowNode>();
this.rootStatement = statement;
this.resolveVisitor = resolveVisitor;
ControlFlowNode entryPoint = CreateStartNode(statement);
statement.AcceptVisitor(nodeCreationVisitor, entryPoint);
@ -167,6 +183,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -167,6 +183,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
this.labels = null;
this.gotoStatements = null;
this.rootStatement = null;
this.resolveVisitor = null;
}
}
@ -206,7 +223,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -206,7 +223,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
ControlFlowNode CreateSpecialNode(Statement statement, ControlFlowNodeType type)
{
ControlFlowNode node = CreateNode(statement, null, type);
ControlFlowNode node = CreateNode(null, statement, type);
nodes.Add(node);
return node;
}
@ -238,7 +255,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -238,7 +255,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// <returns>The constant value of the expression; or null if the expression is not a constant.</returns>
ConstantResolveResult EvaluateConstant(Expression expr)
{
return null; // TODO: implement this using the C# resolver
return resolveVisitor.Resolve(expr) as ConstantResolveResult;
}
/// <summary>
@ -256,7 +273,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -256,7 +273,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
bool AreEqualConstants(ConstantResolveResult c1, ConstantResolveResult c2)
{
return false; // TODO: implement this using the resolver's operator==
if (c1 == null || c2 == null)
return false;
CSharpResolver r = new CSharpResolver(resolveVisitor.TypeResolveContext, resolveVisitor.CancellationToken);
ResolveResult c = r.ResolveBinaryOperator(BinaryOperatorType.Equality, c1, c2);
return c.IsCompileTimeConstant && (c.ConstantValue as bool?) == true;
}
#endregion

25
ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs

@ -5,7 +5,9 @@ using System; @@ -5,7 +5,9 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.Utils;
namespace ICSharpCode.NRefactory.CSharp.Analysis
@ -46,6 +48,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -46,6 +48,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
readonly List<ControlFlowNode> allNodes = new List<ControlFlowNode>();
readonly Dictionary<Statement, ControlFlowNode> beginNodeDict = new Dictionary<Statement, ControlFlowNode>();
readonly Dictionary<Statement, ControlFlowNode> endNodeDict = new Dictionary<Statement, ControlFlowNode>();
readonly ResolveVisitor resolveVisitor;
readonly CancellationToken cancellationToken;
Dictionary<ControlFlowNode, DefiniteAssignmentStatus> nodeStatus = new Dictionary<ControlFlowNode, DefiniteAssignmentStatus>();
Dictionary<ControlFlowEdge, DefiniteAssignmentStatus> edgeStatus = new Dictionary<ControlFlowEdge, DefiniteAssignmentStatus>();
@ -54,19 +58,30 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -54,19 +58,30 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
Queue<ControlFlowNode> nodesWithModifiedInput = new Queue<ControlFlowNode>();
public DefiniteAssignmentAnalysis(Statement rootStatement)
public DefiniteAssignmentAnalysis(Statement rootStatement, ITypeResolveContext context, CancellationToken cancellationToken = default(CancellationToken))
: this(rootStatement, new ResolveVisitor(new CSharpResolver(context, cancellationToken), null, ConstantModeResolveVisitorNavigator.Skip))
{
}
public DefiniteAssignmentAnalysis(Statement rootStatement, ResolveVisitor resolveVisitor)
{
if (rootStatement == null)
throw new ArgumentNullException("rootStatement");
if (resolveVisitor == null)
throw new ArgumentNullException("resolveVisitor");
this.resolveVisitor = resolveVisitor;
this.cancellationToken = resolveVisitor.CancellationToken;
visitor.analysis = this;
ControlFlowGraphBuilder b = new ControlFlowGraphBuilder();
allNodes.AddRange(b.BuildControlFlowGraph(rootStatement));
allNodes.AddRange(b.BuildControlFlowGraph(rootStatement, resolveVisitor));
foreach (AstNode descendant in rootStatement.Descendants) {
// Anonymous methods have separate control flow graphs, but we also need to analyze those.
AnonymousMethodExpression ame = descendant as AnonymousMethodExpression;
if (ame != null)
allNodes.AddRange(b.BuildControlFlowGraph(ame.Body));
allNodes.AddRange(b.BuildControlFlowGraph(ame.Body, resolveVisitor));
LambdaExpression lambda = descendant as LambdaExpression;
if (lambda != null && lambda.Body is Statement)
allNodes.AddRange(b.BuildControlFlowGraph((Statement)lambda.Body));
allNodes.AddRange(b.BuildControlFlowGraph((Statement)lambda.Body, resolveVisitor));
}
// Verify that we created nodes for all statements:
Debug.Assert(!rootStatement.DescendantsAndSelf.OfType<Statement>().Except(allNodes.Select(n => n.NextStatement)).Any());
@ -289,7 +304,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -289,7 +304,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// <returns>The constant value of the expression; or null if the expression is not a constant.</returns>
ConstantResolveResult EvaluateConstant(Expression expr)
{
return null; // TODO: implement this using the C# resolver
return resolveVisitor.Resolve(expr) as ConstantResolveResult;
}
/// <summary>

1
ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs

@ -129,6 +129,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -129,6 +129,7 @@ namespace ICSharpCode.NRefactory.CSharp
get {
AstNode next;
for (AstNode cur = firstChild; cur != null; cur = next) {
Debug.Assert(cur.parent == this);
// Remember next before yielding cur.
// This allows removing/replacing nodes while iterating through the list.
next = cur.nextSibling;

1
ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs

@ -117,6 +117,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -117,6 +117,7 @@ namespace ICSharpCode.NRefactory.CSharp
{
AstNode next;
for (AstNode cur = node.FirstChild; cur != null; cur = next) {
Debug.Assert(cur.Parent == node);
// Remember next before yielding cur.
// This allows removing/replacing nodes while iterating through the list.
next = cur.NextSibling;

4
ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs

@ -374,9 +374,9 @@ namespace ICSharpCode.NRefactory.CSharp @@ -374,9 +374,9 @@ namespace ICSharpCode.NRefactory.CSharp
variable.AddChild (new Identifier (em.Name, Convert (em.Location)), AstNode.Roles.Identifier);
if (em.Initializer != null) {
var initializer = (VariableInitializer)em.Initializer.Accept (this);
var initializer = (Expression)em.Initializer.Accept (this);
if (initializer != null)
variable.AddChild (initializer, AstNode.Roles.Variable);
variable.AddChild (initializer, EnumMemberDeclaration.InitializerRole);
}
newField.AddChild (variable, AstNode.Roles.Variable);

2
ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs

@ -33,7 +33,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -33,7 +33,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
this.context = context;
}
#if !DOTNET35
public CSharpResolver(ITypeResolveContext context, CancellationToken cancellationToken)
{
if (context == null)
@ -41,7 +40,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -41,7 +40,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
this.context = context;
this.cancellationToken = cancellationToken;
}
#endif
#endregion
#region Properties

12
ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs

@ -37,4 +37,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -37,4 +37,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// </summary>
ResolveAll
}
sealed class ConstantModeResolveVisitorNavigator : IResolveVisitorNavigator
{
ResolveVisitorNavigationMode mode;
public static readonly IResolveVisitorNavigator Skip = new ConstantModeResolveVisitorNavigator { mode = ResolveVisitorNavigationMode.Skip };
ResolveVisitorNavigationMode IResolveVisitorNavigator.Scan(AstNode node)
{
return mode;
}
}
}

15
ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs

@ -74,6 +74,20 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -74,6 +74,20 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
#endregion
/// <summary>
/// Gets the TypeResolveContext used by this ResolveVisitor.
/// </summary>
public ITypeResolveContext TypeResolveContext {
get { return resolver.Context; }
}
/// <summary>
/// Gets the CancellationToken used by this ResolveVisitor.
/// </summary>
public CancellationToken CancellationToken {
get { return resolver.cancellationToken; }
}
#region Scan / Resolve
bool resolverEnabled {
get { return mode != ResolveVisitorNavigationMode.Scan; }
@ -118,6 +132,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -118,6 +132,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
mode = ResolveVisitorNavigationMode.Resolve;
ResolveResult result;
if (!cache.TryGetValue(node, out result)) {
resolver.cancellationToken.ThrowIfCancellationRequested();
result = cache[node] = node.AcceptVisitor(this, null) ?? errorResult;
}
if (wasScan)

Loading…
Cancel
Save