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.KotlinExpressionParsing;
044 import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
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 visitKtElement(@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 = KtPsiUtilKt.getQualifiedElementSelector(importedReference);
267 return selector instanceof KtSimpleNameExpression ? (KtSimpleNameExpression) selector : null;
268 }
269
270 public static boolean isSelectorInQualified(@NotNull KtSimpleNameExpression nameExpression) {
271 KtElement qualifiedElement = KtPsiUtilKt.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 public static boolean isScriptDeclaration(@NotNull KtDeclaration namedDeclaration) {
284 return getScript(namedDeclaration) != null;
285 }
286
287 @Nullable
288 public static KtScript getScript(@NotNull KtDeclaration namedDeclaration) {
289 PsiElement parent = namedDeclaration.getParent();
290 if (parent != null && parent.getParent() instanceof KtScript) {
291 return (KtScript) parent.getParent();
292 }
293 else {
294 return null;
295 }
296 }
297
298 public static boolean isVariableNotParameterDeclaration(@NotNull KtDeclaration declaration) {
299 if (!(declaration instanceof KtVariableDeclaration)) return false;
300 if (declaration instanceof KtProperty) return true;
301 assert declaration instanceof KtMultiDeclarationEntry;
302 KtMultiDeclarationEntry multiDeclarationEntry = (KtMultiDeclarationEntry) declaration;
303 return !(multiDeclarationEntry.getParent().getParent() instanceof KtForExpression);
304 }
305
306 @Nullable
307 public static Name getConventionName(@NotNull KtSimpleNameExpression simpleNameExpression) {
308 if (simpleNameExpression.getIdentifier() != null) {
309 return simpleNameExpression.getReferencedNameAsName();
310 }
311
312 PsiElement firstChild = simpleNameExpression.getFirstChild();
313 if (firstChild != null) {
314 IElementType elementType = firstChild.getNode().getElementType();
315 if (elementType instanceof KtToken) {
316 KtToken jetToken = (KtToken) elementType;
317 boolean isPrefixExpression = simpleNameExpression.getParent() instanceof KtPrefixExpression;
318 if (isPrefixExpression) {
319 return OperatorConventions.getNameForOperationSymbol(jetToken, true, false);
320 }
321 return OperatorConventions.getNameForOperationSymbol(jetToken);
322 }
323 }
324
325 return null;
326 }
327
328 @Nullable
329 @Contract("null, _ -> null")
330 public static PsiElement getTopmostParentOfTypes(
331 @Nullable PsiElement element,
332 @NotNull Class<? extends PsiElement>... parentTypes) {
333 if (element instanceof PsiFile) return null;
334
335 PsiElement answer = PsiTreeUtil.getParentOfType(element, parentTypes);
336 if (answer instanceof PsiFile) return answer;
337
338 do {
339 PsiElement next = PsiTreeUtil.getParentOfType(answer, parentTypes);
340 if (next == null) break;
341 answer = next;
342 }
343 while (true);
344
345 return answer;
346 }
347
348 public static boolean isNullConstant(@NotNull KtExpression expression) {
349 KtExpression deparenthesized = deparenthesize(expression);
350 return deparenthesized instanceof KtConstantExpression && deparenthesized.getNode().getElementType() == KtNodeTypes.NULL;
351 }
352
353 public static boolean isTrueConstant(@Nullable KtExpression condition) {
354 return (condition != null && condition.getNode().getElementType() == KtNodeTypes.BOOLEAN_CONSTANT &&
355 condition.getNode().findChildByType(KtTokens.TRUE_KEYWORD) != null);
356 }
357
358 public static boolean isAbstract(@NotNull KtDeclarationWithBody declaration) {
359 return declaration.getBodyExpression() == null;
360 }
361
362 public static boolean isBackingFieldReference(@Nullable DeclarationDescriptor descriptor) {
363 return descriptor instanceof SyntheticFieldDescriptor;
364 }
365
366 @Nullable
367 public static KtExpression getExpressionOrLastStatementInBlock(@Nullable KtExpression expression) {
368 if (expression instanceof KtBlockExpression) {
369 return getLastStatementInABlock((KtBlockExpression) expression);
370 }
371 return expression;
372 }
373
374 @Nullable
375 public static KtExpression getLastStatementInABlock(@Nullable KtBlockExpression blockExpression) {
376 if (blockExpression == null) return null;
377 List<KtExpression> statements = blockExpression.getStatements();
378 return statements.isEmpty() ? null : statements.get(statements.size() - 1);
379 }
380
381 public static boolean isTrait(@NotNull KtClassOrObject classOrObject) {
382 return classOrObject instanceof KtClass && ((KtClass) classOrObject).isInterface();
383 }
384
385 @Nullable
386 public static KtClassOrObject getOutermostClassOrObject(@NotNull KtClassOrObject classOrObject) {
387 KtClassOrObject current = classOrObject;
388 while (true) {
389 PsiElement parent = current.getParent();
390 assert classOrObject.getParent() != null : "Class with no parent: " + classOrObject.getText();
391
392 if (parent instanceof PsiFile) {
393 return current;
394 }
395 if (!(parent instanceof KtClassBody)) {
396 // It is a local class, no legitimate outer
397 return current;
398 }
399
400 current = (KtClassOrObject) parent.getParent();
401 }
402 }
403
404 @Nullable
405 public static KtClassOrObject getClassIfParameterIsProperty(@NotNull KtParameter jetParameter) {
406 if (jetParameter.hasValOrVar()) {
407 PsiElement grandParent = jetParameter.getParent().getParent();
408 if (grandParent instanceof KtPrimaryConstructor) {
409 return ((KtPrimaryConstructor) grandParent).getContainingClassOrObject();
410 }
411 }
412
413 return null;
414 }
415
416 @Nullable
417 private static IElementType getOperation(@NotNull KtExpression expression) {
418 if (expression instanceof KtQualifiedExpression) {
419 return ((KtQualifiedExpression) expression).getOperationSign();
420 }
421 else if (expression instanceof KtOperationExpression) {
422 return ((KtOperationExpression) expression).getOperationReference().getReferencedNameElementType();
423 }
424 return null;
425 }
426
427
428 private static int getPriority(@NotNull KtExpression expression) {
429 int maxPriority = KotlinExpressionParsing.Precedence.values().length + 1;
430
431 // same as postfix operations
432 if (expression instanceof KtPostfixExpression ||
433 expression instanceof KtQualifiedExpression ||
434 expression instanceof KtCallExpression ||
435 expression instanceof KtArrayAccessExpression) {
436 return maxPriority - 1;
437 }
438
439 if (expression instanceof KtPrefixExpression || expression instanceof KtLabeledExpression) return maxPriority - 2;
440
441 if (expression instanceof KtIfExpression) {
442 return KotlinExpressionParsing.Precedence.ASSIGNMENT.ordinal();
443 }
444
445 if (expression instanceof KtSuperExpression) {
446 return maxPriority;
447 }
448
449 if (expression instanceof KtDeclaration || expression instanceof KtStatementExpression) {
450 return 0;
451 }
452
453 IElementType operation = getOperation(expression);
454 for (KotlinExpressionParsing.Precedence precedence : KotlinExpressionParsing.Precedence.values()) {
455 if (precedence != KotlinExpressionParsing.Precedence.PREFIX && precedence != KotlinExpressionParsing.Precedence.POSTFIX &&
456 precedence.getOperations().contains(operation)) {
457 return maxPriority - precedence.ordinal() - 1;
458 }
459 }
460
461 return maxPriority;
462 }
463
464 public static boolean areParenthesesUseless(@NotNull KtParenthesizedExpression expression) {
465 KtExpression innerExpression = expression.getExpression();
466 if (innerExpression == null) return true;
467
468 PsiElement parent = expression.getParent();
469 if (!(parent instanceof KtExpression)) return true;
470
471 return !areParenthesesNecessary(innerExpression, expression, (KtExpression) parent);
472 }
473
474 public static boolean areParenthesesNecessary(@NotNull KtExpression innerExpression, @NotNull KtExpression currentInner, @NotNull KtExpression parentExpression) {
475 if (parentExpression instanceof KtParenthesizedExpression || innerExpression instanceof KtParenthesizedExpression) {
476 return false;
477 }
478
479 if (parentExpression instanceof KtPackageDirective) return false;
480
481 if (parentExpression instanceof KtWhenExpression || innerExpression instanceof KtWhenExpression) {
482 return false;
483 }
484
485 if (innerExpression instanceof KtIfExpression) {
486 if (parentExpression instanceof KtQualifiedExpression) return true;
487
488 PsiElement current = parentExpression;
489
490 while (!(current instanceof KtBlockExpression || current instanceof KtDeclaration || current instanceof KtStatementExpression)) {
491 if (current.getTextRange().getEndOffset() != currentInner.getTextRange().getEndOffset()) {
492 return current.getText().charAt(current.getTextLength() - 1) != ')'; // if current expression is "guarded" by parenthesis, no extra parenthesis is necessary
493 }
494
495 current = current.getParent();
496 }
497 }
498
499 if (parentExpression instanceof KtCallExpression && currentInner == ((KtCallExpression) parentExpression).getCalleeExpression()) {
500 if (innerExpression instanceof KtSimpleNameExpression) return false;
501 if (KtPsiUtilKt.getQualifiedExpressionForSelector(parentExpression) != null) return true;
502 return !(innerExpression instanceof KtThisExpression
503 || innerExpression instanceof KtArrayAccessExpression
504 || innerExpression instanceof KtConstantExpression
505 || innerExpression instanceof KtStringTemplateExpression
506 || innerExpression instanceof KtCallExpression);
507 }
508
509 IElementType innerOperation = getOperation(innerExpression);
510 IElementType parentOperation = getOperation(parentExpression);
511
512 // 'return (@label{...})' case
513 if (parentExpression instanceof KtReturnExpression
514 && (innerExpression instanceof KtLabeledExpression || innerExpression instanceof KtAnnotatedExpression)) return true;
515
516 // '(x: Int) < y' case
517 if (innerExpression instanceof KtBinaryExpressionWithTypeRHS && parentOperation == KtTokens.LT) {
518 return true;
519 }
520
521 if (parentExpression instanceof KtLabeledExpression) return false;
522
523 // 'x ?: ...' case
524 if (parentExpression instanceof KtBinaryExpression && parentOperation == KtTokens.ELVIS && currentInner == ((KtBinaryExpression) parentExpression).getRight()) {
525 return false;
526 }
527
528 int innerPriority = getPriority(innerExpression);
529 int parentPriority = getPriority(parentExpression);
530
531 if (innerPriority == parentPriority) {
532 if (parentExpression instanceof KtBinaryExpression) {
533 if (innerOperation == KtTokens.ANDAND || innerOperation == KtTokens.OROR) {
534 return false;
535 }
536 return ((KtBinaryExpression) parentExpression).getRight() == currentInner;
537 }
538
539 //'-(-x)' case
540 if (parentExpression instanceof KtPrefixExpression && innerExpression instanceof KtPrefixExpression) {
541 return innerOperation == parentOperation && (innerOperation == KtTokens.PLUS || innerOperation == KtTokens.MINUS);
542 }
543 return false;
544 }
545
546 return innerPriority < parentPriority;
547 }
548
549 public static boolean isAssignment(@NotNull PsiElement element) {
550 return element instanceof KtBinaryExpression &&
551 KtTokens.ALL_ASSIGNMENTS.contains(((KtBinaryExpression) element).getOperationToken());
552 }
553
554 public static boolean isOrdinaryAssignment(@NotNull PsiElement element) {
555 return element instanceof KtBinaryExpression &&
556 ((KtBinaryExpression) element).getOperationToken().equals(KtTokens.EQ);
557 }
558
559 public static boolean checkVariableDeclarationInBlock(@NotNull KtBlockExpression block, @NotNull String varName) {
560 for (KtExpression element : block.getStatements()) {
561 if (element instanceof KtVariableDeclaration) {
562 if (((KtVariableDeclaration) element).getNameAsSafeName().asString().equals(varName)) {
563 return true;
564 }
565 }
566 }
567
568 return false;
569 }
570
571 public static boolean checkWhenExpressionHasSingleElse(@NotNull KtWhenExpression whenExpression) {
572 int elseCount = 0;
573 for (KtWhenEntry entry : whenExpression.getEntries()) {
574 if (entry.isElse()) {
575 elseCount++;
576 }
577 }
578 return (elseCount == 1);
579 }
580
581 @Nullable
582 public static PsiElement skipTrailingWhitespacesAndComments(@Nullable PsiElement element) {
583 return PsiTreeUtil.skipSiblingsForward(element, PsiWhiteSpace.class, PsiComment.class);
584 }
585
586 public static final Predicate<KtElement> ANY_JET_ELEMENT = new Predicate<KtElement>() {
587 @Override
588 public boolean apply(@Nullable KtElement input) {
589 return true;
590 }
591 };
592
593 @NotNull
594 public static String getText(@Nullable PsiElement element) {
595 return element != null ? element.getText() : "";
596 }
597
598 @Nullable
599 public static String getNullableText(@Nullable PsiElement element) {
600 return element != null ? element.getText() : null;
601 }
602
603 /**
604 * CommentUtilCore.isComment fails if element <strong>inside</strong> comment.
605 *
606 * Also, we can not add KDocTokens to COMMENTS TokenSet, because it is used in JetParserDefinition.getCommentTokens(),
607 * and therefor all COMMENTS tokens will be ignored by PsiBuilder.
608 *
609 * @param element
610 * @return
611 */
612 public static boolean isInComment(PsiElement element) {
613 return CommentUtilCore.isComment(element) || element instanceof KDocElement;
614 }
615
616 @Nullable
617 public static PsiElement getOutermostParent(@NotNull PsiElement element, @NotNull PsiElement upperBound, boolean strict) {
618 PsiElement parent = strict ? element.getParent() : element;
619 while (parent != null && parent.getParent() != upperBound) {
620 parent = parent.getParent();
621 }
622
623 return parent;
624 }
625
626 public static <T extends PsiElement> T getLastChildByType(@NotNull PsiElement root, @NotNull Class<? extends T>... elementTypes) {
627 PsiElement[] children = root.getChildren();
628
629 for (int i = children.length - 1; i >= 0; i--) {
630 if (PsiTreeUtil.instanceOf(children[i], elementTypes)) {
631 //noinspection unchecked
632 return (T) children[i];
633 }
634 }
635
636 return null;
637 }
638
639 @Nullable
640 public static KtElement getOutermostDescendantElement(
641 @Nullable PsiElement root,
642 boolean first,
643 final @NotNull Predicate<KtElement> predicate
644 ) {
645 if (!(root instanceof KtElement)) return null;
646
647 final List<KtElement> results = Lists.newArrayList();
648
649 root.accept(
650 new KtVisitorVoid() {
651 @Override
652 public void visitKtElement(@NotNull KtElement element) {
653 if (predicate.apply(element)) {
654 //noinspection unchecked
655 results.add(element);
656 }
657 else {
658 element.acceptChildren(this);
659 }
660 }
661 }
662 );
663
664 if (results.isEmpty()) return null;
665
666 return first ? results.get(0) : results.get(results.size() - 1);
667 }
668
669 @Nullable
670 public static PsiElement findChildByType(@NotNull PsiElement element, @NotNull IElementType type) {
671 ASTNode node = element.getNode().findChildByType(type);
672 return node == null ? null : node.getPsi();
673 }
674
675 @Nullable
676 public static PsiElement skipSiblingsBackwardByPredicate(@Nullable PsiElement element, Predicate<PsiElement> elementsToSkip) {
677 if (element == null) return null;
678 for (PsiElement e = element.getPrevSibling(); e != null; e = e.getPrevSibling()) {
679 if (elementsToSkip.apply(e)) continue;
680 return e;
681 }
682 return null;
683 }
684
685 public static PsiElement ascendIfPropertyAccessor(PsiElement element) {
686 if (element instanceof KtPropertyAccessor) {
687 return element.getParent();
688 }
689 return element;
690 }
691
692 @Nullable
693 public static KtModifierList replaceModifierList(@NotNull KtModifierListOwner owner, @Nullable KtModifierList modifierList) {
694 KtModifierList oldModifierList = owner.getModifierList();
695 if (modifierList == null) {
696 if (oldModifierList != null) oldModifierList.delete();
697 return null;
698 }
699 else {
700 if (oldModifierList == null) {
701 PsiElement firstChild = owner.getFirstChild();
702 return (KtModifierList) owner.addBefore(modifierList, firstChild);
703 }
704 else {
705 return (KtModifierList) oldModifierList.replace(modifierList);
706 }
707 }
708 }
709
710 @Nullable
711 public static String getPackageName(@NotNull KtElement element) {
712 KtFile file = element.getContainingKtFile();
713 KtPackageDirective header = PsiTreeUtil.findChildOfType(file, KtPackageDirective.class);
714
715 return header != null ? header.getQualifiedName() : null;
716 }
717
718 @Nullable
719 public static KtElement getEnclosingElementForLocalDeclaration(@NotNull KtDeclaration declaration) {
720 return getEnclosingElementForLocalDeclaration(declaration, true);
721 }
722
723 private static boolean isMemberOfObjectExpression(@NotNull KtCallableDeclaration propertyOrFunction) {
724 PsiElement parent = PsiTreeUtil.getStubOrPsiParent(propertyOrFunction);
725 if (!(parent instanceof KtClassBody)) return false;
726 PsiElement grandparent = PsiTreeUtil.getStubOrPsiParent(parent);
727 if (!(grandparent instanceof KtObjectDeclaration)) return false;
728 return PsiTreeUtil.getStubOrPsiParent(grandparent) instanceof KtObjectLiteralExpression;
729 }
730
731 @Nullable
732 public static KtElement getEnclosingElementForLocalDeclaration(@NotNull KtDeclaration declaration, boolean skipParameters) {
733 if (declaration instanceof KtTypeParameter && skipParameters) {
734 declaration = PsiTreeUtil.getParentOfType(declaration, KtNamedDeclaration.class);
735 }
736 else if (declaration instanceof KtParameter) {
737 PsiElement parent = declaration.getParent();
738
739 // val/var parameter of primary constructor should be considered as local according to containing class
740 if (((KtParameter) declaration).hasValOrVar() && parent != null && parent.getParent() instanceof KtPrimaryConstructor) {
741 return getEnclosingElementForLocalDeclaration(((KtPrimaryConstructor) parent.getParent()).getContainingClassOrObject(), skipParameters);
742 }
743
744 else if (skipParameters && parent != null && parent.getParent() instanceof KtNamedFunction) {
745 declaration = (KtNamedFunction) parent.getParent();
746 }
747 }
748
749 if (declaration instanceof PsiFile) {
750 return declaration;
751 }
752
753 // No appropriate stub-tolerant method in PsiTreeUtil, nor JetStubbedPsiUtil, writing manually
754 PsiElement current = PsiTreeUtil.getStubOrPsiParent(declaration);
755 while (current != null) {
756 PsiElement parent = PsiTreeUtil.getStubOrPsiParent(current);
757 if (parent instanceof KtScript) return null;
758 if (current instanceof KtClassInitializer) {
759 return ((KtClassInitializer) current).getBody();
760 }
761 if (current instanceof KtProperty || current instanceof KtFunction) {
762 if (parent instanceof KtFile) {
763 return (KtElement) current;
764 }
765 else if (parent instanceof KtClassBody && !isMemberOfObjectExpression((KtCallableDeclaration) current)) {
766 return (KtElement) parent;
767 }
768 }
769 if (current instanceof KtBlockExpression || current instanceof KtParameter) {
770 return (KtElement) current;
771 }
772
773 current = parent;
774 }
775 return null;
776 }
777
778 public static boolean isLocal(@NotNull KtDeclaration declaration) {
779 return getEnclosingElementForLocalDeclaration(declaration) != null;
780 }
781
782 @Nullable
783 public static KtToken getOperationToken(@NotNull KtOperationExpression expression) {
784 KtSimpleNameExpression operationExpression = expression.getOperationReference();
785 IElementType elementType = operationExpression.getReferencedNameElementType();
786 assert elementType == null || elementType instanceof KtToken :
787 "JetOperationExpression should have operation token of type KtToken: " +
788 expression;
789 return (KtToken) elementType;
790 }
791
792 public static boolean isLabelIdentifierExpression(PsiElement element) {
793 return element instanceof KtLabelReferenceExpression;
794 }
795
796 @Nullable
797 public static KtExpression getParentCallIfPresent(@NotNull KtExpression expression) {
798 PsiElement parent = expression.getParent();
799 while (parent != null) {
800 if (parent instanceof KtBinaryExpression ||
801 parent instanceof KtUnaryExpression ||
802 parent instanceof KtLabeledExpression ||
803 parent instanceof KtDotQualifiedExpression ||
804 parent instanceof KtCallExpression ||
805 parent instanceof KtArrayAccessExpression ||
806 parent instanceof KtMultiDeclaration) {
807
808 if (parent instanceof KtLabeledExpression) {
809 parent = parent.getParent();
810 continue;
811 }
812
813 //check that it's in inlineable call would be in resolve call of parent
814 return (KtExpression) parent;
815 }
816 else if (parent instanceof KtParenthesizedExpression || parent instanceof KtBinaryExpressionWithTypeRHS) {
817 parent = parent.getParent();
818 }
819 else if (parent instanceof KtValueArgument || parent instanceof KtValueArgumentList) {
820 parent = parent.getParent();
821 }
822 else if (parent instanceof KtFunctionLiteralExpression || parent instanceof KtAnnotatedExpression) {
823 parent = parent.getParent();
824 }
825 else {
826 return null;
827 }
828 }
829 return null;
830 }
831
832 @Nullable
833 public static KtExpression getLastElementDeparenthesized(
834 @Nullable KtExpression expression,
835 @NotNull StatementFilter statementFilter
836 ) {
837 KtExpression deparenthesizedExpression = deparenthesize(expression);
838 if (deparenthesizedExpression instanceof KtBlockExpression) {
839 KtBlockExpression blockExpression = (KtBlockExpression) deparenthesizedExpression;
840 // todo
841 // This case is a temporary hack for 'if' branches.
842 // The right way to implement this logic is to interpret 'if' branches as function literals with explicitly-typed signatures
843 // (no arguments and no receiver) and therefore analyze them straight away (not in the 'complete' phase).
844 KtExpression lastStatementInABlock = StatementFilterKt.getLastStatementInABlock(statementFilter, blockExpression);
845 if (lastStatementInABlock != null) {
846 return getLastElementDeparenthesized(lastStatementInABlock, statementFilter);
847 }
848 }
849 return deparenthesizedExpression;
850 }
851 }