Search in sources :

Example 81 with BasePhpElementVisitor

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

the class InfinityLoopInspector method buildVisitor.

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

        @Override
        public void visitPhpMethod(@NotNull Method method) {
            /* case: buggy getter (field/method names identical => accidental recursion) */
            if (!method.isAbstract()) {
                final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(method);
                if (body != null && ExpressionSemanticUtil.countExpressionsInGroup(body) == 1) {
                    /* get the recursion candidate */
                    PsiElement last = ExpressionSemanticUtil.getLastStatement(body);
                    if (last instanceof PhpReturn) {
                        last = ((PhpReturn) last).getArgument();
                    } else if (OpenapiTypesUtil.isStatementImpl(last)) {
                        last = ((Statement) last).getFirstPsiChild();
                    }
                    /* now check for harmful recursion */
                    if (last instanceof MethodReference) {
                        final MethodReference value = (MethodReference) last;
                        final String returnedMethodName = value.getName();
                        if (returnedMethodName != null && returnedMethodName.equals(method.getName())) {
                            final PhpExpression subject = value.getClassReference();
                            final String reference = subject == null ? null : subject.getText();
                            if (reference != null && references.contains(reference)) {
                                holder.registerProblem(value, MessagesPresentationUtil.prefixWithEa(message));
                            }
                        }
                    }
                }
            }
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 82 with BasePhpElementVisitor

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

the class OneTimeUseVariablesInspector method buildVisitor.

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

        void checkOneTimeUse(@NotNull PhpPsiElement construct, @NotNull Variable argument) {
            final String variableName = argument.getName();
            final PsiElement previous = construct.getPrevPsiSibling();
            /* verify preceding expression (assignment needed) */
            if (null != previous && OpenapiTypesUtil.isAssignment(previous.getFirstChild())) {
                final AssignmentExpression assign = (AssignmentExpression) previous.getFirstChild();
                /* ensure variables are the same */
                final PhpPsiElement assignVariable = assign.getVariable();
                final PsiElement assignValue = ExpressionSemanticUtil.getExpressionTroughParenthesis(assign.getValue());
                if (null != assignValue && assignVariable instanceof Variable) {
                    final String assignVariableName = assignVariable.getName();
                    if (assignVariableName == null || !assignVariableName.equals(variableName)) {
                        return;
                    }
                    /* check if variable as a function/use(...) parameter by reference */
                    final Function function = ExpressionSemanticUtil.getScope(construct);
                    if (null != function) {
                        for (final Parameter param : function.getParameters()) {
                            if (param.isPassByRef() && param.getName().equals(variableName)) {
                                return;
                            }
                        }
                        final List<Variable> useList = ExpressionSemanticUtil.getUseListVariables(function);
                        if (useList != null && !useList.isEmpty()) {
                            for (final Variable param : useList) {
                                if (param.getName().equals(variableName)) {
                                    /* detect parameters by reference in use clause */
                                    PsiElement ampersandCandidate = param.getPrevSibling();
                                    if (ampersandCandidate instanceof PsiWhiteSpace) {
                                        ampersandCandidate = ampersandCandidate.getPrevSibling();
                                    }
                                    if (OpenapiTypesUtil.is(ampersandCandidate, PhpTokenTypes.opBIT_AND)) {
                                        return;
                                    }
                                }
                            }
                            useList.clear();
                        }
                    }
                    /* too long return/throw statements can be decoupled as a variable */
                    if (!ALLOW_LONG_STATEMENTS && assign.getText().length() > 80) {
                        return;
                    }
                    /* heavy part, find usage inside function/method to analyze multiple writes */
                    final PhpScopeHolder parentScope = ExpressionSemanticUtil.getScope(assign);
                    if (null != parentScope) {
                        int countWrites = 0;
                        int countReads = 0;
                        for (final PhpAccessVariableInstruction oneCase : OpenapiControlFlowUtil.getFollowingVariableAccessInstructions(parentScope.getControlFlow().getEntryPoint(), variableName)) {
                            final boolean isWrite = oneCase.getAccess().isWrite();
                            if (isWrite) {
                                /* false-positives: type specification */
                                final PsiElement context = oneCase.getAnchor().getParent();
                                if (OpenapiTypesUtil.isAssignment(context)) {
                                    final boolean typeAnnotated = this.isTypeAnnotated((PhpPsiElement) context.getParent(), variableName);
                                    if (typeAnnotated) {
                                        return;
                                    }
                                }
                            }
                            countWrites += isWrite ? 1 : 0;
                            countReads += isWrite ? 0 : 1;
                            if (countWrites > 1 || countReads > 1) {
                                return;
                            }
                        }
                    }
                    if (!(assignValue instanceof NewExpression) || PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP540)) {
                        holder.registerProblem(assignVariable, MessagesPresentationUtil.prefixWithEa(messagePattern.replace("%v%", variableName)), new TheLocalFix(holder.getProject(), assign.getParent(), argument, assignValue));
                    }
                }
            }
        }

        @Override
        public void visitPhpReturn(@NotNull PhpReturn returnStatement) {
            if (ANALYZE_RETURN_STATEMENTS) {
                /* if function returning reference, do not inspect returns */
                final Function callable = ExpressionSemanticUtil.getScope(returnStatement);
                final PsiElement nameNode = NamedElementUtil.getNameIdentifier(callable);
                if (null != callable && null != nameNode) {
                    /* is defined like returning reference */
                    PsiElement referenceCandidate = nameNode.getPrevSibling();
                    if (referenceCandidate instanceof PsiWhiteSpace) {
                        referenceCandidate = referenceCandidate.getPrevSibling();
                    }
                    if (OpenapiTypesUtil.is(referenceCandidate, PhpTokenTypes.opBIT_AND)) {
                        return;
                    }
                }
                /* regular function, check one-time use variables */
                final PsiElement argument = ExpressionSemanticUtil.getExpressionTroughParenthesis(returnStatement.getArgument());
                if (argument instanceof PhpPsiElement) {
                    final Variable variable = this.getVariable((PhpPsiElement) argument);
                    if (null != variable) {
                        checkOneTimeUse(returnStatement, variable);
                    }
                }
            }
        }

        @Override
        public void visitPhpMultiassignmentExpression(@NotNull MultiassignmentExpression multiassignmentExpression) {
            if (ANALYZE_ARRAY_DESTRUCTURING) {
                final PsiElement firstChild = multiassignmentExpression.getFirstChild();
                final IElementType nodeType = null == firstChild ? null : firstChild.getNode().getElementType();
                if (null != nodeType && (PhpTokenTypes.kwLIST == nodeType || PhpTokenTypes.chLBRACKET == nodeType)) {
                    final Variable variable = this.getVariable(multiassignmentExpression.getValue());
                    final PsiElement parent = multiassignmentExpression.getParent();
                    if (null != variable && OpenapiTypesUtil.isStatementImpl(parent)) {
                        checkOneTimeUse((PhpPsiElement) parent, variable);
                    }
                }
            }
        }

        /* TODO: once got bored, add foreach source pattern here =) I'm naive but nevertheless ^_^ */
        @Override
        public void visitPhpThrowExpression(@NotNull PhpThrowExpression expression) {
            if (ANALYZE_THROW_STATEMENTS) {
                final PsiElement argument = ExpressionSemanticUtil.getExpressionTroughParenthesis(expression.getArgument());
                if (argument instanceof PhpPsiElement) {
                    final Variable variable = this.getVariable((PhpPsiElement) argument);
                    if (null != variable) {
                        checkOneTimeUse(expression.getExpression(), variable);
                    }
                }
            }
        }

        @Nullable
        private Variable getVariable(@Nullable PhpPsiElement expression) {
            if (expression != null) {
                if (expression instanceof Variable) {
                    return (Variable) expression;
                } else if (expression instanceof MemberReference) {
                    final MemberReference reference = (MemberReference) expression;
                    final PsiElement candidate = reference.getFirstChild();
                    if (candidate instanceof Variable) {
                        final PsiElement operator = OpenapiPsiSearchUtil.findResolutionOperator(reference);
                        if (OpenapiTypesUtil.is(operator, PhpTokenTypes.ARROW)) {
                            return (Variable) candidate;
                        }
                    }
                } else if (OpenapiTypesUtil.isPhpExpressionImpl(expression)) {
                    /* instanceof passes child classes as well, what isn't correct */
                    return getVariable(expression.getFirstPsiChild());
                }
            }
            return null;
        }

        private boolean isTypeAnnotated(@NotNull PhpPsiElement current, @NotNull String variableName) {
            boolean result = false;
            final PsiElement phpdocCandidate = current.getPrevPsiSibling();
            if (phpdocCandidate instanceof PhpDocComment) {
                final PhpDocTag[] hints = ((PhpDocComment) phpdocCandidate).getTagElementsByName("@var");
                if (hints.length == 1) {
                    final PhpDocVariable specifiedVariable = PsiTreeUtil.findChildOfType(hints[0], PhpDocVariable.class);
                    result = specifiedVariable != null && specifiedVariable.getName().equals(variableName);
                }
            }
            return result;
        }
    };
}
Also used : PhpDocVariable(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocVariable) NotNull(org.jetbrains.annotations.NotNull) PhpDocTag(com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PhpScopeHolder(com.jetbrains.php.codeInsight.PhpScopeHolder) PhpThrowExpression(com.kalessil.phpStorm.phpInspectionsEA.openApi.elements.PhpThrowExpression) PhpAccessVariableInstruction(com.jetbrains.php.codeInsight.controlFlow.instructions.PhpAccessVariableInstruction) IElementType(com.intellij.psi.tree.IElementType) PhpDocComment(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment) PhpDocVariable(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocVariable) Nullable(org.jetbrains.annotations.Nullable) NotNull(org.jetbrains.annotations.NotNull)

Example 83 with BasePhpElementVisitor

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

the class UselessReturnInspector method buildVisitor.

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

        @Override
        public void visitPhpReturn(@NotNull PhpReturn expression) {
            final PhpExpression returnValue = ExpressionSemanticUtil.getReturnValue(expression);
            if (returnValue instanceof AssignmentExpression) {
                final AssignmentExpression assignment = (AssignmentExpression) returnValue;
                final PsiElement assignmentVariable = assignment.getVariable();
                final PsiElement assignmentValue = assignment.getValue();
                if (assignmentValue != null && assignmentVariable instanceof Variable) {
                    final Function scope = ExpressionSemanticUtil.getScope(expression);
                    if (scope != null) {
                        final Variable variable = (Variable) assignmentVariable;
                        final boolean isTarget = !this.isArgumentReference(variable, scope) && !this.isBoundReference(variable, scope) && !this.isStaticVariable(variable, scope) && !this.isUsedInFinally(variable, scope);
                        if (isTarget) {
                            final String replacement = String.format("return %s;", assignmentValue.getText());
                            holder.registerProblem(expression, MessagesPresentationUtil.prefixWithEa(messageConfusing), new SimplifyFix(replacement));
                        }
                    }
                }
            }
        }

        @Override
        public void visitPhpMethod(@NotNull Method method) {
            if (!method.isAbstract()) {
                this.inspectForSenselessReturn(method);
            }
        }

        @Override
        public void visitPhpFunction(@NotNull Function function) {
            this.inspectForSenselessReturn(function);
        }

        private void inspectForSenselessReturn(@NotNull Function callable) {
            final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(callable);
            final PsiElement lastStatement = body == null ? null : ExpressionSemanticUtil.getLastStatement(body);
            if (lastStatement instanceof PhpReturn) {
                final PhpExpression returnValue = ExpressionSemanticUtil.getReturnValue((PhpReturn) lastStatement);
                if (returnValue == null) {
                    holder.registerProblem(lastStatement, MessagesPresentationUtil.prefixWithEa(messageSenseless), ProblemHighlightType.LIKE_UNUSED_SYMBOL);
                }
            }
        }

        private boolean isArgumentReference(@NotNull Variable variable, @NotNull Function function) {
            boolean result = false;
            final String variableName = variable.getName();
            for (final Parameter parameter : function.getParameters()) {
                if (parameter.getName().equals(variableName) && parameter.isPassByRef()) {
                    result = true;
                    break;
                }
            }
            return result;
        }

        private boolean isBoundReference(@NotNull Variable variable, @NotNull Function function) {
            boolean result = false;
            final String variableName = variable.getName();
            if (!variableName.isEmpty() && !(result = this.isReferenceBoundTo(variableName, function))) {
                final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(function);
                if (body != null) {
                    result = PsiTreeUtil.findChildrenOfType(body, Function.class).stream().anyMatch(l -> this.isReferenceBoundTo(variableName, l));
                }
            }
            return result;
        }

        private boolean isReferenceBoundTo(@NotNull String variableName, @NotNull Function function) {
            boolean result = false;
            if (OpenapiTypesUtil.isLambda(function)) {
                final List<Variable> used = ExpressionSemanticUtil.getUseListVariables(function);
                if (used != null) {
                    final Optional<Variable> match = used.stream().filter(v -> v.getName().equals(variableName)).findFirst();
                    if (match.isPresent()) {
                        final PsiElement previous = match.get().getPrevSibling();
                        final PsiElement candidate = previous instanceof PsiWhiteSpace ? previous.getPrevSibling() : previous;
                        result = OpenapiTypesUtil.is(candidate, PhpTokenTypes.opBIT_AND);
                    }
                    used.clear();
                }
            }
            return result;
        }

        private boolean isUsedInFinally(@NotNull Variable variable, @NotNull Function function) {
            boolean result = false;
            final Try tryScope = PsiTreeUtil.getParentOfType(variable, Try.class, false, Function.class);
            if (tryScope != null) {
                final Finally finallyScope = tryScope.getFinallyBlock();
                final GroupStatement body = finallyScope == null ? null : ExpressionSemanticUtil.getGroupStatement(finallyScope);
                if (body != null) {
                    final String variableName = variable.getName();
                    result = PsiTreeUtil.findChildrenOfType(body, Variable.class).stream().anyMatch(v -> variableName.equals(v.getName()));
                }
            }
            return result;
        }

        private boolean isStaticVariable(@NotNull Variable variable, @NotNull Function function) {
            boolean result = false;
            final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(function);
            if (body != null) {
                final String variableName = variable.getName();
                for (final PhpStaticStatement candidate : PsiTreeUtil.findChildrenOfType(body, PhpStaticStatement.class)) {
                    result = candidate.getDeclarations().stream().anyMatch(declaration -> {
                        final PhpPsiElement declared = declaration.getVariable();
                        return declared instanceof Variable && variableName.equals(declared.getName());
                    });
                    if (result) {
                        break;
                    }
                }
            }
            return result;
        }
    };
}
Also used : PhpPsiElementFactory(com.jetbrains.php.lang.psi.PhpPsiElementFactory) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) ExpressionSemanticUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.ExpressionSemanticUtil) com.jetbrains.php.lang.psi.elements(com.jetbrains.php.lang.psi.elements) PhpTokenTypes(com.jetbrains.php.lang.lexer.PhpTokenTypes) PsiTreeUtil(com.intellij.psi.util.PsiTreeUtil) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) OpenapiTypesUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiTypesUtil) List(java.util.List) ProblemDescriptor(com.intellij.codeInspection.ProblemDescriptor) MessagesPresentationUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil) PsiElement(com.intellij.psi.PsiElement) PsiWhiteSpace(com.intellij.psi.PsiWhiteSpace) Project(com.intellij.openapi.project.Project) Optional(java.util.Optional) ProblemHighlightType(com.intellij.codeInspection.ProblemHighlightType) NotNull(org.jetbrains.annotations.NotNull) LocalQuickFix(com.intellij.codeInspection.LocalQuickFix) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PsiElement(com.intellij.psi.PsiElement) PsiWhiteSpace(com.intellij.psi.PsiWhiteSpace) NotNull(org.jetbrains.annotations.NotNull)

Example 84 with BasePhpElementVisitor

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

the class SenselessTernaryOperatorInspector 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 condition = ExpressionSemanticUtil.getExpressionTroughParenthesis(expression.getCondition());
            if (condition instanceof BinaryExpression) {
                final BinaryExpression binary = (BinaryExpression) condition;
                final IElementType operationType = binary.getOperationType();
                if (operationType == PhpTokenTypes.opIDENTICAL || operationType == PhpTokenTypes.opNOT_IDENTICAL) {
                    final boolean isInverted = operationType == PhpTokenTypes.opNOT_IDENTICAL;
                    final PsiElement trueVariant = isInverted ? expression.getFalseVariant() : expression.getTrueVariant();
                    final PsiElement falseVariant = isInverted ? expression.getTrueVariant() : expression.getFalseVariant();
                    if (trueVariant != null && falseVariant != null) {
                        final PsiElement value = binary.getRightOperand();
                        final PsiElement subject = binary.getLeftOperand();
                        if (value != null && subject != null) {
                            final boolean isLeftPartReturned = OpenapiEquivalenceUtil.areEqual(value, trueVariant) || OpenapiEquivalenceUtil.areEqual(value, falseVariant);
                            if (isLeftPartReturned) {
                                final boolean isRightPartReturned = OpenapiEquivalenceUtil.areEqual(subject, falseVariant) || OpenapiEquivalenceUtil.areEqual(subject, trueVariant);
                                if (isRightPartReturned) {
                                    final String replacement = falseVariant.getText();
                                    holder.registerProblem(expression, String.format(MessagesPresentationUtil.prefixWithEa(patternUseOperands), replacement), new SimplifyFix(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 85 with BasePhpElementVisitor

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

the class NestedTernaryOperatorInspector 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 condition = ExpressionSemanticUtil.getExpressionTroughParenthesis(expression.getCondition());
            if (condition instanceof TernaryExpression) {
                holder.registerProblem(condition, MessagesPresentationUtil.prefixWithEa(messageNested));
            }
            final PsiElement trueVariant = ExpressionSemanticUtil.getExpressionTroughParenthesis(expression.getTrueVariant());
            if (trueVariant instanceof TernaryExpression) {
                holder.registerProblem(trueVariant, MessagesPresentationUtil.prefixWithEa(messageNested));
            }
            final PsiElement falseVariant = expression.getFalseVariant();
            final PsiElement unboxedFalseVariant = ExpressionSemanticUtil.getExpressionTroughParenthesis(falseVariant);
            if (unboxedFalseVariant instanceof TernaryExpression) {
                final boolean allow = falseVariant instanceof TernaryExpression && expression.isShort() && ((TernaryExpression) falseVariant).isShort();
                if (!allow) {
                    holder.registerProblem(unboxedFalseVariant, MessagesPresentationUtil.prefixWithEa(messageNested));
                }
            }
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) TernaryExpression(com.jetbrains.php.lang.psi.elements.TernaryExpression) 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