use of com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag in project phpinspectionsea by kalessil.
the class ThrowsResolveUtil method collectThrownAndInherited.
private static boolean collectThrownAndInherited(@NotNull Method method, @NotNull Collection<PhpClass> exceptionsRegistry, @NotNull Collection<Method> processedMethods, boolean lookupWorkflow) {
processedMethods.add(method);
boolean result = false;
final PhpDocComment annotations = method.getDocComment();
if (annotations == null) {
/* if PhpDoc is missing, check workflow; but we'll search only `throw new ...` statements */
if (lookupWorkflow && !method.isAbstract()) {
for (final PhpThrow thrown : PsiTreeUtil.findChildrenOfType(method, PhpThrow.class)) {
final PsiElement argument = thrown.getArgument();
if (argument instanceof NewExpression) {
final PsiElement classReference = ((NewExpression) argument).getClassReference();
if (classReference != null) {
/* false-positives: lambdas and anonymous classes */
if (PsiTreeUtil.getParentOfType(thrown, Function.class) != method) {
continue;
} else /* false-positives: ALL try-enclosed throw statements */
if (PsiTreeUtil.getParentOfType(thrown, Try.class, false, (Class) Method.class) != null) {
continue;
}
final PsiElement clazz = OpenapiResolveUtil.resolveReference((PsiReference) classReference);
if (clazz instanceof PhpClass) {
exceptionsRegistry.add((PhpClass) clazz);
}
}
}
}
result = true;
}
} else {
/* find all @throws and remember FQNs, @throws can be combined with @inheritdoc */
for (final PhpDocTag candidate : PsiTreeUtil.findChildrenOfType(annotations, PhpDocTag.class)) {
if (candidate.getName().equalsIgnoreCase("@throws")) {
/* definition styles can differ: single tags, pipe concatenated or combined */
for (final PhpDocType type : PsiTreeUtil.findChildrenOfType(candidate, PhpDocType.class)) {
final PsiElement clazz = OpenapiResolveUtil.resolveReference(type);
if (clazz instanceof PhpClass) {
exceptionsRegistry.add((PhpClass) clazz);
}
}
}
}
/* resolve inherit doc tags */
if (annotations.hasInheritDocTag()) {
collectInherited(method, exceptionsRegistry, processedMethods);
}
result = true;
}
return result;
}
use of com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag in project yii2support by nvlad.
the class ClassUtils method findClassInSeeTags.
@Nullable
public static PhpClass findClassInSeeTags(PhpIndex index, PhpClass phpClass, String searchClassFQN) {
if (phpClass.getDocComment() == null)
return null;
PhpClass activeRecordClass = null;
PhpDocTag[] tags = phpClass.getDocComment().getTagElementsByName("@see");
for (PhpDocTag tag : tags) {
String className = tag.getText().replace(tag.getName(), "").trim();
if (className.indexOf('\\') == -1) {
className = phpClass.getNamespaceName() + className;
}
PhpClass classInSee = getClass(index, className);
if (isClassInheritsOrEqual(classInSee, getClass(index, searchClassFQN))) {
activeRecordClass = classInSee;
break;
}
}
return activeRecordClass;
}
use of com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag in project phpinspectionsea by kalessil.
the class PhpUnitTestsInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpMethod(@NotNull Method method) {
final PhpClass clazz = method.getContainingClass();
final PsiElement nameNode = NamedElementUtil.getNameIdentifier(method);
final PhpDocComment phpDoc = method.getDocComment();
if (null == clazz || null == nameNode || phpDoc == null) {
return;
}
final boolean isMethodNamedAsTest = method.getName().startsWith("test");
for (final PhpDocTag tag : PsiTreeUtil.findChildrenOfType(phpDoc, PhpDocTag.class)) {
final String tagName = tag.getName();
if (tagName.equals("@dataProvider")) {
final PsiElement candidate = tag.getFirstPsiChild();
if (candidate instanceof PhpDocRef && this.isAnnotation(tag)) {
final List<PsiReference> references = Arrays.asList(candidate.getReferences());
if (!references.isEmpty()) {
Collections.reverse(references);
final PsiElement resolved = OpenapiResolveUtil.resolveReference(references.get(0));
if (resolved instanceof Method) {
if (SUGGEST_TO_USE_NAMED_DATASETS && !((Method) resolved).isAbstract()) {
final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(resolved);
final PsiElement last = body == null ? null : ExpressionSemanticUtil.getLastStatement(body);
if (last instanceof PhpReturn) {
final PsiElement value = ExpressionSemanticUtil.getReturnValue((PhpReturn) last);
if (value instanceof ArrayCreationExpression) {
final PsiElement firstChild = ((ArrayCreationExpression) value).getFirstPsiChild();
boolean isNamedDataset = firstChild == null;
if (firstChild instanceof ArrayHashElement) {
final PsiElement key = ((ArrayHashElement) firstChild).getKey();
isNamedDataset = key instanceof StringLiteralExpression;
}
if (!isNamedDataset) {
holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(messageNamedProvider));
}
}
}
}
} else {
holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(messageDataProvider), ProblemHighlightType.GENERIC_ERROR);
}
} else {
holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(messageDataProvider), ProblemHighlightType.GENERIC_ERROR);
}
}
} else if (tagName.equals("@depends")) {
final PsiElement candidate = tag.getFirstPsiChild();
if (candidate instanceof PhpDocRef && this.isAnnotation(tag)) {
final List<PsiReference> references = Arrays.asList(candidate.getReferences());
if (!references.isEmpty()) {
Collections.reverse(references);
final PsiElement resolved = OpenapiResolveUtil.resolveReference(references.get(0));
if (resolved instanceof Method) {
final Method dependency = (Method) resolved;
if (!dependency.getName().startsWith("test")) {
final PhpDocComment docBlock = dependency.getDocComment();
if (docBlock == null || docBlock.getTagElementsByName("@test").length == 0) {
holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(messageDepends), ProblemHighlightType.GENERIC_ERROR);
}
}
} else {
holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(messageDepends), ProblemHighlightType.GENERIC_ERROR);
}
} else {
holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(messageDepends), ProblemHighlightType.GENERIC_ERROR);
}
}
} else if (tagName.equals("@covers")) {
final PsiElement candidate = tag.getFirstPsiChild();
if (candidate instanceof PhpDocRef && this.isAnnotation(tag)) {
final PhpDocRef referenceNeeded = (PhpDocRef) candidate;
final List<PsiReference> references = Arrays.asList(referenceNeeded.getReferences());
Collections.reverse(references);
/* resolve references, populate information about provided entries */
boolean hasCallableReference = false;
boolean hasClassReference = false;
final String referenceText = referenceNeeded.getText();
for (final PsiReference ref : references) {
final PsiElement resolved = OpenapiResolveUtil.resolveReference(ref);
if (resolved instanceof PhpClass) {
hasClassReference = true;
hasCallableReference = referenceText.endsWith("::");
break;
} else if (resolved instanceof Function) {
hasCallableReference = true;
hasClassReference = resolved instanceof Method;
break;
}
}
final boolean callableNeeded = referenceText.contains("::") && !referenceText.contains("::<");
if ((callableNeeded && !hasCallableReference) || (!callableNeeded && !hasClassReference)) {
holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(String.format(messageCovers, referenceText)), ProblemHighlightType.GENERIC_ERROR);
}
}
} else if (tagName.equals("@test")) {
if (isMethodNamedAsTest && this.isAnnotation(tag)) {
holder.registerProblem(tag.getFirstChild(), MessagesPresentationUtil.prefixWithEa(messageTest), ProblemHighlightType.LIKE_DEPRECATED, new AmbiguousTestAnnotationLocalFix());
}
}
}
}
private boolean isAnnotation(@NotNull PhpDocTag tag) {
PsiElement previous = tag.getPrevSibling();
previous = previous instanceof PsiWhiteSpace ? previous.getPrevSibling() : previous;
final IElementType start = previous == null ? null : previous.getNode().getElementType();
return start == PhpTokenTypes.DOC_COMMENT_START || start == PhpTokenTypes.DOC_LEADING_ASTERISK;
}
@Override
public void visitPhpMethodReference(@NotNull MethodReference reference) {
final String methodName = reference.getName();
if (methodName != null) {
if (methodName.startsWith("assert") && !methodName.equals("assert")) {
final List<BooleanSupplier> callbacks = new ArrayList<>();
callbacks.add(() -> AssertBoolInvertedStrategy.apply(methodName, reference, holder));
callbacks.add(() -> AssertBoolOfComparisonStrategy.apply(methodName, reference, holder));
if (SUGGEST_TO_USE_ASSERTSAME) {
callbacks.add(() -> AssertSameStrategy.apply(methodName, reference, holder));
}
if (PROMOTE_PHPUNIT_API) {
final PhpUnitVersion version = PHP_UNIT_VERSION == null ? PhpUnitVersion.PHPUNIT80 : PHP_UNIT_VERSION;
callbacks.add(() -> AssertEmptyStrategy.apply(methodName, reference, holder));
callbacks.add(() -> AssertConstantStrategy.apply(methodName, reference, holder));
callbacks.add(() -> AssertInternalTypeStrategy.apply(methodName, reference, holder, version));
callbacks.add(() -> AssertInstanceOfStrategy.apply(methodName, reference, holder));
callbacks.add(() -> AssertResourceExistsStrategy.apply(methodName, reference, holder, version));
callbacks.add(() -> AssertCountStrategy.apply(methodName, reference, holder));
callbacks.add(() -> AssertContainsStrategy.apply(methodName, reference, holder, version));
callbacks.add(() -> AssertRegexStrategy.apply(methodName, reference, holder));
/* AssertFileEqualsStrategy and AssertStringEqualsFileStrategy order is important */
callbacks.add(() -> AssertFileEqualsStrategy.apply(methodName, reference, holder));
callbacks.add(() -> AssertStringEqualsFileStrategy.apply(methodName, reference, holder));
}
for (final BooleanSupplier callback : callbacks) {
if (callback.getAsBoolean()) {
break;
}
}
callbacks.clear();
} else if (methodName.equals("expects")) {
if (PROMOTE_MOCKING_ONCE) {
ExpectsOnceStrategy.apply(methodName, reference, holder);
}
} else if (methodName.equals("will")) {
if (PROMOTE_MOCKING_WILL_RETURN) {
WillReturnStrategy.apply(methodName, reference, holder);
}
}
}
}
};
}
use of com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag in project phpinspectionsea by kalessil.
the class UnknownInspectionInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpDocTag(@NotNull PhpDocTag tag) {
if (tag.getName().equals("@noinspection")) {
final String[] candidates = tag.getTagValue().replaceAll("[^\\p{L}\\p{Nd}]+", " ").trim().split("\\s+");
if (candidates.length > 0) {
final List<String> unknown = Stream.of(candidates[0]).filter(c -> !inspections.contains(c) && !inspections.contains(c + "Inspection")).collect(Collectors.toList());
if (!unknown.isEmpty()) {
final PsiElement target = tag.getFirstChild();
if (target != null) {
holder.registerProblem(target, String.format(MessagesPresentationUtil.prefixWithEa(message), String.join(", ", unknown)));
}
unknown.clear();
}
}
}
}
};
}
use of com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag in project phpinspectionsea by kalessil.
the class UnusedConstructorDependenciesInspector method buildVisitor.
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@NotNull
private Map<String, Field> getPrivateFields(@NotNull PhpClass clazz) {
final Map<String, Field> privateFields = new HashMap<>();
for (final Field field : clazz.getOwnFields()) {
if (!field.isConstant()) {
final PhpModifier modifiers = field.getModifier();
if (modifiers.isPrivate() && !modifiers.isStatic()) {
final PhpDocTag[] tags = PsiTreeUtil.getChildrenOfType(field.getDocComment(), PhpDocTag.class);
final boolean annotated = tags != null && Arrays.stream(tags).anyMatch(t -> !t.getName().equals(t.getName().toLowerCase()));
if (!annotated) {
privateFields.put(field.getName(), field);
}
}
}
}
return privateFields;
}
@NotNull
private Map<String, List<FieldReference>> getFieldReferences(@NotNull Method method, @NotNull Map<String, Field> privateFields) {
final Map<String, List<FieldReference>> filteredReferences = new HashMap<>();
if (!method.isAbstract()) {
final Collection<FieldReference> references = PsiTreeUtil.findChildrenOfType(method, FieldReference.class);
for (final FieldReference ref : references) {
final String fieldName = ref.getName();
if (fieldName != null && privateFields.containsKey(fieldName)) {
final PsiElement resolved = OpenapiResolveUtil.resolveReference(ref);
if (resolved == null || (resolved instanceof Field && privateFields.containsValue(resolved))) {
filteredReferences.computeIfAbsent(fieldName, k -> new ArrayList<>()).add(ref);
}
}
}
references.clear();
}
return filteredReferences;
}
@NotNull
private Map<String, List<FieldReference>> getMethodsFieldReferences(@NotNull PhpClass clazz, @NotNull Method constructor, @NotNull Map<String, Field> privateFields) {
final Map<String, List<FieldReference>> filteredReferences = new HashMap<>();
/* collect methods to inspect, incl. once from traits */
final List<Method> methodsToCheck = new ArrayList<>();
Collections.addAll(methodsToCheck, clazz.getOwnMethods());
for (final PhpClass trait : clazz.getTraits()) {
Collections.addAll(methodsToCheck, trait.getOwnMethods());
}
/* find references */
for (final Method method : methodsToCheck) {
if (method != constructor) {
final Map<String, List<FieldReference>> innerReferences = getFieldReferences(method, privateFields);
if (!innerReferences.isEmpty()) {
/* merge method's scan results into common container */
innerReferences.forEach((fieldName, references) -> {
filteredReferences.computeIfAbsent(fieldName, name -> new ArrayList<>()).addAll(references);
references.clear();
});
}
innerReferences.clear();
}
}
methodsToCheck.clear();
return filteredReferences;
}
@Override
public void visitPhpMethod(@NotNull Method method) {
/* filter classes which needs to be analyzed */
final PhpClass clazz = method.getContainingClass();
if (null == clazz || clazz.isInterface() || clazz.isTrait() || null == clazz.getOwnConstructor() || 0 == clazz.getOwnFields().length || 0 == clazz.getOwnMethods().length) {
return;
}
/* run inspection only in constructors and if own private fields being defined */
final Method constructor = clazz.getOwnConstructor();
if (method != constructor) {
return;
}
final Map<String, Field> clazzPrivateFields = this.getPrivateFields(clazz);
if (clazzPrivateFields.isEmpty()) {
return;
}
/* === intensive part : extract references === */
final Map<String, List<FieldReference>> constructorsReferences = getFieldReferences(constructor, clazzPrivateFields);
if (!constructorsReferences.isEmpty()) {
/* constructor's references being identified */
final Map<String, List<FieldReference>> otherReferences = getMethodsFieldReferences(clazz, constructor, clazzPrivateFields);
/* methods's references being identified, time to re-visit constructor's references */
constructorsReferences.forEach((fieldName, references) -> {
/* field is not used, report in constructor, IDE detects unused fields */
if (!otherReferences.containsKey(fieldName)) {
/* ensure the field reference in constructor is not bound to closures only */
if (references.stream().anyMatch(r -> ExpressionSemanticUtil.getScope(r) == constructor)) {
this.doReport(holder, references);
}
}
references.clear();
});
/* release references found in the methods */
otherReferences.values().forEach(List::clear);
otherReferences.clear();
}
constructorsReferences.clear();
}
private void doReport(@NotNull ProblemsHolder holder, @NotNull List<FieldReference> fields) {
fields.stream().filter(reference -> {
final PsiElement parent = reference.getParent();
if (OpenapiTypesUtil.isAssignment(parent)) {
return reference == ((AssignmentExpression) parent).getVariable();
}
return false;
}).forEach(reference -> holder.registerProblem(reference, MessagesPresentationUtil.prefixWithEa(message)));
}
};
}
Aggregations