Browse Source

Fixed bug in reachability analysis when lambda/anonymous method contains a condition or switch statement.

newNRvisualizers
Daniel Grunwald 14 years ago
parent
commit
67d19127f5
  1. 20
      ICSharpCode.NRefactory.CSharp/Analysis/ControlFlow.cs
  2. 9
      ICSharpCode.NRefactory.CSharp/Analysis/ReachabilityAnalysis.cs
  3. 6
      ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs
  4. 4
      ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs
  5. 14
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/LambdaTests.cs

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

@ -21,9 +21,10 @@ using System.Collections.Generic; @@ -21,9 +21,10 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.CSharp.TypeSystem;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
namespace ICSharpCode.NRefactory.CSharp.Analysis
@ -156,7 +157,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -156,7 +157,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
}
Statement rootStatement;
CSharpAstResolver resolver;
CSharpTypeResolveContext typeResolveContext;
Func<AstNode, CancellationToken, ResolveResult> resolver;
List<ControlFlowNode> nodes;
Dictionary<string, ControlFlowNode> labels;
List<ControlFlowNode> gotoStatements;
@ -164,6 +166,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -164,6 +166,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement, CancellationToken cancellationToken = default(CancellationToken))
{
if (statement == null)
throw new ArgumentNullException("statement");
CSharpResolver r = new CSharpResolver(MinimalCorlib.Instance.CreateCompilation());
return BuildControlFlowGraph(statement, new CSharpAstResolver(r, statement), cancellationToken);
}
@ -174,7 +178,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -174,7 +178,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
throw new ArgumentNullException("statement");
if (resolver == null)
throw new ArgumentNullException("resolver");
return BuildControlFlowGraph(statement, resolver.Resolve, resolver.TypeResolveContext, cancellationToken);
}
internal IList<ControlFlowNode> BuildControlFlowGraph(Statement statement, Func<AstNode, CancellationToken, ResolveResult> resolver, CSharpTypeResolveContext typeResolveContext, CancellationToken cancellationToken)
{
NodeCreationVisitor nodeCreationVisitor = new NodeCreationVisitor();
nodeCreationVisitor.builder = this;
try {
@ -183,6 +191,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -183,6 +191,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
this.gotoStatements = new List<ControlFlowNode>();
this.rootStatement = statement;
this.resolver = resolver;
this.typeResolveContext = typeResolveContext;
this.cancellationToken = cancellationToken;
ControlFlowNode entryPoint = CreateStartNode(statement);
@ -205,6 +214,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -205,6 +214,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
this.gotoStatements = null;
this.rootStatement = null;
this.resolver = null;
this.typeResolveContext = null;
this.cancellationToken = CancellationToken.None;
}
}
@ -288,7 +298,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -288,7 +298,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
if (!(expr is PrimitiveExpression || expr is NullReferenceExpression))
return null;
}
return resolver.Resolve(expr, cancellationToken);
return resolver(expr, cancellationToken);
}
/// <summary>
@ -308,7 +318,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -308,7 +318,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
{
if (c1 == null || c2 == null || !c1.IsCompileTimeConstant || !c2.IsCompileTimeConstant)
return false;
CSharpResolver r = new CSharpResolver(resolver.TypeResolveContext);
CSharpResolver r = new CSharpResolver(typeResolveContext);
ResolveResult c = r.ResolveBinaryOperator(BinaryOperatorType.Equality, c1, c2);
return c.IsCompileTimeConstant && (c.ConstantValue as bool?) == true;
}

9
ICSharpCode.NRefactory.CSharp/Analysis/ReachabilityAnalysis.cs

@ -20,6 +20,8 @@ using System; @@ -20,6 +20,8 @@ using System;
using System.Collections.Generic;
using System.Threading;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.CSharp.TypeSystem;
using ICSharpCode.NRefactory.Semantics;
namespace ICSharpCode.NRefactory.CSharp.Analysis
{
@ -42,6 +44,13 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -42,6 +44,13 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
return Create(cfg, cancellationToken);
}
internal static ReachabilityAnalysis Create(Statement statement, Func<AstNode, CancellationToken, ResolveResult> resolver, CSharpTypeResolveContext typeResolveContext, CancellationToken cancellationToken)
{
var cfgBuilder = new ControlFlowGraphBuilder();
var cfg = cfgBuilder.BuildControlFlowGraph(statement, resolver, typeResolveContext, cancellationToken);
return Create(cfg, cancellationToken);
}
public static ReachabilityAnalysis Create(IList<ControlFlowNode> controlFlowGraph, CancellationToken cancellationToken = default(CancellationToken))
{
if (controlFlowGraph == null)

6
ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs

@ -78,12 +78,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -78,12 +78,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
this.resolveVisitor = new ResolveVisitor(initialResolverState, parsedFile);
}
internal CSharpAstResolver(ResolveVisitor resolveVisitor)
{
this.resolveVisitor = resolveVisitor;
this.resolverInitialized = true;
}
/// <summary>
/// Gets the type resolve context for the root resolver.
/// </summary>

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

@ -2319,7 +2319,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -2319,7 +2319,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
// Failure to infer a return type does not make the lambda invalid,
// so we can ignore the 'tiSuccess' value
if (isValidAsVoidMethod && returnExpressions.Count == 0 && body is Statement) {
var reachabilityAnalysis = ReachabilityAnalysis.Create((Statement)body, new CSharpAstResolver(this), cancellationToken);
var reachabilityAnalysis = ReachabilityAnalysis.Create(
(Statement)body, (node, _) => resolveResultCache[node],
resolver.CurrentTypeResolveContext, cancellationToken);
isEndpointUnreachable = !reachabilityAnalysis.IsEndpointReachable((Statement)body);
}
}

14
ICSharpCode.NRefactory.Tests/CSharp/Resolver/LambdaTests.cs

@ -446,6 +446,20 @@ class Test { @@ -446,6 +446,20 @@ class Test {
Assert.IsTrue(c.IsValid);
}
[Test]
public void AnonymousMethodInNewEventHandler()
{
// The switch statement causes the control flow analysis to ask the resolver if it's a constant,
// which caused a bug.
string program = @"using System;
class Test {
static bool b;
object x = new EventHandler<AssemblyLoadEventArgs>($delegate (object sender, AssemblyLoadEventArgs e) { switch (e.Action) {} }$);
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
}
[Test]
public void ThrowingAnonymousMethodIsConvertibleToFunc()
{

Loading…
Cancel
Save