use of com.jetbrains.php.lang.psi.PhpFile 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.psi.PhpFile in project phpinspectionsea by kalessil.
the class UnqualifiedReferenceInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpFunctionCall(@NotNull FunctionReference reference) {
final String functionName = reference.getName();
if (functionName != null && !functionName.isEmpty()) {
/* ensure php version is at least PHP 7.0; makes sense only with PHP7+ opcode */
if (PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP700)) {
if (REPORT_ALL_FUNCTIONS || advancedOpcode.contains(functionName)) {
this.analyzeReference(reference, functionName);
}
if (callbacksPositions.containsKey(functionName)) {
this.analyzeCallback(reference, functionName);
}
}
}
}
@Override
public void visitPhpConstantReference(@NotNull ConstantReference reference) {
final String constantName = reference.getName();
if (constantName != null && !constantName.isEmpty() && REPORT_CONSTANTS) {
/* ensure php version is at least PHP 7.0; makes sense only with PHP7+ opcode */
if (PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP700)) {
this.analyzeReference(reference, constantName);
}
}
}
private void analyzeCallback(@NotNull FunctionReference reference, @NotNull String functionName) {
final PsiElement[] arguments = reference.getParameters();
if (arguments.length >= 2) {
final Integer callbackPosition = callbacksPositions.get(functionName);
if (arguments[callbackPosition] instanceof StringLiteralExpression) {
final StringLiteralExpression callback = (StringLiteralExpression) arguments[callbackPosition];
if (callback.getFirstPsiChild() == null) {
final String function = callback.getContents();
final boolean isCandidate = !function.startsWith("\\") && !function.contains("::");
if (isCandidate && (REPORT_ALL_FUNCTIONS || advancedOpcode.contains(function))) {
final PhpIndex index = PhpIndex.getInstance(holder.getProject());
if (!index.getFunctionsByFQN('\\' + functionName).isEmpty()) {
holder.registerProblem(callback, String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), function), new TheLocalFix());
}
}
}
}
}
}
private void analyzeReference(@NotNull PhpReference reference, @NotNull String referenceName) {
/* some constants prefixing is making no sense IMO */
if (reference instanceof ConstantReference && falsePositives.contains(referenceName)) {
return;
}
/* NS specification is identified differently for { define } and { call, constant } */
final PsiElement nsCandidate = reference.getFirstChild();
if (nsCandidate instanceof PhpNamespaceReference || OpenapiTypesUtil.is(nsCandidate, PhpTokenTypes.NAMESPACE_RESOLUTION)) {
return;
}
final PhpNamespace ns = this.findNamespace(reference);
if (ns == null) {
return;
}
/* resolve the constant/function, report if it's from the root NS */
final PsiElement subject = OpenapiResolveUtil.resolveReference(reference);
final boolean isFunction = subject instanceof Function;
if (isFunction || subject instanceof Constant) {
/* false-positives: non-root NS function/constant referenced */
if (((PhpNamedElement) subject).getFQN().equals('\\' + referenceName)) {
final GroupStatement body = ns.getStatements();
if (body != null) {
final List<PhpUse> imports = new ArrayList<>();
for (final PsiElement child : body.getChildren()) {
if (child instanceof PhpUseList) {
Collections.addAll(imports, ((PhpUseList) child).getDeclarations());
}
}
/* false-positive: function/constant are imported already */
boolean isImported = false;
for (final PhpUse use : imports) {
final PsiElement candidate = use.getFirstPsiChild();
String importedSymbol = null;
if (candidate instanceof FunctionReference) {
importedSymbol = ((FunctionReference) candidate).getName();
} else if (candidate instanceof ConstantReference) {
importedSymbol = ((ConstantReference) candidate).getName();
}
if (isImported = referenceName.equals(importedSymbol)) {
break;
}
}
imports.clear();
if (!isImported) {
holder.registerProblem(reference, String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), referenceName + (isFunction ? "(...)" : "")), new TheLocalFix());
}
}
}
}
}
@Nullable
private PhpNamespace findNamespace(@NotNull PhpReference reference) {
final PsiFile file = reference.getContainingFile();
if (file.getFileType() == PhpFileType.INSTANCE) {
final List<PhpNamespace> namespaces = new ArrayList<>();
((PhpFile) file).getTopLevelDefs().values().stream().filter(definition -> definition instanceof PhpNamespace).forEach(definition -> namespaces.add((PhpNamespace) definition));
if (namespaces.isEmpty()) {
return null;
} else if (namespaces.size() == 1) {
return namespaces.get(0);
}
namespaces.clear();
}
return (PhpNamespace) PsiTreeUtil.findFirstParent(reference, PARENT_NAMESPACE);
}
};
}
use of com.jetbrains.php.lang.psi.PhpFile in project phpinspectionsea by kalessil.
the class AutoloadingIssuesInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpFile(@NotNull PhpFile file) {
final String fileName = file.getName();
if (fileName.endsWith(".php") && !ignoredFiles.contains(fileName) && !laravelMigration.matcher(fileName).matches()) {
final List<PhpClass> classes = new ArrayList<>();
file.getTopLevelDefs().values().stream().filter(definition -> definition instanceof PhpClass).forEach(definition -> classes.add((PhpClass) definition));
if (classes.size() == 1) {
final PhpClass clazz = classes.get(0);
final String className = clazz.getName();
/* PSR-0 classloading (Package_Subpackage_Class) naming */
String extractedClassName = className;
if (clazz.getFQN().lastIndexOf('\\') == 0 && extractedClassName.indexOf('_') != -1) {
extractedClassName = extractedClassName.substring(1 + extractedClassName.lastIndexOf('_'));
}
/* check the file name as per extraction compliant with PSR-0/PSR-4 standards */
final String expectedClassName = fileName.substring(0, fileName.indexOf('.'));
if (this.isBreakingPsrStandard(className, expectedClassName, extractedClassName) && !this.isWordpressStandard(className, fileName)) {
final PsiElement classNameNode = NamedElementUtil.getNameIdentifier(clazz);
if (classNameNode != null) {
holder.registerProblem(classNameNode, MessagesPresentationUtil.prefixWithEa(message));
}
}
}
classes.clear();
}
}
private boolean isBreakingPsrStandard(@NotNull String className, @NotNull String expectedClassName, @NotNull String extractedClassName) {
return !expectedClassName.equals(extractedClassName) && !expectedClassName.equals(className);
}
private boolean isWordpressStandard(@NotNull String className, @NotNull String fileName) {
final String wordpressFileName = String.format("class-%s.php", className.toLowerCase().replaceAll("_", "-"));
return fileName.endsWith(wordpressFileName);
}
};
}
use of com.jetbrains.php.lang.psi.PhpFile in project phpinspectionsea by kalessil.
the class SuspiciousLoopInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpForeach(@NotNull ForeachStatement statement) {
if (VERIFY_VARIABLES_OVERRIDE) {
this.inspectVariables(statement);
}
}
@Override
public void visitPhpFor(@NotNull For statement) {
if (statement.getConditionalExpressions().length > 1) {
holder.registerProblem(statement.getFirstChild(), MessagesPresentationUtil.prefixWithEa(messageMultipleConditions));
}
if (VERIFY_VARIABLES_OVERRIDE) {
this.inspectVariables(statement);
}
}
private void inspectVariables(@NotNull PhpPsiElement loop) {
final Set<String> loopVariables = this.getLoopVariables(loop);
final Function function = ExpressionSemanticUtil.getScope(loop);
if (null != function) {
final HashSet<String> parameters = new HashSet<>();
for (final Parameter param : function.getParameters()) {
parameters.add(param.getName());
}
loopVariables.forEach(variable -> {
if (parameters.contains(variable)) {
final String message = patternOverridesParameter.replace("%v%", variable).replace("%t%", function instanceof Method ? "method" : "function");
holder.registerProblem(loop.getFirstChild(), MessagesPresentationUtil.prefixWithEa(message));
}
});
parameters.clear();
}
/* scan parents until reached file/callable */
PsiElement parent = loop.getParent();
while (null != parent && !(parent instanceof Function) && !(parent instanceof PhpFile)) {
/* inspect parent loops for conflicted variables */
if (parent instanceof For || parent instanceof ForeachStatement) {
final Set<String> parentVariables = this.getLoopVariables((PhpPsiElement) parent);
loopVariables.forEach(variable -> {
if (parentVariables.contains(variable)) {
holder.registerProblem(loop.getFirstChild(), MessagesPresentationUtil.prefixWithEa(patternOverridesLoopVars.replace("%v%", variable)));
}
});
parentVariables.clear();
}
parent = parent.getParent();
}
loopVariables.clear();
}
@NotNull
private Set<String> getLoopVariables(@NotNull PhpPsiElement loop) {
final Set<String> variables = new HashSet<>();
if (loop instanceof For) {
/* get variables from assignments */
Stream.of(((For) loop).getInitialExpressions()).forEach(init -> {
if (init instanceof AssignmentExpression) {
final PhpPsiElement variable = ((AssignmentExpression) init).getVariable();
if (variable instanceof Variable) {
final String variableName = variable.getName();
if (variableName != null) {
variables.add(variableName);
}
}
}
});
} else if (loop instanceof ForeachStatement) {
((ForeachStatement) loop).getVariables().forEach(variable -> variables.add(variable.getName()));
}
return variables;
}
};
}
use of com.jetbrains.php.lang.psi.PhpFile 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;
}
};
}
Aggregations