8 changed files with 212 additions and 23 deletions
@ -0,0 +1,133 @@
@@ -0,0 +1,133 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using ICSharpCode.NRefactory.Ast; |
||||
using ICSharpCode.SharpDevelop.Dom; |
||||
using ICSharpCode.SharpDevelop.Editor; |
||||
using ICSharpCode.SharpDevelop.Refactoring; |
||||
|
||||
namespace SharpRefactoring.ContextActions |
||||
{ |
||||
/// <summary>
|
||||
/// For expressions like "a.b.c" adds checks for null "if (a != null && a.b != null)".
|
||||
/// </summary>
|
||||
public class CheckMemberNotNull : ContextAction |
||||
{ |
||||
public override string Title { |
||||
get { return "Add null checks"; } |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Expression at caret.
|
||||
/// </summary>
|
||||
Expression currentExpr; |
||||
/// <summary>
|
||||
/// Expression to be checked for null.
|
||||
/// </summary>
|
||||
Expression targetExpr; |
||||
|
||||
public override bool IsAvailable(EditorContext context) |
||||
{ |
||||
this.currentExpr = context.GetContainingElement<Expression>(); |
||||
if (currentExpr is InvocationExpression) { |
||||
// InvocationExpression (e.g. "e.Foo()") has MemberReferenceExpression as TargetObject (e.g. "e.Foo")
|
||||
// "e.Foo() -> e"
|
||||
this.targetExpr = GetTarget(((InvocationExpression)currentExpr).TargetObject); |
||||
} else { |
||||
// "a.b" -> "a"
|
||||
this.targetExpr = GetTarget(currentExpr); |
||||
} |
||||
return |
||||
this.targetExpr is MemberReferenceExpression || |
||||
this.targetExpr is CastExpression || |
||||
this.targetExpr is ParenthesizedExpression; |
||||
//this.targetExpr is IdentifierExpression; // "don't offer the action for just a.b, only for a.b.c"
|
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets "a.b" from "a.b.c"
|
||||
/// </summary>
|
||||
Expression GetTarget(Expression memberReferenceExpr) |
||||
{ |
||||
if (memberReferenceExpr is MemberReferenceExpression) { |
||||
return ((MemberReferenceExpression)memberReferenceExpr).TargetObject; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public override void Execute(EditorContext context) |
||||
{ |
||||
var conditionExpr = BuildCondition(this.targetExpr); |
||||
if (conditionExpr == null) |
||||
return; |
||||
var ifExpr = new IfElseStatement(conditionExpr, new BlockStatement()); |
||||
|
||||
context.Editor.InsertCodeBefore(this.currentExpr, ifExpr); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Turns "(a.b as T).d.e" into "(a != null) && (a.b is T) && ((a.b as T).d != null)"
|
||||
/// </summary>
|
||||
Expression BuildCondition(Expression targetExpr) |
||||
{ |
||||
var parts = GetConditionParts(targetExpr); |
||||
Expression condition = null; |
||||
foreach (var part in parts) { |
||||
if (condition == null) { |
||||
// first
|
||||
condition = new ParenthesizedExpression(part); |
||||
} else { |
||||
condition = new BinaryOperatorExpression(new ParenthesizedExpression(part), BinaryOperatorType.LogicalAnd, condition); |
||||
} |
||||
} |
||||
return condition; |
||||
} |
||||
|
||||
List<Expression> GetConditionParts(Expression targetExpr) |
||||
{ |
||||
var result = new List<Expression>(); |
||||
Expression current = targetExpr; |
||||
while (current != null) |
||||
{ |
||||
// process current expr
|
||||
if (current is MemberReferenceExpression) { |
||||
var memberExpr = (MemberReferenceExpression)current; |
||||
// expr != null
|
||||
result.Add(new BinaryOperatorExpression(memberExpr, BinaryOperatorType.InEquality, new PrimitiveExpression(null))); |
||||
} else if (current is CastExpression) { |
||||
var castExpr = (CastExpression)current; |
||||
// expr is T
|
||||
result.Add(new TypeOfIsExpression(castExpr.Expression, castExpr.CastTo)); |
||||
// no need to check (a != null) && (a is T): (a is T) is enough - skip the "a"
|
||||
current = StepIn(current); |
||||
} |
||||
else if (current is IdentifierExpression) { |
||||
result.Add(new BinaryOperatorExpression((IdentifierExpression)current, BinaryOperatorType.InEquality, new PrimitiveExpression(null))); |
||||
} |
||||
|
||||
// step down the AST (e.g. from "a as T" to "a")
|
||||
current = StepIn(current); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
Expression StepIn(Expression expr) |
||||
{ |
||||
if (expr is MemberReferenceExpression) { |
||||
return ((MemberReferenceExpression)expr).TargetObject; |
||||
} else if (expr is CastExpression) { |
||||
return ((CastExpression)expr).Expression; |
||||
} |
||||
else if (expr is ParenthesizedExpression) { |
||||
return ((ParenthesizedExpression)expr).Expression; |
||||
} |
||||
else if (expr is IdentifierExpression) { |
||||
return null; |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue