From b008febf871b67c12d31e6ac326c4821809ed5e9 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 29 May 2017 13:50:25 +0700 Subject: [PATCH 1/3] Revert: erroneously committed files from b209ade --- .../bytecodeAnalysis/BytecodeAnalysisIndex.java | 2 +- .../codeInspection/bytecodeAnalysis/PurityAnalysis.java | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java index ed2ae157cabe..8c21417f2e51 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java @@ -53,7 +53,7 @@ public class BytecodeAnalysisIndex extends ScalarIndexExtension { private static final ID NAME = ID.create("bytecodeAnalysis"); private static final HKeyDescriptor KEY_DESCRIPTOR = new HKeyDescriptor(); private static final VirtualFileGist> ourGist = GistManager.getInstance().newVirtualFileGist( - "BytecodeAnalysisIndex", 5, new HEquationsExternalizer(), new ClassDataIndexer()); + "BytecodeAnalysisIndex", 4, new HEquationsExternalizer(), new ClassDataIndexer()); @NotNull @Override diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/PurityAnalysis.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/PurityAnalysis.java index ea30ecf32557..c3976f856639 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/PurityAnalysis.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/PurityAnalysis.java @@ -556,6 +556,9 @@ final class HardCodedPurity { // Maybe overloaded and be not pure, but this would be definitely bad code style // Used in Throwable(Throwable) ctor, so this helps to infer purity of many exception constructors new Method("java/lang/Throwable", "toString", "()Ljava/lang/String;"), + // Declared in final class StringBuilder + new Method("java/lang/StringBuilder", "toString", "()Ljava/lang/String;"), + new Method("java/lang/StringBuffer", "toString", "()Ljava/lang/String;"), // Native new Method("java/lang/Object", "getClass", "()Ljava/lang/Class;"), new Method("java/lang/Class", "getComponentType", "()Ljava/lang/Class;"), @@ -593,8 +596,7 @@ final class HardCodedPurity { } static boolean isPureMethod(Key key) { - return key.method.methodName.equals("toString") && key.method.methodDesc.equals("()Ljava/lang/String;") || - pureMethods.contains(key.method); + return pureMethods.contains(key.method); } static boolean isOwnedField(FieldInsnNode fieldInsn) { From 44f3df8aa2ded1b3bd0d216b2bae5a60f3477664 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 29 May 2017 14:35:16 +0700 Subject: [PATCH 2/3] StatementExtractor#createNode: RuntimeExceptionWithAttachments used; foldNode is extracted (IDEA-CR-21189) --- .../siyeh/ig/psiutils/StatementExtractor.java | 70 +++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/StatementExtractor.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/StatementExtractor.java index d0afdc3dbeef..c2e917d42179 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/StatementExtractor.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/StatementExtractor.java @@ -15,6 +15,8 @@ */ package com.siyeh.ig.psiutils; +import com.intellij.openapi.diagnostic.Attachment; +import com.intellij.openapi.diagnostic.RuntimeExceptionWithAttachments; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; @@ -60,49 +62,59 @@ public class StatementExtractor { return result.toString(); } + @NotNull private static Node createNode(@NotNull PsiExpression expression, @NotNull PsiExpression root) { Node result = new Expr(expression); PsiExpression parent; - for (; expression != root; expression = parent) { + while (expression != root) { PsiElement parentElement = expression.getParent(); if(parentElement instanceof PsiExpressionList) { parentElement = parentElement.getParent(); } parent = ObjectUtils.tryCast(parentElement, PsiExpression.class); if (parent == null) { - throw new IllegalStateException(expression.getText() + ": expected to have expression parent; root = " + root.getText()); - } - if (parent instanceof PsiPolyadicExpression) { - PsiPolyadicExpression polyadic = (PsiPolyadicExpression)parent; - IElementType type = polyadic.getOperationTokenType(); - boolean and; - if (type == JavaTokenType.ANDAND) { - and = true; - } - else if (type == JavaTokenType.OROR) { - and = false; - } - else { - continue; - } - PsiExpression[] operands = polyadic.getOperands(); - int index = ArrayUtil.indexOf(operands, expression); - if (index == 0) continue; - result = new Cond(parent, parent, index, and ? result : EMPTY, and ? EMPTY : result); - } - if (parent instanceof PsiConditionalExpression) { - PsiConditionalExpression ternary = (PsiConditionalExpression)parent; - if (expression == ternary.getThenExpression()) { - result = new Cond(ternary, ternary.getCondition(), -1, result, EMPTY); - } - else if (expression == ternary.getElseExpression()) { - result = new Cond(ternary, ternary.getCondition(), -1, EMPTY, result); - } + throw new RuntimeExceptionWithAttachments("Expected to have expression parent", + new Attachment("expression.txt", expression.getText()), + new Attachment("root.txt", root.getText())); } + result = foldNode(result, expression, parent); + expression = parent; } return result; } + @NotNull + private static Node foldNode(@NotNull Node node, @NotNull PsiExpression expression, @NotNull PsiExpression parent) { + if (parent instanceof PsiPolyadicExpression) { + PsiPolyadicExpression polyadic = (PsiPolyadicExpression)parent; + IElementType type = polyadic.getOperationTokenType(); + boolean and; + if (type == JavaTokenType.ANDAND) { + and = true; + } + else if (type == JavaTokenType.OROR) { + and = false; + } + else { + return node; + } + PsiExpression[] operands = polyadic.getOperands(); + int index = ArrayUtil.indexOf(operands, expression); + if (index == 0) return node; + return new Cond(parent, parent, index, and ? node : EMPTY, and ? EMPTY : node); + } + if (parent instanceof PsiConditionalExpression) { + PsiConditionalExpression ternary = (PsiConditionalExpression)parent; + if (expression == ternary.getThenExpression()) { + return new Cond(ternary, ternary.getCondition(), -1, node, EMPTY); + } + if (expression == ternary.getElseExpression()) { + return new Cond(ternary, ternary.getCondition(), -1, EMPTY, node); + } + } + return node; + } + private static abstract class Node { final PsiExpression myAnchor; From 5b3d50273cb0776a7c3c7f3e77176cb105539c92 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 29 May 2017 14:53:31 +0700 Subject: [PATCH 3/3] ValueConstraint#negate throw if cannot be negated; canBeNegated added (IDEA-CR-21320) --- .../BytecodeAnalysisConverter.java | 2 +- .../codeInspection/dataFlow/MethodContract.java | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java index 0a75a3ffde90..d6cedf2d82c2 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java @@ -421,7 +421,7 @@ public class BytecodeAnalysisConverter { ValueConstraint left = c1.arguments[i]; ValueConstraint right = c2.arguments[i]; if (left == ValueConstraint.ANY_VALUE && right == ValueConstraint.ANY_VALUE) continue; - if (idx >= 0 || left == right || left != right.negate()) return null; + if (idx >= 0 || !right.canBeNegated() || left != right.negate()) return null; idx = i; } return c1; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java index 43634fb68f5b..f80e104c69ef 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java @@ -160,7 +160,17 @@ public abstract class MethodContract { } /** - * @return negated constraint or this if the constraint cannot be negated + * @return true if constraint can be negated + * @see #negate() + */ + public boolean canBeNegated() { + return this != ANY_VALUE && this != THROW_EXCEPTION; + } + + /** + * @return negated constraint + * @throws IllegalStateException if constraint cannot be negated + * @see #canBeNegated() */ public ValueConstraint negate() { switch (this) { @@ -168,7 +178,8 @@ public abstract class MethodContract { case NOT_NULL_VALUE: return NULL_VALUE; case TRUE_VALUE: return FALSE_VALUE; case FALSE_VALUE: return TRUE_VALUE; - default: return this; + default: + throw new IllegalStateException("ValueConstraint = " + this); } }