Search in sources :

Example 41 with FunctionReference

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

the class StringCaseManipulationInspector method buildVisitor.

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

        @Override
        public void visitPhpFunctionCall(@NotNull FunctionReference reference) {
            final String functionName = reference.getName();
            if (functionName != null && functions.containsKey(functionName)) {
                final PsiElement[] arguments = reference.getParameters();
                if (arguments.length == 2) {
                    final PsiElement first = this.getSubject(arguments[0]);
                    final PsiElement second = this.getSubject(arguments[1]);
                    if (first != null || second != null) {
                        final String replacement = "%f%(%a1%, %a2%)".replace("%a2%", (second == null ? arguments[1] : second).getText()).replace("%a1%", (first == null ? arguments[0] : first).getText()).replace("%f%", functions.get(functionName));
                        holder.registerProblem(reference, String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), replacement), new SimplifyFix(replacement));
                    }
                }
            }
        }

        @Nullable
        private PsiElement getSubject(@NotNull PsiElement expression) {
            PsiElement result = null;
            if (OpenapiTypesUtil.isFunctionReference(expression)) {
                final FunctionReference reference = (FunctionReference) expression;
                final String functionName = reference.getName();
                if (functionName != null && innerFunctions.contains(functionName)) {
                    final PsiElement[] arguments = reference.getParameters();
                    if (arguments.length == 1) {
                        result = arguments[0];
                    }
                }
            }
            return result;
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) FunctionReference(com.jetbrains.php.lang.psi.elements.FunctionReference) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 42 with FunctionReference

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

the class JsonEncodingApiUsageInspector method buildVisitor.

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

        @Override
        public void visitPhpFunctionCall(@NotNull FunctionReference reference) {
            final String functionName = reference.getName();
            if (functionName != null) {
                if (functionName.equals("json_decode") && this.isFromRootNamespace(reference)) {
                    final PsiElement[] arguments = reference.getParameters();
                    if (HARDEN_DECODING_RESULT_TYPE && arguments.length == 1) {
                        final String replacement = String.format("%sjson_decode(%s, %s)", reference.getImmediateNamespaceName(), arguments[0].getText(), DECODE_AS_ARRAY ? "true" : "false");
                        holder.registerProblem(reference, MessagesPresentationUtil.prefixWithEa(messageResultType), DECODE_AS_ARRAY ? new DecodeIntoArrayFix(replacement) : new DecodeIntoObjectFix(replacement));
                    }
                    if (HARDEN_ERRORS_HANDLING && arguments.length > 0 && PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP730)) {
                        final boolean hasFlag = arguments.length >= 4 && this.hasStricterHandlingFlags(arguments[3]);
                        if (!hasFlag) {
                            final String replacement = String.format("%sjson_decode(%s, %s, %s, %s)", reference.getImmediateNamespaceName(), arguments[0].getText(), arguments.length > 1 ? arguments[1].getText() : (HARDEN_DECODING_RESULT_TYPE && DECODE_AS_ARRAY ? "true" : "false"), arguments.length > 2 ? arguments[2].getText() : "512", arguments.length > 3 ? "JSON_THROW_ON_ERROR | " + arguments[3].getText() : "JSON_THROW_ON_ERROR");
                            holder.registerProblem(reference, MessagesPresentationUtil.prefixWithEa(messageErrorsHandling), new HardenErrorsHandlingFix(replacement));
                        }
                    }
                } else if (functionName.equals("json_encode") && this.isFromRootNamespace(reference)) {
                    if (HARDEN_ERRORS_HANDLING && PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP730)) {
                        final PsiElement[] arguments = reference.getParameters();
                        final boolean hasFlag = arguments.length >= 2 && this.hasStricterHandlingFlags(arguments[1]);
                        if (!hasFlag && arguments.length > 0) {
                            final String replacement;
                            if (arguments.length > 2) {
                                replacement = String.format("%sjson_encode(%s, %s, %s)", reference.getImmediateNamespaceName(), arguments[0].getText(), "JSON_THROW_ON_ERROR | " + arguments[1].getText(), arguments[2].getText());
                            } else {
                                replacement = String.format("%sjson_encode(%s, %s)", reference.getImmediateNamespaceName(), arguments[0].getText(), arguments.length > 1 ? "JSON_THROW_ON_ERROR | " + arguments[1].getText() : "JSON_THROW_ON_ERROR");
                            }
                            holder.registerProblem(reference, MessagesPresentationUtil.prefixWithEa(messageErrorsHandling), new HardenErrorsHandlingFix(replacement));
                        }
                    }
                }
            }
        }

        private boolean hasStricterHandlingFlags(@NotNull PsiElement argument) {
            boolean hasFlag = false;
            final Set<PsiElement> options = argument instanceof ConstantReference ? new HashSet<>(Collections.singletonList(argument)) : PossibleValuesDiscoveryUtil.discover(argument);
            if (options.size() == 1) {
                final PsiElement option = options.iterator().next();
                if (OpenapiTypesUtil.isNumber(option)) {
                    /* properly resolved value */
                    hasFlag = strictHandlingFlags.containsValue(option.getText());
                } else if (option instanceof ConstantReference) {
                    /* constant value resolution fails for some reason */
                    hasFlag = strictHandlingFlags.containsKey(((ConstantReference) option).getName());
                } else {
                    /* a complex case like local variable or implicit flags combination */
                    hasFlag = PsiTreeUtil.findChildrenOfType(option, ConstantReference.class).stream().anyMatch(r -> strictHandlingFlags.containsKey(r.getName()));
                }
            }
            options.clear();
            return hasFlag;
        }
    };
}
Also used : ConstantReference(com.jetbrains.php.lang.psi.elements.ConstantReference) NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) FunctionReference(com.jetbrains.php.lang.psi.elements.FunctionReference) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 43 with FunctionReference

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

the class StringNormalizationInspector method buildVisitor.

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

        @Override
        public void visitPhpFunctionCall(@NotNull FunctionReference reference) {
            final String functionName = reference.getName();
            if (functionName != null) {
                final PsiElement[] arguments = reference.getParameters();
                if (arguments.length > 0 && OpenapiTypesUtil.isFunctionReference(arguments[0])) {
                    final FunctionReference innerCall = (FunctionReference) arguments[0];
                    final String innerCallName = innerCall.getName();
                    if (innerCallName != null) {
                        final PsiElement[] innerArguments = innerCall.getParameters();
                        if (innerArguments.length > 0) {
                            if (lengthManipulation.contains(functionName) && caseManipulation.contains(innerCallName)) {
                                final boolean isTarget = !functionName.endsWith("trim") || arguments.length == 1 || (arguments[1] instanceof StringLiteralExpression && !regexTrimmedCharacters.matcher(arguments[1].getText()).matches());
                                if (isTarget) {
                                    final String theString = innerArguments[0].getText();
                                    final String newInnerCall = reference.getText().replace(arguments[0].getText(), theString);
                                    final String replacement = innerCall.getText().replace(theString, newInnerCall);
                                    holder.registerProblem(reference, MessagesPresentationUtil.prefixWithEa(String.format(patternInvertedNesting, replacement)), new NormalizationFix(replacement));
                                }
                            } else if (caseManipulation.contains(functionName) && caseManipulation.contains(innerCallName)) {
                                if (functionName.equals(innerCallName)) {
                                    holder.registerProblem(innerCall, MessagesPresentationUtil.prefixWithEa(String.format(patternSenselessNesting, innerCallName)), new NormalizationFix(innerArguments[0].getText()));
                                } else if (!innerCaseManipulation.contains(innerCallName)) {
                                    /* false-positives: ucwords with 2 arguments */
                                    final boolean isTarget = !innerCallName.equals("ucwords") || innerArguments.length == 1;
                                    if (isTarget) {
                                        holder.registerProblem(innerCall, MessagesPresentationUtil.prefixWithEa(String.format(patternSenselessNesting, innerCallName)), new NormalizationFix(innerArguments[0].getText()));
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) StringLiteralExpression(com.jetbrains.php.lang.psi.elements.StringLiteralExpression) FunctionReference(com.jetbrains.php.lang.psi.elements.FunctionReference) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 44 with FunctionReference

use of com.jetbrains.php.lang.psi.elements.FunctionReference 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 45 with FunctionReference

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

the class ArrayUniqueCanBeUsedInspector method buildVisitor.

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

        @Override
        public void visitPhpFunctionCall(@NotNull FunctionReference reference) {
            if (PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP720)) {
                final String functionName = reference.getName();
                if (functionName != null && functionName.equals("array_count_values")) {
                    final PsiElement[] arguments = reference.getParameters();
                    if (arguments.length == 1) {
                        final PsiElement context = reference.getParent().getParent();
                        if (OpenapiTypesUtil.isFunctionReference(context)) {
                            final String parentFunctionName = ((FunctionReference) context).getName();
                            if (parentFunctionName != null) {
                                if (parentFunctionName.equals("array_keys")) {
                                    final String replacement = "array_values(array_unique(%a%))".replace("%a%", arguments[0].getText());
                                    holder.registerProblem(context, MessagesPresentationUtil.prefixWithEa(messagePattern.replace("%e%", replacement)), new ReplaceFix(replacement));
                                } else if (parentFunctionName.equals("count")) {
                                    final String replacement = "count(array_unique(%a%))".replace("%a%", arguments[0].getText());
                                    holder.registerProblem(context, MessagesPresentationUtil.prefixWithEa(messagePattern.replace("%e%", replacement)), new ReplaceFix(replacement));
                                }
                            }
                        }
                    }
                }
            }
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) FunctionReference(com.jetbrains.php.lang.psi.elements.FunctionReference) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Aggregations

FunctionReference (com.jetbrains.php.lang.psi.elements.FunctionReference)58 PsiElement (com.intellij.psi.PsiElement)55 NotNull (org.jetbrains.annotations.NotNull)46 BasePhpElementVisitor (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor)43 BinaryExpression (com.jetbrains.php.lang.psi.elements.BinaryExpression)16 StringLiteralExpression (com.jetbrains.php.lang.psi.elements.StringLiteralExpression)15 IElementType (com.intellij.psi.tree.IElementType)11 PhpUnitAssertFixer (com.kalessil.phpStorm.phpInspectionsEA.fixers.PhpUnitAssertFixer)9 ArrayCreationExpression (com.jetbrains.php.lang.psi.elements.ArrayCreationExpression)6 UnaryExpression (com.jetbrains.php.lang.psi.elements.UnaryExpression)6 Function (com.jetbrains.php.lang.psi.elements.Function)5 MethodReference (com.jetbrains.php.lang.psi.elements.MethodReference)5 ParameterList (com.jetbrains.php.lang.psi.elements.ParameterList)5 PhpLanguageLevel (com.jetbrains.php.config.PhpLanguageLevel)4 ArrayList (java.util.ArrayList)4 Nullable (org.jetbrains.annotations.Nullable)4 ProblemsHolder (com.intellij.codeInspection.ProblemsHolder)3 PsiWhiteSpace (com.intellij.psi.PsiWhiteSpace)3 ArrayAccessExpression (com.jetbrains.php.lang.psi.elements.ArrayAccessExpression)3 HashSet (java.util.HashSet)3