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