use of com.jetbrains.php.lang.psi.resolve.types.PhpType in project phpinspectionsea by kalessil.
the class ClassInStringContextStrategy method apply.
public static boolean apply(@Nullable PsiElement nonStringOperand, @NotNull ProblemsHolder holder, @NotNull PsiElement expression, @NotNull String classHasNoToStringMessage) {
if (null == nonStringOperand) {
return false;
}
final Set<String> resolvedTypes = new HashSet<>();
if (nonStringOperand instanceof PhpTypedElement) {
final PhpType resolved = OpenapiResolveUtil.resolveType((PhpTypedElement) nonStringOperand, holder.getProject());
if (resolved != null) {
resolved.filterUnknown().getTypes().forEach(t -> resolvedTypes.add(Types.getType(t)));
}
}
if (!TypesSemanticsUtil.isNullableObjectInterface(resolvedTypes)) {
resolvedTypes.clear();
return false;
}
/* collect classes to check if __toString() is there */
final PhpIndex index = PhpIndex.getInstance(holder.getProject());
final List<PhpClass> listClasses = new ArrayList<>();
resolvedTypes.stream().filter(fqn -> fqn.charAt(0) == '\\').forEach(fqn -> listClasses.addAll(OpenapiResolveUtil.resolveClassesAndInterfacesByFQN(fqn, index)));
resolvedTypes.clear();
/* check methods, error on first one violated requirements */
for (final PhpClass clazz : listClasses) {
if (OpenapiResolveUtil.resolveMethod(clazz, "__toString") == null) {
holder.registerProblem(expression, MessagesPresentationUtil.prefixWithEa(classHasNoToStringMessage.replace("%class%", clazz.getFQN())), ProblemHighlightType.ERROR);
break;
}
}
/* terminate inspection, php will call __toString() */
listClasses.clear();
return true;
}
use of com.jetbrains.php.lang.psi.resolve.types.PhpType in project phpinspectionsea by kalessil.
the class IsEmptyFunctionUsageInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpEmpty(@NotNull PhpEmpty emptyExpression) {
final PhpExpression[] values = emptyExpression.getVariables();
if (values.length == 1) {
final PsiElement subject = ExpressionSemanticUtil.getExpressionTroughParenthesis(values[0]);
if (subject == null || subject instanceof ArrayAccessExpression) {
/* currently, php docs lacks of array structure notations, skip it */
return;
}
final PsiElement parent = emptyExpression.getParent();
final PsiElement operation = parent instanceof UnaryExpression ? ((UnaryExpression) parent).getOperation() : null;
final boolean isInverted = OpenapiTypesUtil.is(operation, PhpTokenTypes.opNOT);
/* extract types */
final Set<String> resolvedTypes = new HashSet<>();
if (subject instanceof PhpTypedElement) {
final PhpType resolved = OpenapiResolveUtil.resolveType((PhpTypedElement) subject, holder.getProject());
if (resolved != null) {
resolved.filterUnknown().getTypes().forEach(t -> resolvedTypes.add(Types.getType(t)));
}
}
/* Case 1: empty(array) - hidden logic - empty array */
if (SUGGEST_TO_USE_COUNT_CHECK && this.isCountableType(resolvedTypes)) {
final String comparison = isInverted ? "!==" : "===";
final String replacement = ComparisonStyle.isRegular() ? String.format("count(%s) %s 0", subject.getText(), comparison) : String.format("0 %s count(%s)", comparison, subject.getText());
final PsiElement target = isInverted ? parent : emptyExpression;
holder.registerProblem(target, String.format(MessagesPresentationUtil.prefixWithEa(patternAlternative), replacement), new UseCountFix(replacement));
resolvedTypes.clear();
return;
}
/* case 2: nullable classes, nullable target core types */
if (SUGGEST_TO_USE_NULL_COMPARISON && ((SUGGEST_NULL_COMPARISON_FOR_SCALARS && this.isNullableCoreType(resolvedTypes)) || TypesSemanticsUtil.isNullableObjectInterface(resolvedTypes))) {
/* false-positive: a field reference used in the subject expression */
PsiElement base = subject;
while (base instanceof PhpPsiElement) {
if (base instanceof FieldReference) {
break;
}
base = ((PhpPsiElement) base).getFirstPsiChild();
}
if (!(base instanceof FieldReference)) {
final String comparison = isInverted ? "!==" : "===";
final String replacement = ComparisonStyle.isRegular() ? String.format("%s %s null", subject.getText(), comparison) : String.format("null %s %s", comparison, subject.getText());
holder.registerProblem(isInverted ? parent : emptyExpression, String.format(MessagesPresentationUtil.prefixWithEa(patternAlternative), replacement), new CompareToNullFix(replacement));
}
resolvedTypes.clear();
return;
}
resolvedTypes.clear();
}
if (REPORT_EMPTY_USAGE) {
holder.registerProblem(emptyExpression, MessagesPresentationUtil.prefixWithEa(messageDoNotUse));
}
}
private boolean isCountableType(@NotNull Set<String> resolvedTypesSet) {
if (!resolvedTypesSet.isEmpty()) {
return resolvedTypesSet.stream().allMatch(t -> {
boolean isIterable = false;
if (t.equals(Types.strArray)) {
isIterable = true;
} else if (t.startsWith("\\")) {
final List<PhpClass> resolved = OpenapiResolveUtil.resolveClassesAndInterfacesByFQN(t, PhpIndex.getInstance(holder.getProject()));
isIterable = resolved.stream().anyMatch(r -> InterfacesExtractUtil.getCrawlInheritanceTree(r, true).stream().anyMatch(c -> c.getFQN().equals("\\Countable")));
}
return isIterable;
});
}
return false;
}
private boolean isNullableCoreType(@NotNull Set<String> resolvedTypesSet) {
boolean result = false;
if (resolvedTypesSet.size() == 2 && resolvedTypesSet.contains(Types.strNull)) {
result = resolvedTypesSet.contains(Types.strInteger) || resolvedTypesSet.contains(Types.strFloat) || resolvedTypesSet.contains(Types.strBoolean) || resolvedTypesSet.contains(Types.strResource);
}
return result;
}
};
}
use of com.jetbrains.php.lang.psi.resolve.types.PhpType in project phpinspectionsea by kalessil.
the class PropertyInitializationFlawsInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpField(@NotNull Field field) {
if (REPORT_DEFAULTS_FLAWS && !field.isConstant()) {
final PhpClass clazz = field.getContainingClass();
final PhpClass parentClazz = clazz == null ? null : OpenapiResolveUtil.resolveSuperClass(clazz);
final Field originField = parentClazz == null ? null : OpenapiResolveUtil.resolveField(parentClazz, field.getName());
final PsiElement fieldDefault = OpenapiResolveUtil.resolveDefaultValue(field);
final PsiElement originDefault = originField == null ? null : OpenapiResolveUtil.resolveDefaultValue(originField);
if (PhpLanguageUtil.isNull(fieldDefault)) {
/* false-positives: typed properties PS will take care of them */
if (!this.isNullableTypedProperty(field)) {
holder.registerProblem(fieldDefault, MessagesPresentationUtil.prefixWithEa(messageDefaultNull), ProblemHighlightType.LIKE_UNUSED_SYMBOL, new DropFieldDefaultValueFix());
}
} else if (fieldDefault instanceof PhpPsiElement && originDefault instanceof PhpPsiElement) {
final boolean isDefaultDuplicate = !originField.getModifier().getAccess().isPrivate() && OpenapiEquivalenceUtil.areEqual(fieldDefault, originDefault);
if (isDefaultDuplicate) {
boolean report = true;
/* false-positives: classes reference are the same, but resolved to different classes */
final Set<String> originalClasses = this.findReferencedClasses(originDefault);
if (!originalClasses.isEmpty()) {
final Set<String> fieldClasses = this.findReferencedClasses(fieldDefault);
report = !originalClasses.addAll(fieldClasses);
fieldClasses.clear();
originalClasses.clear();
}
if (report) {
holder.registerProblem(fieldDefault, MessagesPresentationUtil.prefixWithEa(messageSenselessWrite), ProblemHighlightType.LIKE_UNUSED_SYMBOL);
}
}
}
}
}
private boolean isNullableTypedProperty(@Nullable Field field) {
if (field != null && PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP740)) {
final PhpType resolved = OpenapiResolveUtil.resolveDeclaredType(field);
return !resolved.isEmpty() && resolved.getTypes().stream().map(Types::getType).anyMatch(t -> t.equals(Types.strNull) || t.equals(Types.strMixed));
}
return false;
}
@NotNull
private Set<String> findReferencedClasses(@NotNull PsiElement where) {
return PsiTreeUtil.findChildrenOfType(where, ClassReference.class).stream().map(r -> {
final PsiElement resolved = OpenapiResolveUtil.resolveReference(r);
return resolved instanceof PhpClass ? ((PhpClass) resolved).getFQN() : null;
}).collect(Collectors.toSet());
}
@Override
public void visitPhpMethod(@NotNull Method method) {
/* configuration-based toggle */
if (!REPORT_INIT_FLAWS) {
return;
}
/* process only constructors with non-empty body */
final PhpClass clazz = method.getContainingClass();
if (null == clazz || !method.getName().equals("__construct") || clazz.isInterface() || clazz.isTrait()) {
return;
}
final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(method);
if (null == body || 0 == ExpressionSemanticUtil.countExpressionsInGroup(body)) {
return;
}
/* collect private properties with default values; stop inspection if none found */
/* protected/public properties init in __construct can be bypassed, so defaults might have sense */
final Map<String, PsiElement> propertiesToCheck = new HashMap<>();
for (final Field field : clazz.getOwnFields()) {
if (!field.isConstant()) {
final PhpModifier modifiers = field.getModifier();
if (modifiers.isPrivate() && !modifiers.isStatic()) {
final PsiElement defaultValue = OpenapiResolveUtil.resolveDefaultValue(field);
if (defaultValue instanceof PhpPsiElement && !PhpLanguageUtil.isNull(defaultValue)) {
propertiesToCheck.put(field.getName(), defaultValue);
} else {
propertiesToCheck.put(field.getName(), null);
}
}
}
}
if (propertiesToCheck.isEmpty()) {
return;
}
/* iterate 1st level instructions and analyze overriding properties */
for (final PsiElement expression : body.getChildren()) {
final PsiElement assignmentCandidate = expression.getFirstChild();
if (!OpenapiTypesUtil.isAssignment(assignmentCandidate)) {
continue;
}
final AssignmentExpression assignment = (AssignmentExpression) assignmentCandidate;
final PsiElement container = assignment.getVariable();
final PsiElement value = assignment.getValue();
if (container instanceof FieldReference && container.getFirstChild().getText().equals("$this")) {
final String overriddenProperty = ((FieldReference) container).getName();
if (null == value || null == overriddenProperty || !propertiesToCheck.containsKey(overriddenProperty)) {
continue;
}
final PsiElement fieldDefault = propertiesToCheck.get(overriddenProperty);
/* Pattern: written and default values are identical */
if ((null == fieldDefault && PhpLanguageUtil.isNull(value)) || (null != fieldDefault && OpenapiEquivalenceUtil.areEqual(value, fieldDefault))) {
/* false-positives: typed properties */
if (!this.isNullableTypedProperty(OpenapiResolveUtil.resolveField(clazz, overriddenProperty))) {
holder.registerProblem(expression, MessagesPresentationUtil.prefixWithEa(messageSenselessWrite), ProblemHighlightType.LIKE_UNUSED_SYMBOL);
}
continue;
}
if (null == fieldDefault) {
continue;
}
/* false-positive: property is involved into generating new value */
boolean isPropertyReused = false;
for (final FieldReference candidate : PsiTreeUtil.findChildrenOfType(value, FieldReference.class)) {
if (OpenapiEquivalenceUtil.areEqual(container, candidate)) {
isPropertyReused = true;
break;
}
}
if (!isPropertyReused && REPORT_DEFAULTS_FLAWS) {
holder.registerProblem(fieldDefault, MessagesPresentationUtil.prefixWithEa(messageDefaultOverride), new DropFieldDefaultValueFix());
}
}
}
propertiesToCheck.clear();
}
};
}
use of com.jetbrains.php.lang.psi.resolve.types.PhpType in project phpinspectionsea by kalessil.
the class NullCoalescingOperatorCanBeUsedInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpTernaryExpression(@NotNull TernaryExpression expression) {
if (SUGGEST_SIMPLIFYING_TERNARIES && !expression.isShort() && PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP700)) {
final PsiElement condition = ExpressionSemanticUtil.getExpressionTroughParenthesis(expression.getCondition());
if (condition != null) {
final PsiElement extracted = this.getTargetCondition(condition);
if (extracted != null) {
final PsiElement firstValue = expression.getTrueVariant();
final PsiElement secondValue = expression.getFalseVariant();
if (firstValue != null && secondValue != null) {
final String replacement = this.generateReplacement(condition, extracted, firstValue, secondValue);
if (replacement != null) {
holder.registerProblem(expression, String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), replacement), new ReplaceSingleConstructFix(replacement));
}
}
}
}
}
}
@Override
public void visitPhpIf(@NotNull If statement) {
final Project project = holder.getProject();
if (SUGGEST_SIMPLIFYING_IFS && PhpLanguageLevel.get(project).atLeast(PhpLanguageLevel.PHP700)) {
final PsiElement condition = ExpressionSemanticUtil.getExpressionTroughParenthesis(statement.getCondition());
if (condition != null && statement.getElseIfBranches().length == 0) {
final PsiElement extracted = this.getTargetCondition(condition);
if (extracted != null) {
final Couple<Couple<PsiElement>> fragments = this.extract(statement);
final PsiElement firstValue = fragments.second.first;
final PsiElement secondValue = fragments.second.second;
if (firstValue != null) {
final String coalescing = this.generateReplacement(condition, extracted, firstValue, secondValue);
if (coalescing != null) {
final PsiElement context = firstValue.getParent();
if (context instanceof PhpReturn) {
final String replacement = String.format("return %s", coalescing);
holder.registerProblem(statement.getFirstChild(), String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), replacement), new ReplaceMultipleConstructFix(project, fragments.first.first, fragments.first.second, replacement));
} else if (context instanceof AssignmentExpression) {
final PsiElement container = ((AssignmentExpression) context).getVariable();
final String replacement = String.format("%s = %s", container.getText(), coalescing);
holder.registerProblem(statement.getFirstChild(), String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), replacement), new ReplaceMultipleConstructFix(project, fragments.first.first, fragments.first.second, replacement));
}
}
}
}
}
}
}
private boolean wrap(@Nullable PsiElement expression) {
if (expression instanceof TernaryExpression || expression instanceof AssignmentExpression) {
return true;
} else if (expression instanceof BinaryExpression) {
return ((BinaryExpression) expression).getOperationType() != PhpTokenTypes.opCOALESCE;
}
return false;
}
@Nullable
private String generateReplacement(@NotNull PsiElement condition, @NotNull PsiElement extracted, @NotNull PsiElement first, @Nullable PsiElement second) {
String coalescing = null;
if (extracted instanceof PhpIsset) {
coalescing = this.generateReplacementForIsset(condition, (PhpIsset) extracted, first, second);
} else if (extracted instanceof PhpEmpty) {
coalescing = this.generateReplacementForPropertyAccess(condition, (PhpEmpty) extracted, first, second);
} else if (extracted instanceof Variable || extracted instanceof ArrayAccessExpression || extracted instanceof FieldReference) {
coalescing = this.generateReplacementForPropertyAccess(condition, extracted, first, second);
} else if (extracted instanceof FunctionReference) {
if (second != null) {
coalescing = this.generateReplacementForExists(condition, (FunctionReference) extracted, first, second);
}
} else if (extracted instanceof BinaryExpression) {
if (second != null) {
coalescing = this.generateReplacementForIdentity(condition, (BinaryExpression) extracted, first, second);
}
}
return coalescing;
}
@Nullable
private String generateReplacementForExists(@NotNull PsiElement condition, @NotNull FunctionReference extracted, @NotNull PsiElement first, @NotNull PsiElement second) {
final PsiElement[] arguments = extracted.getParameters();
if (arguments.length == 2) {
final boolean expectsToBeSet = condition == extracted;
final PsiElement candidate = expectsToBeSet ? first : second;
final PsiElement alternative = expectsToBeSet ? second : first;
if (candidate instanceof ArrayAccessExpression && PhpLanguageUtil.isNull(alternative)) {
final ArrayAccessExpression access = (ArrayAccessExpression) candidate;
final PsiElement container = access.getValue();
if (container != null && OpenapiEquivalenceUtil.areEqual(container, arguments[1])) {
final ArrayIndex index = access.getIndex();
if (index != null) {
final PsiElement key = index.getValue();
if (key != null && OpenapiEquivalenceUtil.areEqual(key, arguments[0])) {
return String.format("%s ?? %s", String.format(this.wrap(candidate) ? "(%s)" : "%s", candidate.getText()), String.format(this.wrap(alternative) ? "(%s)" : "%s", alternative.getText()));
}
}
}
}
}
return null;
}
@Nullable
private String generateReplacementForIdentity(@NotNull PsiElement condition, @NotNull BinaryExpression extracted, @NotNull PsiElement first, @NotNull PsiElement second) {
PsiElement subject = extracted.getLeftOperand();
if (PhpLanguageUtil.isNull(subject)) {
subject = extracted.getRightOperand();
}
if (subject != null) {
final IElementType operator = extracted.getOperationType();
final boolean expectsToBeSet = (operator == PhpTokenTypes.opNOT_IDENTICAL && condition == extracted) || (operator == PhpTokenTypes.opIDENTICAL && condition != extracted);
final PsiElement candidate = expectsToBeSet ? first : second;
if (OpenapiEquivalenceUtil.areEqual(candidate, subject)) {
final PsiElement alternative = expectsToBeSet ? second : first;
return String.format("%s ?? %s", String.format(this.wrap(candidate) ? "(%s)" : "%s", candidate.getText()), String.format(this.wrap(alternative) ? "(%s)" : "%s", alternative.getText()));
}
}
return null;
}
@Nullable
private String generateReplacementForIsset(@NotNull PsiElement condition, @NotNull PhpIsset extracted, @NotNull PsiElement first, @Nullable PsiElement second) {
final PsiElement subject = extracted.getVariables()[0];
if (subject != null) {
final boolean expectsToBeSet = condition == extracted;
final PsiElement candidate = expectsToBeSet ? first : second;
if (candidate != null && OpenapiEquivalenceUtil.areEqual(candidate, subject)) {
final PsiElement alternative = expectsToBeSet ? second : first;
return String.format("%s ?? %s", String.format(this.wrap(candidate) ? "(%s)" : "%s", candidate.getText()), String.format(this.wrap(alternative) ? "(%s)" : "%s", alternative == null ? "null" : alternative.getText()));
}
}
return null;
}
@Nullable
private String generateReplacementForPropertyAccess(@NotNull PsiElement condition, @NotNull PsiElement extracted, @NotNull PsiElement first, @Nullable PsiElement second) {
final boolean expectsToBeNotEmpty = condition == extracted;
final PsiElement candidate = expectsToBeNotEmpty ? first : second;
if (candidate instanceof FieldReference) {
final FieldReference fieldReference = (FieldReference) candidate;
final PsiElement base = fieldReference.getClassReference();
if (base != null && OpenapiEquivalenceUtil.areEqual(extracted, base)) {
final PhpType resolved = OpenapiResolveUtil.resolveType(fieldReference, holder.getProject());
if (resolved != null && !resolved.filterUnknown().isEmpty()) {
final PsiElement alternative = expectsToBeNotEmpty ? second : first;
final boolean isNullable = resolved.filterUnknown().getTypes().stream().map(Types::getType).anyMatch(t -> t.equals(Types.strNull));
if (!isNullable || (alternative == null || PhpLanguageUtil.isNull(alternative))) {
return String.format("%s ?? %s", String.format(this.wrap(fieldReference) ? "(%s)" : "%s", fieldReference.getText()), String.format(this.wrap(alternative) ? "(%s)" : "%s", alternative == null ? "null" : alternative.getText()));
}
}
}
}
return null;
}
@Nullable
private String generateReplacementForPropertyAccess(@NotNull PsiElement condition, @NotNull PhpEmpty extracted, @NotNull PsiElement first, @Nullable PsiElement second) {
final PsiElement subject = extracted.getVariables()[0];
if (subject != null) {
final boolean expectsToBeNotEmpty = condition != extracted;
final PsiElement candidate = expectsToBeNotEmpty ? first : second;
if (candidate instanceof FieldReference) {
final PsiElement reference = ((FieldReference) candidate).getClassReference();
if (reference != null && OpenapiEquivalenceUtil.areEqual(subject, reference)) {
final PsiElement alternative = expectsToBeNotEmpty ? second : first;
return String.format("%s ?? %s", String.format(this.wrap(candidate) ? "(%s)" : "%s", candidate.getText()), String.format(this.wrap(alternative) ? "(%s)" : "%s", alternative == null ? "null" : alternative.getText()));
}
}
}
return null;
}
@Nullable
private PsiElement getTargetCondition(@NotNull PsiElement condition) {
/* un-wrap inverted conditions */
if (condition instanceof UnaryExpression) {
final UnaryExpression unary = (UnaryExpression) condition;
if (OpenapiTypesUtil.is(unary.getOperation(), PhpTokenTypes.opNOT)) {
condition = ExpressionSemanticUtil.getExpressionTroughParenthesis(unary.getValue());
}
}
/* do check */
if (condition instanceof Variable || condition instanceof ArrayAccessExpression || condition instanceof FieldReference) {
return condition;
} else if (condition instanceof PhpIsset) {
final PhpIsset isset = (PhpIsset) condition;
if (isset.getVariables().length == 1) {
return condition;
}
} else if (condition instanceof PhpEmpty) {
final PhpEmpty empty = (PhpEmpty) condition;
if (empty.getVariables().length == 1) {
return condition;
}
} else if (condition instanceof BinaryExpression) {
final BinaryExpression binary = (BinaryExpression) condition;
final IElementType operator = binary.getOperationType();
if (operator == PhpTokenTypes.opIDENTICAL || operator == PhpTokenTypes.opNOT_IDENTICAL) {
if (PhpLanguageUtil.isNull(binary.getRightOperand())) {
return condition;
} else if (PhpLanguageUtil.isNull(binary.getLeftOperand())) {
return condition;
}
}
} else if (OpenapiTypesUtil.isFunctionReference(condition)) {
final String functionName = ((FunctionReference) condition).getName();
if (functionName != null && functionName.equals("array_key_exists")) {
return condition;
}
}
return null;
}
/* first pair: what to drop, second positive and negative branching values */
private Couple<Couple<PsiElement>> extract(@NotNull If statement) {
Couple<Couple<PsiElement>> result = new Couple<>(new Couple<>(null, null), new Couple<>(null, null));
final GroupStatement ifBody = ExpressionSemanticUtil.getGroupStatement(statement);
if (ifBody != null && ExpressionSemanticUtil.countExpressionsInGroup(ifBody) == 1) {
final PsiElement ifLast = this.extractCandidate(ExpressionSemanticUtil.getLastStatement(ifBody));
if (ifLast != null) {
/* extract all related constructs */
final PsiElement ifNext = this.extractCandidate(statement.getNextPsiSibling());
final PsiElement ifPrevious = this.extractCandidate(statement.getPrevPsiSibling());
if (statement.getElseBranch() != null) {
PsiElement elseLast = null;
final GroupStatement elseBody = ExpressionSemanticUtil.getGroupStatement(statement.getElseBranch());
if (elseBody != null && ExpressionSemanticUtil.countExpressionsInGroup(elseBody) == 1) {
elseLast = this.extractCandidate(ExpressionSemanticUtil.getLastStatement(elseBody));
}
/* if - return - else - return */
if (ifLast instanceof PhpReturn && elseLast instanceof PhpReturn) {
result = new Couple<>(new Couple<>(statement, statement), new Couple<>(((PhpReturn) ifLast).getArgument(), ((PhpReturn) elseLast).getArgument()));
} else /* if - assign - else - assign */
if (ifLast instanceof AssignmentExpression && elseLast instanceof AssignmentExpression) {
final AssignmentExpression ifAssignment = (AssignmentExpression) ifLast;
final AssignmentExpression elseAssignment = (AssignmentExpression) elseLast;
final PsiElement ifContainer = ifAssignment.getVariable();
final PsiElement elseContainer = elseAssignment.getVariable();
if (ifContainer instanceof Variable && elseContainer instanceof Variable) {
final boolean isTarget = OpenapiEquivalenceUtil.areEqual(ifContainer, elseContainer);
if (isTarget) {
result = new Couple<>(new Couple<>(statement, statement), new Couple<>(ifAssignment.getValue(), elseAssignment.getValue()));
}
}
}
} else {
/* assign - if - assign */
if (ifPrevious instanceof AssignmentExpression && ifLast instanceof AssignmentExpression) {
final AssignmentExpression previousAssignment = (AssignmentExpression) ifPrevious;
final AssignmentExpression ifAssignment = (AssignmentExpression) ifLast;
final PsiElement previousContainer = previousAssignment.getVariable();
final PsiElement ifContainer = ifAssignment.getVariable();
if (previousContainer instanceof Variable && ifContainer instanceof Variable) {
final boolean isTarget = OpenapiEquivalenceUtil.areEqual(previousContainer, ifContainer);
/* false-positives: assignment by value */
if (isTarget && !OpenapiTypesUtil.isAssignmentByReference(previousAssignment)) {
final PsiElement previousValue = previousAssignment.getValue();
if (!(previousValue instanceof AssignmentExpression)) {
/* false-positives: assignment of processed container value */
final boolean isContainerProcessing = PsiTreeUtil.findChildrenOfType(previousValue, previousContainer.getClass()).stream().anyMatch(c -> OpenapiEquivalenceUtil.areEqual(c, previousContainer));
if (!isContainerProcessing) {
result = new Couple<>(new Couple<>(ifPrevious.getParent(), statement), new Couple<>(ifAssignment.getValue(), previousValue));
}
}
}
}
} else /* if - return - return */
if (ifLast instanceof PhpReturn && ifNext instanceof PhpReturn) {
result = new Couple<>(new Couple<>(statement, ifNext), new Couple<>(((PhpReturn) ifLast).getArgument(), ((PhpReturn) ifNext).getArgument()));
} else /* if - return - [end-of-function] */
if (ifLast instanceof PhpReturn && ifNext == null && statement.getNextPsiSibling() == null) {
final boolean isInFunction = statement.getParent().getParent() instanceof Function;
if (isInFunction) {
result = new Couple<>(new Couple<>(statement, statement), new Couple<>(((PhpReturn) ifLast).getArgument(), null));
}
}
}
}
}
return result;
}
@Nullable
private PsiElement extractCandidate(@Nullable PsiElement statement) {
if (statement instanceof PhpReturn) {
return statement;
} else if (OpenapiTypesUtil.isStatementImpl(statement)) {
final PsiElement possiblyAssignment = statement.getFirstChild();
if (OpenapiTypesUtil.isAssignment(possiblyAssignment)) {
final AssignmentExpression assignment = (AssignmentExpression) possiblyAssignment;
final PsiElement container = assignment.getVariable();
if (container instanceof Variable) {
return assignment;
}
}
}
return null;
}
};
}
use of com.jetbrains.php.lang.psi.resolve.types.PhpType in project phpinspectionsea by kalessil.
the class MagicMethodsValidityInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpMethod(@NotNull Method method) {
final PhpClass clazz = method.getContainingClass();
final String methodName = method.getName();
final PsiElement nameNode = NamedElementUtil.getNameIdentifier(method);
if (clazz == null || nameNode == null || !methodName.startsWith("_") || method.isAbstract()) {
return;
}
switch(methodName) {
case "__construct":
CanNotBeStaticStrategy.apply(method, holder);
CanNotReturnTypeStrategy.apply(method, holder);
if (!this.isTestContext(clazz)) {
NormallyCallsParentMethodStrategy.apply(method, holder);
}
break;
case "__destruct":
case "__clone":
CanNotBeStaticStrategy.apply(method, holder);
CanNotReturnTypeStrategy.apply(method, holder);
CanNotTakeArgumentsStrategy.apply(method, holder);
NormallyCallsParentMethodStrategy.apply(method, holder);
break;
case "__get":
case "__isset":
case "__unset":
TakesExactAmountOfArgumentsStrategy.apply(1, method, holder);
CanNotBeStaticStrategy.apply(method, holder);
MustBePublicStrategy.apply(method, holder);
CanNotTakeArgumentsByReferenceStrategy.apply(method, holder);
HasAlsoMethodStrategy.apply(method, "__set", holder);
break;
case "__set":
case "__call":
TakesExactAmountOfArgumentsStrategy.apply(2, method, holder);
CanNotBeStaticStrategy.apply(method, holder);
MustBePublicStrategy.apply(method, holder);
CanNotTakeArgumentsByReferenceStrategy.apply(method, holder);
if (methodName.equals("__set")) {
HasAlsoMethodStrategy.apply(method, "__isset", holder);
HasAlsoMethodStrategy.apply(method, "__get", holder);
}
break;
case "__callStatic":
TakesExactAmountOfArgumentsStrategy.apply(2, method, holder);
MustBeStaticStrategy.apply(method, holder);
MustBePublicStrategy.apply(method, holder);
CanNotTakeArgumentsByReferenceStrategy.apply(method, holder);
break;
case "__toString":
CanNotBeStaticStrategy.apply(method, holder);
CanNotTakeArgumentsStrategy.apply(method, holder);
MustBePublicStrategy.apply(method, holder);
MustReturnSpecifiedTypeStrategy.apply(stringType, method, holder);
break;
case "__debugInfo":
CanNotBeStaticStrategy.apply(method, holder);
CanNotTakeArgumentsStrategy.apply(method, holder);
MustBePublicStrategy.apply(method, holder);
MustReturnSpecifiedTypeStrategy.apply(arrayOrNullType, method, holder);
MinimalPhpVersionStrategy.apply(method, holder, PhpLanguageLevel.PHP560);
break;
case "__set_state":
TakesExactAmountOfArgumentsStrategy.apply(1, method, holder);
MustBeStaticStrategy.apply(method, holder);
MustBePublicStrategy.apply(method, holder);
final PhpType returnTypes = (new PhpType()).add(clazz.getFQN()).add(Types.strStatic);
MustReturnSpecifiedTypeStrategy.apply(returnTypes, method, holder);
break;
case "__invoke":
CanNotBeStaticStrategy.apply(method, holder);
MustBePublicStrategy.apply(method, holder);
break;
case "__wakeup":
CanNotBeStaticStrategy.apply(method, holder);
CanNotTakeArgumentsStrategy.apply(method, holder);
CanNotReturnTypeStrategy.apply(method, holder);
break;
case "__unserialize":
CanNotBeStaticStrategy.apply(method, holder);
MustBePublicStrategy.apply(method, holder);
TakesExactAmountOfArgumentsStrategy.apply(1, method, holder);
CanNotReturnTypeStrategy.apply(method, holder);
break;
case "__sleep":
case "__serialize":
CanNotBeStaticStrategy.apply(method, holder);
MustBePublicStrategy.apply(method, holder);
CanNotTakeArgumentsStrategy.apply(method, holder);
MustReturnSpecifiedTypeStrategy.apply(arrayType, method, holder);
break;
case "__autoload":
TakesExactAmountOfArgumentsStrategy.apply(1, method, holder);
CanNotReturnTypeStrategy.apply(method, holder);
holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(messageUseSplAutoloading), ProblemHighlightType.LIKE_DEPRECATED);
break;
default:
if (methodName.startsWith("__") && !knownNonMagic.contains(methodName)) {
holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(messageNotMagic));
} else {
MissingUnderscoreStrategy.apply(method, holder);
}
break;
}
}
};
}
Aggregations