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