Search in sources :

Example 26 with BinaryExpression

use of com.jetbrains.php.lang.psi.elements.BinaryExpression in project phpinspectionsea by kalessil.

the class TernaryOperatorSimplifyInspector method buildVisitor.

@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
    return new BasePhpElementVisitor() {

        @Override
        public void visitPhpTernaryExpression(@NotNull TernaryExpression expression) {
            final PsiElement rawCondition = expression.getCondition();
            if (rawCondition != null) {
                final PsiElement condition = ExpressionSemanticUtil.getExpressionTroughParenthesis(rawCondition);
                if (condition instanceof BinaryExpression) {
                    final PsiElement trueVariant = ExpressionSemanticUtil.getExpressionTroughParenthesis(expression.getTrueVariant());
                    if (trueVariant != null) {
                        final PsiElement falseVariant = ExpressionSemanticUtil.getExpressionTroughParenthesis(expression.getFalseVariant());
                        if (falseVariant != null) {
                            final boolean isTarget = PhpLanguageUtil.isBoolean(trueVariant) && PhpLanguageUtil.isBoolean(falseVariant);
                            if (isTarget) {
                                final String replacement = this.generateReplacement((BinaryExpression) condition, trueVariant);
                                if (replacement != null) {
                                    holder.registerProblem(expression, MessagesPresentationUtil.prefixWithEa(String.format(messagePattern, replacement)), new SimplifyFix(replacement));
                                }
                            }
                        }
                    }
                }
            }
        }

        @Nullable
        private String generateReplacement(@NotNull BinaryExpression binary, @NotNull PsiElement trueVariant) {
            final IElementType operator = binary.getOperationType();
            if (null == operator) {
                return null;
            }
            final String replacement;
            final boolean useParentheses = !oppositeOperators.containsKey(operator);
            final boolean isInverted = PhpLanguageUtil.isFalse(trueVariant);
            if (useParentheses) {
                final boolean isLogical = PhpTokenTypes.opAND == operator || PhpTokenTypes.opOR == operator;
                final String boolCasting = isLogical ? "" : "(bool)";
                replacement = ((isInverted ? "!" : boolCasting) + "(%e%)").replace("%e%", binary.getText());
            } else {
                if (isInverted) {
                    final PsiElement left = binary.getLeftOperand();
                    final PsiElement right = binary.getRightOperand();
                    if (null == left || null == right) {
                        return null;
                    }
                    replacement = "%l% %o% %r%".replace("%r%", right.getText()).replace("%o%", oppositeOperators.get(operator)).replace("%l%", left.getText());
                } else {
                    replacement = binary.getText();
                }
            }
            return replacement;
        }
    };
}
Also used : IElementType(com.intellij.psi.tree.IElementType) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) TernaryExpression(com.jetbrains.php.lang.psi.elements.TernaryExpression) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 27 with BinaryExpression

use of com.jetbrains.php.lang.psi.elements.BinaryExpression in project phpinspectionsea by kalessil.

the class TypeUnsafeComparisonInspector method buildVisitor.

@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
    return new BasePhpElementVisitor() {

        @Override
        public void visitPhpBinaryExpression(@NotNull BinaryExpression expression) {
            final IElementType operator = expression.getOperationType();
            if (operator == PhpTokenTypes.opEQUAL || operator == PhpTokenTypes.opNOT_EQUAL) {
                this.analyze(expression, operator);
            }
        }

        private void analyze(@NotNull final BinaryExpression subject, @NotNull final IElementType operator) {
            final String targetOperator = PhpTokenTypes.opEQUAL == operator ? "===" : "!==";
            final PsiElement left = subject.getLeftOperand();
            final PsiElement right = subject.getRightOperand();
            if (right instanceof StringLiteralExpression || left instanceof StringLiteralExpression) {
                final PsiElement nonStringOperand;
                final String literalValue;
                if (right instanceof StringLiteralExpression) {
                    literalValue = ((StringLiteralExpression) right).getContents();
                    nonStringOperand = ExpressionSemanticUtil.getExpressionTroughParenthesis(left);
                } else {
                    literalValue = ((StringLiteralExpression) left).getContents();
                    nonStringOperand = ExpressionSemanticUtil.getExpressionTroughParenthesis(right);
                }
                /* resolve 2nd operand type, if class ensure __toString is implemented */
                if (ClassInStringContextStrategy.apply(nonStringOperand, holder, subject, messageToStringMethodMissing)) {
                    /* TODO: weak warning regarding under-the-hood string casting */
                    return;
                }
                /* string literal is numeric or empty, no strict compare possible */
                if (!literalValue.isEmpty() && !literalValue.matches("^[+-]?[0-9]*\\.?[0-9]+$")) {
                    holder.registerProblem(subject, String.format(MessagesPresentationUtil.prefixWithEa(patternCompareStrict), targetOperator), new CompareStrictFix(targetOperator));
                    return;
                }
            }
            /* some objects supporting direct comparison: search for .compare_objects in PHP sources */
            if (left != null && right != null) {
                final boolean isComparableObject = this.isComparableObject(left) || this.isComparableObject(right);
                if (!isComparableObject) {
                    holder.registerProblem(subject, String.format(MessagesPresentationUtil.prefixWithEa(patternHarden), targetOperator), ProblemHighlightType.WEAK_WARNING);
                }
            }
        }

        private boolean isComparableObject(@NotNull PsiElement operand) {
            if (operand instanceof PhpTypedElement) {
                final Project project = holder.getProject();
                final PhpType resolved = OpenapiResolveUtil.resolveType((PhpTypedElement) operand, project);
                if (resolved != null) {
                    final PhpIndex index = PhpIndex.getInstance(project);
                    final Set<PhpClass> classes = new HashSet<>();
                    resolved.filterUnknown().getTypes().stream().filter(t -> t.charAt(0) == '\\').forEach(t -> classes.addAll(OpenapiResolveUtil.resolveClassesAndInterfacesByFQN(Types.getType(t), index)));
                    for (final PhpClass clazz : classes) {
                        final boolean hasAny = comparable.contains(clazz.getFQN()) || InterfacesExtractUtil.getCrawlInheritanceTree(clazz, true).stream().anyMatch(c -> comparable.contains(c.getFQN()));
                        if (hasAny) {
                            classes.clear();
                            return true;
                        }
                    }
                    classes.clear();
                }
            }
            return false;
        }
    };
}
Also used : IElementType(com.intellij.psi.tree.IElementType) PhpTokenTypes(com.jetbrains.php.lang.lexer.PhpTokenTypes) PhpIndex(com.jetbrains.php.PhpIndex) PhpTypedElement(com.jetbrains.php.lang.psi.elements.PhpTypedElement) HashSet(java.util.HashSet) ClassInStringContextStrategy(com.kalessil.phpStorm.phpInspectionsEA.utils.strategy.ClassInStringContextStrategy) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) ProblemDescriptor(com.intellij.codeInspection.ProblemDescriptor) MessagesPresentationUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil) PsiElement(com.intellij.psi.PsiElement) Project(com.intellij.openapi.project.Project) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) LocalQuickFix(com.intellij.codeInspection.LocalQuickFix) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) PhpPsiElementFactory(com.jetbrains.php.lang.psi.PhpPsiElementFactory) PhpClass(com.jetbrains.php.lang.psi.elements.PhpClass) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) ExpressionSemanticUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.ExpressionSemanticUtil) Types(com.kalessil.phpStorm.phpInspectionsEA.utils.Types) InterfacesExtractUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.hierarhy.InterfacesExtractUtil) LeafPsiElement(com.intellij.psi.impl.source.tree.LeafPsiElement) Set(java.util.Set) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) OpenapiResolveUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiResolveUtil) ProblemHighlightType(com.intellij.codeInspection.ProblemHighlightType) StringLiteralExpression(com.jetbrains.php.lang.psi.elements.StringLiteralExpression) NotNull(org.jetbrains.annotations.NotNull) StringLiteralExpression(com.jetbrains.php.lang.psi.elements.StringLiteralExpression) PhpIndex(com.jetbrains.php.PhpIndex) PhpClass(com.jetbrains.php.lang.psi.elements.PhpClass) NotNull(org.jetbrains.annotations.NotNull) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) IElementType(com.intellij.psi.tree.IElementType) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) Project(com.intellij.openapi.project.Project) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) PhpTypedElement(com.jetbrains.php.lang.psi.elements.PhpTypedElement) PsiElement(com.intellij.psi.PsiElement) LeafPsiElement(com.intellij.psi.impl.source.tree.LeafPsiElement) HashSet(java.util.HashSet) NotNull(org.jetbrains.annotations.NotNull)

Example 28 with BinaryExpression

use of com.jetbrains.php.lang.psi.elements.BinaryExpression in project phpinspectionsea by kalessil.

the class NestedNotOperatorsInspector method buildVisitor.

@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
    return new BasePhpElementVisitor() {

        @Override
        public void visitPhpUnaryExpression(@NotNull UnaryExpression expression) {
            /* process ony not operations */
            if (!OpenapiTypesUtil.is(expression.getOperation(), PhpTokenTypes.opNOT)) {
                return;
            }
            /* process only deepest not-operator: get contained expression */
            final PsiElement value = ExpressionSemanticUtil.getExpressionTroughParenthesis(expression.getValue());
            if (null == value) {
                return;
            }
            /* if contained expression is also inversion, do nothing -> to not report several times */
            if (value instanceof UnaryExpression) {
                if (OpenapiTypesUtil.is(((UnaryExpression) value).getOperation(), PhpTokenTypes.opNOT)) {
                    return;
                }
            }
            /* check nesting level */
            PsiElement target = null;
            int nestingLevel = 1;
            PsiElement parent = expression.getParent();
            while (parent instanceof UnaryExpression || parent instanceof ParenthesizedExpression) {
                if (!(parent instanceof ParenthesizedExpression)) {
                    expression = (UnaryExpression) parent;
                    if (OpenapiTypesUtil.is(expression.getOperation(), PhpTokenTypes.opNOT)) {
                        ++nestingLevel;
                        target = parent;
                    }
                }
                parent = parent.getParent();
            }
            if (nestingLevel > 1) {
                final String subject = String.format(value instanceof BinaryExpression ? "(%s)" : "%s", value.getText());
                final String replacement = String.format(nestingLevel % 2 == 0 ? "(bool) %s" : "! %s", subject);
                holder.registerProblem(target, MessagesPresentationUtil.prefixWithEa(String.format(messagePattern, replacement)), nestingLevel % 2 == 0 ? new UseCastingLocalFix(replacement) : new UseSingleNotLocalFix(replacement));
            }
        }
    };
}
Also used : ParenthesizedExpression(com.jetbrains.php.lang.psi.elements.ParenthesizedExpression) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) UnaryExpression(com.jetbrains.php.lang.psi.elements.UnaryExpression) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 29 with BinaryExpression

use of com.jetbrains.php.lang.psi.elements.BinaryExpression in project phpinspectionsea by kalessil.

the class SubStrUsedAsStrPosInspector method buildVisitor.

@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
    return new BasePhpElementVisitor() {

        /* dropped pattern: '$string[0] === '?'' -> 'substr(...) === 0' */
        @Override
        public void visitPhpFunctionCall(@NotNull FunctionReference reference) {
            final String functionName = reference.getName();
            if (functionName != null && substringFunctions.contains(functionName)) {
                final PsiElement[] arguments = reference.getParameters();
                if (arguments.length == 3 || arguments.length == 4) {
                    /* checking 2nd and 3rd arguments is not needed/simplified:
                         *   - 2nd re-used as it is (should be a positive number!)
                         *   - 3rd is not important, as we'll rely on parent comparison operand instead
                         */
                    final String index = arguments[1].getText();
                    if (OpenapiTypesUtil.isNumber(arguments[1]) && index.equals("0")) {
                        if (!OpenapiTypesUtil.isFunctionReference(arguments[2]) || lengthFunctions.contains(((FunctionReference) arguments[2]).getName())) {
                            /* prepare variables, so we could properly process polymorphic pattern */
                            PsiElement highLevelCall = reference;
                            PsiElement parentExpression = reference.getParent();
                            if (parentExpression instanceof ParameterList) {
                                parentExpression = parentExpression.getParent();
                            }
                            /* if the call wrapped with case manipulation, propose to use stripos */
                            boolean caseManipulated = false;
                            if (OpenapiTypesUtil.isFunctionReference(parentExpression)) {
                                final FunctionReference parentCall = (FunctionReference) parentExpression;
                                final PsiElement[] parentArguments = parentCall.getParameters();
                                final String parentName = parentCall.getName();
                                if (parentName != null && parentArguments.length == 1 && outerFunctions.contains(parentName)) {
                                    caseManipulated = true;
                                    highLevelCall = parentExpression;
                                    parentExpression = parentExpression.getParent();
                                }
                            }
                            /* check parent expression, to ensure pattern matched */
                            if (parentExpression instanceof BinaryExpression) {
                                final BinaryExpression parent = (BinaryExpression) parentExpression;
                                if (OpenapiTypesUtil.tsCOMPARE_EQUALITY_OPS.contains(parent.getOperationType())) {
                                    final PsiElement secondOperand = OpenapiElementsUtil.getSecondOperand(parent, highLevelCall);
                                    final PsiElement operationNode = parent.getOperation();
                                    if (secondOperand != null && operationNode != null) {
                                        final String operator = operationNode.getText();
                                        final boolean isMbFunction = functionName.equals("mb_substr");
                                        final boolean hasEncoding = isMbFunction && arguments.length == 4;
                                        final String call = String.format("%s%s(%s, %s%s)", reference.getImmediateNamespaceName(), (isMbFunction ? "mb_" : "") + (caseManipulated ? "stripos" : "strpos"), arguments[0].getText(), secondOperand.getText(), hasEncoding ? (", " + arguments[3].getText()) : "");
                                        final boolean isRegular = ComparisonStyle.isRegular();
                                        final String replacement = String.format("%s %s %s", isRegular ? call : index, operator.length() == 2 ? (operator + '=') : operator, isRegular ? index : call);
                                        holder.registerProblem(parentExpression, String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), replacement), new UseStringSearchFix(replacement));
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) FunctionReference(com.jetbrains.php.lang.psi.elements.FunctionReference) ParameterList(com.jetbrains.php.lang.psi.elements.ParameterList) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 30 with BinaryExpression

use of com.jetbrains.php.lang.psi.elements.BinaryExpression in project phpinspectionsea by kalessil.

the class StrStartsWithCanBeUsedInspector method buildVisitor.

@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) {
    return new BasePhpElementVisitor() {

        @Override
        public void visitPhpFunctionCall(@NotNull FunctionReference reference) {
            final String functionName = reference.getName();
            if (functionName != null && (functionName.equals("strpos") || functionName.equals("mb_strpos"))) {
                final boolean isTargetVersion = PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP800);
                if (isTargetVersion) {
                    final PsiElement[] arguments = reference.getParameters();
                    if (arguments.length == 2) {
                        final PsiElement context = reference.getParent();
                        if (context instanceof BinaryExpression) {
                            final BinaryExpression binary = (BinaryExpression) context;
                            final IElementType operation = binary.getOperationType();
                            if (operation == PhpTokenTypes.opNOT_IDENTICAL || operation == PhpTokenTypes.opIDENTICAL) {
                                final PsiElement second = OpenapiElementsUtil.getSecondOperand(binary, reference);
                                if (second != null && OpenapiTypesUtil.isNumber(second) && second.getText().equals("0")) {
                                    final String replacement = String.format("%s%sstr_starts_with(%s, %s)", operation == PhpTokenTypes.opNOT_IDENTICAL ? "! " : "", reference.getImmediateNamespaceName(), arguments[0].getText(), arguments[1].getText());
                                    holder.registerProblem(binary, String.format(MessagesPresentationUtil.prefixWithEa(message), replacement), new UseStrStartsWithFix(replacement));
                                }
                            }
                        }
                    }
                }
            }
        }
    };
}
Also used : IElementType(com.intellij.psi.tree.IElementType) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) FunctionReference(com.jetbrains.php.lang.psi.elements.FunctionReference) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Aggregations

BinaryExpression (com.jetbrains.php.lang.psi.elements.BinaryExpression)35 PsiElement (com.intellij.psi.PsiElement)34 IElementType (com.intellij.psi.tree.IElementType)26 BasePhpElementVisitor (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor)25 NotNull (org.jetbrains.annotations.NotNull)25 FunctionReference (com.jetbrains.php.lang.psi.elements.FunctionReference)16 UnaryExpression (com.jetbrains.php.lang.psi.elements.UnaryExpression)9 ParenthesizedExpression (com.jetbrains.php.lang.psi.elements.ParenthesizedExpression)6 PhpType (com.jetbrains.php.lang.psi.resolve.types.PhpType)5 Nullable (org.jetbrains.annotations.Nullable)5 PhpTypedElement (com.jetbrains.php.lang.psi.elements.PhpTypedElement)4 StringLiteralExpression (com.jetbrains.php.lang.psi.elements.StringLiteralExpression)4 ProblemsHolder (com.intellij.codeInspection.ProblemsHolder)3 PsiElementVisitor (com.intellij.psi.PsiElementVisitor)3 PhpTokenTypes (com.jetbrains.php.lang.lexer.PhpTokenTypes)3 TernaryExpression (com.jetbrains.php.lang.psi.elements.TernaryExpression)3 BasePhpInspection (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection)3 ArrayList (java.util.ArrayList)3 HashSet (java.util.HashSet)3 Objects (java.util.Objects)3