Search in sources :

Example 86 with BasePhpElementVisitor

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

the class StrlenInEmptyStringCheckContextInspection 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 && (functionName.equals("strlen") || functionName.equals("mb_strlen"))) {
                final PsiElement[] arguments = reference.getParameters();
                if (arguments.length > 0 && ExpressionSemanticUtil.getBlockScope(reference) != null) {
                    boolean isMatchedPattern = false;
                    boolean isEmptyString = false;
                    PsiElement target = null;
                    /* check explicit numbers comparisons */
                    final PsiElement parent = reference.getParent();
                    if (parent instanceof BinaryExpression) {
                        final BinaryExpression binary = (BinaryExpression) parent;
                        final PsiElement left = binary.getLeftOperand();
                        final PsiElement secondOperand = OpenapiElementsUtil.getSecondOperand(binary, reference);
                        /* second operand should be a number */
                        if (OpenapiTypesUtil.isNumber(secondOperand)) {
                            final String number = secondOperand.getText();
                            /* check cases when comparing with 1 */
                            final IElementType operator = binary.getOperationType();
                            if (operator == PhpTokenTypes.opGREATER) {
                                isMatchedPattern = left == reference && number.equals("0");
                                target = binary;
                                isEmptyString = false;
                            } else if (operator == PhpTokenTypes.opLESS || operator == PhpTokenTypes.opGREATER_OR_EQUAL) {
                                isMatchedPattern = left == reference && number.equals("1");
                                target = binary;
                                isEmptyString = operator == PhpTokenTypes.opLESS;
                            }
                            /* check cases when checking equality to 0 */
                            if (!isMatchedPattern && OpenapiTypesUtil.tsCOMPARE_EQUALITY_OPS.contains(operator)) {
                                isMatchedPattern = number.equals("0");
                                target = binary;
                                isEmptyString = operator == PhpTokenTypes.opIDENTICAL || operator == PhpTokenTypes.opEQUAL;
                            }
                        }
                    }
                    /* checks NON-implicit boolean comparison patterns */
                    if (!isMatchedPattern && ExpressionSemanticUtil.isUsedAsLogicalOperand(reference)) {
                        isMatchedPattern = true;
                        target = reference;
                        final PsiElement operation = parent instanceof UnaryExpression ? ((UnaryExpression) parent).getOperation() : null;
                        if (operation != null) {
                            isEmptyString = OpenapiTypesUtil.is(operation, PhpTokenTypes.opNOT);
                            target = parent;
                        }
                    }
                    /* investigate possible issues */
                    if (isMatchedPattern) {
                        final boolean isRegular = ComparisonStyle.isRegular();
                        final String operator = (isEmptyString ? "=" : "!") + (this.canApplyIdentityOperator(arguments[0]) ? "==" : "=");
                        final String replacement = String.format(isRegular ? "%s %s ''" : "'' %s %s", isRegular ? arguments[0].getText() : operator, isRegular ? operator : arguments[0].getText());
                        holder.registerProblem(target, String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), replacement), new CompareToEmptyStringFix(replacement));
                    }
                }
            }
        }

        private boolean canApplyIdentityOperator(@NotNull PsiElement value) {
            if (value instanceof PhpTypedElement) {
                final PhpType resolved = OpenapiResolveUtil.resolveType((PhpTypedElement) value, holder.getProject());
                if (resolved != null && resolved.size() == 1) {
                    return Types.strString.equals(Types.getType(resolved.getTypes().iterator().next()));
                }
            }
            return false;
        }
    };
}
Also used : UnaryExpression(com.jetbrains.php.lang.psi.elements.UnaryExpression) NotNull(org.jetbrains.annotations.NotNull) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) IElementType(com.intellij.psi.tree.IElementType) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) FunctionReference(com.jetbrains.php.lang.psi.elements.FunctionReference) PhpTypedElement(com.jetbrains.php.lang.psi.elements.PhpTypedElement) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 87 with BasePhpElementVisitor

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

the class EmptyClassInspector method buildVisitor.

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

        @Override
        public void visitPhpClass(@NotNull PhpClass clazz) {
            if (!clazz.isInterface() && !clazz.isDeprecated() && !clazz.isAnonymous()) {
                final boolean isEmpty = clazz.getOwnFields().length == 0 && clazz.getOwnMethods().length == 0 && clazz.getTraits().length == 0;
                if (isEmpty) {
                    final PhpClass parent = OpenapiResolveUtil.resolveSuperClass(clazz);
                    if (parent != null) {
                        /* we can be forced to introduce an empty class: abstract parent, exception classes */
                        final boolean skip = parent.isAbstract() || InterfacesExtractUtil.getCrawlInheritanceTree(clazz, true).stream().anyMatch(c -> c.getFQN().equals("\\Exception"));
                        if (skip) {
                            return;
                        }
                    }
                    final PsiElement nameNode = NamedElementUtil.getNameIdentifier(clazz);
                    if (nameNode != null) {
                        holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(message));
                    }
                }
            }
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PhpClass(com.jetbrains.php.lang.psi.elements.PhpClass) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) OpenapiResolveUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiResolveUtil) MessagesPresentationUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil) InterfacesExtractUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.hierarhy.InterfacesExtractUtil) PsiElement(com.intellij.psi.PsiElement) NamedElementUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.NamedElementUtil) NotNull(org.jetbrains.annotations.NotNull) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PhpClass(com.jetbrains.php.lang.psi.elements.PhpClass) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 88 with BasePhpElementVisitor

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

the class MissingIssetImplementationInspector method buildVisitor.

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

        @Override
        public void visitPhpEmpty(@NotNull PhpEmpty expression) {
            this.analyzeDispatchedExpressions(expression.getVariables());
        }

        @Override
        public void visitPhpIsset(@NotNull PhpIsset expression) {
            this.analyzeDispatchedExpressions(expression.getVariables());
        }

        private void analyzeDispatchedExpressions(@NotNull PhpExpression[] parameters) {
            final Project project = holder.getProject();
            final PhpIndex projectIndex = PhpIndex.getInstance(project);
            for (final PhpExpression parameter : parameters) {
                if (parameter instanceof FieldReference) {
                    final FieldReference reference = (FieldReference) parameter;
                    final ASTNode nameNode = reference.getNameNode();
                    final String parameterName = parameter.getName();
                    /* if the field name is not implicit or the field resolved, continue */
                    if ((nameNode == null || nameNode instanceof Variable) || (parameterName == null || parameterName.isEmpty()) || !OpenapiTypesUtil.is(OpenapiPsiSearchUtil.findResolutionOperator(reference), PhpTokenTypes.ARROW) || OpenapiResolveUtil.resolveReference(reference) != null) {
                        continue;
                    }
                    /* false-positives: in the $this context we are dealing with dynamic properties */
                    final PhpExpression variable = reference.getClassReference();
                    if (null == variable || variable.getText().equals("$this")) {
                        continue;
                    }
                    /* long way around: identify an lookup classes */
                    final Set<String> resolvedTypes = new HashSet<>();
                    final PhpType resolved = OpenapiResolveUtil.resolveType(variable, project);
                    if (resolved != null) {
                        resolved.filterUnknown().getTypes().forEach(t -> resolvedTypes.add(Types.getType(t)));
                    }
                    for (final String type : resolvedTypes) {
                        /* false-positives: SimpleXMLElement, stdClass */
                        if (type.startsWith("\\") && !magicClasses.contains(type)) {
                            final Collection<PhpClass> classes = projectIndex.getClassesByFQN(type);
                            final PhpClass clazz = classes.isEmpty() ? null : classes.iterator().next();
                            /* resolved class FQN might differ from what type states */
                            if (clazz != null && !magicClasses.contains(clazz.getFQN())) {
                                final boolean hasField = OpenapiResolveUtil.resolveField(clazz, parameterName) != null;
                                if (!hasField && OpenapiResolveUtil.resolveMethod(clazz, "__isset") == null) {
                                    holder.registerProblem(parameter, MessagesPresentationUtil.prefixWithEa(messagePattern.replace("%c%", type)), ProblemHighlightType.GENERIC_ERROR);
                                    break;
                                }
                            }
                        }
                    }
                    resolvedTypes.clear();
                }
            }
        }
    };
}
Also used : PhpIndex(com.jetbrains.php.PhpIndex) 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) ASTNode(com.intellij.lang.ASTNode) HashSet(java.util.HashSet) NotNull(org.jetbrains.annotations.NotNull)

Example 89 with BasePhpElementVisitor

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

the class SenselessMethodDuplicationInspector method buildVisitor.

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

        @Override
        public void visitPhpMethod(@NotNull Method method) {
            /* process only real classes and methods */
            if (method.isAbstract() || method.isDeprecated() || method.getModifier().isPrivate() || this.isTestContext(method)) {
                return;
            }
            final PhpClass clazz = method.getContainingClass();
            if (clazz == null || clazz.isTrait() || clazz.isInterface()) {
                return;
            }
            /* don't take too heavy work */
            final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(method);
            final int countExpressions = body == null ? 0 : ExpressionSemanticUtil.countExpressionsInGroup(body);
            if (countExpressions == 0 || countExpressions > MAX_METHOD_SIZE) {
                return;
            }
            /* ensure parent, parent methods are existing and contains the same amount of expressions */
            final PhpClass parent = OpenapiResolveUtil.resolveSuperClass(clazz);
            final Method parentMethod = null == parent ? null : OpenapiResolveUtil.resolveMethod(parent, method.getName());
            if (parentMethod == null || parentMethod.isAbstract() || parentMethod.isDeprecated() || parentMethod.getModifier().isPrivate()) {
                return;
            }
            final GroupStatement parentBody = ExpressionSemanticUtil.getGroupStatement(parentMethod);
            if (parentBody == null || ExpressionSemanticUtil.countExpressionsInGroup(parentBody) != countExpressions) {
                return;
            }
            /* iterate and compare expressions */
            PhpPsiElement ownExpression = body.getFirstPsiChild();
            PhpPsiElement parentExpression = parentBody.getFirstPsiChild();
            for (int index = 0; index <= countExpressions; ++index) {
                /* skip doc-blocks */
                while (ownExpression instanceof PhpDocComment) {
                    ownExpression = ownExpression.getNextPsiSibling();
                }
                while (parentExpression instanceof PhpDocComment) {
                    parentExpression = parentExpression.getNextPsiSibling();
                }
                if (null == ownExpression || null == parentExpression) {
                    break;
                }
                /* process comparing 2 nodes */
                if (!OpenapiEquivalenceUtil.areEqual(ownExpression, parentExpression)) {
                    return;
                }
                ownExpression = ownExpression.getNextPsiSibling();
                parentExpression = parentExpression.getNextPsiSibling();
            }
            /* methods seem to be identical: resolve used classes to avoid ns/imports magic */
            boolean areReferencesMatching = true;
            final Collection<String> ownReferences = this.getUsedReferences(body);
            if (!ownReferences.isEmpty()) {
                final Collection<String> parentReferences = this.getUsedReferences(parentBody);
                areReferencesMatching = !ownReferences.contains(null) && ownReferences.equals(parentReferences);
                parentReferences.clear();
            }
            ownReferences.clear();
            if (!areReferencesMatching) {
                return;
            }
            final PsiElement methodName = NamedElementUtil.getNameIdentifier(method);
            if (methodName != null && !this.isOperatingOnPrivateMembers(parentMethod)) {
                final boolean canFix = !parentMethod.getAccess().isPrivate();
                if (method.getAccess().equals(parentMethod.getAccess())) {
                    holder.registerProblem(methodName, String.format(MessagesPresentationUtil.prefixWithEa(messagePatternIdentical), method.getName()), canFix ? new DropMethodFix() : null);
                } else {
                    holder.registerProblem(methodName, String.format(MessagesPresentationUtil.prefixWithEa(messagePatternProxy), method.getName()), canFix ? new ProxyCallFix() : null);
                }
            }
        }

        private Collection<String> getUsedReferences(@NotNull GroupStatement body) {
            final Set<String> fqns = new HashSet<>();
            for (final PhpReference reference : PsiTreeUtil.findChildrenOfAnyType(body, ClassReference.class, ConstantReference.class, FunctionReference.class)) {
                if (!(reference instanceof MethodReference)) {
                    final PsiElement entry = OpenapiResolveUtil.resolveReference(reference);
                    if (entry instanceof PhpNamedElement) {
                        // We have to use this over resolved entry FQN as some of PhpStorm versions might not resolve the proper symbol
                        fqns.add(reference.getFQN());
                    } else {
                        fqns.add(null);
                    }
                }
            }
            return fqns;
        }

        private boolean isOperatingOnPrivateMembers(@NotNull Method method) {
            final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(method);
            if (body != null) {
                for (final MemberReference reference : PsiTreeUtil.findChildrenOfType(body, MemberReference.class)) {
                    final PsiElement base = reference.getFirstChild();
                    final boolean resolve = (base instanceof Variable && ((Variable) base).getName().equals("this")) || (base instanceof ClassReference && base.getText().equals("self"));
                    if (resolve) {
                        final PsiElement resolved = OpenapiResolveUtil.resolveReference(reference);
                        if (resolved instanceof PhpClassMember && ((PhpClassMember) resolved).getModifier().isPrivate()) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }
    };
}
Also used : NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PhpDocComment(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment) DropMethodFix(com.kalessil.phpStorm.phpInspectionsEA.fixers.DropMethodFix) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 90 with BasePhpElementVisitor

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

the class SuspiciousLoopInspector method buildVisitor.

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

        @Override
        public void visitPhpForeach(@NotNull ForeachStatement statement) {
            if (VERIFY_VARIABLES_OVERRIDE) {
                this.inspectVariables(statement);
            }
        }

        @Override
        public void visitPhpFor(@NotNull For statement) {
            if (statement.getConditionalExpressions().length > 1) {
                holder.registerProblem(statement.getFirstChild(), MessagesPresentationUtil.prefixWithEa(messageMultipleConditions));
            }
            if (VERIFY_VARIABLES_OVERRIDE) {
                this.inspectVariables(statement);
            }
        }

        private void inspectVariables(@NotNull PhpPsiElement loop) {
            final Set<String> loopVariables = this.getLoopVariables(loop);
            final Function function = ExpressionSemanticUtil.getScope(loop);
            if (null != function) {
                final HashSet<String> parameters = new HashSet<>();
                for (final Parameter param : function.getParameters()) {
                    parameters.add(param.getName());
                }
                loopVariables.forEach(variable -> {
                    if (parameters.contains(variable)) {
                        final String message = patternOverridesParameter.replace("%v%", variable).replace("%t%", function instanceof Method ? "method" : "function");
                        holder.registerProblem(loop.getFirstChild(), MessagesPresentationUtil.prefixWithEa(message));
                    }
                });
                parameters.clear();
            }
            /* scan parents until reached file/callable */
            PsiElement parent = loop.getParent();
            while (null != parent && !(parent instanceof Function) && !(parent instanceof PhpFile)) {
                /* inspect parent loops for conflicted variables */
                if (parent instanceof For || parent instanceof ForeachStatement) {
                    final Set<String> parentVariables = this.getLoopVariables((PhpPsiElement) parent);
                    loopVariables.forEach(variable -> {
                        if (parentVariables.contains(variable)) {
                            holder.registerProblem(loop.getFirstChild(), MessagesPresentationUtil.prefixWithEa(patternOverridesLoopVars.replace("%v%", variable)));
                        }
                    });
                    parentVariables.clear();
                }
                parent = parent.getParent();
            }
            loopVariables.clear();
        }

        @NotNull
        private Set<String> getLoopVariables(@NotNull PhpPsiElement loop) {
            final Set<String> variables = new HashSet<>();
            if (loop instanceof For) {
                /* get variables from assignments */
                Stream.of(((For) loop).getInitialExpressions()).forEach(init -> {
                    if (init instanceof AssignmentExpression) {
                        final PhpPsiElement variable = ((AssignmentExpression) init).getVariable();
                        if (variable instanceof Variable) {
                            final String variableName = variable.getName();
                            if (variableName != null) {
                                variables.add(variableName);
                            }
                        }
                    }
                });
            } else if (loop instanceof ForeachStatement) {
                ((ForeachStatement) loop).getVariables().forEach(variable -> variables.add(variable.getName()));
            }
            return variables;
        }
    };
}
Also used : 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) Set(java.util.Set) OptionsComponent(com.kalessil.phpStorm.phpInspectionsEA.options.OptionsComponent) HashSet(java.util.HashSet) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) Stream(java.util.stream.Stream) PhpFile(com.jetbrains.php.lang.psi.PhpFile) MessagesPresentationUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) javax.swing(javax.swing) PhpFile(com.jetbrains.php.lang.psi.PhpFile) NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PsiElement(com.intellij.psi.PsiElement) HashSet(java.util.HashSet) 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