Search in sources :

Example 96 with BasePhpElementVisitor

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

the class MissingArrayInitializationInspector method buildVisitor.

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

        @Override
        public void visitPhpArrayAccessExpression(@NotNull ArrayAccessExpression expression) {
            final ArrayIndex index = expression.getIndex();
            if (index != null && index.getValue() == null) {
                final Function scope = ExpressionSemanticUtil.getScope(expression);
                if (scope != null) {
                    /* identify nesting level */
                    int nestingLevel = 0;
                    PsiElement parent = expression.getParent();
                    while (parent != null && parent != scope) {
                        if (OpenapiTypesUtil.isLoop(parent) && ++nestingLevel >= 2) {
                            break;
                        }
                        parent = parent.getParent();
                    }
                    /* target 2+ nesting levels */
                    if (nestingLevel >= 2) {
                        PsiElement container = expression.getValue();
                        while (container instanceof ArrayAccessExpression) {
                            container = ((ArrayAccessExpression) container).getValue();
                        }
                        if (container instanceof Variable) {
                            final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(scope);
                            if (body != null) {
                                final String variableName = ((Variable) container).getName();
                                /* false-positives: parameters */
                                if (Arrays.stream(scope.getParameters()).anyMatch(p -> p.getName().equals(variableName))) {
                                    return;
                                }
                                /* false-positives: use-variables */
                                final List<Variable> uses = ExpressionSemanticUtil.getUseListVariables(scope);
                                if (uses != null && uses.stream().anyMatch(p -> p.getName().equals(variableName))) {
                                    return;
                                }
                                for (final PsiElement candidate : PsiTreeUtil.findChildrenOfType(body, container.getClass())) {
                                    final PsiElement context = candidate.getParent();
                                    /* a value has been written */
                                    if (context instanceof AssignmentExpression) {
                                        final PsiElement value = ((AssignmentExpression) context).getValue();
                                        if (value != container && OpenapiEquivalenceUtil.areEqual(candidate, container)) {
                                            return;
                                        }
                                    }
                                    /* container initialized by a foreach loop */
                                    if (context instanceof ForeachStatement) {
                                        final boolean areSame = OpenapiEquivalenceUtil.areEqual(candidate, container);
                                        if (areSame) {
                                            return;
                                        }
                                    }
                                }
                                problemsHolder.registerProblem(expression, MessagesPresentationUtil.prefixWithEa(message));
                            }
                        }
                    }
                }
            }
        }
    };
}
Also used : Arrays(java.util.Arrays) 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) PsiTreeUtil(com.intellij.psi.util.PsiTreeUtil) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) OpenapiTypesUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiTypesUtil) List(java.util.List) MessagesPresentationUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil) OpenapiEquivalenceUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiEquivalenceUtil) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull) 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) NotNull(org.jetbrains.annotations.NotNull)

Example 97 with BasePhpElementVisitor

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

the class SuspiciousBinaryOperationInspector method buildVisitor.

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

        @Override
        public void visitPhpBinaryExpression(@NotNull BinaryExpression expression) {
            final Collection<BooleanSupplier> callbacks = new ArrayList<>();
            callbacks.add(() -> InstanceOfTraitStrategy.apply(expression, holder));
            callbacks.add(() -> EqualsInAssignmentContextStrategy.apply(expression, holder));
            callbacks.add(() -> GreaterOrEqualInHashElementStrategy.apply(expression, holder));
            callbacks.add(() -> NullableArgumentComparisonStrategy.apply(expression, holder));
            callbacks.add(() -> IdenticalOperandsStrategy.apply(expression, holder));
            callbacks.add(() -> MisplacedOperatorStrategy.apply(expression, holder));
            callbacks.add(() -> NullCoalescingOperatorCorrectnessStrategy.apply(expression, holder));
            callbacks.add(() -> ConcatenationWithArrayStrategy.apply(expression, holder));
            if (VERIFY_CONSTANTS_IN_CONDITIONS) {
                callbacks.add(() -> HardcodedConstantValuesStrategy.apply(expression, holder));
            }
            if (VERIFY_UNCLEAR_OPERATIONS_PRIORITIES) {
                callbacks.add(() -> UnclearOperationsPriorityStrategy.apply(expression, holder));
            }
            /* run through strategies until the first one fired something */
            for (final BooleanSupplier strategy : callbacks) {
                if (strategy.getAsBoolean()) {
                    break;
                }
            }
            callbacks.clear();
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) ArrayList(java.util.ArrayList) NotNull(org.jetbrains.annotations.NotNull) BooleanSupplier(java.util.function.BooleanSupplier) NotNull(org.jetbrains.annotations.NotNull)

Example 98 with BasePhpElementVisitor

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

the class PassingByReferenceCorrectnessInspector 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.isEmpty() && !skippedFunctionsCache.containsKey(functionName)) {
                final boolean skip = skippedFunctions.contains(functionName) && this.isFromRootNamespace(reference);
                if (!skip && this.hasIncompatibleArguments(reference)) {
                    this.analyze(reference);
                }
            }
        }

        @Override
        public void visitPhpMethodReference(@NotNull MethodReference reference) {
            final String methodName = reference.getName();
            if (methodName != null && !methodName.isEmpty() && this.hasIncompatibleArguments(reference)) {
                this.analyze(reference);
            }
        }

        private boolean hasIncompatibleArguments(@NotNull FunctionReference reference) {
            final PsiElement[] arguments = reference.getParameters();
            if (arguments.length > 0) {
                final boolean supportsNew = PhpLanguageLevel.get(holder.getProject()).below(PhpLanguageLevel.PHP700);
                return !Arrays.stream(arguments).allMatch(a -> a instanceof Variable || (supportsNew && a instanceof NewExpression));
            }
            return false;
        }

        private void analyze(@NotNull FunctionReference reference) {
            final PsiElement resolved = OpenapiResolveUtil.resolveReference(reference);
            if (resolved instanceof Function) {
                final Function function = (Function) resolved;
                final Parameter[] parameters = function.getParameters();
                final PsiElement[] arguments = reference.getParameters();
                /* search for anomalies */
                for (int index = 0, max = Math.min(parameters.length, arguments.length); index < max; ++index) {
                    if (parameters[index].isPassByRef()) {
                        final PsiElement argument = arguments[index];
                        if (argument instanceof FunctionReference && !this.isByReference(argument)) {
                            final PsiElement inner = OpenapiResolveUtil.resolveReference((FunctionReference) argument);
                            if (inner instanceof Function) {
                                final PsiElement name = NamedElementUtil.getNameIdentifier((Function) inner);
                                if (!this.isByReference(name)) {
                                    holder.registerProblem(argument, MessagesPresentationUtil.prefixWithEa(message));
                                }
                            }
                        } else if (argument instanceof NewExpression) {
                            holder.registerProblem(argument, MessagesPresentationUtil.prefixWithEa(message));
                        }
                    }
                }
                /* remember global functions without references */
                if (parameters.length > 0 && OpenapiTypesUtil.isFunctionReference(reference)) {
                    final boolean hasReferences = Arrays.stream(parameters).anyMatch(Parameter::isPassByRef);
                    if (!hasReferences) {
                        final String functionName = function.getName();
                        final boolean isFromRootNamespace = function.getFQN().equals('\\' + functionName);
                        if (isFromRootNamespace) {
                            skippedFunctionsCache.putIfAbsent(functionName, functionName);
                        }
                    }
                }
            }
        }

        private boolean isByReference(@Nullable PsiElement element) {
            boolean result = false;
            if (element != null) {
                PsiElement ampersandCandidate = element.getPrevSibling();
                if (ampersandCandidate instanceof PsiWhiteSpace) {
                    ampersandCandidate = ampersandCandidate.getPrevSibling();
                }
                result = OpenapiTypesUtil.is(ampersandCandidate, PhpTokenTypes.opBIT_AND);
            }
            return result;
        }
    };
}
Also used : Arrays(java.util.Arrays) 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) PhpTokenTypes(com.jetbrains.php.lang.lexer.PhpTokenTypes) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Set(java.util.Set) HashSet(java.util.HashSet) Nullable(org.jetbrains.annotations.Nullable) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) OpenapiTypesUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiTypesUtil) OpenapiResolveUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiResolveUtil) MessagesPresentationUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil) Map(java.util.Map) PsiElement(com.intellij.psi.PsiElement) PsiWhiteSpace(com.intellij.psi.PsiWhiteSpace) NamedElementUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.NamedElementUtil) NotNull(org.jetbrains.annotations.NotNull) 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) Nullable(org.jetbrains.annotations.Nullable) PsiWhiteSpace(com.intellij.psi.PsiWhiteSpace) NotNull(org.jetbrains.annotations.NotNull)

Example 99 with BasePhpElementVisitor

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

the class ClassReImplementsParentInterfaceInspector method buildVisitor.

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

        @Override
        public void visitPhpClass(@NotNull PhpClass clazz) {
            final List<ClassReference> implemented = clazz.getImplementsList().getReferenceElements();
            if (!implemented.isEmpty()) {
                /* resolve own interfaces an maintain relation to original element */
                final Map<PsiElement, PhpClass> ownInterfaces = new LinkedHashMap<>(implemented.size());
                for (final ClassReference reference : implemented) {
                    final PsiElement resolved = OpenapiResolveUtil.resolveReference(reference);
                    if (resolved instanceof PhpClass) {
                        ownInterfaces.put(reference, (PhpClass) resolved);
                    }
                }
                implemented.clear();
                if (!ownInterfaces.isEmpty()) {
                    /* Case: indirect declaration duplication (parent already implements) */
                    final PhpClass parent = OpenapiResolveUtil.resolveSuperClass(clazz);
                    if (parent != null) {
                        final Set<PhpClass> inherited = InterfacesExtractUtil.getCrawlInheritanceTree(parent, false);
                        if (!inherited.isEmpty()) {
                            final Set<PsiElement> processed = new HashSet<>();
                            for (final Map.Entry<PsiElement, PhpClass> entry : ownInterfaces.entrySet()) {
                                final PhpClass ownInterface = entry.getValue();
                                if (inherited.contains(ownInterface) && processed.add(entry.getKey())) {
                                    holder.registerProblem(entry.getKey(), String.format(MessagesPresentationUtil.prefixWithEa(patternIndirectDuplication), ownInterface.getFQN(), parent.getFQN()), new TheLocalFix());
                                }
                            }
                            processed.clear();
                            inherited.clear();
                        }
                    }
                    ownInterfaces.clear();
                }
            }
        }
    };
}
Also used : PhpClass(com.jetbrains.php.lang.psi.elements.PhpClass) NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) ClassReference(com.jetbrains.php.lang.psi.elements.ClassReference) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 100 with BasePhpElementVisitor

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

the class MkdirRaceConditionInspector 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("mkdir")) {
                return;
            }
            final PsiElement[] arguments = reference.getParameters();
            if (arguments.length == 0 || arguments.length > 3) {
                return;
            }
            /* false-positives: test classes and functions not from root NS */
            if (this.isTestContext(reference) || !this.isFromRootNamespace(reference)) {
                return;
            }
            /* ind out expression where the call is contained - quite big set of variations */
            final ExpressionLocateResult searchResult = new ExpressionLocateResult();
            this.locateExpression(reference, searchResult);
            final PsiElement target = searchResult.getReportingTarget();
            if (target == null) {
                return;
            }
            final PsiElement context = target.getParent();
            // case 1: if ([!]mkdir(...) [===|!== true|false])
            if (context instanceof If || OpenapiTypesUtil.isStatementImpl(context)) {
                final List<String> fixerArguments = Arrays.stream(arguments).map(PsiElement::getText).collect(Collectors.toList());
                final String binary = searchResult.isInverted ? patternFailAndCondition : patternFailOrCondition;
                final String messagePattern = (context instanceof If ? binary : patternDirectCall);
                holder.registerProblem(context instanceof If ? target : context, MessagesPresentationUtil.prefixWithEa(String.format(messagePattern, String.join(", ", fixerArguments))), context instanceof If ? new HardenConditionFix(arguments[0], fixerArguments, searchResult.isInverted) : new ThrowExceptionFix(arguments[0], fixerArguments));
            } else // case 2: && and || expressions
            if (context instanceof BinaryExpression) {
                boolean isSecondExistenceCheckExists = false;
                /* false-positive: `... or die` construct */
                BinaryExpression binary = (BinaryExpression) context;
                if (binary.getRightOperand() instanceof PhpExit) {
                    return;
                }
                /* deal with nested conditions */
                final PsiElement parent = binary.getParent();
                if (binary.getRightOperand() == target && parent instanceof BinaryExpression) {
                    binary = (BinaryExpression) parent;
                }
                /* check if following expression contains is_dir */
                final PsiElement candidate = binary.getRightOperand();
                final List<FunctionReference> calls = new ArrayList<>();
                if (candidate instanceof FunctionReference) {
                    calls.add((FunctionReference) candidate);
                }
                calls.addAll(PsiTreeUtil.findChildrenOfType(candidate, FunctionReference.class));
                for (final FunctionReference call : calls) {
                    final String name = call.getName();
                    if (name != null && name.equals("is_dir") && OpenapiTypesUtil.isFunctionReference(call)) {
                        /* TODO: argument needs match as well */
                        isSecondExistenceCheckExists = true;
                        break;
                    }
                }
                calls.clear();
                /* report when needed */
                if (!isSecondExistenceCheckExists) {
                    final List<String> fixerArguments = Arrays.stream(arguments).map(PsiElement::getText).collect(Collectors.toList());
                    final String messagePattern = (PhpTokenTypes.tsSHORT_CIRCUIT_AND_OPS.contains(binary.getOperationType()) ? patternFailAndCondition : patternFailOrCondition);
                    holder.registerProblem(target, MessagesPresentationUtil.prefixWithEa(String.format(messagePattern, String.join(", ", fixerArguments), arguments[0].getText())), new HardenConditionFix(arguments[0], fixerArguments, searchResult.isInverted));
                }
            }
        }

        private void locateExpression(@NotNull PsiElement expression, @NotNull ExpressionLocateResult status) {
            final PsiElement parent = expression.getParent();
            if (parent instanceof If || parent instanceof AssignmentExpression || OpenapiTypesUtil.isStatementImpl(parent)) {
                status.setReportingTarget(expression);
            } else if (parent instanceof ParenthesizedExpression) {
                this.locateExpression(parent, status);
            } else if (parent instanceof UnaryExpression) {
                final PsiElement operation = ((UnaryExpression) parent).getOperation();
                if (operation != null) {
                    if (OpenapiTypesUtil.is(operation, PhpTokenTypes.opNOT)) {
                        status.setInverted(!status.isInverted());
                        this.locateExpression(parent, status);
                    } else if (OpenapiTypesUtil.is(operation, PhpTokenTypes.opSILENCE)) {
                        this.locateExpression(parent, status);
                    }
                }
            } else if (parent instanceof BinaryExpression) {
                final BinaryExpression binary = (BinaryExpression) parent;
                final IElementType operation = binary.getOperationType();
                if (PhpTokenTypes.tsSHORT_CIRCUIT_AND_OPS.contains(operation) || PhpTokenTypes.tsSHORT_CIRCUIT_OR_OPS.contains(operation)) {
                    status.setReportingTarget(expression);
                } else {
                    if (operation == PhpTokenTypes.opIDENTICAL || operation == PhpTokenTypes.opNOT_IDENTICAL) {
                        final PsiElement second = OpenapiElementsUtil.getSecondOperand(binary, expression);
                        if (PhpLanguageUtil.isBoolean(second)) {
                            if (PhpLanguageUtil.isFalse(second)) {
                                status.setInverted(!status.isInverted());
                            }
                            if (operation == PhpTokenTypes.opNOT_IDENTICAL) {
                                status.setInverted(!status.isInverted());
                            }
                        }
                    }
                    this.locateExpression(parent, status);
                }
            }
        }
    };
}
Also used : NotNull(org.jetbrains.annotations.NotNull) IElementType(com.intellij.psi.tree.IElementType) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) ArrayList(java.util.ArrayList) List(java.util.List) 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