use of com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor in project phpinspectionsea by kalessil.
the class InfinityLoopInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpMethod(@NotNull Method method) {
/* case: buggy getter (field/method names identical => accidental recursion) */
if (!method.isAbstract()) {
final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(method);
if (body != null && ExpressionSemanticUtil.countExpressionsInGroup(body) == 1) {
/* get the recursion candidate */
PsiElement last = ExpressionSemanticUtil.getLastStatement(body);
if (last instanceof PhpReturn) {
last = ((PhpReturn) last).getArgument();
} else if (OpenapiTypesUtil.isStatementImpl(last)) {
last = ((Statement) last).getFirstPsiChild();
}
/* now check for harmful recursion */
if (last instanceof MethodReference) {
final MethodReference value = (MethodReference) last;
final String returnedMethodName = value.getName();
if (returnedMethodName != null && returnedMethodName.equals(method.getName())) {
final PhpExpression subject = value.getClassReference();
final String reference = subject == null ? null : subject.getText();
if (reference != null && references.contains(reference)) {
holder.registerProblem(value, MessagesPresentationUtil.prefixWithEa(message));
}
}
}
}
}
}
};
}
use of com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor in project phpinspectionsea by kalessil.
the class OneTimeUseVariablesInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
void checkOneTimeUse(@NotNull PhpPsiElement construct, @NotNull Variable argument) {
final String variableName = argument.getName();
final PsiElement previous = construct.getPrevPsiSibling();
/* verify preceding expression (assignment needed) */
if (null != previous && OpenapiTypesUtil.isAssignment(previous.getFirstChild())) {
final AssignmentExpression assign = (AssignmentExpression) previous.getFirstChild();
/* ensure variables are the same */
final PhpPsiElement assignVariable = assign.getVariable();
final PsiElement assignValue = ExpressionSemanticUtil.getExpressionTroughParenthesis(assign.getValue());
if (null != assignValue && assignVariable instanceof Variable) {
final String assignVariableName = assignVariable.getName();
if (assignVariableName == null || !assignVariableName.equals(variableName)) {
return;
}
/* check if variable as a function/use(...) parameter by reference */
final Function function = ExpressionSemanticUtil.getScope(construct);
if (null != function) {
for (final Parameter param : function.getParameters()) {
if (param.isPassByRef() && param.getName().equals(variableName)) {
return;
}
}
final List<Variable> useList = ExpressionSemanticUtil.getUseListVariables(function);
if (useList != null && !useList.isEmpty()) {
for (final Variable param : useList) {
if (param.getName().equals(variableName)) {
/* detect parameters by reference in use clause */
PsiElement ampersandCandidate = param.getPrevSibling();
if (ampersandCandidate instanceof PsiWhiteSpace) {
ampersandCandidate = ampersandCandidate.getPrevSibling();
}
if (OpenapiTypesUtil.is(ampersandCandidate, PhpTokenTypes.opBIT_AND)) {
return;
}
}
}
useList.clear();
}
}
/* too long return/throw statements can be decoupled as a variable */
if (!ALLOW_LONG_STATEMENTS && assign.getText().length() > 80) {
return;
}
/* heavy part, find usage inside function/method to analyze multiple writes */
final PhpScopeHolder parentScope = ExpressionSemanticUtil.getScope(assign);
if (null != parentScope) {
int countWrites = 0;
int countReads = 0;
for (final PhpAccessVariableInstruction oneCase : OpenapiControlFlowUtil.getFollowingVariableAccessInstructions(parentScope.getControlFlow().getEntryPoint(), variableName)) {
final boolean isWrite = oneCase.getAccess().isWrite();
if (isWrite) {
/* false-positives: type specification */
final PsiElement context = oneCase.getAnchor().getParent();
if (OpenapiTypesUtil.isAssignment(context)) {
final boolean typeAnnotated = this.isTypeAnnotated((PhpPsiElement) context.getParent(), variableName);
if (typeAnnotated) {
return;
}
}
}
countWrites += isWrite ? 1 : 0;
countReads += isWrite ? 0 : 1;
if (countWrites > 1 || countReads > 1) {
return;
}
}
}
if (!(assignValue instanceof NewExpression) || PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP540)) {
holder.registerProblem(assignVariable, MessagesPresentationUtil.prefixWithEa(messagePattern.replace("%v%", variableName)), new TheLocalFix(holder.getProject(), assign.getParent(), argument, assignValue));
}
}
}
}
@Override
public void visitPhpReturn(@NotNull PhpReturn returnStatement) {
if (ANALYZE_RETURN_STATEMENTS) {
/* if function returning reference, do not inspect returns */
final Function callable = ExpressionSemanticUtil.getScope(returnStatement);
final PsiElement nameNode = NamedElementUtil.getNameIdentifier(callable);
if (null != callable && null != nameNode) {
/* is defined like returning reference */
PsiElement referenceCandidate = nameNode.getPrevSibling();
if (referenceCandidate instanceof PsiWhiteSpace) {
referenceCandidate = referenceCandidate.getPrevSibling();
}
if (OpenapiTypesUtil.is(referenceCandidate, PhpTokenTypes.opBIT_AND)) {
return;
}
}
/* regular function, check one-time use variables */
final PsiElement argument = ExpressionSemanticUtil.getExpressionTroughParenthesis(returnStatement.getArgument());
if (argument instanceof PhpPsiElement) {
final Variable variable = this.getVariable((PhpPsiElement) argument);
if (null != variable) {
checkOneTimeUse(returnStatement, variable);
}
}
}
}
@Override
public void visitPhpMultiassignmentExpression(@NotNull MultiassignmentExpression multiassignmentExpression) {
if (ANALYZE_ARRAY_DESTRUCTURING) {
final PsiElement firstChild = multiassignmentExpression.getFirstChild();
final IElementType nodeType = null == firstChild ? null : firstChild.getNode().getElementType();
if (null != nodeType && (PhpTokenTypes.kwLIST == nodeType || PhpTokenTypes.chLBRACKET == nodeType)) {
final Variable variable = this.getVariable(multiassignmentExpression.getValue());
final PsiElement parent = multiassignmentExpression.getParent();
if (null != variable && OpenapiTypesUtil.isStatementImpl(parent)) {
checkOneTimeUse((PhpPsiElement) parent, variable);
}
}
}
}
/* TODO: once got bored, add foreach source pattern here =) I'm naive but nevertheless ^_^ */
@Override
public void visitPhpThrowExpression(@NotNull PhpThrowExpression expression) {
if (ANALYZE_THROW_STATEMENTS) {
final PsiElement argument = ExpressionSemanticUtil.getExpressionTroughParenthesis(expression.getArgument());
if (argument instanceof PhpPsiElement) {
final Variable variable = this.getVariable((PhpPsiElement) argument);
if (null != variable) {
checkOneTimeUse(expression.getExpression(), variable);
}
}
}
}
@Nullable
private Variable getVariable(@Nullable PhpPsiElement expression) {
if (expression != null) {
if (expression instanceof Variable) {
return (Variable) expression;
} else if (expression instanceof MemberReference) {
final MemberReference reference = (MemberReference) expression;
final PsiElement candidate = reference.getFirstChild();
if (candidate instanceof Variable) {
final PsiElement operator = OpenapiPsiSearchUtil.findResolutionOperator(reference);
if (OpenapiTypesUtil.is(operator, PhpTokenTypes.ARROW)) {
return (Variable) candidate;
}
}
} else if (OpenapiTypesUtil.isPhpExpressionImpl(expression)) {
/* instanceof passes child classes as well, what isn't correct */
return getVariable(expression.getFirstPsiChild());
}
}
return null;
}
private boolean isTypeAnnotated(@NotNull PhpPsiElement current, @NotNull String variableName) {
boolean result = false;
final PsiElement phpdocCandidate = current.getPrevPsiSibling();
if (phpdocCandidate instanceof PhpDocComment) {
final PhpDocTag[] hints = ((PhpDocComment) phpdocCandidate).getTagElementsByName("@var");
if (hints.length == 1) {
final PhpDocVariable specifiedVariable = PsiTreeUtil.findChildOfType(hints[0], PhpDocVariable.class);
result = specifiedVariable != null && specifiedVariable.getName().equals(variableName);
}
}
return result;
}
};
}
use of com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor in project phpinspectionsea by kalessil.
the class UselessReturnInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpReturn(@NotNull PhpReturn expression) {
final PhpExpression returnValue = ExpressionSemanticUtil.getReturnValue(expression);
if (returnValue instanceof AssignmentExpression) {
final AssignmentExpression assignment = (AssignmentExpression) returnValue;
final PsiElement assignmentVariable = assignment.getVariable();
final PsiElement assignmentValue = assignment.getValue();
if (assignmentValue != null && assignmentVariable instanceof Variable) {
final Function scope = ExpressionSemanticUtil.getScope(expression);
if (scope != null) {
final Variable variable = (Variable) assignmentVariable;
final boolean isTarget = !this.isArgumentReference(variable, scope) && !this.isBoundReference(variable, scope) && !this.isStaticVariable(variable, scope) && !this.isUsedInFinally(variable, scope);
if (isTarget) {
final String replacement = String.format("return %s;", assignmentValue.getText());
holder.registerProblem(expression, MessagesPresentationUtil.prefixWithEa(messageConfusing), new SimplifyFix(replacement));
}
}
}
}
}
@Override
public void visitPhpMethod(@NotNull Method method) {
if (!method.isAbstract()) {
this.inspectForSenselessReturn(method);
}
}
@Override
public void visitPhpFunction(@NotNull Function function) {
this.inspectForSenselessReturn(function);
}
private void inspectForSenselessReturn(@NotNull Function callable) {
final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(callable);
final PsiElement lastStatement = body == null ? null : ExpressionSemanticUtil.getLastStatement(body);
if (lastStatement instanceof PhpReturn) {
final PhpExpression returnValue = ExpressionSemanticUtil.getReturnValue((PhpReturn) lastStatement);
if (returnValue == null) {
holder.registerProblem(lastStatement, MessagesPresentationUtil.prefixWithEa(messageSenseless), ProblemHighlightType.LIKE_UNUSED_SYMBOL);
}
}
}
private boolean isArgumentReference(@NotNull Variable variable, @NotNull Function function) {
boolean result = false;
final String variableName = variable.getName();
for (final Parameter parameter : function.getParameters()) {
if (parameter.getName().equals(variableName) && parameter.isPassByRef()) {
result = true;
break;
}
}
return result;
}
private boolean isBoundReference(@NotNull Variable variable, @NotNull Function function) {
boolean result = false;
final String variableName = variable.getName();
if (!variableName.isEmpty() && !(result = this.isReferenceBoundTo(variableName, function))) {
final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(function);
if (body != null) {
result = PsiTreeUtil.findChildrenOfType(body, Function.class).stream().anyMatch(l -> this.isReferenceBoundTo(variableName, l));
}
}
return result;
}
private boolean isReferenceBoundTo(@NotNull String variableName, @NotNull Function function) {
boolean result = false;
if (OpenapiTypesUtil.isLambda(function)) {
final List<Variable> used = ExpressionSemanticUtil.getUseListVariables(function);
if (used != null) {
final Optional<Variable> match = used.stream().filter(v -> v.getName().equals(variableName)).findFirst();
if (match.isPresent()) {
final PsiElement previous = match.get().getPrevSibling();
final PsiElement candidate = previous instanceof PsiWhiteSpace ? previous.getPrevSibling() : previous;
result = OpenapiTypesUtil.is(candidate, PhpTokenTypes.opBIT_AND);
}
used.clear();
}
}
return result;
}
private boolean isUsedInFinally(@NotNull Variable variable, @NotNull Function function) {
boolean result = false;
final Try tryScope = PsiTreeUtil.getParentOfType(variable, Try.class, false, Function.class);
if (tryScope != null) {
final Finally finallyScope = tryScope.getFinallyBlock();
final GroupStatement body = finallyScope == null ? null : ExpressionSemanticUtil.getGroupStatement(finallyScope);
if (body != null) {
final String variableName = variable.getName();
result = PsiTreeUtil.findChildrenOfType(body, Variable.class).stream().anyMatch(v -> variableName.equals(v.getName()));
}
}
return result;
}
private boolean isStaticVariable(@NotNull Variable variable, @NotNull Function function) {
boolean result = false;
final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(function);
if (body != null) {
final String variableName = variable.getName();
for (final PhpStaticStatement candidate : PsiTreeUtil.findChildrenOfType(body, PhpStaticStatement.class)) {
result = candidate.getDeclarations().stream().anyMatch(declaration -> {
final PhpPsiElement declared = declaration.getVariable();
return declared instanceof Variable && variableName.equals(declared.getName());
});
if (result) {
break;
}
}
}
return result;
}
};
}
use of com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor in project phpinspectionsea by kalessil.
the class SenselessTernaryOperatorInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpTernaryExpression(@NotNull TernaryExpression expression) {
final PsiElement condition = ExpressionSemanticUtil.getExpressionTroughParenthesis(expression.getCondition());
if (condition instanceof BinaryExpression) {
final BinaryExpression binary = (BinaryExpression) condition;
final IElementType operationType = binary.getOperationType();
if (operationType == PhpTokenTypes.opIDENTICAL || operationType == PhpTokenTypes.opNOT_IDENTICAL) {
final boolean isInverted = operationType == PhpTokenTypes.opNOT_IDENTICAL;
final PsiElement trueVariant = isInverted ? expression.getFalseVariant() : expression.getTrueVariant();
final PsiElement falseVariant = isInverted ? expression.getTrueVariant() : expression.getFalseVariant();
if (trueVariant != null && falseVariant != null) {
final PsiElement value = binary.getRightOperand();
final PsiElement subject = binary.getLeftOperand();
if (value != null && subject != null) {
final boolean isLeftPartReturned = OpenapiEquivalenceUtil.areEqual(value, trueVariant) || OpenapiEquivalenceUtil.areEqual(value, falseVariant);
if (isLeftPartReturned) {
final boolean isRightPartReturned = OpenapiEquivalenceUtil.areEqual(subject, falseVariant) || OpenapiEquivalenceUtil.areEqual(subject, trueVariant);
if (isRightPartReturned) {
final String replacement = falseVariant.getText();
holder.registerProblem(expression, String.format(MessagesPresentationUtil.prefixWithEa(patternUseOperands), replacement), new SimplifyFix(replacement));
}
}
}
}
}
}
}
};
}
use of com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor in project phpinspectionsea by kalessil.
the class NestedTernaryOperatorInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpTernaryExpression(@NotNull TernaryExpression expression) {
final PsiElement condition = ExpressionSemanticUtil.getExpressionTroughParenthesis(expression.getCondition());
if (condition instanceof TernaryExpression) {
holder.registerProblem(condition, MessagesPresentationUtil.prefixWithEa(messageNested));
}
final PsiElement trueVariant = ExpressionSemanticUtil.getExpressionTroughParenthesis(expression.getTrueVariant());
if (trueVariant instanceof TernaryExpression) {
holder.registerProblem(trueVariant, MessagesPresentationUtil.prefixWithEa(messageNested));
}
final PsiElement falseVariant = expression.getFalseVariant();
final PsiElement unboxedFalseVariant = ExpressionSemanticUtil.getExpressionTroughParenthesis(falseVariant);
if (unboxedFalseVariant instanceof TernaryExpression) {
final boolean allow = falseVariant instanceof TernaryExpression && expression.isShort() && ((TernaryExpression) falseVariant).isShort();
if (!allow) {
holder.registerProblem(unboxedFalseVariant, MessagesPresentationUtil.prefixWithEa(messageNested));
}
}
}
};
}
Aggregations