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 }