Search in sources :

Example 1 with PhpDocComment

use of com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment in project phpinspectionsea by kalessil.

the class ThrowsResolveUtil method collectThrownAndInherited.

private static boolean collectThrownAndInherited(@NotNull Method method, @NotNull Collection<PhpClass> exceptionsRegistry, @NotNull Collection<Method> processedMethods, boolean lookupWorkflow) {
    processedMethods.add(method);
    boolean result = false;
    final PhpDocComment annotations = method.getDocComment();
    if (annotations == null) {
        /* if PhpDoc is missing, check workflow; but we'll search only `throw new ...` statements */
        if (lookupWorkflow && !method.isAbstract()) {
            for (final PhpThrow thrown : PsiTreeUtil.findChildrenOfType(method, PhpThrow.class)) {
                final PsiElement argument = thrown.getArgument();
                if (argument instanceof NewExpression) {
                    final PsiElement classReference = ((NewExpression) argument).getClassReference();
                    if (classReference != null) {
                        /* false-positives: lambdas and anonymous classes */
                        if (PsiTreeUtil.getParentOfType(thrown, Function.class) != method) {
                            continue;
                        } else /* false-positives: ALL try-enclosed throw statements */
                        if (PsiTreeUtil.getParentOfType(thrown, Try.class, false, (Class) Method.class) != null) {
                            continue;
                        }
                        final PsiElement clazz = OpenapiResolveUtil.resolveReference((PsiReference) classReference);
                        if (clazz instanceof PhpClass) {
                            exceptionsRegistry.add((PhpClass) clazz);
                        }
                    }
                }
            }
            result = true;
        }
    } else {
        /* find all @throws and remember FQNs, @throws can be combined with @inheritdoc */
        for (final PhpDocTag candidate : PsiTreeUtil.findChildrenOfType(annotations, PhpDocTag.class)) {
            if (candidate.getName().equalsIgnoreCase("@throws")) {
                /* definition styles can differ: single tags, pipe concatenated or combined  */
                for (final PhpDocType type : PsiTreeUtil.findChildrenOfType(candidate, PhpDocType.class)) {
                    final PsiElement clazz = OpenapiResolveUtil.resolveReference(type);
                    if (clazz instanceof PhpClass) {
                        exceptionsRegistry.add((PhpClass) clazz);
                    }
                }
            }
        }
        /* resolve inherit doc tags */
        if (annotations.hasInheritDocTag()) {
            collectInherited(method, exceptionsRegistry, processedMethods);
        }
        result = true;
    }
    return result;
}
Also used : PhpDocTag(com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag) PhpDocComment(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment) PhpDocType(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocType) PsiElement(com.intellij.psi.PsiElement)

Example 2 with PhpDocComment

use of com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment in project yii2support by nvlad.

the class DatabaseUtils method getDocComment.

@Nullable
public static PhpDocComment getDocComment(PsiElement element) {
    PsiElement nextElement = element.getParent();
    int limit = 10;
    while (limit > 0) {
        if (nextElement instanceof PhpDocComment)
            return (PhpDocComment) nextElement;
        else if (nextElement == null)
            return null;
        nextElement = nextElement.getParent();
        limit--;
    }
    return null;
}
Also used : PhpDocComment(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment) PsiElement(com.intellij.psi.PsiElement) Nullable(org.jetbrains.annotations.Nullable)

Example 3 with PhpDocComment

use of com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment 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 4 with PhpDocComment

use of com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment in project phpinspectionsea by kalessil.

the class AlterInForeachInspector method buildVisitor.

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

        @Override
        public void visitPhpForeach(@NotNull ForeachStatement foreach) {
            /* lookup for reference preceding value */
            final Variable objForeachValue = foreach.getValue();
            if (null != objForeachValue) {
                PsiElement prevElement = objForeachValue.getPrevSibling();
                if (prevElement instanceof PsiWhiteSpace) {
                    prevElement = prevElement.getPrevSibling();
                }
                if (null != prevElement) {
                    PhpPsiElement nextExpression = foreach.getNextPsiSibling();
                    if (OpenapiTypesUtil.is(prevElement, PhpTokenTypes.opBIT_AND)) {
                        /* === requested by the community, not part of original idea === */
                        /* look up for parents which doesn't have following statements, eg #53 */
                        PsiElement foreachParent = foreach.getParent();
                        while (null == nextExpression && null != foreachParent) {
                            if (!(foreachParent instanceof GroupStatement)) {
                                nextExpression = ((PhpPsiElement) foreachParent).getNextPsiSibling();
                            }
                            foreachParent = foreachParent.getParent();
                            if (null == foreachParent || foreachParent instanceof Function || foreachParent instanceof PhpFile) {
                                break;
                            }
                        }
                        /* === requested by the community, not part of original idea === */
                        /* allow return/end of control flow after the loop - no issues can be introduced */
                        boolean isRequirementFullFilled = false;
                        while (nextExpression instanceof PhpDocComment) {
                            nextExpression = nextExpression.getNextPsiSibling();
                        }
                        if (nextExpression == null || nextExpression instanceof PhpReturn || OpenapiTypesUtil.isThrowExpression(nextExpression)) {
                            isRequirementFullFilled = true;
                        }
                        /* check unset is applied to value-variable */
                        final String foreachValueName = objForeachValue.getName();
                        if (nextExpression instanceof PhpUnset && !StringUtils.isEmpty(foreachValueName)) {
                            for (PhpPsiElement unsetArgument : ((PhpUnset) nextExpression).getArguments()) {
                                // skip non-variable expressions
                                if (!(unsetArgument instanceof Variable)) {
                                    continue;
                                }
                                // check argument matched foreach value name
                                final String unsetArgumentName = unsetArgument.getName();
                                if (!StringUtils.isEmpty(unsetArgumentName) && unsetArgumentName.equals(foreachValueName)) {
                                    isRequirementFullFilled = true;
                                    break;
                                }
                            }
                        }
                        /* check if warning needs to be reported */
                        if (!isRequirementFullFilled) {
                            holder.registerProblem(objForeachValue, MessagesPresentationUtil.prefixWithEa(messageMissingUnset));
                        }
                    } else {
                        /* check for unset in parent foreach-statements: foreach-{foreach}-unset */
                        ForeachStatement currentForeach = foreach;
                        while (!(nextExpression instanceof PhpUnset) && currentForeach.getParent() instanceof GroupStatement && currentForeach.getParent().getParent() instanceof ForeachStatement) {
                            currentForeach = (ForeachStatement) currentForeach.getParent().getParent();
                            nextExpression = currentForeach.getNextPsiSibling();
                        }
                        /* check if un-sets non-reference value - not needed at all, probably forgotten to cleanup */
                        if (nextExpression instanceof PhpUnset) {
                            final PhpPsiElement[] unsetArguments = ((PhpUnset) nextExpression).getArguments();
                            if (unsetArguments.length > 0) {
                                final String foreachValueName = objForeachValue.getName();
                                for (PhpPsiElement unsetExpression : unsetArguments) {
                                    if (!(unsetArguments[0] instanceof Variable)) {
                                        continue;
                                    }
                                    final String unsetArgumentName = unsetExpression.getName();
                                    if (!StringUtils.isEmpty(unsetArgumentName) && !StringUtils.isEmpty(foreachValueName) && unsetArgumentName.equals(foreachValueName)) {
                                        holder.registerProblem(unsetExpression, MessagesPresentationUtil.prefixWithEa(patternAmbiguousUnset.replace("%v%", foreachValueName)), ProblemHighlightType.WEAK_WARNING);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        @Override
        public void visitPhpAssignmentExpression(@NotNull AssignmentExpression assignmentExpression) {
            if (!SUGGEST_USING_VALUE_BY_REF) /*|| ... PHP7 ...*/
            {
                return;
            }
            final PhpPsiElement operand = assignmentExpression.getVariable();
            if (!(operand instanceof ArrayAccessExpression)) {
                return;
            }
            /* ensure assignment structure is complete */
            final ArrayAccessExpression container = (ArrayAccessExpression) operand;
            if (null == container.getIndex() || null == container.getValue() || !(container.getIndex().getValue() instanceof Variable)) {
                return;
            }
            /* get parts of assignment */
            final PhpPsiElement objForeachSourceCandidate = container.getValue();
            final PhpPsiElement objForeachKeyCandidate = container.getIndex().getValue();
            PsiElement parent = assignmentExpression.getParent();
            while (null != parent && !(parent instanceof PhpFile)) {
                /* terminate if reached callable */
                if (parent instanceof Function) {
                    return;
                }
                if (parent instanceof ForeachStatement) {
                    /* get parts of foreach: array, key, value */
                    final ForeachStatement objForeach = (ForeachStatement) parent;
                    final Variable objForeachValue = objForeach.getValue();
                    final Variable objForeachKey = objForeach.getKey();
                    final PsiElement objForeachArray = objForeach.getArray();
                    /* report if aggressive optimization possible: foreach(... as &$value) */
                    if (null != objForeachArray && null != objForeachKey && null != objForeachValue && OpenapiEquivalenceUtil.areEqual(objForeachKey, objForeachKeyCandidate) && OpenapiEquivalenceUtil.areEqual(objForeachArray, objForeachSourceCandidate)) {
                        final String strName = objForeachValue.getName();
                        if (!StringUtils.isEmpty(strName)) {
                            holder.registerProblem(operand, MessagesPresentationUtil.prefixWithEa(patternSuggestReference.replace("%c%", strName).replace("%v%", strName)), ProblemHighlightType.WEAK_WARNING);
                            return;
                        }
                    }
                }
                parent = parent.getParent();
            }
        }
    };
}
Also used : PhpFile(com.jetbrains.php.lang.psi.PhpFile) NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PhpDocComment(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment) PsiElement(com.intellij.psi.PsiElement) PsiWhiteSpace(com.intellij.psi.PsiWhiteSpace) NotNull(org.jetbrains.annotations.NotNull)

Example 5 with PhpDocComment

use of com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment in project phpinspectionsea by kalessil.

the class DropMethodFix method applyFix.

@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
    final PsiElement expression = descriptor.getPsiElement().getParent();
    if (expression instanceof Method && !project.isDisposed()) {
        /* delete preceding PhpDoc */
        final PhpPsiElement previous = ((Method) expression).getPrevPsiSibling();
        if (previous instanceof PhpDocComment) {
            previous.delete();
        }
        /* delete space after the method */
        final PsiElement nextExpression = expression.getNextSibling();
        if (nextExpression instanceof PsiWhiteSpace) {
            nextExpression.delete();
        }
        /* delete the method itself */
        expression.delete();
    }
}
Also used : PhpDocComment(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment) Method(com.jetbrains.php.lang.psi.elements.Method) PhpPsiElement(com.jetbrains.php.lang.psi.elements.PhpPsiElement) PsiElement(com.intellij.psi.PsiElement) PhpPsiElement(com.jetbrains.php.lang.psi.elements.PhpPsiElement) PsiWhiteSpace(com.intellij.psi.PsiWhiteSpace)

Aggregations

PhpDocComment (com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment)16 PsiElement (com.intellij.psi.PsiElement)14 NotNull (org.jetbrains.annotations.NotNull)11 BasePhpElementVisitor (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor)9 PsiWhiteSpace (com.intellij.psi.PsiWhiteSpace)5 PhpType (com.jetbrains.php.lang.psi.resolve.types.PhpType)5 ProblemsHolder (com.intellij.codeInspection.ProblemsHolder)4 IElementType (com.intellij.psi.tree.IElementType)4 PhpDocTag (com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag)4 com.jetbrains.php.lang.psi.elements (com.jetbrains.php.lang.psi.elements)4 com.kalessil.phpStorm.phpInspectionsEA.utils (com.kalessil.phpStorm.phpInspectionsEA.utils)4 Project (com.intellij.openapi.project.Project)3 PsiTreeUtil (com.intellij.psi.util.PsiTreeUtil)3 PhpDocType (com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocType)3 PhpTokenTypes (com.jetbrains.php.lang.lexer.PhpTokenTypes)3 BasePhpInspection (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection)3 OptionsComponent (com.kalessil.phpStorm.phpInspectionsEA.options.OptionsComponent)3 java.util (java.util)3 Collectors (java.util.stream.Collectors)3 javax.swing (javax.swing)3