Search in sources :

Example 26 with BasePhpElementVisitor

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

the class PrintfScanfArgumentsInspector 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 || !functions.containsKey(functionName)) {
                return;
            }
            /* resolve needed parameter */
            final int neededPosition = functions.get(functionName);
            final int minimumArgumentsForAnalysis = neededPosition + 1;
            StringLiteralExpression pattern = null;
            final PsiElement[] params = reference.getParameters();
            if (params.length >= minimumArgumentsForAnalysis) {
                pattern = ExpressionSemanticUtil.resolveAsStringLiteral(params[neededPosition]);
            }
            /* not available */
            if (null == pattern) {
                return;
            }
            final String content = pattern.getContents().trim();
            if (!content.isEmpty()) {
                /* find valid placeholders and extract positions specifiers as well */
                int countWithoutPositionSpecifier = 0;
                int maxPositionSpecifier = 0;
                int countParsedAll = 0;
                /* do normalization: %%, inline variables */
                final String contentAdapted = content.replace("%%", "");
                final String contentNoVars = contentAdapted.replaceAll("\\$\\{?\\$?[a-zA-Z0-9]+\\}?", "");
                if (contentAdapted.length() != contentNoVars.length()) {
                    return;
                }
                final Matcher regexMatcher = regexPlaceHolders.matcher(contentAdapted);
                while (regexMatcher.find()) {
                    ++countParsedAll;
                    if (null != regexMatcher.group(2)) {
                        maxPositionSpecifier = Math.max(maxPositionSpecifier, Integer.parseInt(regexMatcher.group(2)));
                        continue;
                    }
                    ++countWithoutPositionSpecifier;
                }
                final int expectedParametersCount = minimumArgumentsForAnalysis + Math.max(countWithoutPositionSpecifier, maxPositionSpecifier);
                /* check for pattern validity */
                final int parametersInPattern = StringUtils.countMatches(content.replace("%%", "").replace("%*", ""), "%");
                if (countParsedAll != parametersInPattern) {
                    holder.registerProblem(params[neededPosition], MessagesPresentationUtil.prefixWithEa(messagePattern), ProblemHighlightType.GENERIC_ERROR);
                    return;
                }
                /* check for arguments matching */
                if (expectedParametersCount != params.length) {
                    /* fscanf/sscanf will also return parsed values as an array if no values containers provided */
                    if (params.length == 2) {
                        final boolean returnsArray = functionName.equals("fscanf") || functionName.equals("sscanf");
                        final PsiElement parent = returnsArray ? reference.getParent() : null;
                        final PsiElement grandParent = parent == null ? null : parent.getParent();
                        if (returnsArray && parent != null) {
                            /* false-positive: dispatching/deconstructing into containers */
                            if (parent instanceof AssignmentExpression || grandParent instanceof AssignmentExpression) {
                                return;
                            } else /* false-positive: dispatching into calls */
                            if (parent instanceof ParameterList && grandParent instanceof FunctionReference) {
                                return;
                            }
                        }
                    }
                    /* false-positives: variadic */
                    if (OpenapiTypesUtil.is(params[params.length - 1].getPrevSibling(), PhpTokenTypes.opVARIADIC)) {
                        return;
                    }
                    /* false-positives: variable modification */
                    final PsiElement argumentWithPattern = params[neededPosition];
                    if (argumentWithPattern instanceof Variable) {
                        final Function function = ExpressionSemanticUtil.getScope(argumentWithPattern);
                        final GroupStatement body = function == null ? null : ExpressionSemanticUtil.getGroupStatement(function);
                        if (body != null) {
                            for (final Variable candidate : PsiTreeUtil.findChildrenOfType(body, Variable.class)) {
                                final PsiElement parent = candidate.getParent();
                                final boolean isModified = parent instanceof AssignmentExpression && !OpenapiTypesUtil.isAssignment(parent) && candidate == ((AssignmentExpression) parent).getVariable() && OpenapiEquivalenceUtil.areEqual(candidate, argumentWithPattern);
                                if (isModified) {
                                    return;
                                }
                            }
                        }
                    }
                    holder.registerProblem(reference, MessagesPresentationUtil.prefixWithEa(messageParameters.replace("%c%", String.valueOf(expectedParametersCount))), ProblemHighlightType.GENERIC_ERROR);
                }
            }
        }
    };
}
Also used : Matcher(java.util.regex.Matcher) NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 27 with BasePhpElementVisitor

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

the class ThrowRawExceptionInspector method buildVisitor.

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

        @Override
        public void visitPhpThrowExpression(@NotNull PhpThrowExpression expression) {
            final PsiElement argument = expression.getArgument();
            if (argument instanceof NewExpression) {
                final NewExpression newExpression = (NewExpression) argument;
                final ClassReference classReference = newExpression.getClassReference();
                final String classFqn = classReference == null ? null : classReference.getFQN();
                if (classFqn != null) {
                    if (classFqn.equals("\\Exception")) {
                        holder.registerProblem(classReference, MessagesPresentationUtil.prefixWithEa(messageRawException), new TheLocalFix());
                    } else if (REPORT_MISSING_ARGUMENTS && newExpression.getParameters().length == 0) {
                        final PsiElement resolved = OpenapiResolveUtil.resolveReference(classReference);
                        if (resolved instanceof PhpClass && this.isTarget((PhpClass) resolved)) {
                            holder.registerProblem(newExpression, MessagesPresentationUtil.prefixWithEa(messageNoArguments));
                        }
                    }
                }
            }
        }

        private boolean isTarget(@NotNull PhpClass clazz) {
            final Method constructor = clazz.getConstructor();
            return constructor != null && constructor.getParameters().length == 3 && clazz.findOwnFieldByName("message", false) == null;
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) NewExpression(com.jetbrains.php.lang.psi.elements.NewExpression) PhpClass(com.jetbrains.php.lang.psi.elements.PhpClass) PhpThrowExpression(com.kalessil.phpStorm.phpInspectionsEA.openApi.elements.PhpThrowExpression) ClassReference(com.jetbrains.php.lang.psi.elements.ClassReference) Method(com.jetbrains.php.lang.psi.elements.Method) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 28 with BasePhpElementVisitor

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

the class UnqualifiedReferenceInspector 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.isEmpty()) {
                /* ensure php version is at least PHP 7.0; makes sense only with PHP7+ opcode */
                if (PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP700)) {
                    if (REPORT_ALL_FUNCTIONS || advancedOpcode.contains(functionName)) {
                        this.analyzeReference(reference, functionName);
                    }
                    if (callbacksPositions.containsKey(functionName)) {
                        this.analyzeCallback(reference, functionName);
                    }
                }
            }
        }

        @Override
        public void visitPhpConstantReference(@NotNull ConstantReference reference) {
            final String constantName = reference.getName();
            if (constantName != null && !constantName.isEmpty() && REPORT_CONSTANTS) {
                /* ensure php version is at least PHP 7.0; makes sense only with PHP7+ opcode */
                if (PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP700)) {
                    this.analyzeReference(reference, constantName);
                }
            }
        }

        private void analyzeCallback(@NotNull FunctionReference reference, @NotNull String functionName) {
            final PsiElement[] arguments = reference.getParameters();
            if (arguments.length >= 2) {
                final Integer callbackPosition = callbacksPositions.get(functionName);
                if (arguments[callbackPosition] instanceof StringLiteralExpression) {
                    final StringLiteralExpression callback = (StringLiteralExpression) arguments[callbackPosition];
                    if (callback.getFirstPsiChild() == null) {
                        final String function = callback.getContents();
                        final boolean isCandidate = !function.startsWith("\\") && !function.contains("::");
                        if (isCandidate && (REPORT_ALL_FUNCTIONS || advancedOpcode.contains(function))) {
                            final PhpIndex index = PhpIndex.getInstance(holder.getProject());
                            if (!index.getFunctionsByFQN('\\' + functionName).isEmpty()) {
                                holder.registerProblem(callback, String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), function), new TheLocalFix());
                            }
                        }
                    }
                }
            }
        }

        private void analyzeReference(@NotNull PhpReference reference, @NotNull String referenceName) {
            /* some constants prefixing is making no sense IMO */
            if (reference instanceof ConstantReference && falsePositives.contains(referenceName)) {
                return;
            }
            /* NS specification is identified differently for { define } and { call, constant } */
            final PsiElement nsCandidate = reference.getFirstChild();
            if (nsCandidate instanceof PhpNamespaceReference || OpenapiTypesUtil.is(nsCandidate, PhpTokenTypes.NAMESPACE_RESOLUTION)) {
                return;
            }
            final PhpNamespace ns = this.findNamespace(reference);
            if (ns == null) {
                return;
            }
            /* resolve the constant/function, report if it's from the root NS */
            final PsiElement subject = OpenapiResolveUtil.resolveReference(reference);
            final boolean isFunction = subject instanceof Function;
            if (isFunction || subject instanceof Constant) {
                /* false-positives: non-root NS function/constant referenced */
                if (((PhpNamedElement) subject).getFQN().equals('\\' + referenceName)) {
                    final GroupStatement body = ns.getStatements();
                    if (body != null) {
                        final List<PhpUse> imports = new ArrayList<>();
                        for (final PsiElement child : body.getChildren()) {
                            if (child instanceof PhpUseList) {
                                Collections.addAll(imports, ((PhpUseList) child).getDeclarations());
                            }
                        }
                        /* false-positive: function/constant are imported already */
                        boolean isImported = false;
                        for (final PhpUse use : imports) {
                            final PsiElement candidate = use.getFirstPsiChild();
                            String importedSymbol = null;
                            if (candidate instanceof FunctionReference) {
                                importedSymbol = ((FunctionReference) candidate).getName();
                            } else if (candidate instanceof ConstantReference) {
                                importedSymbol = ((ConstantReference) candidate).getName();
                            }
                            if (isImported = referenceName.equals(importedSymbol)) {
                                break;
                            }
                        }
                        imports.clear();
                        if (!isImported) {
                            holder.registerProblem(reference, String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), referenceName + (isFunction ? "(...)" : "")), new TheLocalFix());
                        }
                    }
                }
            }
        }

        @Nullable
        private PhpNamespace findNamespace(@NotNull PhpReference reference) {
            final PsiFile file = reference.getContainingFile();
            if (file.getFileType() == PhpFileType.INSTANCE) {
                final List<PhpNamespace> namespaces = new ArrayList<>();
                ((PhpFile) file).getTopLevelDefs().values().stream().filter(definition -> definition instanceof PhpNamespace).forEach(definition -> namespaces.add((PhpNamespace) definition));
                if (namespaces.isEmpty()) {
                    return null;
                } else if (namespaces.size() == 1) {
                    return namespaces.get(0);
                }
                namespaces.clear();
            }
            return (PhpNamespace) PsiTreeUtil.findFirstParent(reference, PARENT_NAMESPACE);
        }
    };
}
Also used : java.util(java.util) com.jetbrains.php.lang.psi.elements(com.jetbrains.php.lang.psi.elements) PhpTokenTypes(com.jetbrains.php.lang.lexer.PhpTokenTypes) PhpIndex(com.jetbrains.php.PhpIndex) PsiTreeUtil(com.intellij.psi.util.PsiTreeUtil) OpenapiTypesUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiTypesUtil) ProblemDescriptor(com.intellij.codeInspection.ProblemDescriptor) MessagesPresentationUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil) PsiElement(com.intellij.psi.PsiElement) Project(com.intellij.openapi.project.Project) PsiFile(com.intellij.psi.PsiFile) LocalQuickFix(com.intellij.codeInspection.LocalQuickFix) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) PhpFileType(com.jetbrains.php.lang.PhpFileType) PhpPsiElementFactory(com.jetbrains.php.lang.psi.PhpPsiElementFactory) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) PhpLanguageLevel(com.kalessil.phpStorm.phpInspectionsEA.openApi.PhpLanguageLevel) OptionsComponent(com.kalessil.phpStorm.phpInspectionsEA.options.OptionsComponent) Nullable(org.jetbrains.annotations.Nullable) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PhpFile(com.jetbrains.php.lang.psi.PhpFile) OpenapiResolveUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiResolveUtil) NotNull(org.jetbrains.annotations.NotNull) Condition(com.intellij.openapi.util.Condition) javax.swing(javax.swing) PhpFile(com.jetbrains.php.lang.psi.PhpFile) NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PsiFile(com.intellij.psi.PsiFile) PsiElement(com.intellij.psi.PsiElement) PhpIndex(com.jetbrains.php.PhpIndex) NotNull(org.jetbrains.annotations.NotNull)

Example 29 with BasePhpElementVisitor

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

the class ArrayPushMissUseInspector 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("array_push")) {
                final PsiElement[] arguments = reference.getParameters();
                if (arguments.length == 2 && OpenapiTypesUtil.isStatementImpl(reference.getParent())) {
                    PsiElement variadicCandidate = arguments[1].getPrevSibling();
                    if (variadicCandidate instanceof PsiWhiteSpace) {
                        variadicCandidate = variadicCandidate.getPrevSibling();
                    }
                    if (!OpenapiTypesUtil.is(variadicCandidate, PhpTokenTypes.opVARIADIC)) {
                        final String replacement = String.format("%s[] = %s", arguments[0].getText(), arguments[1].getText());
                        holder.registerProblem(reference, String.format(MessagesPresentationUtil.prefixWithEa(messageMisuse), replacement), new UseElementPushFix(replacement));
                    }
                }
            }
        }

        @Override
        public void visitPhpArrayAccessExpression(@NotNull ArrayAccessExpression expression) {
            if (REPORT_EXCESSIVE_COUNT_CALLS) {
                final PsiElement parent = expression.getParent();
                if (OpenapiTypesUtil.isAssignment(parent)) {
                    final PsiElement value = ((AssignmentExpression) parent).getValue();
                    if (value != expression) {
                        final ArrayIndex index = expression.getIndex();
                        if (index != null) {
                            final PsiElement candidate = index.getValue();
                            if (OpenapiTypesUtil.isFunctionReference(candidate)) {
                                final FunctionReference reference = (FunctionReference) candidate;
                                final String functionName = reference.getName();
                                if (functionName != null && functionName.equals("count")) {
                                    final PsiElement[] arguments = reference.getParameters();
                                    if (arguments.length == 1) {
                                        final PsiElement container = expression.getValue();
                                        if (container != null && OpenapiEquivalenceUtil.areEqual(container, arguments[0])) {
                                            holder.registerProblem(reference, MessagesPresentationUtil.prefixWithEa(messageUnneeded), ProblemHighlightType.LIKE_UNUSED_SYMBOL);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) AssignmentExpression(com.jetbrains.php.lang.psi.elements.AssignmentExpression) FunctionReference(com.jetbrains.php.lang.psi.elements.FunctionReference) NotNull(org.jetbrains.annotations.NotNull) ArrayAccessExpression(com.jetbrains.php.lang.psi.elements.ArrayAccessExpression) ArrayIndex(com.jetbrains.php.lang.psi.elements.ArrayIndex) PsiElement(com.intellij.psi.PsiElement) PsiWhiteSpace(com.intellij.psi.PsiWhiteSpace) NotNull(org.jetbrains.annotations.NotNull)

Example 30 with BasePhpElementVisitor

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

the class DateIntervalSpecificationInspector method buildVisitor.

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

        @Override
        public void visitPhpNewExpression(@NotNull NewExpression expression) {
            /* before inspecting check parameters amount */
            final PsiElement[] params = expression.getParameters();
            if (params.length == 1) {
                final ClassReference classReference = expression.getClassReference();
                final String classFQN = classReference == null ? null : classReference.getFQN();
                if (classFQN == null || !classFQN.equals("\\DateInterval")) {
                    /* TODO: child classes support */
                    return;
                }
                /* now try getting string literal and test against valid patterns */
                final StringLiteralExpression pattern = ExpressionSemanticUtil.resolveAsStringLiteral(params[0]);
                if (pattern != null && pattern.getFirstPsiChild() == null) {
                    final String input = pattern.getContents();
                    if (!regexRegular.matcher(input).find() && !regexDateTimeAlike.matcher(input).find()) {
                        holder.registerProblem(pattern, MessagesPresentationUtil.prefixWithEa(message));
                    }
                }
            }
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) StringLiteralExpression(com.jetbrains.php.lang.psi.elements.StringLiteralExpression) NewExpression(com.jetbrains.php.lang.psi.elements.NewExpression) ClassReference(com.jetbrains.php.lang.psi.elements.ClassReference) 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