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