Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jetbrains.kotlin.psi.JetPsiUtil Maven / Gradle / Ivy
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.psi;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.codeInsight.CommentUtilCore;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.JetNodeTypes;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor;
import org.jetbrains.kotlin.kdoc.psi.api.KDocElement;
import org.jetbrains.kotlin.lexer.JetToken;
import org.jetbrains.kotlin.lexer.JetTokens;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.name.SpecialNames;
import org.jetbrains.kotlin.parsing.JetExpressionParsing;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage;
import org.jetbrains.kotlin.resolve.ResolvePackage;
import org.jetbrains.kotlin.resolve.StatementFilter;
import org.jetbrains.kotlin.types.expressions.OperatorConventions;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class JetPsiUtil {
private JetPsiUtil() {
}
public interface JetExpressionWrapper {
JetExpression getBaseExpression();
}
public static void visitChildren(@NotNull JetElement element, @NotNull JetVisitor visitor, D data) {
PsiElement child = element.getFirstChild();
while (child != null) {
if (child instanceof JetElement) {
((JetElement) child).accept(visitor, data);
}
child = child.getNextSibling();
}
}
@NotNull
public static JetExpression safeDeparenthesize(@NotNull JetExpression expression) {
return safeDeparenthesize(expression, true);
}
@NotNull
public static JetExpression safeDeparenthesize(@NotNull JetExpression expression, boolean deparenthesizeBinaryExpressionWithTypeRHS) {
JetExpression deparenthesized = deparenthesize(expression, deparenthesizeBinaryExpressionWithTypeRHS);
return deparenthesized != null ? deparenthesized : expression;
}
@Nullable
public static JetExpression deparenthesize(@Nullable JetExpression expression) {
return deparenthesize(expression, /* deparenthesizeBinaryExpressionWithTypeRHS = */ true);
}
@Nullable
public static JetExpression deparenthesize(
@Nullable JetExpression expression,
boolean deparenthesizeBinaryExpressionWithTypeRHS
) {
return deparenthesizeWithResolutionStrategy(
expression, deparenthesizeBinaryExpressionWithTypeRHS, /* deparenthesizeRecursively = */ null);
}
@Nullable
public static JetExpression deparenthesizeOnce(
@Nullable JetExpression expression,
boolean deparenthesizeBinaryExpressionWithTypeRHS
) {
return deparenthesizeOnce(expression, deparenthesizeBinaryExpressionWithTypeRHS, null);
}
@Nullable
public static JetExpression deparenthesizeWithResolutionStrategy(
@Nullable JetExpression expression,
@Nullable Function typeResolutionStrategy
) {
return deparenthesizeWithResolutionStrategy(expression, true, typeResolutionStrategy);
}
@Nullable
private static JetExpression deparenthesizeWithResolutionStrategy(
@Nullable JetExpression expression,
boolean deparenthesizeBinaryExpressionWithTypeRHS,
@Nullable Function typeResolutionStrategy
) {
while (true) {
JetExpression baseExpression =
deparenthesizeOnce(expression, deparenthesizeBinaryExpressionWithTypeRHS, typeResolutionStrategy);
if (baseExpression == expression) return baseExpression;
expression = baseExpression;
}
}
@Nullable
private static JetExpression deparenthesizeOnce(
@Nullable JetExpression expression,
boolean deparenthesizeBinaryExpressionWithTypeRHS,
@Nullable Function typeResolutionStrategy
) {
if (deparenthesizeBinaryExpressionWithTypeRHS && expression instanceof JetBinaryExpressionWithTypeRHS) {
JetBinaryExpressionWithTypeRHS binaryExpression = (JetBinaryExpressionWithTypeRHS) expression;
JetSimpleNameExpression operationSign = binaryExpression.getOperationReference();
if (JetTokens.COLON.equals(operationSign.getReferencedNameElementType())) {
JetTypeReference typeReference = binaryExpression.getRight();
if (typeResolutionStrategy != null && typeReference != null) {
typeResolutionStrategy.apply(typeReference);
}
return binaryExpression.getLeft();
}
return expression;
}
else if (expression instanceof JetAnnotatedExpression) {
return ((JetAnnotatedExpression) expression).getBaseExpression();
}
else if (expression instanceof JetLabeledExpression) {
return ((JetLabeledExpression) expression).getBaseExpression();
}
else if (expression instanceof JetExpressionWrapper) {
return ((JetExpressionWrapper) expression).getBaseExpression();
}
else if (expression instanceof JetParenthesizedExpression) {
return ((JetParenthesizedExpression) expression).getExpression();
}
return expression;
}
@NotNull
public static Name safeName(@Nullable String name) {
return name == null ? SpecialNames.NO_NAME_PROVIDED : Name.identifier(name);
}
@NotNull
public static Set findRootExpressions(@NotNull Collection unreachableElements) {
Set rootElements = new HashSet();
final Set shadowedElements = new HashSet();
JetVisitorVoid shadowAllChildren = new JetVisitorVoid() {
@Override
public void visitJetElement(@NotNull JetElement element) {
if (shadowedElements.add(element)) {
element.acceptChildren(this);
}
}
};
for (JetElement element : unreachableElements) {
if (shadowedElements.contains(element)) continue;
element.acceptChildren(shadowAllChildren);
rootElements.removeAll(shadowedElements);
rootElements.add(element);
}
return rootElements;
}
@NotNull
public static String unquoteIdentifier(@NotNull String quoted) {
if (quoted.indexOf('`') < 0) {
return quoted;
}
if (quoted.startsWith("`") && quoted.endsWith("`") && quoted.length() >= 2) {
return quoted.substring(1, quoted.length() - 1);
}
else {
return quoted;
}
}
@NotNull
public static String unquoteIdentifierOrFieldReference(@NotNull String quoted) {
if (quoted.indexOf('`') < 0) {
return quoted;
}
if (quoted.startsWith("$")) {
return "$" + unquoteIdentifier(quoted.substring(1));
}
else {
return unquoteIdentifier(quoted);
}
}
/** @return null
iff the tye has syntactic errors */
@Nullable
public static FqName toQualifiedName(@NotNull JetUserType userType) {
List reversedNames = Lists.newArrayList();
JetUserType current = userType;
while (current != null) {
String name = current.getReferencedName();
if (name == null) return null;
reversedNames.add(name);
current = current.getQualifier();
}
return FqName.fromSegments(ContainerUtil.reverse(reversedNames));
}
@Nullable
public static Name getShortName(@NotNull JetAnnotationEntry annotation) {
JetTypeReference typeReference = annotation.getTypeReference();
assert typeReference != null : "Annotation entry hasn't typeReference " + annotation.getText();
JetTypeElement typeElement = typeReference.getTypeElement();
if (typeElement instanceof JetUserType) {
JetUserType userType = (JetUserType) typeElement;
String shortName = userType.getReferencedName();
if (shortName != null) {
return Name.identifier(shortName);
}
}
return null;
}
public static boolean isDeprecated(@NotNull JetModifierListOwner owner) {
JetModifierList modifierList = owner.getModifierList();
if (modifierList != null) {
List annotationEntries = modifierList.getAnnotationEntries();
for (JetAnnotationEntry annotation : annotationEntries) {
Name shortName = getShortName(annotation);
if (KotlinBuiltIns.FQ_NAMES.deprecated.shortName().equals(shortName)) {
return true;
}
}
}
return false;
}
@Nullable
public static T getDirectParentOfTypeForBlock(@NotNull JetBlockExpression block, @NotNull Class aClass) {
T parent = PsiTreeUtil.getParentOfType(block, aClass);
if (parent instanceof JetIfExpression) {
JetIfExpression ifExpression = (JetIfExpression) parent;
if (ifExpression.getElse() == block || ifExpression.getThen() == block) {
return parent;
}
}
if (parent instanceof JetWhenExpression) {
JetWhenExpression whenExpression = (JetWhenExpression) parent;
for (JetWhenEntry whenEntry : whenExpression.getEntries()) {
if (whenEntry.getExpression() == block) {
return parent;
}
}
}
if (parent instanceof JetFunctionLiteral) {
JetFunctionLiteral functionLiteral = (JetFunctionLiteral) parent;
if (functionLiteral.getBodyExpression() == block) {
return parent;
}
}
if (parent instanceof JetTryExpression) {
JetTryExpression tryExpression = (JetTryExpression) parent;
if (tryExpression.getTryBlock() == block) {
return parent;
}
for (JetCatchClause clause : tryExpression.getCatchClauses()) {
if (clause.getCatchBody() == block) {
return parent;
}
}
}
return null;
}
@Nullable
public static Name getAliasName(@NotNull JetImportDirective importDirective) {
if (importDirective.isAllUnder()) {
return null;
}
String aliasName = importDirective.getAliasName();
JetExpression importedReference = importDirective.getImportedReference();
if (importedReference == null) {
return null;
}
JetSimpleNameExpression referenceExpression = getLastReference(importedReference);
if (aliasName == null) {
aliasName = referenceExpression != null ? referenceExpression.getReferencedName() : null;
}
return aliasName != null && !aliasName.isEmpty() ? Name.identifier(aliasName) : null;
}
@Nullable
public static JetSimpleNameExpression getLastReference(@NotNull JetExpression importedReference) {
JetElement selector = PsiUtilPackage.getQualifiedElementSelector(importedReference);
return selector instanceof JetSimpleNameExpression ? (JetSimpleNameExpression) selector : null;
}
public static boolean isSelectorInQualified(@NotNull JetSimpleNameExpression nameExpression) {
JetElement qualifiedElement = PsiUtilPackage.getQualifiedElement(nameExpression);
return qualifiedElement instanceof JetQualifiedExpression
|| ((qualifiedElement instanceof JetUserType) && ((JetUserType) qualifiedElement).getQualifier() != null);
}
public static boolean isLHSOfDot(@NotNull JetExpression expression) {
PsiElement parent = expression.getParent();
if (!(parent instanceof JetQualifiedExpression)) return false;
JetQualifiedExpression qualifiedParent = (JetQualifiedExpression) parent;
return qualifiedParent.getReceiverExpression() == expression || isLHSOfDot(qualifiedParent);
}
// SCRIPT: is declaration in script?
public static boolean isScriptDeclaration(@NotNull JetDeclaration namedDeclaration) {
return getScript(namedDeclaration) != null;
}
// SCRIPT: get script from top-level declaration
@Nullable
public static JetScript getScript(@NotNull JetDeclaration namedDeclaration) {
PsiElement parent = namedDeclaration.getParent();
if (parent != null && parent.getParent() instanceof JetScript) {
return (JetScript) parent.getParent();
}
else {
return null;
}
}
public static boolean isVariableNotParameterDeclaration(@NotNull JetDeclaration declaration) {
if (!(declaration instanceof JetVariableDeclaration)) return false;
if (declaration instanceof JetProperty) return true;
assert declaration instanceof JetMultiDeclarationEntry;
JetMultiDeclarationEntry multiDeclarationEntry = (JetMultiDeclarationEntry) declaration;
return !(multiDeclarationEntry.getParent().getParent() instanceof JetForExpression);
}
@Nullable
public static Name getConventionName(@NotNull JetSimpleNameExpression simpleNameExpression) {
if (simpleNameExpression.getIdentifier() != null) {
return simpleNameExpression.getReferencedNameAsName();
}
PsiElement firstChild = simpleNameExpression.getFirstChild();
if (firstChild != null) {
IElementType elementType = firstChild.getNode().getElementType();
if (elementType instanceof JetToken) {
JetToken jetToken = (JetToken) elementType;
return OperatorConventions.getNameForOperationSymbol(jetToken);
}
}
return null;
}
@Nullable
@Contract("null, _ -> null")
public static PsiElement getTopmostParentOfTypes(
@Nullable PsiElement element,
@NotNull Class... parentTypes) {
if (element instanceof PsiFile) return null;
PsiElement answer = PsiTreeUtil.getParentOfType(element, parentTypes);
if (answer instanceof PsiFile) return answer;
do {
PsiElement next = PsiTreeUtil.getParentOfType(answer, parentTypes);
if (next == null) break;
answer = next;
}
while (true);
return answer;
}
public static boolean isNullConstant(@NotNull JetExpression expression) {
JetExpression deparenthesized = deparenthesize(expression);
return deparenthesized instanceof JetConstantExpression && deparenthesized.getNode().getElementType() == JetNodeTypes.NULL;
}
public static boolean isTrueConstant(@Nullable JetExpression condition) {
return (condition != null && condition.getNode().getElementType() == JetNodeTypes.BOOLEAN_CONSTANT &&
condition.getNode().findChildByType(JetTokens.TRUE_KEYWORD) != null);
}
public static boolean isAbstract(@NotNull JetDeclarationWithBody declaration) {
return declaration.getBodyExpression() == null;
}
public static boolean isBackingFieldReference(@NotNull JetSimpleNameExpression expression, @Nullable DeclarationDescriptor descriptor) {
return descriptor instanceof SyntheticFieldDescriptor || expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER;
}
public static boolean isBackingFieldReference(@Nullable JetElement element, @Nullable DeclarationDescriptor descriptor) {
return element instanceof JetSimpleNameExpression && isBackingFieldReference((JetSimpleNameExpression) element, descriptor);
}
@Nullable
public static JetExpression getExpressionOrLastStatementInBlock(@Nullable JetExpression expression) {
if (expression instanceof JetBlockExpression) {
return getLastStatementInABlock((JetBlockExpression) expression);
}
return expression;
}
@Nullable
public static JetExpression getLastStatementInABlock(@Nullable JetBlockExpression blockExpression) {
if (blockExpression == null) return null;
List statements = blockExpression.getStatements();
return statements.isEmpty() ? null : statements.get(statements.size() - 1);
}
public static boolean isTrait(@NotNull JetClassOrObject classOrObject) {
return classOrObject instanceof JetClass && ((JetClass) classOrObject).isInterface();
}
@Nullable
public static JetClassOrObject getOutermostClassOrObject(@NotNull JetClassOrObject classOrObject) {
JetClassOrObject current = classOrObject;
while (true) {
PsiElement parent = current.getParent();
assert classOrObject.getParent() != null : "Class with no parent: " + classOrObject.getText();
if (parent instanceof PsiFile) {
return current;
}
if (!(parent instanceof JetClassBody)) {
// It is a local class, no legitimate outer
return current;
}
current = (JetClassOrObject) parent.getParent();
}
}
@Nullable
public static JetClassOrObject getClassIfParameterIsProperty(@NotNull JetParameter jetParameter) {
if (jetParameter.hasValOrVar()) {
PsiElement grandParent = jetParameter.getParent().getParent();
if (grandParent instanceof JetPrimaryConstructor) {
return ((JetPrimaryConstructor) grandParent).getContainingClassOrObject();
}
}
return null;
}
@Nullable
private static IElementType getOperation(@NotNull JetExpression expression) {
if (expression instanceof JetQualifiedExpression) {
return ((JetQualifiedExpression) expression).getOperationSign();
}
else if (expression instanceof JetOperationExpression) {
return ((JetOperationExpression) expression).getOperationReference().getReferencedNameElementType();
}
return null;
}
private static int getPriority(@NotNull JetExpression expression) {
int maxPriority = JetExpressionParsing.Precedence.values().length + 1;
// same as postfix operations
if (expression instanceof JetPostfixExpression ||
expression instanceof JetQualifiedExpression ||
expression instanceof JetCallExpression ||
expression instanceof JetArrayAccessExpression) {
return maxPriority - 1;
}
if (expression instanceof JetPrefixExpression || expression instanceof JetLabeledExpression) return maxPriority - 2;
if (expression instanceof JetIfExpression) {
return JetExpressionParsing.Precedence.ASSIGNMENT.ordinal();
}
if (expression instanceof JetSuperExpression) {
return maxPriority;
}
if (expression instanceof JetDeclaration || expression instanceof JetStatementExpression) {
return 0;
}
IElementType operation = getOperation(expression);
for (JetExpressionParsing.Precedence precedence : JetExpressionParsing.Precedence.values()) {
if (precedence != JetExpressionParsing.Precedence.PREFIX && precedence != JetExpressionParsing.Precedence.POSTFIX &&
precedence.getOperations().contains(operation)) {
return maxPriority - precedence.ordinal() - 1;
}
}
return maxPriority;
}
public static boolean areParenthesesUseless(@NotNull JetParenthesizedExpression expression) {
JetExpression innerExpression = expression.getExpression();
if (innerExpression == null) return true;
PsiElement parent = expression.getParent();
if (!(parent instanceof JetExpression)) return true;
return !areParenthesesNecessary(innerExpression, expression, (JetExpression) parent);
}
public static boolean areParenthesesNecessary(@NotNull JetExpression innerExpression, @NotNull JetExpression currentInner, @NotNull JetExpression parentExpression) {
if (parentExpression instanceof JetParenthesizedExpression || innerExpression instanceof JetParenthesizedExpression) {
return false;
}
if (parentExpression instanceof JetPackageDirective) return false;
if (parentExpression instanceof JetWhenExpression || innerExpression instanceof JetWhenExpression) {
return false;
}
if (innerExpression instanceof JetIfExpression) {
PsiElement current = parentExpression;
while (!(current instanceof JetBlockExpression || current instanceof JetDeclaration || current instanceof JetStatementExpression)) {
if (current.getTextRange().getEndOffset() != currentInner.getTextRange().getEndOffset()) {
return current.getText().charAt(current.getTextLength() - 1) != ')'; // if current expression is "guarded" by parenthesis, no extra parenthesis is necessary
}
current = current.getParent();
}
}
if (parentExpression instanceof JetCallExpression && currentInner == ((JetCallExpression) parentExpression).getCalleeExpression()) {
if (innerExpression instanceof JetSimpleNameExpression) return false;
if (PsiUtilPackage.getQualifiedExpressionForSelector(parentExpression) != null) return true;
return !(innerExpression instanceof JetThisExpression
|| innerExpression instanceof JetArrayAccessExpression
|| innerExpression instanceof JetConstantExpression
|| innerExpression instanceof JetStringTemplateExpression
|| innerExpression instanceof JetCallExpression);
}
IElementType innerOperation = getOperation(innerExpression);
IElementType parentOperation = getOperation(parentExpression);
// 'return (@label{...})' case
if (parentExpression instanceof JetReturnExpression
&& (innerExpression instanceof JetLabeledExpression || innerExpression instanceof JetAnnotatedExpression)) return true;
// '(x: Int) < y' case
if (innerExpression instanceof JetBinaryExpressionWithTypeRHS && parentOperation == JetTokens.LT) {
return true;
}
if (parentExpression instanceof JetLabeledExpression) return false;
// 'x ?: ...' case
if (parentExpression instanceof JetBinaryExpression && parentOperation == JetTokens.ELVIS && currentInner == ((JetBinaryExpression) parentExpression).getRight()) {
return false;
}
int innerPriority = getPriority(innerExpression);
int parentPriority = getPriority(parentExpression);
if (innerPriority == parentPriority) {
if (parentExpression instanceof JetBinaryExpression) {
if (innerOperation == JetTokens.ANDAND || innerOperation == JetTokens.OROR) {
return false;
}
return ((JetBinaryExpression) parentExpression).getRight() == currentInner;
}
//'-(-x)' case
if (parentExpression instanceof JetPrefixExpression && innerExpression instanceof JetPrefixExpression) {
return innerOperation == parentOperation && (innerOperation == JetTokens.PLUS || innerOperation == JetTokens.MINUS);
}
return false;
}
return innerPriority < parentPriority;
}
public static boolean isAssignment(@NotNull PsiElement element) {
return element instanceof JetBinaryExpression &&
JetTokens.ALL_ASSIGNMENTS.contains(((JetBinaryExpression) element).getOperationToken());
}
public static boolean isOrdinaryAssignment(@NotNull PsiElement element) {
return element instanceof JetBinaryExpression &&
((JetBinaryExpression) element).getOperationToken().equals(JetTokens.EQ);
}
public static boolean checkVariableDeclarationInBlock(@NotNull JetBlockExpression block, @NotNull String varName) {
for (JetExpression element : block.getStatements()) {
if (element instanceof JetVariableDeclaration) {
if (((JetVariableDeclaration) element).getNameAsSafeName().asString().equals(varName)) {
return true;
}
}
}
return false;
}
public static boolean checkWhenExpressionHasSingleElse(@NotNull JetWhenExpression whenExpression) {
int elseCount = 0;
for (JetWhenEntry entry : whenExpression.getEntries()) {
if (entry.isElse()) {
elseCount++;
}
}
return (elseCount == 1);
}
@Nullable
public static PsiElement skipTrailingWhitespacesAndComments(@Nullable PsiElement element) {
return PsiTreeUtil.skipSiblingsForward(element, PsiWhiteSpace.class, PsiComment.class);
}
public static final Predicate ANY_JET_ELEMENT = new Predicate() {
@Override
public boolean apply(@Nullable JetElement input) {
return true;
}
};
@NotNull
public static String getText(@Nullable PsiElement element) {
return element != null ? element.getText() : "";
}
@Nullable
public static String getNullableText(@Nullable PsiElement element) {
return element != null ? element.getText() : null;
}
/**
* CommentUtilCore.isComment fails if element inside comment.
*
* Also, we can not add KDocTokens to COMMENTS TokenSet, because it is used in JetParserDefinition.getCommentTokens(),
* and therefor all COMMENTS tokens will be ignored by PsiBuilder.
*
* @param element
* @return
*/
public static boolean isInComment(PsiElement element) {
return CommentUtilCore.isComment(element) || element instanceof KDocElement;
}
@Nullable
public static PsiElement getOutermostParent(@NotNull PsiElement element, @NotNull PsiElement upperBound, boolean strict) {
PsiElement parent = strict ? element.getParent() : element;
while (parent != null && parent.getParent() != upperBound) {
parent = parent.getParent();
}
return parent;
}
public static T getLastChildByType(@NotNull PsiElement root, @NotNull Class... elementTypes) {
PsiElement[] children = root.getChildren();
for (int i = children.length - 1; i >= 0; i--) {
if (PsiTreeUtil.instanceOf(children[i], elementTypes)) {
//noinspection unchecked
return (T) children[i];
}
}
return null;
}
@Nullable
public static JetElement getOutermostDescendantElement(
@Nullable PsiElement root,
boolean first,
final @NotNull Predicate predicate
) {
if (!(root instanceof JetElement)) return null;
final List results = Lists.newArrayList();
root.accept(
new JetVisitorVoid() {
@Override
public void visitJetElement(@NotNull JetElement element) {
if (predicate.apply(element)) {
//noinspection unchecked
results.add(element);
}
else {
element.acceptChildren(this);
}
}
}
);
if (results.isEmpty()) return null;
return first ? results.get(0) : results.get(results.size() - 1);
}
@Nullable
public static PsiElement findChildByType(@NotNull PsiElement element, @NotNull IElementType type) {
ASTNode node = element.getNode().findChildByType(type);
return node == null ? null : node.getPsi();
}
@Nullable
public static PsiElement skipSiblingsBackwardByPredicate(@Nullable PsiElement element, Predicate elementsToSkip) {
if (element == null) return null;
for (PsiElement e = element.getPrevSibling(); e != null; e = e.getPrevSibling()) {
if (elementsToSkip.apply(e)) continue;
return e;
}
return null;
}
public static PsiElement ascendIfPropertyAccessor(PsiElement element) {
if (element instanceof JetPropertyAccessor) {
return element.getParent();
}
return element;
}
@Nullable
public static JetModifierList replaceModifierList(@NotNull JetModifierListOwner owner, @Nullable JetModifierList modifierList) {
JetModifierList oldModifierList = owner.getModifierList();
if (modifierList == null) {
if (oldModifierList != null) oldModifierList.delete();
return null;
}
else {
if (oldModifierList == null) {
PsiElement firstChild = owner.getFirstChild();
return (JetModifierList) owner.addBefore(modifierList, firstChild);
}
else {
return (JetModifierList) oldModifierList.replace(modifierList);
}
}
}
@Nullable
public static String getPackageName(@NotNull JetElement element) {
JetFile file = element.getContainingJetFile();
JetPackageDirective header = PsiTreeUtil.findChildOfType(file, JetPackageDirective.class);
return header != null ? header.getQualifiedName() : null;
}
@Nullable
public static JetElement getEnclosingElementForLocalDeclaration(@NotNull JetDeclaration declaration) {
return getEnclosingElementForLocalDeclaration(declaration, true);
}
private static boolean isMemberOfObjectExpression(@NotNull JetCallableDeclaration propertyOrFunction) {
PsiElement parent = PsiTreeUtil.getStubOrPsiParent(propertyOrFunction);
if (!(parent instanceof JetClassBody)) return false;
PsiElement grandparent = PsiTreeUtil.getStubOrPsiParent(parent);
if (!(grandparent instanceof JetObjectDeclaration)) return false;
return PsiTreeUtil.getStubOrPsiParent(grandparent) instanceof JetObjectLiteralExpression;
}
@Nullable
public static JetElement getEnclosingElementForLocalDeclaration(@NotNull JetDeclaration declaration, boolean skipParameters) {
if (declaration instanceof JetTypeParameter && skipParameters) {
declaration = PsiTreeUtil.getParentOfType(declaration, JetNamedDeclaration.class);
}
else if (declaration instanceof JetParameter) {
PsiElement parent = declaration.getParent();
// val/var parameter of primary constructor should be considered as local according to containing class
if (((JetParameter) declaration).hasValOrVar() && parent != null && parent.getParent() instanceof JetPrimaryConstructor) {
return getEnclosingElementForLocalDeclaration(((JetPrimaryConstructor) parent.getParent()).getContainingClassOrObject(), skipParameters);
}
else if (skipParameters && parent != null && parent.getParent() instanceof JetNamedFunction) {
declaration = (JetNamedFunction) parent.getParent();
}
}
if (declaration instanceof PsiFile) {
return declaration;
}
// No appropriate stub-tolerant method in PsiTreeUtil, nor JetStubbedPsiUtil, writing manually
PsiElement current = PsiTreeUtil.getStubOrPsiParent(declaration);
while (current != null) {
PsiElement parent = PsiTreeUtil.getStubOrPsiParent(current);
if (parent instanceof JetScript) return null;
if (current instanceof JetClassInitializer) {
return ((JetClassInitializer) current).getBody();
}
if (current instanceof JetProperty || current instanceof JetFunction) {
if (parent instanceof JetFile) {
return (JetElement) current;
}
else if (parent instanceof JetClassBody && !isMemberOfObjectExpression((JetCallableDeclaration) current)) {
return (JetElement) parent;
}
}
if (current instanceof JetBlockExpression || current instanceof JetParameter) {
return (JetElement) current;
}
current = parent;
}
return null;
}
public static boolean isLocal(@NotNull JetDeclaration declaration) {
return getEnclosingElementForLocalDeclaration(declaration) != null;
}
@Nullable
public static JetToken getOperationToken(@NotNull JetOperationExpression expression) {
JetSimpleNameExpression operationExpression = expression.getOperationReference();
IElementType elementType = operationExpression.getReferencedNameElementType();
assert elementType == null || elementType instanceof JetToken :
"JetOperationExpression should have operation token of type JetToken: " +
expression;
return (JetToken) elementType;
}
public static boolean isLabelIdentifierExpression(PsiElement element) {
return element instanceof JetLabelReferenceExpression;
}
@Nullable
public static JetExpression getParentCallIfPresent(@NotNull JetExpression expression) {
PsiElement parent = expression.getParent();
while (parent != null) {
if (parent instanceof JetBinaryExpression ||
parent instanceof JetUnaryExpression ||
parent instanceof JetLabeledExpression ||
parent instanceof JetDotQualifiedExpression ||
parent instanceof JetCallExpression ||
parent instanceof JetArrayAccessExpression ||
parent instanceof JetMultiDeclaration) {
if (parent instanceof JetLabeledExpression) {
parent = parent.getParent();
continue;
}
//check that it's in inlineable call would be in resolve call of parent
return (JetExpression) parent;
}
else if (parent instanceof JetParenthesizedExpression || parent instanceof JetBinaryExpressionWithTypeRHS) {
parent = parent.getParent();
}
else if (parent instanceof JetValueArgument || parent instanceof JetValueArgumentList) {
parent = parent.getParent();
}
else if (parent instanceof JetFunctionLiteralExpression || parent instanceof JetAnnotatedExpression) {
parent = parent.getParent();
}
else {
return null;
}
}
return null;
}
@Nullable
public static JetExpression getLastElementDeparenthesized(
@Nullable JetExpression expression,
@NotNull StatementFilter statementFilter
) {
JetExpression deparenthesizedExpression = JetPsiUtil.deparenthesize(expression, false);
if (deparenthesizedExpression instanceof JetBlockExpression) {
JetBlockExpression blockExpression = (JetBlockExpression) deparenthesizedExpression;
// todo
// This case is a temporary hack for 'if' branches.
// The right way to implement this logic is to interpret 'if' branches as function literals with explicitly-typed signatures
// (no arguments and no receiver) and therefore analyze them straight away (not in the 'complete' phase).
JetExpression lastStatementInABlock = ResolvePackage.getLastStatementInABlock(statementFilter, blockExpression);
if (lastStatementInABlock != null) {
return getLastElementDeparenthesized(lastStatementInABlock, statementFilter);
}
}
return deparenthesizedExpression;
}
}