diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs index 247c1fb394..2e94a1c589 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs @@ -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 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 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 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 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)); + } } } diff --git a/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs b/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs index 2f1e2b47e3..9a2fa141d5 100644 --- a/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs +++ b/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs @@ -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 } Statement rootStatement; + ResolveVisitor resolveVisitor; List nodes; Dictionary labels; List gotoStatements; - public IList BuildControlFlowGraph(Statement statement) + public IList BuildControlFlowGraph(Statement statement, ITypeResolveContext context, CancellationToken cancellationToken = default(CancellationToken)) { + return BuildControlFlowGraph(statement, new ResolveVisitor( + new CSharpResolver(context, cancellationToken), null, ConstantModeResolveVisitorNavigator.Skip)); + } + + public IList 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 this.labels = new Dictionary(); this.gotoStatements = new List(); this.rootStatement = statement; + this.resolveVisitor = resolveVisitor; ControlFlowNode entryPoint = CreateStartNode(statement); statement.AcceptVisitor(nodeCreationVisitor, entryPoint); @@ -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 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 /// The constant value of the expression; or null if the expression is not a constant. ConstantResolveResult EvaluateConstant(Expression expr) { - return null; // TODO: implement this using the C# resolver + return resolveVisitor.Resolve(expr) as ConstantResolveResult; } /// @@ -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 diff --git a/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs b/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs index 534e423258..e377022398 100644 --- a/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs +++ b/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs @@ -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 readonly List allNodes = new List(); readonly Dictionary beginNodeDict = new Dictionary(); readonly Dictionary endNodeDict = new Dictionary(); + readonly ResolveVisitor resolveVisitor; + readonly CancellationToken cancellationToken; Dictionary nodeStatus = new Dictionary(); Dictionary edgeStatus = new Dictionary(); @@ -54,19 +58,30 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis Queue nodesWithModifiedInput = new Queue(); - 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().Except(allNodes.Select(n => n.NextStatement)).Any()); @@ -289,7 +304,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis /// The constant value of the expression; or null if the expression is not a constant. ConstantResolveResult EvaluateConstant(Expression expr) { - return null; // TODO: implement this using the C# resolver + return resolveVisitor.Resolve(expr) as ConstantResolveResult; } /// diff --git a/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs b/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs index 8264bdc9d2..bd5fbcc586 100644 --- a/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs +++ b/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs @@ -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; diff --git a/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs b/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs index 07e0626721..a8c11cb993 100644 --- a/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs +++ b/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs @@ -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; diff --git a/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs b/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs index 68ac7ee2f6..a056c8ba16 100644 --- a/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs +++ b/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs @@ -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); diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs index 904bbae995..fd43e18c4e 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs @@ -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 this.context = context; this.cancellationToken = cancellationToken; } - #endif #endregion #region Properties diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs b/ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs index 0ae7b02a28..be956b22fa 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs @@ -37,4 +37,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// ResolveAll } + + sealed class ConstantModeResolveVisitorNavigator : IResolveVisitorNavigator + { + ResolveVisitorNavigationMode mode; + + public static readonly IResolveVisitorNavigator Skip = new ConstantModeResolveVisitorNavigator { mode = ResolveVisitorNavigationMode.Skip }; + + ResolveVisitorNavigationMode IResolveVisitorNavigator.Scan(AstNode node) + { + return mode; + } + } } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs b/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs index 060bdfa7fa..2c4002a416 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs @@ -74,6 +74,20 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } #endregion + /// + /// Gets the TypeResolveContext used by this ResolveVisitor. + /// + public ITypeResolveContext TypeResolveContext { + get { return resolver.Context; } + } + + /// + /// Gets the CancellationToken used by this ResolveVisitor. + /// + 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 mode = ResolveVisitorNavigationMode.Resolve; ResolveResult result; if (!cache.TryGetValue(node, out result)) { + resolver.cancellationToken.ThrowIfCancellationRequested(); result = cache[node] = node.AcceptVisitor(this, null) ?? errorResult; } if (wasScan)