Search in sources :

Example 51 with PsiElementVisitor

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

the class TypeUnsafeComparisonInspector method buildVisitor.

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

        @Override
        public void visitPhpBinaryExpression(@NotNull BinaryExpression expression) {
            final IElementType operator = expression.getOperationType();
            if (operator == PhpTokenTypes.opEQUAL || operator == PhpTokenTypes.opNOT_EQUAL) {
                this.analyze(expression, operator);
            }
        }

        private void analyze(@NotNull final BinaryExpression subject, @NotNull final IElementType operator) {
            final String targetOperator = PhpTokenTypes.opEQUAL == operator ? "===" : "!==";
            final PsiElement left = subject.getLeftOperand();
            final PsiElement right = subject.getRightOperand();
            if (right instanceof StringLiteralExpression || left instanceof StringLiteralExpression) {
                final PsiElement nonStringOperand;
                final String literalValue;
                if (right instanceof StringLiteralExpression) {
                    literalValue = ((StringLiteralExpression) right).getContents();
                    nonStringOperand = ExpressionSemanticUtil.getExpressionTroughParenthesis(left);
                } else {
                    literalValue = ((StringLiteralExpression) left).getContents();
                    nonStringOperand = ExpressionSemanticUtil.getExpressionTroughParenthesis(right);
                }
                /* resolve 2nd operand type, if class ensure __toString is implemented */
                if (ClassInStringContextStrategy.apply(nonStringOperand, holder, subject, messageToStringMethodMissing)) {
                    /* TODO: weak warning regarding under-the-hood string casting */
                    return;
                }
                /* string literal is numeric or empty, no strict compare possible */
                if (!literalValue.isEmpty() && !literalValue.matches("^[+-]?[0-9]*\\.?[0-9]+$")) {
                    holder.registerProblem(subject, String.format(MessagesPresentationUtil.prefixWithEa(patternCompareStrict), targetOperator), new CompareStrictFix(targetOperator));
                    return;
                }
            }
            /* some objects supporting direct comparison: search for .compare_objects in PHP sources */
            if (left != null && right != null) {
                final boolean isComparableObject = this.isComparableObject(left) || this.isComparableObject(right);
                if (!isComparableObject) {
                    holder.registerProblem(subject, String.format(MessagesPresentationUtil.prefixWithEa(patternHarden), targetOperator), ProblemHighlightType.WEAK_WARNING);
                }
            }
        }

        private boolean isComparableObject(@NotNull PsiElement operand) {
            if (operand instanceof PhpTypedElement) {
                final Project project = holder.getProject();
                final PhpType resolved = OpenapiResolveUtil.resolveType((PhpTypedElement) operand, project);
                if (resolved != null) {
                    final PhpIndex index = PhpIndex.getInstance(project);
                    final Set<PhpClass> classes = new HashSet<>();
                    resolved.filterUnknown().getTypes().stream().filter(t -> t.charAt(0) == '\\').forEach(t -> classes.addAll(OpenapiResolveUtil.resolveClassesAndInterfacesByFQN(Types.getType(t), index)));
                    for (final PhpClass clazz : classes) {
                        final boolean hasAny = comparable.contains(clazz.getFQN()) || InterfacesExtractUtil.getCrawlInheritanceTree(clazz, true).stream().anyMatch(c -> comparable.contains(c.getFQN()));
                        if (hasAny) {
                            classes.clear();
                            return true;
                        }
                    }
                    classes.clear();
                }
            }
            return false;
        }
    };
}
Also used : IElementType(com.intellij.psi.tree.IElementType) PhpTokenTypes(com.jetbrains.php.lang.lexer.PhpTokenTypes) PhpIndex(com.jetbrains.php.PhpIndex) PhpTypedElement(com.jetbrains.php.lang.psi.elements.PhpTypedElement) HashSet(java.util.HashSet) ClassInStringContextStrategy(com.kalessil.phpStorm.phpInspectionsEA.utils.strategy.ClassInStringContextStrategy) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) ProblemDescriptor(com.intellij.codeInspection.ProblemDescriptor) MessagesPresentationUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil) PsiElement(com.intellij.psi.PsiElement) Project(com.intellij.openapi.project.Project) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) LocalQuickFix(com.intellij.codeInspection.LocalQuickFix) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) PhpPsiElementFactory(com.jetbrains.php.lang.psi.PhpPsiElementFactory) PhpClass(com.jetbrains.php.lang.psi.elements.PhpClass) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) ExpressionSemanticUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.ExpressionSemanticUtil) Types(com.kalessil.phpStorm.phpInspectionsEA.utils.Types) InterfacesExtractUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.hierarhy.InterfacesExtractUtil) LeafPsiElement(com.intellij.psi.impl.source.tree.LeafPsiElement) Set(java.util.Set) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) OpenapiResolveUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiResolveUtil) ProblemHighlightType(com.intellij.codeInspection.ProblemHighlightType) StringLiteralExpression(com.jetbrains.php.lang.psi.elements.StringLiteralExpression) NotNull(org.jetbrains.annotations.NotNull) StringLiteralExpression(com.jetbrains.php.lang.psi.elements.StringLiteralExpression) PhpIndex(com.jetbrains.php.PhpIndex) PhpClass(com.jetbrains.php.lang.psi.elements.PhpClass) 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) Project(com.intellij.openapi.project.Project) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) PhpTypedElement(com.jetbrains.php.lang.psi.elements.PhpTypedElement) PsiElement(com.intellij.psi.PsiElement) LeafPsiElement(com.intellij.psi.impl.source.tree.LeafPsiElement) HashSet(java.util.HashSet) NotNull(org.jetbrains.annotations.NotNull)

Example 52 with PsiElementVisitor

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

the class ClassConstantUsageCorrectnessInspector method buildVisitor.

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

        @Override
        public void visitPhpClassConstantReference(@NotNull ClassConstantReference constantReference) {
            final String constantName = constantReference.getName();
            if (constantName != null && constantName.equals("class")) {
                final PsiElement reference = constantReference.getClassReference();
                if (reference instanceof ClassReference) {
                    final ClassReference clazz = (ClassReference) reference;
                    final String referencedQn = reference.getText();
                    final PsiElement resolved = validReferences.contains(referencedQn) ? null : OpenapiResolveUtil.resolveReference(clazz);
                    if (resolved instanceof PhpClass) {
                        /* the resolved class will accumulate case issue in its FQN */
                        final List<String> variants = this.getVariants(clazz, (PhpClass) resolved);
                        if (!variants.isEmpty()) {
                            if (variants.stream().noneMatch(referencedQn::equals)) {
                                holder.registerProblem(reference, MessagesPresentationUtil.prefixWithEa(message));
                            }
                            variants.clear();
                        }
                    }
                }
            }
        }

        @NotNull
        private List<String> getVariants(@NotNull ClassReference reference, @NotNull PhpClass clazz) {
            final List<String> result = new ArrayList<>();
            final String referenceText = reference.getText();
            final String referenceTextLowercase = referenceText.toLowerCase();
            if (referenceText.startsWith("\\")) {
                /* FQN specified, resolve as we might have case issues there */
                final Project project = holder.getProject();
                final Collection<PhpClass> resolved = PhpIndex.getInstance(project).getClassesByFQN(referenceText);
                if (!resolved.isEmpty()) {
                    result.add(resolved.iterator().next().getFQN());
                }
            } else {
                PhpNamespace namespace = null;
                PsiElement current = reference.getParent();
                while (current != null && !(current instanceof PsiFile)) {
                    if (current instanceof PhpNamespace) {
                        namespace = (PhpNamespace) current;
                        break;
                    }
                    current = current.getParent();
                }
                final String classFqn = clazz.getFQN();
                if (referenceText.contains("\\")) {
                    /* RQN specified, check if resolved class in the same NS */
                    final String NsFqn = namespace == null ? null : namespace.getFQN();
                    if (NsFqn != null && !NsFqn.equals("\\")) {
                        final String classFqnLowercase = classFqn.toLowerCase();
                        if (classFqnLowercase.startsWith(NsFqn.toLowerCase()) || classFqnLowercase.endsWith('\\' + referenceTextLowercase)) {
                            result.add(classFqn.substring(classFqn.length() - referenceText.length()));
                        }
                    }
                    /* RQN specified, check if resolved class in aliased NS */
                    final List<PhpUse> uses = new ArrayList<>();
                    if (namespace != null) {
                        final GroupStatement body = namespace.getStatements();
                        if (body != null) {
                            Arrays.stream(body.getStatements()).filter(statement -> statement instanceof PhpUseList).forEach(statement -> Collections.addAll(uses, ((PhpUseList) statement).getDeclarations()));
                        }
                        for (final PhpUse use : uses) {
                            final String alias = use.getAliasName();
                            if (alias != null && referenceTextLowercase.startsWith(alias.toLowerCase())) {
                                final PhpReference targetReference = use.getTargetReference();
                                if (targetReference != null) {
                                    result.add(clazz.getFQN().replace(targetReference.getText(), alias).replaceAll("^\\\\", ""));
                                }
                            }
                        }
                    }
                    uses.clear();
                } else {
                    final List<PhpUse> uses = new ArrayList<>();
                    if (namespace != null) {
                        /* find imports inside know namespace */
                        final GroupStatement body = namespace.getStatements();
                        if (body != null) {
                            Arrays.stream(body.getStatements()).filter(statement -> statement instanceof PhpUseList).forEach(statement -> Collections.addAll(uses, ((PhpUseList) statement).getDeclarations()));
                        }
                    } else {
                        final PsiFile file = reference.getContainingFile();
                        if (file instanceof PhpFile) {
                            /* find imports inside a file without namespace */
                            ((PhpFile) file).getTopLevelDefs().values().stream().filter(definition -> definition instanceof PhpUse).forEach(definition -> uses.add((PhpUse) definition));
                        } else {
                            /* fallback, the most greedy strategy */
                            uses.addAll(PsiTreeUtil.findChildrenOfType(current, PhpUse.class));
                        }
                    }
                    /* imports (incl. aliases) */
                    for (final PhpUse use : uses) {
                        if (use.getFQN().equalsIgnoreCase(classFqn)) {
                            final String alias = use.getAliasName();
                            final PsiElement what = use.getFirstChild();
                            if (alias != null) {
                                /* alias as it is */
                                result.add(alias);
                            } else if (what instanceof ClassReference) {
                                /* resolve the imported class, as its the source for correct naming */
                                final PsiElement resolved = OpenapiResolveUtil.resolveReference((ClassReference) what);
                                if (resolved instanceof PhpClass) {
                                    final PhpClass resolvedImport = (PhpClass) resolved;
                                    final boolean importPrecise = resolvedImport.getFQN().endsWith(what.getText());
                                    if (!importPrecise || !resolvedImport.getName().equals(referenceText)) {
                                        result.add(resolvedImport.getFQN());
                                    }
                                }
                            }
                        } else {
                            final String alias = use.getAliasName();
                            if (alias != null && alias.equalsIgnoreCase(referenceText)) {
                                result.add(alias);
                            }
                        }
                    }
                    uses.clear();
                }
            }
            return result;
        }
    };
}
Also used : java.util(java.util) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) com.jetbrains.php.lang.psi.elements(com.jetbrains.php.lang.psi.elements) PhpIndex(com.jetbrains.php.PhpIndex) PsiTreeUtil(com.intellij.psi.util.PsiTreeUtil) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PhpFile(com.jetbrains.php.lang.psi.PhpFile) OpenapiResolveUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiResolveUtil) MessagesPresentationUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil) PsiElement(com.intellij.psi.PsiElement) Project(com.intellij.openapi.project.Project) PsiFile(com.intellij.psi.PsiFile) NotNull(org.jetbrains.annotations.NotNull) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) PhpFile(com.jetbrains.php.lang.psi.PhpFile) NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) Project(com.intellij.openapi.project.Project) PsiFile(com.intellij.psi.PsiFile) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 53 with PsiElementVisitor

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

the class DisconnectedForeachInstructionInspector 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 GroupStatement foreachBody = ExpressionSemanticUtil.getGroupStatement(foreach);
            /* ensure foreach structure is ready for inspection */
            if (foreachBody != null) {
                final PsiElement[] statements = foreachBody.getChildren();
                if (statements.length > 0 && Stream.of(statements).anyMatch(s -> OpenapiTypesUtil.is(s, PhpElementTypes.HTML))) {
                    return;
                }
                /* pre-collect introduced and internally used variables */
                final Set<String> allModifiedVariables = this.collectCurrentAndOuterLoopVariables(foreach);
                final Map<PsiElement, Set<String>> instructionDependencies = new HashMap<>();
                /* iteration 1 - investigate what are dependencies and influence */
                for (final PsiElement oneInstruction : statements) {
                    if (oneInstruction instanceof PhpPsiElement && !(oneInstruction instanceof PsiComment)) {
                        final Set<String> individualDependencies = new HashSet<>();
                        instructionDependencies.put(oneInstruction, individualDependencies);
                        investigateInfluence((PhpPsiElement) oneInstruction, individualDependencies, allModifiedVariables);
                    }
                }
                /* iteration 2 - analyse dependencies */
                for (final PsiElement oneInstruction : statements) {
                    if (oneInstruction instanceof PhpPsiElement && !(oneInstruction instanceof PsiComment)) {
                        boolean isDependOnModified = false;
                        /* check if any dependency is overridden */
                        final Set<String> individualDependencies = instructionDependencies.get(oneInstruction);
                        if (individualDependencies != null && !individualDependencies.isEmpty()) {
                            isDependOnModified = individualDependencies.stream().anyMatch(allModifiedVariables::contains);
                            individualDependencies.clear();
                        }
                        /* verify and report if violation detected */
                        if (!isDependOnModified) {
                            final ExpressionType target = getExpressionType(oneInstruction);
                            if (ExpressionType.NEW != target && ExpressionType.ASSIGNMENT != target && ExpressionType.CLONE != target && ExpressionType.INCREMENT != target && ExpressionType.DECREMENT != target && ExpressionType.DOM_ELEMENT_CREATE != target && ExpressionType.ACCUMULATE_IN_ARRAY != target && ExpressionType.CONTROL_STATEMENTS != target) {
                                /* loops, ifs, switches, try's needs to be reported on keyword, others - complete */
                                final PsiElement reportingTarget = oneInstruction instanceof ControlStatement || oneInstruction instanceof Try || oneInstruction instanceof PhpSwitch ? oneInstruction.getFirstChild() : oneInstruction;
                                /* secure exceptions with '<?= ?>' constructions, false-positives with html */
                                if (!OpenapiTypesUtil.isPhpExpressionImpl(oneInstruction) && oneInstruction.getTextLength() > 0) {
                                    /* inner looping termination/continuation should be taken into account */
                                    final PsiElement loopInterrupter = PsiTreeUtil.findChildOfAnyType(oneInstruction, true, PhpBreak.class, PhpContinue.class, PhpReturn.class, OpenapiPlatformUtil.classes.get("PhpThrow"));
                                    /* operating with variables should be taken into account */
                                    final boolean isVariablesUsed = PsiTreeUtil.findChildOfAnyType(oneInstruction, true, (Class) Variable.class) != null;
                                    if (null == loopInterrupter && isVariablesUsed) {
                                        holder.registerProblem(reportingTarget, MessagesPresentationUtil.prefixWithEa(messageDisconnected));
                                    }
                                }
                            }
                            if (SUGGEST_USING_CLONE && (ExpressionType.DOM_ELEMENT_CREATE == target || ExpressionType.NEW == target)) {
                                holder.registerProblem(oneInstruction, MessagesPresentationUtil.prefixWithEa(messageUseClone));
                            }
                        }
                    }
                }
                /* release containers content */
                allModifiedVariables.clear();
                instructionDependencies.values().forEach(Set::clear);
                instructionDependencies.clear();
            }
        }

        private Set<String> collectCurrentAndOuterLoopVariables(@NotNull ForeachStatement foreach) {
            final Set<String> variables = new HashSet<>();
            PsiElement current = foreach;
            while (current != null && !(current instanceof Function) && !(current instanceof PsiFile)) {
                if (current instanceof ForeachStatement) {
                    ((ForeachStatement) current).getVariables().forEach(v -> variables.add(v.getName()));
                }
                current = current.getParent();
            }
            return variables;
        }

        private void investigateInfluence(@Nullable PhpPsiElement oneInstruction, @NotNull Set<String> individualDependencies, @NotNull Set<String> allModifiedVariables) {
            for (final Variable variable : PsiTreeUtil.findChildrenOfType(oneInstruction, Variable.class)) {
                final String variableName = variable.getName();
                PsiElement valueContainer = variable;
                PsiElement parent = variable.getParent();
                while (parent instanceof FieldReference) {
                    valueContainer = parent;
                    parent = parent.getParent();
                }
                /* a special case: `[] = ` and `array() = ` unboxing */
                if (OpenapiTypesUtil.is(parent, PhpElementTypes.ARRAY_VALUE)) {
                    parent = parent.getParent();
                    if (parent instanceof ArrayCreationExpression) {
                        parent = parent.getParent();
                    }
                }
                final PsiElement grandParent = parent.getParent();
                /* writing into variable */
                if (parent instanceof AssignmentExpression) {
                    /* php-specific `list(...) =` , `[...] =` construction */
                    if (parent instanceof MultiassignmentExpression) {
                        final MultiassignmentExpression assignment = (MultiassignmentExpression) parent;
                        if (assignment.getValue() != variable) {
                            allModifiedVariables.add(variableName);
                            individualDependencies.add(variableName);
                            continue;
                        }
                    } else {
                        final AssignmentExpression assignment = (AssignmentExpression) parent;
                        if (assignment.getVariable() == valueContainer) {
                            /* we are modifying the variable */
                            allModifiedVariables.add(variableName);
                            /* self-assignment and field assignment counted as the variable dependent on itself  */
                            if (assignment instanceof SelfAssignmentExpression || valueContainer instanceof FieldReference) {
                                individualDependencies.add(variableName);
                            }
                            /* assignments as call arguments counted as the variable dependent on itself */
                            if (grandParent instanceof ParameterList) {
                                individualDependencies.add(variableName);
                            }
                            continue;
                        }
                    }
                }
                /* adding into an arrays; we both depend and modify the container */
                if (parent instanceof ArrayAccessExpression && valueContainer == ((ArrayAccessExpression) parent).getValue()) {
                    allModifiedVariables.add(variableName);
                    individualDependencies.add(variableName);
                }
                if (parent instanceof ParameterList) {
                    if (grandParent instanceof MethodReference) {
                        /* an object consumes the variable, perhaps modification takes place */
                        final MethodReference reference = (MethodReference) grandParent;
                        final PsiElement referenceOperator = OpenapiPsiSearchUtil.findResolutionOperator(reference);
                        if (OpenapiTypesUtil.is(referenceOperator, PhpTokenTypes.ARROW)) {
                            final PsiElement variableCandidate = reference.getFirstPsiChild();
                            if (variableCandidate instanceof Variable) {
                                allModifiedVariables.add(((Variable) variableCandidate).getName());
                                continue;
                            }
                        }
                    } else if (OpenapiTypesUtil.isFunctionReference(grandParent)) {
                        /* php will create variable, if it is by reference */
                        final FunctionReference reference = (FunctionReference) grandParent;
                        final int position = ArrayUtils.indexOf(reference.getParameters(), variable);
                        if (position != -1) {
                            final PsiElement resolved = OpenapiResolveUtil.resolveReference(reference);
                            if (resolved instanceof Function) {
                                final Parameter[] parameters = ((Function) resolved).getParameters();
                                if (parameters.length > position && parameters[position].isPassByRef()) {
                                    allModifiedVariables.add(variableName);
                                    individualDependencies.add(variableName);
                                    continue;
                                }
                            }
                        }
                    }
                }
                /* increment/decrement are also write operations */
                final ExpressionType type = this.getExpressionType(parent);
                if (ExpressionType.INCREMENT == type || ExpressionType.DECREMENT == type) {
                    allModifiedVariables.add(variableName);
                    individualDependencies.add(variableName);
                    continue;
                }
                /* TODO: lookup for array access and property access */
                individualDependencies.add(variableName);
            }
            /* handle compact function usage */
            for (final FunctionReference reference : PsiTreeUtil.findChildrenOfType(oneInstruction, FunctionReference.class)) {
                if (OpenapiTypesUtil.isFunctionReference(reference)) {
                    final String functionName = reference.getName();
                    if (functionName != null && functionName.equals("compact")) {
                        for (final PsiElement argument : reference.getParameters()) {
                            if (argument instanceof StringLiteralExpression) {
                                final String compactedVariableName = ((StringLiteralExpression) argument).getContents();
                                if (!compactedVariableName.isEmpty()) {
                                    individualDependencies.add(compactedVariableName);
                                }
                            }
                        }
                    }
                }
            }
        }

        @NotNull
        private ExpressionType getExpressionType(@Nullable PsiElement expression) {
            if (expression instanceof PhpBreak || expression instanceof PhpContinue || expression instanceof PhpReturn) {
                return ExpressionType.CONTROL_STATEMENTS;
            }
            /* regular '...;' statements */
            if (OpenapiTypesUtil.isStatementImpl(expression)) {
                return getExpressionType(((Statement) expression).getFirstPsiChild());
            }
            /* unary operations */
            if (expression instanceof UnaryExpression) {
                final PsiElement operation = ((UnaryExpression) expression).getOperation();
                if (OpenapiTypesUtil.is(operation, PhpTokenTypes.opINCREMENT)) {
                    return ExpressionType.INCREMENT;
                }
                if (OpenapiTypesUtil.is(operation, PhpTokenTypes.opDECREMENT)) {
                    return ExpressionType.DECREMENT;
                }
            }
            /* different types of assignments */
            if (expression instanceof AssignmentExpression) {
                final AssignmentExpression assignment = (AssignmentExpression) expression;
                final PsiElement variable = assignment.getVariable();
                if (variable instanceof Variable) {
                    final PsiElement value = assignment.getValue();
                    if (value instanceof NewExpression) {
                        return ExpressionType.NEW;
                    } else if (value instanceof UnaryExpression) {
                        if (OpenapiTypesUtil.is(((UnaryExpression) value).getOperation(), PhpTokenTypes.kwCLONE)) {
                            return ExpressionType.CLONE;
                        }
                    } else if (value instanceof MethodReference) {
                        final MethodReference call = (MethodReference) value;
                        final String methodName = call.getName();
                        if (methodName != null && methodName.equals("createElement")) {
                            final PsiElement resolved = OpenapiResolveUtil.resolveReference(call);
                            if (resolved instanceof Method && ((Method) resolved).getFQN().equals("\\DOMDocument.createElement")) {
                                return ExpressionType.DOM_ELEMENT_CREATE;
                            }
                        }
                    }
                    /* allow all assignations afterwards */
                    return ExpressionType.ASSIGNMENT;
                }
                /* accumulating something in external container */
                if (variable instanceof ArrayAccessExpression) {
                    final ArrayAccessExpression storage = (ArrayAccessExpression) variable;
                    if (null == storage.getIndex() || null == storage.getIndex().getValue()) {
                        return ExpressionType.ACCUMULATE_IN_ARRAY;
                    }
                }
            }
            return ExpressionType.OTHER;
        }
    };
}
Also used : BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) com.jetbrains.php.lang.psi.elements(com.jetbrains.php.lang.psi.elements) PhpTokenTypes(com.jetbrains.php.lang.lexer.PhpTokenTypes) Set(java.util.Set) HashMap(java.util.HashMap) OptionsComponent(com.kalessil.phpStorm.phpInspectionsEA.options.OptionsComponent) HashSet(java.util.HashSet) Nullable(org.jetbrains.annotations.Nullable) PsiTreeUtil(com.intellij.psi.util.PsiTreeUtil) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) com.kalessil.phpStorm.phpInspectionsEA.utils(com.kalessil.phpStorm.phpInspectionsEA.utils) Stream(java.util.stream.Stream) Map(java.util.Map) PsiComment(com.intellij.psi.PsiComment) PsiElement(com.intellij.psi.PsiElement) PsiFile(com.intellij.psi.PsiFile) NotNull(org.jetbrains.annotations.NotNull) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) PhpElementTypes(com.jetbrains.php.lang.parser.PhpElementTypes) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) ArrayUtils(org.apache.commons.lang.ArrayUtils) javax.swing(javax.swing) Set(java.util.Set) HashSet(java.util.HashSet) HashMap(java.util.HashMap) NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PsiFile(com.intellij.psi.PsiFile) PsiElement(com.intellij.psi.PsiElement) HashSet(java.util.HashSet) PsiComment(com.intellij.psi.PsiComment) Nullable(org.jetbrains.annotations.Nullable) NotNull(org.jetbrains.annotations.NotNull)

Example 54 with PsiElementVisitor

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

the class MockingMethodsCorrectnessInspector method buildVisitor.

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

        @Override
        public void visitPhpMethodReference(@NotNull MethodReference reference) {
            final String methodName = reference.getName();
            if (methodName != null) {
                if (methodName.equals("willReturn")) {
                    final PsiElement[] arguments = reference.getParameters();
                    if (arguments.length == 1 && arguments[0] instanceof MethodReference) {
                        final String innerMethodName = ((MethodReference) arguments[0]).getName();
                        if (innerMethodName != null) {
                            final boolean isTarget = innerMethodName.equals("returnCallback") || innerMethodName.equals("returnValue");
                            if (isTarget && this.isTestContext(reference)) {
                                final PsiElement nameNode = NamedElementUtil.getNameIdentifier(reference);
                                if (nameNode != null) {
                                    holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(messageWillMethod), new UseWillMethodFix());
                                }
                            }
                        }
                    }
                } else if (methodName.equals("method")) {
                    final PsiElement[] arguments = reference.getParameters();
                    if (arguments.length == 1 && arguments[0] instanceof StringLiteralExpression) {
                        PhpPsiElement mock = reference.getFirstPsiChild();
                        /* Handle following construct (->expects())->method('non-existing') */
                        if (mock instanceof MethodReference && "expects".equals(mock.getName())) {
                            mock = mock.getFirstPsiChild();
                        }
                        if (mock != null && this.isTestContext(reference)) {
                            this.checkIfMockHasMethod(mock, (StringLiteralExpression) arguments[0]);
                        }
                    }
                }
            }
        }

        private void checkIfMockHasMethod(@NotNull PsiElement mock, @NotNull StringLiteralExpression methodName) {
            final Set<PsiElement> variants = PossibleValuesDiscoveryUtil.discover(mock);
            if (variants.size() == 1) {
                /* Handle following construct ->getMockBuilder(::class)->getMock() +  */
                final PsiElement source = variants.iterator().next();
                if (source instanceof MethodReference && "getMock".equals(((MethodReference) source).getName())) {
                    final Optional<MethodReference> builder = PsiTreeUtil.findChildrenOfType(source, MethodReference.class).stream().filter(reference -> "getMockBuilder".equals(reference.getName())).findFirst();
                    if (builder.isPresent()) {
                        /* find out if the method has been set with setMethods() method */
                        final Optional<MethodReference> methodsSetter = PsiTreeUtil.findChildrenOfType(source, MethodReference.class).stream().filter(reference -> "setMethods".equals(reference.getName())).findFirst();
                        if (methodsSetter.isPresent()) {
                            final PsiElement[] methodSetterArguments = methodsSetter.get().getParameters();
                            if (methodSetterArguments.length > 0 && methodSetterArguments[0] instanceof ArrayCreationExpression) {
                                final boolean isSet = PsiTreeUtil.findChildrenOfType(methodSetterArguments[0], StringLiteralExpression.class).stream().anyMatch(l -> OpenapiEquivalenceUtil.areEqual(l, methodName));
                                if (isSet) {
                                    return;
                                }
                            }
                        }
                        /* find out which class it was and check if it has the method */
                        final PsiElement[] builderArguments = builder.get().getParameters();
                        if (builderArguments.length == 1 && builderArguments[0] instanceof ClassConstantReference) {
                            final ClassConstantReference clazz = (ClassConstantReference) builderArguments[0];
                            if ("class".equals(clazz.getName())) {
                                final PsiElement classReference = clazz.getClassReference();
                                if (classReference instanceof ClassReference) {
                                    final PsiElement resolved = OpenapiResolveUtil.resolveReference((ClassReference) classReference);
                                    if (resolved instanceof PhpClass) {
                                        final Method method = OpenapiResolveUtil.resolveMethod((PhpClass) resolved, methodName.getContents());
                                        if (method == null) {
                                            holder.registerProblem(methodName, MessagesPresentationUtil.prefixWithEa(messageUnresolvedMethod), ProblemHighlightType.GENERIC_ERROR);
                                        } else if (method.isFinal()) {
                                            holder.registerProblem(methodName, MessagesPresentationUtil.prefixWithEa(messageFinalMethod), ProblemHighlightType.GENERIC_ERROR);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            variants.clear();
        }
    };
}
Also used : BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) com.jetbrains.php.lang.psi.elements(com.jetbrains.php.lang.psi.elements) Set(java.util.Set) PsiTreeUtil(com.intellij.psi.util.PsiTreeUtil) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) com.kalessil.phpStorm.phpInspectionsEA.utils(com.kalessil.phpStorm.phpInspectionsEA.utils) ProblemDescriptor(com.intellij.codeInspection.ProblemDescriptor) PsiElement(com.intellij.psi.PsiElement) 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) NotNull(org.jetbrains.annotations.NotNull)

Example 55 with PsiElementVisitor

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

the class MisorderedModifiersInspector method buildVisitor.

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

        @Override
        public void visitPhpMethod(@NotNull Method method) {
            if (method.isStatic() || method.isAbstract() || method.isFinal()) {
                final PhpModifierList modifiersNode = PsiTreeUtil.findChildOfType(method, PhpModifierList.class);
                final List<LeafPsiElement> modifiers = PsiTreeUtil.findChildrenOfType(modifiersNode, LeafPsiElement.class).stream().filter(element -> !(element instanceof PsiWhiteSpace)).collect(Collectors.toList());
                if (modifiersNode != null && modifiers.size() >= 2) {
                    final String original = this.getOriginalOrder(modifiers);
                    final String expected = this.getExpectedOrder(original, standardOrder);
                    if (!original.equals(expected)) {
                        problemsHolder.registerProblem(modifiersNode, MessagesPresentationUtil.prefixWithEa(message), new TheLocalFix(expected));
                    }
                }
            }
        }

        @NotNull
        private String getOriginalOrder(@NotNull Collection<LeafPsiElement> original) {
            return original.stream().map(LeafElement::getText).collect(Collectors.joining(" ")).toLowerCase();
        }

        @NotNull
        private String getExpectedOrder(@NotNull String original, @NotNull Collection<String> expected) {
            return expected.stream().filter(original::contains).collect(Collectors.joining(" "));
        }
    };
}
Also used : PhpPsiElementFactory(com.jetbrains.php.lang.psi.PhpPsiElementFactory) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) Collection(java.util.Collection) LeafPsiElement(com.intellij.psi.impl.source.tree.LeafPsiElement) Method(com.jetbrains.php.lang.psi.elements.Method) Collectors(java.util.stream.Collectors) ArrayList(java.util.ArrayList) PsiTreeUtil(com.intellij.psi.util.PsiTreeUtil) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) List(java.util.List) ProblemDescriptor(com.intellij.codeInspection.ProblemDescriptor) LeafElement(com.intellij.psi.impl.source.tree.LeafElement) MessagesPresentationUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil) PsiElement(com.intellij.psi.PsiElement) PsiWhiteSpace(com.intellij.psi.PsiWhiteSpace) 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) PhpModifierList(com.jetbrains.php.lang.psi.elements.PhpModifierList) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) LeafPsiElement(com.intellij.psi.impl.source.tree.LeafPsiElement) Collection(java.util.Collection) Method(com.jetbrains.php.lang.psi.elements.Method) NotNull(org.jetbrains.annotations.NotNull) LeafElement(com.intellij.psi.impl.source.tree.LeafElement) PhpModifierList(com.jetbrains.php.lang.psi.elements.PhpModifierList) PsiWhiteSpace(com.intellij.psi.PsiWhiteSpace) 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