use of com.jetbrains.php.lang.psi.elements.BinaryExpression in project phpinspectionsea by kalessil.
the class TernaryOperatorSimplifyInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpTernaryExpression(@NotNull TernaryExpression expression) {
final PsiElement rawCondition = expression.getCondition();
if (rawCondition != null) {
final PsiElement condition = ExpressionSemanticUtil.getExpressionTroughParenthesis(rawCondition);
if (condition instanceof BinaryExpression) {
final PsiElement trueVariant = ExpressionSemanticUtil.getExpressionTroughParenthesis(expression.getTrueVariant());
if (trueVariant != null) {
final PsiElement falseVariant = ExpressionSemanticUtil.getExpressionTroughParenthesis(expression.getFalseVariant());
if (falseVariant != null) {
final boolean isTarget = PhpLanguageUtil.isBoolean(trueVariant) && PhpLanguageUtil.isBoolean(falseVariant);
if (isTarget) {
final String replacement = this.generateReplacement((BinaryExpression) condition, trueVariant);
if (replacement != null) {
holder.registerProblem(expression, MessagesPresentationUtil.prefixWithEa(String.format(messagePattern, replacement)), new SimplifyFix(replacement));
}
}
}
}
}
}
}
@Nullable
private String generateReplacement(@NotNull BinaryExpression binary, @NotNull PsiElement trueVariant) {
final IElementType operator = binary.getOperationType();
if (null == operator) {
return null;
}
final String replacement;
final boolean useParentheses = !oppositeOperators.containsKey(operator);
final boolean isInverted = PhpLanguageUtil.isFalse(trueVariant);
if (useParentheses) {
final boolean isLogical = PhpTokenTypes.opAND == operator || PhpTokenTypes.opOR == operator;
final String boolCasting = isLogical ? "" : "(bool)";
replacement = ((isInverted ? "!" : boolCasting) + "(%e%)").replace("%e%", binary.getText());
} else {
if (isInverted) {
final PsiElement left = binary.getLeftOperand();
final PsiElement right = binary.getRightOperand();
if (null == left || null == right) {
return null;
}
replacement = "%l% %o% %r%".replace("%r%", right.getText()).replace("%o%", oppositeOperators.get(operator)).replace("%l%", left.getText());
} else {
replacement = binary.getText();
}
}
return replacement;
}
};
}
use of com.jetbrains.php.lang.psi.elements.BinaryExpression 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;
}
};
}
use of com.jetbrains.php.lang.psi.elements.BinaryExpression in project phpinspectionsea by kalessil.
the class NestedNotOperatorsInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpUnaryExpression(@NotNull UnaryExpression expression) {
/* process ony not operations */
if (!OpenapiTypesUtil.is(expression.getOperation(), PhpTokenTypes.opNOT)) {
return;
}
/* process only deepest not-operator: get contained expression */
final PsiElement value = ExpressionSemanticUtil.getExpressionTroughParenthesis(expression.getValue());
if (null == value) {
return;
}
/* if contained expression is also inversion, do nothing -> to not report several times */
if (value instanceof UnaryExpression) {
if (OpenapiTypesUtil.is(((UnaryExpression) value).getOperation(), PhpTokenTypes.opNOT)) {
return;
}
}
/* check nesting level */
PsiElement target = null;
int nestingLevel = 1;
PsiElement parent = expression.getParent();
while (parent instanceof UnaryExpression || parent instanceof ParenthesizedExpression) {
if (!(parent instanceof ParenthesizedExpression)) {
expression = (UnaryExpression) parent;
if (OpenapiTypesUtil.is(expression.getOperation(), PhpTokenTypes.opNOT)) {
++nestingLevel;
target = parent;
}
}
parent = parent.getParent();
}
if (nestingLevel > 1) {
final String subject = String.format(value instanceof BinaryExpression ? "(%s)" : "%s", value.getText());
final String replacement = String.format(nestingLevel % 2 == 0 ? "(bool) %s" : "! %s", subject);
holder.registerProblem(target, MessagesPresentationUtil.prefixWithEa(String.format(messagePattern, replacement)), nestingLevel % 2 == 0 ? new UseCastingLocalFix(replacement) : new UseSingleNotLocalFix(replacement));
}
}
};
}
use of com.jetbrains.php.lang.psi.elements.BinaryExpression in project phpinspectionsea by kalessil.
the class SubStrUsedAsStrPosInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
/* dropped pattern: '$string[0] === '?'' -> 'substr(...) === 0' */
@Override
public void visitPhpFunctionCall(@NotNull FunctionReference reference) {
final String functionName = reference.getName();
if (functionName != null && substringFunctions.contains(functionName)) {
final PsiElement[] arguments = reference.getParameters();
if (arguments.length == 3 || arguments.length == 4) {
/* checking 2nd and 3rd arguments is not needed/simplified:
* - 2nd re-used as it is (should be a positive number!)
* - 3rd is not important, as we'll rely on parent comparison operand instead
*/
final String index = arguments[1].getText();
if (OpenapiTypesUtil.isNumber(arguments[1]) && index.equals("0")) {
if (!OpenapiTypesUtil.isFunctionReference(arguments[2]) || lengthFunctions.contains(((FunctionReference) arguments[2]).getName())) {
/* prepare variables, so we could properly process polymorphic pattern */
PsiElement highLevelCall = reference;
PsiElement parentExpression = reference.getParent();
if (parentExpression instanceof ParameterList) {
parentExpression = parentExpression.getParent();
}
/* if the call wrapped with case manipulation, propose to use stripos */
boolean caseManipulated = false;
if (OpenapiTypesUtil.isFunctionReference(parentExpression)) {
final FunctionReference parentCall = (FunctionReference) parentExpression;
final PsiElement[] parentArguments = parentCall.getParameters();
final String parentName = parentCall.getName();
if (parentName != null && parentArguments.length == 1 && outerFunctions.contains(parentName)) {
caseManipulated = true;
highLevelCall = parentExpression;
parentExpression = parentExpression.getParent();
}
}
/* check parent expression, to ensure pattern matched */
if (parentExpression instanceof BinaryExpression) {
final BinaryExpression parent = (BinaryExpression) parentExpression;
if (OpenapiTypesUtil.tsCOMPARE_EQUALITY_OPS.contains(parent.getOperationType())) {
final PsiElement secondOperand = OpenapiElementsUtil.getSecondOperand(parent, highLevelCall);
final PsiElement operationNode = parent.getOperation();
if (secondOperand != null && operationNode != null) {
final String operator = operationNode.getText();
final boolean isMbFunction = functionName.equals("mb_substr");
final boolean hasEncoding = isMbFunction && arguments.length == 4;
final String call = String.format("%s%s(%s, %s%s)", reference.getImmediateNamespaceName(), (isMbFunction ? "mb_" : "") + (caseManipulated ? "stripos" : "strpos"), arguments[0].getText(), secondOperand.getText(), hasEncoding ? (", " + arguments[3].getText()) : "");
final boolean isRegular = ComparisonStyle.isRegular();
final String replacement = String.format("%s %s %s", isRegular ? call : index, operator.length() == 2 ? (operator + '=') : operator, isRegular ? index : call);
holder.registerProblem(parentExpression, String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), replacement), new UseStringSearchFix(replacement));
}
}
}
}
}
}
}
}
};
}
use of com.jetbrains.php.lang.psi.elements.BinaryExpression in project phpinspectionsea by kalessil.
the class StrStartsWithCanBeUsedInspector 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("strpos") || functionName.equals("mb_strpos"))) {
final boolean isTargetVersion = PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP800);
if (isTargetVersion) {
final PsiElement[] arguments = reference.getParameters();
if (arguments.length == 2) {
final PsiElement context = reference.getParent();
if (context instanceof BinaryExpression) {
final BinaryExpression binary = (BinaryExpression) context;
final IElementType operation = binary.getOperationType();
if (operation == PhpTokenTypes.opNOT_IDENTICAL || operation == PhpTokenTypes.opIDENTICAL) {
final PsiElement second = OpenapiElementsUtil.getSecondOperand(binary, reference);
if (second != null && OpenapiTypesUtil.isNumber(second) && second.getText().equals("0")) {
final String replacement = String.format("%s%sstr_starts_with(%s, %s)", operation == PhpTokenTypes.opNOT_IDENTICAL ? "! " : "", reference.getImmediateNamespaceName(), arguments[0].getText(), arguments[1].getText());
holder.registerProblem(binary, String.format(MessagesPresentationUtil.prefixWithEa(message), replacement), new UseStrStartsWithFix(replacement));
}
}
}
}
}
}
}
};
}
Aggregations