Search in sources :

Example 11 with PhpLanguageLevel

use of com.jetbrains.php.config.PhpLanguageLevel in project phpinspectionsea by kalessil.

the class PropertyInitializationFlawsInspectorTest method testTypedPropertiesPatterns.

public void testTypedPropertiesPatterns() {
    final PhpLanguageLevel level = PhpLanguageLevel.parse("7.4");
    if (level != null && level.getVersionString().equals("7.4")) {
        PhpProjectConfigurationFacade.getInstance(myFixture.getProject()).setLanguageLevel(level);
        myFixture.enableInspections(new PropertyInitializationFlawsInspector());
        myFixture.configureByFile("testData/fixtures/codeStyle/property-initialization-typed-properties.php");
        myFixture.testHighlighting(true, false, true);
    }
}
Also used : PropertyInitializationFlawsInspector(com.kalessil.phpStorm.phpInspectionsEA.inspectors.apiUsage.PropertyInitializationFlawsInspector) PhpLanguageLevel(com.jetbrains.php.config.PhpLanguageLevel)

Example 12 with PhpLanguageLevel

use of com.jetbrains.php.config.PhpLanguageLevel in project phpinspectionsea by kalessil.

the class ReferenceMismatchInspector method buildVisitor.

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

        /**
         * TODO: checkReferenceReturnedByCallable - ternary operator, argument usages ?
         */
        /* parameters by reference */
        @Override
        public void visitPhpMethod(@NotNull Method method) {
            /* PHP7 seems to be ref mismatch free */
            final PhpLanguageLevel php = PhpProjectConfigurationFacade.getInstance(holder.getProject()).getLanguageLevel();
            if (!php.hasFeature(PhpLanguageFeature.SCALAR_TYPE_HINTS)) {
                this.checkParameters(method.getParameters(), method);
            }
        }

        @Override
        public void visitPhpFunction(@NotNull Function function) {
            /* PHP7 seems to be ref mismatch free */
            final PhpLanguageLevel phpVersion = PhpProjectConfigurationFacade.getInstance(holder.getProject()).getLanguageLevel();
            if (phpVersion.hasFeature(PhpLanguageFeature.SCALAR_TYPE_HINTS)) {
                // PHP7 and newer
                return;
            }
            /* older versions are still affected */
            this.checkParameters(function.getParameters(), function);
        }

        private void checkParameters(Parameter[] arrParameters, Function objScopeHolder) {
            PhpEntryPointInstruction objEntryPoint = objScopeHolder.getControlFlow().getEntryPoint();
            HashSet<PsiElement> emptyReportedItemsRegistry = ReferenceMismatchInspector.getFunctionReportingRegistry(objScopeHolder);
            for (Parameter parameter : arrParameters) {
                /* skip un-discoverable and non-reference parameters */
                String strParameterName = parameter.getName();
                if (!parameter.isPassByRef() || StringUtils.isEmpty(strParameterName)) {
                    continue;
                }
                inspectScopeForReferenceMissUsages(objEntryPoint, strParameterName, emptyReportedItemsRegistry);
            }
            emptyReportedItemsRegistry.clear();
        }

        /* = & variable/property patterns */
        @Override
        public void visitPhpAssignmentExpression(@NotNull AssignmentExpression assignmentExpression) {
            /* PHP7 seems to be ref mismatch free */
            final PhpLanguageLevel phpVersion = PhpProjectConfigurationFacade.getInstance(holder.getProject()).getLanguageLevel();
            if (phpVersion.hasFeature(PhpLanguageFeature.SCALAR_TYPE_HINTS)) {
                // PHP7 and newer
                return;
            }
            /* older versions are still affected */
            PsiElement value = assignmentExpression.getValue();
            PsiElement variable = assignmentExpression.getVariable();
            if (variable instanceof Variable && (value instanceof Variable || value instanceof FieldReference || value instanceof ArrayAccessExpression || value instanceof FunctionReference)) {
                String strVariable = ((Variable) variable).getName();
                PsiElement operation = value.getPrevSibling();
                if (operation instanceof PsiWhiteSpace) {
                    operation = operation.getPrevSibling();
                }
                if (!StringUtils.isEmpty(strVariable) && null != operation && operation.getText().replaceAll("\\s+", "").equals("=&")) {
                    /* the case, scan for miss-usages assuming variable is unique */
                    Function scope = ExpressionSemanticUtil.getScope(assignmentExpression);
                    if (null != scope) {
                        // report items, but ensure no duplicated messages
                        HashSet<PsiElement> reportedItemsRegistry = ReferenceMismatchInspector.getFunctionReportingRegistry(scope);
                        inspectScopeForReferenceMissUsages(scope.getControlFlow().getEntryPoint(), strVariable, reportedItemsRegistry);
                    }
                }
            }
        }

        /* assign reference from function */
        @Override
        public void visitPhpMethodReference(@NotNull MethodReference reference) {
            /* PHP7 seems to be ref mismatch free */
            final PhpLanguageLevel phpVersion = PhpProjectConfigurationFacade.getInstance(holder.getProject()).getLanguageLevel();
            if (phpVersion.hasFeature(PhpLanguageFeature.SCALAR_TYPE_HINTS)) {
                // PHP7 and newer
                return;
            }
            /* older versions are still affected */
            this.checkReferenceReturnedByCallable(reference);
        }

        @Override
        public void visitPhpFunctionCall(@NotNull FunctionReference reference) {
            /* PHP7 seems to be ref mismatch free */
            final PhpLanguageLevel php = PhpProjectConfigurationFacade.getInstance(holder.getProject()).getLanguageLevel();
            if (!php.hasFeature(PhpLanguageFeature.SCALAR_TYPE_HINTS)) {
                this.checkReferenceReturnedByCallable(reference);
            }
        }

        /* aggressive foreach optimization when value is reference */
        @Override
        public void visitPhpForeach(@NotNull ForeachStatement foreach) {
            /* PHP7 seems to be ref mismatch free */
            final PhpLanguageLevel phpVersion = PhpProjectConfigurationFacade.getInstance(holder.getProject()).getLanguageLevel();
            if (phpVersion.hasFeature(PhpLanguageFeature.SCALAR_TYPE_HINTS)) {
                // PHP7 and newer
                return;
            }
            /* older versions are still affected */
            /* lookup for reference preceding value */
            Variable objForeachValue = foreach.getValue();
            if (null != objForeachValue) {
                String strVariable = objForeachValue.getName();
                PsiElement prevElement = objForeachValue.getPrevSibling();
                if (prevElement instanceof PsiWhiteSpace) {
                    prevElement = prevElement.getPrevSibling();
                }
                if (!StringUtils.isEmpty(strVariable) && OpenapiTypesUtil.is(prevElement, PhpTokenTypes.opBIT_AND)) {
                    /* the case, scan for miss-usages assuming value is unique */
                    Function scope = ExpressionSemanticUtil.getScope(foreach);
                    if (null != scope) {
                        // report items, but ensure no duplicated messages
                        HashSet<PsiElement> reportedItemsRegistry = ReferenceMismatchInspector.getFunctionReportingRegistry(scope);
                        reportedItemsRegistry.add(objForeachValue);
                        inspectScopeForReferenceMissUsages(scope.getControlFlow().getEntryPoint(), strVariable, reportedItemsRegistry);
                    }
                }
            }
        }

        private void inspectScopeForReferenceMissUsages(@NotNull PhpEntryPointInstruction objEntryPoint, @NotNull String strParameterName, @NotNull Set<PsiElement> reportedItemsRegistry) {
            PsiElement previous;
            PsiElement objExpression = null;
            /* find usage inside scope */
            PhpAccessVariableInstruction[] arrUsages = PhpControlFlowUtil.getFollowingVariableAccessInstructions(objEntryPoint, strParameterName, false);
            for (final PhpAccessVariableInstruction objInstruction : arrUsages) {
                previous = objExpression;
                objExpression = objInstruction.getAnchor().getParent();
                /* collided with foreach index/value => bug */
                if (objExpression instanceof ForeachStatement) {
                    final ForeachStatement foreach = (ForeachStatement) objExpression;
                    if (previous instanceof PhpUnset) {
                        break;
                    }
                    final Variable foreachValue = foreach.getValue();
                    if (null != foreachValue && !StringUtils.isEmpty(foreachValue.getName()) && foreachValue.getName().equals(strParameterName)) {
                        if (!reportedItemsRegistry.contains(foreachValue)) {
                            reportedItemsRegistry.add(foreachValue);
                            holder.registerProblem(foreachValue, strErrorForeachIntoReference, ProblemHighlightType.ERROR);
                        }
                        continue;
                    }
                    final Variable foreachKey = foreach.getKey();
                    if (null != foreachKey && !StringUtils.isEmpty(foreachKey.getName()) && foreachKey.getName().equals(strParameterName)) {
                        if (!reportedItemsRegistry.contains(foreachKey)) {
                            reportedItemsRegistry.add(foreachKey);
                            holder.registerProblem(foreachKey, strErrorForeachIntoReference, ProblemHighlightType.ERROR);
                        }
                        continue;
                    }
                }
                /* test if provided as non-reference argument (copy dispatched) */
                if (objExpression instanceof ParameterList && objExpression.getParent() instanceof FunctionReference) {
                    FunctionReference reference = (FunctionReference) objExpression.getParent();
                    /* not resolved or known re-unsafe function */
                    final PsiElement callable = OpenapiResolveUtil.resolveReference(reference);
                    if (!(callable instanceof Function)) {
                        continue;
                    }
                    final String strCallableName = ((Function) callable).getName();
                    if (!StringUtils.isEmpty(strCallableName) && legalizedMismatchingFunctions.contains(strCallableName)) {
                        continue;
                    }
                    /* check if call arguments contains our parameter */
                    int indexInArguments = -1;
                    boolean providedAsArgument = false;
                    for (PsiElement callArgument : reference.getParameters()) {
                        ++indexInArguments;
                        if (callArgument instanceof Variable) {
                            Variable argument = (Variable) callArgument;
                            String argumentName = argument.getName();
                            if (!StringUtils.isEmpty(argumentName) && argumentName.equals(strParameterName)) {
                                providedAsArgument = true;
                                break;
                            }
                        }
                    }
                    /* if not found, keep processing usages */
                    if (!providedAsArgument) {
                        continue;
                    }
                    /* now check what is declared in resolved callable */
                    final Parameter[] usageCallableParameters = ((Function) callable).getParameters();
                    if (usageCallableParameters.length >= indexInArguments + 1) {
                        final Parameter parameter = usageCallableParameters[indexInArguments];
                        if (!parameter.isPassByRef()) {
                            /* additionally try filtering types for reducing false-positives on scalars */
                            final PhpType type = OpenapiResolveUtil.resolveType(parameter, holder.getProject());
                            if (type != null && !PhpType.isSubType(type, legalizedTypesForMismatchingSet)) {
                                final PsiElement target = reference.getParameters()[indexInArguments];
                                if (!reportedItemsRegistry.contains(target)) {
                                    holder.registerProblem(target, "Reference mismatch, copy will be dispatched into function", ProblemHighlightType.WEAK_WARNING);
                                    reportedItemsRegistry.add(target);
                                }
                                continue;
                            }
                        }
                    }
                }
                /* test is assigned to a variable without stating its reference (copy stored) */
                if (objExpression instanceof AssignmentExpression) {
                    /* assignment structure verify */
                    AssignmentExpression assignment = (AssignmentExpression) objExpression;
                    if (assignment.getValue() instanceof Variable) {
                        Variable variable = (Variable) assignment.getValue();
                        String strVariable = variable.getName();
                        /* references parameter */
                        if (!StringUtils.isEmpty(strVariable) && strVariable.equals(strParameterName)) {
                            /* check if assignments states reference usage */
                            PsiElement operation = variable.getPrevSibling();
                            if (operation instanceof PsiWhiteSpace) {
                                operation = operation.getPrevSibling();
                            }
                            /* report if not */
                            if (null != operation && !operation.getText().replaceAll("\\s+", "").equals("=&")) {
                                if (!reportedItemsRegistry.contains(objExpression)) {
                                    holder.registerProblem(objExpression, "Reference mismatch, copy will be stored (for non-objects)", ProblemHighlightType.WEAK_WARNING);
                                    reportedItemsRegistry.add(objExpression);
                                }
                            }
                        }
                    }
                }
            }
        }

        private void checkReferenceReturnedByCallable(@NotNull FunctionReference reference) {
            /* check context before resolving anything  */
            final PsiElement parent = reference.getParent();
            if (parent instanceof AssignmentExpression) {
                /* assignment structure verify */
                final AssignmentExpression assignment = (AssignmentExpression) parent;
                if (assignment.getValue() == reference) {
                    /* try resolving now */
                    final PsiElement callable = OpenapiResolveUtil.resolveReference(reference);
                    if (callable instanceof Function) {
                        /* ensure name discoverable */
                        final Function function = (Function) callable;
                        final PsiElement nameNode = NamedElementUtil.getNameIdentifier(function);
                        if (null != nameNode) {
                            /* is defined like returning reference */
                            PsiElement prevElement = nameNode.getPrevSibling();
                            if (prevElement instanceof PsiWhiteSpace) {
                                prevElement = prevElement.getPrevSibling();
                            }
                            if (OpenapiTypesUtil.is(prevElement, PhpTokenTypes.opBIT_AND)) {
                                /* check if assignments states reference usage */
                                PsiElement operation = reference.getPrevSibling();
                                if (operation instanceof PsiWhiteSpace) {
                                    operation = operation.getPrevSibling();
                                }
                                /* report if not */
                                if (null != operation && !operation.getText().replaceAll("\\s+", "").equals("=&")) {
                                    holder.registerProblem(parent, "Reference mismatch, copy will be stored (for non-objects)", ProblemHighlightType.WEAK_WARNING);
                                }
                            }
                        }
                    }
                }
            }
        }
    };
}
Also used : HashSet(java.util.HashSet) Set(java.util.Set) 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) PsiElement(com.intellij.psi.PsiElement) PhpAccessVariableInstruction(com.jetbrains.php.codeInsight.controlFlow.instructions.PhpAccessVariableInstruction) PhpLanguageLevel(com.jetbrains.php.config.PhpLanguageLevel) PsiWhiteSpace(com.intellij.psi.PsiWhiteSpace) NotNull(org.jetbrains.annotations.NotNull)

Example 13 with PhpLanguageLevel

use of com.jetbrains.php.config.PhpLanguageLevel in project phpinspectionsea by kalessil.

the class AccessModifierPresentedInspector method buildVisitor.

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

        @Override
        public void visitPhpClass(@NotNull PhpClass clazz) {
            /* community request: interfaces have only public methods, what is default access levels */
            if (!ANALYZE_INTERFACES && clazz.isInterface()) {
                return;
            }
            /* inspect methods */
            for (final Method method : clazz.getOwnMethods()) {
                final PsiElement methodName = NamedElementUtil.getNameIdentifier(method);
                if (methodName != null && method.getAccess().isPublic()) {
                    final PhpModifierList modifiers = PsiTreeUtil.findChildOfType(method, PhpModifierList.class);
                    if (modifiers != null && !modifiers.getText().toLowerCase().contains("public")) {
                        final String message = String.format(messagePattern, method.getName());
                        holder.registerProblem(methodName, message, new MemberVisibilityFix(modifiers));
                    }
                }
            }
            /* inspect fields */
            final PhpLanguageLevel phpVersion = PhpProjectConfigurationFacade.getInstance(holder.getProject()).getLanguageLevel();
            final boolean checkConstantVisibility = phpVersion.compareTo(PhpLanguageLevel.PHP710) >= 0;
            for (final Field field : clazz.getOwnFields()) {
                final PsiElement fieldName = NamedElementUtil.getNameIdentifier(field);
                if (fieldName != null && field.getModifier().isPublic()) {
                    if (field.isConstant()) {
                        /* {const}.isPublic() always returns true, even if visibility is not declared */
                        if (ANALYZE_CONSTANTS && checkConstantVisibility && field.getPrevPsiSibling() == null) {
                            final String message = String.format(messagePattern, field.getName());
                            holder.registerProblem(fieldName, message, new ConstantVisibilityFix(field));
                        }
                    } else {
                        final PhpModifierList modifiers = PsiTreeUtil.findChildOfType(field.getParent(), PhpModifierList.class);
                        if (modifiers != null && !modifiers.getText().toLowerCase().contains("public")) {
                            final String message = String.format(messagePattern, field.getName());
                            holder.registerProblem(fieldName, message, new MemberVisibilityFix(modifiers));
                        }
                    }
                }
            }
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) Field(com.jetbrains.php.lang.psi.elements.Field) PhpClass(com.jetbrains.php.lang.psi.elements.PhpClass) Method(com.jetbrains.php.lang.psi.elements.Method) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) PhpLanguageLevel(com.jetbrains.php.config.PhpLanguageLevel) PhpModifierList(com.jetbrains.php.lang.psi.elements.PhpModifierList) NotNull(org.jetbrains.annotations.NotNull)

Example 14 with PhpLanguageLevel

use of com.jetbrains.php.config.PhpLanguageLevel in project phpinspectionsea by kalessil.

the class PowerOperatorCanBeUsedInspector method buildVisitor.

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

        @Override
        public void visitPhpFunctionCall(@NotNull FunctionReference reference) {
            final PhpLanguageLevel php = PhpProjectConfigurationFacade.getInstance(holder.getProject()).getLanguageLevel();
            if (php.compareTo(PhpLanguageLevel.PHP560) >= 0) {
                final String functionName = reference.getName();
                if (functionName != null && functionName.equals("pow")) {
                    final PsiElement[] arguments = reference.getParameters();
                    if (arguments.length == 2) {
                        final String replacement = (reference.getParent() instanceof BinaryExpression ? "(%b% ** %p%)" : "%b% ** %p%").replace("%p%", arguments[1] instanceof BinaryExpression ? "(%p%)" : "%p%").replace("%b%", arguments[0] instanceof BinaryExpression ? "(%b%)" : "%b%").replace("%p%", arguments[1].getText()).replace("%b%", arguments[0].getText());
                        holder.registerProblem(reference, String.format(messagePattern, replacement), new UseTheOperatorFix(replacement));
                    }
                }
            }
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) BinaryExpression(com.jetbrains.php.lang.psi.elements.BinaryExpression) FunctionReference(com.jetbrains.php.lang.psi.elements.FunctionReference) NotNull(org.jetbrains.annotations.NotNull) PhpLanguageLevel(com.jetbrains.php.config.PhpLanguageLevel) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 15 with PhpLanguageLevel

use of com.jetbrains.php.config.PhpLanguageLevel in project phpinspectionsea by kalessil.

the class ShortListSyntaxCanBeUsedInspector method buildVisitor.

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

        public void visitPhpMultiassignmentExpression(MultiassignmentExpression multiassignmentExpression) {
            /* ensure php version is at least PHP 7.1 */
            final PhpLanguageLevel phpVersion = PhpProjectConfigurationFacade.getInstance(holder.getProject()).getLanguageLevel();
            if (phpVersion.compareTo(PhpLanguageLevel.PHP710) < 0) {
                return;
            }
            /* verify if it's dedicated statement and it's the list(...) construction */
            final PsiElement parent = multiassignmentExpression.getParent();
            if (!OpenapiTypesUtil.isStatementImpl(parent)) {
                return;
            }
            final PsiElement listKeyword = multiassignmentExpression.getFirstChild();
            if (null != listKeyword && PhpTokenTypes.kwLIST == listKeyword.getNode().getElementType()) {
                holder.registerProblem(listKeyword, messageAssign, ProblemHighlightType.WEAK_WARNING, new TheLocalFix());
            }
        }

        public void visitPhpForeach(ForeachStatement foreach) {
            /* ensure php version is at least PHP 7.1 */
            final PhpLanguageLevel phpVersion = PhpProjectConfigurationFacade.getInstance(holder.getProject()).getLanguageLevel();
            if (phpVersion.compareTo(PhpLanguageLevel.PHP710) < 0) {
                return;
            }
            final List<Variable> variables = foreach.getVariables();
            if (variables.size() > 0) {
                PsiElement childNode = foreach.getFirstChild();
                while (null != childNode) {
                    if (childNode.getClass() == LeafPsiElement.class && PhpTokenTypes.kwLIST == childNode.getNode().getElementType()) {
                        holder.registerProblem(childNode, messageForeach, ProblemHighlightType.WEAK_WARNING, new TheLocalFix());
                        break;
                    }
                    childNode = childNode.getNextSibling();
                    if (childNode instanceof GroupStatement) {
                        break;
                    }
                }
            }
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) GroupStatement(com.jetbrains.php.lang.psi.elements.GroupStatement) Variable(com.jetbrains.php.lang.psi.elements.Variable) LeafPsiElement(com.intellij.psi.impl.source.tree.LeafPsiElement) MultiassignmentExpression(com.jetbrains.php.lang.psi.elements.MultiassignmentExpression) PhpLanguageLevel(com.jetbrains.php.config.PhpLanguageLevel) LeafPsiElement(com.intellij.psi.impl.source.tree.LeafPsiElement) PsiElement(com.intellij.psi.PsiElement) ForeachStatement(com.jetbrains.php.lang.psi.elements.ForeachStatement) NotNull(org.jetbrains.annotations.NotNull)

Aggregations

PhpLanguageLevel (com.jetbrains.php.config.PhpLanguageLevel)17 BasePhpElementVisitor (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor)8 NotNull (org.jetbrains.annotations.NotNull)8 PsiElement (com.intellij.psi.PsiElement)7 FunctionReference (com.jetbrains.php.lang.psi.elements.FunctionReference)4 StringLiteralExpression (com.jetbrains.php.lang.psi.elements.StringLiteralExpression)2 Variable (com.jetbrains.php.lang.psi.elements.Variable)2 LocalQuickFix (com.intellij.codeInspection.LocalQuickFix)1 ProblemDescriptor (com.intellij.codeInspection.ProblemDescriptor)1 ProblemHighlightType (com.intellij.codeInspection.ProblemHighlightType)1 ProblemsHolder (com.intellij.codeInspection.ProblemsHolder)1 Project (com.intellij.openapi.project.Project)1 PsiElementVisitor (com.intellij.psi.PsiElementVisitor)1 PsiWhiteSpace (com.intellij.psi.PsiWhiteSpace)1 SmartPointerManager (com.intellij.psi.SmartPointerManager)1 SmartPsiElementPointer (com.intellij.psi.SmartPsiElementPointer)1 LeafPsiElement (com.intellij.psi.impl.source.tree.LeafPsiElement)1 PhpAccessVariableInstruction (com.jetbrains.php.codeInsight.controlFlow.instructions.PhpAccessVariableInstruction)1 PhpEntryPointInstruction (com.jetbrains.php.codeInsight.controlFlow.instructions.PhpEntryPointInstruction)1 PhpLanguageFeature (com.jetbrains.php.config.PhpLanguageFeature)1