better check for side effects in debugger value hint;

fix handling method call expressions on alt-click
This commit is contained in:
Eugene Zhuravlev
2010-08-26 16:15:44 +04:00
parent 0246906262
commit 8a7c002c56
3 changed files with 93 additions and 80 deletions
@@ -20,6 +20,7 @@ import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.DebuggerInvocationUtil;
import com.intellij.debugger.DebuggerManagerEx;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.evaluation.*;
import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilderImpl;
import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
@@ -190,17 +191,8 @@ public class ValueHint extends AbstractValueHint {
return tree;
}
// call inside ReadAction only
private static boolean canProcess(PsiExpression expression) {
if (expression instanceof PsiReferenceExpression) {
PsiExpression qualifier = ((PsiReferenceExpression)expression).getQualifierExpression();
return qualifier == null || canProcess(qualifier);
}
return !(expression instanceof PsiMethodCallExpression);
}
@Nullable
private static Pair<PsiExpression, TextRange> findExpression(PsiElement element) {
private static Pair<PsiExpression, TextRange> findExpression(PsiElement element, boolean allowMethodCalls) {
if (!(element instanceof PsiIdentifier || element instanceof PsiKeyword)) {
return null;
}
@@ -210,21 +202,30 @@ public class ValueHint extends AbstractValueHint {
if (parent instanceof PsiVariable) {
expression = element;
}
else if (parent instanceof PsiReferenceExpression && canProcess((PsiExpression)parent)) {
else if (parent instanceof PsiReferenceExpression) {
final PsiElement pparent = parent.getParent();
if (pparent instanceof PsiMethodCallExpression) {
parent = pparent;
}
if (allowMethodCalls || !DebuggerUtils.hasSideEffects(parent)) {
expression = parent;
}
}
else if (parent instanceof PsiThisExpression) {
expression = parent;
}
if (parent instanceof PsiThisExpression) {
expression = parent;
}
try {
if (expression != null) {
if (expression != null) {
try {
PsiElement context = element;
if(parent instanceof PsiParameter) {
try {
context = ((PsiMethod)((PsiParameter)parent).getDeclarationScope()).getBody();
}
catch (Throwable e) {}
} else {
catch (Throwable ignored) {
}
}
else {
while(context != null && !(context instanceof PsiStatement) && !(context instanceof PsiClass)) {
context = context.getParent();
}
@@ -233,8 +234,9 @@ public class ValueHint extends AbstractValueHint {
PsiExpression psiExpression = JavaPsiFacade.getInstance(expression.getProject()).getElementFactory().createExpressionFromText(expression.getText(), context);
return Pair.create(psiExpression, textRange);
}
} catch (IncorrectOperationException e) {
LOG.debug(e);
catch (IncorrectOperationException e) {
LOG.debug(e);
}
}
return null;
}
@@ -271,7 +273,7 @@ public class ValueHint extends AbstractValueHint {
if(currentRange.get() == null) {
PsiElement elementAtCursor = psiFile.findElementAt(offset);
if (elementAtCursor == null) return;
Pair<PsiExpression, TextRange> pair = findExpression(elementAtCursor);
Pair<PsiExpression, TextRange> pair = findExpression(elementAtCursor, type == ValueHintType.MOUSE_CLICK_HINT || type == ValueHintType.MOUSE_ALT_OVER_HINT);
if (pair == null) return;
selectedExpression.set(pair.getFirst());
currentRange.set(pair.getSecond());
@@ -47,7 +47,6 @@ import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.util.ui.tree.TreeModelAdapter;
@@ -59,7 +58,6 @@ import javax.swing.event.TreeModelEvent;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
public class FrameDebuggerTree extends DebuggerTree {
private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.impl.FrameDebuggerTree");
@@ -457,7 +455,7 @@ public class FrameDebuggerTree extends DebuggerTree {
@Override public void visitMethodCallExpression(final PsiMethodCallExpression expression) {
final PsiMethod psiMethod = expression.resolveMethod();
if (psiMethod != null && !hasSideEffects(expression)) {
if (psiMethod != null && !DebuggerUtils.hasSideEffectsOrReferencesMissingVars(expression, myVisibleLocals)) {
myExpressions.add(new TextWithImportsImpl(expression));
}
super.visitMethodCallExpression(expression);
@@ -469,7 +467,7 @@ public class FrameDebuggerTree extends DebuggerTree {
if (psiElement instanceof PsiVariable) {
final PsiVariable var = (PsiVariable)psiElement;
if (var instanceof PsiField) {
if (!hasSideEffects(reference)) {
if (!DebuggerUtils.hasSideEffectsOrReferencesMissingVars(reference, myVisibleLocals)) {
if (var instanceof PsiEnumConstant && reference.getQualifier() == null) {
final PsiClass enumClass = ((PsiEnumConstant)var).getContainingClass();
if (enumClass != null) {
@@ -497,7 +495,7 @@ public class FrameDebuggerTree extends DebuggerTree {
}
@Override public void visitArrayAccessExpression(final PsiArrayAccessExpression expression) {
if (!hasSideEffects(expression)) {
if (!DebuggerUtils.hasSideEffectsOrReferencesMissingVars(expression, myVisibleLocals)) {
myExpressions.add(new TextWithImportsImpl(expression));
}
super.visitArrayAccessExpression(expression);
@@ -519,61 +517,9 @@ public class FrameDebuggerTree extends DebuggerTree {
}
}
@Override public void visitClass(final PsiClass aClass) {
@Override
public void visitClass(final PsiClass aClass) {
// Do not step in to local and anonymous classes...
}
private boolean hasSideEffects(PsiElement element) {
final AtomicBoolean rv = new AtomicBoolean(false);
element.accept(new JavaRecursiveElementWalkingVisitor() {
@Override public void visitPostfixExpression(final PsiPostfixExpression expression) {
rv.set(true);
}
@Override public void visitReferenceExpression(final PsiReferenceExpression expression) {
final PsiElement psiElement = expression.resolve();
if (psiElement instanceof PsiLocalVariable) {
if (!myVisibleLocals.contains(((PsiLocalVariable)psiElement).getName())) {
rv.set(true);
}
}
else if (psiElement instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)psiElement;
if (!DebuggerUtils.isSimpleGetter(method)) {
rv.set(true);
}
}
if (!rv.get()) {
super.visitReferenceExpression(expression);
}
}
@Override public void visitPrefixExpression(final PsiPrefixExpression expression) {
final IElementType op = expression.getOperationTokenType();
if (JavaTokenType.PLUSPLUS.equals(op) || JavaTokenType.MINUSMINUS.equals(op)) {
rv.set(true);
}
else {
super.visitPrefixExpression(expression);
}
}
@Override public void visitAssignmentExpression(final PsiAssignmentExpression expression) {
rv.set(true);
}
@Override public void visitCallExpression(final PsiCallExpression callExpression) {
final PsiMethod method = callExpression.resolveMethod();
if (method == null || !DebuggerUtils.isSimpleGetter(method)) {
rv.set(true);
}
else {
super.visitCallExpression(callExpression);
}
}
});
return rv.get();
}
}
}
@@ -33,9 +33,11 @@ import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.StringBuilderSpinAllocator;
@@ -368,6 +370,69 @@ public abstract class DebuggerUtils implements ApplicationComponent {
}
}
public static boolean hasSideEffects(PsiElement element) {
return hasSideEffectsOrReferencesMissingVars(element, null);
}
public static boolean hasSideEffectsOrReferencesMissingVars(PsiElement element, @Nullable final Set<String> visibleLocalVariables) {
final Ref<Boolean> rv = new Ref<Boolean>(Boolean.FALSE);
element.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitPostfixExpression(final PsiPostfixExpression expression) {
rv.set(Boolean.TRUE);
}
@Override
public void visitReferenceExpression(final PsiReferenceExpression expression) {
final PsiElement psiElement = expression.resolve();
if (psiElement instanceof PsiLocalVariable) {
if (visibleLocalVariables != null) {
if (!visibleLocalVariables.contains(((PsiLocalVariable)psiElement).getName())) {
rv.set(Boolean.TRUE);
}
}
}
else if (psiElement instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)psiElement;
if (!isSimpleGetter(method)) {
rv.set(Boolean.TRUE);
}
}
if (!rv.get().booleanValue()) {
super.visitReferenceExpression(expression);
}
}
@Override
public void visitPrefixExpression(final PsiPrefixExpression expression) {
final IElementType op = expression.getOperationTokenType();
if (JavaTokenType.PLUSPLUS.equals(op) || JavaTokenType.MINUSMINUS.equals(op)) {
rv.set(Boolean.TRUE);
}
else {
super.visitPrefixExpression(expression);
}
}
@Override
public void visitAssignmentExpression(final PsiAssignmentExpression expression) {
rv.set(Boolean.TRUE);
}
@Override
public void visitCallExpression(final PsiCallExpression callExpression) {
final PsiMethod method = callExpression.resolveMethod();
if (method == null || !isSimpleGetter(method)) {
rv.set(Boolean.TRUE);
}
else {
super.visitCallExpression(callExpression);
}
}
});
return rv.get().booleanValue();
}
public abstract String findAvailableDebugAddress(boolean useSockets) throws ExecutionException;
public static boolean isSynthetic(TypeComponent typeComponent) {