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