From 5b1230ec4626106f22461a6b1a9efd2ed71460f2 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 13 Jul 2011 15:54:52 +0400 Subject: [PATCH] introduced PsiPolyadicExpression --- .../expression/EvaluatorBuilderImpl.java | 27 +++ .../daemon/impl/analysis/HighlightUtil.java | 26 ++- .../impl/analysis/HighlightVisitorImpl.java | 7 +- .../SimplifyBooleanExpressionFix.java | 79 ++++--- .../ConcatenationToMessageFormatAction.java | 97 +++------ .../intention/impl/SplitIfAction.java | 57 +++-- .../dataFlow/ControlFlowAnalyzer.java | 205 ++++++++++-------- .../dataFlow/DfaMemoryStateImpl.java | 13 +- .../codeInspection/dataFlow/DfaUtil.java | 27 ++- .../dataFlow/InstructionFactory.java | 6 +- .../dataFlow/StandardDataFlowRunner.java | 10 +- .../dataFlow/StandardInstructionVisitor.java | 11 +- .../instructions/BinopInstruction.java | 15 +- .../ConditionalGotoInstruction.java | 2 +- .../instructions/InstanceofInstruction.java | 7 +- .../dataFlow/value/DfaRelationValue.java | 50 +++-- .../lang/java/parser/ExpressionParser.java | 30 ++- .../psi/controlFlow/ControlFlowAnalyzer.java | 105 +++++---- .../psi/formatter/java/AbstractJavaBlock.java | 19 +- .../psi/formatter/java/FormattingAstUtil.java | 13 +- .../java/JavaSpacePropertyProcessor.java | 6 +- .../wrap/impl/JavaChildBlockWrapFactory.java | 9 +- .../java/wrap/impl/JavaChildWrapArranger.java | 13 +- .../psi/impl/ConstantExpressionVisitor.java | 70 +++--- .../compiled/ClsBinaryExpressionImpl.java | 10 + .../impl/source/resolve/JavaResolveCache.java | 17 +- .../psi/impl/source/tree/ElementType.java | 4 +- .../psi/impl/source/tree/JavaElementType.java | 1 + .../JavaConcatenationInjectorManager.java | 57 +++-- .../tree/java/PsiBinaryExpressionImpl.java | 76 ++----- .../tree/java/PsiPolyadicExpressionImpl.java | 127 +++++++++++ .../tree/java/ReplaceExpressionUtil.java | 12 +- .../inline/InlineMethodProcessor.java | 12 +- .../introduceField/ElementToWorkOn.java | 3 +- .../InplaceIntroduceParameterUI.java | 12 +- .../IntroduceParameterDialog.java | 7 +- .../IntroduceParameterHandler.java | 14 +- .../IntroduceParameterSettingsUI.java | 7 +- .../refactoring/introduceParameter/Util.java | 17 +- .../IntroduceVariableBase.java | 3 +- .../typeMigration/TypeEvaluator.java | 5 +- .../numericOverflow/simple/expected.xml | 10 +- .../numericOverflow/simple/src/Foo.java | 4 +- .../testData/psi/controlFlow/flowAnd2.txt | 4 +- .../parser-full/expressionParsing/And0.txt | 27 ++- .../expressionParsing/CondAnd0.txt | 27 ++- .../parser-full/expressionParsing/CondOr0.txt | 27 ++- .../psi/parser-full/expressionParsing/Or0.txt | 27 ++- .../parser-full/expressionParsing/Xor0.txt | 27 ++- .../parser-partial/expressions/CondOr0.txt | 27 ++- .../psi/parser-partial/expressions/Or0.txt | 27 ++- .../daemon/RecursiveVisitorTest.java | 18 +- .../parser/partial/ExpressionParserTest.java | 3 +- .../intellij/psi/JavaSOEOnReparseTest.java | 17 +- .../refactoring/IntroduceParameterTest.java | 37 ++-- .../codeInsight/CodeInsightServicesUtil.java | 42 ++-- .../com/intellij/psi/JavaElementVisitor.java | 6 +- .../com/intellij/psi/PsiBinaryExpression.java | 2 +- .../intellij/psi/PsiPolyadicExpression.java | 30 +++ .../psi/util/IsConstantExpressionVisitor.java | 17 +- .../psi/util/PsiConcatenationUtil.java | 138 ++++++++++++ .../psi/util/PsiExpressionTrimRenderer.java | 15 +- .../intellij/psi/util/RedundantCastUtil.java | 26 ++- .../intellij/psi/util/TypeConversionUtil.java | 67 ++++++ .../tree/injected/InjectedLanguageUtil.java | 49 ++++- .../testFramework/ParsingTestCase.java | 2 +- ...CharUsedInArithmeticContextInspection.java | 55 +++-- .../StringConcatenationInLoopsInspection.java | 67 +++--- .../siyeh/ig/psiutils/ParenthesesUtils.java | 73 +++---- .../UnnecessaryParenthesesInspection.java | 6 +- .../expected.xml | 16 +- .../inject/java/ConcatenationInjector.java | 2 +- .../util/ContextComputationProcessor.java | 9 +- .../Jdk5StringConcatenationPredicate.java | 17 +- ...oncatenationWithFormatStringIntention.java | 57 +---- .../ipp/psiutils/ConcatenationUtils.java | 22 +- .../HexadecimalLiteral_after.java | 2 +- .../IntroduceParameterTest.java | 34 +-- .../i18n/I18nizeConcatenationQuickFix.java | 42 ++-- 79 files changed, 1372 insertions(+), 994 deletions(-) create mode 100644 java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiPolyadicExpressionImpl.java create mode 100644 java/openapi/src/com/intellij/psi/PsiPolyadicExpression.java create mode 100644 java/openapi/src/com/intellij/psi/util/PsiConcatenationUtil.java diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java index 97ff076d4ee5..9c5ace45d494 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java @@ -318,6 +318,33 @@ public class EvaluatorBuilderImpl implements EvaluatorBuilder { myResult = createBinaryEvaluator(lResult, lOperand.getType(), rResult, rOperand.getType(), opType, expressionExpectedType); } + @Override + public void visitPolyadicExpression(PsiPolyadicExpression wideExpression) { + if (LOG.isDebugEnabled()) { + LOG.debug("visitPolyadicExpression " + wideExpression); + } + PsiExpression[] operands = wideExpression.getOperands(); + operands[0].accept(this); + Evaluator result = myResult; + PsiType lType = operands[0].getType(); + for (int i = 1; i < operands.length; i++) { + PsiExpression expression = operands[i]; + if (expression == null) { + throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", wideExpression.getText())); + return; + } + expression.accept(this); + Evaluator rResult = myResult; + IElementType opType = wideExpression.getOperationTokenType(); + PsiType expressionExpectedType = expression.getType(); + if (expressionExpectedType == null) { + throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", expression.getText())); + } + myResult = createBinaryEvaluator(result, lType, rResult, expression.getType(), opType, expressionExpectedType); + lType = TypeConversionUtil.calcTypeForBinaryExpression(lType, expressionExpectedType, opType, true); + result = myResult; + } + } // constructs binary evaluator handling unboxing and numeric promotion issues private static BinaryExpressionEvaluator createBinaryEvaluator( diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index 691dd3d4c1d2..00339413c87f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -1,3 +1,4 @@ + /* * Copyright 2000-2011 JetBrains s.r.o. * @@ -1088,15 +1089,24 @@ public class HighlightUtil { } @Nullable - static HighlightInfo checkBinaryOperatorApplicable(PsiBinaryExpression expression) { - PsiExpression lOperand = expression.getLOperand(); - PsiExpression rOperand = expression.getROperand(); - PsiJavaToken operationSign = expression.getOperationSign(); - if (!TypeConversionUtil.isBinaryOperatorApplicable(operationSign.getTokenType(), lOperand, rOperand, false)) { - String message = JavaErrorMessages - .message("binary.operator.not.applicable", operationSign.getText(), formatType(lOperand.getType()), formatType(rOperand.getType())); - return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, message); + static HighlightInfo checkPolyadicOperatorApplicable(PsiPolyadicExpression expression) { + PsiExpression[] operands = expression.getOperands(); + + PsiType lType = operands[0].getType(); + IElementType operationSign = expression.getOperationTokenType(); + for (int i = 1; i < operands.length; i++) { + PsiExpression operand = operands[i]; + PsiType rType = operand.getType(); + if (!TypeConversionUtil.isBinaryOperatorApplicable(operationSign, lType, rType, false)) { + PsiJavaToken token = expression.getTokenBeforeOperand(operand); + String message = JavaErrorMessages.message("binary.operator.not.applicable", token.getText(), + formatType(lType), + formatType(rType)); + return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, message); + } + lType = TypeConversionUtil.calcTypeForBinaryExpression(lType, rType, operationSign, true); } + return null; } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index 555f9f518ff4..590814e2f9c3 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -231,9 +231,10 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh if (!myHolder.hasErrorResults()) visitExpression(assignment); } - @Override public void visitBinaryExpression(PsiBinaryExpression expression) { - super.visitBinaryExpression(expression); - if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkBinaryOperatorApplicable(expression)); + @Override + public void visitPolyadicExpression(PsiPolyadicExpression expression) { + super.visitPolyadicExpression(expression); + if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkPolyadicOperatorApplicable(expression)); } @Override public void visitBreakStatement(PsiBreakStatement statement) { diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/SimplifyBooleanExpressionFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/SimplifyBooleanExpressionFix.java index 3d27c56a8dd6..ba77163d3ed7 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/SimplifyBooleanExpressionFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/SimplifyBooleanExpressionFix.java @@ -121,14 +121,14 @@ public class SimplifyBooleanExpressionFix implements IntentionAction { final PsiElement[] children = codeBlock.getChildren(); - if(children.length > 2){ - final PsiElement added = - parent.addRangeBefore( - children[1], - children[children.length - 2], - orig); - final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(orig.getManager()); - codeStyleManager.reformat(added); + if (children.length > 2) { + final PsiElement added = + parent.addRangeBefore( + children[1], + children[children.length - 2], + orig); + final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(orig.getManager()); + codeStyleManager.reformat(added); } orig.delete(); } @@ -142,7 +142,8 @@ public class SimplifyBooleanExpressionFix implements IntentionAction { final ExpressionVisitor expressionVisitor = new ExpressionVisitor(expression.getManager(), true); final IncorrectOperationException[] exception = {null}; result[0].accept(new JavaRecursiveElementVisitor() { - @Override public void visitElement(PsiElement element) { + @Override + public void visitElement(PsiElement element) { // read in all children in advance since due to Igorek's exercises element replace involves its siblings invalidation PsiElement[] children = element.getChildren(); for (PsiElement child : children) { @@ -150,7 +151,8 @@ public class SimplifyBooleanExpressionFix implements IntentionAction { } } - @Override public void visitExpression(PsiExpression expression) { + @Override + public void visitExpression(PsiExpression expression) { super.visitExpression(expression); expressionVisitor.clear(); expression.accept(expressionVisitor); @@ -176,19 +178,22 @@ public class SimplifyBooleanExpressionFix implements IntentionAction { PsiExpression newExpression = (PsiExpression)expression.replace(result[0]); simplifyIfStatement(newExpression); } + public static boolean canBeSimplified(@NotNull PsiExpression expression) { if (!(expression instanceof PsiConditionalExpression) && expression.getType() != PsiType.BOOLEAN) return false; final ExpressionVisitor expressionVisitor = new ExpressionVisitor(expression.getManager(), false); final Ref canBeSimplified = new Ref(Boolean.FALSE); expression.accept(new JavaRecursiveElementWalkingVisitor() { - @Override public void visitElement(PsiElement element) { + @Override + public void visitElement(PsiElement element) { if (!canBeSimplified.get().booleanValue()) { super.visitElement(element); } } - @Override public void visitExpression(PsiExpression expression) { + @Override + public void visitExpression(PsiExpression expression) { super.visitExpression(expression); expressionVisitor.clear(); expression.accept(expressionVisitor); @@ -228,19 +233,27 @@ public class SimplifyBooleanExpressionFix implements IntentionAction { return isCreateResult; } - @Override public void visitBinaryExpression(PsiBinaryExpression expression) { - PsiExpression lOperand = expression.getLOperand(); - PsiExpression rOperand = expression.getROperand(); - PsiJavaToken operationSign = expression.getOperationSign(); - IElementType tokenType = operationSign.getTokenType(); - Boolean lConstBoolean = getConstBoolean(lOperand); - Boolean rConstBoolean = getConstBoolean(rOperand); - - if (lConstBoolean != null) { - simplifyBinary(tokenType, lConstBoolean, rOperand); - } - else if (rConstBoolean != null) { - simplifyBinary(tokenType, rConstBoolean, lOperand); + @Override + public void visitPolyadicExpression(PsiPolyadicExpression expression) { + PsiExpression[] operands = expression.getOperands(); + PsiExpression lExpr = operands[0]; + IElementType tokenType = expression.getOperationTokenType(); + for (int i = 1; i < operands.length; i++) { + Boolean l = getConstBoolean(lExpr); + PsiExpression operand = operands[i]; + Boolean r = getConstBoolean(operand); + if (l != null) { + simplifyBinary(tokenType, l, operand); + } + else if (r != null) { + simplifyBinary(tokenType, r, lExpr); + } + else { + resultExpression = null; + } + if (resultExpression != null) { + lExpr = resultExpression; + } } } @@ -276,7 +289,8 @@ public class SimplifyBooleanExpressionFix implements IntentionAction { } } - @Override public void visitConditionalExpression(PsiConditionalExpression expression) { + @Override + public void visitConditionalExpression(PsiConditionalExpression expression) { Boolean condition = getConstBoolean(expression.getCondition()); if (condition == null) return; if (!markAndCheckCreateResult()) { @@ -285,7 +299,7 @@ public class SimplifyBooleanExpressionFix implements IntentionAction { resultExpression = condition.booleanValue() ? expression.getThenExpression() : expression.getElseExpression(); } - private static PsiPrefixExpression createNegatedExpression(PsiExpression otherOperand) { + private static PsiPrefixExpression createNegatedExpression(PsiExpression otherOperand) { PsiPrefixExpression expression = (PsiPrefixExpression)createExpression(otherOperand.getManager(), "!(xxx)"); try { expression.getOperand().replace(otherOperand); @@ -296,7 +310,8 @@ public class SimplifyBooleanExpressionFix implements IntentionAction { return expression; } - @Override public void visitPrefixExpression(PsiPrefixExpression expression) { + @Override + public void visitPrefixExpression(PsiPrefixExpression expression) { PsiExpression operand = expression.getOperand(); Boolean constBoolean = getConstBoolean(operand); if (constBoolean == null) return; @@ -311,7 +326,8 @@ public class SimplifyBooleanExpressionFix implements IntentionAction { } - @Override public void visitParenthesizedExpression(PsiParenthesizedExpression expression) { + @Override + public void visitParenthesizedExpression(PsiParenthesizedExpression expression) { PsiExpression subexpr = expression.getExpression(); Boolean constBoolean = getConstBoolean(subexpr); if (constBoolean == null) return; @@ -321,7 +337,8 @@ public class SimplifyBooleanExpressionFix implements IntentionAction { resultExpression = constBoolean.booleanValue() ? trueExpression : falseExpression; } - @Override public void visitReferenceExpression(PsiReferenceExpression expression) { + @Override + public void visitReferenceExpression(PsiReferenceExpression expression) { visitReferenceElement(expression); } @@ -341,4 +358,4 @@ public class SimplifyBooleanExpressionFix implements IntentionAction { public boolean startInWriteAction() { return true; } -} \ No newline at end of file +} diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConcatenationToMessageFormatAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConcatenationToMessageFormatAction.java index eb0ea9d98b50..3316c448d652 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConcatenationToMessageFormatAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConcatenationToMessageFormatAction.java @@ -20,11 +20,11 @@ import com.intellij.codeInsight.CodeInsightUtilBase; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiConcatenationUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.util.IncorrectOperationException; @@ -51,7 +51,7 @@ public class ConcatenationToMessageFormatAction implements IntentionAction { public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { if (!CodeInsightUtilBase.prepareFileForWrite(file)) return; final PsiElement element = findElementAtCaret(editor, file); - PsiBinaryExpression concatenation = getEnclosingLiteralConcatenation(element); + PsiPolyadicExpression concatenation = getEnclosingLiteralConcatenation(element); if (concatenation == null) return; StringBuilder formatString = new StringBuilder(); List args = new ArrayList(); @@ -86,27 +86,8 @@ public class ConcatenationToMessageFormatAction implements IntentionAction { StringBuilder formatString, List args) throws IncorrectOperationException { - if (expression instanceof PsiBinaryExpression) { - final PsiType type = expression.getType(); - if (type != null && type.equalsToText("java.lang.String") - && ((PsiBinaryExpression)expression).getOperationTokenType() == JavaTokenType.PLUS) { - buildMessageFormatString(((PsiBinaryExpression)expression).getLOperand(), formatString, args); - final PsiExpression rhs = ((PsiBinaryExpression)expression).getROperand(); - if (rhs != null) { - buildMessageFormatString(rhs, formatString, args); - } - } - else { - appendArgument(args, expression, formatString); - } - } - else if (expression instanceof PsiLiteralExpression) { - final String text = String.valueOf(((PsiLiteralExpression)expression).getValue()); - formatString.append(StringUtil.escapeStringCharacters(text).replace("'", "''").replace("{", "'{'")); - } - else { - appendArgument(args, expression, formatString); - } + PsiConcatenationUtil.buildFormatString(expression, formatString, args, false); + } private static void appendArgument(List args, PsiExpression argument, StringBuilder formatString) throws IncorrectOperationException { @@ -117,52 +98,40 @@ public class ConcatenationToMessageFormatAction implements IntentionAction { private static PsiExpression getBoxedArgument(PsiExpression arg) throws IncorrectOperationException { arg = PsiUtil.deparenthesizeExpression(arg); assert arg != null; - if (!PsiUtil.isLanguageLevel5OrHigher(arg)) { - final PsiType type = arg.getType(); - if (type instanceof PsiPrimitiveType && !type.equals(PsiType.NULL)) { - final PsiPrimitiveType primitiveType = (PsiPrimitiveType)type; - final String boxedQName = primitiveType.getBoxedTypeName(); - if (boxedQName != null) { - final GlobalSearchScope resolveScope = arg.getResolveScope(); - final PsiElementFactory factory = JavaPsiFacade.getElementFactory(arg.getProject()); - final PsiJavaCodeReferenceElement ref = factory.createReferenceElementByFQClassName(boxedQName, resolveScope); - final PsiNewExpression newExpr = (PsiNewExpression)factory.createExpressionFromText("new A(b)", null); - final PsiElement classRef = newExpr.getClassReference(); - assert classRef != null; - classRef.replace(ref); - final PsiExpressionList argumentList = newExpr.getArgumentList(); - assert argumentList != null; - argumentList.getExpressions()[0].replace(arg); - return newExpr; - } - } + if (PsiUtil.isLanguageLevel5OrHigher(arg)) { + return arg; } - return arg; + final PsiType type = arg.getType(); + if (!(type instanceof PsiPrimitiveType) || type.equals(PsiType.NULL)) { + return arg; + } + final PsiPrimitiveType primitiveType = (PsiPrimitiveType)type; + final String boxedQName = primitiveType.getBoxedTypeName(); + if (boxedQName == null) { + return arg; + } + final GlobalSearchScope resolveScope = arg.getResolveScope(); + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(arg.getProject()); + final PsiJavaCodeReferenceElement ref = factory.createReferenceElementByFQClassName(boxedQName, resolveScope); + final PsiNewExpression newExpr = (PsiNewExpression)factory.createExpressionFromText("new A(b)", null); + final PsiElement classRef = newExpr.getClassReference(); + assert classRef != null; + classRef.replace(ref); + final PsiExpressionList argumentList = newExpr.getArgumentList(); + assert argumentList != null; + argumentList.getExpressions()[0].replace(arg); + return newExpr; } public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { if (PsiUtil.getLanguageLevel(file).compareTo(LanguageLevel.JDK_1_4) < 0) return false; final PsiElement element = findElementAtCaret(editor, file); - PsiBinaryExpression binaryExpression = PsiTreeUtil.getParentOfType(element, PsiBinaryExpression.class, false, PsiMember.class); - if (binaryExpression == null) return false; - final PsiType type = binaryExpression.getType(); - if (type == null) return false; - if (!type.equalsToText("java.lang.String")) { - return false; - } - return !isInsideAnnotation(binaryExpression); + PsiPolyadicExpression binaryExpression = getEnclosingLiteralConcatenation(element); + return binaryExpression != null && !isInsideAnnotation(binaryExpression); } private static boolean isInsideAnnotation(PsiElement element) { - for (int i = 0; i < 20 && element instanceof PsiBinaryExpression; i++) { - // optimization: don't check deep string concatenations more than 20 levels up. - element = element.getParent(); - if (element instanceof PsiNameValuePair || - element instanceof PsiArrayInitializerMemberValue) { - return true; - } - } - return false; + return PsiTreeUtil.getParentOfType(element, PsiNameValuePair.class, PsiArrayInitializerMemberValue.class) != null; } @Nullable @@ -171,15 +140,15 @@ public class ConcatenationToMessageFormatAction implements IntentionAction { } @Nullable - private static PsiBinaryExpression getEnclosingLiteralConcatenation(final PsiElement element) { - PsiBinaryExpression binaryExpression = PsiTreeUtil.getParentOfType(element, PsiBinaryExpression.class, false, PsiMember.class); + private static PsiPolyadicExpression getEnclosingLiteralConcatenation(final PsiElement element) { + PsiPolyadicExpression binaryExpression = PsiTreeUtil.getParentOfType(element, PsiPolyadicExpression.class, false, PsiMember.class); if (binaryExpression == null) return null; final PsiClassType stringType = PsiType.getJavaLangString(element.getManager(), element.getResolveScope()); if (!stringType.equals(binaryExpression.getType())) return null; while (true) { final PsiElement parent = binaryExpression.getParent(); - if (!(parent instanceof PsiBinaryExpression)) return binaryExpression; - final PsiBinaryExpression parentBinaryExpression = (PsiBinaryExpression)parent; + if (!(parent instanceof PsiPolyadicExpression)) return binaryExpression; + PsiPolyadicExpression parentBinaryExpression = (PsiBinaryExpression)parent; if (!stringType.equals(parentBinaryExpression.getType())) return binaryExpression; binaryExpression = parentBinaryExpression; } diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/SplitIfAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/SplitIfAction.java index 15b9f6f66d48..ecabce5c2eb4 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/SplitIfAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/SplitIfAction.java @@ -36,20 +36,19 @@ public class SplitIfAction extends PsiElementBaseIntentionAction { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.intention.impl.SplitIfAction"); public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { - if (!(element instanceof PsiJavaToken)) { return false; } PsiJavaToken token = (PsiJavaToken)element; - if (!(token.getParent() instanceof PsiBinaryExpression)) return false; + if (!(token.getParent() instanceof PsiPolyadicExpression)) return false; - PsiBinaryExpression expression = (PsiBinaryExpression)token.getParent(); + PsiPolyadicExpression expression = (PsiPolyadicExpression)token.getParent(); boolean isAndExpression = expression.getOperationTokenType() == JavaTokenType.ANDAND; boolean isOrExpression = expression.getOperationTokenType() == JavaTokenType.OROR; if (!isAndExpression && !isOrExpression) return false; - while (expression.getParent() instanceof PsiBinaryExpression) { - expression = (PsiBinaryExpression)expression.getParent(); + while (expression.getParent() instanceof PsiPolyadicExpression) { + expression = (PsiPolyadicExpression)expression.getParent(); if (isAndExpression && expression.getOperationTokenType() != JavaTokenType.ANDAND) return false; if (isOrExpression && expression.getOperationTokenType() != JavaTokenType.OROR) return false; } @@ -80,16 +79,16 @@ public class SplitIfAction extends PsiElementBaseIntentionAction { PsiJavaToken token = (PsiJavaToken)file.findElementAt(offset); LOG.assertTrue(token.getTokenType() == JavaTokenType.ANDAND || token.getTokenType() == JavaTokenType.OROR); - PsiBinaryExpression expression = (PsiBinaryExpression)token.getParent(); + PsiPolyadicExpression expression = (PsiPolyadicExpression)token.getParent(); PsiIfStatement ifStatement = PsiTreeUtil.getParentOfType(expression, PsiIfStatement.class); LOG.assertTrue(PsiTreeUtil.isAncestor(ifStatement.getCondition(), expression, false)); if (token.getTokenType() == JavaTokenType.ANDAND) { - doAndSplit(ifStatement, expression, editor); + doAndSplit(ifStatement, expression, token, editor); } else if (token.getTokenType() == JavaTokenType.OROR) { - doOrSplit(ifStatement, expression, editor); + doOrSplit(ifStatement, expression, token, editor); } } catch (IncorrectOperationException e) { @@ -97,10 +96,9 @@ public class SplitIfAction extends PsiElementBaseIntentionAction { } } - private static void doAndSplit(PsiIfStatement ifStatement, PsiBinaryExpression expression, Editor editor) throws IncorrectOperationException { - PsiExpression lOperand = expression.getLOperand(); - PsiExpression rOperand = getROperand(expression); - + private static void doAndSplit(PsiIfStatement ifStatement, PsiPolyadicExpression expression, PsiJavaToken token, Editor editor) throws IncorrectOperationException { + PsiExpression lOperand = getLOperands(expression, token); + PsiExpression rOperand = getROperands(expression, token); PsiManager psiManager = ifStatement.getManager(); PsiIfStatement subIf = (PsiIfStatement)ifStatement.copy(); @@ -109,7 +107,8 @@ public class SplitIfAction extends PsiElementBaseIntentionAction { ifStatement.getCondition().replace(RefactoringUtil.unparenthesizeExpression(lOperand)); if (ifStatement.getThenBranch() instanceof PsiBlockStatement) { - PsiBlockStatement blockStmt = (PsiBlockStatement)JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory().createStatementFromText("{}", null); + PsiBlockStatement blockStmt = + (PsiBlockStatement)JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory().createStatementFromText("{}", null); blockStmt = (PsiBlockStatement)CodeStyleManager.getInstance(psiManager.getProject()).reformat(blockStmt); blockStmt = (PsiBlockStatement)ifStatement.getThenBranch().replace(blockStmt); blockStmt.getCodeBlock().add(subIf); @@ -125,17 +124,33 @@ public class SplitIfAction extends PsiElementBaseIntentionAction { editor.getSelectionModel().removeSelection(); } - private static PsiExpression getROperand(PsiBinaryExpression expression) throws IncorrectOperationException { - PsiElement e = expression; - while (!(e.getParent() instanceof PsiIfStatement)) e = e.getParent(); + private static PsiExpression getROperands(PsiPolyadicExpression expression, PsiJavaToken separator) throws IncorrectOperationException { + PsiElement next = separator.getNextSibling(); + if (next instanceof PsiWhiteSpace) next = next.getNextSibling(); + if (next == null) { + throw new IncorrectOperationException("Unable to split '"+expression.getText()+"' at '"+separator+"' (offset "+separator.getStartOffsetInParent()+")"); + } - return JavaPsiFacade.getInstance(expression.getProject()).getElementFactory().createExpressionFromText( - e.getText().substring(expression.getROperand().getTextRange().getStartOffset() - e.getTextRange().getStartOffset()), e.getParent()); + PsiElementFactory factory = JavaPsiFacade.getInstance(expression.getProject()).getElementFactory(); + String rOperands = expression.getText().substring(next.getStartOffsetInParent()); + return factory.createExpressionFromText(rOperands, expression.getParent()); } - private static void doOrSplit(PsiIfStatement ifStatement, PsiBinaryExpression expression, Editor editor) throws IncorrectOperationException { - PsiExpression lOperand = expression.getLOperand(); - PsiExpression rOperand = getROperand(expression); + private static PsiExpression getLOperands(PsiPolyadicExpression expression, PsiJavaToken separator) throws IncorrectOperationException { + PsiElement next = separator; + if (next.getPrevSibling() instanceof PsiWhiteSpace) next = next.getPrevSibling(); + if (next == null) { + throw new IncorrectOperationException("Unable to split '"+expression.getText()+"' left to '"+separator+"' (offset "+separator.getStartOffsetInParent()+")"); + } + + PsiElementFactory factory = JavaPsiFacade.getInstance(expression.getProject()).getElementFactory(); + String rOperands = expression.getText().substring(0, next.getStartOffsetInParent()); + return factory.createExpressionFromText(rOperands, expression.getParent()); + } + + private static void doOrSplit(PsiIfStatement ifStatement, PsiPolyadicExpression expression, PsiJavaToken token, Editor editor) throws IncorrectOperationException { + PsiExpression lOperand = getLOperands(expression, token); + PsiExpression rOperand = getROperands(expression, token); PsiIfStatement secondIf = (PsiIfStatement)ifStatement.copy(); diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java index 128980674a03..e2accc212325 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java @@ -33,10 +33,7 @@ import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; -import java.util.Stack; +import java.util.*; class ControlFlowAnalyzer extends JavaElementVisitor { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.ControlFlowAnalyzer"); @@ -170,7 +167,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor { } else if (op == JavaTokenType.XOREQ) { if (isBoolean) { - generateXorExpression(expression, lExpr, rExpr, type); + generateXorExpression(expression, new PsiExpression[]{lExpr, rExpr}, type); } else { generateDefaultBinop(lExpr, rExpr, type); @@ -873,69 +870,35 @@ class ControlFlowAnalyzer extends JavaElementVisitor { finishElement(expression); } - @Override public void visitBinaryExpression(PsiBinaryExpression expression) { + @Override + public void visitPolyadicExpression(PsiPolyadicExpression expression) { startElement(expression); try { DfaValue dfaValue = myFactory.create(expression); if (dfaValue != null) { addInstruction(new PushInstruction(dfaValue, expression)); + return; + } + IElementType op = expression.getOperationTokenType(); + + PsiExpression[] operands = expression.getOperands(); + if (operands.length <= 1) { + pushUnknown(); + return; + } + PsiType type = expression.getType(); + if (op == JavaTokenType.ANDAND) { + generateAndExpression(operands, type); + } + else if (op == JavaTokenType.OROR) { + generateOrExpression(operands, type); + } + else if (op == JavaTokenType.XOR && PsiType.BOOLEAN.equals(type)) { + generateXorExpression(expression, operands, type); } else { - IElementType op = expression.getOperationTokenType(); - PsiExpression lExpr = expression.getLOperand(); - PsiExpression rExpr = expression.getROperand(); - - if (rExpr == null) { - pushUnknown(); - return; - } - PsiType type = expression.getType(); - if (op == JavaTokenType.ANDAND) { - generateAndExpression(lExpr, rExpr, type); - } - else if (op == JavaTokenType.OROR) { - generateOrExpression(lExpr, rExpr, type); - } - else if (op == JavaTokenType.XOR && PsiType.BOOLEAN.equals(type)) { - generateXorExpression(expression, lExpr, rExpr, type); - } - else { - lExpr.accept(this); - boolean comparing = op == JavaTokenType.EQEQ || op == JavaTokenType.NE; - PsiType lType = lExpr.getType(); - PsiType rType = rExpr.getType(); - - boolean comparingRef = comparing - && !TypeConversionUtil.isPrimitiveAndNotNull(lType) - && !TypeConversionUtil.isPrimitiveAndNotNull(rType); - - boolean comparingPrimitiveNumerics = comparing && - TypeConversionUtil.isPrimitiveAndNotNull(lType) && - TypeConversionUtil.isPrimitiveAndNotNull(rType) && - TypeConversionUtil.isNumericType(lType) && - TypeConversionUtil.isNumericType(rType); - - PsiType castType = comparingPrimitiveNumerics ? PsiType.LONG : type; - - if (!comparingRef) { - generateBoxingUnboxingInstructionFor(lExpr,castType); - } - rExpr.accept(this); - if (!comparingRef) { - generateBoxingUnboxingInstructionFor(rExpr,castType); - } - - String opSign = expression.getOperationSign().getText(); - if ("+".equals(opSign)) { - if (type == null || !type.equalsToText("java.lang.String")) { - opSign = null; - } - } - - PsiElement psiAnchor = expression.isPhysical() ? expression : null; - addInstruction(new BinopInstruction(opSign, psiAnchor, expression.getProject())); - } + generateOther(expression, op, operands, type); } } finally { @@ -943,6 +906,55 @@ class ControlFlowAnalyzer extends JavaElementVisitor { } } + private void generateOther(PsiPolyadicExpression expression, + IElementType op, + PsiExpression[] operands, + PsiType type) { + boolean comparing = op == JavaTokenType.EQEQ || op == JavaTokenType.NE; + PsiExpression lExpr = operands[0]; + lExpr.accept(this); + PsiType lType = lExpr.getType(); + PsiExpression rExpr = operands[1]; + + for (int i = 1; i < operands.length; i++) { + rExpr = operands[i]; + PsiType rType = rExpr.getType(); + + boolean comparingRef = comparing + && !TypeConversionUtil.isPrimitiveAndNotNull(lType) + && !TypeConversionUtil.isPrimitiveAndNotNull(rType); + + boolean comparingPrimitiveNumerics = comparing && + TypeConversionUtil.isPrimitiveAndNotNull(lType) && + TypeConversionUtil.isPrimitiveAndNotNull(rType) && + TypeConversionUtil.isNumericType(lType) && + TypeConversionUtil.isNumericType(rType); + + PsiType castType = comparingPrimitiveNumerics ? PsiType.LONG : type; + + if (!comparingRef) { + generateBoxingUnboxingInstructionFor(lExpr,castType); + } + + lExpr = rExpr; + lType = rType; + + rExpr.accept(this); + if (!comparingRef) { + generateBoxingUnboxingInstructionFor(rExpr,castType); + } + + if (JavaTokenType.PLUS == op) { + if (type == null || !type.equalsToText("java.lang.String")) { + op = null; + } + } + + PsiElement psiAnchor = expression.isPhysical() ? expression : null; + addInstruction(new BinopInstruction(op, psiAnchor, expression.getProject())); + } + } + private void generateBoxingUnboxingInstructionFor(PsiExpression expression, PsiType expectedType) { PsiType exprType = expression.getType(); @@ -966,23 +978,31 @@ class ControlFlowAnalyzer extends JavaElementVisitor { } } - private void generateXorExpression(PsiExpression expression, PsiExpression lExpr, PsiExpression rExpr, final PsiType exprType) { - lExpr.accept(this); - generateBoxingUnboxingInstructionFor(lExpr,exprType); - rExpr.accept(this); - generateBoxingUnboxingInstructionFor(rExpr,exprType); - PsiElement psiAnchor = expression.isPhysical() ? expression : null; - addInstruction(new BinopInstruction("!=", psiAnchor, expression.getProject())); + private void generateXorExpression(PsiExpression expression, PsiExpression[] operands, final PsiType exprType) { + PsiExpression operand = operands[0]; + operand.accept(this); + generateBoxingUnboxingInstructionFor(operand, exprType); + for (int i = 1; i < operands.length; i++) { + operand = operands[i]; + operand.accept(this); + generateBoxingUnboxingInstructionFor(operand, exprType); + PsiElement psiAnchor = expression.isPhysical() ? expression : null; + addInstruction(new BinopInstruction(JavaTokenType.NE, psiAnchor, expression.getProject())); + } } - private void generateOrExpression(PsiExpression lExpr, PsiExpression rExpr, final PsiType exprType) { - lExpr.accept(this); - generateBoxingUnboxingInstructionFor(lExpr,exprType); - addInstruction(new ConditionalGotoInstruction(getStartOffset(rExpr), true, lExpr)); - addInstruction(new PushInstruction(myFactory.getConstFactory().getTrue(), null)); - addInstruction(new GotoInstruction(getEndOffset(rExpr))); - rExpr.accept(this); - generateBoxingUnboxingInstructionFor(rExpr,exprType); + private void generateOrExpression(PsiExpression[] operands, final PsiType exprType) { + for (int i = 0; i < operands.length; i++) { + PsiExpression operand = operands[i]; + operand.accept(this); + generateBoxingUnboxingInstructionFor(operand, exprType); + PsiExpression nextOperand = i == operands.length - 1 ? null : operands[i + 1]; + if (nextOperand != null) { + addInstruction(new ConditionalGotoInstruction(getStartOffset(nextOperand), true, operand)); + addInstruction(new PushInstruction(myFactory.getConstFactory().getTrue(), null)); + addInstruction(new GotoInstruction(getEndOffset(operands[operands.length - 1]))); + } + } } private void generateNonMaccartyExpression(boolean and, PsiExpression lExpression, PsiExpression rExpression, final PsiType exprType) { @@ -1008,21 +1028,30 @@ class ControlFlowAnalyzer extends JavaElementVisitor { overPushSuccess.setOffset(pushSuccess.getIndex() + 1); } - private void generateAndExpression(PsiExpression lExpr, PsiExpression rExpr, final PsiType exprType) { - lExpr.accept(this); - generateBoxingUnboxingInstructionFor(lExpr, exprType); - ConditionalGotoInstruction firstTrueGoto = new ConditionalGotoInstruction(-1, true, lExpr); - addInstruction(firstTrueGoto); - rExpr.accept(this); - generateBoxingUnboxingInstructionFor(rExpr, exprType); + private void generateAndExpression(PsiExpression[] operands, final PsiType exprType) { + List branchToFail = new ArrayList(); + for (int i = 0; i < operands.length; i++) { + PsiExpression operand = operands[i]; + operand.accept(this); + generateBoxingUnboxingInstructionFor(operand, exprType); + PsiExpression nextOperand = i == operands.length - 1 ? null : operands[i + 1]; - GotoInstruction overPushFalse = new GotoInstruction(-1); - addInstruction(overPushFalse); + if (nextOperand != null) { + ConditionalGotoInstruction onFail = new ConditionalGotoInstruction(-1, true, operand); + branchToFail.add(onFail); + addInstruction(onFail); + } + } + + GotoInstruction toSuccess = new GotoInstruction(-1); + addInstruction(toSuccess); PushInstruction pushFalse = new PushInstruction(myFactory.getConstFactory().getFalse(), null); addInstruction(pushFalse); + for (ConditionalGotoInstruction toFail : branchToFail) { + toFail.setOffset(pushFalse.getIndex()); + } + toSuccess.setOffset(pushFalse.getIndex()+1); - firstTrueGoto.setOffset(pushFalse.getIndex()); - overPushFalse.setOffset(pushFalse.getIndex() + 1); } @Override public void visitClassObjectAccessExpression(PsiClassObjectAccessExpression expression) { @@ -1196,7 +1225,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor { pushParameters(params, true, !testng); addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null)); - addInstruction(new BinopInstruction("==", null, expression.getProject())); + addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, expression.getProject())); conditionalExit(exitPoint, false); return true; } @@ -1204,7 +1233,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor { pushParameters(params, true, !testng); addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null)); - addInstruction(new BinopInstruction("==", null, expression.getProject())); + addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, expression.getProject())); conditionalExit(exitPoint, true); return true; } diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java index 7cdb9aaaf144..ec33cc801cc1 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java @@ -27,6 +27,7 @@ package com.intellij.codeInspection.dataFlow; import com.intellij.codeInspection.dataFlow.value.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressManager; +import com.intellij.psi.JavaTokenType; import com.intellij.psi.PsiType; import com.intellij.psi.PsiVariable; import com.intellij.psi.util.TypeConversionUtil; @@ -261,16 +262,16 @@ public class DfaMemoryStateImpl implements DfaMemoryState { getVariableState(var).setValue(value); if (value instanceof DfaNotNullValue) { DfaTypeValue dfaType = myFactory.getTypeFactory().create(((DfaNotNullValue)value).getType()); - DfaRelationValue dfaInstanceof = myFactory.getRelationFactory().create(var, dfaType, "instanceof", false); + DfaRelationValue dfaInstanceof = myFactory.getRelationFactory().create(var, dfaType, JavaTokenType.INSTANCEOF_KEYWORD, false); applyCondition(dfaInstanceof); applyCondition(compareToNull(var, true)); } else if (value instanceof DfaTypeValue) { - DfaRelationValue dfaInstanceof = myFactory.getRelationFactory().create(var, value, "instanceof", false); + DfaRelationValue dfaInstanceof = myFactory.getRelationFactory().create(var, value, JavaTokenType.INSTANCEOF_KEYWORD, false); applyInstanceofOrNull(dfaInstanceof); } else { - DfaRelationValue dfaEqual = myFactory.getRelationFactory().create(var, value, "==", false); + DfaRelationValue dfaEqual = myFactory.getRelationFactory().create(var, value, JavaTokenType.EQEQ, false); if (dfaEqual == null) return; applyCondition(dfaEqual); @@ -514,7 +515,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState { DfaVariableValue dfaNormalVar = isNegated ? (DfaVariableValue)dfaVar.createNegated() : dfaVar; DfaConstValue dfaTrue = myFactory.getConstFactory().getTrue(); final DfaValue boxedTrue = myFactory.getBoxedFactory().createBoxed(dfaTrue); - DfaRelationValue dfaEqualsTrue = myFactory.getRelationFactory().create(dfaNormalVar, boxedTrue, "==", isNegated); + DfaRelationValue dfaEqualsTrue = myFactory.getRelationFactory().create(dfaNormalVar, boxedTrue, JavaTokenType.EQEQ, isNegated); return applyCondition(dfaEqualsTrue); } @@ -523,7 +524,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState { boolean isNegated = dfaVar.isNegated(); DfaVariableValue dfaNormalVar = isNegated ? (DfaVariableValue)dfaVar.createNegated() : dfaVar; DfaConstValue dfaTrue = myFactory.getConstFactory().getTrue(); - DfaRelationValue dfaEqualsTrue = myFactory.getRelationFactory().create(dfaNormalVar, dfaTrue, "==", isNegated); + DfaRelationValue dfaEqualsTrue = myFactory.getRelationFactory().create(dfaNormalVar, dfaTrue, JavaTokenType.EQEQ, isNegated); return applyCondition(dfaEqualsTrue); } @@ -643,7 +644,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState { @Nullable private DfaRelationValue compareToNull(DfaValue dfaVar, boolean negated) { DfaConstValue dfaNull = myFactory.getConstFactory().getNull(); - return myFactory.getRelationFactory().create(dfaVar, dfaNull, "==", negated); + return myFactory.getRelationFactory().create(dfaVar, dfaNull, JavaTokenType.EQEQ, negated); } public DfaVariableState getVariableState(DfaVariableValue dfaVar) { diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java index f9d05fd30278..6b3e025cb79b 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java @@ -15,6 +15,11 @@ */ package com.intellij.codeInspection.dataFlow; +import com.intellij.codeInspection.dataFlow.instructions.AssignInstruction; +import com.intellij.codeInspection.dataFlow.instructions.Instruction; +import com.intellij.codeInspection.dataFlow.instructions.PushInstruction; +import com.intellij.codeInspection.dataFlow.value.DfaValue; +import com.intellij.codeInspection.dataFlow.value.DfaVariableValue; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.MultiValuesMap; import com.intellij.openapi.util.Ref; @@ -28,14 +33,10 @@ import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.NullableFunction; import com.intellij.util.containers.ContainerUtil; -import com.intellij.codeInspection.dataFlow.instructions.PushInstruction; -import com.intellij.codeInspection.dataFlow.instructions.AssignInstruction; -import com.intellij.codeInspection.dataFlow.instructions.Instruction; -import com.intellij.codeInspection.dataFlow.value.DfaVariableValue; -import com.intellij.codeInspection.dataFlow.value.DfaValue; +import com.intellij.util.containers.Stack; import gnu.trove.THashSet; -import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.*; @@ -213,16 +214,14 @@ public class DfaUtil { if (expression == null) return false; if (expression instanceof PsiLiteralExpression) return true; if (expression instanceof PsiBinaryExpression) { - final LinkedList stack = new LinkedList(); + Stack stack = new Stack(); stack.add(expression); while (!stack.isEmpty()) { - final PsiExpression psiExpression = stack.removeFirst(); - if (psiExpression instanceof PsiBinaryExpression) { - final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)psiExpression; - stack.addLast(binaryExpression.getLOperand()); - final PsiExpression right = binaryExpression.getROperand(); - if (right != null) { - stack.addLast(right); + PsiExpression psiExpression = stack.pop(); + if (psiExpression instanceof PsiPolyadicExpression) { + PsiPolyadicExpression binaryExpression = (PsiPolyadicExpression)psiExpression; + for (PsiExpression op : binaryExpression.getOperands()) { + stack.push(op); } } else if (!(psiExpression instanceof PsiLiteralExpression)) { diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/InstructionFactory.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/InstructionFactory.java index f0b8fd770126..dd618ae606e7 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/InstructionFactory.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/InstructionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2011 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,12 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.intellij.codeInspection.dataFlow; /** - * @author Gregory.Shrago + * User: cdr */ public class InstructionFactory { - } diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java index 7ae9a281bbea..3ed81ec2dc72 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java @@ -43,11 +43,11 @@ import java.util.Set; public class StandardDataFlowRunner extends AnnotationsAwareDataFlowRunner { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.DataFlowRunner"); - private final HashSet myNPEInstructions = new HashSet(); - private final HashSet myCCEInstructions = new HashSet(); - private final HashSet myNullableArguments = new HashSet(); - private final HashSet myNullableAssignments = new HashSet(); - private final HashSet myNullableReturns = new HashSet(); + private final Set myNPEInstructions = new HashSet(); + private final Set myCCEInstructions = new HashSet(); + private final Set myNullableArguments = new HashSet(); + private final Set myNullableAssignments = new HashSet(); + private final Set myNullableReturns = new HashSet(); private final boolean mySuggestNullableAnnotations; private boolean myInNullableMethod = false; diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java index 02cce92a84ea..1d0c8aef988e 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java @@ -20,6 +20,7 @@ import com.intellij.codeInsight.NullableNotNullManager; import com.intellij.codeInspection.dataFlow.instructions.*; import com.intellij.codeInspection.dataFlow.value.*; import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.FactoryMap; @@ -136,7 +137,7 @@ public class StandardInstructionVisitor extends InstructionVisitor { DfaValue dfaExpr = factory.create(instruction.getCasted()); if (dfaExpr != null) { DfaTypeValue dfaType = factory.getTypeFactory().create(instruction.getCastTo()); - DfaRelationValue dfaInstanceof = factory.getRelationFactory().create(dfaExpr, dfaType, "instanceof", false); + DfaRelationValue dfaInstanceof = factory.getRelationFactory().create(dfaExpr, dfaType, JavaTokenType.INSTANCEOF_KEYWORD, false); if (dfaInstanceof != null && !memState.applyInstanceofOrNull(dfaInstanceof)) { onInstructionProducesCCE(instruction, runner); } @@ -239,12 +240,12 @@ public class StandardInstructionVisitor extends InstructionVisitor { DfaValue dfaRight = memState.pop(); DfaValue dfaLeft = memState.pop(); - final String opSign = instruction.getOperationSign(); + final IElementType opSign = instruction.getOperationSign(); if (opSign != null) { final DfaValueFactory factory = runner.getFactory(); - if (("==".equals(opSign) || "!=".equals(opSign)) && + if ((JavaTokenType.EQEQ == opSign || JavaTokenType.NE == opSign) && dfaLeft instanceof DfaConstValue && dfaRight instanceof DfaConstValue) { - boolean negated = "!=".equals(opSign) ^ (memState.canBeNaN(dfaLeft) || memState.canBeNaN(dfaRight)); + boolean negated = (JavaTokenType.NE == opSign) ^ (memState.canBeNaN(dfaLeft) || memState.canBeNaN(dfaRight)); if (dfaLeft == dfaRight ^ negated) { memState.push(factory.getConstFactory().getTrue()); instruction.setTrueReachable(); @@ -282,7 +283,7 @@ public class StandardInstructionVisitor extends InstructionVisitor { return states.toArray(new DfaInstructionState[states.size()]); } - else if ("+".equals(opSign)) { + else if (JavaTokenType.PLUS == opSign) { memState.push(instruction.getNonNullStringValue(factory)); instruction.setTrueReachable(); // Not a branching instruction actually. instruction.setFalseReachable(); diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java index 3f79ddd4dd90..5e13d70118f2 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java @@ -31,21 +31,18 @@ import com.intellij.codeInspection.dataFlow.InstructionVisitor; import com.intellij.codeInspection.dataFlow.value.DfaValue; import com.intellij.codeInspection.dataFlow.value.DfaValueFactory; import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiClassType; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiManager; -import com.intellij.psi.PsiType; +import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; -import org.jetbrains.annotations.NonNls; +import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; public class BinopInstruction extends BranchingInstruction { - private final String myOperationSign; + private final IElementType myOperationSign; private final Project myProject; - public BinopInstruction(@NonNls String opSign, PsiElement psiAnchor, @NotNull Project project) { + public BinopInstruction(IElementType opSign, PsiElement psiAnchor, @NotNull Project project) { myProject = project; - if (opSign != null && ("==".equals(opSign) || "!=".equals(opSign) || "instanceof".equals(opSign) || "+".equals(opSign))) { + if (JavaTokenType.EQEQ == opSign || JavaTokenType.NE == opSign || JavaTokenType.INSTANCEOF_KEYWORD == opSign || JavaTokenType.PLUS == opSign) { myOperationSign = opSign; } else { @@ -71,7 +68,7 @@ public class BinopInstruction extends BranchingInstruction { return "BINOP " + myOperationSign; } - public String getOperationSign() { + public IElementType getOperationSign() { return myOperationSign; } } diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/instructions/ConditionalGotoInstruction.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/instructions/ConditionalGotoInstruction.java index ccd3a3f247b9..4459c35d1b40 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/instructions/ConditionalGotoInstruction.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/instructions/ConditionalGotoInstruction.java @@ -51,7 +51,7 @@ public class ConditionalGotoInstruction extends BranchingInstruction { } public String toString() { - return "cond_goto " + myOffset; + return (isNegated() ? "!":"") + "cond?_goto " + myOffset; } public int getOffset() { diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/instructions/InstanceofInstruction.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/instructions/InstanceofInstruction.java index 1f3e65810772..68a8282a5135 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/instructions/InstanceofInstruction.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/instructions/InstanceofInstruction.java @@ -20,10 +20,7 @@ import com.intellij.codeInspection.dataFlow.DfaInstructionState; import com.intellij.codeInspection.dataFlow.DfaMemoryState; import com.intellij.codeInspection.dataFlow.InstructionVisitor; import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiExpression; -import com.intellij.psi.PsiKeyword; -import com.intellij.psi.PsiType; +import com.intellij.psi.*; import org.jetbrains.annotations.NotNull; /** @@ -34,7 +31,7 @@ public class InstanceofInstruction extends BinopInstruction { @NotNull private final PsiType myCastType; public InstanceofInstruction(PsiElement psiAnchor, @NotNull Project project, PsiExpression left, PsiType castType) { - super(PsiKeyword.INSTANCEOF, psiAnchor, project); + super(JavaTokenType.INSTANCEOF_KEYWORD, psiAnchor, project); myLeft = left; myCastType = castType; } diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java index 63246a6f20f8..9a25452431cd 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java @@ -25,6 +25,8 @@ package com.intellij.codeInspection.dataFlow.value; import com.intellij.openapi.util.Comparing; +import com.intellij.psi.JavaTokenType; +import com.intellij.psi.tree.IElementType; import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; @@ -34,7 +36,7 @@ import java.util.ArrayList; public class DfaRelationValue extends DfaValue { private DfaValue myLeftOperand; private DfaValue myRightOperand; - private String myRelation; + private IElementType myRelation; private boolean myIsNegated; public static class Factory { @@ -49,9 +51,9 @@ public class DfaRelationValue extends DfaValue { } @Nullable - public DfaRelationValue create(DfaValue dfaLeft, DfaValue dfaRight, @NonNls String relation, boolean negated) { - if (dfaRight instanceof DfaTypeValue && !"instanceof".equals(relation)) return null; - if ("+".equals(relation)) return null; + public DfaRelationValue create(DfaValue dfaLeft, DfaValue dfaRight, IElementType relation, boolean negated) { + if (dfaRight instanceof DfaTypeValue && JavaTokenType.INSTANCEOF_KEYWORD != relation) return null; + if (JavaTokenType.PLUS == relation) return null; if (dfaLeft instanceof DfaVariableValue || dfaLeft instanceof DfaBoxedValue || dfaLeft instanceof DfaUnboxedValue || dfaRight instanceof DfaVariableValue || dfaRight instanceof DfaBoxedValue || dfaRight instanceof DfaUnboxedValue) { @@ -72,21 +74,21 @@ public class DfaRelationValue extends DfaValue { } } - private DfaRelationValue createCanonicalRelation(String relation, + private DfaRelationValue createCanonicalRelation(IElementType relation, boolean negated, final DfaValue dfaLeft, final DfaValue dfaRight) { // To canonical form. - if ("!=".equals(relation)) { - relation = "=="; + if (JavaTokenType.NE == relation) { + relation = JavaTokenType.EQEQ; negated = !negated; } - else if ("<".equals(relation)) { - relation = ">="; + else if (JavaTokenType.LT == relation) { + relation = JavaTokenType.GE; negated = !negated; } - else if ("<=".equals(relation)) { - relation = ">"; + else if (JavaTokenType.LE == relation) { + relation = JavaTokenType.GT; negated = !negated; } @@ -112,18 +114,18 @@ public class DfaRelationValue extends DfaValue { return result; } - private static String getSymmetricOperation(String sign) { - if ("<".equals(sign)) { - return ">"; + private static IElementType getSymmetricOperation(IElementType sign) { + if (JavaTokenType.LT == sign) { + return JavaTokenType.GT; } - else if (">=".equals(sign)) { - return "<="; + else if (JavaTokenType.GE == sign) { + return JavaTokenType.LE; } - else if (">".equals(sign)) { - return "<"; + else if (JavaTokenType.GT == sign) { + return JavaTokenType.LT; } - else if ("<=".equals(sign)) { - return ">="; + else if (JavaTokenType.LE == sign) { + return JavaTokenType.GE; } return sign; @@ -134,7 +136,7 @@ public class DfaRelationValue extends DfaValue { super(factory); } - private DfaRelationValue(DfaValue myLeftOperand, DfaValue myRightOperand, String myRelation, boolean myIsNegated, + private DfaRelationValue(DfaValue myLeftOperand, DfaValue myRightOperand, IElementType myRelation, boolean myIsNegated, DfaValueFactory factory) { super(factory); this.myLeftOperand = myLeftOperand; @@ -161,9 +163,9 @@ public class DfaRelationValue extends DfaValue { private boolean hardEquals(DfaRelationValue rel) { return Comparing.equal(rel.myLeftOperand,myLeftOperand) - && Comparing.equal(rel.myRightOperand,myRightOperand) && - rel.myRelation.equals(myRelation) && - rel.myIsNegated == myIsNegated; + && Comparing.equal(rel.myRightOperand,myRightOperand) && + rel.myRelation == myRelation && + rel.myIsNegated == myIsNegated; } @NonNls public String toString() { diff --git a/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java b/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java index ee6cb3bac458..8c8642e46870 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java @@ -171,28 +171,34 @@ public class ExpressionParser { @Nullable private static PsiBuilder.Marker parseBinary(final PsiBuilder builder, final ExprType type, final TokenSet ops) { - PsiBuilder.Marker left = parseExpression(builder, type); - if (left == null) return null; + PsiBuilder.Marker result = parseExpression(builder, type); + if (result == null) return null; + int operandCount = 1; + IElementType tokenType = getGtTokenType(builder); + IElementType currentExprTokenType = tokenType; while (true) { - final IElementType tokenType = getGtTokenType(builder); if (tokenType == null || !ops.contains(tokenType)) break; - final PsiBuilder.Marker binary = left.precede(); advanceGtToken(builder, tokenType); final PsiBuilder.Marker right = parseExpression(builder, type); - if (right == null) { - error(builder, JavaErrorMessages.message("expected.expression")); - binary.done(JavaElementType.BINARY_EXPRESSION); - return binary; + operandCount++; + tokenType = getGtTokenType(builder); + if (tokenType == null || !ops.contains(tokenType) || tokenType != currentExprTokenType || right == null) { + // save + result = result.precede(); + if (right == null) { + error(builder, JavaErrorMessages.message("expected.expression")); + } + result.done(operandCount > 2 ? JavaElementType.POLYADIC_EXPRESSION : JavaElementType.BINARY_EXPRESSION); + if (right == null) break; + currentExprTokenType = tokenType; + operandCount = 1; } - - binary.done(JavaElementType.BINARY_EXPRESSION); - left = binary; } - return left; + return result; } @Nullable diff --git a/java/java-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java b/java/java-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java index 858707bacaf9..f92bcd8ba8ec 100644 --- a/java/java-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java +++ b/java/java-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java @@ -1301,58 +1301,71 @@ class ControlFlowAnalyzer extends JavaJspElementVisitor { finishElement(expression); } - @Override public void visitBinaryExpression(PsiBinaryExpression expression) { - startElement(expression); + private static enum Shortcut { + NO_SHORTCUT, // a || b + SKIP_CURRENT_OPERAND, // false || a + STOP_EXPRESSION // true || a + } - final PsiExpression lOperand = expression.getLOperand(); - final PsiExpression rOperand = expression.getROperand(); + @Override + public void visitPolyadicExpression(PsiPolyadicExpression expression) { + startElement(expression); IElementType signTokenType = expression.getOperationTokenType(); boolean isAndAnd = signTokenType == JavaTokenType.ANDAND; boolean isOrOr = signTokenType == JavaTokenType.OROR; - if ((isAndAnd || isOrOr) && myEnabledShortCircuit) { - Object exprValue = myConstantEvaluationHelper.computeConstantExpression(lOperand); - Boolean lvalue = null; - if (exprValue instanceof Boolean) { - myCurrentFlow.setConstantConditionOccurred(true); - lvalue = shouldCalculateConstantExpression(expression) ? (Boolean)exprValue : null; - } - exprValue = myConstantEvaluationHelper.computeConstantExpression(rOperand); - Boolean rvalue = null; - if (exprValue instanceof Boolean) { - myCurrentFlow.setConstantConditionOccurred(true); - rvalue = shouldCalculateConstantExpression(expression) ? (Boolean)exprValue : null; - } - Boolean doShortcut; - if (lvalue != null) { - doShortcut = lvalue.booleanValue() != isAndAnd; - } - else if (rvalue != null && rvalue.booleanValue() != isAndAnd) { - doShortcut = Boolean.TRUE; - } - else { - doShortcut = null; - } - - generateLOperand(lOperand, rOperand, signTokenType); - BranchingInstruction.Role role = isAndAnd ? myEndJumpRoles.peek() : myStartJumpRoles.peek(); - PsiElement gotoElement = isAndAnd ? myEndStatementStack.peekElement() : myStartStatementStack.peekElement(); - boolean gotoIsAtStart = isAndAnd ? myEndStatementStack.peekAtStart() : myStartStatementStack.peekAtStart(); - if (doShortcut == null) { - myCurrentFlow.addInstruction(new ConditionalGoToInstruction(0, role, lOperand)); - addElementOffsetLater(gotoElement, gotoIsAtStart); - } - else if (doShortcut.booleanValue()) { - myCurrentFlow.addInstruction(new GoToInstruction(0, role)); - addElementOffsetLater(gotoElement, gotoIsAtStart); - } - } - else { - generateLOperand(lOperand, rOperand, signTokenType); - } - if (rOperand != null) { - rOperand.accept(this); + PsiExpression[] operands = expression.getOperands(); + Boolean lValue = isAndAnd; + PsiExpression lOperand = null; + Boolean rValue = null; + for (int i = 0; i < operands.length; i++) { + PsiExpression rOperand = operands[i]; + if ((isAndAnd || isOrOr) && myEnabledShortCircuit) { + Object exprValue = myConstantEvaluationHelper.computeConstantExpression(rOperand); + if (exprValue instanceof Boolean) { + myCurrentFlow.setConstantConditionOccurred(true); + rValue = shouldCalculateConstantExpression(expression) ? (Boolean)exprValue : null; + } + + BranchingInstruction.Role role = isAndAnd ? myEndJumpRoles.peek() : myStartJumpRoles.peek(); + PsiElement gotoElement = isAndAnd ? myEndStatementStack.peekElement() : myStartStatementStack.peekElement(); + boolean gotoIsAtStart = isAndAnd ? myEndStatementStack.peekAtStart() : myStartStatementStack.peekAtStart(); + + Shortcut shortcut; + if (lValue != null) { + shortcut = lValue.booleanValue() == isOrOr ? Shortcut.STOP_EXPRESSION : Shortcut.SKIP_CURRENT_OPERAND; + } + else if (rValue != null && rValue.booleanValue() == isOrOr) { + shortcut = Shortcut.STOP_EXPRESSION; + } + else { + shortcut = Shortcut.NO_SHORTCUT; + } + + switch (shortcut) { + case NO_SHORTCUT: + assert lOperand != null; + myCurrentFlow.addInstruction(new ConditionalGoToInstruction(0, role, lOperand)); + addElementOffsetLater(gotoElement, gotoIsAtStart); + + break; + case STOP_EXPRESSION: + if (lOperand != null) { + myCurrentFlow.addInstruction(new GoToInstruction(0, role)); + addElementOffsetLater(gotoElement, gotoIsAtStart); + } + break; + case SKIP_CURRENT_OPERAND: + break; + } + + if (shortcut == Shortcut.STOP_EXPRESSION) break; + } + generateLOperand(rOperand, i == operands.length-1 ? null : operands[i+1],signTokenType); + + lOperand = rOperand; + lValue = rValue; } finishElement(expression); diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java b/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java index 2606eb6d768c..fa7ab1ee9ef0 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java @@ -323,7 +323,8 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo @Nullable protected Alignment createChildAlignment() { - final IElementType nodeType = myNode.getElementType(); + IElementType nodeType = myNode.getElementType(); + if (nodeType == JavaElementType.POLYADIC_EXPRESSION) nodeType = JavaElementType.BINARY_EXPRESSION; if (nodeType == JavaElementType.ASSIGNMENT_EXPRESSION) { if (myNode.getTreeParent() != null && myNode.getTreeParent().getElementType() == JavaElementType.ASSIGNMENT_EXPRESSION @@ -368,7 +369,6 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo else if (nodeType == JavaElementType.MODIFIER_LIST || nodeType == JavaElementType.NEW_EXPRESSION) { return myAlignment; } - else { return null; } @@ -407,10 +407,10 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo } private boolean shouldInheritAlignment() { - if (myNode.getElementType() == JavaElementType.BINARY_EXPRESSION) { + if (myNode instanceof PsiPolyadicExpression) { final ASTNode treeParent = myNode.getTreeParent(); - if (treeParent != null && treeParent.getElementType() == JavaElementType.BINARY_EXPRESSION) { - return FormattingAstUtil.binaryExpressionHasTheSamePriority(myNode, treeParent); + if (treeParent instanceof PsiPolyadicExpression) { + return FormattingAstUtil.areSamePriorityBinaryExpressions(myNode, treeParent); } } return false; @@ -452,7 +452,8 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo arrangeChildAlignment(child, alignmentStrategy))); } else { - final IElementType nodeType = myNode.getElementType(); + IElementType nodeType = myNode.getElementType(); + if (nodeType == JavaElementType.POLYADIC_EXPRESSION) nodeType = JavaElementType.BINARY_EXPRESSION; if (childType == JavaTokenType.LBRACE && nodeType == JavaElementType.ARRAY_INITIALIZER_EXPRESSION) { final Wrap wrap = Wrap.createWrap(getWrapType(mySettings.ARRAY_INITIALIZER_WRAP), false); @@ -524,7 +525,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo child = processField(result, child, alignmentStrategy, defaultWrap, childIndent); } else if (childType == JavaElementType.LOCAL_VARIABLE - || (childType == JavaElementType.DECLARATION_STATEMENT && myNode.getElementType() == JavaElementType.METHOD)) + || childType == JavaElementType.DECLARATION_STATEMENT && myNode.getElementType() == JavaElementType.METHOD) { result.add(new SimpleJavaBlock(child, defaultWrap, alignmentStrategy, childIndent, mySettings)); } @@ -545,8 +546,8 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo if (block instanceof AbstractJavaBlock) { final AbstractJavaBlock javaBlock = (AbstractJavaBlock)block; - if ((nodeType == JavaElementType.METHOD_CALL_EXPRESSION && childType == JavaElementType.REFERENCE_EXPRESSION) - || (nodeType == JavaElementType.REFERENCE_EXPRESSION && childType == JavaElementType.METHOD_CALL_EXPRESSION)) + if (nodeType == JavaElementType.METHOD_CALL_EXPRESSION && childType == JavaElementType.REFERENCE_EXPRESSION + || nodeType == JavaElementType.REFERENCE_EXPRESSION && childType == JavaElementType.METHOD_CALL_EXPRESSION) { javaBlock.setReservedWrap(getReservedWrap(nodeType), nodeType); javaBlock.setReservedWrap(getReservedWrap(childType), childType); diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/FormattingAstUtil.java b/java/java-impl/src/com/intellij/psi/formatter/java/FormattingAstUtil.java index 2d7c70271602..b39ab7499d35 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/FormattingAstUtil.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/FormattingAstUtil.java @@ -16,9 +16,8 @@ package com.intellij.psi.formatter.java; import com.intellij.lang.ASTNode; -import com.intellij.psi.PsiBinaryExpression; +import com.intellij.psi.PsiPolyadicExpression; import com.intellij.psi.TokenType; -import com.intellij.psi.impl.source.SourceTreeToPsiMap; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; @@ -136,23 +135,23 @@ public class FormattingAstUtil { } /** - * Allows to check if given AST nodes refer to binary expressions and have the same priority. + * Allows to check if given AST nodes refer to binary expressions which have the same priority. * * @param node1 node to check * @param node2 node to check * @return true if given nodes are binary expressions and have the same priority; * false otherwise */ - public static boolean binaryExpressionHasTheSamePriority(ASTNode node1, ASTNode node2) { + public static boolean areSamePriorityBinaryExpressions(ASTNode node1, ASTNode node2) { if (node1 == null || node2 == null) { return false; } - if (node1.getElementType() != JavaElementType.BINARY_EXPRESSION || node2.getElementType() != JavaElementType.BINARY_EXPRESSION) { + if (!(node1 instanceof PsiPolyadicExpression) || !(node2 instanceof PsiPolyadicExpression)) { return false; } - PsiBinaryExpression expression1 = (PsiBinaryExpression)SourceTreeToPsiMap.treeElementToPsi(node1); - PsiBinaryExpression expression2 = (PsiBinaryExpression)SourceTreeToPsiMap.treeElementToPsi(node2); + PsiPolyadicExpression expression1 = (PsiPolyadicExpression)node1; + PsiPolyadicExpression expression2 = (PsiPolyadicExpression)node2; return expression1.getOperationTokenType() == expression2.getOperationTokenType(); } } diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java index 0f3f7cb93e7b..a20263688e1f 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java @@ -889,10 +889,10 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { } } - @Override public void visitBinaryExpression(PsiBinaryExpression expression) { - PsiJavaToken sign = expression.getOperationSign(); - IElementType i = sign.getTokenType(); + @Override + public void visitPolyadicExpression(PsiPolyadicExpression expression) { if (myRole1 == ChildRole.OPERATION_SIGN || myRole2 == ChildRole.OPERATION_SIGN) { + IElementType i = expression.getOperationTokenType(); if (i == JavaTokenType.OROR || i == JavaTokenType.ANDAND) { createSpaceInCode(mySettings.SPACE_AROUND_LOGICAL_OPERATORS); } diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildBlockWrapFactory.java b/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildBlockWrapFactory.java index 8bcf0e7d4dfa..31fef7db5ef3 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildBlockWrapFactory.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildBlockWrapFactory.java @@ -19,6 +19,7 @@ import com.intellij.formatting.ASTBlock; import com.intellij.formatting.Wrap; import com.intellij.formatting.WrapType; import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiPolyadicExpression; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.formatter.java.FormattingAstUtil; import com.intellij.psi.formatter.java.wrap.ReservedWrapsProvider; @@ -55,17 +56,17 @@ public class JavaChildBlockWrapFactory { if (nodeType == JavaElementType.EXTENDS_LIST || nodeType == JavaElementType.IMPLEMENTS_LIST) { return Wrap.createWrap(settings.EXTENDS_LIST_WRAP, false); } - else if (nodeType == JavaElementType.BINARY_EXPRESSION) { + else if (node instanceof PsiPolyadicExpression) { Wrap actualWrap = wrap != null ? wrap : reservedWrapsProvider.getReservedWrap(JavaElementType.BINARY_EXPRESSION); if (actualWrap == null) { return Wrap.createWrap(settings.BINARY_OPERATION_WRAP, false); } else { - if (!FormattingAstUtil.binaryExpressionHasTheSamePriority(node, node.getTreeParent())) { - return Wrap.createChildWrap(actualWrap, WrapType.byLegacyRepresentation(settings.BINARY_OPERATION_WRAP), false); + if (FormattingAstUtil.areSamePriorityBinaryExpressions(node, node.getTreeParent())) { + return actualWrap; } else { - return actualWrap; + return Wrap.createChildWrap(actualWrap, WrapType.byLegacyRepresentation(settings.BINARY_OPERATION_WRAP), false); } } } diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java b/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java index c6bd33d4ce1a..2caaf432705c 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java @@ -19,6 +19,7 @@ import com.intellij.formatting.Wrap; import com.intellij.formatting.WrapType; import com.intellij.lang.ASTNode; import com.intellij.psi.JavaTokenType; +import com.intellij.psi.PsiPolyadicExpression; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.formatter.java.FormattingAstUtil; import com.intellij.psi.formatter.java.wrap.JavaWrapManager; @@ -27,6 +28,7 @@ import com.intellij.psi.impl.source.tree.ChildRole; import com.intellij.psi.impl.source.tree.CompositeElement; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.tree.IElementType; +import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.Nullable; /** @@ -61,15 +63,14 @@ public class JavaChildWrapArranger { */ @SuppressWarnings({"MethodMayBeStatic"}) @Nullable - public Wrap arrange(ASTNode child, ASTNode parent, CodeStyleSettings settings, Wrap suggestedWrap, - ReservedWrapsProvider reservedWrapsProvider) - { - final ASTNode directParent = child.getTreeParent(); + public Wrap arrange(ASTNode child, ASTNode parent, CodeStyleSettings settings, Wrap suggestedWrap, ReservedWrapsProvider reservedWrapsProvider) { + ASTNode directParent = child.getTreeParent(); int role = ((CompositeElement)directParent).getChildRole(child); final IElementType nodeType = parent.getElementType(); - if (nodeType == JavaElementType.BINARY_EXPRESSION) { + if (parent instanceof PsiPolyadicExpression) { if (role == ChildRole.OPERATION_SIGN && !settings.BINARY_OPERATION_SIGN_ON_NEXT_LINE) return null; - if (role == ChildRole.ROPERAND && settings.BINARY_OPERATION_SIGN_ON_NEXT_LINE) return null; + boolean rOperand = ArrayUtil.indexOf(((PsiPolyadicExpression)parent).getOperands(), child.getPsi()) > 0; + if (settings.BINARY_OPERATION_SIGN_ON_NEXT_LINE && rOperand) return null; return suggestedWrap; } final IElementType childType = child.getElementType(); diff --git a/java/java-impl/src/com/intellij/psi/impl/ConstantExpressionVisitor.java b/java/java-impl/src/com/intellij/psi/impl/ConstantExpressionVisitor.java index 6555f82162c2..60fadca3a733 100644 --- a/java/java-impl/src/com/intellij/psi/impl/ConstantExpressionVisitor.java +++ b/java/java-impl/src/com/intellij/psi/impl/ConstantExpressionVisitor.java @@ -105,26 +105,39 @@ class ConstantExpressionVisitor extends JavaElementVisitor implements PsiConstan } @Override - public void visitBinaryExpression(PsiBinaryExpression expression) { - Object lOperandValue = getStoredValue(expression.getLOperand()); - if (lOperandValue == null) { + public void visitPolyadicExpression(PsiPolyadicExpression expression) { + PsiExpression[] operands = expression.getOperands(); + Object lValue = getStoredValue(operands[0]); + if (lValue == null) { myResult = null; return; } - - Object rOperandValue = getStoredValue(expression.getROperand()); - if (rOperandValue == null) { - myResult = null; - return; + IElementType tokenType = expression.getOperationTokenType(); + for (int i = 1; i < operands.length; i++) { + PsiExpression operand = operands[i]; + Object rValue = getStoredValue(operand); + if (rValue == null) { + myResult = null; + break; + } + myResult = compute(lValue, rValue, tokenType, expression); + if (myResult == null) { + break; + } + lValue = myResult; } + if (myResult instanceof String) { + myResult = myInterner.intern((String)myResult); + } + } - PsiJavaToken operationSign = expression.getOperationSign(); - final IElementType tokenType = operationSign.getTokenType(); - + private Object compute(Object lOperandValue, Object rOperandValue, IElementType tokenType, PsiElement expression) { Object value = null; if (tokenType == JavaTokenType.PLUS) { if (lOperandValue instanceof String || rOperandValue instanceof String) { - value = myInterner.intern(lOperandValue.toString() + rOperandValue.toString()); + String l = lOperandValue.toString(); + String r = rOperandValue.toString(); + value = l + r; } else { if (lOperandValue instanceof Character) lOperandValue = Integer.valueOf(((Character)lOperandValue).charValue()); @@ -182,27 +195,23 @@ class ConstantExpressionVisitor extends JavaElementVisitor implements PsiConstan } else if (tokenType == JavaTokenType.ANDAND) { if (lOperandValue instanceof Boolean && !((Boolean)lOperandValue).booleanValue()) { - myResult = Boolean.FALSE; - return; + value = Boolean.FALSE; } - if (rOperandValue instanceof Boolean && !((Boolean)rOperandValue).booleanValue()) { - myResult = Boolean.FALSE; - return; + else if (rOperandValue instanceof Boolean && !((Boolean)rOperandValue).booleanValue()) { + value = Boolean.FALSE; } - if (lOperandValue instanceof Boolean && rOperandValue instanceof Boolean) { + else if (lOperandValue instanceof Boolean && rOperandValue instanceof Boolean) { value = Boolean.valueOf(((Boolean)lOperandValue).booleanValue() && ((Boolean)rOperandValue).booleanValue()); } } else if (tokenType == JavaTokenType.OROR) { if (lOperandValue instanceof Boolean && ((Boolean)lOperandValue).booleanValue()) { - myResult = Boolean.TRUE; - return; + value = Boolean.TRUE; } - if (rOperandValue instanceof Boolean && ((Boolean)rOperandValue).booleanValue()) { - myResult = Boolean.TRUE; - return; + else if (rOperandValue instanceof Boolean && ((Boolean)rOperandValue).booleanValue()) { + value = Boolean.TRUE; } - if (lOperandValue instanceof Boolean && rOperandValue instanceof Boolean) { + else if (lOperandValue instanceof Boolean && rOperandValue instanceof Boolean) { value = Boolean.valueOf(((Boolean)lOperandValue).booleanValue() || ((Boolean)rOperandValue).booleanValue()); } } @@ -421,8 +430,7 @@ class ConstantExpressionVisitor extends JavaElementVisitor implements PsiConstan value = Boolean.valueOf(((Boolean)lOperandValue).booleanValue() ^ ((Boolean)rOperandValue).booleanValue()); } } - - myResult = value; + return value; } @Override public void visitPrefixExpression(PsiPrefixExpression expression) { @@ -432,7 +440,7 @@ class ConstantExpressionVisitor extends JavaElementVisitor implements PsiConstan myResult = null; return; } - IElementType tokenType = expression.getOperationSign().getTokenType(); + IElementType tokenType = expression.getOperationTokenType(); Object value = null; if (tokenType == JavaTokenType.MINUS) { if (operandValue instanceof Character) operandValue = Integer.valueOf(((Character)operandValue).charValue()); @@ -543,13 +551,13 @@ class ConstantExpressionVisitor extends JavaElementVisitor implements PsiConstan return o instanceof Long || o instanceof Integer || o instanceof Short || o instanceof Byte || o instanceof Character; } - private void checkDivisionOverflow(long l, final long r, long minValue, PsiBinaryExpression expression) { + private void checkDivisionOverflow(long l, final long r, long minValue, PsiElement expression) { if (!myThrowExceptionOnOverflow) return; if (r == 0) throw new ConstantEvaluationOverflowException(expression); if (r == -1 && l == minValue) throw new ConstantEvaluationOverflowException(expression); } - private void checkMultiplicationOverflow(long result, long l, long r, PsiExpression expression) { + private void checkMultiplicationOverflow(long result, long l, long r, PsiElement expression) { if (!myThrowExceptionOnOverflow) return; if (r == 0 || l == 0) return; if (result / r != l || ((l < 0) ^ (r < 0) != (result < 0))) throw new ConstantEvaluationOverflowException(expression); @@ -557,7 +565,7 @@ class ConstantExpressionVisitor extends JavaElementVisitor implements PsiConstan private void checkAdditionOverflow(boolean resultPositive, boolean lPositive, - boolean rPositive, PsiBinaryExpression expression) { + boolean rPositive, PsiElement expression) { if (!myThrowExceptionOnOverflow) return; boolean overflow = lPositive == rPositive && lPositive != resultPositive; if (overflow) throw new ConstantEvaluationOverflowException(expression); @@ -565,7 +573,7 @@ class ConstantExpressionVisitor extends JavaElementVisitor implements PsiConstan private void checkRealNumberOverflow(Object result, Object lOperandValue, - Object rOperandValue, PsiExpression expression) { + Object rOperandValue, PsiElement expression) { if (!myThrowExceptionOnOverflow) return; if (lOperandValue instanceof Float && ((Float) lOperandValue).isInfinite()) return; if (lOperandValue instanceof Double && ((Double) lOperandValue).isInfinite()) return; diff --git a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsBinaryExpressionImpl.java b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsBinaryExpressionImpl.java index 92c3e9b56653..929d8e787302 100644 --- a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsBinaryExpressionImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsBinaryExpressionImpl.java @@ -114,6 +114,11 @@ public class ClsBinaryExpressionImpl extends ClsElementImpl implements PsiBinary return myOperation.getTokenType(); } + @Override + public PsiJavaToken getTokenBeforeOperand(@NotNull PsiExpression operand) { + return getOperationSign(); + } + @Override public PsiType getType() { return myLOperand.getType(); @@ -123,4 +128,9 @@ public class ClsBinaryExpressionImpl extends ClsElementImpl implements PsiBinary public String toString() { return "PsiBinaryExpression:" + getText(); } + @NotNull + @Override + public PsiExpression[] getOperands() { + return new PsiExpression[]{getLOperand(), getROperand()}; + } } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/JavaResolveCache.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/JavaResolveCache.java index cc16aef88444..a79a5d9381c7 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/JavaResolveCache.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/JavaResolveCache.java @@ -24,15 +24,14 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.NotNullLazyKey; -import com.intellij.psi.PsiEllipsisType; import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiType; import com.intellij.psi.PsiVariable; import com.intellij.psi.impl.PsiManagerEx; +import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.ConcurrencyUtil; import com.intellij.util.Function; import com.intellij.util.containers.ConcurrentWeakHashMap; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -56,16 +55,6 @@ public class JavaResolveCache { private final Map myVarToConstValueMap2; private static final Object NULL = Key.create("NULL"); - public static final PsiType NULL_TYPE = new PsiEllipsisType(PsiType.NULL){ - public boolean isValid() { - return true; - } - - @NonNls - public String getPresentableText() { - return "FAKE TYPE"; - } - }; public JavaResolveCache(PsiManagerEx manager) { ResolveCache cache = manager.getResolveCache(); @@ -93,7 +82,7 @@ public class JavaResolveCache { if (type == null) { type = f.fun(expr); if (type == null) { - type = NULL_TYPE; + type = TypeConversionUtil.NULL_TYPE; } type = ConcurrencyUtil.cacheOrGet(myCalculatedTypes, expr, type); } @@ -105,7 +94,7 @@ public class JavaResolveCache { LOG.error("Expression: '"+expr+"' is invalid, must not be used for getType()"); } } - return type == NULL_TYPE ? null : type; + return type == TypeConversionUtil.NULL_TYPE ? null : type; } @Nullable diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/ElementType.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/ElementType.java index 2a6fd9d6af5f..94ad273d8bd3 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/ElementType.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/ElementType.java @@ -53,7 +53,7 @@ public interface ElementType extends JavaTokenType, JavaDocTokenType, TokenSet EXPRESSION_BIT_SET = TokenSet.create( REFERENCE_EXPRESSION, LITERAL_EXPRESSION, THIS_EXPRESSION, SUPER_EXPRESSION, PARENTH_EXPRESSION, METHOD_CALL_EXPRESSION, - TYPE_CAST_EXPRESSION, PREFIX_EXPRESSION, POSTFIX_EXPRESSION, BINARY_EXPRESSION, CONDITIONAL_EXPRESSION, ASSIGNMENT_EXPRESSION, + TYPE_CAST_EXPRESSION, PREFIX_EXPRESSION, POSTFIX_EXPRESSION, BINARY_EXPRESSION, POLYADIC_EXPRESSION, CONDITIONAL_EXPRESSION, ASSIGNMENT_EXPRESSION, NEW_EXPRESSION, ARRAY_ACCESS_EXPRESSION, ARRAY_INITIALIZER_EXPRESSION, INSTANCE_OF_EXPRESSION, CLASS_OBJECT_ACCESS_EXPRESSION, EMPTY_EXPRESSION); @@ -62,7 +62,7 @@ public interface ElementType extends JavaTokenType, JavaDocTokenType, TokenSet ARRAY_DIMENSION_BIT_SET = TokenSet.create( REFERENCE_EXPRESSION, LITERAL_EXPRESSION, THIS_EXPRESSION, SUPER_EXPRESSION, PARENTH_EXPRESSION, METHOD_CALL_EXPRESSION, - TYPE_CAST_EXPRESSION, PREFIX_EXPRESSION, POSTFIX_EXPRESSION, BINARY_EXPRESSION, CONDITIONAL_EXPRESSION, ASSIGNMENT_EXPRESSION, + TYPE_CAST_EXPRESSION, PREFIX_EXPRESSION, POSTFIX_EXPRESSION, BINARY_EXPRESSION, POLYADIC_EXPRESSION, CONDITIONAL_EXPRESSION, ASSIGNMENT_EXPRESSION, NEW_EXPRESSION, ARRAY_ACCESS_EXPRESSION, INSTANCE_OF_EXPRESSION, CLASS_OBJECT_ACCESS_EXPRESSION, EMPTY_EXPRESSION); TokenSet JAVA_STATEMENT_BIT_SET = TokenSet.create( diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java index 9060dfdc225e..2e86b41548dd 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java @@ -98,6 +98,7 @@ public interface JavaElementType { IElementType PREFIX_EXPRESSION = new JavaCompositeElementType("PREFIX_EXPRESSION", PsiPrefixExpressionImpl.class); IElementType POSTFIX_EXPRESSION = new JavaCompositeElementType("POSTFIX_EXPRESSION", PsiPostfixExpressionImpl.class); IElementType BINARY_EXPRESSION = new JavaCompositeElementType("BINARY_EXPRESSION", PsiBinaryExpressionImpl.class); + IElementType POLYADIC_EXPRESSION = new JavaCompositeElementType("POLYADIC_EXPRESSION", PsiPolyadicExpressionImpl.class); IElementType CONDITIONAL_EXPRESSION = new JavaCompositeElementType("CONDITIONAL_EXPRESSION", PsiConditionalExpressionImpl.class); IElementType ASSIGNMENT_EXPRESSION = new JavaCompositeElementType("ASSIGNMENT_EXPRESSION", PsiAssignmentExpressionImpl.class); IElementType NEW_EXPRESSION = new JavaCompositeElementType("NEW_EXPRESSION", PsiNewExpressionImpl.class); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/JavaConcatenationInjectorManager.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/JavaConcatenationInjectorManager.java index ca0416a603e1..8c53cbb84aa6 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/JavaConcatenationInjectorManager.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/JavaConcatenationInjectorManager.java @@ -74,8 +74,8 @@ public class JavaConcatenationInjectorManager implements ModificationTracker { public CachedValueProvider.Result compute(PsiElement context) { PsiElement element = context; PsiElement parent = context.getParent(); - while (parent instanceof PsiBinaryExpression && ((PsiBinaryExpression)parent).getOperationSign().getTokenType() == JavaTokenType.PLUS - || parent instanceof PsiAssignmentExpression && ((PsiAssignmentExpression)parent).getOperationSign().getTokenType() == JavaTokenType.PLUSEQ + while (parent instanceof PsiPolyadicExpression && ((PsiPolyadicExpression)parent).getOperationTokenType() == JavaTokenType.PLUS + || parent instanceof PsiAssignmentExpression && ((PsiAssignmentExpression)parent).getOperationTokenType() == JavaTokenType.PLUSEQ || parent instanceof PsiConditionalExpression && ((PsiConditionalExpression)parent).getCondition() != element || parent instanceof PsiTypeCastExpression || parent instanceof PsiParenthesizedExpression) { @@ -85,9 +85,9 @@ public class JavaConcatenationInjectorManager implements ModificationTracker { PsiElement[] operands; PsiElement anchor; - if (element instanceof PsiBinaryExpression || element instanceof PsiAssignmentExpression) { + if (element instanceof PsiPolyadicExpression || element instanceof PsiAssignmentExpression) { List operandList = new ArrayList(); - collectOperands(element, operandList); + collectOperands((PsiExpression)element, operandList); operands = PsiUtilBase.toPsiElementArray(operandList); anchor = element; } @@ -105,18 +105,22 @@ public class JavaConcatenationInjectorManager implements ModificationTracker { CachedValueProvider.Result result = CachedValueProvider.Result.create(registrar, PsiModificationTracker.MODIFICATION_COUNT, concatenationInjectorManager); + ParameterizedCachedValue cachedValue; if (registrar.result != null) { // store this everywhere - ParameterizedCachedValue cachedValue = - CachedValuesManager.getManager(context.getProject()).createParameterizedCachedValue(this, false); + cachedValue = CachedValuesManager.getManager(context.getProject()).createParameterizedCachedValue(this, false); ((PsiParameterizedCachedValue)cachedValue).setValue(result); - for (PsiElement operand : operands) { - operand.putUserData(INJECTED_PSI_IN_CONCATENATION, cachedValue); - } - anchor.putUserData(INJECTED_PSI_IN_CONCATENATION, cachedValue); - context.putUserData(INJECTED_PSI_IN_CONCATENATION, cachedValue); } + else { + cachedValue = InjectedLanguageUtil.NULL_VALUE; + } + for (PsiElement operand : operands) { + operand.putUserData(INJECTED_PSI_IN_CONCATENATION, cachedValue); + } + anchor.putUserData(INJECTED_PSI_IN_CONCATENATION, cachedValue); + context.putUserData(INJECTED_PSI_IN_CONCATENATION, cachedValue); + return result; } @@ -156,15 +160,28 @@ public class JavaConcatenationInjectorManager implements ModificationTracker { } } - private static void collectOperands(PsiElement expression, List operands) { - if (expression instanceof PsiBinaryExpression) { - final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)expression; - collectOperands(binaryExpression.getLOperand(), operands); - collectOperands(binaryExpression.getROperand(), operands); - } - else if (expression != null) { - operands.add(expression); - } + private static void collectOperands(PsiExpression expression, final List operands) { + expression.accept(new JavaRecursiveElementWalkingVisitor() { + @Override + public void visitPolyadicExpression(PsiPolyadicExpression expression) { + super.visitElement(expression); + } + + @Override + public void visitExpression(PsiExpression expression) { + operands.add(expression); // add leaf expression to the operand list + } + + @Override + public void visitReferenceExpression(PsiReferenceExpression expression) { + visitExpression(expression); + } + + @Override + public void visitElement(PsiElement element) { + // do not go deeper + } + }); } private final List myConcatenationInjectors = ContainerUtil.createEmptyCOWList(); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiBinaryExpressionImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiBinaryExpressionImpl.java index 934fb03dd616..a75bc65a25c3 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiBinaryExpressionImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiBinaryExpressionImpl.java @@ -29,13 +29,15 @@ import com.intellij.psi.tree.TokenSet; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.Function; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; public class PsiBinaryExpressionImpl extends ExpressionPsiElement implements PsiBinaryExpression { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.PsiBinaryExpressionImpl"); public PsiBinaryExpressionImpl() { - super(JavaElementType.BINARY_EXPRESSION); + this(JavaElementType.BINARY_EXPRESSION); + } + protected PsiBinaryExpressionImpl(@NotNull IElementType elementType) { + super(elementType); } @NotNull @@ -57,6 +59,11 @@ public class PsiBinaryExpressionImpl extends ExpressionPsiElement implements Psi return getOperationSign().getTokenType(); } + @Override + public PsiJavaToken getTokenBeforeOperand(@NotNull PsiExpression operand) { + return getOperationSign(); + } + private static PsiType doGetType(PsiBinaryExpressionImpl param) { PsiExpression lOperand = param.getLOperand(); PsiExpression rOperand = param.getROperand(); @@ -64,8 +71,8 @@ public class PsiBinaryExpressionImpl extends ExpressionPsiElement implements Psi PsiType rType = rOperand.getType(); IElementType sign = param.getOperationSign().getNode().getElementType(); // optimization: if we can calculate type based on right type only - PsiType type = calcTypeForBinaryExpression(null, rType, sign, false); - if (type != JavaResolveCache.NULL_TYPE) return type; + PsiType type = TypeConversionUtil.calcTypeForBinaryExpression(null, rType, sign, false); + if (type != TypeConversionUtil.NULL_TYPE) return type; if (lOperand instanceof PsiBinaryExpressionImpl && !JavaResolveCache.getInstance(param.getProject()).isTypeCached(lOperand)) { // cache all intermediate expression types from bottom up @@ -86,7 +93,7 @@ public class PsiBinaryExpressionImpl extends ExpressionPsiElement implements Psi }); } PsiType lType = lOperand.getType(); - return calcTypeForBinaryExpression(lType, rType, sign, true); + return TypeConversionUtil.calcTypeForBinaryExpression(lType, rType, sign, true); } private static final Function MY_TYPE_EVALUATOR = new Function() { @@ -147,60 +154,11 @@ public class PsiBinaryExpressionImpl extends ExpressionPsiElement implements Psi return "PsiBinaryExpression:" + getText(); } - @Nullable - public static PsiType calcTypeForBinaryExpression(PsiType lType, PsiType rType, IElementType sign, boolean accessLType) { - if (sign == JavaTokenType.PLUS) { - // evaluate right argument first, since '+-/*%' is left associative and left operand tends to be bigger - if (rType == null) return null; - if (rType.equalsToText("java.lang.String")) { - return rType; - } - if (!accessLType) return JavaResolveCache.NULL_TYPE; - if (lType == null) return null; - if (lType.equalsToText("java.lang.String")) { - return lType; - } - return TypeConversionUtil.unboxAndBalanceTypes(lType, rType); - } - if (sign == JavaTokenType.MINUS || sign == JavaTokenType.ASTERISK || sign == JavaTokenType.DIV || sign == JavaTokenType.PERC) { - if (rType == null) return null; - if (!accessLType) return JavaResolveCache.NULL_TYPE; - if (lType == null) return null; - return TypeConversionUtil.unboxAndBalanceTypes(lType, rType); - } - if (sign == JavaTokenType.LTLT || sign == JavaTokenType.GTGT || sign == JavaTokenType.GTGTGT) { - if (!accessLType) return JavaResolveCache.NULL_TYPE; - if (PsiType.BYTE.equals(lType) || PsiType.CHAR.equals(lType) || PsiType.SHORT.equals(lType)) { - return PsiType.INT; - } - if (lType instanceof PsiClassType) lType = PsiPrimitiveType.getUnboxedType(lType); - return lType; - } - if (sign == JavaTokenType.EQEQ || - sign == JavaTokenType.NE || - sign == JavaTokenType.LT || - sign == JavaTokenType.GT || - sign == JavaTokenType.LE || - sign == JavaTokenType.GE || - sign == JavaTokenType.OROR || - sign == JavaTokenType.ANDAND) { - return PsiType.BOOLEAN; - } - if (sign == JavaTokenType.OR || sign == JavaTokenType.XOR || sign == JavaTokenType.AND) { - if (rType instanceof PsiClassType) rType = PsiPrimitiveType.getUnboxedType(rType); - - if (lType instanceof PsiClassType) lType = PsiPrimitiveType.getUnboxedType(lType); - - if (rType == null) return null; - if (PsiType.BOOLEAN.equals(rType)) return PsiType.BOOLEAN; - if (!accessLType) return JavaResolveCache.NULL_TYPE; - if (lType == null) return null; - if (PsiType.BOOLEAN.equals(lType)) return PsiType.BOOLEAN; - if (PsiType.LONG.equals(lType) || PsiType.LONG.equals(rType)) return PsiType.LONG; - return PsiType.INT; - } - LOG.error("Unknown token: "+sign); - return null; + @NotNull + @Override + public PsiExpression[] getOperands() { + PsiExpression rOperand = getROperand(); + return rOperand == null ? new PsiExpression[]{getLOperand()} : new PsiExpression[]{getLOperand(), rOperand}; } } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiPolyadicExpressionImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiPolyadicExpressionImpl.java new file mode 100644 index 000000000000..af90b3a334d8 --- /dev/null +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiPolyadicExpressionImpl.java @@ -0,0 +1,127 @@ +/* + * Copyright 2000-2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.impl.source.tree.java; + +import com.intellij.lang.ASTNode; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.psi.*; +import com.intellij.psi.impl.source.Constants; +import com.intellij.psi.impl.source.resolve.JavaResolveCache; +import com.intellij.psi.impl.source.tree.ChildRole; +import com.intellij.psi.impl.source.tree.ElementType; +import com.intellij.psi.impl.source.tree.JavaElementType; +import com.intellij.psi.tree.ChildRoleBase; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.tree.TokenSet; +import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.util.Function; +import org.jetbrains.annotations.NotNull; + +public class PsiPolyadicExpressionImpl extends ExpressionPsiElement implements PsiPolyadicExpression { + private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.PsiPolyadicExpressionImpl"); + + public PsiPolyadicExpressionImpl() { + super(JavaElementType.POLYADIC_EXPRESSION); + } + + @NotNull + public IElementType getOperationTokenType() { + return ((PsiJavaToken)findChildByRoleAsPsiElement(ChildRole.OPERATION_SIGN)).getTokenType(); + } + + @Override + public PsiJavaToken getTokenBeforeOperand(@NotNull PsiExpression operand) { + PsiElement element = operand; + while(element != null) { + if (getChildRole(element.getNode()) == ChildRole.OPERATION_SIGN) return (PsiJavaToken)element; + element = element.getPrevSibling(); + } + return null; + } + + public PsiType getType() { + return JavaResolveCache.getInstance(getProject()).getType(this, MY_TYPE_EVALUATOR); + } + private static final Function MY_TYPE_EVALUATOR = new Function() { + public PsiType fun(PsiPolyadicExpressionImpl expression) { + return doGetType(expression); + } + }; + + private static PsiType doGetType(PsiPolyadicExpressionImpl param) { + PsiExpression[] operands = param.getOperands(); + PsiType lType = null; + + IElementType sign = param.getOperationTokenType(); + for (int i=1; i classMemberRefs) { @@ -138,10 +149,10 @@ public class Util { PsiElement element = reference.getElement(); boolean stillCanBeRemoved = false; if (element != null) { - stillCanBeRemoved = PsiTreeUtil.isAncestor(expr, element, false) || PsiUtil.isInsideJavadocComment(element); + stillCanBeRemoved = isAncestor(expr, element, false) || PsiUtil.isInsideJavadocComment(getPhysical(element)); if (!stillCanBeRemoved && occurences != null) { for (PsiExpression occurence : occurences) { - if (PsiTreeUtil.isAncestor(occurence, element, false)) { + if (isAncestor(occurence, element, false)) { stillCanBeRemoved = true; break; } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java index 7236cb19437f..d77b16db97ab 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java @@ -219,10 +219,9 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme } /** - * @return can return NotNull value thought extraction should fail: reason could be retrieved from {@link #getErrorMessage(PsiExpression)} + * @return can return NotNull value although extraction will fail: reason could be retrieved from {@link #getErrorMessage(PsiExpression)} */ public static PsiExpression getSelectedExpression(final Project project, final PsiFile file, int startOffset, int endOffset) { - PsiElement elementAtStart = file.findElementAt(startOffset); if (elementAtStart == null || elementAtStart instanceof PsiWhiteSpace || elementAtStart instanceof PsiComment) { elementAtStart = PsiTreeUtil.skipSiblingsForward(elementAtStart, PsiWhiteSpace.class, PsiComment.class); diff --git a/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeEvaluator.java b/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeEvaluator.java index 86a6aced41c1..5445953fa566 100644 --- a/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeEvaluator.java +++ b/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeEvaluator.java @@ -21,7 +21,6 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.impl.PsiImplUtil; -import com.intellij.psi.impl.source.tree.java.PsiBinaryExpressionImpl; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; @@ -151,8 +150,8 @@ public class TypeEvaluator { final PsiExpression lOperand = ((PsiBinaryExpression)expr).getLOperand(); final PsiExpression rOperand = ((PsiBinaryExpression)expr).getROperand(); final PsiJavaToken operationSign = ((PsiBinaryExpression)expr).getOperationSign(); - return PsiBinaryExpressionImpl.calcTypeForBinaryExpression(evaluateType(lOperand), evaluateType(rOperand), operationSign.getTokenType(), - true); + return TypeConversionUtil.calcTypeForBinaryExpression(evaluateType(lOperand), evaluateType(rOperand), operationSign.getTokenType(), + true); } else if (expr instanceof PsiPostfixExpression) { return evaluateType(((PsiPostfixExpression)expr).getOperand()); diff --git a/java/java-tests/testData/inspection/numericOverflow/simple/expected.xml b/java/java-tests/testData/inspection/numericOverflow/simple/expected.xml index 197b87606e12..78d22bf6d352 100644 --- a/java/java-tests/testData/inspection/numericOverflow/simple/expected.xml +++ b/java/java-tests/testData/inspection/numericOverflow/simple/expected.xml @@ -639,18 +639,12 @@ Foo.java - 73 - + 72 + Numeric overflow in expression - - Foo.java - 73 - - Numeric overflow in expression - diff --git a/java/java-tests/testData/inspection/numericOverflow/simple/src/Foo.java b/java/java-tests/testData/inspection/numericOverflow/simple/src/Foo.java index 153606c89b08..1aed3c6bcf46 100644 --- a/java/java-tests/testData/inspection/numericOverflow/simple/src/Foo.java +++ b/java/java-tests/testData/inspection/numericOverflow/simple/src/Foo.java @@ -69,8 +69,8 @@ class c { l1 = LONG_MIN_VALUE / -1; l1 = 30 * 24 * 60 * 60 * 1000; - l1 = 30000000 * 243232323 - * (LONG_MAX_VALUE +3) / 5; + l1 = 30000000 * 243232323 * (LONG_MAX_VALUE +3) / 5; + System.out.println(l1); diff --git a/java/java-tests/testData/psi/controlFlow/flowAnd2.txt b/java/java-tests/testData/psi/controlFlow/flowAnd2.txt index 3e23cd10adf3..203f5b69b5f5 100644 --- a/java/java-tests/testData/psi/controlFlow/flowAnd2.txt +++ b/java/java-tests/testData/psi/controlFlow/flowAnd2.txt @@ -1,6 +1,6 @@ 0: EMPTY 1: READ file -2: COND_GOTO [END] 9 +2: COND_GOTO [ELSE] 9 3: READ b1 4: COND_GOTO [ELSE] 9 5: READ b2 @@ -9,4 +9,4 @@ 8: GOTO [END] 10 9: WRITE highlighter 10: READ highlighter -11: GOTO [END] 12 RETURN \ No newline at end of file +11: GOTO [END] 12 RETURN diff --git a/java/java-tests/testData/psi/parser-full/expressionParsing/And0.txt b/java/java-tests/testData/psi/parser-full/expressionParsing/And0.txt index 1cd5533086ac..26388a9a9608 100644 --- a/java/java-tests/testData/psi/parser-full/expressionParsing/And0.txt +++ b/java/java-tests/testData/psi/parser-full/expressionParsing/And0.txt @@ -24,19 +24,18 @@ PsiJavaFile:And0.java PsiReturnStatement PsiKeyword:return('return') PsiWhiteSpace(' ') - PsiBinaryExpression:a & b & c - PsiBinaryExpression:a & b - PsiReferenceExpression:a - PsiReferenceParameterList - - PsiIdentifier:a('a') - PsiWhiteSpace(' ') - PsiJavaToken:AND('&') - PsiWhiteSpace(' ') - PsiReferenceExpression:b - PsiReferenceParameterList - - PsiIdentifier:b('b') + PsiPolyadicExpression: a & b & c + PsiReferenceExpression:a + PsiReferenceParameterList + + PsiIdentifier:a('a') + PsiWhiteSpace(' ') + PsiJavaToken:AND('&') + PsiWhiteSpace(' ') + PsiReferenceExpression:b + PsiReferenceParameterList + + PsiIdentifier:b('b') PsiWhiteSpace(' ') PsiJavaToken:AND('&') PsiWhiteSpace(' ') @@ -48,4 +47,4 @@ PsiJavaFile:And0.java PsiWhiteSpace('\n ') PsiJavaToken:RBRACE('}') PsiWhiteSpace('\n') - PsiJavaToken:RBRACE('}') \ No newline at end of file + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-full/expressionParsing/CondAnd0.txt b/java/java-tests/testData/psi/parser-full/expressionParsing/CondAnd0.txt index f09a0448ecd6..383e71055e40 100644 --- a/java/java-tests/testData/psi/parser-full/expressionParsing/CondAnd0.txt +++ b/java/java-tests/testData/psi/parser-full/expressionParsing/CondAnd0.txt @@ -24,19 +24,18 @@ PsiJavaFile:CondAnd0.java PsiReturnStatement PsiKeyword:return('return') PsiWhiteSpace(' ') - PsiBinaryExpression:a && b && c - PsiBinaryExpression:a && b - PsiReferenceExpression:a - PsiReferenceParameterList - - PsiIdentifier:a('a') - PsiWhiteSpace(' ') - PsiJavaToken:ANDAND('&&') - PsiWhiteSpace(' ') - PsiReferenceExpression:b - PsiReferenceParameterList - - PsiIdentifier:b('b') + PsiPolyadicExpression: a && b && c + PsiReferenceExpression:a + PsiReferenceParameterList + + PsiIdentifier:a('a') + PsiWhiteSpace(' ') + PsiJavaToken:ANDAND('&&') + PsiWhiteSpace(' ') + PsiReferenceExpression:b + PsiReferenceParameterList + + PsiIdentifier:b('b') PsiWhiteSpace(' ') PsiJavaToken:ANDAND('&&') PsiWhiteSpace(' ') @@ -48,4 +47,4 @@ PsiJavaFile:CondAnd0.java PsiWhiteSpace('\n ') PsiJavaToken:RBRACE('}') PsiWhiteSpace('\n') - PsiJavaToken:RBRACE('}') \ No newline at end of file + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-full/expressionParsing/CondOr0.txt b/java/java-tests/testData/psi/parser-full/expressionParsing/CondOr0.txt index 7bfdcfb60596..926a8bfe4d62 100644 --- a/java/java-tests/testData/psi/parser-full/expressionParsing/CondOr0.txt +++ b/java/java-tests/testData/psi/parser-full/expressionParsing/CondOr0.txt @@ -24,19 +24,18 @@ PsiJavaFile:CondOr0.java PsiReturnStatement PsiKeyword:return('return') PsiWhiteSpace(' ') - PsiBinaryExpression:a || b || c - PsiBinaryExpression:a || b - PsiReferenceExpression:a - PsiReferenceParameterList - - PsiIdentifier:a('a') - PsiWhiteSpace(' ') - PsiJavaToken:OROR('||') - PsiWhiteSpace(' ') - PsiReferenceExpression:b - PsiReferenceParameterList - - PsiIdentifier:b('b') + PsiPolyadicExpression: a || b || c + PsiReferenceExpression:a + PsiReferenceParameterList + + PsiIdentifier:a('a') + PsiWhiteSpace(' ') + PsiJavaToken:OROR('||') + PsiWhiteSpace(' ') + PsiReferenceExpression:b + PsiReferenceParameterList + + PsiIdentifier:b('b') PsiWhiteSpace(' ') PsiJavaToken:OROR('||') PsiWhiteSpace(' ') @@ -48,4 +47,4 @@ PsiJavaFile:CondOr0.java PsiWhiteSpace('\n ') PsiJavaToken:RBRACE('}') PsiWhiteSpace('\n') - PsiJavaToken:RBRACE('}') \ No newline at end of file + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-full/expressionParsing/Or0.txt b/java/java-tests/testData/psi/parser-full/expressionParsing/Or0.txt index 386ba8fb8ffb..d53ef8a225e1 100644 --- a/java/java-tests/testData/psi/parser-full/expressionParsing/Or0.txt +++ b/java/java-tests/testData/psi/parser-full/expressionParsing/Or0.txt @@ -24,19 +24,18 @@ PsiJavaFile:Or0.java PsiReturnStatement PsiKeyword:return('return') PsiWhiteSpace(' ') - PsiBinaryExpression:a | b | c - PsiBinaryExpression:a | b - PsiReferenceExpression:a - PsiReferenceParameterList - - PsiIdentifier:a('a') - PsiWhiteSpace(' ') - PsiJavaToken:OR('|') - PsiWhiteSpace(' ') - PsiReferenceExpression:b - PsiReferenceParameterList - - PsiIdentifier:b('b') + PsiPolyadicExpression: a | b | c + PsiReferenceExpression:a + PsiReferenceParameterList + + PsiIdentifier:a('a') + PsiWhiteSpace(' ') + PsiJavaToken:OR('|') + PsiWhiteSpace(' ') + PsiReferenceExpression:b + PsiReferenceParameterList + + PsiIdentifier:b('b') PsiWhiteSpace(' ') PsiJavaToken:OR('|') PsiWhiteSpace(' ') @@ -48,4 +47,4 @@ PsiJavaFile:Or0.java PsiWhiteSpace('\n ') PsiJavaToken:RBRACE('}') PsiWhiteSpace('\n') - PsiJavaToken:RBRACE('}') \ No newline at end of file + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-full/expressionParsing/Xor0.txt b/java/java-tests/testData/psi/parser-full/expressionParsing/Xor0.txt index 04d8e9d6a7e4..bd93036fdfa2 100644 --- a/java/java-tests/testData/psi/parser-full/expressionParsing/Xor0.txt +++ b/java/java-tests/testData/psi/parser-full/expressionParsing/Xor0.txt @@ -24,19 +24,18 @@ PsiJavaFile:Xor0.java PsiReturnStatement PsiKeyword:return('return') PsiWhiteSpace(' ') - PsiBinaryExpression:a ^ b ^ c - PsiBinaryExpression:a ^ b - PsiReferenceExpression:a - PsiReferenceParameterList - - PsiIdentifier:a('a') - PsiWhiteSpace(' ') - PsiJavaToken:XOR('^') - PsiWhiteSpace(' ') - PsiReferenceExpression:b - PsiReferenceParameterList - - PsiIdentifier:b('b') + PsiPolyadicExpression: a ^ b ^ c + PsiReferenceExpression:a + PsiReferenceParameterList + + PsiIdentifier:a('a') + PsiWhiteSpace(' ') + PsiJavaToken:XOR('^') + PsiWhiteSpace(' ') + PsiReferenceExpression:b + PsiReferenceParameterList + + PsiIdentifier:b('b') PsiWhiteSpace(' ') PsiJavaToken:XOR('^') PsiWhiteSpace(' ') @@ -48,4 +47,4 @@ PsiJavaFile:Xor0.java PsiWhiteSpace('\n ') PsiJavaToken:RBRACE('}') PsiWhiteSpace('\n') - PsiJavaToken:RBRACE('}') \ No newline at end of file + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/expressions/CondOr0.txt b/java/java-tests/testData/psi/parser-partial/expressions/CondOr0.txt index a2405e24d608..45df89dd19ad 100644 --- a/java/java-tests/testData/psi/parser-partial/expressions/CondOr0.txt +++ b/java/java-tests/testData/psi/parser-partial/expressions/CondOr0.txt @@ -1,21 +1,20 @@ PsiJavaFile:CondOr0.java - PsiBinaryExpression:a || b || c - PsiBinaryExpression:a || b - PsiReferenceExpression:a - PsiReferenceParameterList - - PsiIdentifier:a('a') - PsiWhiteSpace(' ') - PsiJavaToken:OROR('||') - PsiWhiteSpace(' ') - PsiReferenceExpression:b - PsiReferenceParameterList - - PsiIdentifier:b('b') + PsiPolyadicExpression: a || b || c + PsiReferenceExpression:a + PsiReferenceParameterList + + PsiIdentifier:a('a') + PsiWhiteSpace(' ') + PsiJavaToken:OROR('||') + PsiWhiteSpace(' ') + PsiReferenceExpression:b + PsiReferenceParameterList + + PsiIdentifier:b('b') PsiWhiteSpace(' ') PsiJavaToken:OROR('||') PsiWhiteSpace(' ') PsiReferenceExpression:c PsiReferenceParameterList - PsiIdentifier:c('c') + PsiIdentifier:c('c') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/expressions/Or0.txt b/java/java-tests/testData/psi/parser-partial/expressions/Or0.txt index 20bd74839bdd..56cf4e01ead1 100644 --- a/java/java-tests/testData/psi/parser-partial/expressions/Or0.txt +++ b/java/java-tests/testData/psi/parser-partial/expressions/Or0.txt @@ -1,21 +1,20 @@ PsiJavaFile:Or0.java - PsiBinaryExpression:a | b | c - PsiBinaryExpression:a | b - PsiReferenceExpression:a - PsiReferenceParameterList - - PsiIdentifier:a('a') - PsiWhiteSpace(' ') - PsiJavaToken:OR('|') - PsiWhiteSpace(' ') - PsiReferenceExpression:b - PsiReferenceParameterList - - PsiIdentifier:b('b') + PsiPolyadicExpression: a | b | c + PsiReferenceExpression:a + PsiReferenceParameterList + + PsiIdentifier:a('a') + PsiWhiteSpace(' ') + PsiJavaToken:OR('|') + PsiWhiteSpace(' ') + PsiReferenceExpression:b + PsiReferenceParameterList + + PsiIdentifier:b('b') PsiWhiteSpace(' ') PsiJavaToken:OR('|') PsiWhiteSpace(' ') PsiReferenceExpression:c PsiReferenceParameterList - PsiIdentifier:c('c') + PsiIdentifier:c('c') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/RecursiveVisitorTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/RecursiveVisitorTest.java index c8d7facfa0da..fbd79b495bea 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/RecursiveVisitorTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/RecursiveVisitorTest.java @@ -10,8 +10,8 @@ import com.intellij.util.ThrowableRunnable; */ public class RecursiveVisitorTest extends LightDaemonAnalyzerTestCase{ public void testHugeConcatenationVisitingPerformance() throws IncorrectOperationException { - StringBuilder text = new StringBuilder("String s = \"xxx\""); - final int N = 2; + StringBuilder text = new StringBuilder("String s = null"); + final int N = 20000; for (int i = 0; i < N; i++) { text.append("+\"xxx\""); } @@ -19,26 +19,26 @@ public class RecursiveVisitorTest extends LightDaemonAnalyzerTestCase{ final PsiElement expression = JavaPsiFacade.getInstance(getProject()).getElementFactory().createStatementFromText(text.toString(), null); final int[] n = {0}; - PlatformTestUtil.startPerformanceTest(getTestName(false), 20, new ThrowableRunnable() { + PlatformTestUtil.startPerformanceTest(getTestName(false), 100, new ThrowableRunnable() { @Override public void run() throws Exception { n[0] = 0; expression.accept(new JavaRecursiveElementWalkingVisitor() { @Override - public void visitBinaryExpression(final PsiBinaryExpression expression) { - PsiExpression s = expression.getLOperand(); - super.visitBinaryExpression(expression); + public void visitExpression(PsiExpression expression) { + PsiExpression s = expression; + super.visitExpression(expression); s.hashCode(); //hold on stack n[0]++; } }); - assertEquals(N, n[0]); + assertEquals(N+2, n[0]); } }).cpuBound().assertTiming(); } public void testHugeMethodChainingVisitingPerformance() throws IncorrectOperationException { StringBuilder text = new StringBuilder("Object s = new StringBuilder()"); - final int N = 1500; + final int N = 20000; for (int i = 0; i < N; i++) { text.append(".append(\"xxx\")"); } @@ -46,7 +46,7 @@ public class RecursiveVisitorTest extends LightDaemonAnalyzerTestCase{ final PsiElement expression = JavaPsiFacade.getInstance(getProject()).getElementFactory().createStatementFromText(text.toString(), null); final int[] n = {0}; - PlatformTestUtil.startPerformanceTest(getTestName(false), 100, new ThrowableRunnable() { + PlatformTestUtil.startPerformanceTest(getTestName(false), 200, new ThrowableRunnable() { @Override public void run() throws Exception { n[0] = 0; diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java index ec8531d4a16c..4316a5c12c5d 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java @@ -19,6 +19,7 @@ import com.intellij.lang.PsiBuilder; import com.intellij.lang.java.parser.ExpressionParser; import com.intellij.lang.java.parser.JavaParsingTestCase; import com.intellij.pom.java.LanguageLevel; +import org.jetbrains.annotations.NonNls; public class ExpressionParserTest extends JavaParsingTestCase { @@ -115,7 +116,7 @@ public class ExpressionParserTest extends JavaParsingTestCase { public void testChainedClassObjectAccess() { doParserTest("A.class.B.class"); } public void testChainedThisObjectAccess() { doParserTest("A.this.B.this"); } - private void doParserTest(final String text) { + private void doParserTest(@NonNls final String text) { doParserTest(text, new TestParser() { @Override public void parse(final PsiBuilder builder) { diff --git a/java/java-tests/testSrc/com/intellij/psi/JavaSOEOnReparseTest.java b/java/java-tests/testSrc/com/intellij/psi/JavaSOEOnReparseTest.java index 1920c4a08dce..39f8461a1eea 100644 --- a/java/java-tests/testSrc/com/intellij/psi/JavaSOEOnReparseTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/JavaSOEOnReparseTest.java @@ -15,16 +15,15 @@ */ package com.intellij.psi; +import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.testFramework.LightCodeInsightTestCase; -public class JavaSOEOnReparseTest extends LightCodeInsightTestCase { - private static final String HUGE_EXPR; +public class JavaSOEOnReparseTest extends LightDaemonAnalyzerTestCase { + private static final StringBuilder HUGE_EXPR; static { - final StringBuilder sb = new StringBuilder("\"-\""); - for (int i = 0; i < 10000; i++) sb.append("+\"b\""); - HUGE_EXPR = sb.toString(); + HUGE_EXPR = new StringBuilder("\"-\""); + for (int i = 0; i < 100000; i++) HUGE_EXPR.append("+\"b\""); } public void testOnHugeBinaryExprInFile() throws Exception { @@ -37,7 +36,7 @@ public class JavaSOEOnReparseTest extends LightCodeInsightTestCase { doTest(); } - private static void doTest() { + private void doTest() { final int pos = getEditor().getDocument().getText().indexOf("\"\""); // replace small expression with huge binary one @@ -45,23 +44,27 @@ public class JavaSOEOnReparseTest extends LightCodeInsightTestCase { getEditor().getDocument().replaceString(pos, pos + 2, HUGE_EXPR); PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); }}); + doTestConfiguredFile(false, false); // modify huge binary expression (1) ApplicationManager.getApplication().runWriteAction(new Runnable() { public void run() { getEditor().getDocument().insertString(pos, "\".\"+"); PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); }}); + doTestConfiguredFile(false, false); // modify huge binary expression (2) ApplicationManager.getApplication().runWriteAction(new Runnable() { public void run() { getEditor().getDocument().replaceString(pos, pos + 4, ""); PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); }}); + doTestConfiguredFile(false, false); // replace huge binary expression with small one ApplicationManager.getApplication().runWriteAction(new Runnable() { public void run() { getEditor().getDocument().replaceString(pos, pos + HUGE_EXPR.length(), "\".\""); PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); }}); + doTestConfiguredFile(false, false); } } diff --git a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceParameterTest.java b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceParameterTest.java index 0eff3ae90c2f..653e997218ea 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceParameterTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceParameterTest.java @@ -11,11 +11,13 @@ package com.intellij.refactoring; import com.intellij.JavaTestUtil; import com.intellij.codeInsight.CodeInsightUtil; import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.util.Pass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiLocalVariable; import com.intellij.psi.PsiMethod; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.refactoring.introduceField.ElementToWorkOn; import com.intellij.refactoring.introduceParameter.IntroduceParameterHandler; import com.intellij.refactoring.introduceParameter.IntroduceParameterProcessor; import com.intellij.refactoring.introduceParameter.Util; @@ -317,24 +319,25 @@ public class IntroduceParameterTest extends LightCodeInsightTestCase { final boolean removeUnusedParameters, final boolean generateDelegate, int enclosingLevel) { - int startOffset = myEditor.getSelectionModel().getSelectionStart(); - int endOffset = myEditor.getSelectionModel().getSelectionEnd(); + final ElementToWorkOn[] elementToWorkOn = new ElementToWorkOn[1]; + ElementToWorkOn + .processElementToWorkOn(myEditor, myFile, "INtr param", HelpID.INTRODUCE_PARAMETER, getProject(), new Pass() { + @Override + public void pass(final ElementToWorkOn e) { + if (e == null) return; - PsiExpression expr = CodeInsightUtil.findExpressionInRange(myFile, startOffset, endOffset); + elementToWorkOn[0] = e; + } + }); - PsiLocalVariable localVariable = null; - if (expr == null) { - PsiElement element = CodeInsightUtil.findElementInRange(myFile, startOffset, endOffset, PsiElement.class); - localVariable = PsiTreeUtil.getParentOfType(element, PsiLocalVariable.class); - if (localVariable == null) { - return false; - } - } - PsiElement context = expr == null ? localVariable : expr; + final PsiExpression expr = elementToWorkOn[0].getExpression(); + final PsiLocalVariable localVar = elementToWorkOn[0].getLocalVariable(); + + PsiElement context = expr == null ? localVar : expr; PsiMethod method = Util.getContainingMethod(context); if (method == null) return false; - final List methods = IntroduceParameterHandler.getEnclosingMethods(method); + final List methods = com.intellij.refactoring.introduceParameter.IntroduceParameterHandler.getEnclosingMethods(method); assertTrue(methods.size() > enclosingLevel); method = methods.get(enclosingLevel); @@ -345,11 +348,11 @@ public class IntroduceParameterTest extends LightCodeInsightTestCase { else { methodToSearchFor = method; } - PsiExpression[] occurences = null; + PsiExpression[] occurences; PsiExpression initializer; if (expr == null) { - initializer = localVariable.getInitializer(); - occurences = CodeInsightUtil.findReferenceExpressions(method, localVariable); + initializer = localVar.getInitializer(); + occurences = CodeInsightUtil.findReferenceExpressions(method, localVar); } else { initializer = expr; @@ -357,7 +360,7 @@ public class IntroduceParameterTest extends LightCodeInsightTestCase { } TIntArrayList parametersToRemove = removeUnusedParameters ? Util.findParametersToRemove(method, initializer, occurences) : new TIntArrayList(); new IntroduceParameterProcessor( - getProject(), method, methodToSearchFor, initializer, expr, localVariable, true, parameterName, replaceAllOccurences, + getProject(), method, methodToSearchFor, initializer, expr, localVar, true, parameterName, replaceAllOccurences, replaceFieldsWithGetters, declareFinal, generateDelegate, null, parametersToRemove).run(); diff --git a/java/openapi/src/com/intellij/codeInsight/CodeInsightServicesUtil.java b/java/openapi/src/com/intellij/codeInsight/CodeInsightServicesUtil.java index f18999049d9b..b8b47b351624 100644 --- a/java/openapi/src/com/intellij/codeInsight/CodeInsightServicesUtil.java +++ b/java/openapi/src/com/intellij/codeInsight/CodeInsightServicesUtil.java @@ -26,7 +26,7 @@ import com.intellij.util.IncorrectOperationException; public class CodeInsightServicesUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.CodeInsightServicesUtil"); - private static final IElementType[] ourTokenMap = new IElementType[]{ + private static final IElementType[] ourTokenMap = { JavaTokenType.EQEQ, JavaTokenType.NE, JavaTokenType.LT, JavaTokenType.GE, JavaTokenType.LE, JavaTokenType.GT, @@ -36,39 +36,46 @@ public class CodeInsightServicesUtil { public static PsiExpression invertCondition(PsiExpression booleanExpression) throws IncorrectOperationException { PsiElementFactory factory = JavaPsiFacade.getInstance(booleanExpression.getProject()).getElementFactory(); - if (booleanExpression instanceof PsiBinaryExpression) { - PsiBinaryExpression expression = (PsiBinaryExpression) booleanExpression; - PsiJavaToken operationSign = expression.getOperationSign(); + if (booleanExpression instanceof PsiPolyadicExpression) { + PsiPolyadicExpression expression = (PsiPolyadicExpression)booleanExpression; + IElementType operationSign = expression.getOperationTokenType(); for (int i = 0; i < ourTokenMap.length; i++) { IElementType tokenType = ourTokenMap[i]; - if (operationSign.getTokenType() == tokenType) { + if (operationSign == tokenType) { expression = (PsiBinaryExpression)expression.copy(); - expression.getOperationSign().replace(createOperationToken(factory, ourTokenMap[i + (i % 2 == 0 ? 1 : -1)])); - if (tokenType == JavaTokenType.OROR || tokenType == JavaTokenType.ANDAND) { - expression.getLOperand().replace(invertCondition(expression.getLOperand())); - expression.getROperand().replace(invertCondition(expression.getROperand())); + PsiExpression[] operands = expression.getOperands(); + for (int o = 0; o < operands.length; o++) { + PsiExpression op = operands[o]; + if (o != 0) { + expression.getTokenBeforeOperand(op).replace(createOperationToken(factory, ourTokenMap[i + (i % 2 == 0 ? 1 : -1)])); + } + if (tokenType == JavaTokenType.OROR || tokenType == JavaTokenType.ANDAND) { + op.replace(invertCondition(op)); + } } return expression; } } - } else if (booleanExpression instanceof PsiPrefixExpression) { - PsiPrefixExpression expression = (PsiPrefixExpression) booleanExpression; + } + else if (booleanExpression instanceof PsiPrefixExpression) { + PsiPrefixExpression expression = (PsiPrefixExpression)booleanExpression; PsiJavaToken operationSign = expression.getOperationSign(); if (operationSign.getTokenType() == JavaTokenType.EXCL) { PsiExpression operand = expression.getOperand(); if (operand instanceof PsiParenthesizedExpression) { - operand = ((PsiParenthesizedExpression) operand).getExpression(); + operand = ((PsiParenthesizedExpression)operand).getExpression(); } return operand; } - } else if (booleanExpression instanceof PsiLiteralExpression) { + } + else if (booleanExpression instanceof PsiLiteralExpression) { return booleanExpression.getText().equals("true") ? factory.createExpressionFromText("false", null) : factory.createExpressionFromText("true", null); } if (booleanExpression instanceof PsiParenthesizedExpression) { - PsiExpression operand = ((PsiParenthesizedExpression) booleanExpression).getExpression(); + PsiExpression operand = ((PsiParenthesizedExpression)booleanExpression).getExpression(); operand.replace(invertCondition(operand)); return booleanExpression; } @@ -76,8 +83,9 @@ public class CodeInsightServicesUtil { PsiPrefixExpression result = (PsiPrefixExpression)factory.createExpressionFromText("!(a)", null); if (!(booleanExpression instanceof PsiBinaryExpression)) { result.getOperand().replace(booleanExpression); - } else { - PsiParenthesizedExpression e = (PsiParenthesizedExpression) result.getOperand(); + } + else { + PsiParenthesizedExpression e = (PsiParenthesizedExpression)result.getOperand(); e.getExpression().replace(booleanExpression); } @@ -115,7 +123,7 @@ public class CodeInsightServicesUtil { s = "=="; } - PsiBinaryExpression expression = (PsiBinaryExpression) factory.createExpressionFromText("a" + s + "b", null); + PsiBinaryExpression expression = (PsiBinaryExpression)factory.createExpressionFromText("a" + s + "b", null); return expression.getOperationSign(); } } diff --git a/java/openapi/src/com/intellij/psi/JavaElementVisitor.java b/java/openapi/src/com/intellij/psi/JavaElementVisitor.java index cce2a7916ded..7ece4a6065ae 100644 --- a/java/openapi/src/com/intellij/psi/JavaElementVisitor.java +++ b/java/openapi/src/com/intellij/psi/JavaElementVisitor.java @@ -41,7 +41,7 @@ public abstract class JavaElementVisitor extends PsiElementVisitor { } public void visitBinaryExpression(PsiBinaryExpression expression) { - visitExpression(expression); + visitPolyadicExpression(expression); } public void visitBlockStatement(PsiBlockStatement statement) { @@ -367,4 +367,8 @@ public abstract class JavaElementVisitor extends PsiElementVisitor { public void visitCodeFragment(JavaCodeFragment codeFragment) { visitFile(codeFragment); } + + public void visitPolyadicExpression(PsiPolyadicExpression expression) { + visitExpression(expression); + } } diff --git a/java/openapi/src/com/intellij/psi/PsiBinaryExpression.java b/java/openapi/src/com/intellij/psi/PsiBinaryExpression.java index f97dcd7451c9..a59607bae701 100644 --- a/java/openapi/src/com/intellij/psi/PsiBinaryExpression.java +++ b/java/openapi/src/com/intellij/psi/PsiBinaryExpression.java @@ -22,7 +22,7 @@ import com.intellij.psi.tree.IElementType; /** * Represents a Java binary expression (addition, multiplication and so on). */ -public interface PsiBinaryExpression extends PsiExpression { +public interface PsiBinaryExpression extends PsiExpression, PsiPolyadicExpression { /** * Returns the left operand of the expression. * diff --git a/java/openapi/src/com/intellij/psi/PsiPolyadicExpression.java b/java/openapi/src/com/intellij/psi/PsiPolyadicExpression.java new file mode 100644 index 000000000000..c1b40d0968e7 --- /dev/null +++ b/java/openapi/src/com/intellij/psi/PsiPolyadicExpression.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi; + +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NotNull; + +/** + * User: cdr + */ +public interface PsiPolyadicExpression extends PsiExpression { + @NotNull PsiExpression[] getOperands(); + + @NotNull + IElementType getOperationTokenType(); + PsiJavaToken getTokenBeforeOperand(@NotNull PsiExpression operand); +} diff --git a/java/openapi/src/com/intellij/psi/util/IsConstantExpressionVisitor.java b/java/openapi/src/com/intellij/psi/util/IsConstantExpressionVisitor.java index 79723f2a7462..bbed9e3d747d 100644 --- a/java/openapi/src/com/intellij/psi/util/IsConstantExpressionVisitor.java +++ b/java/openapi/src/com/intellij/psi/util/IsConstantExpressionVisitor.java @@ -82,20 +82,11 @@ public class IsConstantExpressionVisitor extends JavaElementVisitor { myIsConstant = false; } - @Override public void visitBinaryExpression(PsiBinaryExpression expression) { - while (true) { - // check right operand first since it tends to be shorter - PsiExpression rOperand = expression.getROperand(); - if (rOperand == null) return; - rOperand.accept(this); + @Override + public void visitPolyadicExpression(PsiPolyadicExpression expression) { + for (PsiExpression operand : expression.getOperands()) { + operand.accept(this); if (!myIsConstant) return; - - PsiExpression lOperand = expression.getLOperand(); - if (!(lOperand instanceof PsiBinaryExpression)) { - lOperand.accept(this); - break; - } - expression = (PsiBinaryExpression)lOperand; } } diff --git a/java/openapi/src/com/intellij/psi/util/PsiConcatenationUtil.java b/java/openapi/src/com/intellij/psi/util/PsiConcatenationUtil.java new file mode 100644 index 000000000000..9b60456845ac --- /dev/null +++ b/java/openapi/src/com/intellij/psi/util/PsiConcatenationUtil.java @@ -0,0 +1,138 @@ +/* + * Copyright 2000-2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.util; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.IncorrectOperationException; + +import java.util.List; + +/** + * User: cdr + */ +public class PsiConcatenationUtil { + public static void buildFormatString( + PsiExpression expression, StringBuilder formatString, + List formatParameters, boolean printfFormat) { + if (expression instanceof PsiLiteralExpression) { + final PsiLiteralExpression literalExpression = + (PsiLiteralExpression) expression; + final String text = String.valueOf(literalExpression.getValue()); + final String formatText; + if (printfFormat) { + formatText = StringUtil.escapeStringCharacters(text) + .replace("%", "%%").replace("\\'", "'"); + } + else { + formatText = StringUtil.escapeStringCharacters(text) + .replace("'", "''").replace("{", "'{").replace("}", "'}"); + } + formatString.append(formatText); + } else if (expression instanceof PsiPolyadicExpression) { + final PsiType type = expression.getType(); + if (type != null && type.equalsToText("java.lang.String")) { + final PsiPolyadicExpression binaryExpression = + (PsiPolyadicExpression) expression; + PsiExpression[] operands = binaryExpression.getOperands(); + PsiType left = operands[0].getType(); + boolean stringStarted = left != null && left.equalsToText("java.lang.String"); + if (stringStarted) { + buildFormatString(operands[0], formatString, formatParameters, printfFormat); + } + for (int i = 1; i < operands.length; i++) { + PsiExpression op = operands[i]; + PsiType optype = op.getType(); + PsiType r = TypeConversionUtil.calcTypeForBinaryExpression(left, optype, binaryExpression.getOperationTokenType(), true); + if (r != null && r.equalsToText("java.lang.String") && !stringStarted) { + stringStarted = true; + PsiElement element = binaryExpression.getTokenBeforeOperand(op); + if (element.getPrevSibling() instanceof PsiWhiteSpace) element = element.getPrevSibling(); + String text = binaryExpression.getText().substring(0, element.getStartOffsetInParent()); + PsiExpression subExpression = JavaPsiFacade.getInstance(binaryExpression.getProject()).getElementFactory() + .createExpressionFromText(text, binaryExpression); + addFormatParameter(subExpression, formatString, formatParameters, printfFormat); + } + if (stringStarted) { + if (optype != null && (optype.equalsToText("java.lang.String") || optype == PsiType.CHAR)) { + buildFormatString(op, formatString, formatParameters, printfFormat); + } + else { + addFormatParameter(op, formatString, formatParameters, printfFormat); + } + } + left = r; + } + } + else { + addFormatParameter(expression, formatString, formatParameters, printfFormat); + } + } + else { + addFormatParameter(expression, formatString, formatParameters, printfFormat); + } + } + + private static void addFormatParameter(PsiExpression expression, + StringBuilder formatString, + List formatParameters, boolean printfFormat) { + final PsiType type = expression.getType(); + if (!printfFormat) { + formatString.append("{" + formatParameters.size() + "}"); + } + else if (type != null && + (type.equalsToText("long") || + type.equalsToText("int") || + type.equalsToText("java.lang.Long") || + type.equalsToText("java.lang.Integer"))) { + formatString.append("%d"); + } + else { + formatString.append("%s"); + } + formatParameters.add(getBoxedArgument(expression)); + } + + private static PsiExpression getBoxedArgument(PsiExpression arg) throws IncorrectOperationException { + arg = PsiUtil.deparenthesizeExpression(arg); + assert arg != null; + if (PsiUtil.isLanguageLevel5OrHigher(arg)) { + return arg; + } + final PsiType type = arg.getType(); + if (!(type instanceof PsiPrimitiveType) || type.equals(PsiType.NULL)) { + return arg; + } + final PsiPrimitiveType primitiveType = (PsiPrimitiveType)type; + final String boxedQName = primitiveType.getBoxedTypeName(); + if (boxedQName == null) { + return arg; + } + final GlobalSearchScope resolveScope = arg.getResolveScope(); + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(arg.getProject()); + final PsiJavaCodeReferenceElement ref = factory.createReferenceElementByFQClassName(boxedQName, resolveScope); + final PsiNewExpression newExpr = (PsiNewExpression)factory.createExpressionFromText("new A(b)", null); + final PsiElement classRef = newExpr.getClassReference(); + assert classRef != null; + classRef.replace(ref); + final PsiExpressionList argumentList = newExpr.getArgumentList(); + assert argumentList != null; + argumentList.getExpressions()[0].replace(arg); + return newExpr; + } + +} diff --git a/java/openapi/src/com/intellij/psi/util/PsiExpressionTrimRenderer.java b/java/openapi/src/com/intellij/psi/util/PsiExpressionTrimRenderer.java index 1e82ddd1319a..73a87a08b14b 100644 --- a/java/openapi/src/com/intellij/psi/util/PsiExpressionTrimRenderer.java +++ b/java/openapi/src/com/intellij/psi/util/PsiExpressionTrimRenderer.java @@ -94,12 +94,15 @@ public class PsiExpressionTrimRenderer extends JavaRecursiveElementWalkingVisito } @Override - public void visitBinaryExpression(final PsiBinaryExpression expression) { - expression.getLOperand().accept(this); - myBuf.append(" ").append(expression.getOperationSign().getText()).append(" "); - final PsiExpression rOperand = expression.getROperand(); - if (rOperand != null) { - rOperand.accept(this); + public void visitPolyadicExpression(PsiPolyadicExpression expression) { + PsiExpression[] operands = expression.getOperands(); + for (int i = 0; i < operands.length; i++) { + PsiExpression operand = operands[i]; + operand.accept(this); + if (i != 0) { + PsiJavaToken token = expression.getTokenBeforeOperand(operand); + myBuf.append(" ").append(token.getText()).append(" "); + } } } diff --git a/java/openapi/src/com/intellij/psi/util/RedundantCastUtil.java b/java/openapi/src/com/intellij/psi/util/RedundantCastUtil.java index 15f928500624..8ac58847d456 100644 --- a/java/openapi/src/com/intellij/psi/util/RedundantCastUtil.java +++ b/java/openapi/src/com/intellij/psi/util/RedundantCastUtil.java @@ -136,25 +136,29 @@ public class RedundantCastUtil { super.visitReturnStatement(statement); } - @Override public void visitBinaryExpression(PsiBinaryExpression expression) { - PsiExpression rExpr = deparenthesizeExpression(expression.getLOperand()); - PsiExpression lExpr = deparenthesizeExpression(expression.getROperand()); - - if (rExpr != null && lExpr != null) { - final IElementType binaryToken = expression.getOperationTokenType(); - processBinaryExpressionOperand(lExpr, rExpr, binaryToken); - processBinaryExpressionOperand(rExpr, lExpr, binaryToken); + @Override + public void visitPolyadicExpression(PsiPolyadicExpression expression) { + IElementType tokenType = expression.getOperationTokenType(); + PsiExpression[] operands = expression.getOperands(); + if (operands.length >= 2) { + PsiType lType = operands[0].getType(); + processBinaryExpressionOperand(deparenthesizeExpression(operands[0]), operands[1].getType(), tokenType); + for (int i = 1; i < operands.length; i++) { + PsiExpression operand = deparenthesizeExpression(operands[i]); + processBinaryExpressionOperand(operand, lType, tokenType); + lType = TypeConversionUtil.calcTypeForBinaryExpression(lType, operand.getType(), tokenType, true); + } } - super.visitBinaryExpression(expression); + super.visitPolyadicExpression(expression); } private void processBinaryExpressionOperand(final PsiExpression operand, - final PsiExpression otherOperand, + final PsiType otherType, final IElementType binaryToken) { if (operand instanceof PsiTypeCastExpression) { PsiTypeCastExpression typeCast = (PsiTypeCastExpression)operand; PsiExpression toCast = typeCast.getOperand(); - if (toCast != null && TypeConversionUtil.isBinaryOperatorApplicable(binaryToken, toCast, otherOperand, false)) { + if (toCast != null && TypeConversionUtil.isBinaryOperatorApplicable(binaryToken, toCast.getType(), otherType, false)) { addToResults(typeCast); } } diff --git a/java/openapi/src/com/intellij/psi/util/TypeConversionUtil.java b/java/openapi/src/com/intellij/psi/util/TypeConversionUtil.java index eff8f437a258..139030a835ff 100644 --- a/java/openapi/src/com/intellij/psi/util/TypeConversionUtil.java +++ b/java/openapi/src/com/intellij/psi/util/TypeConversionUtil.java @@ -29,6 +29,7 @@ import com.intellij.util.containers.HashMap; import gnu.trove.THashMap; import gnu.trove.THashSet; import gnu.trove.TObjectIntHashMap; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -62,6 +63,16 @@ public class TypeConversionUtil { private static final int BOOL_RANK = 10; private static final int STRING_RANK = 100; private static final int MAX_NUMERIC_RANK = DOUBLE_RANK; + public static final PsiType NULL_TYPE = new PsiEllipsisType(PsiType.NULL){ + public boolean isValid() { + return true; + } + + @NonNls + public String getPresentableText() { + return "FAKE TYPE"; + } + }; static { TYPE_TO_RANK_MAP.put(PsiType.BYTE, BYTE_RANK); @@ -1269,6 +1280,62 @@ public class TypeConversionUtil { return opSign; } + @Nullable + public static PsiType calcTypeForBinaryExpression(PsiType lType, PsiType rType, IElementType sign, boolean accessLType) { + if (sign == JavaTokenType.PLUS) { + // evaluate right argument first, since '+-/*%' is left associative and left operand tends to be bigger + if (rType == null) return null; + if (rType.equalsToText("java.lang.String")) { + return rType; + } + if (!accessLType) return NULL_TYPE; + if (lType == null) return null; + if (lType.equalsToText("java.lang.String")) { + return lType; + } + return unboxAndBalanceTypes(lType, rType); + } + if (sign == JavaTokenType.MINUS || sign == JavaTokenType.ASTERISK || sign == JavaTokenType.DIV || sign == JavaTokenType.PERC) { + if (rType == null) return null; + if (!accessLType) return NULL_TYPE; + if (lType == null) return null; + return unboxAndBalanceTypes(lType, rType); + } + if (sign == JavaTokenType.LTLT || sign == JavaTokenType.GTGT || sign == JavaTokenType.GTGTGT) { + if (!accessLType) return NULL_TYPE; + if (PsiType.BYTE.equals(lType) || PsiType.CHAR.equals(lType) || PsiType.SHORT.equals(lType)) { + return PsiType.INT; + } + if (lType instanceof PsiClassType) lType = PsiPrimitiveType.getUnboxedType(lType); + return lType; + } + if (sign == JavaTokenType.EQEQ || + sign == JavaTokenType.NE || + sign == JavaTokenType.LT || + sign == JavaTokenType.GT || + sign == JavaTokenType.LE || + sign == JavaTokenType.GE || + sign == JavaTokenType.OROR || + sign == JavaTokenType.ANDAND) { + return PsiType.BOOLEAN; + } + if (sign == JavaTokenType.OR || sign == JavaTokenType.XOR || sign == JavaTokenType.AND) { + if (rType instanceof PsiClassType) rType = PsiPrimitiveType.getUnboxedType(rType); + + if (lType instanceof PsiClassType) lType = PsiPrimitiveType.getUnboxedType(lType); + + if (rType == null) return null; + if (PsiType.BOOLEAN.equals(rType)) return PsiType.BOOLEAN; + if (!accessLType) return NULL_TYPE; + if (lType == null) return null; + if (PsiType.BOOLEAN.equals(lType)) return PsiType.BOOLEAN; + if (PsiType.LONG.equals(lType) || PsiType.LONG.equals(rType)) return PsiType.LONG; + return PsiType.INT; + } + LOG.error("Unknown token: "+sign); + return null; + } + private interface Caster { Object cast(Object operand); } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java index f01dbeb77f0d..45c452ed9330 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java @@ -16,10 +16,7 @@ package com.intellij.psi.impl.source.tree.injected; -import com.intellij.injected.editor.DocumentWindow; -import com.intellij.injected.editor.DocumentWindowImpl; -import com.intellij.injected.editor.EditorWindow; -import com.intellij.injected.editor.VirtualFileWindow; +import com.intellij.injected.editor.*; import com.intellij.lang.Language; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.editor.Document; @@ -40,6 +37,7 @@ import com.intellij.psi.impl.PsiParameterizedCachedValue; import com.intellij.psi.impl.source.DummyHolder; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.*; +import com.intellij.util.ParameterizedCachedValueImpl; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -190,21 +188,37 @@ public class InjectedLanguageUtil { private static final InjectedPsiCachedValueProvider INJECTED_PSI_PROVIDER = new InjectedPsiCachedValueProvider(); private static final Key> INJECTED_PSI_KEY = Key.create("INJECTED_PSI"); + public static ParameterizedCachedValue NULL_VALUE = new ParameterizedCachedValueImpl(new ParameterizedCachedValueProvider() { + @Override + public CachedValueProvider.Result compute(PsiElement param) { + return CachedValueProvider.Result.create(NULL_REGISTRAR, param.getManager().getModificationTracker()); + } + }) { + @Override + public boolean isFromMyProject(Project project) { + return true; + } + }; + + private static final MultiHostRegistrarImpl NULL_REGISTRAR = new MultiHostRegistrarImpl(); + + private static MultiHostRegistrarImpl probeElementsUp(@NotNull PsiElement element, @NotNull PsiFile hostPsiFile, boolean probeUp) { PsiManager psiManager = hostPsiFile.getManager(); final Project project = psiManager.getProject(); InjectedLanguageManagerImpl injectedManager = InjectedLanguageManagerImpl.getInstanceImpl(project); if (injectedManager == null) return null; //for tests - for (PsiElement current = element; current != null && current != hostPsiFile; current = current.getParent()) { + MultiHostRegistrarImpl registrar = null; + PsiElement current; + nextParent: + for (current = element; current != null && current != hostPsiFile; current = current.getParent()) { ProgressManager.checkCanceled(); if ("EL".equals(current.getLanguage().getID())) break; ParameterizedCachedValue data = current.getUserData(INJECTED_PSI_KEY); - MultiHostRegistrarImpl registrar; if (data == null) { registrar = InjectedPsiCachedValueProvider.doCompute(current, injectedManager, project, hostPsiFile); if (registrar != null) { - // pollute user data only if there is injected fragment there ParameterizedCachedValue cachedValue = CachedValuesManager.getManager(psiManager.getProject()).createParameterizedCachedValue(INJECTED_PSI_PROVIDER, false); Document hostDocument = hostPsiFile.getViewProvider().getDocument(); @@ -216,6 +230,10 @@ public class InjectedLanguageUtil { else { registrar = data.getValue(current); } + if (registrar == NULL_REGISTRAR) { + registrar = null; + break; + } if (registrar != null) { List> places = registrar.result; // check that injections found intersect with queried element @@ -224,14 +242,21 @@ public class InjectedLanguageUtil { Place place = pair.first; for (PsiLanguageInjectionHost.Shred shred : place) { if (shred.host.getTextRange().intersects(elementRange)) { - if (place.isValid()) return registrar; + if (place.isValid()) break nextParent; } } } } if (!probeUp) break; } - return null; + if (registrar == null) { + for (PsiElement e = element; e != current && e != null && e != hostPsiFile; e = e.getParent()) { + ProgressManager.checkCanceled(); + //store no-injection flag + e.putUserData(INJECTED_PSI_KEY, NULL_VALUE); + } + } + return registrar; } @Nullable @@ -324,10 +349,10 @@ public class InjectedLanguageUtil { } public static void clearCaches(@NotNull PsiFile injected, @NotNull DocumentWindowImpl documentWindow) { - VirtualFileWindow virtualFile = (VirtualFileWindow)injected.getVirtualFile(); + VirtualFileWindowImpl virtualFile = (VirtualFileWindowImpl)injected.getVirtualFile(); PsiManagerEx psiManagerEx = (PsiManagerEx)injected.getManager(); if (psiManagerEx.getProject().isDisposed()) return; - psiManagerEx.getFileManager().setViewProvider((VirtualFile)virtualFile, null); + psiManagerEx.getFileManager().setViewProvider(virtualFile, null); PsiElement context = injected.getContext(); PsiFile hostFile; if (context != null) { @@ -349,6 +374,8 @@ public class InjectedLanguageUtil { } } } + //FileDocumentManagerImpl.registerDocument(null, virtualFile); + //FileDocumentManagerImpl.registerDocument(documentWindow, null); } diff --git a/platform/testFramework/src/com/intellij/testFramework/ParsingTestCase.java b/platform/testFramework/src/com/intellij/testFramework/ParsingTestCase.java index bdde15051217..69cb188f5026 100644 --- a/platform/testFramework/src/com/intellij/testFramework/ParsingTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/ParsingTestCase.java @@ -110,7 +110,7 @@ public abstract class ParsingTestCase extends LightPlatformTestCase { try { text = text.trim(); String expectedText = doLoadFile(myFullDataPath, targetDataName); - assertEquals(expectedText, text); + assertEquals(targetDataName, expectedText, text); } catch(FileNotFoundException e){ String fullName = myFullDataPath + File.separatorChar + targetDataName; diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/numeric/CharUsedInArithmeticContextInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/numeric/CharUsedInArithmeticContextInspection.java index 30a2e959fcfc..ece02825083e 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/numeric/CharUsedInArithmeticContextInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/numeric/CharUsedInArithmeticContextInspection.java @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.siyeh.ig.numeric; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.IncorrectOperationException; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; @@ -50,8 +52,8 @@ public class CharUsedInArithmeticContextInspection extends BaseInspection { @NotNull @Override protected InspectionGadgetsFix[] buildFixes(Object... infos) { - final List result = new ArrayList(); - final PsiElement expression = (PsiElement)infos[0]; + List result = new ArrayList(); + PsiElement expression = (PsiElement)infos[0]; PsiElement parent = expression.getParent(); if (parent instanceof PsiExpression) { final PsiExpression binaryExpression = @@ -65,10 +67,8 @@ public class CharUsedInArithmeticContextInspection extends BaseInspection { if (!(expression instanceof PsiLiteralExpression)) { return result.toArray(new InspectionGadgetsFix[result.size()]); } - while (parent instanceof PsiBinaryExpression) { - final PsiBinaryExpression binaryExpression = - (PsiBinaryExpression) parent; - if (TypeUtils.expressionHasType(binaryExpression, + while (parent instanceof PsiPolyadicExpression) { + if (TypeUtils.expressionHasType((PsiExpression)parent, CommonClassNames.JAVA_LANG_STRING)) { result.add(new CharUsedInArithmeticContentFix()); break; @@ -142,32 +142,31 @@ public class CharUsedInArithmeticContextInspection extends BaseInspection { private static class CharUsedInArithmeticContextVisitor extends BaseInspectionVisitor { - - @Override - public void visitBinaryExpression(PsiBinaryExpression expression) { - super.visitBinaryExpression(expression); - final PsiType type = expression.getType(); - if (type == null || type.equalsToText("java.lang.String")) { - return; - } + @Override + public void visitPolyadicExpression(PsiPolyadicExpression expression) { + super.visitPolyadicExpression(expression); final IElementType tokenType = expression.getOperationTokenType(); if (ComparisonUtils.isComparisonOperation(tokenType)) { return; } - final PsiExpression lhs = expression.getLOperand(); - final PsiType lhsType = lhs.getType(); - if (PsiType.CHAR.equals(lhsType)) { - registerError(lhs, lhs); - } - final PsiExpression rhs = expression.getROperand(); - if (rhs == null) { - return; - } - final PsiType rhsType = rhs.getType(); - if (!PsiType.CHAR.equals(rhsType)) { - return; - } - registerError(rhs, rhs); + PsiExpression[] operands = expression.getOperands(); + PsiType left = operands[0].getType(); + for (int i = 1; i < operands.length; i++) { + PsiExpression operand = operands[i]; + final PsiType rType = operand.getType(); + PsiType opType = TypeConversionUtil.calcTypeForBinaryExpression(left, rType, tokenType, true); + if (opType == null || opType.equalsToText("java.lang.String")) { + return; + } + + if (PsiType.CHAR.equals(rType)) { + registerError(operand, operand); + } + if (PsiType.CHAR.equals(left) && i == 1) { + registerError(operands[0], operands[0]); + } + left = rType; } + } } } diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/performance/StringConcatenationInLoopsInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/performance/StringConcatenationInLoopsInspection.java index bc68834f7b09..0e1162419f2d 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/performance/StringConcatenationInLoopsInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/performance/StringConcatenationInLoopsInspection.java @@ -70,41 +70,38 @@ public class StringConcatenationInLoopsInspection extends BaseInspection { private class StringConcatenationInLoopsVisitor extends BaseInspectionVisitor { - - @Override public void visitBinaryExpression( - @NotNull PsiBinaryExpression expression) { - super.visitBinaryExpression(expression); - if (expression.getROperand() == null) { + @Override + public void visitPolyadicExpression(PsiPolyadicExpression expression) { + super.visitPolyadicExpression(expression); + PsiExpression[] operands = expression.getOperands(); + if (operands.length <= 1) { return; } - final PsiJavaToken sign = expression.getOperationSign(); - final IElementType tokenType = sign.getTokenType(); - if (!tokenType.equals(JavaTokenType.PLUS)) { - return; - } - final PsiType type = expression.getType(); - if (type == null) { - return; - } - if (!TypeUtils.isJavaLangString(type)) { - return; - } - if (!ControlFlowUtils.isInLoop(expression)) { - return; - } - if (ControlFlowUtils.isInExitStatement(expression)) { - return; - } - if (ExpressionUtils.isEvaluatedAtCompileTime(expression)) { - return; - } - if (containingStatementExits(expression)) { - return; - } - if (m_ignoreUnlessAssigned && !isAppendedRepeatedly(expression)) { - return; - } - registerError(sign); + final IElementType tokenType = expression.getOperationTokenType(); + if (!tokenType.equals(JavaTokenType.PLUS)) { + return; + } + final PsiType type = expression.getType(); + if (!TypeUtils.isJavaLangString(type)) { + return; + } + if (!ControlFlowUtils.isInLoop(expression)) { + return; + } + if (ControlFlowUtils.isInExitStatement(expression)) { + return; + } + if (ExpressionUtils.isEvaluatedAtCompileTime(expression)) { + return; + } + if (containingStatementExits(expression)) { + return; + } + if (m_ignoreUnlessAssigned && !isAppendedRepeatedly(expression)) { + return; + } + final PsiJavaToken sign = expression.getTokenBeforeOperand(operands[1]); + registerError(sign); } @Override public void visitAssignmentExpression( @@ -164,7 +161,7 @@ public class StringConcatenationInLoopsInspection extends BaseInspection { private boolean isAppendedRepeatedly(PsiExpression expression) { PsiElement parent = expression.getParent(); while (parent instanceof PsiParenthesizedExpression || - parent instanceof PsiBinaryExpression) { + parent instanceof PsiPolyadicExpression) { parent = parent.getParent(); } if (!(parent instanceof PsiAssignmentExpression)) { @@ -197,4 +194,4 @@ public class StringConcatenationInLoopsInspection extends BaseInspection { VariableAccessUtils.variableIsUsed(variable, rhs); } } -} \ No newline at end of file +} diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ParenthesesUtils.java b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ParenthesesUtils.java index bbfb0a75a10f..c4d14cc628ab 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ParenthesesUtils.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ParenthesesUtils.java @@ -126,11 +126,11 @@ public class ParenthesesUtils{ if(expression instanceof PsiPostfixExpression){ return POSTFIX_PRECEDENCE; } - if(expression instanceof PsiBinaryExpression){ - final PsiBinaryExpression binaryExpression = - (PsiBinaryExpression)expression; - final PsiJavaToken sign = - binaryExpression.getOperationSign(); + if(expression instanceof PsiPolyadicExpression){ + final PsiPolyadicExpression binaryExpression = + (PsiPolyadicExpression)expression; + final IElementType sign = + binaryExpression.getOperationTokenType(); return getPrecedenceForBinaryOperator(sign); } if(expression instanceof PsiInstanceOfExpression){ @@ -214,9 +214,9 @@ public class ParenthesesUtils{ removeParensFromPostfixExpression(postfixExpression, ignoreClarifyingParentheses); } - if(expression instanceof PsiBinaryExpression){ - final PsiBinaryExpression binaryExpression = - (PsiBinaryExpression)expression; + if(expression instanceof PsiPolyadicExpression){ + final PsiPolyadicExpression binaryExpression = + (PsiPolyadicExpression)expression; removeParensFromBinaryExpression(binaryExpression, ignoreClarifyingParentheses); } @@ -282,23 +282,22 @@ public class ParenthesesUtils{ removeParentheses(expression, ignoreClarifyingParentheses); } } else if(parentPrecedence == childPrecedence){ - if(parentExpression instanceof PsiBinaryExpression && - body instanceof PsiBinaryExpression){ - final PsiBinaryExpression parentBinaryExpression = - (PsiBinaryExpression)parentExpression; + if(parentExpression instanceof PsiPolyadicExpression && + body instanceof PsiPolyadicExpression){ + final PsiPolyadicExpression parentBinaryExpression = + (PsiPolyadicExpression)parentExpression; final IElementType parentOperator = parentBinaryExpression.getOperationTokenType(); - final PsiBinaryExpression bodyBinaryExpression = - (PsiBinaryExpression)body; + final PsiPolyadicExpression bodyBinaryExpression = + (PsiPolyadicExpression)body; final IElementType bodyOperator = bodyBinaryExpression.getOperationTokenType(); final PsiType parentType = parentBinaryExpression.getType(); final PsiType bodyType = body.getType(); if(parentType != null && parentType.equals(bodyType) && parentOperator.equals(bodyOperator)) { - final PsiExpression rhs = - parentBinaryExpression.getROperand(); - if (!PsiTreeUtil.isAncestor(rhs, body, true) || + PsiExpression[] parentOperands = parentBinaryExpression.getOperands(); + if (PsiTreeUtil.isAncestor(parentOperands[0], body, true) || isCommutativeBinaryOperator(bodyOperator)) { // use addAfter() + delete() instead of replace() to // workaround automatic insertion of parentheses by psi @@ -327,8 +326,8 @@ public class ParenthesesUtils{ } } else { if (ignoreClarifyingParentheses && - parent instanceof PsiBinaryExpression && - (body instanceof PsiBinaryExpression || + parent instanceof PsiPolyadicExpression && + (body instanceof PsiPolyadicExpression || body instanceof PsiInstanceOfExpression)) { removeParentheses(body, ignoreClarifyingParentheses); } else { @@ -366,15 +365,12 @@ public class ParenthesesUtils{ } private static void removeParensFromBinaryExpression( - @NotNull PsiBinaryExpression binaryExpression, + @NotNull PsiPolyadicExpression binaryExpression, boolean ignoreClarifyingParentheses) throws IncorrectOperationException { - final PsiExpression lhs = binaryExpression.getLOperand(); - removeParentheses(lhs, ignoreClarifyingParentheses); - final PsiExpression rhs = binaryExpression.getROperand(); - if (rhs != null) { - removeParentheses(rhs, ignoreClarifyingParentheses); - } + for (PsiExpression operand : binaryExpression.getOperands()) { + removeParentheses(operand, ignoreClarifyingParentheses); + } } private static void removeParensFromPostfixExpression( @@ -495,12 +491,13 @@ public class ParenthesesUtils{ public static boolean areParenthesesNeeded( PsiExpression expression, PsiElement parentExpression, boolean ignoreClarifyingParentheses) { - if (parentExpression instanceof PsiBinaryExpression) { - final PsiBinaryExpression parentBinaryExpression = - (PsiBinaryExpression) parentExpression; - if (expression instanceof PsiBinaryExpression) { - final PsiBinaryExpression childBinaryExpression = - (PsiBinaryExpression)expression; + if (parentExpression instanceof PsiPolyadicExpression) { + final PsiPolyadicExpression parentBinaryExpression = + (PsiPolyadicExpression) parentExpression; + PsiExpression[] parentOperands = parentBinaryExpression.getOperands(); + if (expression instanceof PsiPolyadicExpression) { + final PsiPolyadicExpression childBinaryExpression = + (PsiPolyadicExpression)expression; final IElementType childOperator = childBinaryExpression.getOperationTokenType(); final IElementType parentOperator = @@ -517,24 +514,24 @@ public class ParenthesesUtils{ if (!parentType.equals(childType)) { return true; } - if (PsiTreeUtil.isAncestor(parentBinaryExpression.getROperand(), - expression, false)) { + if (!PsiTreeUtil.isAncestor(parentOperands[0], + expression, false)) { if (!isCommutativeBinaryOperator(parentOperator)) { return true; } } return false; } else if (expression instanceof PsiConditionalExpression) { - if (PsiTreeUtil.isAncestor(parentBinaryExpression.getROperand(), - expression, false)) { + if (!PsiTreeUtil.isAncestor(parentOperands[0], + expression, false)) { return true; } } } else if (parentExpression instanceof PsiPrefixExpression) { - if (expression instanceof PsiBinaryExpression) { + if (expression instanceof PsiPolyadicExpression) { return true; } } return false; } -} \ No newline at end of file +} diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/style/UnnecessaryParenthesesInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/style/UnnecessaryParenthesesInspection.java index 76f642b3a54b..9855aba7cfa2 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/style/UnnecessaryParenthesesInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/style/UnnecessaryParenthesesInspection.java @@ -112,8 +112,8 @@ public class UnnecessaryParenthesesInspection extends BaseInspection { final int childPrecedence = ParenthesesUtils.getPrecedence(child); if (parentPrecedence > childPrecedence) { if (ignoreClarifyingParentheses) { - if (parent instanceof PsiBinaryExpression && - child instanceof PsiBinaryExpression) { + if (parent instanceof PsiPolyadicExpression && + child instanceof PsiPolyadicExpression) { return; } else if (child instanceof PsiInstanceOfExpression) { return; @@ -143,4 +143,4 @@ public class UnnecessaryParenthesesInspection extends BaseInspection { super.visitParenthesizedExpression(expression); } } -} \ No newline at end of file +} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/performance/string_concatenation_in_loops/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/performance/string_concatenation_in_loops/expected.xml index d52bd4acd226..4b3ba72440f5 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/performance/string_concatenation_in_loops/expected.xml +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/performance/string_concatenation_in_loops/expected.xml @@ -8,20 +8,6 @@ String concatenation <code>+</code> in loop #loc - - StringConcatenationInLoop.java - 14 - String concatenation in loop - String concatenation <code>+</code> in loop #loc - - - - StringConcatenationInLoop.java - 15 - String concatenation in loop - String concatenation <code>+</code> in loop #loc - - StringConcatenationInLoop.java 15 @@ -43,4 +29,4 @@ String concatenation <code>+=</code> in loop #loc - \ No newline at end of file + diff --git a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/java/ConcatenationInjector.java b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/java/ConcatenationInjector.java index 9367ae2dd04a..67689d6fe807 100644 --- a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/java/ConcatenationInjector.java +++ b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/java/ConcatenationInjector.java @@ -407,7 +407,7 @@ public class ConcatenationInjector implements ConcatenationAwareInjector { return true; } } - else if (parent instanceof PsiBinaryExpression) { + else if (parent instanceof PsiBinaryExpression || parent instanceof PsiPolyadicExpression) { return true; } return false; diff --git a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/util/ContextComputationProcessor.java b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/util/ContextComputationProcessor.java index 2bc55e1e6fa7..7fedc7cdb25e 100644 --- a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/util/ContextComputationProcessor.java +++ b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/util/ContextComputationProcessor.java @@ -82,6 +82,13 @@ public class ContextComputationProcessor { collectOperands(binaryExpression.getLOperand(), result, unparsable); collectOperands(binaryExpression.getROperand(), result, unparsable); } + else if (expression instanceof PsiPolyadicExpression && + ((PsiPolyadicExpression)expression).getOperationTokenType() == JavaTokenType.PLUS) { + final PsiPolyadicExpression binaryExpression = (PsiPolyadicExpression)expression; + for (PsiExpression operand : binaryExpression.getOperands()) { + collectOperands(operand, result, unparsable); + } + } else if (expression instanceof PsiAssignmentExpression && ((PsiAssignmentExpression)expression).getOperationTokenType() == JavaTokenType.PLUSEQ) { unparsable.set(Boolean.TRUE); @@ -112,7 +119,7 @@ public class ContextComputationProcessor { PsiElement target = host; PsiElement parent = target.getParent(); for (; parent != null; target = parent, parent = target.getParent()) { - if (parent instanceof PsiBinaryExpression) continue; + if (parent instanceof PsiPolyadicExpression) continue; if (parent instanceof PsiParenthesizedExpression) continue; if (parent instanceof PsiConditionalExpression && ((PsiConditionalExpression)parent).getCondition() != target) continue; if (parent instanceof PsiArrayInitializerMemberValue) continue; diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/Jdk5StringConcatenationPredicate.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/Jdk5StringConcatenationPredicate.java index e289206334d3..85aaced800d8 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/Jdk5StringConcatenationPredicate.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/Jdk5StringConcatenationPredicate.java @@ -15,17 +15,14 @@ */ package com.siyeh.ipp.concatenation; -import com.intellij.psi.PsiArrayInitializerMemberValue; -import com.intellij.psi.PsiBinaryExpression; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiNameValuePair; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.siyeh.ipp.base.PsiElementPredicate; import com.siyeh.ipp.psiutils.ConcatenationUtils; import com.siyeh.ipp.psiutils.ErrorUtil; class Jdk5StringConcatenationPredicate implements PsiElementPredicate { - public boolean satisfiedBy(PsiElement element) { if (!PsiUtil.isLanguageLevel5OrHigher(element)) { return false; @@ -40,14 +37,6 @@ class Jdk5StringConcatenationPredicate implements PsiElementPredicate { } private static boolean isInsideAnnotation(PsiElement element) { - for (int i = 0; i < 20 && element instanceof PsiBinaryExpression; i++) { - // optimization: don't check deep string concatenations more than 20 levels up. - element = element.getParent(); - if (element instanceof PsiNameValuePair || - element instanceof PsiArrayInitializerMemberValue) { - return true; - } - } - return false; + return PsiTreeUtil.getParentOfType(element, PsiNameValuePair.class, PsiArrayInitializerMemberValue.class) != null; } } diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/ReplaceConcatenationWithFormatStringIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/ReplaceConcatenationWithFormatStringIntention.java index 7693623a9974..95f628014c79 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/ReplaceConcatenationWithFormatStringIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/ReplaceConcatenationWithFormatStringIntention.java @@ -15,8 +15,8 @@ */ package com.siyeh.ipp.concatenation; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; +import com.intellij.psi.util.PsiConcatenationUtil; import com.intellij.util.IncorrectOperationException; import com.siyeh.ipp.base.Intention; import com.siyeh.ipp.base.PsiElementPredicate; @@ -38,11 +38,11 @@ public class ReplaceConcatenationWithFormatStringIntention @Override protected void processIntention(@NotNull PsiElement element) throws IncorrectOperationException { - PsiBinaryExpression expression = - (PsiBinaryExpression)element; + PsiPolyadicExpression expression = + (PsiPolyadicExpression)element; PsiElement parent = expression.getParent(); while (ConcatenationUtils.isConcatenation(parent)) { - expression = (PsiBinaryExpression)parent; + expression = (PsiPolyadicExpression)parent; if (expression == null) { return; } @@ -51,7 +51,7 @@ public class ReplaceConcatenationWithFormatStringIntention final StringBuilder formatString = new StringBuilder(); final List formatParameters = new ArrayList(); - buildFormatString(expression, formatString, formatParameters); + PsiConcatenationUtil.buildFormatString(expression, formatString, formatParameters, true); if (replaceWithPrintfExpression(expression, formatString, formatParameters)) { return; @@ -69,7 +69,7 @@ public class ReplaceConcatenationWithFormatStringIntention } private static boolean replaceWithPrintfExpression( - PsiBinaryExpression expression, + PsiExpression expression, CharSequence formatString, List formatParameters) throws IncorrectOperationException { @@ -129,49 +129,4 @@ public class ReplaceConcatenationWithFormatStringIntention return true; } - private static void buildFormatString( - PsiExpression expression, StringBuilder formatString, - List formatParameters) { - if (expression instanceof PsiLiteralExpression) { - final PsiLiteralExpression literalExpression = - (PsiLiteralExpression) expression; - final String text = String.valueOf(literalExpression.getValue()); - final String formatText = - StringUtil.escapeStringCharacters(text) - .replace("%", "%%").replace("\\'", "'"); - formatString.append(formatText); - } else if (expression instanceof PsiBinaryExpression) { - final PsiType type = expression.getType(); - if (type != null && type.equalsToText("java.lang.String")) { - final PsiBinaryExpression binaryExpression = - (PsiBinaryExpression) expression; - final PsiExpression lhs = binaryExpression.getLOperand(); - buildFormatString(lhs, formatString, formatParameters); - final PsiExpression rhs = binaryExpression.getROperand(); - if (rhs != null) { - buildFormatString(rhs, formatString, formatParameters); - } - } else { - addFormatParameter(expression, formatString, formatParameters); - } - } else { - addFormatParameter(expression, formatString, formatParameters); - } - } - - private static void addFormatParameter(PsiExpression expression, - StringBuilder formatString, - List formatParameters) { - final PsiType type = expression.getType(); - if (type != null && - (type.equalsToText("long") || - type.equalsToText("int") || - type.equalsToText("java.lang.Long") || - type.equalsToText("java.lang.Integer"))) { - formatString.append("%d"); - } else { - formatString.append("%s"); - } - formatParameters.add(expression); - } } diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/ConcatenationUtils.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/ConcatenationUtils.java index 3c579e6d370c..3d4d72511f91 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/ConcatenationUtils.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/ConcatenationUtils.java @@ -25,31 +25,27 @@ public class ConcatenationUtils{ } public static boolean isConcatenation(PsiElement element){ - if(!(element instanceof PsiBinaryExpression)){ + if(!(element instanceof PsiPolyadicExpression)){ return false; } - final PsiBinaryExpression expression = (PsiBinaryExpression) element; - final PsiJavaToken sign = expression.getOperationSign(); - final IElementType tokenType = sign.getTokenType(); + final PsiPolyadicExpression expression = (PsiPolyadicExpression) element; + final IElementType tokenType = expression.getOperationTokenType(); if(!tokenType.equals(JavaTokenType.PLUS)){ return false; } - final PsiExpression rhs = expression.getROperand(); - if(rhs == null){ - return false; - } + PsiExpression[] operands = expression.getOperands(); + if (operands.length <= 1) return false; final PsiType type = expression.getType(); if(type == null){ - final PsiExpression lhs = expression.getLOperand(); - return hasStringType(lhs) || hasStringType(rhs); + for (PsiExpression operand : operands) { + if (hasStringType(operand)) return true; + } + return false; } return type.equalsToText("java.lang.String"); } private static boolean hasStringType(PsiExpression expression) { - if (expression == null) { - return false; - } final PsiType type = expression.getType(); return type != null && type.equalsToText("java.lang.String"); } diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/concatenation/string_format/HexadecimalLiteral_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/concatenation/string_format/HexadecimalLiteral_after.java index 897397e05dfc..a5cadb01651b 100644 --- a/plugins/IntentionPowerPak/test/com/siyeh/ipp/concatenation/string_format/HexadecimalLiteral_after.java +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/concatenation/string_format/HexadecimalLiteral_after.java @@ -1,3 +1,3 @@ class C { - String s = String.format("or not16"); + String s = String.format("or not%d", 0x10); } \ No newline at end of file diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/introduceParameter/IntroduceParameterTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/introduceParameter/IntroduceParameterTest.java index 2a5f58f23970..870ca0eaa75b 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/introduceParameter/IntroduceParameterTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/introduceParameter/IntroduceParameterTest.java @@ -16,15 +16,16 @@ package org.jetbrains.plugins.groovy.refactoring.introduceParameter; -import com.intellij.codeInsight.CodeInsightUtil; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pass; import com.intellij.psi.*; import com.intellij.psi.impl.source.PostprocessReformattingAspect; -import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.refactoring.HelpID; import com.intellij.refactoring.IntroduceParameterRefactoring; +import com.intellij.refactoring.introduceField.ElementToWorkOn; import com.intellij.refactoring.introduceParameter.IntroduceParameterProcessor; import com.intellij.refactoring.introduceParameter.Util; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; @@ -71,19 +72,22 @@ public class IntroduceParameterTest extends LightCodeInsightFixtureTestCase { int startOffset = editor.getSelectionModel().getSelectionStart(); int endOffset = editor.getSelectionModel().getSelectionEnd(); - final PsiFile file = myFixture.getFile(); - PsiExpression expr = CodeInsightUtil.findExpressionInRange(file, startOffset, endOffset); + final PsiFile myFile = myFixture.getFile(); + final ElementToWorkOn[] elementToWorkOn = new ElementToWorkOn[1]; + ElementToWorkOn + .processElementToWorkOn(editor, myFile, "INtr param", HelpID.INTRODUCE_PARAMETER, getProject(), new Pass() { + @Override + public void pass(final ElementToWorkOn e) { + if (e == null) return; - PsiLocalVariable localVariable = null; - if (expr == null) { - PsiElement element = CodeInsightUtil.findElementInRange(file, startOffset, endOffset, PsiElement.class); - localVariable = PsiTreeUtil.getParentOfType(element, PsiLocalVariable.class); - if (localVariable == null) { - return false; - } - } + elementToWorkOn[0] = e; + } + }); - PsiElement context = expr == null ? localVariable : expr; + final PsiExpression expr = elementToWorkOn[0].getExpression(); + final PsiLocalVariable localVar = elementToWorkOn[0].getLocalVariable(); + + PsiElement context = expr == null ? localVar : expr; PsiMethod method = Util.getContainingMethod(context); if (method == null) return false; @@ -96,11 +100,11 @@ public class IntroduceParameterTest extends LightCodeInsightFixtureTestCase { methodToSearchFor = method; } - PsiExpression initializer = (expr == null) ? localVariable.getInitializer() : expr; + PsiExpression initializer = expr == null ? localVar.getInitializer() : expr; TIntArrayList parametersToRemove = removeUnusedParameters ? Util.findParametersToRemove(method, initializer, null) : new TIntArrayList(); final Project project = myFixture.getProject(); final IntroduceParameterProcessor processor = - new IntroduceParameterProcessor(project, method, methodToSearchFor, initializer, expr, localVariable, true, parameterName, + new IntroduceParameterProcessor(project, method, methodToSearchFor, initializer, expr, localVar, true, parameterName, replaceAllOccurences, replaceFieldsWithGetters, declareFinal, generateDelegate, null, parametersToRemove); diff --git a/plugins/java-i18n/src/com/intellij/codeInspection/i18n/I18nizeConcatenationQuickFix.java b/plugins/java-i18n/src/com/intellij/codeInspection/i18n/I18nizeConcatenationQuickFix.java index 7528eca7cba4..0bd365da2f3f 100644 --- a/plugins/java-i18n/src/com/intellij/codeInspection/i18n/I18nizeConcatenationQuickFix.java +++ b/plugins/java-i18n/src/com/intellij/codeInspection/i18n/I18nizeConcatenationQuickFix.java @@ -39,14 +39,14 @@ public class I18nizeConcatenationQuickFix extends I18nizeQuickFix{ @NonNls private static final String PARAMETERS_OPTION_KEY = "PARAMETERS"; public void checkApplicability(final PsiFile psiFile, final Editor editor) throws IncorrectOperationException { - PsiBinaryExpression concatenation = getEnclosingLiteralConcatenation(psiFile, editor); + PsiPolyadicExpression concatenation = getEnclosingLiteralConcatenation(psiFile, editor); if (concatenation != null) return; String message = CodeInsightBundle.message("quickfix.i18n.concatentation.error"); throw new IncorrectOperationException(message); } public JavaI18nizeQuickFixDialog createDialog(Project project, Editor editor, PsiFile psiFile) { - PsiBinaryExpression concatenation = getEnclosingLiteralConcatenation(psiFile, editor); + PsiPolyadicExpression concatenation = getEnclosingLiteralConcatenation(psiFile, editor); PsiLiteralExpression literalExpression = getContainingLiteral(concatenation); if (literalExpression == null) return null; return createDialog(project, psiFile, literalExpression); @@ -61,7 +61,7 @@ public class I18nizeConcatenationQuickFix extends I18nizeQuickFix{ @NotNull final Editor editor, @Nullable PsiLiteralExpression literalExpression, String i18nizedText) throws IncorrectOperationException { - PsiBinaryExpression concatenation = getEnclosingLiteralConcatenation(psiFile, editor); + PsiPolyadicExpression concatenation = getEnclosingLiteralConcatenation(psiFile, editor); PsiExpression expression = JavaPsiFacade.getInstance(psiFile.getProject()).getElementFactory().createExpressionFromText(i18nizedText, concatenation); return concatenation.replace(expression); } @@ -79,7 +79,7 @@ public class I18nizeConcatenationQuickFix extends I18nizeQuickFix{ } protected JavaI18nizeQuickFixDialog createDialog(final Project project, final PsiFile context, final PsiLiteralExpression literalExpression) { - PsiBinaryExpression concatenation = getEnclosingLiteralConcatenation(literalExpression); + PsiPolyadicExpression concatenation = getEnclosingLiteralConcatenation(literalExpression); StringBuilder formatString = new StringBuilder(); final List args = new ArrayList(); try { @@ -111,45 +111,39 @@ public class I18nizeConcatenationQuickFix extends I18nizeQuickFix{ } @Nullable - private static PsiBinaryExpression getEnclosingLiteralConcatenation(@NotNull PsiFile file, @NotNull Editor editor) { + private static PsiPolyadicExpression getEnclosingLiteralConcatenation(@NotNull PsiFile file, @NotNull Editor editor) { final PsiElement elementAt = file.findElementAt(editor.getCaretModel().getOffset()); return getEnclosingLiteralConcatenation(elementAt); } @Nullable - public static PsiBinaryExpression getEnclosingLiteralConcatenation(final PsiElement psiElement) { - PsiBinaryExpression element = PsiTreeUtil.getParentOfType(psiElement, PsiBinaryExpression.class, false, PsiMember.class); + public static PsiPolyadicExpression getEnclosingLiteralConcatenation(final PsiElement psiElement) { + PsiPolyadicExpression element = PsiTreeUtil.getParentOfType(psiElement, PsiPolyadicExpression.class, false, PsiMember.class); if (element == null) return null; - PsiBinaryExpression concatenation = null; + + PsiPolyadicExpression concatenation = null; boolean stringLiteralOccured = false; while (true) { - PsiExpression lOperand = element.getLOperand(); - PsiExpression rOperand = element.getROperand(); if (element.getOperationTokenType() != JavaTokenType.PLUS) return concatenation; - stringLiteralOccured |= lOperand instanceof PsiLiteralExpression && ((PsiLiteralExpression)lOperand).getValue() instanceof String || - rOperand instanceof PsiLiteralExpression && ((PsiLiteralExpression)rOperand).getValue() instanceof String; + for (PsiExpression operand : element.getOperands()) { + stringLiteralOccured |= operand instanceof PsiLiteralExpression && ((PsiLiteralExpression)operand).getValue() instanceof String; + } if (stringLiteralOccured) { concatenation = element; } PsiElement parent = element.getParent(); - if (!(parent instanceof PsiBinaryExpression)) return concatenation; - element = (PsiBinaryExpression) parent; + if (!(parent instanceof PsiPolyadicExpression)) return concatenation; + element = (PsiPolyadicExpression) parent; } } - private static PsiLiteralExpression getContainingLiteral(final PsiBinaryExpression concatenation) { - PsiExpression operand = concatenation.getLOperand(); - PsiLiteralExpression literalExpression = null; - if (operand instanceof PsiLiteralExpression) { - literalExpression = (PsiLiteralExpression)operand; - } - else { - operand = concatenation.getROperand(); + private static PsiLiteralExpression getContainingLiteral(final PsiPolyadicExpression concatenation) { + for (PsiExpression operand : concatenation.getOperands()) { if (operand instanceof PsiLiteralExpression) { - literalExpression = (PsiLiteralExpression)operand; + return (PsiLiteralExpression)operand; } } - return literalExpression; + return null; } }