Search in sources :

Example 6 with PhpType

use of com.jetbrains.php.lang.psi.resolve.types.PhpType in project idea-php-typo3-plugin by cedricziel.

the class PhpLangUtil method getClassName.

public static String getClassName(@NotNull PsiElement element) {
    ParameterList parameterList = PsiTreeUtil.getParentOfType(element, ParameterList.class);
    if (parameterList == null) {
        return null;
    }
    MethodReference methodReference = PsiTreeUtil.getParentOfType(element, MethodReference.class);
    if (methodReference == null) {
        return null;
    }
    Variable variableBeingCalledOn = PsiTreeUtil.findChildOfType(methodReference, Variable.class);
    if (variableBeingCalledOn != null && variableBeingCalledOn.getInferredType() != null) {
        PhpType inferredType = variableBeingCalledOn.getInferredType();
        return inferredType.toString();
    }
    ClassReference classReference = PsiTreeUtil.getChildOfType(methodReference, ClassReference.class);
    return extractFqnFromClassReference(methodReference, classReference);
}
Also used : PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType)

Example 7 with PhpType

use of com.jetbrains.php.lang.psi.resolve.types.PhpType in project idea-php-typo3-plugin by cedricziel.

the class ObjectManagerTypeProvider method getType.

@Nullable
@Override
public PhpType getType(PsiElement psiElement) {
    if (DumbService.getInstance(psiElement.getProject()).isDumb()) {
        return null;
    }
    if (!(psiElement instanceof MethodReference) || !PhpElementsUtil.isMethodWithFirstStringOrFieldReference(psiElement, "get")) {
        return null;
    }
    MethodReference methodReference = (MethodReference) psiElement;
    if (methodReference.getParameters().length == 0) {
        return null;
    }
    PsiElement firstParam = methodReference.getParameters()[0];
    if (firstParam instanceof PhpReference) {
        PhpReference ref = (PhpReference) firstParam;
        if (ref.getText().toLowerCase().contains("::class")) {
            return new PhpType().add("#" + this.getKey() + ref.getSignature());
        }
    }
    return null;
}
Also used : PhpReference(com.jetbrains.php.lang.psi.elements.PhpReference) MethodReference(com.jetbrains.php.lang.psi.elements.MethodReference) PsiElement(com.intellij.psi.PsiElement) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) Nullable(org.jetbrains.annotations.Nullable)

Example 8 with PhpType

use of com.jetbrains.php.lang.psi.resolve.types.PhpType in project idea-php-typo3-plugin by cedricziel.

the class PhpGlobalsTypeProvider method getType.

@Nullable
@Override
public PhpType getType(PsiElement psiElement) {
    // $GLOBALS['TYPO3_DB']
    if (!(psiElement instanceof ArrayAccessExpression)) {
        return null;
    }
    VariableImpl variable = PsiTreeUtil.getChildOfType(psiElement, VariableImpl.class);
    if (variable == null || !variable.getName().equals("GLOBALS")) {
        return null;
    }
    ArrayIndex arrayIndex = PsiTreeUtil.getChildOfType(psiElement, ArrayIndex.class);
    if (arrayIndex == null) {
        return null;
    }
    StringLiteralExpression arrayIndexName = PsiTreeUtil.getChildOfType(arrayIndex, StringLiteralExpressionImpl.class);
    if (arrayIndexName == null) {
        return null;
    }
    switch(arrayIndexName.getContents()) {
        case "TYPO3_DB":
            return new PhpType().add("#C\\TYPO3\\CMS\\Core\\Database\\DatabaseConnection");
        case "TSFE":
            return new PhpType().add("#C\\TYPO3\\CMS\\Frontend\\Controller\\TypoScriptFrontendController");
        case "BE_USER":
            return new PhpType().add("#C\\TYPO3\\CMS\\Core\\Authentication\\BackendUserAuthentication");
        case "LANG":
            return new PhpType().add("#C\\TYPO3\\CMS\\Lang\\LanguageService");
        default:
            return null;
    }
}
Also used : VariableImpl(com.jetbrains.php.lang.psi.elements.impl.VariableImpl) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) Nullable(org.jetbrains.annotations.Nullable)

Example 9 with PhpType

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

the class CascadeStringReplacementInspector method buildVisitor.

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

        @Override
        public void visitPhpReturn(@NotNull PhpReturn returnStatement) {
            final FunctionReference functionCall = this.getFunctionReference(returnStatement);
            if (functionCall != null) {
                this.analyze(functionCall, returnStatement);
            }
        }

        @Override
        public void visitPhpAssignmentExpression(@NotNull AssignmentExpression assignmentExpression) {
            final FunctionReference functionCall = this.getFunctionReference(assignmentExpression);
            if (functionCall != null) {
                this.analyze(functionCall, assignmentExpression);
            }
        }

        private void analyze(@NotNull FunctionReference functionCall, @NotNull PsiElement expression) {
            final PsiElement[] arguments = functionCall.getParameters();
            if (arguments.length == 3) {
                /* case: cascading replacements */
                final AssignmentExpression previous = this.getPreviousAssignment(expression);
                final FunctionReference previousCall = previous == null ? null : this.getFunctionReference(previous);
                if (previousCall != null) {
                    final PsiElement transitionVariable = previous.getVariable();
                    if (transitionVariable instanceof Variable && arguments[2] instanceof Variable) {
                        final Variable callSubject = (Variable) arguments[2];
                        final Variable previousVariable = (Variable) transitionVariable;
                        final PsiElement callResultStorage = expression instanceof AssignmentExpression ? ((AssignmentExpression) expression).getVariable() : callSubject;
                        if (callResultStorage != null && callSubject.getName().equals(previousVariable.getName()) && OpenapiEquivalenceUtil.areEqual(transitionVariable, callResultStorage) && this.canMergeArguments(functionCall, previousCall)) {
                            holder.registerProblem(functionCall, MessagesPresentationUtil.prefixWithEa(messageCascading), new MergeStringReplaceCallsFix(holder.getProject(), functionCall, previousCall, USE_SHORT_ARRAYS_SYNTAX));
                        }
                    }
                }
                /* case: nested replacements */
                this.checkNestedCalls(holder.getProject(), arguments[2], functionCall);
                /* case: search simplification */
                if (arguments[1] instanceof StringLiteralExpression) {
                    final PsiElement search = arguments[0];
                    if (search instanceof ArrayCreationExpression) {
                        this.checkForSimplification((ArrayCreationExpression) search);
                    }
                }
            }
        }

        private void checkForSimplification(@NotNull ArrayCreationExpression candidate) {
            final Set<String> replacements = new HashSet<>();
            for (final PsiElement oneReplacement : candidate.getChildren()) {
                if (oneReplacement instanceof PhpPsiElement) {
                    final PhpPsiElement item = ((PhpPsiElement) oneReplacement).getFirstPsiChild();
                    if (!(item instanceof StringLiteralExpression)) {
                        replacements.clear();
                        return;
                    }
                    replacements.add(item.getText());
                }
            }
            if (replacements.size() == 1) {
                holder.registerProblem(candidate, MessagesPresentationUtil.prefixWithEa(messageReplacements), ProblemHighlightType.WEAK_WARNING, new SimplifyReplacementFix(replacements.iterator().next()));
            }
            replacements.clear();
        }

        private void checkNestedCalls(@NotNull Project project, @NotNull PsiElement callCandidate, @NotNull FunctionReference parentCall) {
            if (OpenapiTypesUtil.isFunctionReference(callCandidate)) {
                final FunctionReference functionCall = (FunctionReference) callCandidate;
                final String functionName = functionCall.getName();
                if (functionName != null && functionName.equals("str_replace") && this.canMergeArguments(functionCall, parentCall)) {
                    holder.registerProblem(callCandidate, MessagesPresentationUtil.prefixWithEa(messageNesting), new MergeStringReplaceCallsFix(project, parentCall, functionCall, USE_SHORT_ARRAYS_SYNTAX));
                }
            }
        }

        private boolean canMergeArguments(@NotNull FunctionReference call, @NotNull FunctionReference previousCall) {
            final PsiElement[] arguments = call.getParameters();
            final PsiElement[] previousArguments = previousCall.getParameters();
            // If an argument is array (non-implicit), we need PHP 7.4+ for QF
            final boolean haveArrayType = Stream.of(arguments[0], arguments[1], previousArguments[0], previousArguments[1]).filter(a -> a instanceof PhpTypedElement && !(a instanceof ArrayCreationExpression)).anyMatch(a -> {
                final PhpType resolved = OpenapiResolveUtil.resolveType((PhpTypedElement) a, holder.getProject());
                if (resolved != null) {
                    final Set<String> types = resolved.filterUnknown().getTypes().stream().map(Types::getType).collect(Collectors.toSet());
                    return types.contains(Types.strArray) && !types.contains(Types.strString);
                }
                return false;
            });
            if (haveArrayType) {
                return PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP740);
            }
            return true;
        }

        @Nullable
        private FunctionReference getFunctionReference(@NotNull AssignmentExpression assignment) {
            FunctionReference result = null;
            final PsiElement value = ExpressionSemanticUtil.getExpressionTroughParenthesis(assignment.getValue());
            if (OpenapiTypesUtil.isFunctionReference(value)) {
                final String functionName = ((FunctionReference) value).getName();
                if (functionName != null && functionName.equals("str_replace")) {
                    result = (FunctionReference) value;
                }
            }
            return result;
        }

        @Nullable
        private FunctionReference getFunctionReference(@NotNull PhpReturn phpReturn) {
            FunctionReference result = null;
            final PsiElement value = ExpressionSemanticUtil.getExpressionTroughParenthesis(ExpressionSemanticUtil.getReturnValue(phpReturn));
            if (OpenapiTypesUtil.isFunctionReference(value)) {
                final String functionName = ((FunctionReference) value).getName();
                if (functionName != null && functionName.equals("str_replace")) {
                    result = (FunctionReference) value;
                }
            }
            return result;
        }

        @Nullable
        private AssignmentExpression getPreviousAssignment(@NotNull PsiElement returnOrAssignment) {
            /* get previous non-comment, non-php-doc expression */
            PsiElement previous = null;
            if (returnOrAssignment instanceof PhpReturn) {
                previous = ((PhpReturn) returnOrAssignment).getPrevPsiSibling();
            } else if (returnOrAssignment instanceof AssignmentExpression) {
                previous = returnOrAssignment.getParent().getPrevSibling();
            }
            while (previous != null && !(previous instanceof PhpPsiElement)) {
                previous = previous.getPrevSibling();
            }
            while (previous instanceof PhpDocComment) {
                previous = ((PhpDocComment) previous).getPrevPsiSibling();
            }
            /* grab the target assignment */
            final AssignmentExpression result;
            if (previous != null && previous.getFirstChild() instanceof AssignmentExpression) {
                result = (AssignmentExpression) previous.getFirstChild();
            } else {
                result = null;
            }
            return result;
        }
    };
}
Also used : PhpDocComment(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment) PhpPsiElementFactory(com.jetbrains.php.lang.psi.PhpPsiElementFactory) java.util(java.util) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) PhpLanguageLevel(com.kalessil.phpStorm.phpInspectionsEA.openApi.PhpLanguageLevel) com.jetbrains.php.lang.psi.elements(com.jetbrains.php.lang.psi.elements) LeafPsiElement(com.intellij.psi.impl.source.tree.LeafPsiElement) UseSuggestedReplacementFixer(com.kalessil.phpStorm.phpInspectionsEA.fixers.UseSuggestedReplacementFixer) OptionsComponent(com.kalessil.phpStorm.phpInspectionsEA.options.OptionsComponent) Collectors(java.util.stream.Collectors) Nullable(org.jetbrains.annotations.Nullable) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) com.kalessil.phpStorm.phpInspectionsEA.utils(com.kalessil.phpStorm.phpInspectionsEA.utils) Stream(java.util.stream.Stream) ProblemDescriptor(com.intellij.codeInspection.ProblemDescriptor) Project(com.intellij.openapi.project.Project) ProblemHighlightType(com.intellij.codeInspection.ProblemHighlightType) com.intellij.psi(com.intellij.psi) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) NotNull(org.jetbrains.annotations.NotNull) LocalQuickFix(com.intellij.codeInspection.LocalQuickFix) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) ArrayUtils(org.apache.commons.lang.ArrayUtils) javax.swing(javax.swing) 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) PhpDocComment(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment) LeafPsiElement(com.intellij.psi.impl.source.tree.LeafPsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 10 with PhpType

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

the class UnSafeIsSetOverArrayInspector method buildVisitor.

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

        @Override
        public void visitPhpIsset(@NotNull PhpIsset issetExpression) {
            /*
                 * if no parameters, we don't check;
                 * if multiple parameters, perhaps if-inspection fulfilled and isset's were merged
                 *
                 * TODO: still needs analysis regarding concatenations in indexes
                 */
            final PhpExpression[] arguments = issetExpression.getVariables();
            if (arguments.length != 1) {
                return;
            }
            /* gather context information */
            PsiElement issetParent = issetExpression.getParent();
            boolean issetInverted = false;
            if (issetParent instanceof UnaryExpression) {
                final PsiElement operator = ((UnaryExpression) issetParent).getOperation();
                if (OpenapiTypesUtil.is(operator, PhpTokenTypes.opNOT)) {
                    issetInverted = true;
                    issetParent = issetParent.getParent();
                }
            }
            boolean isResultStored = (issetParent instanceof AssignmentExpression || issetParent instanceof PhpReturn);
            /* false-positives:  ternaries using isset-or-null semantics, there array_key_exist can introduce bugs */
            final PsiElement conditionCandidate = issetInverted ? issetExpression.getParent() : issetExpression;
            boolean isTernaryCondition = issetParent instanceof TernaryExpression && conditionCandidate == ((TernaryExpression) issetParent).getCondition();
            if (isTernaryCondition) {
                final TernaryExpression ternary = (TernaryExpression) issetParent;
                final PsiElement nullCandidate = issetInverted ? ternary.getTrueVariant() : ternary.getFalseVariant();
                if (PhpLanguageUtil.isNull(nullCandidate)) {
                    return;
                }
            }
            /* do analyze  */
            final PsiElement argument = ExpressionSemanticUtil.getExpressionTroughParenthesis(arguments[0]);
            if (argument == null) {
                return;
            }
            /* false positives: variables in template/global context - too unreliable */
            if (argument instanceof Variable && ExpressionSemanticUtil.getBlockScope(argument) == null) {
                return;
            }
            if (!(argument instanceof ArrayAccessExpression)) {
                if (argument instanceof FieldReference) {
                    /* if field is not resolved, it's probably dynamic and isset has a purpose */
                    final PsiReference referencedField = argument.getReference();
                    final PsiElement resolvedField = referencedField == null ? null : OpenapiResolveUtil.resolveReference(referencedField);
                    if (resolvedField == null || !(ExpressionSemanticUtil.getBlockScope(resolvedField) instanceof PhpClass)) {
                        return;
                    }
                }
                if (SUGGEST_TO_USE_NULL_COMPARISON) {
                    /* false-positives: finally, perhaps fallback to initialization in try */
                    if (PsiTreeUtil.getParentOfType(issetExpression, Finally.class) == null) {
                        final List<String> fragments = Arrays.asList(argument.getText(), issetInverted ? "===" : "!==", "null");
                        if (!ComparisonStyle.isRegular()) {
                            Collections.reverse(fragments);
                        }
                        final String replacement = String.join(" ", fragments);
                        holder.registerProblem(issetInverted ? issetExpression.getParent() : issetExpression, String.format(MessagesPresentationUtil.prefixWithEa(patternUseNullComparison), replacement), ProblemHighlightType.WEAK_WARNING, new CompareToNullFix(replacement));
                    }
                }
                return;
            }
            /* TODO: has method/function reference as index */
            if (REPORT_CONCATENATION_IN_INDEXES && !isResultStored && this.hasConcatenationAsIndex((ArrayAccessExpression) argument)) {
                holder.registerProblem(argument, MessagesPresentationUtil.prefixWithEa(messageConcatenationInIndex));
                return;
            }
            if (SUGGEST_TO_USE_ARRAY_KEY_EXISTS && !isArrayAccess((ArrayAccessExpression) argument)) {
                holder.registerProblem(argument, MessagesPresentationUtil.prefixWithEa(messageUseArrayKeyExists), ProblemHighlightType.WEAK_WARNING);
            }
        }

        /* checks if any of indexes is concatenation expression */
        /* TODO: iterator for array access expression */
        private boolean hasConcatenationAsIndex(@NotNull ArrayAccessExpression expression) {
            PsiElement expressionToInspect = expression;
            while (expressionToInspect instanceof ArrayAccessExpression) {
                final ArrayIndex index = ((ArrayAccessExpression) expressionToInspect).getIndex();
                if (index != null && index.getValue() instanceof BinaryExpression) {
                    final PsiElement operation = ((BinaryExpression) index.getValue()).getOperation();
                    if (operation != null && operation.getNode().getElementType() == PhpTokenTypes.opCONCAT) {
                        return true;
                    }
                }
                expressionToInspect = expressionToInspect.getParent();
            }
            return false;
        }

        // TODO: partially duplicates semanticalAnalysis.OffsetOperationsInspector.isContainerSupportsArrayAccess()
        private boolean isArrayAccess(@NotNull ArrayAccessExpression expression) {
            /* ok JB parses `$var[]= ...` always as array, lets make it working properly and report them later */
            final PsiElement container = expression.getValue();
            if (!(container instanceof PhpTypedElement)) {
                return false;
            }
            final Set<String> containerTypes = new HashSet<>();
            final PhpType resolved = OpenapiResolveUtil.resolveType((PhpTypedElement) container, holder.getProject());
            if (resolved != null) {
                resolved.filterUnknown().getTypes().forEach(t -> containerTypes.add(Types.getType(t)));
            }
            /* failed to resolve, don't try to guess anything */
            if (containerTypes.isEmpty()) {
                return false;
            }
            boolean supportsOffsets = false;
            for (final String typeToCheck : containerTypes) {
                /* assume is just null-ble declaration or we shall just rust to mixed */
                if (typeToCheck.equals(Types.strNull)) {
                    continue;
                }
                if (typeToCheck.equals(Types.strMixed)) {
                    supportsOffsets = true;
                    continue;
                }
                /* some of possible types are scalars, what's wrong */
                if (!StringUtils.isEmpty(typeToCheck) && typeToCheck.charAt(0) != '\\') {
                    supportsOffsets = false;
                    break;
                }
                /* assume class has what is needed, OffsetOperationsInspector should report if not */
                supportsOffsets = true;
            }
            containerTypes.clear();
            return supportsOffsets;
        }
    };
}
Also used : NotNull(org.jetbrains.annotations.NotNull) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PsiElement(com.intellij.psi.PsiElement) PsiReference(com.intellij.psi.PsiReference) 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