Search in sources :

Example 6 with PhpTypedElement

use of com.jetbrains.php.lang.psi.elements.PhpTypedElement in project phpinspectionsea by kalessil.

the class ClassInStringContextStrategy method apply.

public static boolean apply(@Nullable PsiElement nonStringOperand, @NotNull ProblemsHolder holder, @NotNull PsiElement expression, @NotNull String classHasNoToStringMessage) {
    if (null == nonStringOperand) {
        return false;
    }
    final Set<String> resolvedTypes = new HashSet<>();
    if (nonStringOperand instanceof PhpTypedElement) {
        final PhpType resolved = OpenapiResolveUtil.resolveType((PhpTypedElement) nonStringOperand, holder.getProject());
        if (resolved != null) {
            resolved.filterUnknown().getTypes().forEach(t -> resolvedTypes.add(Types.getType(t)));
        }
    }
    if (!TypesSemanticsUtil.isNullableObjectInterface(resolvedTypes)) {
        resolvedTypes.clear();
        return false;
    }
    /* collect classes to check if __toString() is there */
    final PhpIndex index = PhpIndex.getInstance(holder.getProject());
    final List<PhpClass> listClasses = new ArrayList<>();
    resolvedTypes.stream().filter(fqn -> fqn.charAt(0) == '\\').forEach(fqn -> listClasses.addAll(OpenapiResolveUtil.resolveClassesAndInterfacesByFQN(fqn, index)));
    resolvedTypes.clear();
    /* check methods, error on first one violated requirements */
    for (final PhpClass clazz : listClasses) {
        if (OpenapiResolveUtil.resolveMethod(clazz, "__toString") == null) {
            holder.registerProblem(expression, MessagesPresentationUtil.prefixWithEa(classHasNoToStringMessage.replace("%class%", clazz.getFQN())), ProblemHighlightType.ERROR);
            break;
        }
    }
    /* terminate inspection, php will call __toString() */
    listClasses.clear();
    return true;
}
Also used : PhpClass(com.jetbrains.php.lang.psi.elements.PhpClass) Types(com.kalessil.phpStorm.phpInspectionsEA.utils.Types) TypesSemanticsUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.TypesSemanticsUtil) Set(java.util.Set) PhpIndex(com.jetbrains.php.PhpIndex) PhpTypedElement(com.jetbrains.php.lang.psi.elements.PhpTypedElement) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) Nullable(org.jetbrains.annotations.Nullable) List(java.util.List) OpenapiResolveUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiResolveUtil) MessagesPresentationUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil) PsiElement(com.intellij.psi.PsiElement) ProblemHighlightType(com.intellij.codeInspection.ProblemHighlightType) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) NotNull(org.jetbrains.annotations.NotNull) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) PhpIndex(com.jetbrains.php.PhpIndex) PhpClass(com.jetbrains.php.lang.psi.elements.PhpClass) ArrayList(java.util.ArrayList) PhpTypedElement(com.jetbrains.php.lang.psi.elements.PhpTypedElement) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) HashSet(java.util.HashSet)

Example 7 with PhpTypedElement

use of com.jetbrains.php.lang.psi.elements.PhpTypedElement in project phpinspectionsea by kalessil.

the class OpAssignShortSyntaxInspector method buildVisitor.

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

        @Override
        public void visitPhpAssignmentExpression(@NotNull AssignmentExpression assignment) {
            final PsiElement value = ExpressionSemanticUtil.getExpressionTroughParenthesis(assignment.getValue());
            if (value instanceof BinaryExpression) {
                final BinaryExpression binary = (BinaryExpression) value;
                final PsiElement operator = binary.getOperation();
                if (operator != null) {
                    final PsiElement left = binary.getLeftOperand();
                    final PsiElement right = binary.getRightOperand();
                    final PsiElement variable = assignment.getVariable();
                    if (variable != null && left != null && right != null) {
                        final IElementType operation = operator.getNode().getElementType();
                        if (mapping.containsKey(operation)) {
                            final LinkedList<PsiElement> fragments = new LinkedList<>();
                            fragments.addLast(right);
                            PsiElement candidate = left;
                            while (candidate instanceof BinaryExpression) {
                                final BinaryExpression current = (BinaryExpression) candidate;
                                final PsiElement rightPart = current.getRightOperand();
                                if (rightPart != null) {
                                    fragments.addLast(rightPart);
                                }
                                if (current.getOperationType() != operation) {
                                    break;
                                }
                                candidate = current.getLeftOperand();
                            }
                            if (candidate != null && OpenapiEquivalenceUtil.areEqual(variable, candidate)) {
                                final boolean canShorten = (fragments.size() == 1 || chainingSafeOperators.contains(operation)) && fragments.stream().noneMatch(f -> f instanceof BinaryExpression);
                                if (canShorten) {
                                    /* false-positives: string elements manipulation, causes a fatal error */
                                    boolean isStringManipulation = false;
                                    if (variable instanceof ArrayAccessExpression) {
                                        final PsiElement stringCandidate = ((ArrayAccessExpression) variable).getValue();
                                        if (stringCandidate instanceof PhpTypedElement) {
                                            final PhpType resolved = OpenapiResolveUtil.resolveType((PhpTypedElement) stringCandidate, holder.getProject());
                                            if (resolved != null && !resolved.hasUnknown()) {
                                                isStringManipulation = resolved.getTypes().stream().anyMatch(t -> Types.getType(t).equals(Types.strString));
                                            }
                                        }
                                    }
                                    if (!isStringManipulation) {
                                        Collections.reverse(fragments);
                                        final String replacement = String.format("%s %s= %s", candidate.getText(), operator.getText(), fragments.stream().map(PsiElement::getText).collect(Collectors.joining(" " + operator.getText() + " ")));
                                        holder.registerProblem(assignment, String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), replacement), new UseShorthandOperatorFix(replacement));
                                    }
                                }
                            }
                            fragments.clear();
                        }
                    }
                }
            }
        }
    };
}
Also used : java.util(java.util) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) IElementType(com.intellij.psi.tree.IElementType) ArrayAccessExpression(com.jetbrains.php.lang.psi.elements.ArrayAccessExpression) AssignmentExpression(com.jetbrains.php.lang.psi.elements.AssignmentExpression) PhpTokenTypes(com.jetbrains.php.lang.lexer.PhpTokenTypes) UseSuggestedReplacementFixer(com.kalessil.phpStorm.phpInspectionsEA.fixers.UseSuggestedReplacementFixer) PhpTypedElement(com.jetbrains.php.lang.psi.elements.PhpTypedElement) Collectors(java.util.stream.Collectors) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) com.kalessil.phpStorm.phpInspectionsEA.utils(com.kalessil.phpStorm.phpInspectionsEA.utils) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) PsiElement(com.intellij.psi.PsiElement) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) NotNull(org.jetbrains.annotations.NotNull) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) 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) AssignmentExpression(com.jetbrains.php.lang.psi.elements.AssignmentExpression) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) PhpTypedElement(com.jetbrains.php.lang.psi.elements.PhpTypedElement) ArrayAccessExpression(com.jetbrains.php.lang.psi.elements.ArrayAccessExpression) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 8 with PhpTypedElement

use of com.jetbrains.php.lang.psi.elements.PhpTypedElement 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 9 with PhpTypedElement

use of com.jetbrains.php.lang.psi.elements.PhpTypedElement in project phpinspectionsea by kalessil.

the class StrlenInEmptyStringCheckContextInspection method buildVisitor.

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

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

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

Aggregations

PsiElement (com.intellij.psi.PsiElement)9 PhpTypedElement (com.jetbrains.php.lang.psi.elements.PhpTypedElement)9 PhpType (com.jetbrains.php.lang.psi.resolve.types.PhpType)9 NotNull (org.jetbrains.annotations.NotNull)7 HashSet (java.util.HashSet)6 ProblemsHolder (com.intellij.codeInspection.ProblemsHolder)5 BasePhpElementVisitor (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor)5 IElementType (com.intellij.psi.tree.IElementType)4 BinaryExpression (com.jetbrains.php.lang.psi.elements.BinaryExpression)4 OpenapiResolveUtil (com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiResolveUtil)4 Set (java.util.Set)4 Project (com.intellij.openapi.project.Project)3 PsiElementVisitor (com.intellij.psi.PsiElementVisitor)3 PhpIndex (com.jetbrains.php.PhpIndex)3 PhpClass (com.jetbrains.php.lang.psi.elements.PhpClass)3 BasePhpInspection (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection)3 MessagesPresentationUtil (com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil)3 Types (com.kalessil.phpStorm.phpInspectionsEA.utils.Types)3 ProblemHighlightType (com.intellij.codeInspection.ProblemHighlightType)2 PhpTokenTypes (com.jetbrains.php.lang.lexer.PhpTokenTypes)2