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 }