001    /*
002     * Copyright 2010-2016 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.kotlin.psi;
018    
019    import com.google.common.base.Predicate;
020    import com.google.common.collect.Lists;
021    import com.intellij.lang.ASTNode;
022    import com.intellij.psi.PsiComment;
023    import com.intellij.psi.PsiElement;
024    import com.intellij.psi.PsiFile;
025    import com.intellij.psi.PsiWhiteSpace;
026    import com.intellij.psi.tree.IElementType;
027    import com.intellij.psi.tree.TokenSet;
028    import com.intellij.psi.util.PsiTreeUtil;
029    import com.intellij.util.codeInsight.CommentUtilCore;
030    import org.jetbrains.annotations.Contract;
031    import org.jetbrains.annotations.NotNull;
032    import org.jetbrains.annotations.Nullable;
033    import org.jetbrains.kotlin.KtNodeTypes;
034    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
035    import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
036    import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor;
037    import org.jetbrains.kotlin.kdoc.psi.api.KDocElement;
038    import org.jetbrains.kotlin.lexer.KtToken;
039    import org.jetbrains.kotlin.lexer.KtTokens;
040    import org.jetbrains.kotlin.name.Name;
041    import org.jetbrains.kotlin.name.SpecialNames;
042    import org.jetbrains.kotlin.parsing.KotlinExpressionParsing;
043    import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
044    import org.jetbrains.kotlin.resolve.StatementFilter;
045    import org.jetbrains.kotlin.resolve.StatementFilterKt;
046    import org.jetbrains.kotlin.types.expressions.OperatorConventions;
047    
048    import java.util.Collection;
049    import java.util.HashSet;
050    import java.util.List;
051    import java.util.Set;
052    
053    public class KtPsiUtil {
054        private KtPsiUtil() {
055        }
056    
057        public interface KtExpressionWrapper {
058            KtExpression getBaseExpression();
059        }
060    
061        public static <D> void visitChildren(@NotNull KtElement element, @NotNull KtVisitor<Void, D> visitor, D data) {
062            PsiElement child = element.getFirstChild();
063            while (child != null) {
064                if (child instanceof KtElement) {
065                    ((KtElement) child).accept(visitor, data);
066                }
067                child = child.getNextSibling();
068            }
069        }
070    
071        @NotNull
072        public static KtExpression safeDeparenthesize(@NotNull KtExpression expression) {
073            KtExpression deparenthesized = deparenthesize(expression);
074            return deparenthesized != null ? deparenthesized : expression;
075        }
076    
077        @Nullable
078        public static KtExpression deparenthesize(@Nullable KtExpression expression ) {
079            while (true) {
080                KtExpression baseExpression = deparenthesizeOnce(expression);
081    
082                if (baseExpression == expression) return baseExpression;
083                expression = baseExpression;
084            }
085        }
086    
087        @Nullable
088        public static KtExpression deparenthesizeOnce(
089                @Nullable KtExpression expression
090        ) {
091            if (expression instanceof KtAnnotatedExpression) {
092                return ((KtAnnotatedExpression) expression).getBaseExpression();
093            }
094            else if (expression instanceof KtLabeledExpression) {
095                return ((KtLabeledExpression) expression).getBaseExpression();
096            }
097            else if (expression instanceof KtExpressionWrapper) {
098                return ((KtExpressionWrapper) expression).getBaseExpression();
099            }
100            else if (expression instanceof KtParenthesizedExpression) {
101                return ((KtParenthesizedExpression) expression).getExpression();
102            }
103            return expression;
104        }
105    
106        @NotNull
107        public static Name safeName(@Nullable String name) {
108            return name == null ? SpecialNames.NO_NAME_PROVIDED : Name.identifier(name);
109        }
110    
111        @NotNull
112        public static Set<KtElement> findRootExpressions(@NotNull Collection<KtElement> unreachableElements) {
113            Set<KtElement> rootElements = new HashSet<KtElement>();
114            final Set<KtElement> shadowedElements = new HashSet<KtElement>();
115            KtVisitorVoid shadowAllChildren = new KtVisitorVoid() {
116                @Override
117                public void visitKtElement(@NotNull KtElement element) {
118                    if (shadowedElements.add(element)) {
119                        element.acceptChildren(this);
120                    }
121                }
122            };
123    
124            for (KtElement element : unreachableElements) {
125                if (shadowedElements.contains(element)) continue;
126                element.acceptChildren(shadowAllChildren);
127    
128                rootElements.removeAll(shadowedElements);
129                rootElements.add(element);
130            }
131            return rootElements;
132        }
133    
134        @NotNull
135        public static String unquoteIdentifier(@NotNull String quoted) {
136            if (quoted.indexOf('`') < 0) {
137                return quoted;
138            }
139    
140            if (quoted.startsWith("`") && quoted.endsWith("`") && quoted.length() >= 2) {
141                return quoted.substring(1, quoted.length() - 1);
142            }
143            else {
144                return quoted;
145            }
146        }
147    
148        @NotNull
149        public static String unquoteIdentifierOrFieldReference(@NotNull String quoted) {
150            if (quoted.indexOf('`') < 0) {
151                return quoted;
152            }
153    
154            if (quoted.startsWith("$")) {
155                return "$" + unquoteIdentifier(quoted.substring(1));
156            }
157            else {
158                return unquoteIdentifier(quoted);
159            }
160        }
161    
162        @Nullable
163        public static Name getShortName(@NotNull KtAnnotationEntry annotation) {
164            KtTypeReference typeReference = annotation.getTypeReference();
165            assert typeReference != null : "Annotation entry hasn't typeReference " + annotation.getText();
166            KtTypeElement typeElement = typeReference.getTypeElement();
167            if (typeElement instanceof KtUserType) {
168                KtUserType userType = (KtUserType) typeElement;
169                String shortName = userType.getReferencedName();
170                if (shortName != null) {
171                    return Name.identifier(shortName);
172                }
173            }
174            return null;
175        }
176    
177        public static boolean isDeprecated(@NotNull KtModifierListOwner owner) {
178            KtModifierList modifierList = owner.getModifierList();
179            if (modifierList != null) {
180                List<KtAnnotationEntry> annotationEntries = modifierList.getAnnotationEntries();
181                for (KtAnnotationEntry annotation : annotationEntries) {
182                    Name shortName = getShortName(annotation);
183                    if (KotlinBuiltIns.FQ_NAMES.deprecated.shortName().equals(shortName)) {
184                        return true;
185                    }
186                }
187            }
188            return false;
189        }
190    
191        @Nullable
192        public static <T extends PsiElement> T getDirectParentOfTypeForBlock(@NotNull KtBlockExpression block, @NotNull Class<T> aClass) {
193            T parent = PsiTreeUtil.getParentOfType(block, aClass);
194            if (parent instanceof KtIfExpression) {
195                KtIfExpression ifExpression = (KtIfExpression) parent;
196                if (ifExpression.getElse() == block || ifExpression.getThen() == block) {
197                    return parent;
198                }
199            }
200            if (parent instanceof KtWhenExpression) {
201                KtWhenExpression whenExpression = (KtWhenExpression) parent;
202                for (KtWhenEntry whenEntry : whenExpression.getEntries()) {
203                    if (whenEntry.getExpression() == block) {
204                        return parent;
205                    }
206                }
207            }
208            if (parent instanceof KtFunctionLiteral) {
209                KtFunctionLiteral functionLiteral = (KtFunctionLiteral) parent;
210                if (functionLiteral.getBodyExpression() == block) {
211                    return parent;
212                }
213            }
214            if (parent instanceof KtTryExpression) {
215                KtTryExpression tryExpression = (KtTryExpression) parent;
216                if (tryExpression.getTryBlock() == block) {
217                    return parent;
218                }
219                for (KtCatchClause clause : tryExpression.getCatchClauses()) {
220                    if (clause.getCatchBody() == block) {
221                        return parent;
222                    }
223                }
224            }
225            return null;
226        }
227    
228        @Nullable
229        public static Name getAliasName(@NotNull KtImportDirective importDirective) {
230            if (importDirective.isAllUnder()) {
231                return null;
232            }
233            String aliasName = importDirective.getAliasName();
234            KtExpression importedReference = importDirective.getImportedReference();
235            if (importedReference == null) {
236                return null;
237            }
238            KtSimpleNameExpression referenceExpression = getLastReference(importedReference);
239            if (aliasName == null) {
240                aliasName = referenceExpression != null ? referenceExpression.getReferencedName() : null;
241            }
242    
243            return aliasName != null && !aliasName.isEmpty() ? Name.identifier(aliasName) : null;
244        }
245    
246        @Nullable
247        public static KtSimpleNameExpression getLastReference(@NotNull KtExpression importedReference) {
248            KtElement selector = KtPsiUtilKt.getQualifiedElementSelector(importedReference);
249            return selector instanceof KtSimpleNameExpression ? (KtSimpleNameExpression) selector : null;
250        }
251    
252        public static boolean isSelectorInQualified(@NotNull KtSimpleNameExpression nameExpression) {
253            KtElement qualifiedElement = KtPsiUtilKt.getQualifiedElement(nameExpression);
254            return qualifiedElement instanceof KtQualifiedExpression
255                   || ((qualifiedElement instanceof KtUserType) && ((KtUserType) qualifiedElement).getQualifier() != null);
256        }
257    
258        public static boolean isLHSOfDot(@NotNull KtExpression expression) {
259            PsiElement parent = expression.getParent();
260            if (!(parent instanceof KtQualifiedExpression)) return false;
261            KtQualifiedExpression qualifiedParent = (KtQualifiedExpression) parent;
262            return qualifiedParent.getReceiverExpression() == expression || isLHSOfDot(qualifiedParent);
263        }
264    
265        public static boolean isScriptDeclaration(@NotNull KtDeclaration namedDeclaration) {
266            return getScript(namedDeclaration) != null;
267        }
268    
269        @Nullable
270        public static KtScript getScript(@NotNull KtDeclaration namedDeclaration) {
271            PsiElement parent = namedDeclaration.getParent();
272            if (parent != null && parent.getParent() instanceof KtScript) {
273                return (KtScript) parent.getParent();
274            }
275            else {
276                return null;
277            }
278        }
279    
280        public static boolean isVariableNotParameterDeclaration(@NotNull KtDeclaration declaration) {
281            if (!(declaration instanceof KtVariableDeclaration)) return false;
282            if (declaration instanceof KtProperty) return true;
283            assert declaration instanceof KtDestructuringDeclarationEntry;
284            KtDestructuringDeclarationEntry multiDeclarationEntry = (KtDestructuringDeclarationEntry) declaration;
285            return !(multiDeclarationEntry.getParent().getParent() instanceof KtForExpression);
286        }
287    
288        @Nullable
289        public static Name getConventionName(@NotNull KtSimpleNameExpression simpleNameExpression) {
290            if (simpleNameExpression.getIdentifier() != null) {
291                return simpleNameExpression.getReferencedNameAsName();
292            }
293    
294            PsiElement firstChild = simpleNameExpression.getFirstChild();
295            if (firstChild != null) {
296                IElementType elementType = firstChild.getNode().getElementType();
297                if (elementType instanceof KtToken) {
298                    KtToken jetToken = (KtToken) elementType;
299                    boolean isPrefixExpression = simpleNameExpression.getParent() instanceof KtPrefixExpression;
300                    if (isPrefixExpression) {
301                        return OperatorConventions.getNameForOperationSymbol(jetToken, true, false);
302                    }
303                    return OperatorConventions.getNameForOperationSymbol(jetToken);
304                }
305            }
306    
307            return null;
308        }
309    
310        @Nullable
311        @Contract("null, _ -> null")
312        public static PsiElement getTopmostParentOfTypes(
313                @Nullable PsiElement element,
314                @NotNull Class<? extends PsiElement>... parentTypes) {
315            if (element instanceof PsiFile) return null;
316    
317            PsiElement answer = PsiTreeUtil.getParentOfType(element, parentTypes);
318            if (answer instanceof PsiFile) return answer;
319    
320            do {
321                PsiElement next = PsiTreeUtil.getParentOfType(answer, parentTypes);
322                if (next == null) break;
323                answer = next;
324            }
325            while (true);
326    
327            return answer;
328        }
329    
330        public static boolean isNullConstant(@NotNull KtExpression expression) {
331            KtExpression deparenthesized = deparenthesize(expression);
332            return deparenthesized instanceof KtConstantExpression && deparenthesized.getNode().getElementType() == KtNodeTypes.NULL;
333        }
334    
335        public static boolean isTrueConstant(@Nullable KtExpression condition) {
336            return (condition != null && condition.getNode().getElementType() == KtNodeTypes.BOOLEAN_CONSTANT &&
337                    condition.getNode().findChildByType(KtTokens.TRUE_KEYWORD) != null);
338        }
339    
340        public static boolean isAbstract(@NotNull KtDeclarationWithBody declaration) {
341            return declaration.getBodyExpression() == null;
342        }
343    
344        public static boolean isBackingFieldReference(@Nullable DeclarationDescriptor descriptor) {
345            return descriptor instanceof SyntheticFieldDescriptor;
346        }
347    
348        @Nullable
349        public static KtExpression getExpressionOrLastStatementInBlock(@Nullable KtExpression expression) {
350            if (expression instanceof KtBlockExpression) {
351                return getLastStatementInABlock((KtBlockExpression) expression);
352            }
353            return expression;
354        }
355    
356        @Nullable
357        public static KtExpression getLastStatementInABlock(@Nullable KtBlockExpression blockExpression) {
358            if (blockExpression == null) return null;
359            List<KtExpression> statements = blockExpression.getStatements();
360            return statements.isEmpty() ? null : statements.get(statements.size() - 1);
361        }
362    
363        public static boolean isTrait(@NotNull KtClassOrObject classOrObject) {
364            return classOrObject instanceof KtClass && ((KtClass) classOrObject).isInterface();
365        }
366    
367        @Nullable
368        public static KtClassOrObject getOutermostClassOrObject(@NotNull KtClassOrObject classOrObject) {
369            KtClassOrObject current = classOrObject;
370            while (true) {
371                PsiElement parent = current.getParent();
372                assert classOrObject.getParent() != null : "Class with no parent: " + classOrObject.getText();
373    
374                if (parent instanceof PsiFile) {
375                    return current;
376                }
377                if (!(parent instanceof KtClassBody)) {
378                    // It is a local class, no legitimate outer
379                    return current;
380                }
381    
382                current = (KtClassOrObject) parent.getParent();
383            }
384        }
385    
386        @Nullable
387        public static KtClassOrObject getClassIfParameterIsProperty(@NotNull KtParameter jetParameter) {
388            if (jetParameter.hasValOrVar()) {
389                PsiElement grandParent = jetParameter.getParent().getParent();
390                if (grandParent instanceof KtPrimaryConstructor) {
391                    return ((KtPrimaryConstructor) grandParent).getContainingClassOrObject();
392                }
393            }
394    
395            return null;
396        }
397    
398        @Nullable
399        private static IElementType getOperation(@NotNull KtExpression expression) {
400            if (expression instanceof KtQualifiedExpression) {
401                return ((KtQualifiedExpression) expression).getOperationSign();
402            }
403            else if (expression instanceof KtOperationExpression) {
404                return ((KtOperationExpression) expression).getOperationReference().getReferencedNameElementType();
405            }
406            return null;
407        }
408    
409    
410        private static int getPriority(@NotNull KtExpression expression) {
411            int maxPriority = KotlinExpressionParsing.Precedence.values().length + 1;
412    
413            // same as postfix operations
414            if (expression instanceof KtPostfixExpression ||
415                expression instanceof KtQualifiedExpression ||
416                expression instanceof KtCallExpression ||
417                expression instanceof KtArrayAccessExpression) {
418                return maxPriority - 1;
419            }
420    
421            if (expression instanceof KtPrefixExpression || expression instanceof KtLabeledExpression) return maxPriority - 2;
422    
423            if (expression instanceof KtIfExpression) {
424                return KotlinExpressionParsing.Precedence.ASSIGNMENT.ordinal();
425            }
426    
427            if (expression instanceof KtSuperExpression) {
428                return maxPriority;
429            }
430    
431            if (expression instanceof KtDeclaration || expression instanceof KtStatementExpression) {
432                return 0;
433            }
434    
435            IElementType operation = getOperation(expression);
436            for (KotlinExpressionParsing.Precedence precedence : KotlinExpressionParsing.Precedence.values()) {
437                if (precedence != KotlinExpressionParsing.Precedence.PREFIX && precedence != KotlinExpressionParsing.Precedence.POSTFIX &&
438                    precedence.getOperations().contains(operation)) {
439                    return maxPriority - precedence.ordinal() - 1;
440                }
441            }
442    
443            return maxPriority;
444        }
445    
446        public static boolean areParenthesesUseless(@NotNull KtParenthesizedExpression expression) {
447            KtExpression innerExpression = expression.getExpression();
448            if (innerExpression == null) return true;
449    
450            PsiElement parent = expression.getParent();
451            if (!(parent instanceof KtExpression)) return true;
452    
453            return !areParenthesesNecessary(innerExpression, expression, (KtExpression) parent);
454        }
455    
456        public static boolean areParenthesesNecessary(
457                @NotNull KtExpression innerExpression,
458                @NotNull KtExpression currentInner,
459                @NotNull KtElement parentElement
460        ) {
461            if (parentElement instanceof KtParenthesizedExpression || innerExpression instanceof KtParenthesizedExpression) {
462                return false;
463            }
464    
465            if (parentElement instanceof KtPackageDirective) return false;
466    
467            if (parentElement instanceof KtWhenExpression || innerExpression instanceof KtWhenExpression) {
468                return false;
469            }
470    
471            if (innerExpression instanceof KtIfExpression) {
472                if (parentElement instanceof KtQualifiedExpression) return true;
473    
474                PsiElement current = parentElement;
475    
476                while (!(current instanceof KtBlockExpression || current instanceof KtDeclaration || current instanceof KtStatementExpression || current instanceof KtFile)) {
477                    if (current.getTextRange().getEndOffset() != currentInner.getTextRange().getEndOffset()) {
478                        return !(current instanceof KtParenthesizedExpression) && !(current instanceof KtValueArgumentList); // if current expression is "guarded" by parenthesis, no extra parenthesis is necessary
479                    }
480    
481                    current = current.getParent();
482                }
483            }
484    
485            if (parentElement instanceof KtCallExpression && currentInner == ((KtCallExpression) parentElement).getCalleeExpression()) {
486                if (innerExpression instanceof KtSimpleNameExpression) return false;
487                if (KtPsiUtilKt.getQualifiedExpressionForSelector(parentElement) != null) return true;
488                return !(innerExpression instanceof KtThisExpression
489                         || innerExpression instanceof KtArrayAccessExpression
490                         || innerExpression instanceof KtConstantExpression
491                         || innerExpression instanceof KtStringTemplateExpression
492                         || innerExpression instanceof KtCallExpression);
493            }
494    
495            if (parentElement instanceof KtValueArgument) {
496                // a(___, d > (e + f)) => a((b < c), d > (e + f)) to prevent parsing < c, d > as type argument list
497                KtValueArgument nextArg = PsiTreeUtil.getNextSiblingOfType(parentElement, KtValueArgument.class);
498                PsiElement nextExpression = nextArg != null ? nextArg.getArgumentExpression() : null;
499                if (innerExpression instanceof KtBinaryExpression &&
500                    ((KtBinaryExpression) innerExpression).getOperationToken() == KtTokens.LT &&
501                    nextExpression instanceof KtBinaryExpression &&
502                    ((KtBinaryExpression) nextExpression).getOperationToken() == KtTokens.GT) return true;
503            }
504    
505            if (!(parentElement instanceof KtExpression)) return false;
506    
507            IElementType innerOperation = getOperation(innerExpression);
508            IElementType parentOperation = getOperation((KtExpression) parentElement);
509    
510            // 'return (@label{...})' case
511            if (parentElement instanceof KtReturnExpression
512                && (innerExpression instanceof KtLabeledExpression || innerExpression instanceof KtAnnotatedExpression)) return true;
513    
514            // '(x: Int) < y' case
515            if (innerExpression instanceof KtBinaryExpressionWithTypeRHS && parentOperation == KtTokens.LT) {
516                return true;
517            }
518    
519            if (parentElement instanceof KtLabeledExpression) return false;
520    
521            // 'x ?: ...' case
522            if (parentElement instanceof KtBinaryExpression && parentOperation == KtTokens.ELVIS && currentInner == ((KtBinaryExpression) parentElement).getRight()) {
523                return false;
524            }
525    
526            int innerPriority = getPriority(innerExpression);
527            int parentPriority = getPriority((KtExpression) parentElement);
528    
529            if (innerPriority == parentPriority) {
530                if (parentElement instanceof KtBinaryExpression) {
531                    if (innerOperation == KtTokens.ANDAND || innerOperation == KtTokens.OROR) {
532                        return false;
533                    }
534                    return ((KtBinaryExpression) parentElement).getRight() == currentInner;
535                }
536    
537                //'-(-x)' case
538                if (parentElement instanceof KtPrefixExpression && innerExpression instanceof KtPrefixExpression) {
539                    return innerOperation == parentOperation && (innerOperation == KtTokens.PLUS || innerOperation == KtTokens.MINUS);
540                }
541                return false;
542            }
543    
544            return innerPriority < parentPriority;
545        }
546    
547        public static boolean isAssignment(@NotNull PsiElement element) {
548            return element instanceof KtBinaryExpression &&
549                   KtTokens.ALL_ASSIGNMENTS.contains(((KtBinaryExpression) element).getOperationToken());
550        }
551    
552        public static boolean isOrdinaryAssignment(@NotNull PsiElement element) {
553            return element instanceof KtBinaryExpression &&
554                   ((KtBinaryExpression) element).getOperationToken().equals(KtTokens.EQ);
555        }
556    
557        public static boolean checkVariableDeclarationInBlock(@NotNull KtBlockExpression block, @NotNull String varName) {
558            for (KtExpression element : block.getStatements()) {
559                if (element instanceof KtVariableDeclaration) {
560                    if (((KtVariableDeclaration) element).getNameAsSafeName().asString().equals(varName)) {
561                        return true;
562                    }
563                }
564            }
565    
566            return false;
567        }
568    
569        public static boolean checkWhenExpressionHasSingleElse(@NotNull KtWhenExpression whenExpression) {
570            int elseCount = 0;
571            for (KtWhenEntry entry : whenExpression.getEntries()) {
572                if (entry.isElse()) {
573                    elseCount++;
574                }
575            }
576            return (elseCount == 1);
577        }
578    
579        @Nullable
580        public static PsiElement skipTrailingWhitespacesAndComments(@Nullable PsiElement element)  {
581            return PsiTreeUtil.skipSiblingsForward(element, PsiWhiteSpace.class, PsiComment.class);
582        }
583    
584        @Nullable
585        public static PsiElement prevLeafIgnoringWhitespaceAndComments(@NotNull PsiElement element) {
586            PsiElement prev = PsiTreeUtil.prevLeaf(element, true);
587            while (prev != null && KtTokens.WHITE_SPACE_OR_COMMENT_BIT_SET.contains(prev.getNode().getElementType())) {
588                prev = PsiTreeUtil.prevLeaf(prev, true);
589            }
590            return prev;
591        }
592    
593        /**
594         * Example:
595         *      code: async* {}
596         *      element = "{}"
597         *      word = "async"
598         *      suffixTokens = [+, -, *, /, %]
599         *
600         *      result = async
601         */
602        @Nullable
603        public static PsiElement getPreviousWord(@NotNull PsiElement element, @NotNull String word, @NotNull TokenSet suffixTokens) {
604            PsiElement prev = prevLeafIgnoringWhitespaceAndComments(element);
605            if (prev != null && suffixTokens.contains(prev.getNode().getElementType())) {
606                prev = PsiTreeUtil.prevLeaf(prev, false);
607            }
608            if (prev != null && prev.getNode().getElementType() == KtTokens.IDENTIFIER && word.equals(prev.getText())) {
609                return prev;
610            }
611    
612            return null;
613        }
614    
615        public static final Predicate<KtElement> ANY_JET_ELEMENT = new Predicate<KtElement>() {
616            @Override
617            public boolean apply(@Nullable KtElement input) {
618                return true;
619            }
620        };
621    
622        @NotNull
623        public static String getText(@Nullable PsiElement element) {
624            return element != null ? element.getText() : "";
625        }
626    
627        @Nullable
628        public static String getNullableText(@Nullable PsiElement element) {
629            return element != null ? element.getText() : null;
630        }
631    
632        /**
633         * CommentUtilCore.isComment fails if element <strong>inside</strong> comment.
634         *
635         * Also, we can not add KDocTokens to COMMENTS TokenSet, because it is used in KotlinParserDefinition.getCommentTokens(),
636         * and therefor all COMMENTS tokens will be ignored by PsiBuilder.
637         *
638         * @param element
639         * @return
640         */
641        public static boolean isInComment(PsiElement element) {
642            return CommentUtilCore.isComment(element) || element instanceof KDocElement;
643        }
644    
645        @Nullable
646        public static PsiElement getOutermostParent(@NotNull PsiElement element, @NotNull PsiElement upperBound, boolean strict) {
647            PsiElement parent = strict ? element.getParent() : element;
648            while (parent != null && parent.getParent() != upperBound) {
649                parent = parent.getParent();
650            }
651    
652            return parent;
653        }
654    
655        public static <T extends PsiElement> T getLastChildByType(@NotNull PsiElement root, @NotNull Class<? extends T>... elementTypes) {
656            PsiElement[] children = root.getChildren();
657    
658            for (int i = children.length - 1; i >= 0; i--) {
659                if (PsiTreeUtil.instanceOf(children[i], elementTypes)) {
660                    //noinspection unchecked
661                    return (T) children[i];
662                }
663            }
664    
665            return null;
666        }
667    
668        @Nullable
669        public static KtElement getOutermostDescendantElement(
670                @Nullable PsiElement root,
671                boolean first,
672                final @NotNull Predicate<KtElement> predicate
673        ) {
674            if (!(root instanceof KtElement)) return null;
675    
676            final List<KtElement> results = Lists.newArrayList();
677    
678            root.accept(
679                    new KtVisitorVoid() {
680                        @Override
681                        public void visitKtElement(@NotNull KtElement element) {
682                            if (predicate.apply(element)) {
683                                //noinspection unchecked
684                                results.add(element);
685                            }
686                            else {
687                                element.acceptChildren(this);
688                            }
689                        }
690                    }
691            );
692    
693            if (results.isEmpty()) return null;
694    
695            return first ? results.get(0) : results.get(results.size() - 1);
696        }
697    
698        @Nullable
699        public static PsiElement findChildByType(@NotNull PsiElement element, @NotNull IElementType type) {
700            ASTNode node = element.getNode().findChildByType(type);
701            return node == null ? null : node.getPsi();
702        }
703    
704        @Nullable
705        public static PsiElement skipSiblingsBackwardByPredicate(@Nullable PsiElement element, Predicate<PsiElement> elementsToSkip) {
706            if (element == null) return null;
707            for (PsiElement e = element.getPrevSibling(); e != null; e = e.getPrevSibling()) {
708                if (elementsToSkip.apply(e)) continue;
709                return e;
710            }
711            return null;
712        }
713    
714        public static PsiElement ascendIfPropertyAccessor(PsiElement element) {
715            if (element instanceof KtPropertyAccessor) {
716                return element.getParent();
717            }
718            return element;
719        }
720    
721        @Nullable
722        @Contract("_, !null -> !null")
723        public static KtModifierList replaceModifierList(@NotNull KtModifierListOwner owner, @Nullable KtModifierList modifierList) {
724            KtModifierList oldModifierList = owner.getModifierList();
725            if (modifierList == null) {
726                if (oldModifierList != null) oldModifierList.delete();
727                return null;
728            }
729            else {
730                if (oldModifierList == null) {
731                    PsiElement firstChild = owner.getFirstChild();
732                    return (KtModifierList) owner.addBefore(modifierList, firstChild);
733                }
734                else {
735                    return (KtModifierList) oldModifierList.replace(modifierList);
736                }
737            }
738        }
739    
740        @Nullable
741        public static String getPackageName(@NotNull KtElement element) {
742            KtFile file = element.getContainingKtFile();
743            KtPackageDirective header = PsiTreeUtil.findChildOfType(file, KtPackageDirective.class);
744    
745            return header != null ? header.getQualifiedName() : null;
746        }
747    
748        @Nullable
749        public static KtElement getEnclosingElementForLocalDeclaration(@NotNull KtDeclaration declaration) {
750            return getEnclosingElementForLocalDeclaration(declaration, true);
751        }
752    
753        private static boolean isMemberOfObjectExpression(@NotNull KtCallableDeclaration propertyOrFunction) {
754            PsiElement parent = PsiTreeUtil.getStubOrPsiParent(propertyOrFunction);
755            if (!(parent instanceof KtClassBody)) return false;
756            PsiElement grandparent = PsiTreeUtil.getStubOrPsiParent(parent);
757            if (!(grandparent instanceof KtObjectDeclaration)) return false;
758            return PsiTreeUtil.getStubOrPsiParent(grandparent) instanceof KtObjectLiteralExpression;
759        }
760    
761        @Nullable
762        public static KtElement getEnclosingElementForLocalDeclaration(@NotNull KtDeclaration declaration, boolean skipParameters) {
763            if (declaration instanceof KtTypeParameter && skipParameters) {
764                declaration = PsiTreeUtil.getParentOfType(declaration, KtNamedDeclaration.class);
765            }
766            else if (declaration instanceof KtParameter) {
767                KtFunctionType functionType = PsiTreeUtil.getParentOfType(declaration, KtFunctionType.class);
768                if (functionType != null) {
769                    return functionType;
770                }
771    
772                PsiElement parent = declaration.getParent();
773    
774                // val/var parameter of primary constructor should be considered as local according to containing class
775                if (((KtParameter) declaration).hasValOrVar() && parent != null && parent.getParent() instanceof KtPrimaryConstructor) {
776                    return getEnclosingElementForLocalDeclaration(((KtPrimaryConstructor) parent.getParent()).getContainingClassOrObject(), skipParameters);
777                }
778    
779                else if (skipParameters && parent != null && parent.getParent() instanceof KtNamedFunction) {
780                    declaration = (KtNamedFunction) parent.getParent();
781                }
782            }
783    
784            if (declaration instanceof PsiFile) {
785                return declaration;
786            }
787    
788            // No appropriate stub-tolerant method in PsiTreeUtil, nor JetStubbedPsiUtil, writing manually
789            PsiElement current = PsiTreeUtil.getStubOrPsiParent(declaration);
790            while (current != null) {
791                PsiElement parent = PsiTreeUtil.getStubOrPsiParent(current);
792                if (parent instanceof KtScript) return null;
793                if (current instanceof KtAnonymousInitializer) {
794                    return ((KtAnonymousInitializer) current).getBody();
795                }
796                if (current instanceof KtProperty || current instanceof KtFunction) {
797                    if (parent instanceof KtFile) {
798                        return (KtElement) current;
799                    }
800                    else if (parent instanceof KtClassBody && !isMemberOfObjectExpression((KtCallableDeclaration) current)) {
801                        return (KtElement) parent;
802                    }
803                }
804                if (current instanceof KtBlockExpression || current instanceof KtParameter) {
805                    return (KtElement) current;
806                }
807                if (current instanceof KtValueArgument) {
808                    return (KtElement) current;
809                }
810    
811                current = parent;
812            }
813            return null;
814        }
815    
816        public static boolean isLocal(@NotNull KtDeclaration declaration) {
817            return getEnclosingElementForLocalDeclaration(declaration) != null;
818        }
819    
820        @Nullable
821        public static KtToken getOperationToken(@NotNull KtOperationExpression expression) {
822            KtSimpleNameExpression operationExpression = expression.getOperationReference();
823            IElementType elementType = operationExpression.getReferencedNameElementType();
824            assert elementType == null || elementType instanceof KtToken :
825                    "JetOperationExpression should have operation token of type KtToken: " +
826                    expression;
827            return (KtToken) elementType;
828        }
829    
830        public static boolean isLabelIdentifierExpression(PsiElement element) {
831            return element instanceof KtLabelReferenceExpression;
832        }
833    
834        @Nullable
835        public static KtExpression getParentCallIfPresent(@NotNull KtExpression expression) {
836            PsiElement parent = expression.getParent();
837            while (parent != null) {
838                if (parent instanceof KtBinaryExpression ||
839                    parent instanceof KtUnaryExpression ||
840                    parent instanceof KtLabeledExpression ||
841                    parent instanceof KtDotQualifiedExpression ||
842                    parent instanceof KtCallExpression ||
843                    parent instanceof KtArrayAccessExpression ||
844                    parent instanceof KtDestructuringDeclaration) {
845    
846                    if (parent instanceof KtLabeledExpression) {
847                        parent = parent.getParent();
848                        continue;
849                    }
850    
851                    //check that it's in inlineable call would be in resolve call of parent
852                    return (KtExpression) parent;
853                }
854                else if (parent instanceof KtParenthesizedExpression || parent instanceof KtBinaryExpressionWithTypeRHS) {
855                    parent = parent.getParent();
856                }
857                else if (parent instanceof KtValueArgument || parent instanceof KtValueArgumentList) {
858                    parent = parent.getParent();
859                }
860                else if (parent instanceof KtLambdaExpression || parent instanceof KtAnnotatedExpression) {
861                    parent = parent.getParent();
862                }
863                else {
864                    return null;
865                }
866            }
867            return null;
868        }
869    
870        @Nullable
871        public static KtExpression getLastElementDeparenthesized(
872                @Nullable KtExpression expression,
873                @NotNull StatementFilter statementFilter
874        ) {
875            KtExpression deparenthesizedExpression = deparenthesize(expression);
876            if (deparenthesizedExpression instanceof KtBlockExpression) {
877                KtBlockExpression blockExpression = (KtBlockExpression) deparenthesizedExpression;
878                // todo
879                // This case is a temporary hack for 'if' branches.
880                // The right way to implement this logic is to interpret 'if' branches as function literals with explicitly-typed signatures
881                // (no arguments and no receiver) and therefore analyze them straight away (not in the 'complete' phase).
882                KtExpression lastStatementInABlock = StatementFilterKt.getLastStatementInABlock(statementFilter, blockExpression);
883                if (lastStatementInABlock != null) {
884                    return getLastElementDeparenthesized(lastStatementInABlock, statementFilter);
885                }
886            }
887            return deparenthesizedExpression;
888        }
889    }