Search in sources :

Example 56 with PsiElementVisitor

use of com.intellij.psi.PsiElementVisitor in project phpinspectionsea by kalessil.

the class ClassMockingCorrectnessInspector method buildVisitor.

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

        @Override
        public void visitPhpClass(@NotNull PhpClass clazz) {
            final PhpClass parent = OpenapiResolveUtil.resolveSuperClass(clazz);
            if (parent != null && parent.getFQN().equals("\\PhpSpec\\ObjectBehavior")) {
                for (final Method method : clazz.getOwnMethods()) {
                    for (final Parameter parameter : method.getParameters()) {
                        /* Since PS 2020.2 union types are introduced (BC-incompatible PSI-changes). Hence traversing node. */
                        final Collection<ClassReference> references = PsiTreeUtil.findChildrenOfType(parameter, ClassReference.class);
                        if (!references.isEmpty()) {
                            for (final ClassReference reference : references) {
                                final PsiElement resolved = OpenapiResolveUtil.resolveReference(reference);
                                if (resolved instanceof PhpClass && ((PhpClass) resolved).isFinal()) {
                                    holder.registerProblem(reference, MessagesPresentationUtil.prefixWithEa(messageFinal));
                                    break;
                                }
                            }
                            references.clear();
                        }
                    }
                }
            }
        }

        @Override
        public void visitPhpMethodReference(@NotNull MethodReference reference) {
            final String methodName = reference.getName();
            final PsiElement[] arguments = reference.getParameters();
            if (methodName != null && arguments.length > 0 && methods.containsValue(methodName)) {
                final PsiElement resolved = OpenapiResolveUtil.resolveReference(reference);
                if (resolved instanceof Method && methods.get(((Method) resolved).getFQN()) != null) {
                    final PhpClass referencedClass = this.getClass(arguments[0]);
                    if (referencedClass != null) {
                        if (methodName.equals("createMock")) {
                            if (referencedClass.isTrait()) {
                                holder.registerProblem(arguments[0], MessagesPresentationUtil.prefixWithEa(messageTrait));
                            } else if (referencedClass.isFinal()) {
                                holder.registerProblem(arguments[0], MessagesPresentationUtil.prefixWithEa(messageFinal));
                            }
                        } else if (methodName.equals("getMockBuilder")) {
                            final PsiElement parent = reference.getParent();
                            String parentName = null;
                            if (parent instanceof MethodReference) {
                                parentName = ((MethodReference) parent).getName();
                            }
                            /* classes might need different mocking methods usage */
                            if (referencedClass.isAbstract() && !referencedClass.isInterface()) {
                                if (parentName == null) {
                                    holder.registerProblem(arguments[0], MessagesPresentationUtil.prefixWithEa(messageMockAbstract));
                                }
                            } else if (referencedClass.isTrait()) {
                                if (parentName == null) {
                                    holder.registerProblem(arguments[0], MessagesPresentationUtil.prefixWithEa(messageMockTrait));
                                }
                            } else if (referencedClass.isFinal()) {
                                holder.registerProblem(arguments[0], MessagesPresentationUtil.prefixWithEa(messageFinal));
                            }
                            /* constructor might require arguments */
                            if (parentName != null && parentName.equals("getMock")) {
                                final Method constructor = referencedClass.getConstructor();
                                if (constructor != null) {
                                    final boolean needsArguments = Arrays.stream(constructor.getParameters()).anyMatch(parameter -> parameter.getDefaultValue() == null);
                                    if (needsArguments) {
                                        holder.registerProblem(arguments[0], MessagesPresentationUtil.prefixWithEa(messageMockConstructor));
                                    }
                                }
                            }
                        } else if (methodName.equals("getMockForTrait")) {
                            if (!referencedClass.isTrait()) {
                                holder.registerProblem(arguments[0], MessagesPresentationUtil.prefixWithEa(messageNeedsTrait));
                            }
                        } else if (methodName.equals("getMockForAbstractClass")) {
                            if (!referencedClass.isAbstract()) {
                                holder.registerProblem(arguments[0], MessagesPresentationUtil.prefixWithEa(messageNeedsAbstract));
                            }
                        } else {
                            if (referencedClass.isFinal()) {
                                holder.registerProblem(arguments[0], MessagesPresentationUtil.prefixWithEa(messageFinal));
                            }
                        }
                    }
                }
            }
        }

        @Nullable
        private PhpClass getClass(@NotNull PsiElement expression) {
            PhpClass result = null;
            if (expression instanceof ClassConstantReference) {
                final ClassConstantReference reference = (ClassConstantReference) expression;
                final String constantName = reference.getName();
                if (constantName != null && constantName.equals("class")) {
                    final PhpExpression classReference = reference.getClassReference();
                    if (classReference instanceof ClassReference) {
                        final PsiElement resolved = OpenapiResolveUtil.resolveReference((ClassReference) classReference);
                        result = resolved instanceof PhpClass ? (PhpClass) resolved : null;
                    }
                }
            } else if (expression instanceof StringLiteralExpression) {
                final StringLiteralExpression string = (StringLiteralExpression) expression;
                final String contents = string.getContents();
                if (string.getFirstPsiChild() == null && contents.length() > 3) {
                    String fqn = contents.replaceAll("\\\\\\\\", "\\\\");
                    fqn = fqn.charAt(0) == '\\' ? fqn : '\\' + fqn;
                    final PhpIndex index = PhpIndex.getInstance(holder.getProject());
                    for (final PhpClass clazz : OpenapiResolveUtil.resolveClassesByFQN(fqn, index)) {
                        if (clazz.isFinal()) {
                            result = clazz;
                            break;
                        }
                    }
                }
            }
            /* TODO: handle __NAMESPACE__.'\Class' */
            return result;
        }
    };
}
Also used : Arrays(java.util.Arrays) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) com.jetbrains.php.lang.psi.elements(com.jetbrains.php.lang.psi.elements) Collection(java.util.Collection) HashMap(java.util.HashMap) PhpIndex(com.jetbrains.php.PhpIndex) Nullable(org.jetbrains.annotations.Nullable) PsiTreeUtil(com.intellij.psi.util.PsiTreeUtil) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) OpenapiResolveUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiResolveUtil) MessagesPresentationUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil) Map(java.util.Map) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) PhpIndex(com.jetbrains.php.PhpIndex) NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 57 with PsiElementVisitor

use of com.intellij.psi.PsiElementVisitor in project phpinspectionsea by kalessil.

the class SelfClassReferencingInspector method buildVisitor.

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

        @Override
        public void visitPhpMethod(@NotNull Method method) {
            final PhpClass clazz = method.getContainingClass();
            if (clazz != null && !clazz.isAnonymous() && !clazz.isTrait() && !method.isAbstract()) {
                final String targetReference = PREFER_CLASS_NAMES ? "self" : clazz.getName();
                final String targetReplacement = PREFER_CLASS_NAMES ? clazz.getName() : "self";
                final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(method);
                PsiTreeUtil.findChildrenOfType(body, ClassReference.class).stream().filter(reference -> targetReference.equals(reference.getName()) && method == PsiTreeUtil.getParentOfType(reference, Function.class) && clazz == OpenapiResolveUtil.resolveReference(reference)).forEach(reference -> {
                    final PsiElement parent = reference.getParent();
                    if (!PREFER_CLASS_NAMES && parent instanceof ClassConstantReference) {
                        final String constantName = ((ClassConstantReference) parent).getName();
                        if (constantName != null && constantName.equals("class")) {
                            final String replacement = "__CLASS__";
                            problemsHolder.registerProblem(parent, MessagesPresentationUtil.prefixWithEa(String.format(messagePattern, parent.getText(), replacement)), new NormalizeReferenceFix(replacement));
                            return;
                        }
                    }
                    if (!(parent instanceof ExtendsList)) {
                        problemsHolder.registerProblem(reference, MessagesPresentationUtil.prefixWithEa(String.format(messagePattern, targetReference, targetReplacement)), new NormalizeReferenceFix(targetReplacement));
                    }
                });
                if (PREFER_CLASS_NAMES) {
                    PsiTreeUtil.findChildrenOfType(body, ConstantReference.class).stream().filter(reference -> "__CLASS__".equals(reference.getName()) && method == PsiTreeUtil.getParentOfType(reference, Function.class)).forEach(reference -> {
                        final String replacement = targetReplacement + "::class";
                        problemsHolder.registerProblem(reference, MessagesPresentationUtil.prefixWithEa(String.format(messagePattern, reference.getText(), replacement)), new NormalizeReferenceFix(replacement));
                    });
                }
            }
        }
    };
}
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) OptionsComponent(com.kalessil.phpStorm.phpInspectionsEA.options.OptionsComponent) PsiTreeUtil(com.intellij.psi.util.PsiTreeUtil) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) OpenapiResolveUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiResolveUtil) ProblemDescriptor(com.intellij.codeInspection.ProblemDescriptor) MessagesPresentationUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil) PsiElement(com.intellij.psi.PsiElement) Project(com.intellij.openapi.project.Project) NotNull(org.jetbrains.annotations.NotNull) LocalQuickFix(com.intellij.codeInspection.LocalQuickFix) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) javax.swing(javax.swing) NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 58 with PsiElementVisitor

use of com.intellij.psi.PsiElementVisitor in project phpinspectionsea by kalessil.

the class SlowArrayOperationsInLoopInspector 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 && greedyFunctions.contains(functionName)) {
                final PsiElement[] arguments = reference.getParameters();
                if (arguments.length > 1 && !(arguments[0] instanceof ArrayAccessExpression)) {
                    PsiElement parent = reference.getParent();
                    if (parent instanceof AssignmentExpression) {
                        /* false-positives: return/break as last group statement expression */
                        boolean canLoop = true;
                        if (OpenapiTypesUtil.isStatementImpl(parent = parent.getParent())) {
                            final PsiElement grandParent = parent.getParent();
                            if (grandParent instanceof GroupStatement) {
                                final PsiElement last = ExpressionSemanticUtil.getLastStatement((GroupStatement) grandParent);
                                canLoop = !(last instanceof PhpBreak) && !(last instanceof PhpReturn);
                            }
                        }
                        while (canLoop && parent != null && !(parent instanceof PhpFile) && !(parent instanceof Function)) {
                            if (OpenapiTypesUtil.isLoop(parent)) {
                                final PsiElement container = ((AssignmentExpression) reference.getParent()).getVariable();
                                if (container != null) {
                                    for (final PsiElement argument : arguments) {
                                        if (OpenapiEquivalenceUtil.areEqual(container, argument)) {
                                            holder.registerProblem(reference, String.format(MessagesPresentationUtil.prefixWithEa(messageGreedyPattern), functionName));
                                            return;
                                        }
                                    }
                                }
                            }
                            parent = parent.getParent();
                        }
                    }
                }
            }
        }

        @Override
        public void visitPhpFor(@NotNull For forStatement) {
            final Set<FunctionReference> references = new HashSet<>();
            Arrays.stream(forStatement.getConditionalExpressions()).forEach(c -> {
                if (c instanceof BinaryExpression) {
                    final BinaryExpression binary = (BinaryExpression) c;
                    Stream.of(binary.getLeftOperand(), binary.getRightOperand()).filter(p -> p instanceof FunctionReference).forEach(p -> references.add((FunctionReference) p));
                }
            });
            references.stream().filter(OpenapiTypesUtil::isFunctionReference).forEach(r -> {
                final String functionName = r.getName();
                if (functionName != null && slowFunctions.contains(functionName)) {
                    final BinaryExpression condition = (BinaryExpression) r.getParent();
                    holder.registerProblem(condition, String.format(MessagesPresentationUtil.prefixWithEa(messageSlowPattern), functionName), ProblemHighlightType.GENERIC_ERROR, new ReduceRepetitiveCallsInForFix(holder.getProject(), forStatement, condition));
                }
            });
            references.clear();
        }
    };
}
Also used : Arrays(java.util.Arrays) com.jetbrains.php.lang.psi.elements(com.jetbrains.php.lang.psi.elements) PhpTokenTypes(com.jetbrains.php.lang.lexer.PhpTokenTypes) HashSet(java.util.HashSet) PsiTreeUtil(com.intellij.psi.util.PsiTreeUtil) OpenapiTypesUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiTypesUtil) ProblemDescriptor(com.intellij.codeInspection.ProblemDescriptor) MessagesPresentationUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil) OpenapiEquivalenceUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiEquivalenceUtil) PsiElement(com.intellij.psi.PsiElement) Project(com.intellij.openapi.project.Project) LocalQuickFix(com.intellij.codeInspection.LocalQuickFix) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) PhpPsiElementFactory(com.jetbrains.php.lang.psi.PhpPsiElementFactory) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) ExpressionSemanticUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.ExpressionSemanticUtil) LeafPsiElement(com.intellij.psi.impl.source.tree.LeafPsiElement) Set(java.util.Set) SmartPointerManager(com.intellij.psi.SmartPointerManager) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) Stream(java.util.stream.Stream) PhpFile(com.jetbrains.php.lang.psi.PhpFile) SmartPsiElementPointer(com.intellij.psi.SmartPsiElementPointer) ProblemHighlightType(com.intellij.codeInspection.ProblemHighlightType) NotNull(org.jetbrains.annotations.NotNull) PhpFile(com.jetbrains.php.lang.psi.PhpFile) NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PsiElement(com.intellij.psi.PsiElement) LeafPsiElement(com.intellij.psi.impl.source.tree.LeafPsiElement) HashSet(java.util.HashSet) NotNull(org.jetbrains.annotations.NotNull)

Example 59 with PsiElementVisitor

use of com.intellij.psi.PsiElementVisitor in project phpinspectionsea by kalessil.

the class SummerTimeUnsafeTimeManipulationInspector method buildVisitor.

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

        @Override
        public void visitPhpBinaryExpression(@NotNull BinaryExpression expression) {
            if (targetOperations.contains(expression.getOperationType())) {
                final PsiElement left = expression.getLeftOperand();
                final PsiElement right = expression.getRightOperand();
                if (right != null && this.isTargetMagicNumber(right) && this.isTargetContext(right)) {
                    if (!this.isTestContext(expression)) {
                        holder.registerProblem(expression, message);
                    }
                } else if (left != null && this.isTargetMagicNumber(left) && this.isTargetContext(left)) {
                    if (!this.isTestContext(expression)) {
                        holder.registerProblem(expression, message);
                    }
                }
            }
        }

        @Override
        public void visitPhpSelfAssignmentExpression(@NotNull SelfAssignmentExpression expression) {
            if (targetAssignments.contains(expression.getOperationType())) {
                final PsiElement value = expression.getValue();
                if (value != null && this.isTargetMagicNumber(value) && !this.isTestContext(expression)) {
                    holder.registerProblem(expression, message);
                }
            }
        }

        private boolean isTargetContext(@NotNull PsiElement magicNumber) {
            boolean result = magicNumber.textMatches("86400");
            if (!result) {
                PsiElement expression = magicNumber.getParent();
                while (expression instanceof ParenthesizedExpression || expression instanceof BinaryExpression) {
                    expression = expression.getParent();
                }
                result = PsiTreeUtil.findChildrenOfType(expression, PhpExpression.class).stream().filter(OpenapiTypesUtil::isNumber).anyMatch(candidate -> {
                    final String text = candidate.getText();
                    return text.equals("60") || text.endsWith("3600");
                });
            }
            return result;
        }

        private boolean isTargetMagicNumber(@NotNull PsiElement candidate) {
            boolean result = false;
            if (OpenapiTypesUtil.isNumber(candidate)) {
                result = candidate.textMatches("24") || candidate.textMatches("86400");
            }
            return result;
        }
    };
}
Also used : SelfAssignmentExpression(com.jetbrains.php.lang.psi.elements.SelfAssignmentExpression) ParenthesizedExpression(com.jetbrains.php.lang.psi.elements.ParenthesizedExpression) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) IElementType(com.intellij.psi.tree.IElementType) PhpTokenTypes(com.jetbrains.php.lang.lexer.PhpTokenTypes) Set(java.util.Set) SelfAssignmentExpression(com.jetbrains.php.lang.psi.elements.SelfAssignmentExpression) HashSet(java.util.HashSet) PsiTreeUtil(com.intellij.psi.util.PsiTreeUtil) ParenthesizedExpression(com.jetbrains.php.lang.psi.elements.ParenthesizedExpression) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) OpenapiTypesUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiTypesUtil) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) PhpExpression(com.jetbrains.php.lang.psi.elements.PhpExpression) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) OpenapiTypesUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiTypesUtil) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 60 with PsiElementVisitor

use of com.intellij.psi.PsiElementVisitor in project phpinspectionsea by kalessil.

the class ForeachSourceInspector method buildVisitor.

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

        @Override
        public void visitPhpForeach(@NotNull ForeachStatement foreach) {
            final PsiElement source = ExpressionSemanticUtil.getExpressionTroughParenthesis(foreach.getArray());
            if (source instanceof PhpTypedElement && !isEnsuredByPyParentIf(foreach, source)) {
                this.analyseContainer(source);
            }
        }

        /* should cover is_array/is_iterable in direct parent if of the loop, while PS types resolving gets improved */
        private boolean isEnsuredByPyParentIf(@NotNull ForeachStatement foreach, @NotNull PsiElement source) {
            boolean result = false;
            if (foreach.getPrevPsiSibling() == null) {
                final PsiElement ifCandidate = foreach.getParent() instanceof GroupStatement ? foreach.getParent().getParent() : null;
                final PsiElement conditions;
                if (ifCandidate instanceof If) {
                    conditions = ((If) ifCandidate).getCondition();
                } else if (ifCandidate instanceof ElseIf) {
                    conditions = ((ElseIf) ifCandidate).getCondition();
                } else {
                    conditions = null;
                }
                if (conditions != null) {
                    for (final PsiElement candidate : PsiTreeUtil.findChildrenOfType(conditions, source.getClass())) {
                        if (OpeanapiEquivalenceUtil.areEqual(candidate, source)) {
                            final PsiElement call = candidate.getParent() instanceof ParameterList ? candidate.getParent().getParent() : null;
                            if (OpenapiTypesUtil.isFunctionReference(call)) {
                                final String functionName = ((FunctionReference) call).getName();
                                if (functionName != null && (functionName.equals("is_array") || functionName.equals("is_iterable"))) {
                                    result = true;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            return result;
        }

        private void analyseContainer(@NotNull PsiElement container) {
            final PhpType resolvedType = OpenapiResolveUtil.resolveType((PhpTypedElement) container, container.getProject());
            if (resolvedType == null) {
                return;
            }
            final Set<String> types = new HashSet<>();
            resolvedType.filterUnknown().getTypes().forEach(t -> types.add(Types.getType(t)));
            if (types.isEmpty()) {
                /* false-positives: pre-defined variables */
                if (container instanceof Variable) {
                    final String variableName = ((Variable) container).getName();
                    if (ExpressionCostEstimateUtil.predefinedVars.contains(variableName)) {
                        return;
                    }
                }
                if (REPORT_UNRECOGNIZED_TYPES) {
                    holder.registerProblem(container, patternNotRecognized, ProblemHighlightType.WEAK_WARNING);
                }
                return;
            }
            /* false-positives: multiple return types checked only in function/method; no global context */
            final PsiElement scope = ExpressionSemanticUtil.getBlockScope(container);
            if (types.size() > 1 && !(scope instanceof Function)) {
                types.clear();
                return;
            }
            /* false-positives: mixed parameter type, parameter overridden before foreach */
            if (types.size() > 1 && scope instanceof Function && container instanceof Variable) {
                final String parameter = ((Variable) container).getName();
                final PhpEntryPointInstruction start = ((Function) scope).getControlFlow().getEntryPoint();
                final PhpAccessVariableInstruction[] uses = PhpControlFlowUtil.getFollowingVariableAccessInstructions(start, parameter, false);
                for (final PhpAccessVariableInstruction instruction : uses) {
                    final PhpPsiElement expression = instruction.getAnchor();
                    /* when matched itself, stop processing */
                    if (expression == container) {
                        break;
                    }
                    final PsiElement parent = expression.getParent();
                    if (parent instanceof AssignmentExpression) {
                        final PsiElement matchCandidate = ((AssignmentExpression) parent).getVariable();
                        if (matchCandidate != null && OpeanapiEquivalenceUtil.areEqual(matchCandidate, container)) {
                            types.clear();
                            return;
                        }
                    }
                }
            }
            /* false-positives: array type parameter declaration adds mixed */
            if (types.size() > 1 && scope instanceof Function && container instanceof ArrayAccessExpression) {
                final PsiElement candidate = ((ArrayAccessExpression) container).getValue();
                if (candidate instanceof Variable && types.contains(Types.strMixed) && types.contains(Types.strArray)) {
                    types.remove(Types.strMixed);
                }
            }
            /* gracefully request to specify exact types which can appear (mixed, object) */
            if (types.contains(Types.strMixed)) {
                /* false-positive: mixed definitions from stub functions */
                boolean isStubFunction = false;
                if (OpenapiTypesUtil.isFunctionReference(container)) {
                    final PsiElement function = OpenapiResolveUtil.resolveReference((FunctionReference) container);
                    final String filePath = function == null ? null : function.getContainingFile().getVirtualFile().getCanonicalPath();
                    isStubFunction = filePath != null && filePath.contains(".jar!") && filePath.contains("/stubs/");
                }
                /* false-positive: mixed definition from array type */
                if (!isStubFunction && !types.contains(Types.strArray) && REPORT_MIXED_TYPES) {
                    final String message = String.format(patternMixedTypes, Types.strMixed);
                    holder.registerProblem(container, message, ProblemHighlightType.WEAK_WARNING);
                }
                types.remove(Types.strMixed);
            }
            if (types.contains(Types.strObject)) {
                if (REPORT_MIXED_TYPES) {
                    final String message = String.format(patternMixedTypes, Types.strObject);
                    holder.registerProblem(container, message, ProblemHighlightType.WEAK_WARNING);
                }
                types.remove(Types.strObject);
            }
            /* respect patter when returned array and bool|null for indicating failures*/
            if (types.size() == 2 && types.contains(Types.strArray)) {
                types.remove(Types.strBoolean);
                types.remove(Types.strNull);
            }
            /* do not process foreach-compatible types */
            types.remove(Types.strArray);
            types.remove(Types.strIterable);
            types.remove("\\Traversable");
            types.remove("\\Iterator");
            types.remove("\\IteratorAggregate");
            /* don't process mysterious empty set type */
            types.remove(Types.strEmptySet);
            /* iterate rest of types */
            if (!types.isEmpty()) {
                final PhpIndex index = PhpIndex.getInstance(holder.getProject());
                for (final String type : types) {
                    /* report if scalar type is met */
                    if (!type.startsWith("\\")) {
                        holder.registerProblem(container, String.format(patternScalar, type), ProblemHighlightType.GENERIC_ERROR);
                        continue;
                    }
                    /* check classes for the Traversable interface in the inheritance chain */
                    final List<PhpClass> classes = OpenapiResolveUtil.resolveClassesAndInterfacesByFQN(type, index);
                    if (!classes.isEmpty()) {
                        boolean hasTraversable = false;
                        for (final PhpClass clazz : classes) {
                            final Set<PhpClass> interfaces = InterfacesExtractUtil.getCrawlInheritanceTree(clazz, false);
                            if (!interfaces.isEmpty()) {
                                hasTraversable = interfaces.stream().anyMatch(i -> i.getFQN().equals("\\Traversable"));
                                interfaces.clear();
                                if (hasTraversable) {
                                    break;
                                }
                            }
                        }
                        classes.clear();
                        if (!hasTraversable) {
                            holder.registerProblem(container, String.format(patternObject, type));
                        }
                    }
                }
                types.clear();
            }
        }
    };
}
Also used : PhpEntryPointInstruction(com.jetbrains.php.codeInsight.controlFlow.instructions.PhpEntryPointInstruction) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) com.jetbrains.php.lang.psi.elements(com.jetbrains.php.lang.psi.elements) InterfacesExtractUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.hierarhy.InterfacesExtractUtil) Set(java.util.Set) ExpressionCostEstimateUtil(com.kalessil.phpStorm.phpInspectionsEA.inspectors.ifs.utils.ExpressionCostEstimateUtil) PhpIndex(com.jetbrains.php.PhpIndex) OptionsComponent(com.kalessil.phpStorm.phpInspectionsEA.options.OptionsComponent) PhpControlFlowUtil(com.jetbrains.php.codeInsight.controlFlow.PhpControlFlowUtil) HashSet(java.util.HashSet) PsiTreeUtil(com.intellij.psi.util.PsiTreeUtil) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) com.kalessil.phpStorm.phpInspectionsEA.utils(com.kalessil.phpStorm.phpInspectionsEA.utils) List(java.util.List) PsiElement(com.intellij.psi.PsiElement) ProblemHighlightType(com.intellij.codeInspection.ProblemHighlightType) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) NotNull(org.jetbrains.annotations.NotNull) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) PhpAccessVariableInstruction(com.jetbrains.php.codeInsight.controlFlow.instructions.PhpAccessVariableInstruction) javax.swing(javax.swing) PhpEntryPointInstruction(com.jetbrains.php.codeInsight.controlFlow.instructions.PhpEntryPointInstruction) NotNull(org.jetbrains.annotations.NotNull) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PsiElement(com.intellij.psi.PsiElement) HashSet(java.util.HashSet) PhpAccessVariableInstruction(com.jetbrains.php.codeInsight.controlFlow.instructions.PhpAccessVariableInstruction) PhpIndex(com.jetbrains.php.PhpIndex) NotNull(org.jetbrains.annotations.NotNull)

Aggregations

PsiElementVisitor (com.intellij.psi.PsiElementVisitor)60 PsiElement (com.intellij.psi.PsiElement)54 NotNull (org.jetbrains.annotations.NotNull)49 ProblemsHolder (com.intellij.codeInspection.ProblemsHolder)39 BasePhpElementVisitor (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor)37 BasePhpInspection (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection)37 com.jetbrains.php.lang.psi.elements (com.jetbrains.php.lang.psi.elements)27 PsiTreeUtil (com.intellij.psi.util.PsiTreeUtil)21 Project (com.intellij.openapi.project.Project)19 MessagesPresentationUtil (com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil)19 PhpTokenTypes (com.jetbrains.php.lang.lexer.PhpTokenTypes)17 Set (java.util.Set)17 com.kalessil.phpStorm.phpInspectionsEA.utils (com.kalessil.phpStorm.phpInspectionsEA.utils)16 HashSet (java.util.HashSet)16 OptionsComponent (com.kalessil.phpStorm.phpInspectionsEA.options.OptionsComponent)15 javax.swing (javax.swing)15 LocalQuickFix (com.intellij.codeInspection.LocalQuickFix)14 PhpType (com.jetbrains.php.lang.psi.resolve.types.PhpType)14 ProblemDescriptor (com.intellij.codeInspection.ProblemDescriptor)12 ProblemHighlightType (com.intellij.codeInspection.ProblemHighlightType)12