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