Search in sources :

Example 31 with PhpType

use of com.jetbrains.php.lang.psi.resolve.types.PhpType in project phpinspectionsea by kalessil.

the class SenselessProxyMethodInspector method buildVisitor.

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

        @Override
        public void visitPhpClass(@NotNull PhpClass clazz) {
            if (clazz.isInterface() || clazz.isTrait()) {
                return;
            }
            for (final Method method : clazz.getOwnMethods()) {
                final PsiElement methodNameNode = NamedElementUtil.getNameIdentifier(method);
                if (null == methodNameNode || method.isAbstract() || method.getAccess().isPrivate()) {
                    continue;
                }
                /* we expect the method to have just one expression - parent invocation */
                final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(method);
                if (null == body || 1 != ExpressionSemanticUtil.countExpressionsInGroup(body)) {
                    continue;
                }
                final PsiElement lastStatement = ExpressionSemanticUtil.getLastStatement(body);
                if (null == lastStatement) {
                    continue;
                }
                /* parent invocation can be both direct or via return */
                final PsiElement parentReferenceCandidate;
                if (lastStatement instanceof PhpReturn) {
                    parentReferenceCandidate = ExpressionSemanticUtil.getReturnValue((PhpReturn) lastStatement);
                } else {
                    parentReferenceCandidate = lastStatement.getFirstChild();
                }
                if (!(parentReferenceCandidate instanceof MethodReference)) {
                    continue;
                }
                final MethodReference reference = (MethodReference) parentReferenceCandidate;
                final String referenceVariable = reference.getFirstChild().getText().trim();
                final String referenceName = reference.getName();
                if (null == referenceName || !referenceVariable.equals("parent") || !referenceName.equals(method.getName())) {
                    continue;
                }
                final Parameter[] methodParameters = method.getParameters();
                /* ensure no transformations/reordering happens when dispatching parameters */
                final PsiElement[] givenParams = reference.getParameters();
                boolean isDispatchingWithoutModifications = (givenParams.length == methodParameters.length);
                if (isDispatchingWithoutModifications) {
                    /* ensure parameters re-dispatched in the same order and state */
                    for (int index = 0; index < givenParams.length; ++index) {
                        if (!(givenParams[index] instanceof Variable) || !((Variable) givenParams[index]).getName().equals(methodParameters[index].getName())) {
                            isDispatchingWithoutModifications = false;
                            break;
                        }
                    }
                }
                /* ensure no signature changes took place */
                boolean isChangingSignature = false;
                final PsiReference referenceToMethod = reference.getReference();
                if (null != referenceToMethod && isDispatchingWithoutModifications) {
                    final PsiElement referenceResolved = OpenapiResolveUtil.resolveReference(referenceToMethod);
                    if (referenceResolved instanceof Method) {
                        final Method nestedMethod = (Method) referenceResolved;
                        final Parameter[] parentParameters = nestedMethod.getParameters();
                        /* verify amount of parameters, visibility, static, abstract, final */
                        if (parentParameters.length == methodParameters.length && nestedMethod.isAbstract() == method.isAbstract() && nestedMethod.isStatic() == method.isStatic() && nestedMethod.isFinal() == method.isFinal() && nestedMethod.getAccess().equals(method.getAccess())) {
                            /* analyze if parameters definition has been changed (only ignore naming changes) */
                            if (methodParameters.length > 0) {
                                for (int index = 0; index < parentParameters.length; ++index) {
                                    /* by-reference declaration changes: not allowed by PHP, hence not checked */
                                    /* default values changes */
                                    final PsiElement parentDefault = parentParameters[index].getDefaultValue();
                                    final PsiElement methodDefault = methodParameters[index].getDefaultValue();
                                    if ((parentDefault == null || methodDefault == null) && parentDefault != methodDefault) {
                                        isChangingSignature = true;
                                        break;
                                    }
                                    if (methodDefault != null && !OpenapiEquivalenceUtil.areEqual(parentDefault, methodDefault)) {
                                        isChangingSignature = true;
                                        break;
                                    }
                                    /* false-positive: magic constants ARE changing signature  */
                                    if (methodDefault instanceof ConstantReference) {
                                        final String constant = ((ConstantReference) methodDefault).getName();
                                        if (constants.contains(constant)) {
                                            isChangingSignature = true;
                                            break;
                                        }
                                    }
                                    /* type definition changes */
                                    final PhpType parentType = OpenapiResolveUtil.resolveDeclaredType(parentParameters[index]);
                                    final PhpType methodType = OpenapiResolveUtil.resolveDeclaredType(methodParameters[index]);
                                    if (!parentType.equals(methodType)) {
                                        isChangingSignature = true;
                                        break;
                                    }
                                }
                            }
                            /* verify returned type declaration */
                            if (!isChangingSignature) {
                                final PsiElement methodReturn = OpenapiElementsUtil.getReturnType(method);
                                final PsiElement parentReturn = OpenapiElementsUtil.getReturnType(nestedMethod);
                                if (methodReturn != parentReturn) {
                                    isChangingSignature = methodReturn == null || parentReturn == null || !OpenapiEquivalenceUtil.areEqual(methodReturn, parentReturn);
                                }
                            }
                        } else {
                            /* okay obviously changed */
                            isChangingSignature = true;
                        }
                    } else {
                        /* we couldn't resolve parent, so we can't report anything */
                        isChangingSignature = true;
                    }
                }
                /* decide if need to report any issues */
                if (isDispatchingWithoutModifications && !isChangingSignature) {
                    holder.registerProblem(methodNameNode, MessagesPresentationUtil.prefixWithEa(messagePattern.replace("%s%", method.getName())), ProblemHighlightType.WEAK_WARNING, new DropMethodFix());
                }
            }
        }
    };
}
Also used : PsiReference(com.intellij.psi.PsiReference) NotNull(org.jetbrains.annotations.NotNull) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) DropMethodFix(com.kalessil.phpStorm.phpInspectionsEA.fixers.DropMethodFix) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 32 with PhpType

use of com.jetbrains.php.lang.psi.resolve.types.PhpType in project phpinspectionsea by kalessil.

the class NullableVariablesStrategy method isNullableResult.

private static boolean isNullableResult(@NotNull AssignmentExpression assignment, @NotNull Project project) {
    boolean result = false;
    final PsiElement assignmentValue = assignment.getValue();
    /* primary strategy: resolve types and check nullability */
    if (assignmentValue instanceof PhpTypedElement) {
        final PhpType resolved = OpenapiResolveUtil.resolveType((PhpTypedElement) assignmentValue, project);
        if (resolved != null) {
            final Set<String> types = new HashSet<>();
            resolved.filterUnknown().getTypes().forEach(t -> types.add(Types.getType(t)));
            if (types.contains(Types.strNull) || types.contains(Types.strVoid)) {
                types.remove(Types.strNull);
                types.remove(Types.strVoid);
                if (!types.isEmpty()) {
                    result = types.stream().noneMatch(t -> !t.startsWith("\\") && !objectTypes.contains(t));
                }
            }
            types.clear();
        }
    }
    /* secondary strategy: support type specification with `@var <type> <variable>` */
    if (result) {
        final PhpPsiElement variable = assignment.getVariable();
        final PsiElement parent = assignment.getParent();
        if (variable != null && OpenapiTypesUtil.isStatementImpl(parent) && OpenapiTypesUtil.isAssignment(assignment)) {
            final PsiElement previous = ((PhpPsiElement) parent).getPrevPsiSibling();
            if (previous instanceof PhpDocComment) {
                final PhpDocTag[] hints = ((PhpDocComment) previous).getTagElementsByName("@var");
                if (hints.length == 1) {
                    final PhpDocVariable specifiedVariable = PsiTreeUtil.findChildOfType(hints[0], PhpDocVariable.class);
                    if (specifiedVariable != null && specifiedVariable.getName().equals(variable.getName())) {
                        result = Arrays.stream(hints[0].getChildren()).anyMatch(t -> t instanceof PhpDocType && Types.getType(t.getText()).equals(Types.strNull));
                    }
                }
            }
        }
    }
    return result;
}
Also used : PhpDocComment(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment) java.util(java.util) com.jetbrains.php.lang.psi.elements(com.jetbrains.php.lang.psi.elements) IElementType(com.intellij.psi.tree.IElementType) PhpTokenTypes(com.jetbrains.php.lang.lexer.PhpTokenTypes) PhpDocType(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocType) Collectors(java.util.stream.Collectors) PhpDocTag(com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag) Nullable(org.jetbrains.annotations.Nullable) PsiTreeUtil(com.intellij.psi.util.PsiTreeUtil) com.kalessil.phpStorm.phpInspectionsEA.utils(com.kalessil.phpStorm.phpInspectionsEA.utils) PsiElement(com.intellij.psi.PsiElement) Project(com.intellij.openapi.project.Project) PhpDocVariable(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocVariable) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) NotNull(org.jetbrains.annotations.NotNull) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) Condition(com.intellij.openapi.util.Condition) PhpDocType(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocType) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) PhpDocTag(com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag) PhpDocComment(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment) PhpDocVariable(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocVariable) PsiElement(com.intellij.psi.PsiElement)

Example 33 with PhpType

use of com.jetbrains.php.lang.psi.resolve.types.PhpType in project phpinspectionsea by kalessil.

the class StrlenInEmptyStringCheckContextInspection method buildVisitor.

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

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

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

Example 34 with PhpType

use of com.jetbrains.php.lang.psi.resolve.types.PhpType in project phpinspectionsea by kalessil.

the class OpenapiResolveUtil method resolveType.

@Nullable
public static PhpType resolveType(@NotNull PhpTypedElement expression, @NotNull Project project) {
    PhpType result = null;
    try {
        if (expression instanceof StringLiteralExpression) {
            result = new PhpType().add(PhpType.STRING);
        } else if (expression instanceof ConstantReference) {
            final ConstantReference reference = (ConstantReference) expression;
            final String referenceName = reference.getName();
            if (referenceName != null && !referenceName.isEmpty()) {
                final PsiElement resolved = OpenapiResolveUtil.resolveReference(reference);
                if (resolved instanceof PhpDefine) {
                    final PsiElement value = ((PhpDefine) resolved).getValue();
                    if (value instanceof PhpTypedElement) {
                        result = resolveType((PhpTypedElement) value, project);
                    }
                }
            }
        } else if (expression instanceof ClassConstantReference) {
            final ClassConstantReference reference = (ClassConstantReference) expression;
            final String referenceName = reference.getName();
            if (referenceName != null && !referenceName.isEmpty()) {
                final PsiElement resolved = resolveReference(reference);
                if (resolved instanceof Field && ((Field) resolved).isConstant()) {
                    final PsiElement value = ((Field) resolved).getDefaultValue();
                    if (value instanceof PhpTypedElement) {
                        result = resolveType((PhpTypedElement) value, project);
                    }
                }
            }
        } else if (expression instanceof FunctionReference) {
            /* resolve function and get it's type or fallback to empty type */
            final FunctionReference reference = (FunctionReference) expression;
            final PsiElement function = resolveReference(reference);
            result = function instanceof Function ? ((Function) function).getType().global(project) : new PhpType();
            if (!(function instanceof Method)) {
                final String name = reference.getName();
                /* override signatures if we specified custom signatures */
                if (name != null && functionReturnTypes.containsKey(name)) {
                    result = functionReturnTypes.get(name);
                }
                /* some replacement functions result can be narrowed from arguments type */
                if (name != null && functionToNarrow.containsKey(name)) {
                    final int targetPosition = functionToNarrow.get(name);
                    final PsiElement[] arguments = reference.getParameters();
                    if (arguments.length > targetPosition && arguments[targetPosition] instanceof PhpTypedElement) {
                        final PhpType argumentType = resolveType((PhpTypedElement) arguments[targetPosition], project);
                        if (argumentType != null && !argumentType.isEmpty() && !argumentType.hasUnknown()) {
                            if (argumentType.getTypes().stream().noneMatch(t -> Types.getType(t).equals(Types.strArray))) {
                                result.getTypes().removeIf(t -> Types.getType(t).equals(Types.strArray));
                            }
                            if (argumentType.getTypes().stream().noneMatch(t -> Types.getType(t).equals(Types.strString))) {
                                result.getTypes().removeIf(t -> Types.getType(t).equals(Types.strString));
                            }
                        }
                    }
                } else if (name != null) {
                    if (name.equals("explode")) {
                        /* explode return false if delimiter is an empty string */
                        final PsiElement[] arguments = reference.getParameters();
                        if (arguments.length >= 2 && arguments[0] instanceof StringLiteralExpression) {
                            final String content = ((StringLiteralExpression) arguments[0]).getContents();
                            result = new PhpType().add(content.isEmpty() ? PhpType.BOOLEAN : PhpType.ARRAY);
                        }
                    } else if (name.equals("parse_url")) {
                        final PsiElement[] arguments = reference.getParameters();
                        if (arguments.length == 2 && arguments[1] instanceof ConstantReference) {
                            final String constantName = ((ConstantReference) arguments[1]).getName();
                            if (constantName != null && constantName.equals("PHP_URL_PORT")) {
                                result = new PhpType().add(PhpType.INT).add(PhpType.NULL);
                            } else {
                                result = new PhpType().add(PhpType.STRING).add(PhpType.NULL);
                            }
                        }
                    } else if (name.equals("microtime")) {
                        final PsiElement[] arguments = reference.getParameters();
                        if (arguments.length == 1 && !PhpLanguageUtil.isFalse(arguments[0])) {
                            result = new PhpType().add(PhpType.FLOAT);
                        } else {
                            result = new PhpType().add(PhpType.INT);
                        }
                    }
                }
            }
        } else if (expression instanceof ArrayAccessExpression) {
            /* `_GET[...] & co` gets resolved with missing string type */
            final ArrayAccessExpression access = (ArrayAccessExpression) expression;
            final PsiElement globalCandidate = access.getValue();
            if (globalCandidate instanceof Variable) {
                final String variableName = ((Variable) globalCandidate).getName();
                if (ExpressionCostEstimateUtil.predefinedVars.contains(variableName)) {
                    final ArrayIndex holder = access.getIndex();
                    final PhpPsiElement index = holder == null ? null : holder.getValue();
                    if (variableName.equals("_SERVER") && index instanceof StringLiteralExpression) {
                        switch(((StringLiteralExpression) index).getContents()) {
                            case "argv":
                                result = new PhpType().add(PhpType.ARRAY);
                                break;
                            case "argc":
                            case "REQUEST_TIME":
                            case "REMOTE_PORT":
                            case "SERVER_PORT":
                                result = new PhpType().add(PhpType.INT);
                                break;
                            case "REQUEST_TIME_FLOAT":
                                result = new PhpType().add(PhpType.FLOAT);
                                break;
                            default:
                                result = new PhpType().add(PhpType.STRING);
                                break;
                        }
                    } else {
                        result = new PhpType().add(PhpType.STRING).add(PhpType.ARRAY);
                    }
                }
            }
        } else if (expression instanceof BinaryExpression) {
            final BinaryExpression binary = (BinaryExpression) expression;
            final IElementType operator = binary.getOperationType();
            if (operator == PhpTokenTypes.opPLUS || operator == PhpTokenTypes.opMINUS || operator == PhpTokenTypes.opMUL) {
                /* workaround for https://youtrack.jetbrains.com/issue//WI-37466 & co */
                boolean hasFloat = true;
                boolean hasArray = false;
                final PsiElement left = binary.getLeftOperand();
                if (left instanceof PhpTypedElement) {
                    final PhpType leftType = resolveType((PhpTypedElement) left, project);
                    if (leftType != null) {
                        final Set<String> leftTypes = new HashSet<>();
                        leftType.filterUnknown().getTypes().forEach(type -> leftTypes.add(Types.getType(type)));
                        hasFloat = leftTypes.isEmpty() || leftTypes.contains(Types.strFloat) || leftTypes.contains(Types.strNumber) || (leftTypes.contains(Types.strString) && !leftTypes.contains(Types.strInteger));
                        hasArray = leftTypes.contains(Types.strArray);
                        leftTypes.clear();
                        if (!hasFloat || (!hasArray && operator == PhpTokenTypes.opPLUS)) {
                            final PsiElement right = binary.getRightOperand();
                            if (right instanceof PhpTypedElement) {
                                final PhpType rightType = resolveType((PhpTypedElement) right, project);
                                if (rightType != null) {
                                    final Set<String> rightTypes = new HashSet<>();
                                    rightType.filterUnknown().getTypes().forEach(type -> rightTypes.add(Types.getType(type)));
                                    hasFloat = hasFloat || rightTypes.isEmpty() || rightTypes.contains(Types.strFloat) || rightTypes.contains(Types.strNumber) || (rightTypes.contains(Types.strString) && !rightTypes.contains(Types.strInteger));
                                    hasArray = (hasArray && !OpenapiTypesUtil.isNumber(right)) || rightTypes.contains(Types.strArray);
                                    rightTypes.clear();
                                }
                            }
                        }
                    }
                }
                result = hasFloat ? new PhpType().add(PhpType.FLOAT) : new PhpType().add(PhpType.INT);
                result = hasArray ? new PhpType().add(PhpType.ARRAY) : result;
            } else if (operator == PhpTokenTypes.opCOALESCE) {
                /* workaround for https://youtrack.jetbrains.com/issue/WI-37013 & co */
                final PsiElement left = binary.getLeftOperand();
                final PsiElement right = binary.getRightOperand();
                result = PhpType.EMPTY;
                if (left instanceof PhpTypedElement && right instanceof PhpTypedElement) {
                    final PhpType leftType = resolveType((PhpTypedElement) left, project);
                    if (leftType != null && !leftType.filterUnknown().isEmpty()) {
                        final PhpType rightType = resolveType((PhpTypedElement) right, project);
                        if (rightType != null && !rightType.filterUnknown().isEmpty()) {
                            result = new PhpType().add(leftType.filterNull()).add(rightType);
                        }
                    }
                }
            }
        } else if (expression instanceof TernaryExpression) {
            final TernaryExpression ternary = (TernaryExpression) expression;
            final PsiElement left = ternary.getTrueVariant();
            final PsiElement right = ternary.getFalseVariant();
            if (left instanceof PhpTypedElement && right instanceof PhpTypedElement) {
                final PhpType leftType = resolveType((PhpTypedElement) left, project);
                if (leftType != null && !leftType.filterUnknown().isEmpty()) {
                    final PhpType rightType = resolveType((PhpTypedElement) right, project);
                    if (rightType != null && !rightType.filterUnknown().isEmpty()) {
                        result = ternary.isShort() ? new PhpType().add(leftType.filterNull()).add(rightType) : new PhpType().add(leftType).add(rightType);
                    }
                }
            }
        } else if (expression instanceof UnaryExpression) {
            final UnaryExpression unary = (UnaryExpression) expression;
            final PsiElement operation = unary.getOperation();
            if (operation != null) {
                if (OpenapiTypesUtil.is(operation, PhpTokenTypes.opBIT_NOT) || OpenapiTypesUtil.is(operation, PhpTokenTypes.opMINUS)) {
                    final PsiElement argument = unary.getValue();
                    if (argument instanceof PhpTypedElement) {
                        result = resolveType((PhpTypedElement) argument, project);
                    }
                } else if (OpenapiTypesUtil.is(operation, PhpTokenTypes.kwCLONE)) {
                    final PsiElement argument = unary.getValue();
                    if (argument instanceof PhpTypedElement) {
                        final PhpType argumentType = resolveType((PhpTypedElement) argument, project);
                        if (argumentType != null && !argumentType.isEmpty()) {
                            result = new PhpType().add(argumentType.filterPrimitives());
                        }
                    }
                }
            }
        } else if (expression instanceof AssignmentExpression && OpenapiTypesUtil.isAssignment((PsiElement) expression)) {
            final PsiElement value = ((AssignmentExpression) expression).getValue();
            if (value instanceof PhpTypedElement) {
                result = resolveType((PhpTypedElement) value, project);
            }
        } else if (expression instanceof FieldReference) {
            final FieldReference reference = (FieldReference) expression;
            final String referenceName = reference.getName();
            if (referenceName != null && !referenceName.isEmpty()) {
                final PsiElement base = reference.getClassReference();
                if (base instanceof Variable && ((Variable) base).getName().equals("this")) {
                    final PsiElement resolvedField = OpenapiResolveUtil.resolveReference(reference);
                    if (resolvedField instanceof Field) {
                        final PhpType declaredType = resolveDeclaredType((Field) resolvedField);
                        if (!declaredType.isEmpty()) {
                            result = declaredType;
                        }
                    }
                }
            }
        } else if (expression instanceof ParenthesizedExpression) {
            final PsiElement value = ((ParenthesizedExpression) expression).getArgument();
            if (value instanceof PhpTypedElement) {
                result = resolveType((PhpTypedElement) value, project);
            }
        } else if (expression instanceof Parameter) {
            /* Incorrect type inference for variadic parameters */
            final Parameter parameter = (Parameter) expression;
            if (parameter.isVariadic()) {
                final PhpType resolved = parameter.getDeclaredType();
                if (resolved.isEmpty()) {
                    result = new PhpType().add(PhpType.ARRAY);
                } else if (resolved.size() == 1 && !Types.getType(resolved.getTypes().iterator().next()).equals(Types.strArray)) {
                    result = new PhpType().add(String.format("%s[]", resolved.getTypes().iterator().next()));
                }
            }
        }
        /* default behaviour */
        result = result == null ? expression.getType().global(project) : result;
    } catch (final Throwable error) {
        if (error instanceof ProcessCanceledException) {
            throw error;
        }
        result = null;
    }
    return result;
}
Also used : java.util(java.util) com.jetbrains.php.lang.psi.elements(com.jetbrains.php.lang.psi.elements) IElementType(com.intellij.psi.tree.IElementType) PhpTokenTypes(com.jetbrains.php.lang.lexer.PhpTokenTypes) PsiReference(com.intellij.psi.PsiReference) ExpressionCostEstimateUtil(com.kalessil.phpStorm.phpInspectionsEA.inspectors.ifs.utils.ExpressionCostEstimateUtil) PhpIndex(com.jetbrains.php.PhpIndex) Nullable(org.jetbrains.annotations.Nullable) ProcessCanceledException(com.intellij.openapi.progress.ProcessCanceledException) ResolveResult(com.intellij.psi.ResolveResult) PsiElement(com.intellij.psi.PsiElement) Project(com.intellij.openapi.project.Project) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) NotNull(org.jetbrains.annotations.NotNull) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) PsiElement(com.intellij.psi.PsiElement) ProcessCanceledException(com.intellij.openapi.progress.ProcessCanceledException) IElementType(com.intellij.psi.tree.IElementType) Nullable(org.jetbrains.annotations.Nullable)

Example 35 with PhpType

use of com.jetbrains.php.lang.psi.resolve.types.PhpType in project phpinspectionsea by kalessil.

the class MissingIssetImplementationInspector method buildVisitor.

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

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

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

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

Aggregations

PhpType (com.jetbrains.php.lang.psi.resolve.types.PhpType)56 PsiElement (com.intellij.psi.PsiElement)46 NotNull (org.jetbrains.annotations.NotNull)41 BasePhpElementVisitor (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor)33 ProblemsHolder (com.intellij.codeInspection.ProblemsHolder)20 Project (com.intellij.openapi.project.Project)20 HashSet (java.util.HashSet)20 BasePhpInspection (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection)16 Nullable (org.jetbrains.annotations.Nullable)16 com.jetbrains.php.lang.psi.elements (com.jetbrains.php.lang.psi.elements)15 PsiElementVisitor (com.intellij.psi.PsiElementVisitor)14 PhpIndex (com.jetbrains.php.PhpIndex)14 com.kalessil.phpStorm.phpInspectionsEA.utils (com.kalessil.phpStorm.phpInspectionsEA.utils)13 IElementType (com.intellij.psi.tree.IElementType)12 Set (java.util.Set)12 PhpTokenTypes (com.jetbrains.php.lang.lexer.PhpTokenTypes)10 PsiTreeUtil (com.intellij.psi.util.PsiTreeUtil)9 PhpTypedElement (com.jetbrains.php.lang.psi.elements.PhpTypedElement)9 InterfacesExtractUtil (com.kalessil.phpStorm.phpInspectionsEA.utils.hierarhy.InterfacesExtractUtil)8 Collectors (java.util.stream.Collectors)8