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));
}
}
};
}
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();
}
};
}
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;
}
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));
}
}
}
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;
}
};
}
Aggregations