use of com.jetbrains.php.lang.psi.elements.PhpClass in project phpinspectionsea by kalessil.
the class ClassOverridesFieldOfSuperClassInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpField(@NotNull Field ownField) {
/* skip un-explorable and test classes */
final PhpClass clazz = ownField.getContainingClass();
if (clazz == null || this.isTestContext(clazz)) {
return;
}
/* skip static, constants and un-processable fields */
final PsiElement ownFieldNameId = NamedElementUtil.getNameIdentifier(ownField);
if (null == ownFieldNameId || ownField.isConstant() || ownField.getModifier().isStatic()) {
return;
}
/* ensure field is not defined via annotation */
if (!(ExpressionSemanticUtil.getBlockScope(ownFieldNameId) instanceof PhpClass)) {
return;
}
/* ensure field doesn't have any user-land annotations */
final PhpDocTag[] tags = PsiTreeUtil.getChildrenOfType(ownField.getDocComment(), PhpDocTag.class);
final boolean annotated = tags != null && Arrays.stream(tags).anyMatch(t -> !t.getName().equals(t.getName().toLowerCase()));
if (annotated) {
return;
}
final PhpClass parent = OpenapiResolveUtil.resolveSuperClass(clazz);
final String ownFieldName = ownField.getName();
final Field parentField = parent == null ? null : OpenapiResolveUtil.resolveField(parent, ownFieldName);
if (parentField != null) {
final PhpClass parentFieldHolder = parentField.getContainingClass();
final PsiElement fieldNameNode = NamedElementUtil.getNameIdentifier(ownField);
if (fieldNameNode != null && parentFieldHolder != null) {
/* re-defining private fields with the same name is pretty suspicious itself */
if (parentField.getModifier().isPrivate()) {
if (REPORT_PRIVATE_REDEFINITION) {
holder.registerProblem(fieldNameNode, MessagesPresentationUtil.prefixWithEa(patternProtectedCandidate.replace("%c%", parentFieldHolder.getFQN())), ProblemHighlightType.WEAK_WARNING);
}
return;
}
/* report only cases when access level is not changed */
if (!ownField.getModifier().getAccess().isWeakerThan(parentField.getModifier().getAccess())) {
/* fire common warning */
holder.registerProblem(fieldNameNode, MessagesPresentationUtil.prefixWithEa(patternShadows.replace("%p%", ownFieldName).replace("%c%", parentFieldHolder.getFQN())), ProblemHighlightType.WEAK_WARNING);
}
}
}
}
};
}
use of com.jetbrains.php.lang.psi.elements.PhpClass in project phpinspectionsea by kalessil.
the class LongInheritanceChainInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpClass(@NotNull PhpClass clazz) {
final PsiElement psiClassName = NamedElementUtil.getNameIdentifier(clazz);
final String className = clazz.getName();
/* skip un-reportable, exception and test classes */
if (psiClassName == null || className.endsWith("Exception") || this.isTestContext(clazz)) {
return;
}
PhpClass classToCheck = clazz;
PhpClass parent = OpenapiResolveUtil.resolveSuperClass(classToCheck);
/* false-positives: abstract class implementation */
if (null != parent && !classToCheck.isAbstract() && parent.isAbstract()) {
return;
}
int parentsCount = 0;
/* in source code class CAN extend itself, PS will report it but data structure is incorrect still */
while (null != parent && clazz != parent) {
classToCheck = parent;
parent = OpenapiResolveUtil.resolveSuperClass(classToCheck);
++parentsCount;
if (null != parent) {
/* show-stoppers: frameworks god classes */
if (showStoppers.contains(parent.getFQN())) {
++parentsCount;
break;
}
/* exceptions named according to DDD, check parents named with exception suffix */
if (parent.getName().endsWith("Exception")) {
return;
}
}
}
if (parentsCount >= COMPLAIN_THRESHOLD && !clazz.isDeprecated()) {
holder.registerProblem(psiClassName, MessagesPresentationUtil.prefixWithEa(messagePattern.replace("%c%", String.valueOf(parentsCount))), ProblemHighlightType.WEAK_WARNING);
}
}
};
}
use of com.jetbrains.php.lang.psi.elements.PhpClass in project phpinspectionsea by kalessil.
the class InstanceOfTraitStrategy method apply.
public static boolean apply(@NotNull BinaryExpression expression, @NotNull ProblemsHolder holder) {
/* general structure expectations */
if (expression.getOperationType() != PhpTokenTypes.kwINSTANCEOF) {
return false;
}
final PsiElement right = expression.getRightOperand();
if (!(right instanceof ClassReference) && !(right instanceof ClassConstantReference)) {
return false;
}
/* $this, self, static are referencing to host classes, skip the case */
if (lateBindingSymbols.contains(right.getText())) {
return false;
}
/* getting class from invariant constructs */
PsiElement resolved = null;
if (right instanceof ClassReference) {
resolved = OpenapiResolveUtil.resolveReference((ClassReference) right);
}
if (right instanceof ClassConstantReference) {
final ClassConstantReference ref = (ClassConstantReference) right;
final PsiElement classReference = ref.getClassReference();
final String constantName = ref.getName();
if (null != constantName && constantName.equals("class") && classReference instanceof ClassReference) {
resolved = OpenapiResolveUtil.resolveReference((ClassReference) classReference);
}
}
/* analysis itself */
if (resolved instanceof PhpClass && ((PhpClass) resolved).isTrait()) {
holder.registerProblem(expression, MessagesPresentationUtil.prefixWithEa(message));
return true;
}
return false;
}
use of com.jetbrains.php.lang.psi.elements.PhpClass in project phpinspectionsea by kalessil.
the class ClassReImplementsParentInterfaceInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpClass(@NotNull PhpClass clazz) {
final List<ClassReference> implemented = clazz.getImplementsList().getReferenceElements();
if (!implemented.isEmpty()) {
/* resolve own interfaces an maintain relation to original element */
final Map<PsiElement, PhpClass> ownInterfaces = new LinkedHashMap<>(implemented.size());
for (final ClassReference reference : implemented) {
final PsiElement resolved = OpenapiResolveUtil.resolveReference(reference);
if (resolved instanceof PhpClass) {
ownInterfaces.put(reference, (PhpClass) resolved);
}
}
implemented.clear();
if (!ownInterfaces.isEmpty()) {
/* Case: indirect declaration duplication (parent already implements) */
final PhpClass parent = OpenapiResolveUtil.resolveSuperClass(clazz);
if (parent != null) {
final Set<PhpClass> inherited = InterfacesExtractUtil.getCrawlInheritanceTree(parent, false);
if (!inherited.isEmpty()) {
final Set<PsiElement> processed = new HashSet<>();
for (final Map.Entry<PsiElement, PhpClass> entry : ownInterfaces.entrySet()) {
final PhpClass ownInterface = entry.getValue();
if (inherited.contains(ownInterface) && processed.add(entry.getKey())) {
holder.registerProblem(entry.getKey(), String.format(MessagesPresentationUtil.prefixWithEa(patternIndirectDuplication), ownInterface.getFQN(), parent.getFQN()), new TheLocalFix());
}
}
processed.clear();
inherited.clear();
}
}
ownInterfaces.clear();
}
}
}
};
}
use of com.jetbrains.php.lang.psi.elements.PhpClass 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