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