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