Search in sources :

Example 6 with BasePhpElementVisitor

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

the class ArgumentEqualsDefaultValueInspector method buildVisitor.

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

        @Override
        public void visitPhpMethodReference(@NotNull MethodReference reference) {
            this.analyze(reference);
        }

        @Override
        public void visitPhpFunctionCall(@NotNull FunctionReference reference) {
            this.analyze(reference);
        }

        private void analyze(@NotNull FunctionReference reference) {
            final String functionName = reference.getName();
            if (functionName != null && !specialFunctions.contains(functionName)) {
                final PsiElement[] arguments = reference.getParameters();
                if (arguments.length > 0) {
                    PsiElement reportFrom = null;
                    PsiElement reportTo = null;
                    final IElementType valueType = arguments[arguments.length - 1].getNode().getElementType();
                    if (OpenapiTypesUtil.DEFAULT_VALUES.contains(valueType)) {
                        final PsiElement resolved = OpenapiResolveUtil.resolveReference(reference);
                        if (resolved instanceof Function) {
                            final Parameter[] parameters = ((Function) resolved).getParameters();
                            if (arguments.length <= parameters.length) {
                                for (int index = Math.min(parameters.length, arguments.length) - 1; index >= 0; --index) {
                                    final PsiElement value = parameters[index].getDefaultValue();
                                    /* false-positives: magic constants */
                                    if (value instanceof ConstantReference && specialConstants.contains(value.getText())) {
                                        break;
                                    }
                                    /* false-positives: unmatched values */
                                    final PsiElement argument = arguments[index];
                                    if (value == null || !OpeanapiEquivalenceUtil.areEqual(value, argument)) {
                                        break;
                                    }
                                    reportFrom = argument;
                                    reportTo = reportTo == null ? argument : reportTo;
                                }
                            }
                        }
                    }
                    if (reportFrom != null) {
                        problemsHolder.registerProblem(problemsHolder.getManager().createProblemDescriptor(reportFrom, reportTo, message, ProblemHighlightType.LIKE_UNUSED_SYMBOL, onTheFly, new TheLocalFix(reportFrom, reportTo)));
                    }
                }
            }
        }
    };
}
Also used : NotNull(org.jetbrains.annotations.NotNull) IElementType(com.intellij.psi.tree.IElementType) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) NotNull(org.jetbrains.annotations.NotNull)

Example 7 with BasePhpElementVisitor

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

the class SenselessCommaInArrayDefinitionInspector method buildVisitor.

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

        @Override
        public void visitPhpArrayCreationExpression(@NotNull ArrayCreationExpression expression) {
            final PsiElement last = expression.getLastChild().getPrevSibling();
            final PsiElement candidate = last instanceof PsiWhiteSpace ? last.getPrevSibling() : last;
            if (OpenapiTypesUtil.is(candidate, PhpTokenTypes.opCOMMA)) {
                holder.registerProblem(candidate, message, new TheLocalFix());
            }
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) ArrayCreationExpression(com.jetbrains.php.lang.psi.elements.ArrayCreationExpression) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) PsiWhiteSpace(com.intellij.psi.PsiWhiteSpace) NotNull(org.jetbrains.annotations.NotNull)

Example 8 with BasePhpElementVisitor

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

the class ExceptionsAnnotatingAndHandlingInspector method buildVisitor.

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

        @Override
        public void visitPhpFinally(@NotNull Finally element) {
            PhpLanguageLevel phpVersion = PhpProjectConfigurationFacade.getInstance(holder.getProject()).getLanguageLevel();
            if (!phpVersion.hasFeature(PhpLanguageFeature.FINALLY)) {
                return;
            }
            final HashSet<PsiElement> processedRegistry = new HashSet<>();
            final HashMap<PhpClass, HashSet<PsiElement>> exceptions = CollectPossibleThrowsUtil.collectNestedAndWorkflowExceptions(element, processedRegistry, holder);
            /* report individual statements */
            if (exceptions.size() > 0) {
                final Set<PsiElement> reportedExpressions = new HashSet<>();
                for (final Set<PsiElement> pool : exceptions.values()) {
                    pool.stream().filter(expression -> !reportedExpressions.contains(expression)).forEach(expression -> {
                        holder.registerProblem(this.getReportingTarget(expression), messageFinallyExceptions, ProblemHighlightType.GENERIC_ERROR);
                        reportedExpressions.add(expression);
                    });
                    pool.clear();
                }
                reportedExpressions.clear();
                exceptions.clear();
            }
            /* report try-blocks */
            if (processedRegistry.size() > 0) {
                processedRegistry.stream().filter(statement -> statement instanceof Try).forEach(statement -> holder.registerProblem(statement.getFirstChild(), messageFinallyExceptions, ProblemHighlightType.GENERIC_ERROR));
                processedRegistry.clear();
            }
        }

        @Override
        public void visitPhpMethod(@NotNull Method method) {
            if (method.isAbstract() || this.isTestContext(method)) {
                return;
            }
            // __toString has magic methods validation, must not raise exceptions
            final PsiElement methodName = NamedElementUtil.getNameIdentifier(method);
            if (null == methodName || method.getName().equals("__toString")) {
                return;
            }
            /* collect announced cases */
            final HashSet<PhpClass> annotatedExceptions = new HashSet<>();
            final boolean hasPhpDoc = method.getDocComment() != null;
            if (!ThrowsResolveUtil.resolveThrownExceptions(method, annotatedExceptions)) {
                return;
            }
            HashSet<PsiElement> processedRegistry = new HashSet<>();
            HashMap<PhpClass, HashSet<PsiElement>> throwsExceptions = CollectPossibleThrowsUtil.collectNestedAndWorkflowExceptions(method, processedRegistry, holder);
            processedRegistry.clear();
            /* exclude annotated exceptions, identify which has not been thrown */
            final Set<PhpClass> annotatedButNotThrownExceptions = new HashSet<>(annotatedExceptions);
            /* release bundled expressions */
            /* actualize un-thrown exceptions registry */
            annotatedExceptions.stream().filter(key -> hasPhpDoc && throwsExceptions.containsKey(key)).forEach(annotated -> {
                /* release bundled expressions */
                throwsExceptions.get(annotated).clear();
                throwsExceptions.remove(annotated);
                /* actualize un-thrown exceptions registry */
                annotatedButNotThrownExceptions.remove(annotated);
            });
            /* do reporting now: exceptions annotated, but not thrown */
            if (REPORT_NON_THROWN_EXCEPTIONS && annotatedButNotThrownExceptions.size() > 0) {
                final List<String> toReport = annotatedButNotThrownExceptions.stream().map(PhpNamedElement::getFQN).collect(Collectors.toList());
                final String message = messagePatternUnthrown.replace("%c%", String.join(", ", toReport));
                holder.registerProblem(methodName, message, ProblemHighlightType.WEAK_WARNING);
                toReport.clear();
            }
            annotatedButNotThrownExceptions.clear();
            /* do reporting now: exceptions thrown but not annotated */
            if (throwsExceptions.size() > 0) {
                /* deeper analysis needed */
                HashMap<PhpClass, HashSet<PsiElement>> unhandledExceptions = new HashMap<>();
                if (!annotatedExceptions.isEmpty() && hasPhpDoc) {
                    /* filter what to report based on annotated exceptions  */
                    for (final PhpClass annotated : annotatedExceptions) {
                        for (final Map.Entry<PhpClass, HashSet<PsiElement>> throwsExceptionsPair : throwsExceptions.entrySet()) {
                            final PhpClass thrown = throwsExceptionsPair.getKey();
                            /* already reported */
                            if (unhandledExceptions.containsKey(thrown)) {
                                continue;
                            }
                            /* check thrown parents, as annotated not processed here */
                            final HashSet<PhpClass> thrownVariants = InterfacesExtractUtil.getCrawlInheritanceTree(thrown, true);
                            if (!thrownVariants.contains(annotated)) {
                                unhandledExceptions.put(thrown, throwsExceptionsPair.getValue());
                                throwsExceptions.put(thrown, null);
                            }
                            thrownVariants.clear();
                        }
                    }
                } else {
                    /* report all, as nothing is annotated */
                    for (Map.Entry<PhpClass, HashSet<PsiElement>> throwsExceptionsPair : throwsExceptions.entrySet()) {
                        final PhpClass thrown = throwsExceptionsPair.getKey();
                        /* already reported */
                        if (unhandledExceptions.containsKey(thrown)) {
                            continue;
                        }
                        unhandledExceptions.put(thrown, throwsExceptionsPair.getValue());
                        throwsExceptions.put(thrown, null);
                    }
                }
                if (unhandledExceptions.size() > 0) {
                    for (final Map.Entry<PhpClass, HashSet<PsiElement>> unhandledExceptionsPair : unhandledExceptions.entrySet()) {
                        final String thrown = unhandledExceptionsPair.getKey().getFQN();
                        final Set<PsiElement> blamedExpressions = unhandledExceptionsPair.getValue();
                        if (!configuration.contains(thrown)) {
                            final String message = messagePattern.replace("%c%", thrown);
                            for (final PsiElement blame : blamedExpressions) {
                                final LocalQuickFix fix = hasPhpDoc ? new MissingThrowAnnotationLocalFix(method, thrown) : null;
                                holder.registerProblem(this.getReportingTarget(blame), message, ProblemHighlightType.WEAK_WARNING, fix);
                            }
                        }
                        blamedExpressions.clear();
                    }
                    unhandledExceptions.clear();
                }
                throwsExceptions.clear();
            }
            annotatedExceptions.clear();
        }

        @NotNull
        private PsiElement getReportingTarget(@NotNull PsiElement expression) {
            PsiElement result = expression;
            if (expression instanceof FunctionReference) {
                final PsiElement nameNode = (PsiElement) ((FunctionReference) expression).getNameNode();
                if (nameNode != null) {
                    result = nameNode;
                }
            } else if (expression instanceof PhpThrow) {
                final PsiElement subject = ((PhpThrow) expression).getArgument();
                if (subject instanceof NewExpression) {
                    final PsiElement reference = ((NewExpression) subject).getClassReference();
                    if (reference != null) {
                        result = reference;
                    }
                }
            }
            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) ThrowsResolveUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.phpDoc.ThrowsResolveUtil) ProblemDescriptor(com.intellij.codeInspection.ProblemDescriptor) PhpLanguageLevel(com.jetbrains.php.config.PhpLanguageLevel) PsiElement(com.intellij.psi.PsiElement) Project(com.intellij.openapi.project.Project) PhpProjectConfigurationFacade(com.jetbrains.php.config.PhpProjectConfigurationFacade) LocalQuickFix(com.intellij.codeInspection.LocalQuickFix) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) PhpPsiElementFactory(com.jetbrains.php.lang.psi.PhpPsiElementFactory) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) InterfacesExtractUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.hierarhy.InterfacesExtractUtil) PhpLanguageFeature(com.jetbrains.php.config.PhpLanguageFeature) OptionsComponent(com.kalessil.phpStorm.phpInspectionsEA.options.OptionsComponent) Collectors(java.util.stream.Collectors) SmartPointerManager(com.intellij.psi.SmartPointerManager) CollectPossibleThrowsUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.phpExceptions.CollectPossibleThrowsUtil) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) SmartPsiElementPointer(com.intellij.psi.SmartPsiElementPointer) NamedElementUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.NamedElementUtil) ProblemHighlightType(com.intellij.codeInspection.ProblemHighlightType) NotNull(org.jetbrains.annotations.NotNull) javax.swing(javax.swing) LocalQuickFix(com.intellij.codeInspection.LocalQuickFix) NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PsiElement(com.intellij.psi.PsiElement) PhpLanguageLevel(com.jetbrains.php.config.PhpLanguageLevel) NotNull(org.jetbrains.annotations.NotNull)

Example 9 with BasePhpElementVisitor

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

the class AdditionOperationOnArraysInspection method buildVisitor.

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

        @Override
        public void visitPhpBinaryExpression(@NotNull BinaryExpression expression) {
            final PsiElement operation = expression.getOperation();
            if (OpenapiTypesUtil.is(operation, PhpTokenTypes.opPLUS)) {
                /* do not check nested operations */
                final boolean isNestedBinary = expression.getParent() instanceof BinaryExpression;
                if (!isNestedBinary) {
                    /* do not report ' ... + []' and '[] + ...' */
                    final PsiElement right = expression.getRightOperand();
                    PsiElement left = expression.getLeftOperand();
                    while (left instanceof BinaryExpression) {
                        left = ((BinaryExpression) left).getLeftOperand();
                    }
                    if (left != null && right != null) {
                        final boolean addsImplicitArray = left instanceof ArrayCreationExpression || right instanceof ArrayCreationExpression;
                        if (!addsImplicitArray) {
                            this.inspectExpression(operation, expression);
                        }
                    }
                }
            }
        }

        @Override
        public void visitPhpSelfAssignmentExpression(@NotNull SelfAssignmentExpression expression) {
            final PsiElement operation = expression.getOperation();
            if (OpenapiTypesUtil.is(operation, PhpTokenTypes.opPLUS_ASGN)) {
                /* do not report '... += []' */
                final boolean addsImplicitArray = expression.getValue() instanceof ArrayCreationExpression;
                if (!addsImplicitArray) {
                    this.inspectExpression(operation, expression);
                }
            }
        }

        /* inspection itself */
        private void inspectExpression(@NotNull PsiElement operation, @NotNull PsiElement expression) {
            if (expression instanceof PhpTypedElement) {
                final Set<String> types = new HashSet<>();
                final PhpType resolved = OpenapiResolveUtil.resolveType((PhpTypedElement) expression, holder.getProject());
                if (resolved != null) {
                    resolved.filterUnknown().getTypes().forEach(t -> types.add(Types.getType(t)));
                }
                if (types.size() == 1 && types.contains(Types.strArray)) {
                    holder.registerProblem(operation, message);
                }
                types.clear();
            }
        }
    };
}
Also used : SelfAssignmentExpression(com.jetbrains.php.lang.psi.elements.SelfAssignmentExpression) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) ArrayCreationExpression(com.jetbrains.php.lang.psi.elements.ArrayCreationExpression) PhpTypedElement(com.jetbrains.php.lang.psi.elements.PhpTypedElement) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) HashSet(java.util.HashSet) NotNull(org.jetbrains.annotations.NotNull)

Example 10 with BasePhpElementVisitor

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

the class ExplodeMissUseInspector method buildVisitor.

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

        @Override
        public void visitPhpFunctionCall(@NotNull FunctionReference reference) {
            /* general structure expectations */
            final String functionName = reference.getName();
            if (functionName == null || !semanticMapping.containsKey(functionName)) {
                return;
            }
            final PsiElement[] arguments = reference.getParameters();
            if (arguments.length != 1) {
                return;
            }
            /* discover possible values */
            final Set<PsiElement> values = PossibleValuesDiscoveryUtil.discover(arguments[0]);
            /* do not analyze invariants */
            if (1 == values.size()) {
                final PsiElement value = values.iterator().next();
                values.clear();
                if (OpenapiTypesUtil.isFunctionReference(value)) {
                    /* inner call must be explode() */
                    final FunctionReference innerCall = (FunctionReference) value;
                    final String innerFunctionName = innerCall.getName();
                    if (innerFunctionName == null || !innerFunctionName.equals("explode")) {
                        return;
                    }
                    final PsiElement[] innerArguments = innerCall.getParameters();
                    if (innerArguments.length != 2) {
                        return;
                    }
                    /* if the parameter is a variable, ensure it used only 2 times (write, read) */
                    if (arguments[0] instanceof Variable) {
                        final PhpScopeHolder parentScope = ExpressionSemanticUtil.getScope(reference);
                        if (null != parentScope) {
                            final PhpAccessVariableInstruction[] usages = PhpControlFlowUtil.getFollowingVariableAccessInstructions(parentScope.getControlFlow().getEntryPoint(), ((Variable) arguments[0]).getName(), false);
                            if (2 != usages.length) {
                                return;
                            }
                        }
                    }
                    final String replacement = semanticMapping.get(functionName).replace("%f%", innerArguments[0].getText()).replace("%s%", innerArguments[1].getText());
                    final String message = messagePattern.replace("%e%", replacement);
                    if (arguments[0] == value) {
                        holder.registerProblem(reference, message, new UseAlternativeFix(replacement));
                    } else {
                        holder.registerProblem(reference, message);
                    }
                }
            }
            values.clear();
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PhpAccessVariableInstruction(com.jetbrains.php.codeInsight.controlFlow.instructions.PhpAccessVariableInstruction) Variable(com.jetbrains.php.lang.psi.elements.Variable) PhpScopeHolder(com.jetbrains.php.codeInsight.PhpScopeHolder) FunctionReference(com.jetbrains.php.lang.psi.elements.FunctionReference) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) 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