Search in sources :

Example 51 with BasePhpElementVisitor

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

the class IsNullFunctionUsageInspector 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("is_null")) {
                final PsiElement[] arguments = reference.getParameters();
                if (arguments.length == 1) {
                    final PsiElement parent = reference.getParent();
                    /* check the context */
                    boolean checksIsNull = true;
                    PsiElement target = reference;
                    if (parent instanceof UnaryExpression) {
                        if (OpenapiTypesUtil.is(((UnaryExpression) parent).getOperation(), PhpTokenTypes.opNOT)) {
                            checksIsNull = false;
                            target = parent;
                        }
                    } else if (parent instanceof BinaryExpression) {
                        /* extract is_nulls' expression parts */
                        final BinaryExpression expression = (BinaryExpression) parent;
                        final PsiElement secondOperand = OpenapiElementsUtil.getSecondOperand(expression, reference);
                        if (PhpLanguageUtil.isBoolean(secondOperand)) {
                            final IElementType operation = expression.getOperationType();
                            if (PhpTokenTypes.opEQUAL == operation || PhpTokenTypes.opIDENTICAL == operation) {
                                target = parent;
                                checksIsNull = PhpLanguageUtil.isTrue(secondOperand);
                            } else if (operation == PhpTokenTypes.opNOT_EQUAL || operation == PhpTokenTypes.opNOT_IDENTICAL) {
                                target = parent;
                                checksIsNull = !PhpLanguageUtil.isTrue(secondOperand);
                            } else {
                                target = reference;
                            }
                        }
                    }
                    /* report the issue */
                    final boolean wrap = arguments[0] instanceof AssignmentExpression || arguments[0] instanceof TernaryExpression || arguments[0] instanceof BinaryExpression;
                    final String wrappedArgument = wrap ? String.format("(%s)", arguments[0].getText()) : arguments[0].getText();
                    final boolean isRegular = ComparisonStyle.isRegular();
                    final String replacement = String.format("%s %s %s", isRegular ? wrappedArgument : "null", checksIsNull ? "===" : "!==", isRegular ? "null" : wrappedArgument);
                    holder.registerProblem(target, String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), replacement), new CompareToNullFix(replacement));
                }
            }
        }
    };
}
Also used : NotNull(org.jetbrains.annotations.NotNull) IElementType(com.intellij.psi.tree.IElementType) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 52 with BasePhpElementVisitor

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

the class IsCountableCanBeUsedInspector method buildVisitor.

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

        @Override
        public void visitPhpFunctionCall(@NotNull FunctionReference reference) {
            final String functionName = reference.getName();
            if (functionName != null && functionName.equals("is_array")) {
                final boolean isTargetVersion = PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP740);
                if (isTargetVersion) {
                    final PsiElement[] arguments = reference.getParameters();
                    final PsiElement parent = reference.getParent();
                    if (parent instanceof BinaryExpression && arguments.length == 1) {
                        final BinaryExpression binary = (BinaryExpression) parent;
                        final IElementType operation = binary.getOperationType();
                        if (operation == PhpTokenTypes.opOR) {
                            /* find the high-level binary expression */
                            BinaryExpression context = binary;
                            while (context instanceof BinaryExpression) {
                                PsiElement up = context.getParent();
                                while (up instanceof ParenthesizedExpression) {
                                    up = up.getParent();
                                }
                                if (up instanceof BinaryExpression && ((BinaryExpression) up).getOperationType() == PhpTokenTypes.opOR) {
                                    context = (BinaryExpression) up;
                                } else {
                                    break;
                                }
                            }
                            /* check the pattern */
                            final List<PsiElement> fragments = this.extract(context, PhpTokenTypes.opOR);
                            if (!fragments.isEmpty()) {
                                if (fragments.size() > 1) {
                                    for (final PsiElement fragment : fragments) {
                                        if (fragment != reference && fragment instanceof BinaryExpression) {
                                            final BinaryExpression candidate = (BinaryExpression) fragment;
                                            if (candidate.getOperationType() == PhpTokenTypes.kwINSTANCEOF) {
                                                final PsiElement clazz = candidate.getRightOperand();
                                                if (clazz instanceof ClassReference && "Countable".equals(((ClassReference) clazz).getName())) {
                                                    final PsiElement subject = candidate.getLeftOperand();
                                                    if (subject != null && OpenapiEquivalenceUtil.areEqual(subject, arguments[0])) {
                                                        final String argument = subject.getText();
                                                        holder.registerProblem(reference, String.format(MessagesPresentationUtil.prefixWithEa(message), argument, argument, argument));
                                                        break;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                fragments.clear();
                            }
                        }
                    }
                }
            }
        }

        @NotNull
        private List<PsiElement> extract(@NotNull BinaryExpression binary, @Nullable IElementType operator) {
            final List<PsiElement> result = new ArrayList<>();
            if (binary.getOperationType() == operator) {
                Stream.of(binary.getLeftOperand(), binary.getRightOperand()).filter(Objects::nonNull).map(ExpressionSemanticUtil::getExpressionTroughParenthesis).forEach(expression -> {
                    if (expression instanceof BinaryExpression) {
                        result.addAll(this.extract((BinaryExpression) expression, operator));
                    } else {
                        result.add(expression);
                    }
                });
            } else {
                result.add(binary);
            }
            return result;
        }
    };
}
Also used : ParenthesizedExpression(com.jetbrains.php.lang.psi.elements.ParenthesizedExpression) ArrayList(java.util.ArrayList) NotNull(org.jetbrains.annotations.NotNull) IElementType(com.intellij.psi.tree.IElementType) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) Objects(java.util.Objects) FunctionReference(com.jetbrains.php.lang.psi.elements.FunctionReference) ClassReference(com.jetbrains.php.lang.psi.elements.ClassReference) PsiElement(com.intellij.psi.PsiElement) Nullable(org.jetbrains.annotations.Nullable) NotNull(org.jetbrains.annotations.NotNull)

Example 53 with BasePhpElementVisitor

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

the class IsIterableCanBeUsedInspector method buildVisitor.

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

        @Override
        public void visitPhpFunctionCall(@NotNull FunctionReference reference) {
            final String functionName = reference.getName();
            if (functionName != null && functionName.equals("is_array")) {
                final boolean isTargetVersion = PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP710);
                if (isTargetVersion) {
                    final PsiElement[] arguments = reference.getParameters();
                    final PsiElement parent = reference.getParent();
                    if (parent instanceof BinaryExpression && arguments.length == 1) {
                        final BinaryExpression binary = (BinaryExpression) parent;
                        final IElementType operation = binary.getOperationType();
                        if (operation == PhpTokenTypes.opOR) {
                            /* find the high-level binary expression */
                            BinaryExpression context = binary;
                            while (context instanceof BinaryExpression) {
                                PsiElement up = context.getParent();
                                while (up instanceof ParenthesizedExpression) {
                                    up = up.getParent();
                                }
                                if (up instanceof BinaryExpression && ((BinaryExpression) up).getOperationType() == PhpTokenTypes.opOR) {
                                    context = (BinaryExpression) up;
                                } else {
                                    break;
                                }
                            }
                            /* check the pattern */
                            final List<PsiElement> fragments = this.extract(context, PhpTokenTypes.opOR);
                            if (!fragments.isEmpty()) {
                                if (fragments.size() > 1) {
                                    for (final PsiElement fragment : fragments) {
                                        if (fragment != reference && fragment instanceof BinaryExpression) {
                                            final BinaryExpression candidate = (BinaryExpression) fragment;
                                            if (candidate.getOperationType() == PhpTokenTypes.kwINSTANCEOF) {
                                                final PsiElement clazz = candidate.getRightOperand();
                                                if (clazz instanceof ClassReference && "Traversable".equals(((ClassReference) clazz).getName())) {
                                                    final PsiElement subject = candidate.getLeftOperand();
                                                    if (subject != null && OpenapiEquivalenceUtil.areEqual(subject, arguments[0])) {
                                                        final String argument = subject.getText();
                                                        holder.registerProblem(reference, String.format(MessagesPresentationUtil.prefixWithEa(message), argument, argument, argument));
                                                        break;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                fragments.clear();
                            }
                        }
                    }
                }
            }
        }

        @NotNull
        private List<PsiElement> extract(@NotNull BinaryExpression binary, @Nullable IElementType operator) {
            final List<PsiElement> result = new ArrayList<>();
            if (binary.getOperationType() == operator) {
                Stream.of(binary.getLeftOperand(), binary.getRightOperand()).filter(Objects::nonNull).map(ExpressionSemanticUtil::getExpressionTroughParenthesis).forEach(expression -> {
                    if (expression instanceof BinaryExpression) {
                        result.addAll(this.extract((BinaryExpression) expression, operator));
                    } else {
                        result.add(expression);
                    }
                });
            } else {
                result.add(binary);
            }
            return result;
        }
    };
}
Also used : ParenthesizedExpression(com.jetbrains.php.lang.psi.elements.ParenthesizedExpression) ArrayList(java.util.ArrayList) NotNull(org.jetbrains.annotations.NotNull) IElementType(com.intellij.psi.tree.IElementType) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) Objects(java.util.Objects) FunctionReference(com.jetbrains.php.lang.psi.elements.FunctionReference) ClassReference(com.jetbrains.php.lang.psi.elements.ClassReference) PsiElement(com.intellij.psi.PsiElement) Nullable(org.jetbrains.annotations.Nullable) NotNull(org.jetbrains.annotations.NotNull)

Example 54 with BasePhpElementVisitor

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

the class PropertyInitializationFlawsInspector method buildVisitor.

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

        @Override
        public void visitPhpField(@NotNull Field field) {
            if (REPORT_DEFAULTS_FLAWS && !field.isConstant()) {
                final PhpClass clazz = field.getContainingClass();
                final PhpClass parentClazz = clazz == null ? null : OpenapiResolveUtil.resolveSuperClass(clazz);
                final Field originField = parentClazz == null ? null : OpenapiResolveUtil.resolveField(parentClazz, field.getName());
                final PsiElement fieldDefault = OpenapiResolveUtil.resolveDefaultValue(field);
                final PsiElement originDefault = originField == null ? null : OpenapiResolveUtil.resolveDefaultValue(originField);
                if (PhpLanguageUtil.isNull(fieldDefault)) {
                    /* false-positives: typed properties PS will take care of them */
                    if (!this.isNullableTypedProperty(field)) {
                        holder.registerProblem(fieldDefault, MessagesPresentationUtil.prefixWithEa(messageDefaultNull), ProblemHighlightType.LIKE_UNUSED_SYMBOL, new DropFieldDefaultValueFix());
                    }
                } else if (fieldDefault instanceof PhpPsiElement && originDefault instanceof PhpPsiElement) {
                    final boolean isDefaultDuplicate = !originField.getModifier().getAccess().isPrivate() && OpenapiEquivalenceUtil.areEqual(fieldDefault, originDefault);
                    if (isDefaultDuplicate) {
                        boolean report = true;
                        /* false-positives: classes reference are the same, but resolved to different classes */
                        final Set<String> originalClasses = this.findReferencedClasses(originDefault);
                        if (!originalClasses.isEmpty()) {
                            final Set<String> fieldClasses = this.findReferencedClasses(fieldDefault);
                            report = !originalClasses.addAll(fieldClasses);
                            fieldClasses.clear();
                            originalClasses.clear();
                        }
                        if (report) {
                            holder.registerProblem(fieldDefault, MessagesPresentationUtil.prefixWithEa(messageSenselessWrite), ProblemHighlightType.LIKE_UNUSED_SYMBOL);
                        }
                    }
                }
            }
        }

        private boolean isNullableTypedProperty(@Nullable Field field) {
            if (field != null && PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP740)) {
                final PhpType resolved = OpenapiResolveUtil.resolveDeclaredType(field);
                return !resolved.isEmpty() && resolved.getTypes().stream().map(Types::getType).anyMatch(t -> t.equals(Types.strNull) || t.equals(Types.strMixed));
            }
            return false;
        }

        @NotNull
        private Set<String> findReferencedClasses(@NotNull PsiElement where) {
            return PsiTreeUtil.findChildrenOfType(where, ClassReference.class).stream().map(r -> {
                final PsiElement resolved = OpenapiResolveUtil.resolveReference(r);
                return resolved instanceof PhpClass ? ((PhpClass) resolved).getFQN() : null;
            }).collect(Collectors.toSet());
        }

        @Override
        public void visitPhpMethod(@NotNull Method method) {
            /* configuration-based toggle */
            if (!REPORT_INIT_FLAWS) {
                return;
            }
            /* process only constructors with non-empty body */
            final PhpClass clazz = method.getContainingClass();
            if (null == clazz || !method.getName().equals("__construct") || clazz.isInterface() || clazz.isTrait()) {
                return;
            }
            final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(method);
            if (null == body || 0 == ExpressionSemanticUtil.countExpressionsInGroup(body)) {
                return;
            }
            /* collect private properties with default values; stop inspection if none found */
            /* protected/public properties init in __construct can be bypassed, so defaults might have sense */
            final Map<String, PsiElement> propertiesToCheck = new HashMap<>();
            for (final Field field : clazz.getOwnFields()) {
                if (!field.isConstant()) {
                    final PhpModifier modifiers = field.getModifier();
                    if (modifiers.isPrivate() && !modifiers.isStatic()) {
                        final PsiElement defaultValue = OpenapiResolveUtil.resolveDefaultValue(field);
                        if (defaultValue instanceof PhpPsiElement && !PhpLanguageUtil.isNull(defaultValue)) {
                            propertiesToCheck.put(field.getName(), defaultValue);
                        } else {
                            propertiesToCheck.put(field.getName(), null);
                        }
                    }
                }
            }
            if (propertiesToCheck.isEmpty()) {
                return;
            }
            /* iterate 1st level instructions and analyze overriding properties */
            for (final PsiElement expression : body.getChildren()) {
                final PsiElement assignmentCandidate = expression.getFirstChild();
                if (!OpenapiTypesUtil.isAssignment(assignmentCandidate)) {
                    continue;
                }
                final AssignmentExpression assignment = (AssignmentExpression) assignmentCandidate;
                final PsiElement container = assignment.getVariable();
                final PsiElement value = assignment.getValue();
                if (container instanceof FieldReference && container.getFirstChild().getText().equals("$this")) {
                    final String overriddenProperty = ((FieldReference) container).getName();
                    if (null == value || null == overriddenProperty || !propertiesToCheck.containsKey(overriddenProperty)) {
                        continue;
                    }
                    final PsiElement fieldDefault = propertiesToCheck.get(overriddenProperty);
                    /* Pattern: written and default values are identical */
                    if ((null == fieldDefault && PhpLanguageUtil.isNull(value)) || (null != fieldDefault && OpenapiEquivalenceUtil.areEqual(value, fieldDefault))) {
                        /* false-positives: typed properties */
                        if (!this.isNullableTypedProperty(OpenapiResolveUtil.resolveField(clazz, overriddenProperty))) {
                            holder.registerProblem(expression, MessagesPresentationUtil.prefixWithEa(messageSenselessWrite), ProblemHighlightType.LIKE_UNUSED_SYMBOL);
                        }
                        continue;
                    }
                    if (null == fieldDefault) {
                        continue;
                    }
                    /* false-positive: property is involved into generating new value */
                    boolean isPropertyReused = false;
                    for (final FieldReference candidate : PsiTreeUtil.findChildrenOfType(value, FieldReference.class)) {
                        if (OpenapiEquivalenceUtil.areEqual(container, candidate)) {
                            isPropertyReused = true;
                            break;
                        }
                    }
                    if (!isPropertyReused && REPORT_DEFAULTS_FLAWS) {
                        holder.registerProblem(fieldDefault, MessagesPresentationUtil.prefixWithEa(messageDefaultOverride), new DropFieldDefaultValueFix());
                    }
                }
            }
            propertiesToCheck.clear();
        }
    };
}
Also used : 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) Set(java.util.Set) HashMap(java.util.HashMap) OptionsComponent(com.kalessil.phpStorm.phpInspectionsEA.options.OptionsComponent) Collectors(java.util.stream.Collectors) 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) ProblemDescriptor(com.intellij.codeInspection.ProblemDescriptor) Map(java.util.Map) PsiElement(com.intellij.psi.PsiElement) Project(com.intellij.openapi.project.Project) ProblemHighlightType(com.intellij.codeInspection.ProblemHighlightType) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) NotNull(org.jetbrains.annotations.NotNull) LocalQuickFix(com.intellij.codeInspection.LocalQuickFix) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) javax.swing(javax.swing) Set(java.util.Set) HashMap(java.util.HashMap) 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) Nullable(org.jetbrains.annotations.Nullable) NotNull(org.jetbrains.annotations.NotNull)

Example 55 with BasePhpElementVisitor

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

the class PhpUnitTestsInspector method buildVisitor.

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

        @Override
        public void visitPhpMethod(@NotNull Method method) {
            final PhpClass clazz = method.getContainingClass();
            final PsiElement nameNode = NamedElementUtil.getNameIdentifier(method);
            final PhpDocComment phpDoc = method.getDocComment();
            if (null == clazz || null == nameNode || phpDoc == null) {
                return;
            }
            final boolean isMethodNamedAsTest = method.getName().startsWith("test");
            for (final PhpDocTag tag : PsiTreeUtil.findChildrenOfType(phpDoc, PhpDocTag.class)) {
                final String tagName = tag.getName();
                if (tagName.equals("@dataProvider")) {
                    final PsiElement candidate = tag.getFirstPsiChild();
                    if (candidate instanceof PhpDocRef && this.isAnnotation(tag)) {
                        final List<PsiReference> references = Arrays.asList(candidate.getReferences());
                        if (!references.isEmpty()) {
                            Collections.reverse(references);
                            final PsiElement resolved = OpenapiResolveUtil.resolveReference(references.get(0));
                            if (resolved instanceof Method) {
                                if (SUGGEST_TO_USE_NAMED_DATASETS && !((Method) resolved).isAbstract()) {
                                    final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(resolved);
                                    final PsiElement last = body == null ? null : ExpressionSemanticUtil.getLastStatement(body);
                                    if (last instanceof PhpReturn) {
                                        final PsiElement value = ExpressionSemanticUtil.getReturnValue((PhpReturn) last);
                                        if (value instanceof ArrayCreationExpression) {
                                            final PsiElement firstChild = ((ArrayCreationExpression) value).getFirstPsiChild();
                                            boolean isNamedDataset = firstChild == null;
                                            if (firstChild instanceof ArrayHashElement) {
                                                final PsiElement key = ((ArrayHashElement) firstChild).getKey();
                                                isNamedDataset = key instanceof StringLiteralExpression;
                                            }
                                            if (!isNamedDataset) {
                                                holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(messageNamedProvider));
                                            }
                                        }
                                    }
                                }
                            } else {
                                holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(messageDataProvider), ProblemHighlightType.GENERIC_ERROR);
                            }
                        } else {
                            holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(messageDataProvider), ProblemHighlightType.GENERIC_ERROR);
                        }
                    }
                } else if (tagName.equals("@depends")) {
                    final PsiElement candidate = tag.getFirstPsiChild();
                    if (candidate instanceof PhpDocRef && this.isAnnotation(tag)) {
                        final List<PsiReference> references = Arrays.asList(candidate.getReferences());
                        if (!references.isEmpty()) {
                            Collections.reverse(references);
                            final PsiElement resolved = OpenapiResolveUtil.resolveReference(references.get(0));
                            if (resolved instanceof Method) {
                                final Method dependency = (Method) resolved;
                                if (!dependency.getName().startsWith("test")) {
                                    final PhpDocComment docBlock = dependency.getDocComment();
                                    if (docBlock == null || docBlock.getTagElementsByName("@test").length == 0) {
                                        holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(messageDepends), ProblemHighlightType.GENERIC_ERROR);
                                    }
                                }
                            } else {
                                holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(messageDepends), ProblemHighlightType.GENERIC_ERROR);
                            }
                        } else {
                            holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(messageDepends), ProblemHighlightType.GENERIC_ERROR);
                        }
                    }
                } else if (tagName.equals("@covers")) {
                    final PsiElement candidate = tag.getFirstPsiChild();
                    if (candidate instanceof PhpDocRef && this.isAnnotation(tag)) {
                        final PhpDocRef referenceNeeded = (PhpDocRef) candidate;
                        final List<PsiReference> references = Arrays.asList(referenceNeeded.getReferences());
                        Collections.reverse(references);
                        /* resolve references, populate information about provided entries */
                        boolean hasCallableReference = false;
                        boolean hasClassReference = false;
                        final String referenceText = referenceNeeded.getText();
                        for (final PsiReference ref : references) {
                            final PsiElement resolved = OpenapiResolveUtil.resolveReference(ref);
                            if (resolved instanceof PhpClass) {
                                hasClassReference = true;
                                hasCallableReference = referenceText.endsWith("::");
                                break;
                            } else if (resolved instanceof Function) {
                                hasCallableReference = true;
                                hasClassReference = resolved instanceof Method;
                                break;
                            }
                        }
                        final boolean callableNeeded = referenceText.contains("::") && !referenceText.contains("::<");
                        if ((callableNeeded && !hasCallableReference) || (!callableNeeded && !hasClassReference)) {
                            holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(String.format(messageCovers, referenceText)), ProblemHighlightType.GENERIC_ERROR);
                        }
                    }
                } else if (tagName.equals("@test")) {
                    if (isMethodNamedAsTest && this.isAnnotation(tag)) {
                        holder.registerProblem(tag.getFirstChild(), MessagesPresentationUtil.prefixWithEa(messageTest), ProblemHighlightType.LIKE_DEPRECATED, new AmbiguousTestAnnotationLocalFix());
                    }
                }
            }
        }

        private boolean isAnnotation(@NotNull PhpDocTag tag) {
            PsiElement previous = tag.getPrevSibling();
            previous = previous instanceof PsiWhiteSpace ? previous.getPrevSibling() : previous;
            final IElementType start = previous == null ? null : previous.getNode().getElementType();
            return start == PhpTokenTypes.DOC_COMMENT_START || start == PhpTokenTypes.DOC_LEADING_ASTERISK;
        }

        @Override
        public void visitPhpMethodReference(@NotNull MethodReference reference) {
            final String methodName = reference.getName();
            if (methodName != null) {
                if (methodName.startsWith("assert") && !methodName.equals("assert")) {
                    final List<BooleanSupplier> callbacks = new ArrayList<>();
                    callbacks.add(() -> AssertBoolInvertedStrategy.apply(methodName, reference, holder));
                    callbacks.add(() -> AssertBoolOfComparisonStrategy.apply(methodName, reference, holder));
                    if (SUGGEST_TO_USE_ASSERTSAME) {
                        callbacks.add(() -> AssertSameStrategy.apply(methodName, reference, holder));
                    }
                    if (PROMOTE_PHPUNIT_API) {
                        final PhpUnitVersion version = PHP_UNIT_VERSION == null ? PhpUnitVersion.PHPUNIT80 : PHP_UNIT_VERSION;
                        callbacks.add(() -> AssertEmptyStrategy.apply(methodName, reference, holder));
                        callbacks.add(() -> AssertConstantStrategy.apply(methodName, reference, holder));
                        callbacks.add(() -> AssertInternalTypeStrategy.apply(methodName, reference, holder, version));
                        callbacks.add(() -> AssertInstanceOfStrategy.apply(methodName, reference, holder));
                        callbacks.add(() -> AssertResourceExistsStrategy.apply(methodName, reference, holder, version));
                        callbacks.add(() -> AssertCountStrategy.apply(methodName, reference, holder));
                        callbacks.add(() -> AssertContainsStrategy.apply(methodName, reference, holder, version));
                        callbacks.add(() -> AssertRegexStrategy.apply(methodName, reference, holder));
                        /* AssertFileEqualsStrategy and AssertStringEqualsFileStrategy order is important */
                        callbacks.add(() -> AssertFileEqualsStrategy.apply(methodName, reference, holder));
                        callbacks.add(() -> AssertStringEqualsFileStrategy.apply(methodName, reference, holder));
                    }
                    for (final BooleanSupplier callback : callbacks) {
                        if (callback.getAsBoolean()) {
                            break;
                        }
                    }
                    callbacks.clear();
                } else if (methodName.equals("expects")) {
                    if (PROMOTE_MOCKING_ONCE) {
                        ExpectsOnceStrategy.apply(methodName, reference, holder);
                    }
                } else if (methodName.equals("will")) {
                    if (PROMOTE_MOCKING_WILL_RETURN) {
                        WillReturnStrategy.apply(methodName, reference, holder);
                    }
                }
            }
        }
    };
}
Also used : ArrayList(java.util.ArrayList) NotNull(org.jetbrains.annotations.NotNull) PhpDocTag(com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PhpDocRef(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocRef) ArrayList(java.util.ArrayList) List(java.util.List) BooleanSupplier(java.util.function.BooleanSupplier) PsiElement(com.intellij.psi.PsiElement) LeafPsiElement(com.intellij.psi.impl.source.tree.LeafPsiElement) PsiReference(com.intellij.psi.PsiReference) IElementType(com.intellij.psi.tree.IElementType) PhpDocComment(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment) PsiWhiteSpace(com.intellij.psi.PsiWhiteSpace) NotNull(org.jetbrains.annotations.NotNull)

Aggregations

BasePhpElementVisitor (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor)169 NotNull (org.jetbrains.annotations.NotNull)169 PsiElement (com.intellij.psi.PsiElement)157 FunctionReference (com.jetbrains.php.lang.psi.elements.FunctionReference)43 ProblemsHolder (com.intellij.codeInspection.ProblemsHolder)40 BasePhpInspection (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection)39 PsiElementVisitor (com.intellij.psi.PsiElementVisitor)37 PhpType (com.jetbrains.php.lang.psi.resolve.types.PhpType)33 IElementType (com.intellij.psi.tree.IElementType)32 com.jetbrains.php.lang.psi.elements (com.jetbrains.php.lang.psi.elements)29 Project (com.intellij.openapi.project.Project)25 BinaryExpression (com.jetbrains.php.lang.psi.elements.BinaryExpression)25 HashSet (java.util.HashSet)24 PsiTreeUtil (com.intellij.psi.util.PsiTreeUtil)22 MessagesPresentationUtil (com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil)19 StringLiteralExpression (com.jetbrains.php.lang.psi.elements.StringLiteralExpression)18 ArrayList (java.util.ArrayList)18 Set (java.util.Set)18 Nullable (org.jetbrains.annotations.Nullable)18 PhpTokenTypes (com.jetbrains.php.lang.lexer.PhpTokenTypes)17