use of com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment 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.PhpDocComment in project yii2support by nvlad.
the class DatabaseUtils method getDocComment.
@Nullable
public static PhpDocComment getDocComment(PsiElement element) {
PsiElement nextElement = element.getParent();
int limit = 10;
while (limit > 0) {
if (nextElement instanceof PhpDocComment)
return (PhpDocComment) nextElement;
else if (nextElement == null)
return null;
nextElement = nextElement.getParent();
limit--;
}
return null;
}
use of com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment in project phpinspectionsea by kalessil.
the class CascadeStringReplacementInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpReturn(@NotNull PhpReturn returnStatement) {
final FunctionReference functionCall = this.getFunctionReference(returnStatement);
if (functionCall != null) {
this.analyze(functionCall, returnStatement);
}
}
@Override
public void visitPhpAssignmentExpression(@NotNull AssignmentExpression assignmentExpression) {
final FunctionReference functionCall = this.getFunctionReference(assignmentExpression);
if (functionCall != null) {
this.analyze(functionCall, assignmentExpression);
}
}
private void analyze(@NotNull FunctionReference functionCall, @NotNull PsiElement expression) {
final PsiElement[] arguments = functionCall.getParameters();
if (arguments.length == 3) {
/* case: cascading replacements */
final AssignmentExpression previous = this.getPreviousAssignment(expression);
final FunctionReference previousCall = previous == null ? null : this.getFunctionReference(previous);
if (previousCall != null) {
final PsiElement transitionVariable = previous.getVariable();
if (transitionVariable instanceof Variable && arguments[2] instanceof Variable) {
final Variable callSubject = (Variable) arguments[2];
final Variable previousVariable = (Variable) transitionVariable;
final PsiElement callResultStorage = expression instanceof AssignmentExpression ? ((AssignmentExpression) expression).getVariable() : callSubject;
if (callResultStorage != null && callSubject.getName().equals(previousVariable.getName()) && OpenapiEquivalenceUtil.areEqual(transitionVariable, callResultStorage) && this.canMergeArguments(functionCall, previousCall)) {
holder.registerProblem(functionCall, MessagesPresentationUtil.prefixWithEa(messageCascading), new MergeStringReplaceCallsFix(holder.getProject(), functionCall, previousCall, USE_SHORT_ARRAYS_SYNTAX));
}
}
}
/* case: nested replacements */
this.checkNestedCalls(holder.getProject(), arguments[2], functionCall);
/* case: search simplification */
if (arguments[1] instanceof StringLiteralExpression) {
final PsiElement search = arguments[0];
if (search instanceof ArrayCreationExpression) {
this.checkForSimplification((ArrayCreationExpression) search);
}
}
}
}
private void checkForSimplification(@NotNull ArrayCreationExpression candidate) {
final Set<String> replacements = new HashSet<>();
for (final PsiElement oneReplacement : candidate.getChildren()) {
if (oneReplacement instanceof PhpPsiElement) {
final PhpPsiElement item = ((PhpPsiElement) oneReplacement).getFirstPsiChild();
if (!(item instanceof StringLiteralExpression)) {
replacements.clear();
return;
}
replacements.add(item.getText());
}
}
if (replacements.size() == 1) {
holder.registerProblem(candidate, MessagesPresentationUtil.prefixWithEa(messageReplacements), ProblemHighlightType.WEAK_WARNING, new SimplifyReplacementFix(replacements.iterator().next()));
}
replacements.clear();
}
private void checkNestedCalls(@NotNull Project project, @NotNull PsiElement callCandidate, @NotNull FunctionReference parentCall) {
if (OpenapiTypesUtil.isFunctionReference(callCandidate)) {
final FunctionReference functionCall = (FunctionReference) callCandidate;
final String functionName = functionCall.getName();
if (functionName != null && functionName.equals("str_replace") && this.canMergeArguments(functionCall, parentCall)) {
holder.registerProblem(callCandidate, MessagesPresentationUtil.prefixWithEa(messageNesting), new MergeStringReplaceCallsFix(project, parentCall, functionCall, USE_SHORT_ARRAYS_SYNTAX));
}
}
}
private boolean canMergeArguments(@NotNull FunctionReference call, @NotNull FunctionReference previousCall) {
final PsiElement[] arguments = call.getParameters();
final PsiElement[] previousArguments = previousCall.getParameters();
// If an argument is array (non-implicit), we need PHP 7.4+ for QF
final boolean haveArrayType = Stream.of(arguments[0], arguments[1], previousArguments[0], previousArguments[1]).filter(a -> a instanceof PhpTypedElement && !(a instanceof ArrayCreationExpression)).anyMatch(a -> {
final PhpType resolved = OpenapiResolveUtil.resolveType((PhpTypedElement) a, holder.getProject());
if (resolved != null) {
final Set<String> types = resolved.filterUnknown().getTypes().stream().map(Types::getType).collect(Collectors.toSet());
return types.contains(Types.strArray) && !types.contains(Types.strString);
}
return false;
});
if (haveArrayType) {
return PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP740);
}
return true;
}
@Nullable
private FunctionReference getFunctionReference(@NotNull AssignmentExpression assignment) {
FunctionReference result = null;
final PsiElement value = ExpressionSemanticUtil.getExpressionTroughParenthesis(assignment.getValue());
if (OpenapiTypesUtil.isFunctionReference(value)) {
final String functionName = ((FunctionReference) value).getName();
if (functionName != null && functionName.equals("str_replace")) {
result = (FunctionReference) value;
}
}
return result;
}
@Nullable
private FunctionReference getFunctionReference(@NotNull PhpReturn phpReturn) {
FunctionReference result = null;
final PsiElement value = ExpressionSemanticUtil.getExpressionTroughParenthesis(ExpressionSemanticUtil.getReturnValue(phpReturn));
if (OpenapiTypesUtil.isFunctionReference(value)) {
final String functionName = ((FunctionReference) value).getName();
if (functionName != null && functionName.equals("str_replace")) {
result = (FunctionReference) value;
}
}
return result;
}
@Nullable
private AssignmentExpression getPreviousAssignment(@NotNull PsiElement returnOrAssignment) {
/* get previous non-comment, non-php-doc expression */
PsiElement previous = null;
if (returnOrAssignment instanceof PhpReturn) {
previous = ((PhpReturn) returnOrAssignment).getPrevPsiSibling();
} else if (returnOrAssignment instanceof AssignmentExpression) {
previous = returnOrAssignment.getParent().getPrevSibling();
}
while (previous != null && !(previous instanceof PhpPsiElement)) {
previous = previous.getPrevSibling();
}
while (previous instanceof PhpDocComment) {
previous = ((PhpDocComment) previous).getPrevPsiSibling();
}
/* grab the target assignment */
final AssignmentExpression result;
if (previous != null && previous.getFirstChild() instanceof AssignmentExpression) {
result = (AssignmentExpression) previous.getFirstChild();
} else {
result = null;
}
return result;
}
};
}
use of com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment in project phpinspectionsea by kalessil.
the class AlterInForeachInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpForeach(@NotNull ForeachStatement foreach) {
/* lookup for reference preceding value */
final Variable objForeachValue = foreach.getValue();
if (null != objForeachValue) {
PsiElement prevElement = objForeachValue.getPrevSibling();
if (prevElement instanceof PsiWhiteSpace) {
prevElement = prevElement.getPrevSibling();
}
if (null != prevElement) {
PhpPsiElement nextExpression = foreach.getNextPsiSibling();
if (OpenapiTypesUtil.is(prevElement, PhpTokenTypes.opBIT_AND)) {
/* === requested by the community, not part of original idea === */
/* look up for parents which doesn't have following statements, eg #53 */
PsiElement foreachParent = foreach.getParent();
while (null == nextExpression && null != foreachParent) {
if (!(foreachParent instanceof GroupStatement)) {
nextExpression = ((PhpPsiElement) foreachParent).getNextPsiSibling();
}
foreachParent = foreachParent.getParent();
if (null == foreachParent || foreachParent instanceof Function || foreachParent instanceof PhpFile) {
break;
}
}
/* === requested by the community, not part of original idea === */
/* allow return/end of control flow after the loop - no issues can be introduced */
boolean isRequirementFullFilled = false;
while (nextExpression instanceof PhpDocComment) {
nextExpression = nextExpression.getNextPsiSibling();
}
if (nextExpression == null || nextExpression instanceof PhpReturn || OpenapiTypesUtil.isThrowExpression(nextExpression)) {
isRequirementFullFilled = true;
}
/* check unset is applied to value-variable */
final String foreachValueName = objForeachValue.getName();
if (nextExpression instanceof PhpUnset && !StringUtils.isEmpty(foreachValueName)) {
for (PhpPsiElement unsetArgument : ((PhpUnset) nextExpression).getArguments()) {
// skip non-variable expressions
if (!(unsetArgument instanceof Variable)) {
continue;
}
// check argument matched foreach value name
final String unsetArgumentName = unsetArgument.getName();
if (!StringUtils.isEmpty(unsetArgumentName) && unsetArgumentName.equals(foreachValueName)) {
isRequirementFullFilled = true;
break;
}
}
}
/* check if warning needs to be reported */
if (!isRequirementFullFilled) {
holder.registerProblem(objForeachValue, MessagesPresentationUtil.prefixWithEa(messageMissingUnset));
}
} else {
/* check for unset in parent foreach-statements: foreach-{foreach}-unset */
ForeachStatement currentForeach = foreach;
while (!(nextExpression instanceof PhpUnset) && currentForeach.getParent() instanceof GroupStatement && currentForeach.getParent().getParent() instanceof ForeachStatement) {
currentForeach = (ForeachStatement) currentForeach.getParent().getParent();
nextExpression = currentForeach.getNextPsiSibling();
}
/* check if un-sets non-reference value - not needed at all, probably forgotten to cleanup */
if (nextExpression instanceof PhpUnset) {
final PhpPsiElement[] unsetArguments = ((PhpUnset) nextExpression).getArguments();
if (unsetArguments.length > 0) {
final String foreachValueName = objForeachValue.getName();
for (PhpPsiElement unsetExpression : unsetArguments) {
if (!(unsetArguments[0] instanceof Variable)) {
continue;
}
final String unsetArgumentName = unsetExpression.getName();
if (!StringUtils.isEmpty(unsetArgumentName) && !StringUtils.isEmpty(foreachValueName) && unsetArgumentName.equals(foreachValueName)) {
holder.registerProblem(unsetExpression, MessagesPresentationUtil.prefixWithEa(patternAmbiguousUnset.replace("%v%", foreachValueName)), ProblemHighlightType.WEAK_WARNING);
}
}
}
}
}
}
}
}
@Override
public void visitPhpAssignmentExpression(@NotNull AssignmentExpression assignmentExpression) {
if (!SUGGEST_USING_VALUE_BY_REF) /*|| ... PHP7 ...*/
{
return;
}
final PhpPsiElement operand = assignmentExpression.getVariable();
if (!(operand instanceof ArrayAccessExpression)) {
return;
}
/* ensure assignment structure is complete */
final ArrayAccessExpression container = (ArrayAccessExpression) operand;
if (null == container.getIndex() || null == container.getValue() || !(container.getIndex().getValue() instanceof Variable)) {
return;
}
/* get parts of assignment */
final PhpPsiElement objForeachSourceCandidate = container.getValue();
final PhpPsiElement objForeachKeyCandidate = container.getIndex().getValue();
PsiElement parent = assignmentExpression.getParent();
while (null != parent && !(parent instanceof PhpFile)) {
/* terminate if reached callable */
if (parent instanceof Function) {
return;
}
if (parent instanceof ForeachStatement) {
/* get parts of foreach: array, key, value */
final ForeachStatement objForeach = (ForeachStatement) parent;
final Variable objForeachValue = objForeach.getValue();
final Variable objForeachKey = objForeach.getKey();
final PsiElement objForeachArray = objForeach.getArray();
/* report if aggressive optimization possible: foreach(... as &$value) */
if (null != objForeachArray && null != objForeachKey && null != objForeachValue && OpenapiEquivalenceUtil.areEqual(objForeachKey, objForeachKeyCandidate) && OpenapiEquivalenceUtil.areEqual(objForeachArray, objForeachSourceCandidate)) {
final String strName = objForeachValue.getName();
if (!StringUtils.isEmpty(strName)) {
holder.registerProblem(operand, MessagesPresentationUtil.prefixWithEa(patternSuggestReference.replace("%c%", strName).replace("%v%", strName)), ProblemHighlightType.WEAK_WARNING);
return;
}
}
}
parent = parent.getParent();
}
}
};
}
use of com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment in project phpinspectionsea by kalessil.
the class DropMethodFix method applyFix.
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement expression = descriptor.getPsiElement().getParent();
if (expression instanceof Method && !project.isDisposed()) {
/* delete preceding PhpDoc */
final PhpPsiElement previous = ((Method) expression).getPrevPsiSibling();
if (previous instanceof PhpDocComment) {
previous.delete();
}
/* delete space after the method */
final PsiElement nextExpression = expression.getNextSibling();
if (nextExpression instanceof PsiWhiteSpace) {
nextExpression.delete();
}
/* delete the method itself */
expression.delete();
}
}
Aggregations