Search in sources :

Example 11 with BasePhpElementVisitor

use of com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor in project phpinspectionsea by kalessil.

the class RandomApiMigrationInspector 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 PhpLanguageLevel php = PhpProjectConfigurationFacade.getInstance(holder.getProject()).getLanguageLevel();
                String suggestion = getMapping(php).get(functionName);
                if (suggestion != null) {
                    /* random_int needs 2 parameters always, so check if mt_rand can be suggested */
                    if (reference.getParameters().length != 2 && suggestion.equals("random_int")) {
                        if (functionName.equals("rand")) {
                            suggestion = "mt_rand";
                        } else {
                            return;
                        }
                    }
                    final String message = messagePattern.replace("%o%", functionName).replace("%n%", suggestion);
                    holder.registerProblem(reference, message, new TheLocalFix(suggestion));
                }
            }
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) FunctionReference(com.jetbrains.php.lang.psi.elements.FunctionReference) NotNull(org.jetbrains.annotations.NotNull) PhpLanguageLevel(com.jetbrains.php.config.PhpLanguageLevel) NotNull(org.jetbrains.annotations.NotNull)

Example 12 with BasePhpElementVisitor

use of com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor in project phpinspectionsea by kalessil.

the class CascadeStringReplacementInspector method buildVisitor.

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

        @Override
        public void visitPhpReturn(@NotNull PhpReturn returnStatement) {
            final FunctionReference functionCall = this.getFunctionReference(returnStatement);
            if (functionCall != null) {
                this.analyze(functionCall, returnStatement);
            }
        }

        @Override
        public void visitPhpAssignmentExpression(@NotNull AssignmentExpression assignmentExpression) {
            final FunctionReference functionCall = this.getFunctionReference(assignmentExpression);
            if (functionCall != null) {
                this.analyze(functionCall, assignmentExpression);
            }
        }

        private void analyze(@NotNull FunctionReference functionCall, @NotNull PsiElement expression) {
            final PsiElement[] arguments = functionCall.getParameters();
            if (arguments.length == 3) {
                /* case: cascading replacements */
                final AssignmentExpression previous = this.getPreviousAssignment(expression);
                final FunctionReference previousCall = previous == null ? null : this.getFunctionReference(previous);
                if (previousCall != null) {
                    final PsiElement transitionVariable = previous.getVariable();
                    if (transitionVariable instanceof Variable && arguments[2] instanceof Variable) {
                        final Variable callSubject = (Variable) arguments[2];
                        final Variable previousVariable = (Variable) transitionVariable;
                        final PsiElement callResultStorage = expression instanceof AssignmentExpression ? ((AssignmentExpression) expression).getVariable() : callSubject;
                        if (callResultStorage != null && callSubject.getName().equals(previousVariable.getName()) && OpenapiEquivalenceUtil.areEqual(transitionVariable, callResultStorage) && this.canMergeArguments(functionCall, previousCall)) {
                            holder.registerProblem(functionCall, MessagesPresentationUtil.prefixWithEa(messageCascading), new MergeStringReplaceCallsFix(holder.getProject(), functionCall, previousCall, USE_SHORT_ARRAYS_SYNTAX));
                        }
                    }
                }
                /* case: nested replacements */
                this.checkNestedCalls(holder.getProject(), arguments[2], functionCall);
                /* case: search simplification */
                if (arguments[1] instanceof StringLiteralExpression) {
                    final PsiElement search = arguments[0];
                    if (search instanceof ArrayCreationExpression) {
                        this.checkForSimplification((ArrayCreationExpression) search);
                    }
                }
            }
        }

        private void checkForSimplification(@NotNull ArrayCreationExpression candidate) {
            final Set<String> replacements = new HashSet<>();
            for (final PsiElement oneReplacement : candidate.getChildren()) {
                if (oneReplacement instanceof PhpPsiElement) {
                    final PhpPsiElement item = ((PhpPsiElement) oneReplacement).getFirstPsiChild();
                    if (!(item instanceof StringLiteralExpression)) {
                        replacements.clear();
                        return;
                    }
                    replacements.add(item.getText());
                }
            }
            if (replacements.size() == 1) {
                holder.registerProblem(candidate, MessagesPresentationUtil.prefixWithEa(messageReplacements), ProblemHighlightType.WEAK_WARNING, new SimplifyReplacementFix(replacements.iterator().next()));
            }
            replacements.clear();
        }

        private void checkNestedCalls(@NotNull Project project, @NotNull PsiElement callCandidate, @NotNull FunctionReference parentCall) {
            if (OpenapiTypesUtil.isFunctionReference(callCandidate)) {
                final FunctionReference functionCall = (FunctionReference) callCandidate;
                final String functionName = functionCall.getName();
                if (functionName != null && functionName.equals("str_replace") && this.canMergeArguments(functionCall, parentCall)) {
                    holder.registerProblem(callCandidate, MessagesPresentationUtil.prefixWithEa(messageNesting), new MergeStringReplaceCallsFix(project, parentCall, functionCall, USE_SHORT_ARRAYS_SYNTAX));
                }
            }
        }

        private boolean canMergeArguments(@NotNull FunctionReference call, @NotNull FunctionReference previousCall) {
            final PsiElement[] arguments = call.getParameters();
            final PsiElement[] previousArguments = previousCall.getParameters();
            // If an argument is array (non-implicit), we need PHP 7.4+ for QF
            final boolean haveArrayType = Stream.of(arguments[0], arguments[1], previousArguments[0], previousArguments[1]).filter(a -> a instanceof PhpTypedElement && !(a instanceof ArrayCreationExpression)).anyMatch(a -> {
                final PhpType resolved = OpenapiResolveUtil.resolveType((PhpTypedElement) a, holder.getProject());
                if (resolved != null) {
                    final Set<String> types = resolved.filterUnknown().getTypes().stream().map(Types::getType).collect(Collectors.toSet());
                    return types.contains(Types.strArray) && !types.contains(Types.strString);
                }
                return false;
            });
            if (haveArrayType) {
                return PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP740);
            }
            return true;
        }

        @Nullable
        private FunctionReference getFunctionReference(@NotNull AssignmentExpression assignment) {
            FunctionReference result = null;
            final PsiElement value = ExpressionSemanticUtil.getExpressionTroughParenthesis(assignment.getValue());
            if (OpenapiTypesUtil.isFunctionReference(value)) {
                final String functionName = ((FunctionReference) value).getName();
                if (functionName != null && functionName.equals("str_replace")) {
                    result = (FunctionReference) value;
                }
            }
            return result;
        }

        @Nullable
        private FunctionReference getFunctionReference(@NotNull PhpReturn phpReturn) {
            FunctionReference result = null;
            final PsiElement value = ExpressionSemanticUtil.getExpressionTroughParenthesis(ExpressionSemanticUtil.getReturnValue(phpReturn));
            if (OpenapiTypesUtil.isFunctionReference(value)) {
                final String functionName = ((FunctionReference) value).getName();
                if (functionName != null && functionName.equals("str_replace")) {
                    result = (FunctionReference) value;
                }
            }
            return result;
        }

        @Nullable
        private AssignmentExpression getPreviousAssignment(@NotNull PsiElement returnOrAssignment) {
            /* get previous non-comment, non-php-doc expression */
            PsiElement previous = null;
            if (returnOrAssignment instanceof PhpReturn) {
                previous = ((PhpReturn) returnOrAssignment).getPrevPsiSibling();
            } else if (returnOrAssignment instanceof AssignmentExpression) {
                previous = returnOrAssignment.getParent().getPrevSibling();
            }
            while (previous != null && !(previous instanceof PhpPsiElement)) {
                previous = previous.getPrevSibling();
            }
            while (previous instanceof PhpDocComment) {
                previous = ((PhpDocComment) previous).getPrevPsiSibling();
            }
            /* grab the target assignment */
            final AssignmentExpression result;
            if (previous != null && previous.getFirstChild() instanceof AssignmentExpression) {
                result = (AssignmentExpression) previous.getFirstChild();
            } else {
                result = null;
            }
            return result;
        }
    };
}
Also used : PhpDocComment(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment) PhpPsiElementFactory(com.jetbrains.php.lang.psi.PhpPsiElementFactory) java.util(java.util) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) PhpLanguageLevel(com.kalessil.phpStorm.phpInspectionsEA.openApi.PhpLanguageLevel) com.jetbrains.php.lang.psi.elements(com.jetbrains.php.lang.psi.elements) LeafPsiElement(com.intellij.psi.impl.source.tree.LeafPsiElement) UseSuggestedReplacementFixer(com.kalessil.phpStorm.phpInspectionsEA.fixers.UseSuggestedReplacementFixer) OptionsComponent(com.kalessil.phpStorm.phpInspectionsEA.options.OptionsComponent) Collectors(java.util.stream.Collectors) Nullable(org.jetbrains.annotations.Nullable) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) com.kalessil.phpStorm.phpInspectionsEA.utils(com.kalessil.phpStorm.phpInspectionsEA.utils) Stream(java.util.stream.Stream) ProblemDescriptor(com.intellij.codeInspection.ProblemDescriptor) Project(com.intellij.openapi.project.Project) ProblemHighlightType(com.intellij.codeInspection.ProblemHighlightType) com.intellij.psi(com.intellij.psi) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) NotNull(org.jetbrains.annotations.NotNull) LocalQuickFix(com.intellij.codeInspection.LocalQuickFix) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) ArrayUtils(org.apache.commons.lang.ArrayUtils) javax.swing(javax.swing) NotNull(org.jetbrains.annotations.NotNull) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) Project(com.intellij.openapi.project.Project) PhpDocComment(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment) LeafPsiElement(com.intellij.psi.impl.source.tree.LeafPsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 13 with BasePhpElementVisitor

use of com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor in project phpinspectionsea by kalessil.

the class FixedTimeStartWithInspector 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 && mapping.containsKey(functionName)) {
                final PsiElement parent = reference.getParent();
                if (parent instanceof BinaryExpression) {
                    final BinaryExpression binary = (BinaryExpression) parent;
                    final IElementType operator = binary.getOperationType();
                    if (operator == PhpTokenTypes.opIDENTICAL || operator == PhpTokenTypes.opNOT_IDENTICAL) {
                        final PsiElement[] arguments = reference.getParameters();
                        if (arguments.length == 2 && arguments[1] instanceof StringLiteralExpression) {
                            final PsiElement zeroCandidate = OpenapiElementsUtil.getSecondOperand(binary, reference);
                            if (zeroCandidate != null && zeroCandidate.getText().equals("0")) {
                                final StringLiteralExpression literal = (StringLiteralExpression) arguments[1];
                                if (literal.getFirstPsiChild() == null) {
                                    final String replacement = String.format("%s(%s, %s, %s)", mapping.get(functionName), arguments[0].getText(), arguments[1].getText(), PhpStringUtil.unescapeText(literal.getContents(), literal.isSingleQuote()).length());
                                    holder.registerProblem(reference, String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), replacement), new UseFirstCharactersCompareFix(replacement));
                                }
                            }
                        }
                    }
                }
            }
        }
    };
}
Also used : IElementType(com.intellij.psi.tree.IElementType) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) 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 14 with BasePhpElementVisitor

use of com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor in project phpinspectionsea by kalessil.

the class StrEndsWithCanBeUsedInspector 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("substr") || functionName.equals("mb_substr"))) {
                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 limitArgument = this.extractLimitArgument(arguments[1]);
                                final PsiElement second = OpenapiElementsUtil.getSecondOperand(binary, reference);
                                if (second != null && limitArgument != null && OpenapiEquivalenceUtil.areEqual(second, limitArgument)) {
                                    final String replacement = String.format("%s%sstr_ends_with(%s, %s)", operation == PhpTokenTypes.opNOT_IDENTICAL ? "! " : "", reference.getImmediateNamespaceName(), arguments[0].getText(), second.getText());
                                    holder.registerProblem(binary, String.format(MessagesPresentationUtil.prefixWithEa(message), replacement), new UseStrEndsWithFix(replacement));
                                }
                            }
                        }
                    }
                }
            }
        }

        @Nullable
        private PsiElement extractLimitArgument(@Nullable PsiElement expression) {
            if (expression instanceof UnaryExpression) {
                final UnaryExpression unary = (UnaryExpression) expression;
                if (OpenapiTypesUtil.is(unary.getOperation(), PhpTokenTypes.opMINUS)) {
                    final PsiElement argument = unary.getValue();
                    if (OpenapiTypesUtil.isFunctionReference(argument)) {
                        final FunctionReference reference = (FunctionReference) argument;
                        final String functionName = reference.getName();
                        if (functionName != null && (functionName.equals("strlen") || functionName.equals("mb_strlen"))) {
                            final PsiElement[] arguments = reference.getParameters();
                            if (arguments.length == 1) {
                                return arguments[0];
                            }
                        }
                    }
                }
            }
            return null;
        }
    };
}
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) UnaryExpression(com.jetbrains.php.lang.psi.elements.UnaryExpression) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) Nullable(org.jetbrains.annotations.Nullable) NotNull(org.jetbrains.annotations.NotNull)

Example 15 with BasePhpElementVisitor

use of com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor in project phpinspectionsea by kalessil.

the class StrStrUsedAsStrPosInspector 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 && mapping.containsKey(functionName)) {
                final PsiElement[] arguments = reference.getParameters();
                if (arguments.length >= 2) {
                    /* checks implicit boolean comparison pattern */
                    final PsiElement parent = reference.getParent();
                    if (parent instanceof BinaryExpression) {
                        final BinaryExpression binary = (BinaryExpression) parent;
                        if (OpenapiTypesUtil.tsCOMPARE_EQUALITY_OPS.contains(binary.getOperationType())) {
                            final PsiElement secondOperand = OpenapiElementsUtil.getSecondOperand(binary, reference);
                            if (PhpLanguageUtil.isFalse(secondOperand)) {
                                final PsiElement operationNode = binary.getOperation();
                                if (operationNode != null) {
                                    String operation = operationNode.getText();
                                    operation = operation.length() == 2 ? operation + '=' : operation;
                                    final String call = String.format("%s%s(%s, %s)", reference.getImmediateNamespaceName(), mapping.get(functionName), arguments[0].getText(), arguments[1].getText());
                                    final boolean isRegular = ComparisonStyle.isRegular();
                                    final String replacement = String.format("%s %s %s", isRegular ? call : "false", operation, isRegular ? "false" : call);
                                    holder.registerProblem(binary, String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), replacement), new UseStrposFix(replacement));
                                    return;
                                }
                            }
                        }
                    }
                    /* checks non-implicit boolean comparison patternS */
                    if (ExpressionSemanticUtil.isUsedAsLogicalOperand(reference)) {
                        final String operation = parent instanceof UnaryExpression ? "===" : "!==";
                        final String call = String.format("%s%s(%s, %s)", reference.getImmediateNamespaceName(), mapping.get(functionName), arguments[0].getText(), arguments[1].getText());
                        final boolean isRegular = ComparisonStyle.isRegular();
                        final String replacement = String.format("%s %s %s", isRegular ? call : "false", operation, isRegular ? "false" : call);
                        holder.registerProblem(parent instanceof UnaryExpression ? parent : reference, String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), replacement), new UseStrposFix(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) UnaryExpression(com.jetbrains.php.lang.psi.elements.UnaryExpression) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Aggregations

BasePhpElementVisitor (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor)169 NotNull (org.jetbrains.annotations.NotNull)169 PsiElement (com.intellij.psi.PsiElement)157 FunctionReference (com.jetbrains.php.lang.psi.elements.FunctionReference)43 ProblemsHolder (com.intellij.codeInspection.ProblemsHolder)40 BasePhpInspection (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection)39 PsiElementVisitor (com.intellij.psi.PsiElementVisitor)37 PhpType (com.jetbrains.php.lang.psi.resolve.types.PhpType)33 IElementType (com.intellij.psi.tree.IElementType)32 com.jetbrains.php.lang.psi.elements (com.jetbrains.php.lang.psi.elements)29 Project (com.intellij.openapi.project.Project)25 BinaryExpression (com.jetbrains.php.lang.psi.elements.BinaryExpression)25 HashSet (java.util.HashSet)24 PsiTreeUtil (com.intellij.psi.util.PsiTreeUtil)22 MessagesPresentationUtil (com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil)19 StringLiteralExpression (com.jetbrains.php.lang.psi.elements.StringLiteralExpression)18 ArrayList (java.util.ArrayList)18 Set (java.util.Set)18 Nullable (org.jetbrains.annotations.Nullable)18 PhpTokenTypes (com.jetbrains.php.lang.lexer.PhpTokenTypes)17