Search in sources :

Example 1 with PhpScopeHolder

use of com.jetbrains.php.codeInsight.PhpScopeHolder in project phpinspectionsea by kalessil.

the class ExplodeMissUseInspector method buildVisitor.

@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
    return new BasePhpElementVisitor() {

        @Override
        public void visitPhpFunctionCall(@NotNull FunctionReference reference) {
            /* general structure expectations */
            final String functionName = reference.getName();
            if (functionName == null || !semanticMapping.containsKey(functionName)) {
                return;
            }
            final PsiElement[] arguments = reference.getParameters();
            if (arguments.length != 1) {
                return;
            }
            /* discover possible values */
            final Set<PsiElement> values = PossibleValuesDiscoveryUtil.discover(arguments[0]);
            /* do not analyze invariants */
            if (1 == values.size()) {
                final PsiElement value = values.iterator().next();
                values.clear();
                if (OpenapiTypesUtil.isFunctionReference(value)) {
                    /* inner call must be explode() */
                    final FunctionReference innerCall = (FunctionReference) value;
                    final String innerFunctionName = innerCall.getName();
                    if (innerFunctionName == null || !innerFunctionName.equals("explode")) {
                        return;
                    }
                    final PsiElement[] innerArguments = innerCall.getParameters();
                    if (innerArguments.length != 2) {
                        return;
                    }
                    /* if the parameter is a variable, ensure it used only 2 times (write, read) */
                    if (arguments[0] instanceof Variable) {
                        final PhpScopeHolder parentScope = ExpressionSemanticUtil.getScope(reference);
                        if (null != parentScope) {
                            final PhpAccessVariableInstruction[] usages = PhpControlFlowUtil.getFollowingVariableAccessInstructions(parentScope.getControlFlow().getEntryPoint(), ((Variable) arguments[0]).getName(), false);
                            if (2 != usages.length) {
                                return;
                            }
                        }
                    }
                    final String replacement = semanticMapping.get(functionName).replace("%f%", innerArguments[0].getText()).replace("%s%", innerArguments[1].getText());
                    final String message = messagePattern.replace("%e%", replacement);
                    if (arguments[0] == value) {
                        holder.registerProblem(reference, message, new UseAlternativeFix(replacement));
                    } else {
                        holder.registerProblem(reference, message);
                    }
                }
            }
            values.clear();
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PhpAccessVariableInstruction(com.jetbrains.php.codeInsight.controlFlow.instructions.PhpAccessVariableInstruction) Variable(com.jetbrains.php.lang.psi.elements.Variable) PhpScopeHolder(com.jetbrains.php.codeInsight.PhpScopeHolder) FunctionReference(com.jetbrains.php.lang.psi.elements.FunctionReference) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 2 with PhpScopeHolder

use of com.jetbrains.php.codeInsight.PhpScopeHolder in project phpinspectionsea by kalessil.

the class CallableParameterUseCaseInTypeContextInspection method buildVisitor.

@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) {
    return new BasePhpElementVisitor() {

        @Override
        public void visitPhpMethod(@NotNull Method method) {
            if (!this.isTestContext(method)) {
                this.inspectUsages(method.getParameters(), method);
            }
        }

        @Override
        public void visitPhpFunction(@NotNull Function function) {
            if (!this.isTestContext(function)) {
                this.inspectUsages(function.getParameters(), function);
            }
        }

        private void inspectUsages(@NotNull Parameter[] parameters, @NotNull PhpScopeHolder scopeHolder) {
            final Project project = holder.getProject();
            final PhpIndex index = PhpIndex.getInstance(project);
            final PhpEntryPointInstruction entryPoint = scopeHolder.getControlFlow().getEntryPoint();
            for (final Parameter parameter : parameters) {
                /* normalize parameter types, skip analysis when mixed or object appears */
                final Set<String> paramTypes = new HashSet<>();
                final PhpType parameterType = OpenapiResolveUtil.resolveType(parameter, project);
                if (parameterType != null) {
                    label: for (final String type : parameterType.filterUnknown().getTypes()) {
                        final String typeNormalized = Types.getType(type);
                        switch(typeNormalized) {
                            case Types.strMixed:
                            case Types.strObject:
                                paramTypes.clear();
                                break label;
                            case Types.strCallable:
                                paramTypes.add(Types.strArray);
                                paramTypes.add(Types.strString);
                                paramTypes.add("\\Closure");
                                break;
                            case Types.strIterable:
                                paramTypes.add(Types.strArray);
                                paramTypes.add("\\Traversable");
                                break;
                        }
                        paramTypes.add(typeNormalized);
                    }
                }
                if (paramTypes.isEmpty()) {
                    continue;
                } else {
                    /* in some case PhpStorm is not recognizing default value as parameter type */
                    final PsiElement defaultValue = parameter.getDefaultValue();
                    if (defaultValue instanceof PhpTypedElement) {
                        final PhpType defaultType = OpenapiResolveUtil.resolveType((PhpTypedElement) defaultValue, project);
                        if (defaultType != null) {
                            defaultType.filterUnknown().getTypes().forEach(t -> paramTypes.add(Types.getType(t)));
                        }
                    }
                }
                /* false-positive: type is not resolved correctly, default null is taken */
                if (paramTypes.size() == 1 && paramTypes.contains(Types.strNull)) {
                    final PsiElement defaultValue = parameter.getDefaultValue();
                    if (PhpLanguageUtil.isNull(defaultValue)) {
                        continue;
                    }
                }
                /* now find instructions operating on the parameter and perform analysis */
                final String parameterName = parameter.getName();
                for (final PhpAccessVariableInstruction instruction : OpenapiControlFlowUtil.getFollowingVariableAccessInstructions(entryPoint, parameterName)) {
                    final PsiElement parent = instruction.getAnchor().getParent();
                    final PsiElement callCandidate = null == parent ? null : parent.getParent();
                    /* Case 1: check if is_* functions being used according to definitions */
                    if (OpenapiTypesUtil.isFunctionReference(callCandidate)) {
                        final FunctionReference functionCall = (FunctionReference) callCandidate;
                        final String functionName = functionCall.getName();
                        if (functionName == null) {
                            continue;
                        }
                        /* we expect that aliases usage has been fixed already */
                        final boolean isTypeAnnounced;
                        switch(functionName) {
                            case "is_array":
                                isTypeAnnounced = paramTypes.contains(Types.strArray) || paramTypes.contains(Types.strIterable);
                                break;
                            case "is_string":
                                isTypeAnnounced = paramTypes.contains(Types.strString);
                                break;
                            case "is_bool":
                                isTypeAnnounced = paramTypes.contains(Types.strBoolean);
                                break;
                            case "is_int":
                                isTypeAnnounced = paramTypes.contains(Types.strInteger) || paramTypes.contains(Types.strNumber);
                                break;
                            case "is_float":
                                isTypeAnnounced = paramTypes.contains(Types.strFloat) || paramTypes.contains(Types.strNumber);
                                break;
                            case "is_resource":
                                isTypeAnnounced = paramTypes.contains(Types.strResource);
                                break;
                            case "is_numeric":
                                if (paramTypes.contains(Types.strString)) {
                                    continue;
                                }
                                isTypeAnnounced = paramTypes.contains(Types.strNumber) || paramTypes.contains(Types.strFloat) || paramTypes.contains(Types.strInteger);
                                break;
                            case "is_callable":
                                isTypeAnnounced = paramTypes.contains(Types.strCallable) || paramTypes.contains(Types.strArray) || paramTypes.contains(Types.strString) || paramTypes.contains("\\Closure");
                                break;
                            case "is_object":
                                isTypeAnnounced = paramTypes.contains(Types.strObject) || paramTypes.contains(Types.strCallable) || paramTypes.stream().anyMatch(t -> classReferences.contains(t) || (t.startsWith("\\") && !t.equals("\\Closure")));
                                break;
                            case "is_a":
                                isTypeAnnounced = paramTypes.contains(Types.strObject) || paramTypes.contains(Types.strString) || paramTypes.stream().anyMatch(t -> (t.startsWith("\\") && !t.equals("\\Closure")) || classReferences.contains(t));
                                break;
                            default:
                                continue;
                        }
                        /* cases: call makes no sense, violation of defined types set */
                        if (!isTypeAnnounced) {
                            final PsiElement callParent = functionCall.getParent();
                            boolean isReversedCheck = false;
                            if (callParent instanceof UnaryExpression) {
                                final PsiElement operation = ((UnaryExpression) callParent).getOperation();
                                isReversedCheck = OpenapiTypesUtil.is(operation, PhpTokenTypes.opNOT);
                            }
                            holder.registerProblem(functionCall, MessagesPresentationUtil.prefixWithEa(isReversedCheck ? messageNoSense : messageViolationInCheck));
                        }
                        continue;
                    }
                    /* Case 2: assignments violating parameter definition */
                    if (OpenapiTypesUtil.isAssignment(parent)) {
                        final AssignmentExpression assignment = (AssignmentExpression) parent;
                        final PhpPsiElement variable = assignment.getVariable();
                        final PhpPsiElement value = assignment.getValue();
                        if (variable instanceof Variable && value instanceof PhpTypedElement) {
                            final String variableName = variable.getName();
                            if (variableName != null && variableName.equals(parameterName)) {
                                final PhpType resolvedType = OpenapiResolveUtil.resolveType((PhpTypedElement) value, project);
                                final Set<String> resolved = new HashSet<>();
                                if (resolvedType != null) {
                                    resolvedType.filterUnknown().getTypes().forEach(t -> resolved.add(Types.getType(t)));
                                }
                                if (resolved.size() >= 2) {
                                    /* false-positives: core functions returning string|array & false|null */
                                    if (resolved.contains(Types.strString) || resolved.contains(Types.strArray)) {
                                        if (resolved.contains(Types.strBoolean)) {
                                            final boolean isFunctionCall = OpenapiTypesUtil.isFunctionReference(value);
                                            if (isFunctionCall) {
                                                resolved.remove(Types.strBoolean);
                                            }
                                        } else if (resolved.contains(Types.strNull)) {
                                            final boolean isFunctionCall = OpenapiTypesUtil.isFunctionReference(value);
                                            if (isFunctionCall) {
                                                resolved.remove(Types.strNull);
                                            }
                                        }
                                    } else /* false-positives: nullable objects */
                                    if (resolved.contains(Types.strNull)) {
                                        final boolean isNullableObject = paramTypes.stream().anyMatch(t -> classReferences.contains(t) || t.startsWith("\\") && !t.equals("\\Closure"));
                                        if (isNullableObject) {
                                            resolved.remove(Types.strNull);
                                        }
                                    }
                                }
                                resolved.remove(Types.strMixed);
                                for (String type : resolved) {
                                    /* translate static/self into FQNs */
                                    if (classReferences.contains(type)) {
                                        PsiElement valueExtract = value;
                                        /* ` = <whatever> ?? <method call>` support */
                                        if (valueExtract instanceof BinaryExpression) {
                                            final BinaryExpression binary = (BinaryExpression) valueExtract;
                                            if (binary.getOperationType() == PhpTokenTypes.opCOALESCE) {
                                                final PsiElement left = binary.getLeftOperand();
                                                if (left != null && OpenapiEquivalenceUtil.areEqual(variable, left)) {
                                                    final PsiElement right = binary.getRightOperand();
                                                    if (right != null) {
                                                        valueExtract = right;
                                                    }
                                                }
                                            }
                                        }
                                        /* method call lookup */
                                        if (valueExtract instanceof MethodReference) {
                                            final PsiElement base = valueExtract.getFirstChild();
                                            if (base instanceof ClassReference) {
                                                final PsiElement resolvedClass = OpenapiResolveUtil.resolveReference((ClassReference) base);
                                                if (resolvedClass instanceof PhpClass) {
                                                    type = ((PhpClass) resolvedClass).getFQN();
                                                }
                                            } else if (base instanceof PhpTypedElement) {
                                                final PhpType clazzTypes = OpenapiResolveUtil.resolveType((PhpTypedElement) base, project);
                                                if (clazzTypes != null) {
                                                    final Set<String> filteredTypes = clazzTypes.filterUnknown().getTypes().stream().map(Types::getType).filter(t -> t.startsWith("\\")).collect(Collectors.toSet());
                                                    final int filteredTypesCount = filteredTypes.size();
                                                    /* clear resolved class or interface + class */
                                                    if (filteredTypesCount == 1 || filteredTypesCount == 2) {
                                                        type = filteredTypes.iterator().next();
                                                    }
                                                    filteredTypes.clear();
                                                }
                                            }
                                        }
                                        /* translate static/self into FQNs didn't work, skip */
                                        if (classReferences.contains(type)) {
                                            continue;
                                        }
                                    }
                                    final boolean isViolation = !this.isTypeCompatibleWith(type, paramTypes, index);
                                    if (isViolation) {
                                        holder.registerProblem(value, String.format(MessagesPresentationUtil.prefixWithEa(patternViolationInAssignment), type));
                                        break;
                                    }
                                }
                                resolved.clear();
                            }
                        }
                    }
                }
                paramTypes.clear();
            }
        }

        private boolean isTypeCompatibleWith(@NotNull String type, @NotNull Collection<String> allowedTypes, @NotNull PhpIndex index) {
            /* first case: implicit match */
            if (allowedTypes.contains(type)) {
                return true;
            }
            /* second case: inherited classes/interfaces */
            final Set<String> possibleTypes = new HashSet<>();
            if (type.startsWith("\\")) {
                index.getAnyByFQN(type).forEach(clazz -> InterfacesExtractUtil.getCrawlInheritanceTree(clazz, true).forEach(c -> possibleTypes.add(c.getFQN())));
            }
            return !possibleTypes.isEmpty() && allowedTypes.stream().anyMatch(possibleTypes::contains);
        }
    };
}
Also used : PhpEntryPointInstruction(com.jetbrains.php.codeInsight.controlFlow.instructions.PhpEntryPointInstruction) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) com.jetbrains.php.lang.psi.elements(com.jetbrains.php.lang.psi.elements) InterfacesExtractUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.hierarhy.InterfacesExtractUtil) PhpTokenTypes(com.jetbrains.php.lang.lexer.PhpTokenTypes) Collection(java.util.Collection) Set(java.util.Set) PhpIndex(com.jetbrains.php.PhpIndex) Collectors(java.util.stream.Collectors) HashSet(java.util.HashSet) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) com.kalessil.phpStorm.phpInspectionsEA.utils(com.kalessil.phpStorm.phpInspectionsEA.utils) PsiElement(com.intellij.psi.PsiElement) Project(com.intellij.openapi.project.Project) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) NotNull(org.jetbrains.annotations.NotNull) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) PhpScopeHolder(com.jetbrains.php.codeInsight.PhpScopeHolder) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) PhpAccessVariableInstruction(com.jetbrains.php.codeInsight.controlFlow.instructions.PhpAccessVariableInstruction) Set(java.util.Set) HashSet(java.util.HashSet) PhpEntryPointInstruction(com.jetbrains.php.codeInsight.controlFlow.instructions.PhpEntryPointInstruction) NotNull(org.jetbrains.annotations.NotNull) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PhpScopeHolder(com.jetbrains.php.codeInsight.PhpScopeHolder) PsiElement(com.intellij.psi.PsiElement) HashSet(java.util.HashSet) PhpAccessVariableInstruction(com.jetbrains.php.codeInsight.controlFlow.instructions.PhpAccessVariableInstruction) PhpIndex(com.jetbrains.php.PhpIndex) Project(com.intellij.openapi.project.Project) Collection(java.util.Collection) NotNull(org.jetbrains.annotations.NotNull)

Example 3 with PhpScopeHolder

use of com.jetbrains.php.codeInsight.PhpScopeHolder 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;
        }
    };
}
Also used : PhpDocVariable(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocVariable) NotNull(org.jetbrains.annotations.NotNull) PhpDocTag(com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PhpScopeHolder(com.jetbrains.php.codeInsight.PhpScopeHolder) PhpThrowExpression(com.kalessil.phpStorm.phpInspectionsEA.openApi.elements.PhpThrowExpression) PhpAccessVariableInstruction(com.jetbrains.php.codeInsight.controlFlow.instructions.PhpAccessVariableInstruction) IElementType(com.intellij.psi.tree.IElementType) PhpDocComment(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment) PhpDocVariable(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocVariable) Nullable(org.jetbrains.annotations.Nullable) NotNull(org.jetbrains.annotations.NotNull)

Example 4 with PhpScopeHolder

use of com.jetbrains.php.codeInsight.PhpScopeHolder in project phpinspectionsea by kalessil.

the class UselessUnsetInspector method buildVisitor.

@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) {
    /* foreach is also a case, but there is no way to get flow entry point in actual JB platform API */
    return new BasePhpElementVisitor() {

        @Override
        public void visitPhpMethod(@NotNull Method method) {
            this.inspectUsages(method.getParameters(), method);
        }

        @Override
        public void visitPhpFunction(@NotNull Function function) {
            this.inspectUsages(function.getParameters(), function);
        }

        private void inspectUsages(@NotNull Parameter[] parameters, @NotNull PhpScopeHolder scope) {
            if (parameters.length > 0) {
                final PhpEntryPointInstruction entry = scope.getControlFlow().getEntryPoint();
                for (final Parameter parameter : parameters) {
                    final String parameterName = parameter.getName();
                    if (!parameterName.isEmpty()) {
                        for (final PhpAccessVariableInstruction usage : OpenapiControlFlowUtil.getFollowingVariableAccessInstructions(entry, parameterName)) {
                            final PsiElement expression = usage.getAnchor();
                            final PsiElement parent = expression.getParent();
                            if (parent instanceof PhpUnset) {
                                int unsetParametersCount = ((PhpUnset) parent).getArguments().length;
                                final PsiElement target = (unsetParametersCount == 1 ? parent : expression);
                                holder.registerProblem(target, MessagesPresentationUtil.prefixWithEa(message), ProblemHighlightType.LIKE_UNUSED_SYMBOL);
                            }
                        }
                    }
                }
            }
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) Function(com.jetbrains.php.lang.psi.elements.Function) PhpAccessVariableInstruction(com.jetbrains.php.codeInsight.controlFlow.instructions.PhpAccessVariableInstruction) PhpEntryPointInstruction(com.jetbrains.php.codeInsight.controlFlow.instructions.PhpEntryPointInstruction) PhpScopeHolder(com.jetbrains.php.codeInsight.PhpScopeHolder) PhpUnset(com.jetbrains.php.lang.psi.elements.PhpUnset) Parameter(com.jetbrains.php.lang.psi.elements.Parameter) Method(com.jetbrains.php.lang.psi.elements.Method) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Aggregations

PhpScopeHolder (com.jetbrains.php.codeInsight.PhpScopeHolder)4 PhpAccessVariableInstruction (com.jetbrains.php.codeInsight.controlFlow.instructions.PhpAccessVariableInstruction)4 BasePhpElementVisitor (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor)4 NotNull (org.jetbrains.annotations.NotNull)4 PsiElement (com.intellij.psi.PsiElement)3 PhpEntryPointInstruction (com.jetbrains.php.codeInsight.controlFlow.instructions.PhpEntryPointInstruction)2 ProblemsHolder (com.intellij.codeInspection.ProblemsHolder)1 Project (com.intellij.openapi.project.Project)1 PsiElementVisitor (com.intellij.psi.PsiElementVisitor)1 IElementType (com.intellij.psi.tree.IElementType)1 PhpIndex (com.jetbrains.php.PhpIndex)1 PhpDocComment (com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment)1 PhpDocVariable (com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocVariable)1 PhpDocTag (com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag)1 PhpTokenTypes (com.jetbrains.php.lang.lexer.PhpTokenTypes)1 com.jetbrains.php.lang.psi.elements (com.jetbrains.php.lang.psi.elements)1 Function (com.jetbrains.php.lang.psi.elements.Function)1 FunctionReference (com.jetbrains.php.lang.psi.elements.FunctionReference)1 Method (com.jetbrains.php.lang.psi.elements.Method)1 Parameter (com.jetbrains.php.lang.psi.elements.Parameter)1