use of com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment in project phpinspectionsea by kalessil.
the class OnlyWritesOnParameterInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpMethod(@NotNull Method method) {
if (!method.isAbstract()) {
this.visitPhpFunction(method);
}
}
@Override
public void visitPhpFunction(@NotNull Function function) {
Arrays.stream(function.getParameters()).filter(parameter -> !parameter.getName().isEmpty() && !parameter.isPassByRef()).filter(parameter -> {
final PhpType declaredType = OpenapiResolveUtil.resolveDeclaredType(parameter).filterUnknown().filterNull();
final boolean isObject = !declaredType.isEmpty() && declaredType.getTypes().stream().anyMatch(t -> {
final String type = Types.getType(t);
return type.equals(Types.strObject) || type.startsWith("\\");
});
return !isObject;
}).forEach(parameter -> this.analyzeAndReturnUsagesCount(parameter.getName(), function));
final List<Variable> variables = ExpressionSemanticUtil.getUseListVariables(function);
if (variables != null) {
this.checkUseVariables(variables, function);
variables.clear();
}
}
@Override
public void visitPhpAssignmentExpression(@NotNull AssignmentExpression assignment) {
/* because this hook fired e.g. for `.=` assignments (a BC break by PhpStorm) */
if (OpenapiTypesUtil.isAssignment(assignment)) {
final PsiElement variable = assignment.getVariable();
if (variable instanceof Variable) {
/* false-positives: predefined and global variables */
final String variableName = ((Variable) variable).getName();
if (variableName.isEmpty() || ExpressionCostEstimateUtil.predefinedVars.contains(variableName)) {
return;
}
/* filter target contexts: we are supporting only certain of them */
final PsiElement parent = assignment.getParent();
final boolean isTargetContext = parent instanceof ParenthesizedExpression || parent instanceof ArrayIndex || (parent instanceof BinaryExpression && OpenapiTypesUtil.tsCOMPARE_EQUALITY_OPS.contains(((BinaryExpression) parent).getOperationType())) || OpenapiTypesUtil.isStatementImpl(parent) || OpenapiTypesUtil.isAssignment(parent);
if (isTargetContext) {
final Function scope = ExpressionSemanticUtil.getScope(assignment);
if (scope != null && Arrays.stream(scope.getParameters()).noneMatch(p -> p.getName().equals(variableName))) {
final List<Variable> uses = ExpressionSemanticUtil.getUseListVariables(scope);
final boolean isUseVariable = uses != null && uses.stream().anyMatch(u -> u.getName().equals(variableName));
if (!isUseVariable) {
this.analyzeAndReturnUsagesCount(variableName, scope);
}
}
}
}
}
}
private void checkUseVariables(@NotNull List<Variable> variables, @NotNull Function function) {
for (final Variable variable : variables) {
final String parameterName = variable.getName();
if (!parameterName.isEmpty()) {
PsiElement previous = variable.getPrevSibling();
if (previous instanceof PsiWhiteSpace) {
previous = previous.getPrevSibling();
}
if (OpenapiTypesUtil.is(previous, PhpTokenTypes.opBIT_AND)) {
if (OpenapiControlFlowUtil.getFollowingVariableAccessInstructions(function.getControlFlow().getEntryPoint(), parameterName).isEmpty()) {
holder.registerProblem(variable, MessagesPresentationUtil.prefixWithEa(messageUnused), ProblemHighlightType.LIKE_UNUSED_SYMBOL);
}
} else if (this.analyzeAndReturnUsagesCount(parameterName, function) == 0) {
holder.registerProblem(variable, MessagesPresentationUtil.prefixWithEa(messageUnused), ProblemHighlightType.LIKE_UNUSED_SYMBOL);
}
}
}
}
private int analyzeAndReturnUsagesCount(@NotNull String parameterName, @NotNull Function function) {
final List<PhpAccessVariableInstruction> usages = OpenapiControlFlowUtil.getFollowingVariableAccessInstructions(function.getControlFlow().getEntryPoint(), parameterName);
if (usages.isEmpty()) {
return 0;
}
final List<PsiElement> targetExpressions = new ArrayList<>();
boolean isReference = false;
int intCountReadAccesses = 0;
int intCountWriteAccesses = 0;
for (final PhpAccessVariableInstruction instruction : usages) {
final PsiElement variable = instruction.getAnchor();
final PsiElement parent = variable.getParent();
if (parent instanceof ArrayAccessExpression) {
/* find out which expression is holder */
PsiElement objLastSemanticExpression = variable;
PsiElement objTopSemanticExpression = objLastSemanticExpression.getParent();
/* TODO: iterator for array access expression */
while (objTopSemanticExpression instanceof ArrayAccessExpression) {
objLastSemanticExpression = objTopSemanticExpression;
objTopSemanticExpression = objTopSemanticExpression.getParent();
}
/* estimate operation type */
if (objTopSemanticExpression instanceof AssignmentExpression && ((AssignmentExpression) objTopSemanticExpression).getVariable() == objLastSemanticExpression) {
intCountWriteAccesses++;
if (isReference) {
/* when modifying the reference it's link READ and linked WRITE semantics */
intCountReadAccesses++;
} else {
/* when modifying non non-reference, register as write only access for reporting */
targetExpressions.add(objLastSemanticExpression);
}
continue;
}
if (objTopSemanticExpression instanceof UnaryExpression) {
final PsiElement operation = ((UnaryExpression) objTopSemanticExpression).getOperation();
if (OpenapiTypesUtil.is(operation, PhpTokenTypes.opINCREMENT) || OpenapiTypesUtil.is(operation, PhpTokenTypes.opDECREMENT)) {
targetExpressions.add(objLastSemanticExpression);
++intCountWriteAccesses;
continue;
}
}
intCountReadAccesses++;
continue;
}
/* ++/-- operations */
if (parent instanceof UnaryExpression) {
final PsiElement operation = ((UnaryExpression) parent).getOperation();
if (OpenapiTypesUtil.is(operation, PhpTokenTypes.opINCREMENT) || OpenapiTypesUtil.is(operation, PhpTokenTypes.opDECREMENT)) {
++intCountWriteAccesses;
if (isReference) {
/* when modifying the reference it's link READ and linked WRITE semantics */
++intCountReadAccesses;
} else {
/* when modifying non-reference, register as write only access for reporting */
targetExpressions.add(parent);
}
}
if (!OpenapiTypesUtil.isStatementImpl(parent.getParent())) {
++intCountReadAccesses;
}
continue;
}
if (parent instanceof SelfAssignmentExpression) {
final SelfAssignmentExpression selfAssignment = (SelfAssignmentExpression) parent;
final PsiElement sameVariableCandidate = selfAssignment.getVariable();
if (sameVariableCandidate instanceof Variable) {
final Variable candidate = (Variable) sameVariableCandidate;
if (candidate.getName().equals(parameterName)) {
++intCountWriteAccesses;
if (isReference) {
/* when modifying the reference it's link READ and linked WRITE semantics */
++intCountReadAccesses;
} else {
/* when modifying non-reference, register as write only access for reporting */
targetExpressions.add(variable);
}
if (!OpenapiTypesUtil.isStatementImpl(parent.getParent())) {
++intCountReadAccesses;
}
continue;
}
}
}
/* if variable assigned with reference, we need to preserve this information for correct checks */
if (parent instanceof AssignmentExpression) {
/* ensure variable with the same name being written */
final AssignmentExpression referenceAssignmentCandidate = (AssignmentExpression) parent;
/* check if the target used as a container */
final PsiElement assignmentVariableCandidate = referenceAssignmentCandidate.getVariable();
if (assignmentVariableCandidate instanceof Variable) {
final Variable candidate = (Variable) assignmentVariableCandidate;
if (candidate.getName().equals(parameterName)) {
++intCountWriteAccesses;
if (isReference) {
/* when modifying the reference it's link READ and linked WRITE semantics */
++intCountReadAccesses;
}
/* now ensure operation is assignment of reference */
if (OpenapiTypesUtil.isAssignmentByReference(referenceAssignmentCandidate)) {
isReference = true;
}
/* false-negative: inline assignment result has been used */
if (usages.size() == 2 && usages.get(0).getAnchor() == usages.get(1).getAnchor()) {
holder.registerProblem(assignmentVariableCandidate, MessagesPresentationUtil.prefixWithEa(messageUnused), ProblemHighlightType.LIKE_UNUSED_SYMBOL);
return 1;
}
continue;
}
}
/* check if the target used as a value */
final PsiElement assignmentValueCandidate = referenceAssignmentCandidate.getValue();
if (assignmentValueCandidate instanceof Variable) {
final Variable candidate = (Variable) assignmentValueCandidate;
if (candidate.getName().equals(parameterName)) {
++intCountReadAccesses;
continue;
}
}
}
/* local variables access wrongly reported write in some cases, so rely on custom checks */
if (parent instanceof ParameterList || parent instanceof PhpUseList || parent instanceof PhpUnset || parent instanceof PhpEmpty || parent instanceof PhpIsset || parent instanceof ForeachStatement) {
intCountReadAccesses++;
continue;
}
/* ok variable usage works well with openapi */
final PhpAccessInstruction.Access instructionAccess = instruction.getAccess();
if (instructionAccess.isWrite()) {
targetExpressions.add(variable);
++intCountWriteAccesses;
} else if (instructionAccess.isRead()) {
++intCountReadAccesses;
}
}
if (intCountReadAccesses == 0 && intCountWriteAccesses > 0 && !this.isAnySuppressed(targetExpressions)) {
final boolean report = IGNORE_INCLUDES || !this.hasIncludes(function);
if (report) {
for (final PsiElement targetExpression : new HashSet<>(targetExpressions)) {
holder.registerProblem(targetExpression, MessagesPresentationUtil.prefixWithEa(messageOnlyWrites), ProblemHighlightType.LIKE_UNUSED_SYMBOL);
}
}
}
targetExpressions.clear();
return usages.size();
}
private boolean isAnySuppressed(@NotNull List<PsiElement> expressions) {
for (final PsiElement one : expressions) {
final PsiElement parent = one.getParent();
if (parent instanceof AssignmentExpression) {
final PsiElement grandParent = parent.getParent();
if (OpenapiTypesUtil.isStatementImpl(grandParent)) {
final PsiElement previous = ((PhpPsiElement) grandParent).getPrevPsiSibling();
if (previous instanceof PhpDocComment) {
final String candidate = previous.getText();
if (candidate.contains("@noinspection") && candidate.contains(getShortName())) {
return true;
}
}
}
}
}
return false;
}
private boolean hasIncludes(@NotNull Function function) {
final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(function);
if (body != null) {
return PsiTreeUtil.findChildOfType(body, Include.class) != null;
}
return false;
}
};
}
use of com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment in project phpinspectionsea by kalessil.
the class MultiAssignmentUsageInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpMultiassignmentExpression(@NotNull MultiassignmentExpression multiassignmentExpression) {
/* ensure php version is at least PHP 5.5 */
if (PhpLanguageLevel.get(holder.getProject()).below(PhpLanguageLevel.PHP550)) {
return;
}
/* verify if it's dedicated statement and it's the list(...) construction */
PsiElement parent = multiassignmentExpression.getParent();
if (!OpenapiTypesUtil.isStatementImpl(parent)) {
return;
}
final PsiElement listKeyword = multiassignmentExpression.getFirstChild();
final IElementType nodeType = null == listKeyword ? null : listKeyword.getNode().getElementType();
if (null == nodeType || (PhpTokenTypes.kwLIST != nodeType && PhpTokenTypes.chLBRACKET != nodeType)) {
return;
}
/* extract container: it needs to be a variable */
PsiElement container = multiassignmentExpression.getValue();
if (OpenapiTypesUtil.isPhpExpressionImpl(container)) {
container = ((PhpExpression) container).getFirstPsiChild();
}
if (!(container instanceof Variable)) {
return;
}
/* lookup parent foreach-statements for providing the container */
/* TODO: check if container being used 2+ times in the foreach-expression */
boolean stopAnalysis = false;
final String containerName = ((Variable) container).getName();
while (null != parent && !(parent instanceof Function) && !(parent instanceof PhpFile)) {
if (parent instanceof ForeachStatement) {
final List<Variable> variables = ((ForeachStatement) parent).getVariables();
for (final Variable variable : variables) {
if (variable.getName().equals(containerName)) {
stopAnalysis = true;
holder.registerProblem(multiassignmentExpression, MessagesPresentationUtil.prefixWithEa(messageImplicitList));
break;
}
}
variables.clear();
if (stopAnalysis) {
break;
}
}
parent = parent.getParent();
}
}
@Override
public void visitPhpAssignmentExpression(@NotNull AssignmentExpression assignmentExpression) {
/* ensure we are writing into a variable */
if (!(assignmentExpression.getVariable() instanceof Variable)) {
return;
}
/* ensure that preceding expression is also an assignment */
final PsiElement parent = assignmentExpression.getParent();
if (!OpenapiTypesUtil.isStatementImpl(parent)) {
return;
}
PsiElement previous = ((Statement) parent).getPrevPsiSibling();
while (previous instanceof PhpDocComment) {
previous = ((PhpDocComment) previous).getPrevPsiSibling();
}
if (!OpenapiTypesUtil.isStatementImpl(previous)) {
return;
}
final PhpPsiElement previousExpression = ((Statement) previous).getFirstPsiChild();
if (!OpenapiTypesUtil.isAssignment(previousExpression)) {
return;
}
/* analyze if containers are matching */
final PsiElement ownContainer = getContainer(assignmentExpression);
if (ownContainer != null) {
final PsiElement previousContainer = getContainer((AssignmentExpression) previousExpression);
if (previousContainer != null && OpenapiEquivalenceUtil.areEqual(ownContainer, previousContainer)) {
final String message = messagePattern.replace("%a%", ownContainer.getText());
holder.registerProblem(assignmentExpression, MessagesPresentationUtil.prefixWithEa(message));
}
}
}
@Nullable
private PsiElement getContainer(@NotNull AssignmentExpression assignmentExpression) {
/* value needs to be a array access expression */
final PsiElement accessCandidate = assignmentExpression.getValue();
if (!(accessCandidate instanceof ArrayAccessExpression)) {
return null;
}
/* ensure we have finished structure */
final ArrayAccessExpression value = (ArrayAccessExpression) accessCandidate;
final PsiElement container = value.getValue();
final ArrayIndex index = value.getIndex();
if (null == container || null == index || null == index.getValue()) {
return null;
}
/* we'll check only numeric arrays */
if (OpenapiTypesUtil.isNumber(index.getValue())) {
return container;
}
return null;
}
};
}
use of com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment in project phpinspectionsea by kalessil.
the class ReturnTypeCanBeDeclaredInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
/* TODO: support functions - see https://github.com/kalessil/phpinspectionsea/pull/320 */
@Override
public void visitPhpMethod(@NotNull Method method) {
final PhpLanguageLevel php = PhpLanguageLevel.get(holder.getProject());
if (php.atLeast(PhpLanguageLevel.PHP700) && !magicMethods.contains(method.getName())) {
final boolean isTarget = OpenapiElementsUtil.getReturnType(method) == null;
if (isTarget) {
final PsiElement methodNameNode = NamedElementUtil.getNameIdentifier(method);
if (methodNameNode != null) {
final boolean supportNullableTypes = php.atLeast(PhpLanguageLevel.PHP710);
if (method.isAbstract()) {
this.handleAbstractMethod(method, methodNameNode, supportNullableTypes);
} else {
this.handleMethod(method, methodNameNode, supportNullableTypes);
}
}
}
}
}
private void handleAbstractMethod(@NotNull Method method, @NotNull PsiElement target, boolean supportNullableTypes) {
final PhpDocComment docBlock = method.getDocComment();
if (docBlock != null && docBlock.getReturnTag() != null) {
this.handleMethod(method, target, supportNullableTypes);
}
}
private void handleMethod(@NotNull Method method, @NotNull PsiElement target, boolean supportNullableTypes) {
/* suggest nothing when the type is only partially resolved */
final PhpType resolvedReturnType = OpenapiResolveUtil.resolveType(method, holder.getProject());
if (resolvedReturnType == null) {
return;
} else if (resolvedReturnType.hasUnknown()) {
/* adding class interface leading to promise-type for interface method */
boolean isInfluencedByInterface = resolvedReturnType.size() == 2 && resolvedReturnType.filterUnknown().size() == 1;
if (!isInfluencedByInterface) {
return;
}
}
/* ignore DocBlock, resolve and normalize types instead (DocBlock is involved, but nevertheless) */
final Set<String> normalizedTypes = resolvedReturnType.filterUnknown().getTypes().stream().map(Types::getType).collect(Collectors.toSet());
this.checkUnrecognizedGenerator(method, normalizedTypes);
this.checkReturnStatements(method, normalizedTypes);
final boolean isVoidAvailable = PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP710);
final int typesCount = normalizedTypes.size();
/* case 1: offer using void */
if (supportNullableTypes && typesCount == 0 && isVoidAvailable) {
final PsiElement firstReturn = PsiTreeUtil.findChildOfType(method, PhpReturn.class);
if (firstReturn == null || ExpressionSemanticUtil.getScope(firstReturn) != method) {
final LocalQuickFix fixer = this.isMethodOverridden(method) ? null : new DeclareReturnTypeFix(Types.strVoid);
final String message = messagePattern.replace("%t%", Types.strVoid).replace("%n%", fixer == null ? " (please use change signature intention to fix this)" : "");
holder.registerProblem(target, MessagesPresentationUtil.prefixWithEa(message), fixer);
}
}
/* case 2: offer using type */
if (1 == typesCount) {
final String singleType = normalizedTypes.iterator().next();
final String suggestedType = isVoidAvailable && voidTypes.contains(singleType) ? Types.strVoid : this.compactType(singleType, method);
final boolean isLegitBasic = singleType.startsWith("\\") || returnTypes.contains(singleType) || suggestedType.equals("self") || suggestedType.equals("static");
final boolean isLegitVoid = !isLegitBasic && supportNullableTypes && suggestedType.equals(Types.strVoid);
if (isLegitBasic || isLegitVoid) {
/* false-positive: '@return static' which is gets resolved into current class since 2019.2 */
final PhpDocComment docBlock = method.getDocComment();
final PhpDocReturnTag tag = docBlock == null ? null : docBlock.getReturnTag();
final boolean isStatic = tag != null && Arrays.stream(tag.getChildren()).map(PsiElement::getText).filter(t -> !t.isEmpty()).allMatch(t -> t.equals("static"));
final boolean isLegitStatic = isStatic && PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP800);
if (!isStatic || isLegitStatic) {
final LocalQuickFix fixer = this.isMethodOverridden(method) ? null : new DeclareReturnTypeFix(isLegitStatic ? "static" : suggestedType);
final String message = messagePattern.replace("%t%", isLegitStatic ? "static" : suggestedType).replace("%n%", fixer == null ? " (please use change signature intention to fix this)" : "");
holder.registerProblem(target, MessagesPresentationUtil.prefixWithEa(message), fixer);
}
}
}
/* case 3: offer using nullable type */
if (supportNullableTypes && 2 == typesCount && normalizedTypes.contains(Types.strNull)) {
normalizedTypes.remove(Types.strNull);
final String nullableType = normalizedTypes.iterator().next();
final String suggestedType = isVoidAvailable && voidTypes.contains(nullableType) ? Types.strVoid : compactType(nullableType, method);
final boolean isLegitNullable = nullableType.startsWith("\\") || returnTypes.contains(nullableType) || suggestedType.equals("self");
final boolean isLegitVoid = !isLegitNullable && suggestedType.equals(Types.strVoid);
if (isLegitNullable || isLegitVoid) {
final String typeHint = isLegitVoid ? suggestedType : '?' + suggestedType;
final LocalQuickFix fixer = this.isMethodOverridden(method) ? null : new DeclareReturnTypeFix(typeHint);
final String message = messagePattern.replace("%t%", typeHint).replace("%n%", fixer == null ? " (please use change signature intention to fix this)" : "");
holder.registerProblem(target, MessagesPresentationUtil.prefixWithEa(message), fixer);
}
}
}
/* use change signature intention promoter */
private boolean isMethodOverridden(@NotNull Method method) {
boolean result = false;
final PhpClass clazz = method.getContainingClass();
if (clazz != null && !clazz.isFinal() && !method.isFinal() && !method.getAccess().isPrivate()) {
final String methodName = method.getName();
result = InterfacesExtractUtil.getCrawlInheritanceTree(clazz, true).stream().anyMatch(c -> c != clazz && c.findOwnMethodByName(methodName) != null) || OpenapiResolveUtil.resolveChildClasses(clazz.getFQN(), PhpIndex.getInstance(holder.getProject())).stream().anyMatch(c -> c.findOwnMethodByName(methodName) != null);
}
return result;
}
@NotNull
private String compactType(@NotNull String type, @NotNull Method method) {
String result = null;
if (type.startsWith("\\") || type.equals("static")) {
/* Strategy 1: respect `@return self` */
if (LOOKUP_PHPDOC_RETURN_DECLARATIONS) {
final PhpDocComment phpDoc = method.getDocComment();
final PhpDocReturnTag phpReturn = phpDoc == null ? null : phpDoc.getReturnTag();
if (phpReturn != null) {
final boolean hasSelfReference = PsiTreeUtil.findChildrenOfType(phpReturn, PhpDocType.class).stream().anyMatch(t -> {
final String text = t.getText();
return text.equals("self") || text.equals("$this");
});
if (hasSelfReference) {
result = "self";
}
}
}
/* be sure to send back static for avoiding false-positives */
result = (result == null && type.equals("static")) ? type : result;
/* Strategy 2: scan imports */
if (result == null) {
PsiElement groupCandidate = method.getContainingClass();
groups: while (groupCandidate != null && !(groupCandidate instanceof PsiFile)) {
if (groupCandidate instanceof GroupStatement) {
final List<PhpUse> imports = new ArrayList<>();
/* scan for imports in current group statement */
for (final PsiElement child : groupCandidate.getChildren()) {
if (child instanceof PhpUseList) {
Collections.addAll(imports, ((PhpUseList) child).getDeclarations());
}
}
/* iterate imports and search for targets */
for (final PhpUse imported : imports) {
final PhpReference useReference = imported.getTargetReference();
if (useReference instanceof ClassReference && type.equals(useReference.getFQN())) {
final String useAlias = imported.getAliasName();
result = useAlias == null ? useReference.getName() : useAlias;
imports.clear();
break groups;
}
}
imports.clear();
}
groupCandidate = groupCandidate.getParent();
}
}
/* Strategy 3: relative QN for classes in sub-namespace */
if (result == null || result.isEmpty()) {
final PhpClass clazz = method.getContainingClass();
final String nameSpace = null == clazz ? null : clazz.getNamespaceName();
if (nameSpace != null && nameSpace.length() > 1 && type.startsWith(nameSpace)) {
result = type.replace(nameSpace, "");
}
}
}
return result == null ? type : result;
}
private void checkUnrecognizedGenerator(@NotNull Method method, @NotNull Set<String> types) {
if (!types.contains("\\Generator")) {
final PhpYield yield = PsiTreeUtil.findChildOfType(method, PhpYield.class);
if (yield != null && ExpressionSemanticUtil.getScope(yield) == method) {
types.add("\\Generator");
if (PsiTreeUtil.findChildOfType(method, PhpReturn.class) == null) {
types.remove(Types.strNull);
}
}
}
}
private void checkReturnStatements(@NotNull Method method, @NotNull Set<String> types) {
if (!types.isEmpty() && !method.isAbstract()) {
/* non-implicit null return: omitted last return statement */
if (!types.contains(Types.strNull) && !types.contains(Types.strVoid)) {
final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(method);
final PsiElement last = body == null ? null : ExpressionSemanticUtil.getLastStatement(body);
if (!(last instanceof PhpReturn) && !OpenapiTypesUtil.isThrowExpression(last)) {
types.add(Types.strNull);
}
}
/* buggy parameter type resolving: no type, but null as default value */
if (types.size() == 1 && types.contains(Types.strNull)) {
final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(method);
if (body != null) {
final PhpReturn expression = PsiTreeUtil.findChildOfType(body, PhpReturn.class);
if (expression != null) {
final PsiElement value = ExpressionSemanticUtil.getReturnValue(expression);
if (value != null && !PhpLanguageUtil.isNull(value)) {
types.remove(Types.strNull);
}
}
}
}
}
}
};
}
use of com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment in project phpinspectionsea by kalessil.
the class QueryUsageStrategy method apply.
public static void apply(@NotNull MethodReference reference, @NotNull ProblemsHolder holder) {
final String methodName = reference.getName();
if (methodName == null || !methodName.equals("execute")) {
return;
}
final PsiElement[] arguments = reference.getParameters();
if (arguments.length > 0) {
return;
}
/* inspect preceding and succeeding statement */
final PsiElement parent = reference.getParent();
PsiElement predecessor = null;
if (OpenapiTypesUtil.isStatementImpl(parent)) {
predecessor = ((Statement) parent).getPrevPsiSibling();
while (predecessor instanceof PhpDocComment) {
predecessor = ((PhpDocComment) predecessor).getPrevPsiSibling();
}
}
if (null != predecessor && predecessor.getFirstChild() instanceof AssignmentExpression) {
/* predecessor needs to be an assignment */
final AssignmentExpression assignment = (AssignmentExpression) predecessor.getFirstChild();
if (!(assignment.getValue() instanceof MethodReference)) {
return;
}
final MethodReference precedingReference = (MethodReference) assignment.getValue();
if (MethodIdentityUtil.isReferencingMethod(precedingReference, "\\PDO", "prepare")) {
final PsiElement variableAssigned = assignment.getVariable();
final PsiElement variableUsed = reference.getClassReference();
if (variableAssigned != null && variableUsed != null && OpenapiEquivalenceUtil.areEqual(variableAssigned, variableUsed)) {
holder.registerProblem(reference, MessagesPresentationUtil.prefixWithEa(message), new UseQueryFix(holder.getProject(), precedingReference));
}
}
}
}
use of com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment in project yii2support by nvlad.
the class PropertiesInspection method buildVisitor.
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder problemsHolder, boolean isOnTheFly) {
return new PhpElementVisitor() {
@Override
public void visitElement(PsiElement element) {
if (element instanceof PhpDocComment && DatabaseUtils.HasConnections(element.getProject())) {
PhpDocComment docComment = (PhpDocComment) element;
PhpIndex index = PhpIndex.getInstance(element.getProject());
PhpClass phpClass = DatabaseUtils.getClassByClassPhpDoc(docComment);
if (phpClass != null && ClassUtils.isClassInheritsOrEqual(phpClass, ClassUtils.getClass(index, "\\yii\\db\\BaseActiveRecord"))) {
Collection<Field> fields = phpClass.getFields();
String table = DatabaseUtils.getTableByActiveRecordClass(phpClass);
ArrayList<VirtualProperty> notDeclaredColumns = DatabaseUtils.getNotDeclaredColumns(table, fields, element.getProject());
if (notDeclaredColumns.size() > 0) {
MissingPropertiesQuickFix qFix = new MissingPropertiesQuickFix(notDeclaredColumns, docComment);
String str1 = notDeclaredColumns.size() > 1 ? "properties" : "property";
problemsHolder.registerProblem(docComment, "Class " + phpClass.getFQN() + " is missing " + notDeclaredColumns.size() + " " + str1 + " that corresponds to database columns", ProblemHighlightType.WEAK_WARNING, qFix);
}
ArrayList<PhpDocPropertyTag> unusedProperties = DatabaseUtils.getUnusedProperties(table, docComment.getPropertyTags(), phpClass);
if (unusedProperties.size() > 0) {
for (PhpDocPropertyTag tag : unusedProperties) {
problemsHolder.registerProblem(tag, "Property is unused in class " + phpClass.getFQN(), ProblemHighlightType.LIKE_UNUSED_SYMBOL);
}
}
}
}
super.visitElement(element);
}
};
}
Aggregations