Search in sources :

Example 21 with StringLiteralExpression

use of com.jetbrains.php.lang.psi.elements.StringLiteralExpression in project phpinspectionsea by kalessil.

the class PackedHashtableOptimizationInspector method buildVisitor.

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

        /* TODO: docs, http://blog.jpauli.tech/2016/04/08/hashtables.html#packed-hashtable-optimization */
        @Override
        public void visitPhpArrayCreationExpression(@NotNull ArrayCreationExpression expression) {
            /* requires PHP7 */
            if (PhpLanguageLevel.get(holder.getProject()).below(PhpLanguageLevel.PHP700)) {
                return;
            }
            /* requires at least 3 children - let array to grow enough */
            final PsiElement[] children = expression.getChildren();
            if (children.length < 3) {
                return;
            }
            /* false-positives: test classes */
            if (this.isTestContext(expression)) {
                return;
            }
            /* step 1: collect indexes and verify array structure */
            final List<PhpPsiElement> indexes = new ArrayList<>();
            for (final PsiElement pairCandidate : children) {
                if (pairCandidate instanceof ArrayHashElement) {
                    final PhpPsiElement key = ((ArrayHashElement) pairCandidate).getKey();
                    if ((key instanceof StringLiteralExpression && key.getFirstPsiChild() == null) || OpenapiTypesUtil.isNumber(key)) {
                        indexes.add(key);
                        continue;
                    }
                }
                break;
            }
            if (indexes.size() != children.length) {
                indexes.clear();
                return;
            }
            /* step 2: analyze collected indexes */
            // if string literal is not numeric => stop
            boolean hasStringIndexes = false;
            boolean hasIncreasingIndexes = true;
            int lastIndex = Integer.MIN_VALUE;
            for (PhpPsiElement index : indexes) {
                final String numericIndex;
                final int integerIndex;
                /* extract text representation of the index */
                if (index instanceof StringLiteralExpression) {
                    hasStringIndexes = true;
                    numericIndex = ((StringLiteralExpression) index).getContents();
                    /* '01' and etc cases can not be converted */
                    if (numericIndex.length() > 1 && '0' == numericIndex.charAt(0)) {
                        indexes.clear();
                        return;
                    }
                } else {
                    numericIndex = index.getText().replaceAll("\\s+", "");
                }
                /* try converting into integer */
                try {
                    integerIndex = Integer.parseInt(numericIndex);
                } catch (NumberFormatException error) {
                    indexes.clear();
                    return;
                }
                if (integerIndex < lastIndex) {
                    hasIncreasingIndexes = false;
                }
                lastIndex = integerIndex;
            }
            /* report if criteria are met */
            if (!hasIncreasingIndexes) {
                holder.registerProblem(expression.getFirstChild(), MessagesPresentationUtil.prefixWithEa(messageReorder));
            }
            if (hasIncreasingIndexes && hasStringIndexes) {
                holder.registerProblem(expression.getFirstChild(), MessagesPresentationUtil.prefixWithEa(messageUseNumericKeys));
            }
        }
    };
}
Also used : ArrayCreationExpression(com.jetbrains.php.lang.psi.elements.ArrayCreationExpression) StringLiteralExpression(com.jetbrains.php.lang.psi.elements.StringLiteralExpression) ArrayList(java.util.ArrayList) ArrayHashElement(com.jetbrains.php.lang.psi.elements.ArrayHashElement) NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PhpPsiElement(com.jetbrains.php.lang.psi.elements.PhpPsiElement) PhpPsiElement(com.jetbrains.php.lang.psi.elements.PhpPsiElement) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 22 with StringLiteralExpression

use of com.jetbrains.php.lang.psi.elements.StringLiteralExpression in project phpinspectionsea by kalessil.

the class DuplicateArrayKeysInspector method buildVisitor.

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

        @Override
        public void visitPhpArrayCreationExpression(@NotNull ArrayCreationExpression expression) {
            final Map<String, PsiElement> processed = new HashMap<>();
            for (final ArrayHashElement pair : expression.getHashElements()) {
                final PhpPsiElement key = pair.getKey();
                if (key instanceof StringLiteralExpression && key.getFirstPsiChild() == null) {
                    final PsiElement value = pair.getValue();
                    if (value != null) {
                        final String literal = ((StringLiteralExpression) key).getContents();
                        if (processed.containsKey(literal)) {
                            final boolean isPairDuplicated = !(value instanceof ArrayCreationExpression) && OpenapiEquivalenceUtil.areEqual(value, processed.get(literal));
                            if (isPairDuplicated) {
                                holder.registerProblem(pair, MessagesPresentationUtil.prefixWithEa(messageDuplicatePair), ProblemHighlightType.LIKE_UNUSED_SYMBOL);
                            } else {
                                holder.registerProblem(key, MessagesPresentationUtil.prefixWithEa(messageDuplicateKey));
                            }
                        }
                        processed.put(literal, value);
                    }
                }
            }
            processed.clear();
        }
    };
}
Also used : PhpElementVisitor(com.jetbrains.php.lang.psi.visitors.PhpElementVisitor) ArrayCreationExpression(com.jetbrains.php.lang.psi.elements.ArrayCreationExpression) HashMap(java.util.HashMap) StringLiteralExpression(com.jetbrains.php.lang.psi.elements.StringLiteralExpression) ArrayHashElement(com.jetbrains.php.lang.psi.elements.ArrayHashElement) NotNull(org.jetbrains.annotations.NotNull) PhpPsiElement(com.jetbrains.php.lang.psi.elements.PhpPsiElement) PhpPsiElement(com.jetbrains.php.lang.psi.elements.PhpPsiElement) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 23 with StringLiteralExpression

use of com.jetbrains.php.lang.psi.elements.StringLiteralExpression in project phpinspectionsea by kalessil.

the class GreaterOrEqualInHashElementStrategy method apply.

public static boolean apply(@NotNull BinaryExpression expression, @NotNull ProblemsHolder holder) {
    /* general structure expectations */
    final PsiElement operation = expression.getOperation();
    if (null == operation || PhpTokenTypes.opGREATER_OR_EQUAL != operation.getNode().getElementType()) {
        return false;
    }
    final PsiElement left = expression.getLeftOperand();
    if (!(left instanceof StringLiteralExpression)) {
        return false;
    }
    /* analysis itself */
    final PsiElement parent = expression.getParent();
    if (null != parent && parent.getParent() instanceof ArrayCreationExpression) {
        holder.registerProblem(operation, MessagesPresentationUtil.prefixWithEa(message));
        return true;
    }
    return false;
}
Also used : StringLiteralExpression(com.jetbrains.php.lang.psi.elements.StringLiteralExpression) ArrayCreationExpression(com.jetbrains.php.lang.psi.elements.ArrayCreationExpression) PsiElement(com.intellij.psi.PsiElement)

Example 24 with StringLiteralExpression

use of com.jetbrains.php.lang.psi.elements.StringLiteralExpression in project phpinspectionsea by kalessil.

the class PlainApiUseCheckStrategy method apply.

public static void apply(final String functionName, @NotNull final FunctionReference reference, @Nullable final String modifiers, final String pattern, @NotNull final ProblemsHolder holder) {
    final PsiElement[] params = reference.getParameters();
    final int parametersCount = params.length;
    if (parametersCount >= 2 && !StringUtils.isEmpty(pattern)) {
        final String patternAdapted = pattern.replace("a-zA-Z", "A-Za-z").replace("0-9A-Za-z", "A-Za-z0-9");
        final Matcher regexMatcher = regexTextSearch.matcher(patternAdapted);
        if (regexMatcher.find()) {
            final boolean ignoreCase = !StringUtils.isEmpty(modifiers) && modifiers.indexOf('i') != -1;
            final boolean startWith = !StringUtils.isEmpty(regexMatcher.group(1));
            final boolean endsWith = !StringUtils.isEmpty(regexMatcher.group(3));
            /* analyse if pattern is the one strategy targeting */
            String message = null;
            LocalQuickFix fixer = null;
            if (parametersCount == 2 && functionName.equals("preg_match")) {
                final boolean isInverted = isPregMatchInverted(reference);
                if (startWith && endsWith && !ignoreCase) {
                    final String replacement = String.format("\"%s\" %s %s", unescape(regexMatcher.group(2)), isInverted ? "!==" : "===", params[1].getText());
                    message = String.format(messagePattern, replacement);
                    fixer = new UseStringComparisonFix(replacement);
                } else if (startWith && !endsWith) {
                    final String replacement = String.format("0 %s %s(%s, \"%s\")", isInverted ? "!==" : "===", ignoreCase ? "stripos" : "strpos", params[1].getText(), unescape(regexMatcher.group(2)));
                    message = String.format(messagePattern, replacement);
                    fixer = new UseStringPositionFix(replacement);
                } else if (!startWith && !endsWith) {
                    final String replacement = String.format("false %s %s(%s, \"%s\")", isInverted ? "===" : "!==", ignoreCase ? "stripos" : "strpos", params[1].getText(), unescape(regexMatcher.group(2)));
                    message = String.format(messagePattern, replacement);
                    fixer = new UseStringPositionFix(replacement);
                }
            } else if (parametersCount == 3 && functionName.equals("preg_replace") && !startWith && !endsWith) {
                // mixed str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] )
                final String replacement = "%f%(\"%p%\", %r%, %s%)".replace("%s%", params[2].getText()).replace("%r%", params[1].getText()).replace("%p%", unescape(regexMatcher.group(2))).replace("%f%", ignoreCase ? "str_ireplace" : "str_replace");
                message = String.format(messagePattern, replacement);
                fixer = new UseStringReplaceFix(replacement);
            }
            if (message != null) {
                holder.registerProblem(getPregMatchContext(reference), MessagesPresentationUtil.prefixWithEa(message), fixer);
                return;
            }
        }
        /* investigate using *trim(...) instead */
        final Matcher trimMatcher = trimPatterns.matcher(patternAdapted);
        if (parametersCount == 3 && functionName.equals("preg_replace") && params[1] instanceof StringLiteralExpression && params[1].getText().length() == 2 && trimMatcher.find()) {
            /* false-positives: the `m` or `u` modifiers making the replacement impossible */
            if (modifiers != null && (modifiers.indexOf('m') != -1 || modifiers.indexOf('u') != -1)) {
                return;
            }
            // mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
            String function = "trim";
            if (!pattern.startsWith("^")) {
                function = "rtrim";
            } else if (!pattern.endsWith("$")) {
                function = "ltrim";
            }
            String characterToTrim = trimMatcher.group(7);
            characterToTrim = characterToTrim == null ? trimMatcher.group(5) : characterToTrim;
            characterToTrim = characterToTrim == null ? trimMatcher.group(3) : characterToTrim;
            final String replacement = "%f%(%s%, '%p%')".replace(", '%p%'", characterToTrim.equals("\\s") ? "" : ", '%p%'").replace("%p%", unescape(characterToTrim)).replace("%s%", params[2].getText()).replace("%f%", function);
            holder.registerProblem(reference, String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), replacement), new UseTrimFix(replacement));
            return;
        }
        /* investigate using explode(...) instead */
        if ((parametersCount == 2 || parametersCount == 3) && functionName.equals("preg_split") && StringUtils.isEmpty(modifiers) && (regexSingleCharSet.matcher(patternAdapted).find() || !regexHasRegexAttributes.matcher(patternAdapted).find())) {
            final String replacement = "explode(\"%p%\", %s%%l%)".replace("%l%", parametersCount > 2 ? ", " + params[2].getText() : "").replace("%s%", params[1].getText()).replace("%p%", unescape(patternAdapted));
            holder.registerProblem(reference, String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), replacement), new UseExplodeFix(replacement));
        }
    }
}
Also used : Matcher(java.util.regex.Matcher) StringLiteralExpression(com.jetbrains.php.lang.psi.elements.StringLiteralExpression) LocalQuickFix(com.intellij.codeInspection.LocalQuickFix) PsiElement(com.intellij.psi.PsiElement)

Example 25 with StringLiteralExpression

use of com.jetbrains.php.lang.psi.elements.StringLiteralExpression in project phpinspectionsea by kalessil.

the class TypeUnsafeComparisonInspector method buildVisitor.

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

        @Override
        public void visitPhpBinaryExpression(@NotNull BinaryExpression expression) {
            final IElementType operator = expression.getOperationType();
            if (operator == PhpTokenTypes.opEQUAL || operator == PhpTokenTypes.opNOT_EQUAL) {
                this.analyze(expression, operator);
            }
        }

        private void analyze(@NotNull final BinaryExpression subject, @NotNull final IElementType operator) {
            final String targetOperator = PhpTokenTypes.opEQUAL == operator ? "===" : "!==";
            final PsiElement left = subject.getLeftOperand();
            final PsiElement right = subject.getRightOperand();
            if (right instanceof StringLiteralExpression || left instanceof StringLiteralExpression) {
                final PsiElement nonStringOperand;
                final String literalValue;
                if (right instanceof StringLiteralExpression) {
                    literalValue = ((StringLiteralExpression) right).getContents();
                    nonStringOperand = ExpressionSemanticUtil.getExpressionTroughParenthesis(left);
                } else {
                    literalValue = ((StringLiteralExpression) left).getContents();
                    nonStringOperand = ExpressionSemanticUtil.getExpressionTroughParenthesis(right);
                }
                /* resolve 2nd operand type, if class ensure __toString is implemented */
                if (ClassInStringContextStrategy.apply(nonStringOperand, holder, subject, messageToStringMethodMissing)) {
                    /* TODO: weak warning regarding under-the-hood string casting */
                    return;
                }
                /* string literal is numeric or empty, no strict compare possible */
                if (!literalValue.isEmpty() && !literalValue.matches("^[+-]?[0-9]*\\.?[0-9]+$")) {
                    holder.registerProblem(subject, String.format(MessagesPresentationUtil.prefixWithEa(patternCompareStrict), targetOperator), new CompareStrictFix(targetOperator));
                    return;
                }
            }
            /* some objects supporting direct comparison: search for .compare_objects in PHP sources */
            if (left != null && right != null) {
                final boolean isComparableObject = this.isComparableObject(left) || this.isComparableObject(right);
                if (!isComparableObject) {
                    holder.registerProblem(subject, String.format(MessagesPresentationUtil.prefixWithEa(patternHarden), targetOperator), ProblemHighlightType.WEAK_WARNING);
                }
            }
        }

        private boolean isComparableObject(@NotNull PsiElement operand) {
            if (operand instanceof PhpTypedElement) {
                final Project project = holder.getProject();
                final PhpType resolved = OpenapiResolveUtil.resolveType((PhpTypedElement) operand, project);
                if (resolved != null) {
                    final PhpIndex index = PhpIndex.getInstance(project);
                    final Set<PhpClass> classes = new HashSet<>();
                    resolved.filterUnknown().getTypes().stream().filter(t -> t.charAt(0) == '\\').forEach(t -> classes.addAll(OpenapiResolveUtil.resolveClassesAndInterfacesByFQN(Types.getType(t), index)));
                    for (final PhpClass clazz : classes) {
                        final boolean hasAny = comparable.contains(clazz.getFQN()) || InterfacesExtractUtil.getCrawlInheritanceTree(clazz, true).stream().anyMatch(c -> comparable.contains(c.getFQN()));
                        if (hasAny) {
                            classes.clear();
                            return true;
                        }
                    }
                    classes.clear();
                }
            }
            return false;
        }
    };
}
Also used : IElementType(com.intellij.psi.tree.IElementType) PhpTokenTypes(com.jetbrains.php.lang.lexer.PhpTokenTypes) PhpIndex(com.jetbrains.php.PhpIndex) PhpTypedElement(com.jetbrains.php.lang.psi.elements.PhpTypedElement) HashSet(java.util.HashSet) ClassInStringContextStrategy(com.kalessil.phpStorm.phpInspectionsEA.utils.strategy.ClassInStringContextStrategy) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) ProblemDescriptor(com.intellij.codeInspection.ProblemDescriptor) MessagesPresentationUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil) PsiElement(com.intellij.psi.PsiElement) Project(com.intellij.openapi.project.Project) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) LocalQuickFix(com.intellij.codeInspection.LocalQuickFix) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) PhpPsiElementFactory(com.jetbrains.php.lang.psi.PhpPsiElementFactory) PhpClass(com.jetbrains.php.lang.psi.elements.PhpClass) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) ExpressionSemanticUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.ExpressionSemanticUtil) Types(com.kalessil.phpStorm.phpInspectionsEA.utils.Types) InterfacesExtractUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.hierarhy.InterfacesExtractUtil) LeafPsiElement(com.intellij.psi.impl.source.tree.LeafPsiElement) Set(java.util.Set) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) OpenapiResolveUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiResolveUtil) ProblemHighlightType(com.intellij.codeInspection.ProblemHighlightType) StringLiteralExpression(com.jetbrains.php.lang.psi.elements.StringLiteralExpression) NotNull(org.jetbrains.annotations.NotNull) StringLiteralExpression(com.jetbrains.php.lang.psi.elements.StringLiteralExpression) PhpIndex(com.jetbrains.php.PhpIndex) PhpClass(com.jetbrains.php.lang.psi.elements.PhpClass) NotNull(org.jetbrains.annotations.NotNull) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) IElementType(com.intellij.psi.tree.IElementType) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) Project(com.intellij.openapi.project.Project) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) PhpTypedElement(com.jetbrains.php.lang.psi.elements.PhpTypedElement) PsiElement(com.intellij.psi.PsiElement) LeafPsiElement(com.intellij.psi.impl.source.tree.LeafPsiElement) HashSet(java.util.HashSet) NotNull(org.jetbrains.annotations.NotNull)

Aggregations

StringLiteralExpression (com.jetbrains.php.lang.psi.elements.StringLiteralExpression)39 PsiElement (com.intellij.psi.PsiElement)35 NotNull (org.jetbrains.annotations.NotNull)28 BasePhpElementVisitor (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor)18 FunctionReference (com.jetbrains.php.lang.psi.elements.FunctionReference)16 HashSet (java.util.HashSet)8 PhpElementVisitor (com.jetbrains.php.lang.psi.visitors.PhpElementVisitor)7 ProblemsHolder (com.intellij.codeInspection.ProblemsHolder)6 PsiElementVisitor (com.intellij.psi.PsiElementVisitor)6 ArrayCreationExpression (com.jetbrains.php.lang.psi.elements.ArrayCreationExpression)6 PhpPsiElement (com.jetbrains.php.lang.psi.elements.PhpPsiElement)6 Set (java.util.Set)6 Project (com.intellij.openapi.project.Project)5 PhpElementTypes (com.jetbrains.php.lang.parser.PhpElementTypes)5 GroupNames (com.intellij.codeInsight.daemon.GroupNames)4 PlatformPatterns (com.intellij.patterns.PlatformPatterns)4 PsiFile (com.intellij.psi.PsiFile)4 FilenameIndex (com.intellij.psi.search.FilenameIndex)4 GlobalSearchScope (com.intellij.psi.search.GlobalSearchScope)4 IElementType (com.intellij.psi.tree.IElementType)4