Search in sources :

Example 1 with PhpFile

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();
            }
        }
    };
}
Also used : PhpFile(com.jetbrains.php.lang.psi.PhpFile) NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PhpDocComment(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment) PsiElement(com.intellij.psi.PsiElement) PsiWhiteSpace(com.intellij.psi.PsiWhiteSpace) NotNull(org.jetbrains.annotations.NotNull)

Example 2 with PhpFile

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);
        }
    };
}
Also used : java.util(java.util) com.jetbrains.php.lang.psi.elements(com.jetbrains.php.lang.psi.elements) PhpTokenTypes(com.jetbrains.php.lang.lexer.PhpTokenTypes) PhpIndex(com.jetbrains.php.PhpIndex) PsiTreeUtil(com.intellij.psi.util.PsiTreeUtil) OpenapiTypesUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiTypesUtil) ProblemDescriptor(com.intellij.codeInspection.ProblemDescriptor) MessagesPresentationUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil) PsiElement(com.intellij.psi.PsiElement) Project(com.intellij.openapi.project.Project) PsiFile(com.intellij.psi.PsiFile) LocalQuickFix(com.intellij.codeInspection.LocalQuickFix) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) PhpFileType(com.jetbrains.php.lang.PhpFileType) PhpPsiElementFactory(com.jetbrains.php.lang.psi.PhpPsiElementFactory) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) PhpLanguageLevel(com.kalessil.phpStorm.phpInspectionsEA.openApi.PhpLanguageLevel) OptionsComponent(com.kalessil.phpStorm.phpInspectionsEA.options.OptionsComponent) Nullable(org.jetbrains.annotations.Nullable) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PhpFile(com.jetbrains.php.lang.psi.PhpFile) OpenapiResolveUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiResolveUtil) NotNull(org.jetbrains.annotations.NotNull) Condition(com.intellij.openapi.util.Condition) javax.swing(javax.swing) PhpFile(com.jetbrains.php.lang.psi.PhpFile) NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PsiFile(com.intellij.psi.PsiFile) PsiElement(com.intellij.psi.PsiElement) PhpIndex(com.jetbrains.php.PhpIndex) NotNull(org.jetbrains.annotations.NotNull)

Example 3 with PhpFile

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);
        }
    };
}
Also used : PhpClass(com.jetbrains.php.lang.psi.elements.PhpClass) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) Collection(java.util.Collection) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) List(java.util.List) PhpFile(com.jetbrains.php.lang.psi.PhpFile) MessagesPresentationUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil) PsiElement(com.intellij.psi.PsiElement) NamedElementUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.NamedElementUtil) Pattern(java.util.regex.Pattern) NotNull(org.jetbrains.annotations.NotNull) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PhpFile(com.jetbrains.php.lang.psi.PhpFile) PhpClass(com.jetbrains.php.lang.psi.elements.PhpClass) ArrayList(java.util.ArrayList) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 4 with PhpFile

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;
        }
    };
}
Also used : BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) ExpressionSemanticUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.ExpressionSemanticUtil) com.jetbrains.php.lang.psi.elements(com.jetbrains.php.lang.psi.elements) Set(java.util.Set) OptionsComponent(com.kalessil.phpStorm.phpInspectionsEA.options.OptionsComponent) HashSet(java.util.HashSet) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) Stream(java.util.stream.Stream) PhpFile(com.jetbrains.php.lang.psi.PhpFile) MessagesPresentationUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) javax.swing(javax.swing) PhpFile(com.jetbrains.php.lang.psi.PhpFile) NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PsiElement(com.intellij.psi.PsiElement) HashSet(java.util.HashSet) NotNull(org.jetbrains.annotations.NotNull)

Example 5 with PhpFile

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;
        }
    };
}
Also used : PhpFile(com.jetbrains.php.lang.psi.PhpFile) NotNull(org.jetbrains.annotations.NotNull) IElementType(com.intellij.psi.tree.IElementType) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PhpDocComment(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Aggregations

PhpFile (com.jetbrains.php.lang.psi.PhpFile)11 PsiElement (com.intellij.psi.PsiElement)9 NotNull (org.jetbrains.annotations.NotNull)9 BasePhpElementVisitor (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor)8 ProblemsHolder (com.intellij.codeInspection.ProblemsHolder)5 PsiElementVisitor (com.intellij.psi.PsiElementVisitor)5 BasePhpInspection (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection)5 MessagesPresentationUtil (com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil)5 Project (com.intellij.openapi.project.Project)4 com.jetbrains.php.lang.psi.elements (com.jetbrains.php.lang.psi.elements)4 PsiTreeUtil (com.intellij.psi.util.PsiTreeUtil)3 HashSet (java.util.HashSet)3 LocalQuickFix (com.intellij.codeInspection.LocalQuickFix)2 ProblemDescriptor (com.intellij.codeInspection.ProblemDescriptor)2 PsiFile (com.intellij.psi.PsiFile)2 PhpIndex (com.jetbrains.php.PhpIndex)2 PhpDocComment (com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment)2 PhpTokenTypes (com.jetbrains.php.lang.lexer.PhpTokenTypes)2 PhpPsiElementFactory (com.jetbrains.php.lang.psi.PhpPsiElementFactory)2 OptionsComponent (com.kalessil.phpStorm.phpInspectionsEA.options.OptionsComponent)2