Search in sources :

Example 76 with BasePhpElementVisitor

use of com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor in project phpinspectionsea by kalessil.

the class UnusedConstructorDependenciesInspector method buildVisitor.

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

        @NotNull
        private Map<String, Field> getPrivateFields(@NotNull PhpClass clazz) {
            final Map<String, Field> privateFields = new HashMap<>();
            for (final Field field : clazz.getOwnFields()) {
                if (!field.isConstant()) {
                    final PhpModifier modifiers = field.getModifier();
                    if (modifiers.isPrivate() && !modifiers.isStatic()) {
                        final PhpDocTag[] tags = PsiTreeUtil.getChildrenOfType(field.getDocComment(), PhpDocTag.class);
                        final boolean annotated = tags != null && Arrays.stream(tags).anyMatch(t -> !t.getName().equals(t.getName().toLowerCase()));
                        if (!annotated) {
                            privateFields.put(field.getName(), field);
                        }
                    }
                }
            }
            return privateFields;
        }

        @NotNull
        private Map<String, List<FieldReference>> getFieldReferences(@NotNull Method method, @NotNull Map<String, Field> privateFields) {
            final Map<String, List<FieldReference>> filteredReferences = new HashMap<>();
            if (!method.isAbstract()) {
                final Collection<FieldReference> references = PsiTreeUtil.findChildrenOfType(method, FieldReference.class);
                for (final FieldReference ref : references) {
                    final String fieldName = ref.getName();
                    if (fieldName != null && privateFields.containsKey(fieldName)) {
                        final PsiElement resolved = OpenapiResolveUtil.resolveReference(ref);
                        if (resolved == null || (resolved instanceof Field && privateFields.containsValue(resolved))) {
                            filteredReferences.computeIfAbsent(fieldName, k -> new ArrayList<>()).add(ref);
                        }
                    }
                }
                references.clear();
            }
            return filteredReferences;
        }

        @NotNull
        private Map<String, List<FieldReference>> getMethodsFieldReferences(@NotNull PhpClass clazz, @NotNull Method constructor, @NotNull Map<String, Field> privateFields) {
            final Map<String, List<FieldReference>> filteredReferences = new HashMap<>();
            /* collect methods to inspect, incl. once from traits */
            final List<Method> methodsToCheck = new ArrayList<>();
            Collections.addAll(methodsToCheck, clazz.getOwnMethods());
            for (final PhpClass trait : clazz.getTraits()) {
                Collections.addAll(methodsToCheck, trait.getOwnMethods());
            }
            /* find references */
            for (final Method method : methodsToCheck) {
                if (method != constructor) {
                    final Map<String, List<FieldReference>> innerReferences = getFieldReferences(method, privateFields);
                    if (!innerReferences.isEmpty()) {
                        /* merge method's scan results into common container */
                        innerReferences.forEach((fieldName, references) -> {
                            filteredReferences.computeIfAbsent(fieldName, name -> new ArrayList<>()).addAll(references);
                            references.clear();
                        });
                    }
                    innerReferences.clear();
                }
            }
            methodsToCheck.clear();
            return filteredReferences;
        }

        @Override
        public void visitPhpMethod(@NotNull Method method) {
            /* filter classes which needs to be analyzed */
            final PhpClass clazz = method.getContainingClass();
            if (null == clazz || clazz.isInterface() || clazz.isTrait() || null == clazz.getOwnConstructor() || 0 == clazz.getOwnFields().length || 0 == clazz.getOwnMethods().length) {
                return;
            }
            /* run inspection only in constructors and if own private fields being defined */
            final Method constructor = clazz.getOwnConstructor();
            if (method != constructor) {
                return;
            }
            final Map<String, Field> clazzPrivateFields = this.getPrivateFields(clazz);
            if (clazzPrivateFields.isEmpty()) {
                return;
            }
            /* === intensive part : extract references === */
            final Map<String, List<FieldReference>> constructorsReferences = getFieldReferences(constructor, clazzPrivateFields);
            if (!constructorsReferences.isEmpty()) {
                /* constructor's references being identified */
                final Map<String, List<FieldReference>> otherReferences = getMethodsFieldReferences(clazz, constructor, clazzPrivateFields);
                /* methods's references being identified, time to re-visit constructor's references */
                constructorsReferences.forEach((fieldName, references) -> {
                    /* field is not used, report in constructor, IDE detects unused fields */
                    if (!otherReferences.containsKey(fieldName)) {
                        /* ensure the field reference in constructor is not bound to closures only */
                        if (references.stream().anyMatch(r -> ExpressionSemanticUtil.getScope(r) == constructor)) {
                            this.doReport(holder, references);
                        }
                    }
                    references.clear();
                });
                /* release references found in the methods */
                otherReferences.values().forEach(List::clear);
                otherReferences.clear();
            }
            constructorsReferences.clear();
        }

        private void doReport(@NotNull ProblemsHolder holder, @NotNull List<FieldReference> fields) {
            fields.stream().filter(reference -> {
                final PsiElement parent = reference.getParent();
                if (OpenapiTypesUtil.isAssignment(parent)) {
                    return reference == ((AssignmentExpression) parent).getVariable();
                }
                return false;
            }).forEach(reference -> holder.registerProblem(reference, MessagesPresentationUtil.prefixWithEa(message)));
        }
    };
}
Also used : java.util(java.util) 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) PhpDocTag(com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag) PsiTreeUtil(com.intellij.psi.util.PsiTreeUtil) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) OpenapiTypesUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiTypesUtil) OpenapiResolveUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.OpenapiResolveUtil) 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) NotNull(org.jetbrains.annotations.NotNull) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) PhpDocTag(com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 77 with BasePhpElementVisitor

use of com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor in project phpinspectionsea by kalessil.

the class LoopWhichDoesNotLoopInspector method buildVisitor.

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

        @Override
        public void visitPhpForeach(@NotNull ForeachStatement loop) {
            final boolean isBlade = holder.getFile().getName().endsWith(".blade.php");
            if (!isBlade && this.isNotLooping(loop)) {
                /* false-positive: return first element from generator, iterable and co */
                final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(loop);
                final PsiElement last = body == null ? null : ExpressionSemanticUtil.getLastStatement(body);
                if (last != null && !OpenapiTypesUtil.isThrowExpression(last)) {
                    final PsiElement source = loop.getArray();
                    if (source instanceof PhpTypedElement) {
                        final PhpType resolved = OpenapiResolveUtil.resolveType((PhpTypedElement) source, holder.getProject());
                        final boolean isValid = resolved != null && resolved.filterUnknown().getTypes().stream().anyMatch(type -> foreachExceptions.contains(Types.getType(type)));
                        if (isValid) {
                            return;
                        }
                    }
                }
                holder.registerProblem(loop.getFirstChild(), MessagesPresentationUtil.prefixWithEa(message));
            }
        }

        @Override
        public void visitPhpFor(@NotNull For loop) {
            final boolean isBlade = holder.getFile().getName().endsWith(".blade.php");
            if (!isBlade && this.isNotLooping(loop)) {
                holder.registerProblem(loop.getFirstChild(), MessagesPresentationUtil.prefixWithEa(message));
            }
        }

        @Override
        public void visitPhpWhile(@NotNull While loop) {
            final boolean isBlade = holder.getFile().getName().endsWith(".blade.php");
            if (!isBlade && this.isNotLooping(loop)) {
                holder.registerProblem(loop.getFirstChild(), MessagesPresentationUtil.prefixWithEa(message));
            }
        }

        @Override
        public void visitPhpDoWhile(@NotNull DoWhile loop) {
            final boolean isBlade = holder.getFile().getName().endsWith(".blade.php");
            if (!isBlade && this.isNotLooping(loop)) {
                holder.registerProblem(loop.getFirstChild(), MessagesPresentationUtil.prefixWithEa(message));
            }
        }

        private boolean isNotLooping(@NotNull PhpPsiElement loop) {
            final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(loop);
            if (null == body) {
                return false;
            }
            final PsiElement lastExpression = ExpressionSemanticUtil.getLastStatement(body);
            final boolean isLoopTerminatedWithLastExpression = lastExpression instanceof PhpBreak || lastExpression instanceof PhpReturn || OpenapiTypesUtil.isThrowExpression(lastExpression);
            /* loop is empty or terminates on first iteration */
            if (null != lastExpression && !isLoopTerminatedWithLastExpression) {
                return false;
            }
            /* detect continue statements, which makes the loop looping */
            for (final PhpContinue expression : PsiTreeUtil.findChildrenOfType(body, PhpContinue.class)) {
                int nestingLevel = 0;
                PsiElement parent = expression.getParent();
                while (null != parent && !(parent instanceof Function) && !(parent instanceof PsiFile)) {
                    if (OpenapiTypesUtil.isLoop(parent)) {
                        ++nestingLevel;
                        if (parent == loop) {
                            /* extract level of continuation from the statement */
                            int continueLevel = 1;
                            final PsiElement argument = expression.getArgument();
                            if (null != argument) {
                                try {
                                    continueLevel = Integer.parseInt(argument.getText());
                                } catch (final NumberFormatException notParsed) {
                                    continueLevel = 1;
                                }
                            }
                            /* matched continue for the current loop */
                            if (continueLevel == nestingLevel) {
                                return false;
                            }
                        }
                    }
                    parent = parent.getParent();
                }
            }
            return true;
        }
    };
}
Also used : BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) com.jetbrains.php.lang.psi.elements(com.jetbrains.php.lang.psi.elements) Set(java.util.Set) HashSet(java.util.HashSet) PsiTreeUtil(com.intellij.psi.util.PsiTreeUtil) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) com.kalessil.phpStorm.phpInspectionsEA.utils(com.kalessil.phpStorm.phpInspectionsEA.utils) PsiElement(com.intellij.psi.PsiElement) PsiFile(com.intellij.psi.PsiFile) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) NotNull(org.jetbrains.annotations.NotNull) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) NotNull(org.jetbrains.annotations.NotNull) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PsiFile(com.intellij.psi.PsiFile) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 78 with BasePhpElementVisitor

use of com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor in project phpinspectionsea by kalessil.

the class DeprecatedConstructorStyleInspector method buildVisitor.

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

        @Override
        public void visitPhpMethod(@NotNull Method method) {
            if (!method.isStatic()) {
                final PhpClass clazz = method.getContainingClass();
                final PsiElement nameNode = NamedElementUtil.getNameIdentifier(method);
                if (clazz != null && nameNode != null && !clazz.isTrait() && !clazz.isInterface()) {
                    final String className = clazz.getName();
                    if (className.equals(method.getName()) && clazz.findOwnMethodByName("__construct") == null) {
                        holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(String.format(messagePattern, className)), ProblemHighlightType.LIKE_DEPRECATED, new RenameConstructorFix());
                    }
                }
            }
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PhpClass(com.jetbrains.php.lang.psi.elements.PhpClass) Method(com.jetbrains.php.lang.psi.elements.Method) NotNull(org.jetbrains.annotations.NotNull) LeafPsiElement(com.intellij.psi.impl.source.tree.LeafPsiElement) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Example 79 with BasePhpElementVisitor

use of com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor in project phpinspectionsea by kalessil.

the class PropertyCanBeStaticInspector method buildVisitor.

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

        @Override
        public void visitPhpField(@NotNull Field field) {
            final PsiElement nameNode = NamedElementUtil.getNameIdentifier(field);
            if (nameNode != null) {
                final PhpModifier modifier = field.getModifier();
                if (!field.isConstant() && !modifier.isStatic() && !modifier.isPublic()) {
                    final PsiElement defaultValue = field.getDefaultValue();
                    if (defaultValue instanceof ArrayCreationExpression) {
                        final PhpClass clazz = field.getContainingClass();
                        if (clazz != null) {
                            final PhpClass parent = OpenapiResolveUtil.resolveSuperClass(clazz);
                            if (parent == null || OpenapiResolveUtil.resolveField(parent, field.getName()) == null) {
                                int intArrayOrStringCount = 0;
                                for (final PsiElement entry : defaultValue.getChildren()) {
                                    PhpPsiElement item = null;
                                    if (entry instanceof ArrayHashElement) {
                                        item = ((ArrayHashElement) entry).getValue();
                                    } else if (entry instanceof PhpPsiElement) {
                                        item = ((PhpPsiElement) entry).getFirstPsiChild();
                                    }
                                    if (item instanceof ArrayCreationExpression || item instanceof StringLiteralExpression) {
                                        if (++intArrayOrStringCount == 3 && !this.isSuppressed(field)) {
                                            final boolean canUseConstants = PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP560);
                                            holder.registerProblem(nameNode, MessagesPresentationUtil.prefixWithEa(canUseConstants ? messageWithConstants : messageNoConstants));
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        private boolean isSuppressed(@NotNull PsiElement expression) {
            final PsiElement parent = expression.getParent();
            if (parent.getParent() instanceof PhpClass) {
                final PsiElement previous = ((PhpPsiElement) parent).getPrevPsiSibling();
                if (previous instanceof PhpDocComment) {
                    final String candidate = previous.getText();
                    if (candidate.contains("@noinspection") && candidate.contains(getShortName())) {
                        return true;
                    }
                }
            }
            return false;
        }
    };
}
Also used : 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) NotNull(org.jetbrains.annotations.NotNull)

Example 80 with BasePhpElementVisitor

use of com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor in project phpinspectionsea by kalessil.

the class SenselessProxyMethodInspector method buildVisitor.

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

        @Override
        public void visitPhpClass(@NotNull PhpClass clazz) {
            if (clazz.isInterface() || clazz.isTrait()) {
                return;
            }
            for (final Method method : clazz.getOwnMethods()) {
                final PsiElement methodNameNode = NamedElementUtil.getNameIdentifier(method);
                if (null == methodNameNode || method.isAbstract() || method.getAccess().isPrivate()) {
                    continue;
                }
                /* we expect the method to have just one expression - parent invocation */
                final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(method);
                if (null == body || 1 != ExpressionSemanticUtil.countExpressionsInGroup(body)) {
                    continue;
                }
                final PsiElement lastStatement = ExpressionSemanticUtil.getLastStatement(body);
                if (null == lastStatement) {
                    continue;
                }
                /* parent invocation can be both direct or via return */
                final PsiElement parentReferenceCandidate;
                if (lastStatement instanceof PhpReturn) {
                    parentReferenceCandidate = ExpressionSemanticUtil.getReturnValue((PhpReturn) lastStatement);
                } else {
                    parentReferenceCandidate = lastStatement.getFirstChild();
                }
                if (!(parentReferenceCandidate instanceof MethodReference)) {
                    continue;
                }
                final MethodReference reference = (MethodReference) parentReferenceCandidate;
                final String referenceVariable = reference.getFirstChild().getText().trim();
                final String referenceName = reference.getName();
                if (null == referenceName || !referenceVariable.equals("parent") || !referenceName.equals(method.getName())) {
                    continue;
                }
                final Parameter[] methodParameters = method.getParameters();
                /* ensure no transformations/reordering happens when dispatching parameters */
                final PsiElement[] givenParams = reference.getParameters();
                boolean isDispatchingWithoutModifications = (givenParams.length == methodParameters.length);
                if (isDispatchingWithoutModifications) {
                    /* ensure parameters re-dispatched in the same order and state */
                    for (int index = 0; index < givenParams.length; ++index) {
                        if (!(givenParams[index] instanceof Variable) || !((Variable) givenParams[index]).getName().equals(methodParameters[index].getName())) {
                            isDispatchingWithoutModifications = false;
                            break;
                        }
                    }
                }
                /* ensure no signature changes took place */
                boolean isChangingSignature = false;
                final PsiReference referenceToMethod = reference.getReference();
                if (null != referenceToMethod && isDispatchingWithoutModifications) {
                    final PsiElement referenceResolved = OpenapiResolveUtil.resolveReference(referenceToMethod);
                    if (referenceResolved instanceof Method) {
                        final Method nestedMethod = (Method) referenceResolved;
                        final Parameter[] parentParameters = nestedMethod.getParameters();
                        /* verify amount of parameters, visibility, static, abstract, final */
                        if (parentParameters.length == methodParameters.length && nestedMethod.isAbstract() == method.isAbstract() && nestedMethod.isStatic() == method.isStatic() && nestedMethod.isFinal() == method.isFinal() && nestedMethod.getAccess().equals(method.getAccess())) {
                            /* analyze if parameters definition has been changed (only ignore naming changes) */
                            if (methodParameters.length > 0) {
                                for (int index = 0; index < parentParameters.length; ++index) {
                                    /* by-reference declaration changes: not allowed by PHP, hence not checked */
                                    /* default values changes */
                                    final PsiElement parentDefault = parentParameters[index].getDefaultValue();
                                    final PsiElement methodDefault = methodParameters[index].getDefaultValue();
                                    if ((parentDefault == null || methodDefault == null) && parentDefault != methodDefault) {
                                        isChangingSignature = true;
                                        break;
                                    }
                                    if (methodDefault != null && !OpenapiEquivalenceUtil.areEqual(parentDefault, methodDefault)) {
                                        isChangingSignature = true;
                                        break;
                                    }
                                    /* false-positive: magic constants ARE changing signature  */
                                    if (methodDefault instanceof ConstantReference) {
                                        final String constant = ((ConstantReference) methodDefault).getName();
                                        if (constants.contains(constant)) {
                                            isChangingSignature = true;
                                            break;
                                        }
                                    }
                                    /* type definition changes */
                                    final PhpType parentType = OpenapiResolveUtil.resolveDeclaredType(parentParameters[index]);
                                    final PhpType methodType = OpenapiResolveUtil.resolveDeclaredType(methodParameters[index]);
                                    if (!parentType.equals(methodType)) {
                                        isChangingSignature = true;
                                        break;
                                    }
                                }
                            }
                            /* verify returned type declaration */
                            if (!isChangingSignature) {
                                final PsiElement methodReturn = OpenapiElementsUtil.getReturnType(method);
                                final PsiElement parentReturn = OpenapiElementsUtil.getReturnType(nestedMethod);
                                if (methodReturn != parentReturn) {
                                    isChangingSignature = methodReturn == null || parentReturn == null || !OpenapiEquivalenceUtil.areEqual(methodReturn, parentReturn);
                                }
                            }
                        } else {
                            /* okay obviously changed */
                            isChangingSignature = true;
                        }
                    } else {
                        /* we couldn't resolve parent, so we can't report anything */
                        isChangingSignature = true;
                    }
                }
                /* decide if need to report any issues */
                if (isDispatchingWithoutModifications && !isChangingSignature) {
                    holder.registerProblem(methodNameNode, MessagesPresentationUtil.prefixWithEa(messagePattern.replace("%s%", method.getName())), ProblemHighlightType.WEAK_WARNING, new DropMethodFix());
                }
            }
        }
    };
}
Also used : PsiReference(com.intellij.psi.PsiReference) NotNull(org.jetbrains.annotations.NotNull) PhpType(com.jetbrains.php.lang.psi.resolve.types.PhpType) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) DropMethodFix(com.kalessil.phpStorm.phpInspectionsEA.fixers.DropMethodFix) PsiElement(com.intellij.psi.PsiElement) NotNull(org.jetbrains.annotations.NotNull)

Aggregations

BasePhpElementVisitor (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor)169 NotNull (org.jetbrains.annotations.NotNull)169 PsiElement (com.intellij.psi.PsiElement)157 FunctionReference (com.jetbrains.php.lang.psi.elements.FunctionReference)43 ProblemsHolder (com.intellij.codeInspection.ProblemsHolder)40 BasePhpInspection (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection)39 PsiElementVisitor (com.intellij.psi.PsiElementVisitor)37 PhpType (com.jetbrains.php.lang.psi.resolve.types.PhpType)33 IElementType (com.intellij.psi.tree.IElementType)32 com.jetbrains.php.lang.psi.elements (com.jetbrains.php.lang.psi.elements)29 Project (com.intellij.openapi.project.Project)25 BinaryExpression (com.jetbrains.php.lang.psi.elements.BinaryExpression)25 HashSet (java.util.HashSet)24 PsiTreeUtil (com.intellij.psi.util.PsiTreeUtil)22 MessagesPresentationUtil (com.kalessil.phpStorm.phpInspectionsEA.utils.MessagesPresentationUtil)19 StringLiteralExpression (com.jetbrains.php.lang.psi.elements.StringLiteralExpression)18 ArrayList (java.util.ArrayList)18 Set (java.util.Set)18 Nullable (org.jetbrains.annotations.Nullable)18 PhpTokenTypes (com.jetbrains.php.lang.lexer.PhpTokenTypes)17